GAMES104:15 游戏引擎的玩法系统基础-学习笔记

ops/2024/9/24 22:43:07/

文章目录

  • 0,游戏性课程框架
  • 一,事件机制
    • 1.1 事件的定义
    • 1.2 callback的注册
    • 1.3 事件的分发系统
  • 二,游戏逻辑与脚本系统
    • 2.1 特点和常见脚本语言
    • 2.2 脚本语言的GO管理
    • 2.3 脚本语言的架构
    • 2.4 可视化脚本
  • 三,Gameplay 开发中的3C :Character、Control、Camera
    • 3.1 角色
    • 3.2 控制
    • 3.3 相机
  • QA


0,游戏性课程框架

在这里插入图片描述

  • Gameplay 的挑战:
    1. 玩法系统要和动画、特效、UI、声效、Audio、外设等多个系统共同合作,因此岗位涉及面很多
    2. 同一个游戏也有很多不同的玩法,比如巫师三和昆特牌,对玩法系统的可拓展性要求高
    3. 玩法的快速迭代

一,事件机制

事件机制就是让GameObject 互相交互的机制,比如走到哪里发生爆炸。这种机制如果全都使用ifelse语句写死条件,系统会崩溃掉,所以一般使用event或者message的形式来实现。

想应对比较复杂的游戏机制时,事件机制一般采用发布-订阅模式(Publish-subscribe Pattern):不同的发布者发送不同类型的事件给事件调度者(event dispatcher),可以理解为一个调度公司,事件调度者把事件告诉对应的订阅者(不知道发布者是谁),然后订阅者作出相应的动作(callback)

  • 该模式的三个关键点分别是 事件的定义、callback、事件的分发系统
    在这里插入图片描述

1.1 事件的定义

Event Type + Event Argument(配置)

Event虽然可以使用基类去继承, 但如果不断加入新的事件类型和对应编辑性,则每次都需要重新编译(与上节课的代码反射相关),这时常见解决方法有:

  1. 将新事件新编译成的C++代码当成 DLL,加入到系统中(UE)
  2. 上层采用 C# 语言,方便动态挂接和扩展
  3. 用脚本语言

1.2 callback的注册

回调函数或触发函数(invoke)。回调函数的注册和触发有一个时间差,在这段时间有可能发生意外,比如订阅者被销毁,因此对象的生命周期和回调函数的安全性是非常重要的。

可以使用c++11中类似智能指针的技术解决:

  1. 强引用:使订阅者在有事件注册时不能销毁。但是会导致系统内存越来越大,所以很少这么用,只有部分情况,比如物体之间有父子关系时使用。
  2. 弱引用:在执行回调函数前,判断订阅者是否存在即可。但是性能会下降一些。

1.3 事件的分发系统

如果分发系统把所有的消息都从头到尾扫一遍的分发效率很低,需要专门的管理。

  • 立即执行:消息来了马上就分发。(但是可能会在线程中打断父函数的调用)

问题:

  1. 当发生连锁反应时,比如手雷引爆其他手雷,会造成回调函数的层数过深的问题。
  2. 如果事件回调本身很耗时,比如粒子系统,帧率会骤降
  3. 调用一层套一层,很难并行
  • 事件队列:把事件存储到Queue中,后续统一处理;并且需要序列化后存储,用到时候再反序列化(c++反射)—跨进程夸网络需要

具体实现:

  1. 使用循环队列Ring Buffer来管理内存(申请一次内存即可)
  2. 使用Batching分批管理:将事件分类,比如网络事件、动画事件、战斗事件等,每一类别分别采用一个队列。

问题:

  1. 不能保证消息执行的顺序(比如先动画后物理等逻辑关系),所以为了保持系统健壮性需要同时支持 PreTick、ImmediateTick,PostTick(这块是bug高发地)
  2. 是one-frame delays系统,会导致一帧左右的延迟(及时性差),因此在需要及时性的地方,比如战斗打击,就要hardcode一下专门处理

二,游戏逻辑与脚本系统

  • 早期游戏逻辑都是直接用c++写的,但这样不可持续,因为:

    1. 每次对游戏逻辑进行修改都要重新编译;
    2. 当遇到错误代码时,很容易导致程序崩溃;
    3. 当已发布游戏遇到 bug 时,难以进行热更新。(现在游戏遇到bug,官方更新一个补丁就能解决,就是用到热更新)
    4. 玩法基本是由设计师负责的,不会写代码,而用脚本语言更容易。
  • 脚本语言运行时会先被编译为bytecode,再到虚拟机上运行,代价是速度慢一些

2.1 特点和常见脚本语言

  • 脚本热更新:可以快速迭代、线上解决bug–其实就是更新函数指针(但需要考虑工程鲁棒性)

  • 脚本语言优点:

    1. 好学易上手
    2. 热更新方便
    3. 脚本语言一般运行在虚拟机沙盒上,可以自己crash而不会导致系统crash,更稳定(大型游戏一旦某个功能crash掉,会立马重新启动脚本重新接入)
    4. 可以快速迭代
  • 脚本语言的问题:

    1. 慢,比如lua就慢很多。可以使用JIT优化—一边解释一边编译
    2. 需要虚拟机去运行脚本语言
    3. 调试不方便
  • 几个受欢迎的脚本语言:

    1. Lua(魔兽世界),轻量内存少,但内库少需要自己写
    2. Python,库强大丰富,不轻量,占用内存多
    3. C#(Unity、暴雪),学习成本低,本身就是编译语言,内存占用中等

2.2 脚本语言的GO管理

脚本语言汇总最难的事对象的管理,但是由谁管理 GO的生命周期呢,是代码还是脚本呢?

  • 引擎代码管理

引擎生命周期管理器–更严谨也有没写好内存泄漏的可能
当脚本涉及到本地对象的时候不安全
当玩法复杂需要创建对象时,在脚本中创建更简单。

  • 脚本管理

GO的生命周期可以被脚本的GC系统(Garbage Collection垃圾回收器)自动管理-----省事但慢,甚至用到10%时间
对象释放的时间不可控(GC 控制)
脚本中如果引用关系过于复杂容易发生内存泄漏

  • 因此一般大型单机都是引擎直接管理,而玩法复杂的比如mmorpg一般用脚本管理GO

2.3 脚本语言的架构

  1. 引擎包脚本:主要玩法写在native code里,把组件扩展成脚本
    在这里插入图片描述
  2. 脚本包引擎:主要玩法写脚本里,把引擎当成 SDK库提供各种服务。(迭代快,需要认真设计接口,但现在用的少—弹幕说网易的自研引擎就是这样)
    在这里插入图片描述

2.4 可视化脚本

  • 比如UE的蓝图,Unity的Visual Scripting、Shader Graph就是可视化脚本,这种形式对艺术家友好,并且可视化更不容易产生错误。
  • 并且可视化脚本debug十分方便,每个节点的输出都可以看到
  • 可视化脚本的问题:
    1. 团队工作时,可视化脚本很难合并,会丧失语义且效率低下(因此很多团队前期用可视化脚本,成熟后会翻译为代码)
    2. 有时候可读性很差,一团乱麻
  • 可以把可视化脚本翻译为脚本,然后再执行(再一次应征可视化脚本就是脚本)

三,Gameplay 开发中的3C :Character、Control、Camera

  • 3C系统是游戏体验的核心
  • 要了解什么是3C系统,《双人成行》可以作为最好的案例。

3.1 角色

  • 角色移动:障碍、上下坡、起步和停下等,要处理非常多细节和复杂状态,比如滑行、滑冰、飞等
    在这里插入图片描述
  • 与环境互动:雪地系统、音效、粒子等等
  • 真实物理互动:状态机控制下的各种状态

3.2 控制

控制系统是游戏丝滑手感的由来

  • 输入设备控制:鼠标键盘、手柄、方向盘、甚至飞行控制器等的控制,比如自动吸附、调整相机等(瞄准辅助也是类似)
  • 反馈感:比如振动或力反馈,键盘背光也算吧
  • 按键组合:拳皇

3.3 相机

  • 相机并不是固定在角色身后的,而是随着角色跑动、走动等状态变化,相机远近大小都跟着变化,或者Camera Track(模仿电影视角更有代入感)
  • Spring Arm:当靠近墙时,保证相机不穿墙。
  • 相机效果:互动抖动、滤镜、各种后处理等
  • 视角变化:第一、三人称切换、武器不同变化、载具不同变化,这个变化还需要插值
  • 除了上述要求,还需要提供一个可视化脚本系统,让设计师能对相机参数进行调整设计。

QA

  • 接入可视化脚本是不是就不需要传统脚本了?不是,很多时候传统脚本效率更高,一般两者都需要
  • 未来平台会不会趋向禁止热更?现在确实一些平台比如ios、ps不喜欢热更,未来会不会变化不能确定
  • 会不会用逻辑和表现分离的模式开发?实际上大部分是分离的,比如一些修仙类游戏的表现比较简单,表现和逻辑可以分的非常开,这样团队规模也可以做得很大,开发效率也会高。但现在更多玩家喜欢互动更多的3A游戏,这样逻辑和表现联系就更紧密,需要糅合去做,对开发者要求也更多。

http://www.ppmy.cn/ops/115493.html

相关文章

通信工程学习:什么是NFVO网络功能虚拟化编排器

NFVO:网络功能虚拟化编排器 NFVO(Network Functions Virtualization Orchestrator),即网络功能虚拟化编排器,是网络功能虚拟化(NFV)架构中的核心组件之一。NFV是一种将传统电信网络中的网络节点…

李宏毅结构化学习 03

文章目录 一、Sequence Labeling 问题概述二、Hidden Markov Model(HMM)三、Conditional Random Field(CRF)四、Structured Perceptron/SVM五、Towards Deep Learning 一、Sequence Labeling 问题概述 二、Hidden Markov Model(HMM) 上图 training data 中的黑色字为x&#xff…

AI+教育|拥抱AI智能科技,让课堂更生动高效

AI在教育领域的应用正逐渐成为现实,提供互动性强的学习体验,正在改变传统教育模式。AI不仅改变了传统的教学模式,还为教育提供了更多的可能性和解决方案。从个性化学习体验到自动化管理任务,AI正在全方位提升教育质量和效率。随着…

二阶滤波算法总结(对RC滤波算法整理的部分修正和完善)

文章目录 1、一阶低通滤波2、一阶高通滤波3、二阶低通滤波器3.1 二阶RC低通滤波器的连续域数学模型3.2 二阶RC低通滤波器的算法推导3.3 matlab仿真 4、二阶高通滤波器4.1 二阶RC高通滤波器的连续域数学模型4.2 二阶RC高通滤波器的算法推导4.3 matlab仿真 5、陷波滤波6、带通滤波…

Redis数据结构之list列表

一.list列表 列表相当于数组或者顺序表 它里面的元素是有序的,也就是可以通过下标进行访问。这里的有序的含义是要根据上下文区分的,有的时候,有序指的是升序/降序,有的时候有序指的是顺序很关键,俩个元素交换后就不…

6-1 jmu-Java-04面向对象进阶-01-接口-匿名内部类ActionListener

分数 10 全屏浏览 切换布局 作者 郑如滨 单位 集美大学 已有MyStarter类(你无需编写,直接使用),其具有:构造函数:public MyStarter(ActionListener ac)方法:start()启动任务 ###main方法执行流程: 输…

Qt开发技巧(四)“tr“使用,时间类使用,Qt容器取值,类对象的删除,QPainter画家类,QString的转换,用好 QVariant类型

继续讲一些Qt技巧操作 1.非必要不用"tr" 如果程序运行场景确定是某一固定语言,就不需要用tr,"tr"之主要针对多语种翻译的,因为tr的本意是包含英文,然后翻译到其他语言比如中文,不要滥用tr,如果没有…

el-upload如何自定展示上传的文件

Element UI 中,el-upload 组件支持通过插槽(slot)来自定义文件列表的展示方式。这通常是通过 file-list 插槽来实现的。下面是一个使用 el-upload 组件并通过 file-list 插槽来自定义文件列表展示的完整示例代码。 在这个示例中,…