使用libgdx框架的一些心得
2013-11-23
事件机制
个人觉得,group和actor的设计,最重要的地方其实不是树形的管理结构。基于它之上的事件以及action,这才是真正强大的地方。
每个actor里面,其实是有两个数组的:
Array<EventListener> listeners;
Array<Action> actions;
暂且先关注其中的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("bg_yaojiang_background"));
Texture_0003 = new Image(Resource.Always.findRegion("bg_yaojiang_title_background"));
button = new Image(Resource.Always.findRegion("btn_pub_money_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
}
}