使用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
    }
}
libgdx