一蓄喇、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文件
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場景搅幅。
3.設(shè)置啟動場景為Start.scene,現(xiàn)在運行后就能看到場景切換效果了蚁廓。
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對象
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ù)說明
可以在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;
一個完整的標(biāo)簽主要由下面幾個部分:
- type IDE屬性類型鲤竹,此類型是指IDE屬性類型屁奏,非真正的屬性類型诚镰,不過大多情況下是一樣的
- name IDE內(nèi)顯示的屬性名稱
- tips IDE內(nèi)鼠標(biāo)經(jīng)過屬性名稱上后检号,顯示的鼠標(biāo)提示脸狸,如果沒有則使用name(可選)
- default 輸入框顯示的默認(rèn)值(可選)