laya2.0的場(chǎng)景Scene和腳本Script

一、laya1.0 UI類結(jié)構(gòu)

1.Sprite常見子類

  • Sprite->Node->EventDispatcher
  • Text->Sprite
  • Stage->Sprite
  • AnimationPlayerBase->Sprite
  • DialogManager->Sprite
  • Component->Sprite
  • Component是ui控件類的基類,有很多子類

2.Box常見子類

  • Box->Component

  • View->Box

  • List->Box

  • Panel->Box

  • LayoutBox->Box

  • VBox,HBox->LayoutBox

  • Dialog->View

  • View子類,更常見的是在我們自己創(chuàng)建的UI導(dǎo)出的類。

二、laya2.0 UI類結(jié)構(gòu)
  • 新增加了一個(gè)Scene->Sprite,Scene只有一個(gè)子類就是View,Dialog仍然繼承View。
  • Component也出現(xiàn)了變化:Component implements ISingletonElement, IDestroy?,F(xiàn)在的Componet變成了一個(gè)腳本的基類,它的子類有相對(duì)布局插件Widget,Script,CommonScript。之前的Component現(xiàn)在被重命名為UIComponent。
  • AnimationPlayerBase被重命名成為AnimationBase

laya1.0創(chuàng)建的UI:xxUI->View->Box->Component->Sprite
laya2.0創(chuàng)建的UI: xxUI->View->Scene->Sprite(創(chuàng)建時(shí)也可以選成Scene類型,xxUI->Scene->Sprite)
需要說明一下,現(xiàn)在創(chuàng)建的UI文件后綴已經(jīng)不是.ui了,統(tǒng)一成.scene。

三、laya2.0的Scene類
public function open(closeOther:Boolean = true, param:* = null):void {
    if (closeOther) closeAll();
    root.addChild(scene);
    onOpened(param);
}
public function close():void {
    if (autoDestroyAtClosed) this.destroy();
    else removeSelf();
    onClosed();
}

override public function destroy(destroyChild:Boolean = true):void {
    _idMap = null;
    super.destroy(destroyChild);
    var list:Array = Scene.unDestroyedScenes;
    for (var i:int = 0, n:int = list.length; i < n; i++) {
        if (list[i] === this) {
            list.splice(i, 1);
            return;
        }
    }
}

/**創(chuàng)建后,還未被銷毀的場(chǎng)景列表,方便查看還未被
銷毀的場(chǎng)景列表,方便內(nèi)存管理,本屬性只讀,請(qǐng)不要直接修改*/
public static var unDestroyedScenes:Array = [];
public function Scene() {
    this._setBit(Const.NOT_READY, true);
    unDestroyedScenes.push(this);
    this._scene = this;
    createChildren();
}

public static function open(url:String, closeOther:Boolean = true,
 complete:Handler = null, param:* = null):void {
    load(url, Handler.create(null, _onSceneLoaded, [closeOther, complete, param]));
}

private static function _onSceneLoaded(closeOther:Boolean,
 complete:Handler, param:*, scene:Scene):void {
    scene.open(closeOther, param);
    if (complete) complete.runWith(scene);
}

public static function close(url:String, name:String = ""):Boolean {
    var flag:Boolean = false;
    var list:Array = Scene.unDestroyedScenes;
    for (var i:int = 0, n:int = list.length; i < n; i++) {
        var scene:Scene = list[i];
        if (scene.parent && scene.url === url && scene.name == name) {
            scene.close();
            flag = true;
        }
    }
    return flag;
}

public static function closeAll():void {
    var root:Sprite = Scene.root;
    for (var i:int = 0, n:int = root.numChildren; i < n; i++) {
        var scene:Scene = root.getChildAt(0) as Scene;
        if (scene is Scene) scene.close();
    }
}

public static function destroy(url:String, name:String = ""):Boolean {
    var flag:Boolean = false;
    var list:Array = Scene.unDestroyedScenes;
    for (var i:int = 0, n:int = list.length; i < n; i++) {
        var scene:Scene = list[i];
        if (scene.url === url && scene.name == name) {
            scene.destroy();
            flag = true;
        }
    }
    return flag;
}

public static function get root():Sprite {
    if (!_root) {
        _root = Laya.stage.addChild(new Sprite()) as Sprite;
        _root.name = "root";
        Laya.stage.on("resize", null, resize);
        function resize():void {
            _root.size(Laya.stage.width, Laya.stage.height);
            _root.event(Event.RESIZE);
        }
        resize();
    }
    return _root;
}

1.autoDestroyAtClosed/**場(chǎng)景被關(guān)閉后,是否自動(dòng)銷毀(銷毀節(jié)點(diǎn)和使用到的資源),默認(rèn)為false*/
在close方法中,可以看到if (autoDestroyAtClosed) this.destroy();

2.在open方法中,看到root.addChild(scene);,而在get root中看到_root = Laya.stage.addChild(new Sprite()) as Sprite,這說明所有打開的Scene都被添加到一個(gè)叫root的Sprite容器里,這個(gè)容器是直接放在stage上了。

3.參考一下官方例子中如何使用scene的:

static startScene:any="test/Test2View.scene";
Laya.Scene.open(GameConfig.startScene);

這里直接調(diào)用static open方法,在里面調(diào)用了load方法

public static function load(url:String, complete:Handler = null):void {
    Laya.loader.resetProgress();
    var loader:SceneLoader = new SceneLoader();
    loader.on(Event.COMPLETE, null, create);
    loader.load(url);
    
    function create():void {
        var obj:Object = Loader.getRes(url);
        if (!obj) throw "Can not find scene:" + url;
        if (!obj.props) throw "Scene data is error:" + url;
        var runtime:String = obj.props.runtime ? obj.props.runtime : obj.type;
        var clas:* = ClassUtils.getClass(runtime);
        if (obj.props.renderType == "instance") {
            var scene:Scene = clas.instance || (clas.instance = new clas());
        } else {
            scene = new clas();
        }
        if (scene && scene is Node) {
            scene.url = url;
            if (!scene._getBit(Const.NOT_READY)) complete.runWith(scene);
            else {
                scene.on("onViewCreated", null, function():void {
                    complete && complete.runWith(scene)
                })
                scene.createView(obj);
            }
        } else {
            throw "Can not find scene:" + runtime;
        }
    }
}

牽涉到SceneLoader類
public static const LoadableExtensions:Object = {"scene": Loader.JSON
這個(gè)映射會(huì)把test/Test2View.scene轉(zhuǎn)化為去加載相應(yīng)的json文件

image.png

4.Dialog
laya1.0創(chuàng)建的Dialog:xxDialogUI->Dialog->View->Box->Component->Sprite
laya2.0創(chuàng)建的Dialog: xxDialogUI->Dialog->View->Scene->Sprite

Dialog會(huì)把Scene中默認(rèn)的open方法覆蓋掉,轉(zhuǎn)交給DialogManager處理

//Dialog:
override public function open(closeOther:Boolean = true, param:* = null):void {
    _dealDragArea();
    _param=param;
    manager.open(this, closeOther, isShowEffect);
    manager.lock(false);
}

//DialogManager:
public class DialogManager extends Sprite

public function DialogManager() {
    this.mouseEnabled = maskLayer.mouseEnabled = true;
    this.zOrder = 1000;
    
    Laya.stage.addChild(this);
    Laya.stage.on(Event.RESIZE, this, _onResize);
    if (UIConfig.closeDialogOnSide) maskLayer.on("click", this, _closeOnSide);
    _onResize(null);
}

public function open(dialog:Dialog, closeOther:Boolean = false, showEffect:Boolean=false):void {
    if (closeOther) _closeAll();
    if (dialog.isPopupCenter) _centerDialog(dialog);
    addChild(dialog);
    if (dialog.isModal || this._getBit(Const.HAS_ZORDER)) Laya.timer.callLater(this, _checkMask);
    if (showEffect && dialog.popupEffect != null) dialog.popupEffect.runWith(dialog);
    else doOpen(dialog);
    event(Event.OPEN);
}

這個(gè)和1.0是一致的,DialogManager作為一個(gè)Sprite被添加到stage上,并且它的zOrder=1000。然后所有的Dialog都添加到DialogManager這個(gè)容器內(nèi)。

4.總結(jié):可以把Scene看作是官方提供的一個(gè)場(chǎng)景管理器,像closeOther,closeAll,onOpened,onClosed都是很實(shí)用的。當(dāng)然也可以自己去定制。

四、先閱讀原文--->官方 場(chǎng)景使用 實(shí)例

在2.0項(xiàng)目開發(fā)中,無論是創(chuàng)建場(chǎng)景Scene,頁面View,對(duì)話框Dialog,3d場(chǎng)景scene3d,文件類型和后綴都是scene。LayaAir2.0開發(fā)思路為組件化,腳本化,場(chǎng)景管理開發(fā),項(xiàng)目采用scene管理方式,來管理場(chǎng)景,LayaAir 已經(jīng)對(duì)scene做了一系列方案,使得開發(fā)者無需考慮場(chǎng)景,關(guān)卡,頁面的資源,內(nèi)存管理,只需要單純的調(diào)用接口,管理場(chǎng)景,其他的交給引擎去做,只需專注游戲邏輯開發(fā)即可。

1.先新建一個(gè)腳本

//Start.ts
export default class Start extends Laya.Script{

    onClick(e: laya.events.Event): void{
        Laya.Scene.open('box2d.scene');
    }
}

2.在Start.scene中放一個(gè)按鈕,就可以添加組件了,這樣運(yùn)行時(shí),點(diǎn)擊按鈕,就會(huì)觸發(fā)onClick方法打開box2d.scene場(chǎng)景。


image.png

3.設(shè)置啟動(dòng)場(chǎng)景為Start.scene,現(xiàn)在運(yùn)行后就能看到場(chǎng)景切換效果了。


image.png

4.在GameConfig的init方法中可以看到

static init(){
    var reg: Function = Laya.ClassUtils.regClass;
    reg("script/Start.ts",Start);
}
//ClassUtils:
public static function regClass(className:String, classDef:*):void {
    _classMap[className] = classDef;
}

5.在bin下面導(dǎo)出的Start.json里是這樣的:

{
"type":"Scene",
"props":{"width":1136,"height":640},
"compId":2,
"child":[
{
    "type":"Button",
    "props":{"y":232,"x":282,"skin":"comp/button.png","label":"label"},
    "compId":4,
    "child":[{"type":"Script","props":{"runtime":"script/Start.ts"},"compId":5}],
    "components":[]
}
],
"loadList":["comp/button.png"],
"loadList3D":[],
"components":[]
}

6.對(duì)比看看只有一張圖片的box2d.json,可以看到掛一個(gè)組件,多出的是"child":[{"type":"Script","props":{"runtime":"script/Start.ts"},"compId":5}]

{"type":"Scene",
"props":{"width":1136,"height":640},
"compId":2,
"child":[
{
"type":"Image",
"props":{"y":51,"x":100,"skin":"comp/image.png"},
"compId":3
}
],
"loadList":["comp/image.png"],
"loadList3D":[],
"components":[]
}

7.創(chuàng)建過程
(1)Scene.open->Scene.load->Scene.createView->SceneUtils.createByData
(2)在SceneUtils.createByData中會(huì)使用createComp遞歸創(chuàng)建節(jié)點(diǎn)

/**
 * 根據(jù)UI數(shù)據(jù)實(shí)例化組件。
 * @param uiView UI數(shù)據(jù)。
 * @param comp 組件本體,如果為空,會(huì)新創(chuàng)建一個(gè)。
 * @param view 組件所在的視圖實(shí)例,用來注冊(cè)var全局變量,如果值為空則不注冊(cè)。
 * @return 一個(gè) Component 對(duì)象。
 */
public static function createComp(uiView:Object, comp:* = null, 
view:* = null, dataMap:Array = null, initTool:InitTool = null):* {

參考注釋,觀察Scene中使用方式是SceneUtils.createByData(this, view);

public static function createByData(root:*, uiView:Object):* {
    var tInitTool:InitTool = InitTool.create();
    
    //遞歸創(chuàng)建節(jié)點(diǎn)
    root = createComp(uiView, root, root, null, tInitTool);
    ...

對(duì)于"child":[{"type":"Script","props":{"runtime":"script/Start.ts"},"compId":5}],這種,會(huì)在遞歸中繼續(xù)創(chuàng)建
tChild = createComp(node, null, view, dataMap, initTool);這時(shí)候就會(huì)解析type:Script這段了:

//處理腳本
if (node.type == "Script") {
    if (tChild is Component) {
        comp._addComponentInstance(tChild);
    } else {
        //兼容老版本
        if ("owner" in tChild) {
            tChild["owner"] = comp;
        } else if ("target" in tChild) {
            tChild["target"] = comp;
        }
    }
}

(3)comp._addComponentInstance(tChild);
這里comp就是Button對(duì)象,而tChild就是Script對(duì)象

image.png
public static function getCompInstance(json:Object):* {
    if (json.type == "UIView") {
        if (json.props && json.props.pageData) {
            return createByData(null, json.props.pageData);
        }
    }
    var runtime:String = (json.props && json.props.runtime) || json.type;
    var compClass:Class = ClassUtils.getClass(runtime);
    if (!compClass) throw "Can not find class " + runtime;
    if (json.type === "Script" && compClass.prototype._doAwake) {
        var comp:* = Pool.createByClass(compClass);
        comp._destroyed = false;
        return comp;
    }
    ...

這里通過ClassUtils.getClass拿到上面注冊(cè)的類:

static init(){
    var reg: Function = Laya.ClassUtils.regClass;
    reg("script/Start.ts",Start);
}

_addComponentInstance在Node當(dāng)中,添加了一些屬性

//Node
public function _addComponentInstance(comp:Component):void {
    _components ||= [];
    _components.push(comp);
    
    comp.owner = this;
    comp._onAdded();
    activeInHierarchy && comp._setActive(true);
    _scene && comp._setActiveInScene(true);
}

注意comp.owner = this;,Component類并不是一個(gè)顯示對(duì)象,無法添加到顯示列表,它是個(gè)組件,掛載到Node里了,Node類是可放在顯示列表中的所有對(duì)象的基類。該顯示列表管理 Laya 運(yùn)行時(shí)中顯示的所有對(duì)象。使用 Node 類排列顯示列表中的顯示對(duì)象。Node 對(duì)象可以有子顯示對(duì)象。

上面_onAdded、_setActive、_setActiveInScene,會(huì)在創(chuàng)建頁面時(shí)觸發(fā),更多細(xì)節(jié)見下一節(jié)。

五、Component類和Node類

在UI方面,Component類有三個(gè)子類:Widget,Script,CommonScript
1.comp._onAdded();

/**
 * 被添加到節(jié)點(diǎn)后調(diào)用,可根據(jù)需要重寫此方法
 * @private
 */
public function _onAdded():void {
    //override it.
}

2.這里介紹一下Node中的private var _components:Array;,簡(jiǎn)單來說這個(gè)數(shù)組維護(hù)了一個(gè)Node添加的所有組件,可以在_addComponentInstance中看到_components.push(comp);

3.Node.activeInHierarchy,參考 activeInHierarchy && comp._setActive(true);,只有Node激活了,掛載的組件才會(huì)激活。

/**
 * 獲取在場(chǎng)景中是否激活。
 *   @return    在場(chǎng)景中是否激活。
 */
public function get activeInHierarchy():Boolean {
    return _getBit(Const.ACTIVE_INHIERARCHY);
}

4.Node 是否激活

public function set active(value:Boolean):void {
    value = ! !value;
    if (!_getBit(Const.NOT_ACTIVE) !== value) {
        _setBit(Const.NOT_ACTIVE, !value);
        if (_parent) {
            if (_parent.activeInHierarchy) {
                if (value) _activeHierarchy();
                else _inActiveHierarchy();
            }
        }
    }
}

public function _activeHierarchy():void {
    _setBit(Const.ACTIVE_INHIERARCHY, true);
    if (_components) {
        for (var i:int = 0, n:int = _components.length; i < n; i++)
            _components[i]._setActive(true);
    }
    _onActive();
    for (i = 0, n = _children.length; i < n; i++) {
        var child:Node = _children[i];
        (!child._getBit(Const.NOT_ACTIVE)) && (child._activeHierarchy());
    }
    if (!_getBit(Const.AWAKED)) {
        _setBit(Const.AWAKED, true);
        onAwake();
    }
    onEnable();
}

public function _inActiveHierarchy():void {
    _onInActive();
    if (_components) {
        for (var i:int = 0, n:int = _components.length; i < n; i++)
            _components[i]._setActive(false);
    }
    _setBit(Const.ACTIVE_INHIERARCHY, false);
    for (i = 0, n = _children.length; i < n; i++) {
        var child:Node = _children[i];
        (!child._getBit(Const.NOT_ACTIVE)) && (child._inActiveHierarchy());
    }
    onDisable();
}

protected function _onActive():void {
    //override it.
}

/**
 * 組件被激活后執(zhí)行,此時(shí)所有節(jié)點(diǎn)和組件均已創(chuàng)建完畢,次方法只執(zhí)行一次
 * 此方法為虛方法,使用時(shí)重寫覆蓋即可
 */
public function onAwake():void {
    //this.name  && trace("onAwake node ", this.name);
}

/**
 * 組件被啟用后執(zhí)行,比如節(jié)點(diǎn)被添加到舞臺(tái)后
 * 此方法為虛方法,使用時(shí)重寫覆蓋即可
 */
public function onEnable():void {
    //this.name  && trace("onEnable node ", this.name);
}

5.Component.as

public function _setActive(value:Boolean):void {
    if (_active === value) return;
    if (!owner.activeInHierarchy) return;
    _active = value;
    if (value) {
        if (!_awaked) {
            _awaked = true;
            _onAwake();
        }
        _enabled && _onEnable();
    } else {
        _enabled && _onDisable();
    }
}
六、官方文檔 LayaAir腳本參數(shù)說明
image.png

可以在Script代碼中添加一些標(biāo)注,然后在UI設(shè)計(jì)界面上使用這些屬性:

export default class Click1 extends Laya.Script {
    /** @prop {name:createBoxInterval,tips:"xxx",type:int,default:1000}*/
    createBoxInterval: number = 1000;
    /** @prop {name:isMy,tips:"test",type:boolean,default:true}*/
    isMy: boolean = false;
    private pref: Laya.Prefab = null;
image.png

一個(gè)完整的標(biāo)簽主要由下面幾個(gè)部分:

  • type IDE屬性類型,此類型是指IDE屬性類型,非真正的屬性類型,不過大多情況下是一樣的
  • name IDE內(nèi)顯示的屬性名稱
  • tips IDE內(nèi)鼠標(biāo)經(jīng)過屬性名稱上后,顯示的鼠標(biāo)提示,如果沒有則使用name(可選)
  • default 輸入框顯示的默認(rèn)值(可選)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 我們都是蕓蕓眾生中微小的一粒沙子,把自己放在宇宙中來觀察,自己的存在簡(jiǎn)直微不足道,沒有人會(huì)注意到你的存在,生命在這...
    袁生斌閱讀 252評(píng)論 0 0
  • 回憶, 是倒后鏡里的公路。 坐在人生的駕駛室里, 我們要全神貫注地望著未來, 時(shí)不時(shí)瞥一下過去, 從過去, 可以看...
    溟夜星辰閱讀 269評(píng)論 0 1
  • 鵬程萬里 心寬天地闊 志遠(yuǎn)任鵬躍 抖擻精氣神 胸中有乾坤
    國(guó)勝閱讀 338評(píng)論 0 0
  • 以前都是在講時(shí)間管理,但是現(xiàn)在越來越多的人已經(jīng)在注重精力管理了。為什么呢?因?yàn)闀r(shí)間對(duì)于每個(gè)人來說都是二十四小時(shí),不...
    sunny視界閱讀 670評(píng)論 0 3
  • 背景 最近公司新購置了好幾臺(tái) Linux 服務(wù)器然后配置一些服務(wù)的時(shí)候很不習(xí)慣,估計(jì)是我平時(shí)自己的 zsh + o...
    小虛大魔王閱讀 317評(píng)論 0 0

友情鏈接更多精彩內(nèi)容