Games104笔记-3-如何构建游戏世界
本文为课程Games104第三节课:How to Build a Game World的个人笔记。
游戏世界有什么?
课程里举了战地2042为例子,并且说这游戏很拉跨,嘻嘻嘻。
但如果以2042为例子,我们首先能想到的是很多动态交互物,例如玩家可以开坦克、开飞机,以及操控自己的小兵角色。当然像飞行中的导弹也一样。(下面图字打错了)
第二类物体就是静态物,这里包括瞭望塔、飞机的机棚、仓库、房屋、街道等。这些物体不可交互,但同样为游戏Gameplay玩法提供了重要的环境支持。
有一类不容易注意到的就是地形系统(UE里有专门的地形编辑工具),以及天空系统,植被等。
有了以上元素,我们可以得到看的见的游戏世界。但是我们的游戏还存在其它的物体,例如Trigger、空气墙等,它们虽然看不见,但是同样是玩法规则的一部分。
以上所有动静态物体统称为游戏对象Game Object(GO)。(注意:大地和天空系统是独立的)有了GO这个概念,我们该如何对某个GO进行操控呢?
Game Object
假设我们要实现一个能够自动巡逻的无人机,它需要具备什么?
第...
UE-MotionMatchingInteraction架构分析
模块:PoseSearch。目前对于MotionMatchingInteraction,无任何官方文档,无任何官方示例(即便是写这篇文章时Epic发布的UE5.7的GASP项目),故仅作参考。
关键类分析
Availabilities
Availability可以理解为我当前角色在本帧声明的交互意图和约束。
类声明在 PoseSearchInteractionAvailability.h 中:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051// input for MotionMatchInteraction_Pure: it declares that the associated character ("AnimContext" that could be an AnimInstance or an AnimNextCharacterComponent)// is willing to parteci...
Games104笔记-2-引擎架构分层
本文为课程Games104第二节课:Layered Architecture of Game Engine的个人笔记。
A Glance of Game Engine Layers
工具层
当我们第一次接触UE或者Unity时,映入眼帘的不是晦涩的代码,而是一个由许多工具组成的UI操作界面(在UE里叫做UE-Editor):
这一层是面对我们引擎用户的,叫做工具层,我们将其放在最顶层。
功能层
引擎的产品是游戏。为了能让游戏能够看的见、场景能够动态变化并且游戏能够和我们用户输入交互,我们肯定需要对这些需求提供相应的功能实现。在面试中经常被问到游戏中碰撞检测的原理,这涉及到的就是物理系统;另外即便是游戏客户端岗位也不可避免地被拷打图形学相关知识,而实时渲染领域则被应用于游戏引擎的渲染系统;另外游戏中玩法相关以及NPC人物也都离不开脚本、AI系统等。
这一层叫做功能层,它为上面的工具层提供支持。
资源层
游戏中有许多不同类型的资源,例如三维模型、骨骼、动画、声音、贴图等。它们需要在同一个引擎内进行统一管理,这就是资源层的职能。它为功能层提供操作对象。...
游戏客户端面经(自整理版)
岗位:游戏客户端开发
C++
1. 深拷贝和浅拷贝
深拷贝和浅拷贝的核心区别在于:拷贝时到底是只复制一层引用,还是把引用指向的内容也一起复制出一份新的。
浅拷贝是什么
定义 浅拷贝是指按字节或按成员逐个复制对象的值。对于指针类型的成员,只复制指针本身(即地址),而不复制指针所指向的内容。 在 C++ 中,如果用户没有显式定义拷贝构造函数和拷贝赋值运算符,编译器会自动生成默认版本,这些默认版本执行的就是浅拷贝。
特点
执行速度快,实现简单
多个对象共享同一份动态资源
容易引发重复释放(double free)或悬垂指针(dangling pointer)问题
深拷贝是什么
定义 深拷贝是指不仅复制对象的成员变量本身,对于指针类型的成员,还会重新分配内存,并将原指针所指向的内容复制到新分配的内存中。这样每个对象都拥有自己独立的资源副本,互不影响。
特点
避免了资源重复释放和意外共享
实现相对复杂,需要显式定义拷贝构造函数和拷贝赋值运算符
性能开销较大(涉及内存分配和数据复制)
2. 编译器默认生成的拷贝构造(拷贝赋值)的语义是什...
TArray
UE源码解析-TArray
这篇文章我们来分析UE里的变长数组的实现,对应模块为 Core 。
在 ContainersFwd.h 中罗列了很多容器模板的声明,里面包含 TArray : 1template<typename T, typename Allocator = FDefaultAllocator> class TArray;
Allocator
默认的分配器是 TSizedHeapAllocator ,在 ContainerAllocationPolicies.h 中声明: 12template <int IndexSize> class TSizedDefaultAllocator : public TSizedHeapAllocator<IndexSize> { public: typedef TSizedHeapAllocator<IndexSize> Typedef; };using FDefaultAllocator = TSizedDefaultAllocator<32>...
cpp3
cpp八股3
这里我们主要手撕一些面试里可能让你手撕的轮子(?
队列
首先看看环形队列,就是固定大小的缓冲区上维护两个指针:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051template<typename T>class CircleQueue{public: CircleQueue(int capacity=5) : cap(capacity), nxt(0), head(0) { buf.resize(cap); } T& Front() { if(Empty()) throw std::runtime_error("Queue is empty"); return buf[head]; } T& Back() ...
cpp2
cpp八股2
实现三个智能指针
首先是 UniquePtr,它是独占式的智能指针,不能被复制,只能被移动。
12345678910111213141516171819202122232425262728293031323334353637383940template<typename T>class UniquePtr{public: UniquePtr(T* p = nullptr) : ptr(p) {} UniquePtr(const UniquePtr&) = delete; UniquePtr& operator=(const UniquePtr&) = delete; UniquePtr(UniquePtr&& other) noexcept : ptr(other.ptr) { other.ptr = nullptr; } UniquePtr& operator=(UniquePtr...
AYS
关于AYShooter
AYShooter是一款使用UE5.7开发的多人在线FPS游戏。
参考游戏:APEX、TTF2、PUBG、CS2、三角洲等。
title
玩法简介
3C
角色采用FPP、TPP双重视角,FPP视角只在客户端渲染,TPP视角在除了自己以外的其他玩家身上渲染。
FPP采用传统状态机模型,TPP采用MotionMatch,考虑使用Lyra的动画作为Database。
扩展CMC,实现冲刺、滑铲、走墙等动作,实现运动状态网络同步以及客户端预测。
使用GAS建模核心玩法操作,例如 GA_Fire , GA_Reload 等。
实现基本的场景交互,例如攀爬、翻越。可以参考GASP的实现。
武器系统
GAS实现武器射击、换弹等功能。
目前先实现一把步枪,后续可以扩展更多武器。
AI
可以考虑使用比较新的StateTree和SmartObject系统。Demo阶段不用考虑。
UI
考虑采用MVC架构,具体用委托实现。这方面无需考虑网络同步。
DevLog1 AYRenderer
开发日志 AYRenderer
渲染Pass,VertexShader
模型矩阵与法线矩阵
在AYRenderer的UI界面中,用户可以自行设置渲染物体的 Transform :
Transform
这个 Transform 会形成所谓的 Model 矩阵,在顶点着色器(VertexShader)中会用到这个矩阵来对顶点进行变换。(即M变换)
当然所谓的顶点不只有位置和旋转的属性,还有法线属性,而法线的变换所对应的 Normal Matrix 则是 Model Matrix 的逆转置矩阵:
123Mat4 modelMatrix = object.transform.GetModelMatrix();// 法线矩阵是模型矩阵的逆转置矩阵Mat4 normalMatrix = modelMatrix.Inverse().Transpose();
我们来证明一下,首先假设模型空间中某顶点对应的切线为 t⃗,该切线可以由模型空间的两个顶点 A, B 表示: t⃗ = B − A.
经过模型变换后,对应的切线 $\vec{t'}$ 为: $$
\vec{t'}...
cpp八股1
cpp八股1
多态
什么是多态?C++中如何实现多态?
多态指的是同一个接口(在不同对象上)有着不同的行为。C++中多态分为编译时多态(静态多态)和运行时多态(动态多态)。
编译时动态顾名思义指的是在编译阶段就能决定哪个接口对应于哪个对象。
典型实现一:函数重载
注意返回值类型不参与重载判断
1234567891011121314151617#include <iostream>using namespace std;// 函数重载:同名函数根据参数不同实现不同行为int add(int a, int b) { return a + b;}double add(double a, double b) { return a + b;}int main() { cout << add(10, 20) << endl; // 调用 int 版本 cout << add(3.5, 2.7) << endl; // 调用 dou...
