libgdx游戏引擎教程(十三)演员类的用户交互功能详解(附源码)

本文深入剖析LibGDX框架中的Actor类交互机制,包括hit()等关键方法的作用及实现方式,通过示例代码展示了如何处理触摸事件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:http://www.apkbus.com/android-60457-1-1.html

 

本讲源码:
edu.nju.wsj.libgdx.rar(3.8 MB, 下载次数: 126)

2012-7-23 17:52 上传
点击文件名下载附件
下载积分: 下载豆 -2



今天是该系列教程的第十三讲,也是系列教程的第二十二篇。这两天有巴友经常问我一些问题,其中很多问到了我今天要讲的内容,我就在这里一起分析一下好了。

这一讲我们来具体分析一下一个我们早就接触到的东西,演员类Actor不过似乎我们都是一直只会简单的使用演员类,我们对Actor的主要工作都集中在draw()函数中,即我们大部分修改都是基于Actor怎么绘制,其实Actor的绘制功能只是其中的一部分,游戏最重要的部分是和用户的交互,即让用户在玩游戏的时候全身心投入,或者说增加用户的粘度。我们如果只在游戏的绘制方面下功夫,这个游戏就好比是动画片,用户只要按下一个按钮就开始看我们辛辛苦苦制作的动画片?以上也是说笑的,但无疑游戏开发中,提高游戏和用户之间的交互应该放在第一位。


Actor类中其实提供了很多对用户交互请求做出响应的函数,还是老样子,我们先来看看文档好了。我们主要关注以下几个方法:

1.PNG

2012-7-23 17:38 上传
下载附件 (2.32 KB)


3.PNG

2012-7-23 17:38 上传
下载附件 (27.45 KB)



其中最主要的是hit() 方法,我们来看看这个是怎么使用的。接下来的代码在十二讲的基础做修改,我们先新建一个Actor 类,ExampleActor
  1. public class ExampleActor extends Actor {

  2. @Override
  3. public void draw(SpriteBatch arg0, float arg1) {
  4. // TODO Auto-generated method stub

  5. }

  6. @Override
  7. public Actor hit(float arg0, float arg1) {
  8. // TODO Auto-generated method stub
  9. return null;
  10. }

  11. }
复制代码
我们修改一下构造函数,可以在ExampleActor 类初始化的时候就对其位置进行设定,并且在Actor draw() 中绘制一张图片,方便我们确认Actor 的位置。 顺便把另外几个我们提到的交互函数也实现一下,其实也就是打印一句话,便于我们追踪Actor 的响应,从而了解其中的机制。 另外为了我们马上加入多个Actor 而用于标识每一个Actor, 我们再设置一个字符串变量,在创建时传入。Actor 的资源是Testin logo 大小我已经做过处理变成了128*64


actor.png

2012-7-23 17:39 上传
下载附件 (7.69 KB)




修改后的代码如下:


 

  1. public class ExampleActor extends Actor {
  2. Texture tx;
  3. String Id;
  4. @Override
  5. public void draw(SpriteBatch arg0, float arg1) {
  6. // TODO Auto-generated method stub
  7. //根据这个Actor的位置来绘制
  8. arg0.draw(tx, this.x, this.y);
  9. }

  10. @Override
  11. public Actor hit(float arg0, float arg1) {
  12. // TODO Auto-generated method stub
  13. Log.i("Testin","hit"+Id);
  14. return null;
  15. }

  16. @Override
  17. public boolean touchDown(float x, float y, int pointer) {
  18. // TODO Auto-generated method stub
  19. Log.i("Testin","touchDown"+Id);
  20. return super.touchDown(x, y, pointer);
  21. }

  22. @Override
  23. public void touchDragged(float x, float y, int pointer) {
  24. // TODO Auto-generated method stub
  25. Log.i("Testin","touchDragged"+Id);
  26. super.touchDragged(x, y, pointer);
  27. }

  28. @Override
  29. public boolean touchMoved(float x, float y) {
  30. // TODO Auto-generated method stub
  31. Log.i("Testin","touchMoved"+Id);
  32. return super.touchMoved(x, y);
  33. }

  34. @Override
  35. public void touchUp(float x, float y, int pointer) {
  36. // TODO Auto-generated method stub
  37. Log.i("Testin","touchUp"+Id);
  38. super.touchUp(x, y, pointer);
  39. }

  40. public ExampleActor(int x,int y,String Id) {
  41. super();
  42. //对坐标进行初始化
  43. this.x=x;
  44. this.y=y;
  45. this.Id=Id;
  46. //对纹理对象进行初始化,资源文件放在assets文件下
  47. tx=new Texture(Gdx.files.internal("actor.png"));
  48. }

  49. }
复制代码
我们接下来在MyGame.java中加入一个Actor,并加入stage,让这个Actor能够正常显示。
  1. ExampleActor example;
复制代码
show()函数中初始化,并加入舞台:
  1. example=new ExampleActor(100, 100,"1");
  2. stage.addActor(example);
复制代码

看一下运行效果:
4.png
2012-7-23 17:38 上传
下载附件 (29.03 KB)



 

没错,Actor 确实显示出来了,那我们触摸一下,看看Logcat 中的输出:


5.jpg

2012-7-23 17:38 上传
下载附件 (98.57 KB)



确实有输出,但是这里有两点需要注意:


 

第一点:



 

我们在stage上触摸的任一点都会有hit+ID 的输出,即系统不经任何判断, 只要 stage捕捉到触摸事件,就会通知其内的所有Actor,调用所有子Actorhit() 方法,为了证明这一点,我们再在MyGame.java stage 中再加入一个ExampleActor 的实例,设置其ID 2 ,我们再来看看输出。
  1. <span lang="EN-US" style="font-size:10.0pt;font-family:
  2. &quot;Courier New&quot;;mso-fareast-font-family:宋体;mso-fareast-theme-font:minor-fareast;
  3. color:black;mso-font-kerning:0pt;mso-ansi-language:EN-US;mso-fareast-language:
  4. ZH-CN;mso-bidi-language:AR-SA">ExampleActor </span><span lang="EN-US" style="font-size:10.0pt;font-family:&quot;Courier New&quot;;mso-fareast-font-family:宋体;
  5. mso-fareast-theme-font:minor-fareast;color:#0000C0;mso-font-kerning:0pt;
  6. mso-ansi-language:EN-US;mso-fareast-language:ZH-CN;mso-bidi-language:AR-SA">example2</span><span lang="EN-US" style="font-size:10.0pt;font-family:&quot;Courier New&quot;;mso-fareast-font-family:
  7. 宋体;mso-fareast-theme-font:minor-fareast;color:black;mso-font-kerning:0pt;
  8. mso-ansi-language:EN-US;mso-fareast-language:ZH-CN;mso-bidi-language:AR-SA">;</span>
复制代码


 

同样的:
  1. Example2=new ExampleActor(200, 200,"2");
  2. stage.addActor(example2);
复制代码
看看效果:


5.png

2012-7-23 17:38 上传
下载附件 (32.95 KB)



随意触摸屏幕上的任何一个位置,看看Logcat 的输出:


6.jpg

2012-7-23 17:38 上传
下载附件 (111.08 KB)



我们看到, 这两个ExampleActor hit() 方法都确实触发了 ,而且我们可以看到,是stage 中的Actor 队列的 末尾的Actor 最先响应 ,然后逐步是队列前部的Actor( 我们先加入example 再加入的example)


 

第二点:


 

很多同学也许看到这里已经有疑惑了,首先,既然触摸事件会由 stage 通知在其内的每一个 Actor 并调用这些 Actor 各自的 hit() 方法,那我们怎么判断触摸的是哪一个 Actor 呢?其次,细心的同学已经注意到了, 为什么只有 hit() 方法被触发了但另外四个方法 都没有输出?



 

其实这里的重点在于 hit() 中的处理,我们再做一个处理,在 hit() 方法中打印传入的两个参数,按照文档的说法:




 

传入的应该是一个x,一个y ,应该指的是一个坐标,可是具体的含义我们还是不清楚,因此在 ExampleActor hit() 方法中添加以下语句:
  1. Log.i("Testin","x:"+arg0+" y:"+arg1);
复制代码
我们再运行一下,并且随意触摸屏幕的一个位置,输出如下:


7.jpg

2012-7-23 17:38 上传
下载附件 (145.61 KB)




我们可以看到,还是同样的有输出。但是我们可以发现一个很奇怪的现象, 如果传入的参数x,y 是指触摸点在屏幕上的位置,为什么两个Actor 的输出x,y 结果不一样呢? 细心的同学可以注意到,这两个x 和两个y 之间都相差100 ,正是我们初始化时这两个ExampleActor 坐标(100,100) (200 200) 之间的差。



 

事实正是这样,libgdx 文档中中并没有对这两个参数做任何的介绍,一开始大家很容易就任何传入的是触摸点的坐标,我们经过实践以后发现并不是这样。 事实上,传入的这个参数是触摸点相对于Actor 的坐标。除了hit() 方法,touchDragged(),touchMoved(),touchDown(),touchUp() 这四个方法中传入的x,y 参数也是触摸点相对于Actor x,y 值,而不是触摸点的绝对坐标。



 

现在我们又面临一个问题,怎么触发touchDragged(),touchMoved(),touchDown(),touchUp() 这四个方法呢?



 

我直接揭晓答案好了。事实上,这四个方法的触发 需要 hit() 方法的配合,我们可以看到默认的hit() 方法返回的是null ,而当经过相应的判定之后,我们在 hit()方法中返回这个Actor 实例,即:
  1. return this;
复制代码
的时候, 这四个事件就会被触发 。那我们对hit() 方法进行相应的修改:
  1. @Override
  2. public Actor hit(float arg0, float arg1) {
  3. // TODO Auto-generated method stub
  4. Log.i("Testin","hit"+Id);
  5. Log.i("Testin","x:"+arg0+" y:"+arg1);
  6. if(arg0>0&&arg1>0&&arg0<tx.getWidth()&&arg1<tx.getHeight())
  7. return this;

  8. return null;
  9. }
复制代码
这里我们将这个Actor 看作是一个和图片资源actor.png 一样大的矩形 。运行一下,看看效果,我触摸了其中一个ExampleActor ,输出如下:
8.jpg
2012-7-23 17:38 上传
下载附件 (125.9 KB)



 

实际上我是触摸了第二个ExampleActor ,即example2 ,因此我们可以看见example2 touchDown 的输出,而看不见第一个的,这就实现了一个简单的触摸检测。



 

那我们怎么能让 ActortouchUp()touchDragged()也接收事件呢? 我也直接揭晓答案好了: 我们看到现在能够触发 touchDown() 了, touchDown()返回true,则该Actor成为group(即这里的Stage)的焦点 ,这意味着该 Actor 可以接收到 touchDragged() touchUp() 事件并做出相应的响应。我们修改 touchDown() 函数:
  1. @Override
  2. public boolean touchDown(float x, float y, int pointer) {
  3. // TODO Auto-generated method stub
  4. Log.i("Testin","touchDown"+Id);
  5. // return super.touchDown(x, y, pointer);
  6. return true;
  7. }
复制代码
再运行一下,并触摸第二个ExampleActor ,观察输出:


9.jpg

2012-7-23 17:38 上传
下载附件 (159.01 KB)




我们可以看到,touchUp() touchDragged() 也做出了响应。但是我们为什么没有看到”hit1” 这个输出? 原因在于,一旦系统找到一个匹配的Actor ,就不会继续搜寻。 这里我们触摸的是第二个ExampleActor 且前面我们说过,系统是从Actor 队列的尾部开始搜寻,因此找到example2 后就不再继续搜寻



 

相反如果我们触摸的是第一个ExampleActor 的话,我们可以看到“hit2 ”这个输出,因为系统发现第二个ExampleActor 不匹配, 它会自动向前继续搜索


10.jpg

2012-7-23 17:38 上传
下载附件 (169.82 KB)



这些都验证了我们之前的猜测。

抱歉,这里忘了和大家说了,touchMoved是libgdx在PC上运行的时候才响应的,因为Libgdx是一个跨平台的引擎,在Android里是不起作用的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值