对象展示
Sui 对象展示是一种标准,它使链上资产的链外可视化表示成为可能。这在 NFT 项目中非常有用。
在启用结构体的展示之前,需要一个归属的发布者(Publisher)对象。发布者是“一次性见证”(One-Time Witness)模式的实现, 我们将在后续课程中介绍更多细节。查看发布者主题以了解更多信息。
sui::display::Display<T>
使用一组带有对象属性占位符的命名模板,例如:
{
"name": "{name}",
"image_url": "https://xxx/{image_id}"
}
name
和 image_id
必须是对象类型 T 的属性。
官方文档建议的属性包括:
name
- 对象名称。description
- 对象描述。link
- 项目中的对象链接。image_url
- 对象的视觉图像 URL。thumbnail_url
- 用作预览的小图像的 URL。project_url
- 项目链接。creator
- 对象创建者。
在我们的 Move Castle 游戏中,模板应为:
{
"name": "{name}",
"link": "https://movecastle.info/castles/{serial_number}",
"image_url": "https://images.movecastle.info/static/media/castles/{image_id}.png",
"description": "{description}",
"project_url": "https://movecastle.info",
"creator": "Castle Builder"
}
城堡图像是预生成的,托管在我们的项目网站 "https://movecastle.info" 下。image_id
是城堡序列号的视觉部分。
在 utils.move
模块中添加一个 serial_number_to_image_id
函数,以从序列号中检索图像 ID:
use std::string::{Self, String};
public(package) fun serial_number_to_image_id(serial_number: u64): String {
let id = serial_number / 10 % 10000u64;
u64_to_string(id, 4)
}
/// 将 u64 转换为字符串,如果长度小于固定长度,则在前面加 "0"
public(package) fun u64_to_string(n: u64, fixed_length: u64): String {
let mut result: vector<u8> = vector::empty<u8>();
if (n == 0) {
vector::push_back(&mut result, 48);
} else {
while (n > 0) {
let digit = ((n % 10) as u8) + 48;
vector::push_back(&mut result, digit);
n = n / 10;
};
// 在字符串前面添加 "0" 直到达到固定长度。
while (vector::length(&result) < fixed_length) {
vector::push_back(&mut result, 48);
};
vector::reverse<u8>(&mut result);
};
string::utf8(result)
}
这个函数从序列号中提取中间的 4 位数字。
在 castle.move
中,我们需要添加 OTW 逻辑,并声明 Publisher
对象:
module move_castle::castle {
use sui::package;
/// 模块的一次性见证,它必须是模块中的第一个结构体,
/// 并且它的名称应该与模块名称相同,但全部大写。
public struct CASTLE has drop {}
fun init(otw: CASTLE, ctx: &mut TxContext) {
let publisher = package::claim(otw, ctx);
transfer::public_transfer(publisher, tx_context::sender(ctx));
}
}
然后我们需要新建一个 Display<Castle>
对象,因此 castle.move
中的整个 init
函数应为:
module move_castle::castle {
use std::string::{Self, utf8, String};
use sui::package;
use sui::display;
/// 模块的一次性见证,它必须是模块中的第一个结构体,
/// 并且它的名称应该与模块名称相同,但全部大写。
public struct CASTLE has drop {}
fun init(otw: CASTLE, ctx: &mut TxContext) {
let keys = vector[
utf8(b"name"),
utf8(b"link"),
utf8(b"image_url"),
utf8(b"description"),
utf8(b"project_url"),
utf8(b"creator"),
];
let values = vector[
utf8(b"{name}"),
utf8(b"https://movecastle.info/castles/{serial_number}"),
utf8(b"https://images.movecastle.info/static/media/castles/{image_id}.png"),
utf8(b"{description}"),
utf8(b"https://movecastle.info"),
utf8(b"Castle Builder"),
];
let publisher = package::claim(otw, ctx);
let mut display = display::new_with_fields<Castle>(&publisher, keys, values, ctx);
display::update_version(&mut display);
transfer::public_transfer(publisher, tx_context::sender(ctx));
transfer::public_transfer(display, tx_context::sender(ctx));
}
}
别忘了在 Castle
结构体中添加 image_id
属性:
module move_castle::castle {
/// 城堡
public struct Castle has key, store {
id: UID,
name: String,
description: String,
serial_number: u64,
image_id: String,
}
/// 创建新城堡
entry fun build_castle(...) {
...
// 生成序列号和图像 ID
let serial_number = utils::generate_castle_serial_number(size, &obj_id);
let image_id = utils::serial_number_to_image_id(serial_number);
// 新建城堡
let castle = Castle {
id: obj_id,
name: string::utf8(name_bytes),
description: string::utf8(desc_bytes),
serial_number: serial_number,
image_id: image_id,
};
...
}
}
将您的包部署到测试网
或开发网
,建造一个城堡并在 Sui Explorer 上查看。