开发类炉石的3D卡牌游戏Demo的阶段性总结(一)

一个开发者的点滴积累

使用工具:UE4 (4.20.3)
使用资源:Dungeon_Areas(付费)、ParagonShinbi(免费)、ParagonSunWukong(免费)
开发耗时:3756分钟

运行流程

打开游戏,先进入游戏开始界面,界面上有两个按钮:游戏开始和退出游戏。
点击游戏开始,加载场景,场景中有两个英雄,我的界面上有3张牌,以及回合结束按钮。
选中牌,然后移动到战场中,召唤一个随从。拖拽自己的英雄到对方的英雄身上,自己的英雄会走过去,攻击,然后退回来。
点击回合结束按钮,然后敌方英雄进行攻击。攻击的时候显示血量减少。然后回合结束。
再由我进行攻击,然后游戏结束,显示结束界面。然后退回到开始界面。

目前开发完成的功能是:
拖拽自己的英雄到对方英雄身上,自己的英雄走过去,攻击,然后退回来。

开发过程与反思

游戏模式

原本没有在意游戏模式,因为几乎所有的教程都只会告诉你游戏模式设置成什么样子,至于游戏模式是用来做什么的都不会提。我也是在机缘巧合之下才会仔细地阅读了一遍游戏模式的文档,虽然没有解决我遇到的问题,但是也让我对游戏模式和游戏状态有了一个很好的了解,这一节就是来介绍游戏模式的。

先来看看UE4文档中时如何介绍游戏模式的:
即便是最开放的游戏也有一些基础规则,这些规则在UE4中被称为游戏模式(Game Mode)。对大多数关卡来说,基础规则包括:

  • 当前玩家和旁观者数量,以及允许的最大玩家数和旁观者数。
  • 玩家如何进入游戏,这包括选择出生点的规则,以及其他出生/重生行为的规则。
  • 游戏能否被暂停,如果可以被暂停,那么如何处理暂停。
  • 关卡之间如何切换,游戏是否要从电影模式启动。

你可以看到,这些规则真的很基础,基础到基本不用理会的地步,事实上,我们真的不需要花太多时间在上面。

大多数情况下,我们的游戏需要一个自定义的Game Mode。而这个Game Mode通常是一个继承自Game Mode的蓝图类。



这是我们可以重新定义类的一部分,这个项目里,只需要重新定义两个类就行了:Player Controller Class和Default Pawn Class。没错,就是后面有黄色返回箭头的那两个。UE4真是太人性化了!

Player Controller是用来定义如何操作的类。UE4默认不会显示鼠标,要显示的话就需要定义一个Player Controller类,然后将鼠标控制的相关操作打开。不过说实话,对Game Mode,Player Controller和Default Pawn三个东西还是自己重新定义来的心安。


新建的Player Controller将鼠标操作全勾选上

最后是character。这里的character并没有实际的意义,游戏本身是一个策略游戏,一个上帝视角的游戏,不是只操作一个character的游戏。所以,这里的character是一个空类,占个位罢了。

观察视角

官方文档中指出:如果你的游戏中没有一个Player Start(玩家起始)对象,那么玩家会从坐标(0,0,0)的位置开始。所以,不管需不需要,最好还是在地图中放一个Player Start对象。
不过我在这个项目里就没有放,因为我觉得不需要的东西就不用存在。

游戏需要一个45度俯视的观察角度,所以在场景中加了一个CameraActor。有两种方法设置这个视角,第一种方法是调用Set View Target with Blend函数,将Camera Actor启用。这种方法的特点是非常灵活,如果你需要切换多个摄像机的话可以使用。第二种方法是直接在CameraActor的细节面板上把Auto Activate for Player设置成Player 0,就跟下面的图片一样:



这种方法就是简单,如果不需要切换摄像机,那么这种方法是最合适的。显然,我采用的就是这种方法。

逻辑控制

如何让角色响应鼠标的控制呢?
首先需要明确,有多少的鼠标消息要响应。满打满算只有四个消息:悬停、不悬停、按下、释放。鼠标移动到角色上时,需要明确地提示玩家这个角色是可以操作的,所以,鼠标的样式需要改变。当玩家按下左键,鼠标样式也要改变,示意玩家已经抓住了这个角色。当玩家抓住这个角色的时候,将鼠标移动到其他地方,角色的朝向就会改变。此时,松开鼠标,角色就会移动过去进行攻击。

两个关键点:1、如何判定已经抓住角色?2、如何计算角色的朝向?

1、如何判定已经抓住角色?

鼠标能抓住角色只有角色在原点的时候才有效。所以,角色有一个初始状态,Idle(一开始的时候用一个bool变量IsAtOrigin来判断角色是否处于原点,随着玩家的状态越来越多,发现这种方式非常不适合,于是改成用玩家的状态来区分,简洁大方)。鼠标必须在角色上(通过IsOnCharacter来判断),然后按下左键才能算是抓住(将这种状态保存到变量IsGrabbed中)。具体的逻辑如下图所示:


判断抓住的逻辑
2、如何计算角色的朝向?

要计算朝向,需要一个前置条件,那就是能否触发。触发的条件有两个,其一:角色必须被抓住;其二:鼠标移动的距离必须大于一定数值(这里是200)。两个条件都满足后,触发这种状态就会被保存到IsTroggleOn变量中。


计算是否触发

如果能触发,还需要计算角色的旋转角度。方法是这样,先在鼠标位置与角色位置之间拉一个向量(鼠标位置-角色位置),将这个向量标准化后,计算与角色朝向向量之间的点积,这个点积就是旋转角度的cos值。然后通过acosd函数将其转换成角度,特别要注意的是,输出的角度需要有正负,用来区分角色是从当前朝向往左转(加负值)还是往右转(加正值):


计算旋转角度

算完角度后,更新角色的朝向就简单了,只需在原来的旋转角度值上加上计算出的角度,像这样:


设置角色角度

整个刷新流程就是这样:


刷新流程

角色动画

对于角色动画的实现,真的是走了很多弯路。我是一个做棋牌游戏开发的程序员,脑子里的思维模式是触发式思维。

触发式思维就是如果要产生什么动作,必须有触发这个动作的一次调用

但是3D游戏不一样,3D游戏的场景永远处于刷新之中,所以角色的动画应该是轮询式(每次刷新时都获取当前的状态,来决定需要绘制成什么样)。所以一开始我做的时候对动画的控制都是放在关卡蓝图里,然后直接设置Animation Blueprint。这种方式如果场景中只有一个角色还能应付,多两个就应付不来了。要意识到这两种模式之间的区别真不是一件容易的事情,我的运气真是太好了。

播放角色动画有两个关键点,一是如何切换角色的动画,二是如何更新角色的位置。

如何切换角色动画?

官方推荐做法:使用Animation Blueprint来控制动画。方式是从新建目录中定位到动画->动画蓝图,然后在弹出的窗口中选好目标骨架,这样就创建好了一个动画蓝图(Animation Blueprint)。

在动画蓝图中,最适当的方式应该就是创建一个状态机,让动画蓝图根据当前的状态来播放适当的动画。角色有六种状态,分别是:待机、前进、停止前进、攻击、后退、停止后退。状态切换的顺序也是按照这个顺序,也就是说,角色只能顺序切换这些状态,不能跳状态切换(例如从待机到攻击)。在状态机中的表现就是这样:


动画状态切换的状态机

切换的条件分别是:

  • Idle -> Moving:角色状态变成MovingFwd(前进)
  • Moving -> Stop:角色状态变成FwdStop(停止前进)
  • Stop -> Attacking:前一个动画剩余时间<=2.5秒
  • Attacking -> MovingBack:前一个动画剩余时间<=0.5秒
  • MovingBack -> BackStop:角色状态变成BwdStop(停止后退)
  • BackStop -> Idle:前一个动画剩余时间<=2.5秒

因为从停下到攻击到返回时一套完整的流程(停止后退到待机状态也是这样),所以不需要根据状态改变来切换,动画放到一定程度就可以到下一阶段了,而且这时候需要将动画的状态同步给角色,因此还需要添加一些动画事件。


动画事件

动画事件有3个:进入攻击状态、进入返回状态、进入待机状态。这些事件可以在状态上添加,也可以在状态之间的切换操作上添加。下图就是在Attacking上添加的事件:


进入攻击状态

动画状态的切换需要让角色知道,动画蓝图也需要从角色蓝图中获取状态以便选取适当的动画。前者可以通过定义一个事件调度器(ShinbiAnimationsStatus)然后调用来实现,后者可以在每一帧都会调用的Update事件里去获取角色状态。


从角色中获取状态
如何更新角色位置?

角色位置更新首先需要在前进或者后退状态才会有效。通过计算原点和目标点之间的向量,标准化之后乘上一个速度,再加到当前位置上就非常容易实现:


位置计算

前进与后退的区别也就是向量的指向不同了而已。

设置角色的位置,调用SetActorLocation即可,另外,当玩家到目标位置或者玩家回到原点之后,需要更新一下玩家的状态,这个操作由Calculate Character Status函数来完成。


更新位置

项目管理

U盘。资源实在太大了,没法上传github,甚至连码云都没法上传(码云单个文件限制大小为100M)。

当前进展

目前要做的就是角色之间的碰撞(collision)响应。只是我对碰撞的了解甚少,所以需要仔细的阅读碰撞相关文档,要完成相关的碰撞功能,时间估计不会低于500分钟。

总结

想要尽快地实现功能,直接参考已有的项目并不是最快的方法(如果没人解释为什么要这么做的话),最快的方式还是老老实实地阅读文档,对整个功能有一个完整的理解。

参考资料:

官方文档(英文)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容