可序列化Map SerializableMap

由于引擎没有提供除了数组以外的可序列化容器,又有需要,所以做了一个可序列化的 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) {
		
	}
}
2赞

这是拿来干嘛的 :joy:

可序列化就是可以将信息变成字节码保存到prefab中,然后运行的时候,又从字节码反序列化成保存时候的对象

序列化我知道 不过装饰器也是可以实现二维数组的 论坛搜一下就有

二维数组和Map也没差了 而且更灵活

二维数组和Map有本质差别

用二维数组new一个map不就好了