当前位置: 首页 > news >正文

游戏中的角色是如何“动”起来的?

最近公众号会陆续的转载一些我知乎上曾经写的文章,主要是挑选那些质量比较高的文章。考虑到很多读者并不是从事游戏开发或者研究虚幻引擎,我会在转发文章前先写一篇科普文章,给大家讲讲游戏开发中的一些基本原理。这篇文章就是为下篇《UE4移动组件原理分析》做科普预热的,欢迎转发分享~

    从红白机时代的超级玛丽,到PC时代CS里面的反恐精英,再到如今主机上栩栩如生的“荒野大镖客”。随着技术的进步,游戏中的这些角色,在移动的表现上愈加真实。

我们会想,为什么有些游戏中的人物,移动起来非常不自然?而有些制作精良的游戏里,每个角色动起来都栩栩如生。这些角色是如何在我们游戏世界中移动的呢?今天这篇文章就会简单的给大家分享一下,游戏角色在游戏世界中的移动原理。


游戏世界

- 真实世界的复制版- 

谈到移动,首先不得不谈一下我们游戏中的世界。游戏世界分为2D世界和3D世界,不妨先从3D游戏的世界说起。3D游戏世界是一个三维立体世界,和我们和现实中的世界相似,我们游戏中的所有角色,都会在这个三维的世界里面出生、移动、交互直至死亡。除了角色以外,游戏中的其他对象也是如此产生的,比如说建筑、武器、道具等等。他们身上都保存着自己的坐标位置,当我们在移动我们的角色的时,其实就是在不断的修改当前角色的坐标值。

对于2D游戏,原理要简单很多,所有物体都存在于一个平面世界,他们的移动也只是改变其X,Y两个坐标轴而已。

移动效果 ≠ 动画效果

- 有动作不代表有位移- 

    现在,让我们再把焦点放在角色的移动上。这里先抛出一个问题,当我们看见一个角色在奔跑时,他真的是在移动么?

很多人可能会说这不是理所当然的么?难道还能原地跑么?没错,游戏中的角色还真就可能是原地奔跑。事实上一个角色的移动和你看见他做什么动作是没有直接关系的。因为在游戏里面,移动表现“不等于”动画表现。不妨看一下下面这张图

图中的这个角色一直在播放移动的动画,但是并没有产生一丁点的位移,因为没有任何逻辑去修改这个角色的位置。


动画

- 像拍电影一样把动作连起来 -

说到这里,我们引出了一个新的游戏概念——动画,那么如何理解游戏中的动画?为什么播放动画不会修改玩家坐标呢?

所谓动画,其实就是角色行为的一种表现方式,和电影或者动漫一样,将一个对象在一段时间内的动作记录并播放,就是一个完整的动画。游戏中针对每个角色都需要美术去单独的为其制作很多段动画,导入到游戏引擎中进行混合与切换,最后实现游戏中的效果。比如说,角色在跑步时有跑步的动画,跳起来会有跳跃的动画,战斗会播放战斗的动画,这些都是不同的动画片段,需要美术们去提前制作。

动画其实也是分为2D动画与3D动画的,以前的游戏都是2D游戏,本质上和我们小时候看的小人书一样,给一个角色创建多张图片,每张上的图片都有着不同的动作,将这些图片连在一起播放就是一个2D动画。

不过与2D动画将每一帧的画面记录下来不同,3D动画记录的角色的3D坐标数据。目前的3D动画实现方式都是由骨骼+蒙皮来做的,也就是说其实我们的每一个角色身上会有一些骨骼,当我们执行某个动画时对应的这些骨骼位置就会发生变化,然后再驱动这个骨骼的“蒙皮”(可以认为我们玩家的皮肤)变化。比如说,一个玩家把手举起来的时候,我们手臂对应的骨骼就会动起来然后包裹着他外面的皮肤也会随之移动,这样你就能看到一个真正的动画了。那么为什么他执行动画的时候不会移动呢?因为所有的动画默认都是相对他自身变化的,相对游戏世界他就是原地静止的。(其实也可以做到相对世界发生变化,这个概念叫RootMotion,在后面的文章里面会进一步讲解)


我们的角色在播放移动动画的时候,其实就是一个循环的动画,你可以看到这个角色的两个腿在不停的走动,手臂也在不停的摆动,但是它只是在不断的播放一个动画。只要处理好动画的开头与结尾就能很好的表现出角色的移动。

移动

- 有“人”暗中操作 -

    了解了动画与移动的关系,我们现在就知道驱动角色移动的其实另有其人。不过这个“人”是谁呢?其实这个“人”是谁并不重要,任何一个能做这个工作的对象都可以成为这个“人”,你把它交给地上的一块石头去处理也是可以的。在代码里面,我们可以随意抽象出来一个“移动控制者”,他的工作就是控制角色如何在游戏世界里面移动。

    现在假如我们给他一点点移动速度(比如每秒0.1m),如果这时候他的动画时迈开步子疯狂地跑的话,就会发现动画与速度是不匹配的。同理,如果把动画调成走路的样子,而速度改为10m/秒,就会发现他一步还没迈出去就已经滑到对面墙上了,这就是我们平时所说的滑步现象(不过没这么夸张),也是很多游戏里面看起表现很差的原因。只有将速度与动画完全匹配,才能让游戏角色移动看起来非常自然。

    当然,真实的情况可能要比这个还要复杂很多。我们的角色在一开始可能还正在缓慢的行走,不一会儿之后就突然开始加速跑了起来。为了模拟真实世界中的移动的样子,我们需要对他的这个移动的过程要做各种精确的模拟,比如说行走、奔跑、甚至是游泳、飞行,不同的状态需要以不同的方式去模拟。对于更加真实的游戏,我们还应该把物理也考虑进去,重力、摩擦力、阻力等等都会影响角色的移动,不过任你怎么复杂核心就是两点——速度与加速度。一般来说,包含了上面这些逻辑的模块我们可以称之为移动系统。同时,由于我们的速度发生变化,还需要动态的修改对应的动画,这个匹配的处理以及播放的逻辑所在的模块我们称为动画系统。如果说,角色的移动是通过你的键盘按键触发,那还会涉及到一个输入系统,输入系统会根据你的按键时长转换成对应的移动速度或者是加速度,具体的转换方式要看游戏的设计逻辑。

到目前为止,如果你认识到了移动表现=动画系统+移动系统,你已经基本理解了游戏角色的移动原理了。现在,不妨再稍微拓展一些。一般美术在制作动画时只会制作走路的动画以及奔跑的动画,他不会制作一个0.5/m的走路动画然后又制作一个0.6m/s的走路动画,如果每个速度都要做一个动画的话美术肯定要累死。那游戏中的玩家,在走路和奔跑切换时该怎么处理呢?答案就是把奔跑动画与行走动画进行融合,根据速度的不同,去混合这两种动画,我们可以称之为blend space,参考下面的图片。

对于游戏质量比较高的3A大作,他们的动画远远不只这么简单,除了有基本的走的动画,还有各种方向的转身动画,包括左转和右转,斜着转,总之各个方向都有一个动画,然后通过很多动画的融合,才能达到一个非常流畅的一个表现效果。而且,移动系统的逻辑也需要模拟的非常精确,来和这些动画达到完美的匹配。

移动同步

- 告诉他你在哪 -

    最后,我们再来简单谈谈移动的同步。当我们在玩网络游戏时,你会在你的显示器上面看到多个其他的玩家并且可以看到他们在移动,那这个过程中他是怎么样做的呢?这在游戏设计中有一个专有名词,叫做网络同步,网络同步是游戏领域里一块非常重要也很复杂的一个功能。简单来说,当其他玩家在移动的时候,他需要把他的坐标实时的通过网络发送到你的客户端,你就可以看到他在你的客户端上移动。在游戏开发中,发送的频率,发送的内容以及发送方式的不同的都会影响到游戏的具体表现,我在下一篇文章里会详细的分析虚幻引擎里面的移动组件是如何处理的。

—END—


技术交流,欢迎加我微信:ezglumes ,拉你入技术交流群。

扫码关注公众号【音视频开发进阶】,一起学习多媒体音视频开发~~~

喜欢就点个「在看」吧 ▽

相关文章:

  • Android JNI 中的线程操作
  • 【音视频连载-004】基础学习篇-SDL 加载图片并显示
  • OpenGL 利用 Alpha 透明度进行测试
  • 【音视频连载-005】基础学习篇-SDL 加载 YUV 文件并显示
  • 【音视频连载-006】基础学习篇-SDL 播放 YUV 视频文件
  • Android 使用 OpenGL ES 绘制球面
  • 【音视频连载-007】基础学习篇-SDL 播放 PCM 音频文件(上)
  • PBO是OpenGL最高效的像素拷贝方式吗?
  • 游戏开发入门(一)游戏发展史
  • 【音视频连载-008】基础学习篇-SDL 播放 PCM 音频文件(下)
  • memcpy速度太慢?掌握这个技术让内存拷贝效率成倍提升
  • DXOMark是如何评价音频质量的
  • 【每周一记-001】~~~
  • 【音视频连载-009】第二季 FFmpeg 打造简易播放器
  • 【每周一记-002】
  • Angular6错误 Service: No provider for Renderer2
  • CentOS7简单部署NFS
  • classpath对获取配置文件的影响
  • Codepen 每日精选(2018-3-25)
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • ES学习笔记(12)--Symbol
  • Linux编程学习笔记 | Linux IO学习[1] - 文件IO
  • mysql 数据库四种事务隔离级别
  • npx命令介绍
  • opencv python Meanshift 和 Camshift
  • PAT A1120
  • Python利用正则抓取网页内容保存到本地
  • spring + angular 实现导出excel
  • SQLServer之创建数据库快照
  • Vim 折腾记
  • WePY 在小程序性能调优上做出的探究
  • 从零开始在ubuntu上搭建node开发环境
  • 官方解决所有 npm 全局安装权限问题
  • 浅谈Golang中select的用法
  • 深入浅出Node.js
  • 无服务器化是企业 IT 架构的未来吗?
  • ​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】
  • # Swust 12th acm 邀请赛# [ A ] A+B problem [题解]
  • ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTr
  • #QT(串口助手-界面)
  • (2)关于RabbitMq 的 Topic Exchange 主题交换机
  • (2020)Java后端开发----(面试题和笔试题)
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (十)【Jmeter】线程(Threads(Users))之jp@gc - Stepping Thread Group (deprecated)
  • (转) Android中ViewStub组件使用
  • .form文件_一篇文章学会文件上传
  • .jks文件(JAVA KeyStore)
  • .Net Core/.Net6/.Net8 ,启动配置/Program.cs 配置
  • .NET MVC第五章、模型绑定获取表单数据
  • .NET 中什么样的类是可使用 await 异步等待的?
  • .net反编译的九款神器
  • .NET和.COM和.CN域名区别
  • .net下简单快捷的数值高低位切换
  • /使用匿名内部类来复写Handler当中的handlerMessage()方法
  • @Mapper作用