由于引擎没有提供除了数组以外的可序列化容器,又有需要,所以做了一个可序列化的 Map,需要的自取,如果有bug 也请反馈。
SerializableMap.ts
/**
* author: ethermedia
*
* 这不是一个万能的 可序列化Map,只包含简单的必要功能
*
* @class SerializableMap
* @extends CCObject
*/
import { _decorator, CCObject, Component, Eventify, Node, ValueType } from 'cc';
import { warn } from 'console';
const { ccclass, property } = _decorator;
type Primitive = string | number
@ccclass('SerializableMap')
export class SerializableMap<K extends Primitive, V extends ValueType> extends CCObject implements Map<K, V>{
@property
private _keys:K[] = Array<K>();
@property
private _values:V[] = Array<V>();
private _kIndex:Map<K, number> = new Map();
private _allocSize:number = 100;
constructor(...args) {
super(...args);
}
destroy(): boolean {
this._keys.length = 0;
this._values.length = 0;
this._kIndex.clear();
return super.destroy();
}
static __deserialize__(
deserializer: any,
object: Record<string, unknown>,
deserialized: Record<string, unknown>,
constructor: any,):void
{
object._keys = deserialized._keys;
object._values = deserialized._values;
object._name = deserialized._name;
object._objFlags = deserialized._objFlags;
//Engine doesn't has a callback when a CCObject is deserialized. You have to inject to it's deserialization process.
//Or, you need to initialized the index map by hand.
if (object.onDeserialized instanceof Function) {
(object as any).onDeserialized();
}
}
onDeserialized (): void {
this.initIndex();
}
private initIndex() {
this.pack();
for (let i = 0; i < this._keys.length; i++) {
if (this._keys[i] == null) {
continue;
}
this._kIndex.set(this._keys[i], i);
}
}
private pack() {
let firstEmtpyIndex = -1;
let findingEmptyKey = true;
for (let scanningIndex = 0; scanningIndex < this._keys.length; scanningIndex++) {
if (findingEmptyKey && this._keys[scanningIndex] == null) {
firstEmtpyIndex = scanningIndex;
findingEmptyKey = false;
continue;
}
if (!findingEmptyKey && this._keys[scanningIndex] != null) {
this._keys[firstEmtpyIndex] = this._keys[scanningIndex];
this._values[firstEmtpyIndex] = this._values[scanningIndex];
this._keys[scanningIndex] = null;
this._values[scanningIndex] = null;
scanningIndex = firstEmtpyIndex + 1;
findingEmptyKey = true;
continue;
}
}
}
getIndex(key:K):number {
if (!this._kIndex.has(key)) {
return -1;
}
return this._kIndex.get(key);
}
//-----Map interfaces-----
clear(): void {
this._keys.length = 0;
this._values.length = 0;
this._kIndex.clear();
}
delete(key: K): boolean {
let index = this.getIndex(key);
if (index == -1) {
return;
}
// this._keys.splice(index, 1);
// this._values.splice(index, 1);
this._keys[index] = null;
this._values[index] = null;
this._kIndex.delete(key);
}
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void {
for (let i = 0; i < this._keys.length; i++) {
callbackfn(this._values[i], this._keys[i], this);
}
}
get(key: K): V {
let index = this.getIndex(key);
if (index == -1) {
return;
}
return this._values[index];
}
has(key: K): boolean {
let index = this.getIndex(key);
if (index == -1) {
return false;
}
return true;
}
private _nextIndex:number = 0;
set(key: K, value: V): this {
if (key == null) {
console.warn("SerializableMap.set(key), key can not be null or undefined.")
return;
}
let index = this.getIndex(key);
if (index == -1) {
//allocate a chunk for decreasing too many sizing operations.
if (this._keys.length <= this._nextIndex) {
this._keys.length += this._allocSize;
this._values.length += this._allocSize;
}
this._keys[this._nextIndex] = key;
this._values[this._nextIndex] = value;
this._kIndex.set(key, this._nextIndex);
this._nextIndex++;
return this;
} else {
this._values[index] = value;
return this;
}
}
get size(): number {
return this._kIndex.size;
}
keys(): IterableIterator<K> {
return this._keys.values();
}
values(): IterableIterator<V> {
return this._values.values();
}
[Symbol.toStringTag]: "SerializableMap";
entries(): IterableIterator<[K, V]> {
let me = this;
let keys = this.keys();
return function* () {
for (let key of keys) {
yield [key, me.get(key)]
}
} ()
}
[Symbol.iterator](): IterableIterator<[K, V]> {
return this.entries();
}
}
Test.ts :
import { _decorator, Component, EventTouch, Node, v2, Vec2 } from 'cc';
import { SerializableMap } from './SerializableMap';
import { EDITOR } from 'cc/env';
const { ccclass, property, executeInEditMode} = _decorator;
@ccclass('Test')
@executeInEditMode
export class Test extends Component {
@property({ type: SerializableMap })
map:SerializableMap<number, Vec2> = new SerializableMap<number, Vec2>();
start() {
if (EDITOR) {
this.map.set(1, v2(1, 1));
this.map.set(2, v2(2, 2));
this.map.set(3, v2(3, 3));
this.map.set(4, v2(4, 4));
console.log("Elements added")
this.map.delete(2);
this.map.delete(4);
console.log("Elements removed")
} else {
console.log(this.map);
}
}
update(deltaTime: number) {
}
}
