从虚幻4动画系统与控制器交互理解数据驱动(一)古老的写法

简介:

游戏开发中古老的思想是认为,游戏是程序和数据来构成的,程序加载数据,并根据当前游戏的各种“状态”来调用对应的代码分支,由对应的代码分支来控制数据的使用,重要的数据之一就是动画。具体表现为,在游戏开发中对于动画会大量的使用状态机。


我们先看古老游戏的动画系统,在后面我们再讨论虚幻4的动画……


一个古老的游戏动画库伪码大概是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Class 动画数据
{
     void  创建(动画数据文件路径)
     void  释放()
     
     void  播放()
     void  绘制()
     ...
}
 
 
Class 动画播放器 //状态机
{
     void  创建(动画数据句柄)
     void  释放()
     
     状态码 获得当前动画状态()
     void  切换动画状态(状态码)
     void  更新动画()
     void  绘制()
     void  绑定回调(...)
     ...
}


然后我们在实际使用的时候,找个游戏做例子,比如:

老板说:我们抄一个《炸弹人》

wKiom1cd2_Cw2M1RAABCNkQbEJI151.jpg


根据需求,我们的主角需要如下动画资源:

角色向上行走动画

角色向下行走动画

角色向左行走动画

角色向右行走动画(可由镜像获得 )

角色死亡动画


这已经把动画素材的需求量减到最小了,至少需要这么多资源


然后我们来实现主角类

1
2
3
4
5
6
7
8
9
10
11
12
13
Class 主角
{
     void  创建()
     {
         动画播放器句柄 = 动画播放器.创建(主角的动画数据句柄)
     }
     
     void  更新()
     void  绘制()
     
     动画播放器句柄
     ...
}


在更新函数中,我们使用状态机来控制分支代码,分支结构有各种各样的写法:函数指针组,if-else,switch-case,状态模式,等等……


用最傻的写法switch-case实现主角::更新()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//需要我们添加一些状态码来记录角色状态,这也是一个状态机——角色的状态机
Class 主角
{
     角色朝向枚举
     {
        
        
        
        
     }
     
     角色动作枚举
     {
         站立
         行走
         死亡        
     }
     
     角色当前动作状态码
     角色当前朝向状态码
}
 
void  主角::更新()
{
     switch (角色当前动作状态码)
     case  站立:
         站立的处理输入() //可能会触发从站立到移动切换、埋雷
         //站立状态没有动画刷新
     case  移动:
         移动的处理输入() //可能会触发从移动到站立切换、埋雷
         移动的逻辑刷新() //比如:处理角色位移(可能有碰撞)
         移动的刷新动画数据() //根据当前角色当前朝向状态码刷新移动动画
     case  死亡:      
         //死亡状态不接受输入
         //死亡状态没有逻辑刷新
         死亡的刷新动画数据() //刷新死亡动画
}
 
void  主角::绘制()
{
     动画播放器句柄.绘制()
}


最后,我们在游戏主循环,刷新角色

1
2
3
4
5
6
7
8
游戏循环( true )
{
     主角句柄.更新()
     ...
     
     主角句柄.绘制()
     ...
}


然后我们就可以执行游戏来测试了,代码似乎也算清晰可用。


然而,完全不是那么回事

  1. 角色的状态很复杂,而且角色状态和动画状态都不是完全对应

  2. 每个状态下都要处理,输入,逻辑更新,动画更新,绘制,这些都用函数封装,这些不同层次的代码,混在一起显得很乱,别人不好读,要脑补很多东西

  3. 没有使用OO的特点,每个角色状态实际上是不同的对象来驱动,用函数既没有类,函数也含有副作用,这几乎没有实现复用


以上代码,对于简单的游戏可以,但是随着游戏需求越来越复杂,最后代码会变得一团乱麻


也许你会想到,我们重构代码来解决这个问题,然而真的能做到吗?我们下篇再讲——





 本文转自 老G 51CTO博客,原文链接:http://blog.51cto.com/goldlion/1767606,如需转载请自行联系原作者


相关文章
|
4月前
|
前端开发 Java UED
JSF 面向组件开发究竟藏着何种奥秘?带你探寻可复用 UI 组件设计的神秘之路
【8月更文挑战第31天】在现代软件开发中,高效与可维护性至关重要。JavaServer Faces(JSF)框架通过其面向组件的开发模式,提供了构建复杂用户界面的强大工具,特别适用于设计可复用的 UI 组件。通过合理设计组件的功能与外观,可以显著提高开发效率并降低维护成本。本文以一个具体的 `MessageComponent` 示例展示了如何创建可复用的 JSF 组件,并介绍了如何在 JSF 页面中使用这些组件。结合其他技术如 PrimeFaces 和 Bootstrap,可以进一步丰富组件库,提升用户体验。
59 0
|
3月前
|
图形学 开发者 UED
Unity游戏开发必备技巧:深度解析事件系统运用之道,从生命周期回调到自定义事件,打造高效逻辑与流畅交互的全方位指南
【8月更文挑战第31天】在游戏开发中,事件系统是连接游戏逻辑与用户交互的关键。Unity提供了多种机制处理事件,如MonoBehaviour生命周期回调、事件系统组件及自定义事件。本文介绍如何有效利用这些机制,包括创建自定义事件和使用Unity内置事件系统提升游戏体验。通过合理安排代码执行时机,如在Awake、Start等方法中初始化组件,以及使用委托和事件处理复杂逻辑,可以使游戏更加高效且逻辑清晰。掌握这些技巧有助于开发者更好地应对游戏开发挑战。
160 0
|
4月前
|
UED 开发者
哇塞!Uno Platform 数据绑定超全技巧大揭秘!从基础绑定到高级转换,优化性能让你的开发如虎添翼
【8月更文挑战第31天】在开发过程中,数据绑定是连接数据模型与用户界面的关键环节,可实现数据自动更新。Uno Platform 提供了简洁高效的数据绑定方式,使属性变化时 UI 自动同步更新。通过示例展示了基本绑定方法及使用 `Converter` 转换数据的高级技巧,如将年龄转换为格式化字符串。此外,还可利用 `BindingMode.OneTime` 提升性能。掌握这些技巧能显著提高开发效率并优化用户体验。
68 0
|
7月前
|
存储 前端开发 JavaScript
【亮剑】在Web开发中,滚动事件(onScroll)是一个常见且强大的交互手段。
【4月更文挑战第30天】在React中,利用`onScroll`事件可实现无限滚动、动态加载和视差效果。通过`componentDidMount`和`componentWillUnmount`(类组件)或`useEffect`(函数组件)添加/移除滚动监听器。为了优化性能,需注意节流、防抖、虚拟滚动和避免同步计算。实战案例展示了如何结合Intersection Observer实现无限滚动列表,当最后一个帖子进入视口时加载更多数据。关注性能,确保应用流畅。
126 0
|
7月前
|
前端开发 JavaScript Java
COM、ActiveX与ActiveXDLL和ActiveXEXE:探索组件化开发的潜力(附图代码及关系详解)
COM、ActiveX与ActiveXDLL和ActiveXEXE:探索组件化开发的潜力(附图代码及关系详解)
|
前端开发 定位技术 容器
webgis前端控件编程
webgis前端控件编程
94 0
|
JSON 前端开发 JavaScript
GIS前端编程-航线动态模拟
GIS前端编程-航线动态模拟
135 0
|
JavaScript 前端开发 开发者
DOM驱动和数据驱动的区别【浅显易懂】
DOM驱动和数据驱动的区别【浅显易懂】
98 0
|
前端开发 JavaScript
前端祖传三件套JavaScript的对象之对象创建的其他组合或演进模式
在 JavaScript 中,对象是一个非常重要的数据类型,用于表示一组相关属性和方法。创建对象的方式有多种,除了构造器模式和原型模式之外,还有其他一些组合或演进模式可以帮助我们更好地管理对象。本文将介绍一些常见的组合或演进模式。
77 0
|
Java
手把手一步一步教你使用Java开发一个大型街机动作闯关类游戏08控制sprite移动
手把手一步一步教你使用Java开发一个大型街机动作闯关类游戏08控制sprite移动
126 0
下一篇
DataWorks