- 致编者:请牢记我们的域名wiki.mcbe-dev.net!
- 致编者:欢迎加入本Wiki的官方交流QQ群或Discord服务器!
- 基岩版1.19.31现已发布!(了解更多)
- Inner Core现已支持Xbox模组联机!(了解更多)
- 如果您是第一次来到本Wiki,欢迎注册一个账户
- 点击顶部的“编辑”或“编辑源代码”按钮即可编辑当前页面
- 请知悉:在不登录时也可以编辑和新建页面,但是您当前的IP地址会记录在编辑历史中
教學:編寫腳本API/動態屬性
引言[編輯]
動態屬性(DynamicProperties) 是腳本系統儲存自訂資料的一種格式。目前動態屬性可以儲存在實體、物品和世界上。相較於資料驅動中屬性(或狀態)的「靜態性」,動態屬性可以實時增刪,且動態屬性的型別不固定,腳本系統以此可以實現資料的動態儲存。
動態屬性以NBT的形式儲存,其資料內容位於DynamicProperties複合標籤下。每個行為包的動態屬性儲存在以該行為包UUID命名的複合標籤內。
- 動態屬性NBT結構:
- DynamicProperties
- <UUID>
- <dynamic_property_id1>
- <dynamic_property_id2>
- X
- Y
- Z
- ……
- <UUID>
取得和設定[編輯]
舊版動屬相對複雜,在新版,提供了以下5個方法來對動態屬性進行操作:
clearDynamicProperties
:清除該物件上的所有動態屬性getDynamicProperty
:取得該物件上指定動態屬性的值getDynamicPropertyIds
:取得該物件上所有動態屬性的IDgetDynamicPropertyTotalByteCount
:取得該物件上儲存的所有動態屬性的總字節數setDynamicProperty
:在該物件上設定一個動態屬性
這些方法可從World、Entity和ItemStack三個類中呼叫。
注意:ItemStack类上此ItemStack必须不可堆叠,这要求在本质上不能堆叠,同时利用nbt修改的堆叠也无效。
動態屬性可以儲存布林型、數值型、字串型和三維向量型的資料,單個動態屬性的資料大小限制在32KB以內。大量的動態屬性資料可能會導致某些裝置上載入緩慢。
設定[編輯]
對任意可使用動屬的物件,使用setDynamicProperty
可設定其動屬。
import {
ItemStack
} from "@minecraft/server";
// setDynamicProperty(identifier: string, value?: boolean | number | string | Vector3): void
const exampleItem = new ItemStack("minecraft:diamond_axe");
exampleItem.setDynamicProperty("example", "Test");
exampleItem.setDynamicProperty("示例", true);
// identifier 在此不是命名空间,可为任意字符串,但推荐使用命名空间
setDynamicProperty
的資料可為一個字串,大小限制為32726個字元。
JSON是一種好的方法,JSON.stringify()
可以把JSON轉為字串,JSON.parse()
又可以把字串轉回JSON。所以可以像下面這麼做:
import {
world
} from "@minecraft/server";
function setData(id, json, Class = world) {
Class.setDynamicProperty(id, JSON.stringify(json));
}
除此外我們也可以清除動屬。
import {
ItemStack
} from "@minecraft/server";
// clearDynamicProperties(): void
const exampleItem = new ItemStack("minecraft:diamond_axe");
exampleItem.setDynamicProperty("example", "Test");
exampleItem.clearDynamicProperties();
clearDynamicProperties()
可以清除物件上的所有動屬。
取得[編輯]
對任意使用了動屬的物件,使用以下方法可取得其動屬。
getDynamicProperty()
可取得目標動屬,範例如下:
import {
ItemStack
} from "@minecraft/server";
// getDynamicProperty(identifier: string): boolean | number | string | Vector3 | undefined
// ...
// 省略定义和设置动属的过程。
let result = exampleItem.getDynamicProperty("example");
console.warn(result);
getDynamicPropertyIds()
可取得物件上使用的可用動態屬性識別碼集,範例如下:
import {
ItemStack
} from "@minecraft/server";
// getDynamicPropertyIds(): string[]
// ...
// 省略定义和设置动属的过程。
let result = exampleItem.getDynamicPropertyIds();
console.warn(result);
getDynamicPropertyTotalByteCount()
可獲得物件儲存的所有動態屬性的總大小(以字節為單位)。這包括鍵和值的大小。這對於診斷效能警告標誌非常有用 - 例如,如果一個實體具有許多兆字節的關聯動態屬性,則在各種裝置上載入它可能會很慢,範例如下:
import {
ItemStack
} from "@minecraft/server";
// getDynamicPropertyTotalByteCount(): number
// ...
// 省略定义和设置动属的过程。
let result = exampleItem.getDynamicPropertyTotalByteCount();
console.warn(result);
再結合上面的setData
,我們可以使用它:
import {
world
} from "@minecraft/server";
function getData(id, Class = world) {
return JSON.parse(Class.getDynamicProperty(id));
}
動態屬性管理[編輯]
對於一些大型的專案,我們可能要進行比較複雜的動態屬性管理。下面有些思路供您參考:
分段[編輯]
32726個字元對於小專案已經足夠使用了,但對於大型專案來說遠遠不夠,可以嘗試將長的字串分割成多部分以供儲存:
// 限制最大分割长度
const BLOCK_LENGTH = 1000;
function readDataOnDynamicProperty(target) {
// 读取分割块数
const quantity = target.getDynamicProperty("data_space_quantity");
// 没有正常读取成功直接返回
if (typeof quantity !== "number") return {};
if (quantity === 0) return {};
// 准备储存量
let value = "";
// 拼接
for (let i = 1; i <= quantity; i++) {
const currentValue = target.getDynamicProperty(`data_space_${i}`);
if (typeof currentValue !== "string" || currentValue === "") return {};
value += currentValue;
}
try {
// 转回对象
return JSON.parse(value);
} catch {
return {};
}
}
function writeDataOnDynamicProperty(target, source_data) {
// 转为字符串
const data = JSON.stringify(source_data);
let dataBlocks = [];
// 分割
for (let i = 0; i < data.length; i += BLOCK_LENGTH) {
dataBlocks.push(data.slice(i, i + BLOCK_LENGTH));
}
// 设置数量
const quantity = dataBlocks.length;
target.setDynamicProperty("data_space_quantity", quantity);
// 保存
for (let i = 1; i <= quantity; i++) {
target.setDynamicProperty(`data_space_${i}`, dataBlocks[i - 1]);
}
}
這裡我們預設將所有的資料儲存在同一個物件中,這樣可以更大程度的利用空間。
壓縮[編輯]
透過各種形式,我們可以壓縮字串來獲得更多的儲存空間,js有一個bota()
方法可以將字串base64化,但我們還有另外的更好方法:
LZString是一個以MIT協定開源(該協定限制很小,在我們的專案中可以很好地使用)的字串壓縮專案(JS),它本來旨在滿足在localStorage
中儲存大量資料的需求,但其原理就是使用壓縮的方式。其壓縮度很高,可以被我們使用:
npm i lz-string
如果您有NodeJs的開發環境的話,請使用上面的程式碼來安裝它。否則,請在這個地方來複製它的單檔案版本。
儲存與讀取[編輯]
上面的方法足以滿足大部分需求,但壓縮和儲存都是消耗大量時間的操作,我們需要一個良好的方式來完成儲存和讀取:
下面只介紹思路,檔案保留在實例中:
- 使用物件(
DataObject
)來控制系統。 - 使用方法來讀取物件的引用,這樣可以透過直接的賦值操作,而無需額外設定。
- 監聽
worldInitialize
或worldLoad
以保證在世界載入完成的時候去讀取動態屬性。 - 同時註冊一個循環呼叫器(
runInterval
),來保證自動儲存,防止資料丟失。 - 監聽
shutdown
來保證在退出遊戲之前儲存資料。 - 監聽
playerSpawn
用來在玩家初次生成時讀取動態屬性,推薦在世界載入的時候也讀取所有玩家的動態屬性(防止/reload
指令引起錯誤)