laya2.0的場景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)
  • 新增加了一個Scene->Sprite禀苦,Scene只有一個子類就是View振乏,Dialog仍然繼承View调限。
  • Component也出現(xiàn)了變化:Component implements ISingletonElement, IDestroy。現(xiàn)在的Componet變成了一個腳本的基類淘钟,它的子類有相對布局插件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)建時也可以選成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)建后袱结,還未被銷毀的場景列表,方便查看還未被
銷毀的場景列表颖榜,方便內(nèi)存管理,本屬性只讀欣硼,請不要直接修改*/
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/**場景被關(guān)閉后,是否自動銷毀(銷毀節(jié)點和使用到的資源)缓熟,默認(rèn)為false*/
在close方法中,可以看到if (autoDestroyAtClosed) this.destroy();

2.在open方法中,看到root.addChild(scene);渴析,而在get root中看到_root = Laya.stage.addChild(new Sprite()) as Sprite俭茧,這說明所有打開的Scene都被添加到一個叫root的Sprite容器里,這個容器是直接放在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
這個映射會把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會把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);
}

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

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

四、先閱讀原文--->官方 場景使用 實例

在2.0項目開發(fā)中借尿,無論是創(chuàng)建場景Scene,頁面View蝶桶,對話框Dialog,3d場景scene3d,文件類型和后綴都是scene旁振。LayaAir2.0開發(fā)思路為組件化梢薪,腳本化甜攀,場景管理開發(fā),項目采用scene管理方式,來管理場景滋饲,LayaAir 已經(jīng)對scene做了一系列方案箍鼓,使得開發(fā)者無需考慮場景,關(guān)卡郭卫,頁面的資源贰军,內(nèi)存管理帘腹,只需要單純的調(diào)用接口,管理場景秽晚,其他的交給引擎去做巢掺,只需專注游戲邏輯開發(fā)即可。

1.先新建一個腳本

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

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

2.在Start.scene中放一個按鈕,就可以添加組件了坝初,這樣運行時绢要,點擊按鈕,就會觸發(fā)onClick方法打開box2d.scene場景搅幅。


image.png

3.設(shè)置啟動場景為Start.scene,現(xiàn)在運行后就能看到場景切換效果了蚁廓。


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.對比看看只有一張圖片的box2d.json,可以看到掛一個組件平绩,多出的是"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中會使用createComp遞歸創(chuàng)建節(jié)點

/**
 * 根據(jù)UI數(shù)據(jù)實例化組件性湿。
 * @param uiView UI數(shù)據(jù)宵荒。
 * @param comp 組件本體,如果為空,會新創(chuàng)建一個膜眠。
 * @param view 組件所在的視圖實例炸宵,用來注冊var全局變量涯曲,如果值為空則不注冊绰沥。
 * @return 一個 Component 對象麸塞。
 */
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é)點
    root = createComp(uiView, root, root, null, tInitTool);
    ...

對于"child":[{"type":"Script","props":{"runtime":"script/Start.ts"},"compId":5}],這種偎捎,會在遞歸中繼續(xù)創(chuàng)建
tChild = createComp(node, null, view, dataMap, initTool);這時候就會解析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對象,而tChild就是Script對象

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拿到上面注冊的類:

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類并不是一個顯示對象扒接,無法添加到顯示列表宗侦,它是個組件男旗,掛載到Node里了观堂,Node類是可放在顯示列表中的所有對象的基類溃睹。該顯示列表管理 Laya 運行時中顯示的所有對象竞滓。使用 Node 類排列顯示列表中的顯示對象茶没。Node 對象可以有子顯示對象。

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

五、Component類和Node類

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

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

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

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

/**
 * 獲取在場景中是否激活傻谁。
 *   @return    在場景中是否激活杭措。
 */
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í)行募舟,此時所有節(jié)點和組件均已創(chuàng)建完畢钉嘹,次方法只執(zhí)行一次
 * 此方法為虛方法,使用時重寫覆蓋即可
 */
public function onAwake():void {
    //this.name  && trace("onAwake node ", this.name);
}

/**
 * 組件被啟用后執(zhí)行震贵,比如節(jié)點被添加到舞臺后
 * 此方法為虛方法隐砸,使用時重寫覆蓋即可
 */
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è)計界面上使用這些屬性:

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

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

  • 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)容合作請聯(lián)系作者
  • 序言:七十年代末颈娜,一起剝皮案震驚了整個濱河市官辽,隨后出現(xiàn)的幾起案子俗批,更是在濱河造成了極大的恐慌干像,老刑警劉巖辅愿,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件颠区,死亡現(xiàn)場離奇詭異,居然都是意外死亡通铲,警方通過查閱死者的電腦和手機(jī)毕莱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颅夺,“玉大人朋截,你說我怎么就攤上這事“苫疲” “怎么了部服?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拗慨。 經(jīng)常有香客問我廓八,道長,這世上最難降的妖魔是什么胆描? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮仗阅,結(jié)果婚禮上昌讲,老公的妹妹穿的比我還像新娘。我一直安慰自己减噪,他們只是感情好短绸,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著筹裕,像睡著了一般醋闭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上朝卒,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天证逻,我揣著相機(jī)與錄音,去河邊找鬼抗斤。 笑死囚企,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瑞眼。 我是一名探鬼主播龙宏,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼伤疙!你這毒婦竟也來了银酗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎黍特,沒想到半個月后蛙讥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡衅澈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年键菱,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片今布。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡经备,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出部默,到底是詐尸還是另有隱情侵蒙,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布傅蹂,位于F島的核電站纷闺,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏份蝴。R本人自食惡果不足惜犁功,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望婚夫。 院中可真熱鬧浸卦,春花似錦、人聲如沸案糙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽时捌。三九已至怒医,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奢讨,已是汗流浹背稚叹。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留拿诸,地道東北人入录。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像佳镜,于是被迫代替她去往敵國和親僚稿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

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