使用libgdx框架的一些心得
2013-11-23
事件机制
个人觉得,group和actor的设计,最重要的地方其实不是树形的管理结构。基于它之上的事件以及action,这才是真正强大的地方。
每个actor里面,其实是有两个数组的:
Array
暂且先关注其中的listeners。Eventlistener是一个interface,有个handle函数:
public boolean handle (Event event);
像ClickListener什么的,都是实现了这个interface的。
事件产生以后会从stage的root group中,向下传。比如在stage的touchDown代码中,就会生成一个touchDown事件,然后调用root group的fire。
event从group一级一级的往下面传递,如果group中的actor中注册了listener,就会调用listener的handle(event)函数。根据函数返回值,决定是否继续传递下去。
handle的返回值表示这个listener是否处理了这个event。如果返回true,那么说明这个actor处理掉了事件,那么事件就不会继续往下传了。
libgdx把这套机制弄好了,实在太好用了。像按了back键或menu键,或者点击了屏幕,或者移动,都是事件。你要处理这些东西,只要给actor加上一个捕获事件的listener。比如click是addListener(ClickListener)。
group与stage的区别
stage中有一个root group,本质上它就是一个gruop。之所以提到group与stage区别,就是我之前很纠结用group做弹窗,还是用stage做弹窗。
stage是一个比较重的group,它上面有SpriteBatch;
stage是实现了InputProcessor的。
stage由于有spritebatch绑定的camera的project matrix,做屏幕适配会方便些。
封装Dialog
libgdx中倒是有一个Dialog了,继承自Windows,用的Skin的那一套东西,觉得很不好用。我自己用group封装了一个Dialog。有个show函数:
public Dialog show(Stage stage) { previousKeyboardFocus = stage.getKeyboardFocus(); previousScrollFocus = stage.getScrollFocus();
stage.addActor(this); stage.setKeyboardFocus(this); stage.setScrollFocus(this); return this; }
在用到的时候才将Dialog加入到stage中,不用后remove掉。
并且我在Dialog中重写了hit函数,如果点到这个Dialog(实际上就是一个Group)中的Actor,则调用Actor的hit函数,否则直接返回this,表示Dialog处理掉了这个事件,这样Dialog作为弹窗时它下面的东西就不能点击。
UI的代码生成
其实封装Dialog是为了配合我的代码生成。我直接利用gleed铺的图生成Dialog和Stage的代码。坐标位置放对什么的就不说了,自己觉得做得比较屌的是配合自定义事件。
我自定义了很多事件,比如ButtonClick是点击按钮的事件。比ButtonClick更高层的事件比如PlayClick,CloseClick这种,分别表示点了play按钮或close按钮。
在Dialog中,对象其实不是处理掉click之类的事件,而是将click封装成我自定义的ButtonClick事件,传给Dialog处理。
Dialog中,可能有些自己处理不了的,则生成更高层的事件,继续向stage传递。在stage中会处理掉像PlayClick或CloseClick之类的事件。
生成代码的框架大概是这个样子:
import com.doodleapp.petrescue.ImageButton; import com.doodleapp.petrescue.Resource; import com.doodleapp.petrescue.actors.LotteryActor; import com.doodleapp.petrescue.actors.Mask;
// generated by tools public class LotteryDialog extends Dialog { // 变量声明...begin ButtonListener btnListener; Image button; Label tite; Label time; LotteryActor booster1;
// 变量声明...end
//构造函数 public LotteryDialog() { // 初始化部分...begin bg = new Image(Resource.Always.findRegion("bgyaojiangbackground")); Texture0003 = new Image(Resource.Always.findRegion("bgyaojiangtitlebackground")); button = new Image(Resource.Always.findRegion("btnpubmoney_big")); tite = new Label("Daily Spin", GameGlobal.normalStyle); tite.setAlignment(Align.center); tite.setWidth(328); tite.setHeight(71); tite.setWrap(true);
booster1 = new LotteryActor();
addActors(); placeActors(); btnListener = new ButtonListener() { // 事件处理函数 @Override public boolean handle(ButtonEvent event) { Actor target = event.getTarget(); // 在这里写自己的事件处理 return false; } }; addListener(btnListener); // 初始化部分...end }
public void addActors() { // 添加函数...begin addActor(bg); addActor(booster8); // 添加函数...end }
// place函数 public void placeActors() { // place函数...begin bg.setPosition(0, 0); tite.setPosition(221, 1040); booster1.setPosition(74, 258); // place函数...end } }