UNPKG

@senspark/ee

Version:

utility library for cocos creator

247 lines (224 loc) 7.49 kB
import assert = require('assert'); import { UnselectableComponent } from './UnselectableComponent'; const { ccclass, disallowMultiple, executeInEditMode, menu, property } = cc._decorator; /** Decorator for nested prefabs. */ export const nest = (type: { prototype: cc.Component } | Array<{ prototype: cc.Component }>) => { if (type instanceof Array) { return nestArray(type[0]); } else { return nestSingle(type); } }; export const nestSingle = (type: { prototype: cc.Component }) => { return (target: any, propertyKey: string) => { const internalProperty = `${propertyKey}_prefab`; // Prefixed with a dash. // Let cocos handle with the internal property. property({ type: NestedPrefab, displayName: propertyKey, visible: true, })(target, internalProperty, { /** Default value is null */ initializer: () => null, }); Object.defineProperty(target, propertyKey, { set(value: any): void { // No effect. }, get(this: any): any { const prefab = this[internalProperty]; if (prefab === undefined) { return null; } const view = prefab.getView(); if (view === null) { return null; } const component = view.getComponent(type); assert(component !== null); return component; }, configurable: false, enumerable: true, }); }; }; export const nestArray = (type: { prototype: cc.Component }) => { return (target: any, propertyKey: string) => { const internalProperty = `${propertyKey}_prefab`; property({ type: [NestedPrefab], displayName: propertyKey, visible: true, })(target, internalProperty, { /** Default value is an empty array. */ initializer: () => [], }); Object.defineProperty(target, propertyKey, { set(value: any): void { // No effect. }, get(this: any): any { const prefabs = this[internalProperty]; if (prefabs === undefined) { return null; } const views = (prefabs as NestedPrefab[]).map(item => item.getView()); const components = views.map(item => item === null ? null : item.getComponent(type)); return components; }, configurable: false, enumerable: true, }); }; }; @ccclass @disallowMultiple @executeInEditMode @menu('ee/NestedPrefab') export class NestedPrefab extends cc.Component { public static createNode(prefab: cc.Prefab): cc.Node | null { const node = new cc.Node(); const comp = node.addComponent(NestedPrefab); comp.prefab = prefab; node.name = prefab.name; const view = comp.getView(); if (view === null) { return null; } node.setContentSize(view.getContentSize()); node.setAnchorPoint(view.getAnchorPoint()); return node; } private instantiated: boolean = false; private view: cc.Node | null = null; @property(cc.Prefab) private _prefab: cc.Prefab | null = null; @property({ type: cc.Prefab }) public get prefab(): cc.Prefab | null { return this._prefab; } public set prefab(value: cc.Prefab | null) { if (this._prefab !== null) { if (this.view === null) { if (CC_EDITOR) { throw new Error('Prefab exist but the view is not present.'); } } else { this.instantiated = false; this.view.destroy(); this.view = null; } } this._prefab = value; if (this._prefab !== null) { if (this.instantiateView()) { this.instantiated = true; this.setupView(); } } } @property(cc.Boolean) private instantiate: boolean = true; @property(cc.Boolean) private synchronize: boolean = false; public onLoad(): void { if (!CC_EDITOR && !this.instantiated && this.instantiate) { if (this.instantiateView()) { this.instantiated = true; this.applySync(); } } } public update(): void { if (!CC_EDITOR) { if (this.instantiate) { assert(this.view !== null, 'View should be present at runtime.'); } return; } if (this.view !== null) { if (!this.view.isValid) { // Object is destroyed but remains referenced. this.view = null; } } if (this.node.childrenCount > 0 && this.node.children[0] !== this.view) { // This node was duplicated. this.node.removeAllChildren(); } if (this.prefab !== null && this.view === null) { if (this.instantiateView()) { this.instantiated = true; this.setupView(); } } this.applySync(); } /** Creates a view using the current prefab. */ public createView(): cc.Node { assert(this.prefab !== null); const view = cc.instantiate(this.prefab!); return view; } public getView(): cc.Node | null { if (!this.instantiate) { return null; } if (this.prefab === null) { return null; } if (!this.instantiated) { if (this.instantiateView()) { this.instantiated = true; this.applySync(); if (CC_EDITOR) { this.setupView(); } } } return this.view; } /** Creates a view from the current prefab and add it to this. */ private instantiateView(): boolean { if (this.view !== null) { assert.fail('View already instantiate.'); return false; } if (this.prefab === null) { assert.fail('Prefab is not exist.'); return false; } this.view = this.createView(); this.node.addChild(this.view); return true; } private setupView(): boolean { if (this.view === null) { assert.fail('View is not present'); return false; } this.freeze(this.view); this.view._objFlags |= cc.Object.Flags.DontSave; return true; } private freeze(node: cc.Node): void { if (node.getComponent(UnselectableComponent) === null) { node.addComponent(UnselectableComponent); } const prefab = node.getComponent(NestedPrefab); if (prefab !== null) { if (!prefab.instantiate) { node.active = false; } } node.children.forEach(child => this.freeze(child)); } public applySync(): void { const view = this.getView(); if (view !== null && this.synchronize) { this.node.setContentSize(view.getContentSize()); this.node.setAnchorPoint(view.getAnchorPoint()); } } }