斯坦福 UE4 C++ ActionRoguelike游戏实例教程 15.创建持续效果BUFF
斯坦福课程 UE4 C++ ActionRoguelike游戏实例教程 0.绪论
概述
本篇文章对应Lecture 18 – Creating Buffs, World Interaction, 71、72节。将会基于之前实现的SurAction能力系统,教你如何定义和创建拥有持续效果的BUFF,例如许多游戏常见的灼烧、中毒效果。
目录
- 分析
- 创建BUFF基类
- 新增燃烧BUFF
- 子弹附魔
- 总结
需求分析
不得不说,持续BUFF是所有RPG游戏中十分重要的一个设定。常见的BUFF有中毒、烧伤等持续掉血的负面BUFF,同样也有速度提升、攻击力提升等正面BUFF。除此以外,我们还可以使用BUFF为角色附加各种标签,比如如果敌方被标记为“易伤”,那么所有攻击对于他都会得到一个增幅的效果。那么这些丰富多彩的BUFF是如何实现的呢?本篇文章将会基于前几节课实现的Action能力系统,构建一个基础的BUFF系统。
首先让我们拆解一下持续型的BUFF需要拥有哪些特点:
- 拥有一个持续时间
- 在持续时间内,每隔一段时间触发一次效果
- 在拥有BUFF的时候马上触发
- 持续时间结束后自动销毁。
OK,目前这些就足够了,具体该如何实现,让我们边做边说。
创建BUFF基类
这里才是基类。我们将其命名为SurActionEffect
,继承于USurAction。
值得一提的是,我们在USurAction新增了一个bool成员变量bAutoStart
,将其暴露为public,当一个Action被创建并添加的时候,可以根据这个变量来判断是否自动开始。
另外,我们重载了StartAction函数,在能力开始的时候,会自动调用定时器,以保证BUFF能自动结束和触发效果。
同理,重载了StopAction函数,当BUFF的持续时间结束时,会自动调用StopAction。注意一下,在调用父类的StopAction之前,会先判断当前是否有周期效果要触发。因为周期触发效果可能会用到Action里面的各种tag,因此我们希望先触发周期效果,再调用父类的StopAction。这里用到了KINDA_SMALL_NUMBER宏来判断是否同时触发。
//.h
class FPSPROJECT_API USurActionEffect : public USurAction
{
GENERATED_BODY()
public:
USurActionEffect();
void StartAction_Implementation(AActor* Instigator) override;
void StopAction_Implementation(AActor* Instigator) override;
protected:
//BUFF的持续时间。我们不希望在蓝图中修改持续时间,因此使用BlueprintReadOnly
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Effect")
float Duration;
//触发BUFF的周期
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Effect")
float Period;
FTimerHandle PeriodHandle;
FTimerHandle DurationHandle;
// BUFF效果,在周期定时器触发时执行
UFUNCTION(BlueprintNativeEvent, Category = "Effect")
void ExecutePeriodEffect(AActor* Instigator);
};
//.cpp
USurActionEffect::USurActionEffect()
{
bAutoStart = true;
}
void USurActionEffect::StartAction_Implementation(AActor* Instigator)
{
Super::StartAction_Implementation(Instigator);
//启动计时器
if(Duration > 0.f)
{
FTimerDelegate Delegate;
Delegate.BindUFunction(this, "StopAction", Instigator);
GetWorld()->GetTimerManager().SetTimer(DurationHandle, Delegate, Duration, false);
}
if(Period > 0.f)
{
FTimerDelegate Delegate;
Delegate.BindUFunction(this, "ExecutePeriodEffect", Instigator);
GetWorld()->GetTimerManager().SetTimer(PeriodHandle, Delegate, Period, true);
}
}
void USurActionEffect::StopAction_Implementation(AActor* Instigator)
{
//检查周期效果是否会同时触发,如果时间近乎一样,那么先触发周期效果,再停止Action
if(GetWorld()->GetTimerManager().GetTimerRemaining(PeriodHandle) < KINDA_SMALL_NUMBER)
{
ExecutePeriodEffect(Instigator);
}
Super::StopAction_Implementation(Instigator);
GetWorld()->GetTimerManager().ClearTimer(DurationHandle);
GetWorld()->GetTimerManager().ClearTimer(PeriodHandle);
USurActionComponent* Comp = GetOwningComponent();
if(Comp)
{
Comp->RemoveAction(this);
}
}
当然,需要为ActionComponent作出相应的修改。
为了让BUFF能够被移除,这里我们为ActionComponent增加一个移除Action的功能。
void USurActionComponent::RemoveAction(USurAction* ActionToRemove)
{
if(ensure(ActionToRemove && !ActionToRemove->IsRunning()))
{
return ;
}
Actions.Remove(ActionToRemove);
}
顺带一提,得亏UE4那魔法一般的垃圾回收机制,才得以让我们不用担心各种内存泄漏的问题。在这段函数的Actions.Remove(ActionToRemove)
仅仅只会将数组中的指针给删除掉,指针所指向的内存空间并不会被销毁。笔者以前也深受C++内存管理的苦痛已久,通常这种情况会导致严重的内存泄漏,因为往往这时候我们就永远失去了访问这段内存空间的方式。所幸UE实现了C++下的垃圾回收机制,当检测到没有指针指向这段内存空间时,UE会自动释放这段内存空间。
虽然使用UObject并不需要担心内存泄漏的问题,笔者仍不希望在习惯于UE的便利性中逐渐失去对内存管理的敏感度,因为作为开发者,我们仍然有可能不小心写出了拥有内存安全问题的代码,请务必小心小心再小心。
我们还修改了AddAction的参数列表。引进了BUFF系统后,由于BUFF很有可能是其他角色为你施加的,因此需要为AddAction添加一个Instigator参数。
void USurActionComponent::AddAction(AActor* Instigator, TSubclassOf<USurAction> ActionClass)
{
if(!ensure(ActionClass))
{
return;
}
USurAction* NewAction = NewObject<USurAction>(this, ActionClass);
if(ensure(NewAction))
{
Actions.Add(NewAction);
if(NewAction->bAutoStart && NewAction->CanStart(Instigator))
{
NewAction->StartAction(Instigator);
}
}
}
当然,其他用到了AddAction的地方也应该做适当的修改。
新增燃烧BUFF
新建一个继承于SurActionEffect的蓝图类,我们将其取名为Effect_Buring,我们将在蓝图中实现这个新的BUFF。
在我们的设想中,燃烧BUFF应该每隔一段时间就给予BUFF的拥有者一定伤害,直到持续时间结束。因此我们需要为其定义伤害(DamageAmount)属性。
接着修改蓝图。重载ExecutePeriodEffect函数,这是我们定义的周期效果函数,每隔一定时间(Period)就会触发一次。在重载的函数中,我们调用定义在GamePlayFunctionLibrary的ApplyDamage函数,如图所示
值得一提的是,UE中自带了一个ApplyDamage函数,好巧不巧我们的函数与其同名,使用的时候请注意这个问题。为了规避这个问题,我们可以使用元描述符(Category)来修改ApplyDamage的类别,实在不行换个名字也成。
简单修改一下燃烧BUFF的默认值。持续时间5s,每隔1s给予BUFF的拥有者1点伤害。
值得一提的是,这里我们为燃烧BUFF添加了一个Status.Buring标签,这意味着BUFF被添加到角色身上时,会为角色加上一个Status.Buring的标签。我们可以通过检测这个标签的方式,为角色加上燃烧的特效,或者添加类似遇水蒸发的效果,任君想象。
为子弹附魔「火焰附加」
没什么复杂的东西,实际上就在在子弹击中敌人并成功判定后,获取敌人身上的Action组件,并为其添加指定的BUFF即可。
为此,我们需要为子弹添加一个EffectActionClass成员。
void ASurMagicProjectile::OnOverlapBegin(UPrimitiveComponent* HitComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
...
if(USurGameplayFunctionLibrary::ApplyDirectionalDamage(GetInstigator(), OtherActor, DamageAmount, SweepResult))
{
Explode();
//附加燃烧效果
if(ActionComp)
{
ActionComp->AddAction(GetInstigator(), EffectActionClass);
}
}
}
}
进入游戏看看效果,当我们击中敌人后,敌人身上会不断冒出-1的数字,表示我们成功为敌人加上了燃烧的BUFF。
还有一个有趣的点,当前设置的燃烧BUFF是可以在短时间内无限叠加的。也就是说,如果角色在短时间内收到了10次子弹攻击,那么他身上就会带有十个燃烧BUFF,意味着他每秒钟会受到10点伤害。有时候我们希望角色只能受到一层BUFF,至于怎么修改,就当是我留给读者的课后作业吧(
总结
本篇文章我们创建了SurActionEffect类作为各种BUFF的基类,他拥有持续触发效果的功能,能够自动开始执行,并自动移除自身在ActionComponent的引用。
之后我们以此基类构建了燃烧BUFF,并为我们的魔发子弹添加了燃烧“附魔”,以此抛砖引玉,希望能够激发大家的想象力。
斯坦福 UE4 C++ ActionRoguelike游戏实例教程 15.创建持续效果BUFF的更多相关文章
- Linux find命令实例教程 15个find命令用法
除了在一个目录结构下查找文件这种基本的操作,你还可以用find命令实现一些实用的操作,使你的命令行之旅更加简易.本文将介绍15种无论是于新手还是老鸟都非常有用的Linux find命令.首先,在你的h ...
- Cocos2d-x3.0游戏实例《不要救我》第十篇(结束)——使用Json配置数据类型的怪物
如今我们有2种类型的怪物,并且创建的时候是写死在代码里的,这是要作死的节奏~ 所以.必须可配置.不然会累死人的. ; i < size; ++i) { int id = root[i][&quo ...
- Cocos2d-x3.0游戏实例之《别救我》第八篇——TiledMap实现关卡编辑器
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/musicvs/article/details/25368273 好吧.我真心全然搞不懂.我如今仅仅只 ...
- 分享本年度最佳的15个 Photoshop 实例教程
毫无疑问,Photoshop 是任何其类型的设计相关工作的最佳工具.有这么多东西,你可以用它来设计,发挥你的想象力,一切皆有可能. 现在,几乎所有的封面图像都会用 Photoshop 来修饰. 您可能 ...
- MySQL7.5.15数据库配置主从服务器实现双机热备实例教程
环境说明 程序在:Web服务器192.168.0.57上面 数据库在:MySQL服务器192.168.0.67上面 实现目的:增加一台MySQL备份服务器(192.168.0.68),做为MySQL服 ...
- MySQL5.7.15数据库配置主从服务器实现双机热备实例教程
环境说明 程序在:Web服务器192.168.0.57上面 数据库在:MySQL服务器192.168.0.67上面 实现目的:增加一台MySQL备份服务器(192.168.0.68),做为MySQL服 ...
- 《Genesis-3D开源游戏引擎完整实例教程-2D射击游戏篇:简介及目录》(附上完整工程文件)
G-3D引擎2D射击类游戏制作教程 游戏类型: 打飞机游戏属于射击类游戏中的一种,可以划分为卷轴射击类游戏. 视觉表现类型为:2D 框架简介: Genesis-3D引擎不仅为开发者提供一个3D游戏制作 ...
- 值得 Web 开发人员收藏的20个 HTML5 实例教程
当开始学习如何创建 Web 应用程序或网站的时候,最流行的建议之一就是阅读教程,并付诸实践.也有大量的 Web 开发的书,但光有理论没有实际行动是无用的.现在由于网络的发展,我们有很多的工具可以用于创 ...
- Unity-2017.3官方实例教程Space-Shooter(一)
由于初学Unity,写下此文作为笔记,文中难免会有疏漏,不当之处还望指正. Unity-2017.3官方实例教程Space-Shooter(二) 章节列表: 一.从Asset Store中下载资源并导 ...
- 3Ds Max实例教程-制作女战士全过程
3Ds Max制作“女战神” 作者:Diego Rodríguez 使用软件:3Ds Max,Photoshop 3Ds Max下载:http://wm.makeding.com/iclk/?zone ...
随机推荐
- Django框架——Web应用、基于SOCKET写一个web应用、 手撸简单web框架、http协议、Web框架(手撸自己的Web框架)、django简介以及简单使用
文章目录 1 Web应用 一 Web应用程序是什么 1.1 Web应用程序的优点 1.2 Web应用程序的缺点 1.3 B/S架构优点 二 基于SOCKET写一个web应用 2.1 main.py 2 ...
- DDMS查看Threads情况
有时候程序运行出现死锁或者信号量卡死是很纠结的问题,单看代码很难分析定位问题,这时候可以借助DDMS来查看threads的运行情况,一目了然. 手机连接上USB,确保adb连通,然后启动Eclipse ...
- Sum of MSLCM 题解
Sum of MSLCM 题目大意 定义 \(\text{MSLCM}(n)\) 为所有满足该数集的 \(\text{lcm}\) 为 \(n\) 的数集中元素个数最多的数集的所有数字的和,现有多次询 ...
- 如何调用Metabase开放API
简介: Metabase是什么? 在传统企业的数据可视化业务中,通常需要从需求到审批,再到安排开发人员和排期,还要开发人员撰写代码最后再做导出.流程繁琐,参与的人员也多,往往需要几天甚至几周的时间! ...
- 18.2 使用NPCAP库抓取数据包
NPCAP 库是一种用于在Windows平台上进行网络数据包捕获和分析的库.它是WinPcap库的一个分支,由Nmap开发团队开发,并在Nmap软件中使用.与WinPcap一样,NPCAP库提供了一些 ...
- 一次完整的Http请求过程(转)
一次完整的Http请求过程 在网上看了很多关于http完整流程的介绍文档,都讲的很不错,但是还是各有缺失,所以自己就根据学习及理解整理了一张图,给大家分享下http一次完整的交互流程,只是大概画了下流 ...
- 潜在威胁信息模型(PTIM)-Potential threats Information Modeling
前言 这只是一位学识浅薄博主的一个突然想法,还望各位专业领域的专家教授轻怼 潜在威胁信息模型 目前的想法是通过全城摄像头建立城市的潜在威胁信息模型,这个潜在威胁可以包括:天气灾害(冰雹.雾霾能见度等) ...
- Modbus 转PROFINET 网关 TS-180在级联通讯中的应用
一.硬件连接 TS-180 具有冗余网口功能,用户可以通过级联方式连接来进行通讯,其他资料可参考说明书.将西门子 S7-300 PLC 通过网线与5台 TS-180 串联,用户可以选择下列两种连接方式 ...
- 跨境 ERP 积加系统与金蝶云星空系统数据集成对接方案
方案简介 由于积加ERP 系统专注服务于亚马逊跨境电商是非常复杂和庞大的应用系统,具有非常丰富的业务流程.复杂的系统架构和服务接口.主要涉及系统解决店铺运营,店铺管理,供应链管理,协同智能补货.采用& ...
- 初探webpack之单应用多端构建
初探webpack之单应用多端构建 在现代化前端开发中,我们可以借助构建工具来简化很多工作,单应用多端构建就是其中应用比较广泛的方案,webpack中提供了loader与plugin来给予开发者非常大 ...