TalesRunner超级跑跑UE复刻版(抄袭版)构建过程

最近要做一个Demo, 思来想去不知道做什么类型的好.
突然想到了初中时代玩的一款游戏超级跑跑.
一来游戏比较简单, 需要的资产也相对容易找到. 敲定目标之后, 就开始着手准备了.

本文章主要用来记录自己整个Demo的设计过程.

游戏类型: 多人竞速网络游戏

GitHub Repository: https://github.com/D1anHua/D_TalesRunner_Replica

思维导图

在游戏开始设计之前, 我打算先分析一下所要实现的Demo的最终效果.
主要从功能、架构两方面出发.

功能列表

分析本游戏所需要实现的功能思维导图, 方便后续各部分设计.

架构设计

分析该游戏为了实现上述功能, 应该在架构方面如何布置和设置:

MultiPlayer设计

服务器类型: 这个游戏是多人联机竞速游戏, 因此选择Dedicated Server.

Authentication Functions

UE Doucmentation: https://docs.unrealengine.com/5.1/en-US/online-subsystem-identity-interface-in-unreal-engine/

FrontendState

Lyra使用了FControlFlow来进行自己的登录前的设置. 具体流程如下:

: 这里Lyra使用的是其自建的CommonGame插件.
该插件其主要就是添加一个使用 CommonUIActivatable Widget Containers作为“图层”的系统,并提供将小部件推送到某些图层的功能。
由于我自己搭建的网络架构名字取得和Lyra的不一样, 因此这里就不使用这个插件了.

LyraExperience模块解读.

Lyra添加了新的Experience模块, 其官方包括网上的一些资料都介绍说它是GameMode的高级版本. 今天, 将首先从LyraExperience这个模块来讲解这个项目.

注:个人也是UE新手, 如果文章中有错误, 希望大家可以批评指出, 我会尽量改正, 避免给大家造成误解.

(PS: 纯看这个项目的话, 确实感觉可以从LyraExperience这个模块开始看, 但是我个人感觉这个项目在各个模块之间的耦合度、以及对Tag && Assets Data的使用超乎我的想象. 要是想要重新复刻这个项目的话, 我建议从Character && Modular Gameplay看起)

先说一些碎碎念吧: 我本身并不是UE老手, 最近想做一个DEMO项目好让自己在求职季多点姿色. 当时想通过学习Lyra项目来学习一些Epic官方的代码风格, 以及学习一下他们是如何构建项目的.

结果不出所料地是, 我完全低估了Lyra项目的难度, 结果不可避免的让自己的Demo没有办法按期完成了(🤨), 让自己当初的想法显得天真了一些.

不过必须要说的是: 虽然自己的Demo是要错过暑期实习的时间了, 不过秋招还是可以努努力的. 另外就是, lyra这个项目还是可以让人学到很多东西的.

剩下的就不多念叨了. 提前感谢大家的观看了.

我的参考链接放在开头, 因为我感觉我自己的文章本身出自别人, 并且自己写的质量也不一定有别人高. 感兴趣的可以直接看别人的文章呀.
1. 知乎---UE5 新项目Gameplay框架设计(以Lyra为例)
2. 知乎---UE5 Lyra的数据驱动之数据加载与引用
3. 知乎---《InsideUE5》GameFeatures架构(一)发展由来
4. Youtube---Modular Game Features in UE5: plug ‘n play, the Unreal way
5. 官方文档: Game Featrues and Modular Gameplay

What is Experience

我个人的理解的话, 故名思意, 就可以理解为是一种游玩的体验, 不用的Experience可能就是不同的游戏风格, 其中包含有角色相关的、控制映射相关的、也有和功能(GameFeature)相关的. 至于它和以前的Gamemode的区别以及优势. 我个人其实对Gamemode的理解也并不深刻. 所以我也就不在这里大放厥词了. 具体的功能以及方便与否还是由个人自己区分的(或者有大佬愿意讲讲哈哈哈)

注: 在介绍之前, 方便的同学可以先看看参考链接4或者5以简单的了解一下GameFeatrue. GameFeatrueLyra中也是深度集成的.

不了解也行, 就知道它是一种在不影响游戏原有Component运行过程之外, 所添加的一些额外的功能.
这里我简单举例就像加入我现在只有一个能行走的角色. 我可以将其他任何的逻辑都通过GameFeatrue在不影响游戏本身以及各个部件之间运行的前提下去增加诸如UI、新的功能等等, 可以丰富这个游戏.(大钊老师的举例很形象. 往主板上拓展的思路)

LyraExperienceDefinition

ExperienceDefinition定义如下:

在UE5中展示如下: 其设置位置在World Setting-->GameMode下:

注: 这里设置的位置其实是Lyra继承了AWorldSettings(可以在以后的工作中也尝试尝试):

1
2
3
4
class LYRAGAME_API ALyraWorldSettings : public AWorldSettings{
UPROPERTY(EditDefaultsOnly, Category=GameMode)
TSoftClassPtr<ULyraExperienceDefinition> DefaultGameplayExperience;
... }

Experience总共由四个成员变量组成, 其中除了第一个之外全是Lyra的自定义类型. 这里我简单介绍一下, 有个概念就行.(这四部分就是整个Experience所提供的服务的全部了, 可以说):
1. GameFeaturesToEnable: 将要使用的是哪一系列的GameFeatures. 2.

LyraModularGameplay && ModularGameplayActor && Enhanced Input(Tag)模块解读

Lyra项目在整个Character的构建过程中, 都运用了ModularGameplay这个插件. 刚入手一个项目的第一步, 我个人觉得还是应该先看看Lyra角色是怎么设置的.

本文章主要包含Lyra的以下几个部分:
1. ModularGameplay
2. Enhanced Input Mapping(同Tag绑定)
3. LyraPawnExtensionComp && LyraHeroComp 之间的Initialization State System.
本部分不会涉及GAS系统相关的部分, GAS会放后面讲.

注:我本人也是UE学习的新手, 对基础概念掌握并不深, 如果有分析错误的地方, 还请大家批评指正, 也避免误导大家.

参考链接如下:
1. 官方文档: Game Framework Component Manager

ModularGameplayActor

Lyra插件目录中有一个插件:ModularGameplayActor, 其中包含了常用的Actor的MudoularGameplay的实现, 为在参考链接1中的第一部分Extension Handler System提供了默认的实现.

在每一个ModularGameplayActor的实现中, 最主要的在PreInitalizeComponents()中注册成为了Reveiver, 并在EndPlay()RemoveReceiver.

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
// ModularCharacter.cpp
void AModularCharacter::PreInitializeComponents()
{
Super::PreInitializeComponents();
UGameFrameworkComponentManager::AddGameFrameworkComponentReceiver(this);
}
void AModularCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
UGameFrameworkComponentManager::RemoveGameFrameworkComponentReceiver(this);
Super::EndPlay(EndPlayReason);
}
void AModularCharacter::BeginPlay()
{
//! 注册了时间名为NAME_GameActorReady的Default Event(这个事件声明在GameFrameworkComponentManager.h的头文件中), 方便其他插件使用. 比如做同步控制时
UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(this, UGameFrameworkComponentManager::NAME_GameActorReady);
Super::BeginPlay();
}

// ModularGameMode.cpp
AModularGameMode::AModularGameMode(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
GameStateClass = AModularGameState::StaticClass();
PlayerControllerClass = AModularPlayerController::StaticClass();
PlayerStateClass = AModularPlayerState::StaticClass();
DefaultPawnClass = AModularPawn::StaticClass();
}

以后可以直接继承这个插件来实现自己GameFeaturePlugins的实现.

Input System

LyraInput System可与GASGameplayTag以及不同的控制器协调工作, 其在EnhancedInput的基础上进行了封装.

Input System Data Stucture

下面放一张正常的使用Enhanced Input System的代码截图, 从代码中可以看出来, 关于Enhanced Input System主要有两部分:

  • InputMappingContext's Bind
  • InputActor's Bind

因此呢, 对应的Lyra分别定义了这两部分的数据结构.

首先是InputMappingContext, Lyra中直接使用了UPlayerMappableInputConfig, 其中维护了一个含有InputMappingContext的Map.

Lyra系统为这个Config封装了CommonInputType(这个枚举类型用来说明所使用的控制器的类型)信息. 并通过HeroComponent来设置.

而另一方面InputActions则对齐封装了GameplayTag信息形成了LyraInputAction. 并根据其时候是Ability而分为了NativeAbility之分. 并通过PawnData进行设置. (注: 这里的PawnData并不是简单的在PawnExtensionComp中进行设置的, 在Lyra其是在Experience的初始化过程中由GameState完成的)