11RIA 闪客社区 - 最赞 Animate Flash 论坛

搜索
查看: 2937|回复: 2
上一主题 下一主题

[★ AS3 引擎] 羔羊引擎之键控对象(想了解格斗游戏业务快进来)

[复制链接] TA的其它主题
发表于 2017-11-16 01:34:53 | 显示全部楼层 |阅读模式

【游客模式】——注册会员,加入11RIA 闪客社区吧!一起见证Flash的再次辉煌……

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
本帖最后由 qq229449388 于 2017-11-28 23:12 编辑

这节教程讲的内容使用的羔羊引擎api是刚更新的1.15版本,请大家下载最新版本
讲到键控,什么是键控,我最不喜欢装逼了,只是不想标题太长,就是键盘控制一个对象。
这一节要说说键盘跟游戏交互的那些事,大家都知道as3可以侦听键盘事件。大家请看下面的demo,用键盘的WSAD控制上下左右方向,用T进行攻击


有关系的几个api分别是,KeyBoardKeyBoardEvent。老司机都知道,侦听键盘,一定要用舞台来统一侦听,为了避免侦听错乱,抢夺键盘,失去焦点的情况。
而在羔羊引擎里面,提供了封装过的键盘管理类GYKeyBoard,是一个单例,如果要使用键盘控制,请给对象实现IKeyBoardObject接口,我看看这个接口的3个方法
  1. function keyFocus():Boolean;
  2.                 function kDown(keyCode:uint):void;
  3.                 function kUp(keyCode:uint):void;
复制代码


keyFocus,需要返回一个Boolean类型,表示此对象侦听的时候,是不是独占键盘
kDown,看名字就知道,当键按下的时候,就会自动调用此方法,参数keyCode则是KeyBoard里面的常量
kUp,就是抬起的时候自动调用的

那么是不是实现接口就相当于侦听了,当然不是,实现了此接口只表示这个对象具有侦听键盘的功能,它仍然需要进行侦听,才会有键盘操控的功能,使用GYKeyBoard.getInstance()得到这个类的单例,用addKeyListener,移除用removeKeyListener,如下
  1. GYKeyboard.getInstance().addKeyListener(实现了IKeyBoardObject的对象);
复制代码
GYKeyBoard里面提供了ctrl,alt,shift等当前是否按下,当然使用isKeyDown(键码),可以得这个键当前是否按下,返回的是按下的时间戳,如果当前没按下,则返回-1。最后一个侦听的对象,优先判断进行键盘判断,如果此对象独占键盘,则后面的对象不能响应键盘的操作。

说到这里,羔羊引擎的键盘类是不是很简单,但是这一节也太简单了,所以再说说用键盘控制的游戏人物的业务

常用键盘的游戏,就要数格斗游戏了,我们说说横版格斗游戏,键盘操控人物以及人物动作切换到一些游戏业务。如果你还没看过第六节的序列图教程,就请先去学习一下第六节的序列图教程,因为下面我将用序列图做格斗游戏的人物动作切换。

在制作前期,先准备好资源,我们现在要制作一个游戏人物,一般是要先定制这个人物有几个动作,我们先简单地定制3个动作,0 站立 1 奔跑 2 攻击。在一些大型的游戏里面还会有各种设计的动作,例如飞行,前冲,跳跃,技能1,技能2等。
美术资源我们为3个动作准备了以下序列图
站立
_res_3.png
奔跑
_res_2.png
攻击
_res_1.png
除此之外,我们需要为这些动作,定制对应的配置,什么是配置?配置就是对于这个动作的一些规格描述,格式由具体动作的设计来定,例如我简单的定制这个动作的配置有offsetX,offsetY,isReserveEnd。
offsetX:这动作图片相对于原点X的偏移量
offsetY:这动作图片相对于原点Y的偏移量

isReserveEnd:是否播放到尽头之后,反向播放,这个属性在第六节讲解GYSeqImage类时说过,就是对应的功能,这样我们就有以下Object类型的配置,当然这个动作的序列图也可以作为属性,定义为bmps,这就是一个动作的配置,如下。
动作:{isReserveEnd:true,offsetX:-72,offsetY:-148,bmps:null}
因为刚刚上面说过有3个动作,所以这时,配置得是一个数组了
[动作1,动作2,动作3]
动作1对应就是Object配置了,如果还有其他设计需求,我们得给这个人物加配置属性,所以我们得先让这个数组定义为一个属性stsArr,如果按照自己的需求,就可以再增加关于人物的其他属性,如下
{stsArr:[动作1,动作2,动作3],其他属性:xxx}
总而言之,配置格式按自己的需求,只要能取得的对应动作的配置就可以了
下面我们需要创建一个人物类,因为要使用GYSeqImage播放序列图,我们直接继承GYSeqImage,重写其中一两个方法

定义Role,首先我们需要想好需要的属性,经过思考,我需要以下属性
  1. private var _cfg:Object;//角色配置
  2.                 private var _sts:int;//当前状态
  3.                 private var _stsLock:Boolean;//当前状态是否锁定
  4.                 private var _nowStsObj:Object;//当前状态的配置
  5.                 private var _direct:int;//当前行走方向
  6.                 private var _matrixDirect:int;//当前左右方向,因为横版反向图片要反转
  7.                 private var _matrixChange:Boolean;//是否转向
  8.                 private var _moveSpdX:int,_moveSpdY:int;//位移分速度
  9.                 private var _moveSpd:int;//位移速度
  10.                 private var _operSts:int;//操作的状态
  11.                 private var _offsetX:int;//偏移x
  12.                 private var _offsetY:int;//偏移y
  13.                 private var _frameW:int;//帧宽
  14.                 private var _frameH:int;//帧高

  15. 另外切换状态的时候,我们需要设置配置
  16. [Actionscript3] 纯文本查看 复制代码
  17. public function setSts(sts:int):void
  18.                 {
  19.                         if(_sts == sts)return;
  20.                         _sts = sts;
  21.                         _nowStsObj = _cfg.stsArr[sts];
  22.                         reserveEnd = _nowStsObj.isReserveEnd;
  23.                         source = _nowStsObj.bmps;
  24.                         _offsetX = _nowStsObj.offsetX;
  25.                         _offsetY = _nowStsObj.offsetY;
  26.                         _frameW = _nowStsObj.bmps[0].realWidth;
  27.                         _frameH = _nowStsObj.bmps[0].realHeight;
  28.                         var w:int=70;
  29.                         var h:int=w*0.3;
  30.                         graphics.clear();
  31.                         graphics.beginFill(0,0.5);
  32.                         graphics.drawEllipse(-w>>1,-h>>1,w,h);
  33.                         graphics.endFill();
  34.                         resetMatrix();
  35.                         archorY = -_offsetY;
  36.                         if(_sts != 1)
  37.                                 _direct = -1;
  38.                         
  39.                         stsLock(_sts == ATTACK);
  40.                 }
复制代码
以上代码,具体就不一句一句说了,大概说下功能
1、配置保存到变量
2、绘制影子,一般使用位图效率比较好,我偷懒一下,这里得说明一下游戏业务,格斗人物的影子,这不只有装饰的作用,这有定位的作用,影子一般在脚下,脚下一般是精灵的原点,所以影子是最好的对坐标的标志,而做动作编辑器,就很需要这个标志了。
3、stsLock方法,状态锁定,意思为是否状态锁定不能切换,在格斗游戏里面,有很多时候,当前状态不能马上被切换,所以定制了一个stsLock方法,来统一处理
  1. private function stsLock(val:Boolean):void
  2.                 {
  3.                         if(_stsLock == val)return;
  4.                         _stsLock = val;
  5.                         kUp(999);
  6.                 }
复制代码


kUp,键抬起方法,待会讲解键盘控制人物的逻辑时再说,先跳过

Role类的其他几个方法,都有注释,实际上主角的逻辑,跟需求,以及自己思考的流程很有关系,不是千篇一律,但是有一个问题必须要注意,就是不能实时更改显示状态,必须放置下一帧,因为当一个人物被攻击的时候,这一帧的遍历检测还没结束,有可能被多个人攻击,如果被攻击就里面切换状态,则会影响其他人的攻击。所以,在当前帧只能计算状态、伤害等,真正切换状态放置在下一帧

下面讲解一下键盘控制人物,基本逻辑都一样,代码如下
  1. public function keyFocus():Boolean
  2.                 {
  3.                         return true;
  4.                 }
  5.                 public function kDown(keyCode:uint):void
  6.                 {
  7.                         if(_stsLock)return;
  8.                         if(_keyBoard.isKeyDown(Keyboard.A))
  9.                         {
  10.                                 _operSts = ATTACK;
  11.                                 return;
  12.                         }
  13.                         if(_keyBoard.isKeyDown(Keyboard.UP))
  14.                         {
  15.                                 if(_keyBoard.isKeyDown(Keyboard.RIGHT))
  16.                                         setDirect(2);
  17.                                 else if(_keyBoard.isKeyDown(Keyboard.LEFT))
  18.                                         setDirect(8);
  19.                                 else
  20.                                         setDirect(1);
  21.                         }
  22.                         else if(_keyBoard.isKeyDown(Keyboard.RIGHT))
  23.                         {
  24.                                 if(_keyBoard.isKeyDown(Keyboard.DOWN))
  25.                                         setDirect(4);
  26.                                 else
  27.                                         setDirect(3);
  28.                         }
  29.                         else if(_keyBoard.isKeyDown(Keyboard.DOWN))
  30.                         {
  31.                                 if(_keyBoard.isKeyDown(Keyboard.LEFT))
  32.                                         setDirect(6);
  33.                                 else
  34.                                         setDirect(5);
  35.                         }
  36.                         else if(_keyBoard.isKeyDown(Keyboard.LEFT))
  37.                         {
  38.                                 setDirect(7);
  39.                         }
  40.                 }
  41.                 public function kUp(keyCode:uint):void
  42.                 {
  43.                         if(_stsLock)
  44.                                 return;
  45.                         var k:GYKeyboard = GYKeyboard.getInstance();
  46.                         if(k.isKeyDown(Keyboard.A) || k.isKeyDown(Keyboard.UP) || k.isKeyDown(Keyboard.DOWN) || k.isKeyDown(Keyboard.LEFT) || k.isKeyDown(Keyboard.RIGHT))
  47.                         {
  48.                                 kDown(999);
  49.                                 return;
  50.                         }
  51.                         _operSts = STAND;
  52.                 }
复制代码


刚刚已经讲解过IGYKeyBoardObject接口了
以上3个都是接口方法
人物的键盘肯定是独占的,所以keyFocus返回true
kDown则是判断当前的按键情况,我设计的方向是8个,从1开始顺时针,上,右上,右,右下,下,左下,左,左上
setDirect方法则是切换方向,因为斜线速度和横向速度纵向速度不一样。还有图片翻转也会在此根据方向判断
  1. private function setDirect(dir:int):void
  2.                 {
  3.                         if(_stsLock)return;
  4.                         if(_direct == dir)return;
  5.                         _direct = dir;
  6.                         _operSts = RUN;
  7.                         var m:int;
  8.                         if(dir==0)
  9.                         {
  10.                                 _operSts=1;
  11.                                 _moveSpdY=0;
  12.                                 _moveSpdX=0;
  13.                                 return;
  14.                         }
  15.                         if(dir==1)
  16.                         {
  17.                                 _moveSpdY=-_moveSpd;
  18.                                 _moveSpdX=0;
  19.                         }
  20.                         else if(dir==2)
  21.                         {
  22.                                 _moveSpdX=Math.SQRT1_2*_moveSpd;
  23.                                 _moveSpdY=-_moveSpdX;
  24.                                 m = 1;
  25.                         }
  26.                         else if(dir==3)
  27.                         {
  28.                                 _moveSpdX=_moveSpd;
  29.                                 _moveSpdY=0;
  30.                                 m = 1;
  31.                         }
  32.                         else if(dir==4)
  33.                         {
  34.                                 _moveSpdY=_moveSpdX=Math.SQRT1_2*_moveSpd;
  35.                                 m = 1;
  36.                         }
  37.                         else if(dir==5)
  38.                         {
  39.                                 _moveSpdY=_moveSpd;
  40.                                 _moveSpdX=0;
  41.                                 m = -1;
  42.                         }
  43.                         else if(dir==6)
  44.                         {
  45.                                 _moveSpdY=Math.SQRT1_2*_moveSpd;
  46.                                 _moveSpdX=-_moveSpdY;
  47.                                 m = -1;
  48.                         }
  49.                         else if(dir==7)
  50.                         {
  51.                                 _moveSpdX=-_moveSpd;
  52.                                 _moveSpdY=0;               
  53.                                 m = -1;
  54.                         }
  55.                         else
  56.                         {
  57.                                 _moveSpdX=_moveSpdY=-Math.SQRT1_2*_moveSpd;
  58.                         }
  59.                         if(m == 0 || _matrixDirect == m)return;
  60.                         _matrixChange = true;
  61.                         _matrixDirect = m;
  62.                 }
复制代码


keyUp方法则是按键抬起,当按键抬起的时候,我们要重新判断当前按键状态,来确定要切换的人物状态,所以keyUp内又再一次判断按键情况,如果有键按下,则再次调用keyDown

下面看看人物逻辑核心
  1. override protected function loop(t:Number):void
  2.                 {
  3.                         super.loop(t);
  4.                         stsCheck();
  5.                         if(_sts == 1)//执行逻辑,当前只有奔跑有逻辑
  6.                                 running();
  7.                 }
复制代码
这是每帧执行的逻辑,super.loop(t)则是序列图播放的父类函数,下面增加了两个逻辑
stsCheck则是每帧去判断状态情况
running就是逻辑方法,因为目前只有奔跑有特殊的逻辑,其他状态都是播放序列帧,如果丰富游戏内容则需要根据sts状态来调用不同的逻辑,例如
if(_sts == 1)
……
else if(_sts == 2)
……
else if(_sts == 3)
……
也可以使用Dictionary进行键值对应,直接一个状态对应一个方法
stsDict[_sts]();

在此,格斗游戏的人物操控已经讲解完毕,总结一下制作思路
1、设计配置
2、准备资源
3、设计人物逻辑代码,主要有3点
(1)状态切换,实现具体状态的判断,例如具体的判断,例如跳跃、死亡、受击不能切换到其他状态,诸如此类的状态判断
(2)键盘侦听,实现按键的逻辑
(3)loop函数内,设计核心逻辑,完成各个状态的逻辑

下次有机会将会给大家说一下,场景逻辑业务
游客,如果您要查看本帖隐藏内容请回复






发表于 2018-8-22 18:35:23 | 显示全部楼层
emm链接到的地址都是失效的天地会
回复

使用道具 举报

发表于 2018-8-26 17:56:48 | 显示全部楼层
,,,,,,,,,,,,,,,,
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐 上一条 /1 下一条

感谢所有支持论坛的朋友:下面展示最新的5位赞助和充值的朋友……更多赞助和充值朋友的信息,请查看:永远的感谢名单

SGlW(66139)、 anghuo(841)、 whdsyes(255)、 longxia(60904)、 囫囵吞澡(58054)

下面展示总排行榜的前3名(T1-T3)和今年排行榜的前3名的朋友(C1-C3)……更多信息,请查看:总排行榜今年排行榜

T1. fhqu1462(969)、 T2. lwlpluto(14232)、 T3. 1367926921(962)  |  C1. anghuo(147)、 C2. fdisker(27945)、 C3. 囫囵吞澡(58054)



快速回复 返回顶部 返回列表