UE5.1.1 C++从0开始(17.GAS游戏能力系统)

news2024/9/23 15:20:18

教程的链接:https://www.bilibili.com/video/BV1nU4y1X7iQ

教程内的老师没用GAS的插件,而是自己写了一个。这一篇文章只是开头,还有很多的内容没有往里面写。

新增了一个object类,新增了一个使用这个类的组件。然后把这个组件用在了我们的SCharacter上面,重构了我们的攻击功能。

新增类:SAction

SAction.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "SAction.generated.h"


//此处的UCLASS宏内需要填入Blueprintable,否则我们不能在创建蓝图的时候从所有类中找到我们的SAction类
UCLASS(Blueprintable)
class ACTIONROGUELIKE_API USAction : public UObject
{
	GENERATED_BODY()

public:
	//行为名字来开始或停止
	UPROPERTY(EditDefaultsOnly,Category="Action")
	FName ActionName;
	
    //宏内的BlueprintNativeEvent意味着蓝图可重写
	UFUNCTION(BlueprintNativeEvent,Category="Action")
	void StartAction(AActor* Instigator);

	UFUNCTION(BlueprintNativeEvent, Category = "Action")
	void StopAction(AActor* Instigator);

	class UWorld* GetWorld() const override;
};

SAction.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "SAction.h"
#include "Logging/LogMacros.h"

void USAction::StartAction_Implementation(AActor* Instigator)
{
	UE_LOG(LogTemp, Log, TEXT("Running: % s"), *GetNameSafe(this));
}

void USAction::StopAction_Implementation(AActor* Instigator)
{
	UE_LOG(LogTemp, Log, TEXT("Stopped: % s"), *GetNameSafe(this));
}

//重写的GetWorld()方法,防止子类调用GetWorld()方法出现错误
class UWorld* USAction::GetWorld() const
{
	UActorComponent* Comp = Cast<UActorComponent>(GetOuter());
	if (Comp)
	{
		return Comp->GetWorld();
	}
	return nullptr;
}

SActionComponent.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "SActionComponent.generated.h"

class USAction;

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class ACTIONROGUELIKE_API USActionComponent : public UActorComponent
{
	GENERATED_BODY()

protected:
    //行为列表
	UPROPERTY()
	TArray<USAction*> Actions;

    //蓝图内填写的行为列表
	UPROPERTY(EditAnywhere, Category = "Actions")
	TArray<TSubclassOf<USAction>> DefaultActions;

public:
    //增加行为函数
	UFUNCTION(BlueprintCallable,Category="Actions")
	void AddAction(TSubclassOf<USAction>ActionClass);

    //通过名字来获取Action类,然后执行Action
	UFUNCTION(BlueprintCallable, Category = "Actions")
	bool StartActionByName(AActor* Instigator, FName ActionName);

    //通过名字来获取Action类,然后停止Action
	UFUNCTION(BlueprintCallable, Category = "Actions")
	bool StopActionByName(AActor* Instigator, FName ActionName);

public:	
	// Sets default values for this component's properties
	USActionComponent();

protected:
	// Called when the game starts
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

		
};

SActionComponent.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "SActionComponent.h"
#include "SAction.h"

void USActionComponent::AddAction(TSubclassOf<USAction>ActionClass)
{
	if (!ensure(ActionClass))
	{
		return;
	}
	USAction* NewAction =  NewObject<USAction>(this,ActionClass);
	if (NewAction)
	{
		Actions.Add(NewAction);
	}
}

bool USActionComponent::StartActionByName(AActor* Instigator, FName ActionName)
{
	for (USAction *Action : Actions)
	{
		if (Action&&Action->ActionName==ActionName)
		{
            //调用开始事件
			Action->StartAction(Instigator);
			return true;
		}
	}
	return false;
}

bool USActionComponent::StopActionByName(AActor* Instigator, FName ActionName)
{
	for (USAction* Action : Actions)
	{
		if (Action && Action->ActionName == ActionName)
		{
            //调用停止事件
			Action->StopAction(Instigator);
			return true;
		}
	}
	return false;
}

// Sets default values for this component's properties
USActionComponent::USActionComponent()
{
	PrimaryComponentTick.bCanEverTick = true;
}


// Called when the game starts
void USActionComponent::BeginPlay()
{
	Super::BeginPlay();

    //将我们在蓝图内填入的Action类丢到我们的类列表内
	for (TSubclassOf<USAction> ActionClass : DefaultActions)
	{
		AddAction(ActionClass);
	}
	
}


// Called every frame
void USActionComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	
}


写完这两个类后,我们可以先进行一个尝试。我们会分别用蓝图和C++来演示如何使用Action这个类,以及如何运行这些Action类的子类。

首先是冲刺的功能,当我们摁住Shift的时候人物移速会加快,而我们放开Shift的时候人物移速会回到平常的样子。我们在蓝图内实现这个功能。以SAction为父类创建一个蓝图子类,在子类里面进行函数的重写。

请添加图片描述

请添加图片描述

蓝图内的默认增长值和事件名字如图所示。

同时我们给SCharacter增加一个事件按键绑定,这一步和教程不一样(因为UE5.1.1版本的缘故)我们需要按照先前的方法进行绑定,然后在绑定函数那里写上

请添加图片描述

然后进入蓝图编辑器的PlayerCharacter内的ActionComp内,在默认事件内把我们的蓝图给填上去。

在这些工作完成之后我们就实现了Shift进行加速的功能。之后便是把我们进行攻击的功能的函数给迁移到我们的Action内,用我们的ActionComp去实现它而不是在我们的SCharacter类里面实现它。

为此我们需要写一个SAction的子类,SAction_ProjectileAttack。

SAction_ProjectileAttack.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "SAction.h"
#include "SAction_ProjectileAttack.generated.h"

/**
 * 
 */
UCLASS()
class ACTIONROGUELIKE_API USAction_ProjectileAttack : public USAction
{
	GENERATED_BODY()

protected:
    //魔法子弹类
	UPROPERTY(EditAnywhere, Category="Attack")
	class TSubclassOf<AActor> ProjectileClass;

    //手部插槽名字
	UPROPERTY(VisibleAnywhere, Category="Effects")
	FName HandSocketName;

    //timer的delay时间长
	UPROPERTY(EditDefaultsOnly, Category="Attack")
	float AttackAnimDelay;

    //动画
	UPROPERTY(EditAnywhere, Category="Attack")
	class UAnimMontage* AttackAnim;

    //手部生成效果
	UPROPERTY(EditAnywhere, Category="Attack")
	UParticleSystem* CastingEffect;

    //timer结束时触发的函数
	UFUNCTION()
	void AttackDelay_Elapsed(ACharacter* InstigatorCharacter);
	
public:
    //开始事件函数重写
	virtual void StartAction_Implementation(AActor* Instigator) override;

	USAction_ProjectileAttack();
};

各位会发现,我们把部分SCharacter类里面的部分UPROPERTY给拿了过来,在Action内去进行使用。

SAction_ProjectileAttack.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "SAction_ProjectileAttack.h"
#include "GameFramework/Character.h"
#include "Kismet/GameplayStatics.h"
#include "SCharacter.h"

void USAction_ProjectileAttack::StartAction_Implementation(AActor* Instigator)
{
	Super::StartAction_Implementation(Instigator);

	ACharacter* Character = Cast<ACharacter>(Instigator);

	if (Character)
	{
		Character->PlayAnimMontage(AttackAnim);

		UGameplayStatics::SpawnEmitterAttached(CastingEffect, Character->GetMesh(), HandSocketName, FVector::ZeroVector, FRotator::ZeroRotator, EAttachLocation::SnapToTarget);

		FTimerHandle TimerHandel_AttackDelay;
		FTimerDelegate Delegate;

		Delegate.BindUFunction(this, "AttackDelay_Elapsed", Character);

		GetWorld()->GetTimerManager().SetTimer(TimerHandel_AttackDelay, Delegate, AttackAnimDelay, false);
	}

	


}

void USAction_ProjectileAttack::AttackDelay_Elapsed(ACharacter* InstigatorCharacter)
{
	if (ensureAlways(ProjectileClass))
	{
		FVector HandLocation = InstigatorCharacter->GetMesh()->GetSocketLocation(HandSocketName);

		//生成参数设置
		FActorSpawnParameters SpawnParams;
		SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
		SpawnParams.Instigator = InstigatorCharacter;

		//检测的球体半径设置
		FCollisionShape Shape;
		Shape.SetSphere(20.0f);

		//增加忽视碰撞的对象
		FCollisionQueryParams Params;
		Params.AddIgnoredActor(InstigatorCharacter);

		//增加检测的通道
		FCollisionObjectQueryParams ObjParams;
		ObjParams.AddObjectTypesToQuery(ECC_WorldDynamic);
		ObjParams.AddObjectTypesToQuery(ECC_WorldStatic);
		ObjParams.AddObjectTypesToQuery(ECC_Pawn);

		ASCharacter* PlayerCharacter = Cast<ASCharacter>(InstigatorCharacter);

		FVector TraceStart = PlayerCharacter->GetPawnViewLocation();

		FVector TraceEnd = TraceStart + (InstigatorCharacter->GetControlRotation().Vector() * 5000);

		FHitResult Hit;

		if (GetWorld()->SweepSingleByObjectType(Hit,TraceStart,TraceEnd,FQuat::Identity,ObjParams,Shape,Params))
		{
			TraceEnd = Hit.ImpactPoint;
		}

        //老师的求旋转方法
		FRotator ProjRotation = FRotationMatrix::MakeFromX(TraceEnd - HandLocation).Rotator();

        //我的求旋转方法
		//FRotator ProjRotation = (TraceEnd - HandLocation).Rotation();

		FTransform SpawnTM = FTransform(ProjRotation, HandLocation);
		GetWorld()->SpawnActor<AActor>(ProjectileClass, SpawnTM, SpawnParams);


	}

	StopAction(InstigatorCharacter);
}

USAction_ProjectileAttack::USAction_ProjectileAttack()
{
	AttackAnimDelay = 0.2f;
	HandSocketName = "Muzzle_01";
}

在我看视频的时候,发现这一部分我的思路和老师的思路大体相同但还是有些细节上是不太一样的(汗)我用的是LineTracting,而老师用的是ObjType,很难说哪个好哪个不好,我觉得更像是一个需求的两个不同实现罢了。包括后面的求Rotation的方法,我是直接进行一个减法,在我的概念内,Rotation实际上是一个方向,也就是一个向量,而向量的长短对我来说是无所谓的东西。而老师用的方法我去网上查看了下,然后看了下源码,大概理解是这样:FRotationMatrix中存放物体相对于世界坐标系的旋转角度信息,具体的我在源码内做个解析

TMatrix<T> TRotationMatrix<T>::MakeFromX(TVector<T> const& XAxis)
{
    //获得一个参数的副本
	TVector<T> const NewX = XAxis.GetSafeNormal();

	// try to use up if possible
    //UE_KINDA_SMALL_NUMBER的大小为1.e-4f就是0.0001f,如果Z方向上的向量小于0.9999那么UpVector赋前值,如果大于,那么赋后值
	TVector<T> const UpVector = (FMath::Abs(NewX.Z) < (1.f - UE_KINDA_SMALL_NUMBER)) ? TVector<T>(0, 0, 1.f) : TVector<T>(1.f, 0, 0);

    //UpVector ^ NewX 我对于^符号的理解是进行异或运算,但是我没想明白向量做异或运算实际上是怎么做的
	const TVector<T> NewY = (UpVector ^ NewX).GetSafeNormal();
	const TVector<T> NewZ = NewX ^ NewY;

    //返回一个矩阵
	return TMatrix<T>(NewX, NewY, NewZ, TVector<T>::ZeroVector);
}

然后调用Rotator()函数,将矩阵变成我们的Rotation进行赋值。

除此之外的内容我的方法与教程给出的方法大同小异,我的方法内会缺一个在进行发射动画的时候手部会生成一个附着的特效,这个是之前的作业,我在之前的文档里面写过没看懂作业要求,这里做个补充说明。

创建完子类之后,我们在蓝图编辑器内去新建我们的MagicProjectile的蓝图类,我这里只用我们的普通魔法子弹做示范:

请添加图片描述

我们填入这些参数之后,在我们的SCharacter类内部进行修改,我们可以将我们攻击相关的绝大多数函数都给删掉了,只保留下一个和我们按键绑定的那一个函数,然后在函数内部写上

请添加图片描述

然后我们就可以重现我们的攻击功能了!同时如果以后我们想要往里面增加更多的魔法子弹相关技能的话,都可以通过这个MagicProjectile去创建,不用像我们以前那样一个函数写很多遍了(其实也可以只写一遍,在我们已经写好的函数内增加一个魔法子弹类,然后按生成参数的方法去生成我们的魔法子弹,但是所有代码都加到SCharacter类内,会让我们的代码显得太过于冗长,对我们的后期维护不友好)。而且对于SCharacter来说,这些功能都是封装起来的,它只不过是调用而已,我们后期维护也更加方便了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/732422.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

text-to-3d方面的论文列表分享

以下给出几篇我个人觉得比较有价值的text-to-3d的论文列表&#xff0c;论文的超链接会连接到arxiv上。 DreamfusionFantasia3DTangoLatent-NeRFMagic-3dClip-ForgeClip-MeshDreamfieldAvatarCLIPPoint-EShape-EText2Mesh

P3804 【模板】后缀自动机(SAM)

题目描述 给定一个只包含小写字母的字符串 S。 请你求出 S 的所有出现次数不为 11 的子串的出现次数乘上该子串长度的最大值。 输入格式 一行一个仅包含小写字母的字符串 S。 输出格式 一个整数&#xff0c;为所求答案。 题解&#xff1a;这里就不讲后缀自动机的模板相关…

zigbee学习之DHT11温湿度传感器+zigbee无线通信

开发环境&#xff1a;IAR烧录器串口调试助手CC2530DHT11 两个模块&#xff1a;一个作为协调器&#xff0c;负责接收数据&#xff0c;一个作为终端&#xff0c;负责发送数据 步骤&#xff1a; 1、SampleApp.c里配引脚P0_6(查看硬件上的标识) 2、DTH11.c里配引脚 3、修改PANID和信…

超纯水抛光混床树脂的选择及工艺流程

一、什么是超纯水&#xff1f; 既将水中的导电介质几乎完全去除&#xff0c;又将水中不离解的胶体物质、气体及有机物均去除至很低程度的水。电阻率大于18MΩ*cm&#xff0c;或接近18.3MΩ*cm极限值。 超纯水是科技界为了研制超纯材料&#xff08;半导体原件材料、纳米精细陶…

服务器搭建oracle,并远程连接教程

下载两个压缩包&#xff0c;然后上传到服务器&#xff0c; 软件安装09&#xff1a;CentOS安装Oracle - 虚拟机 - 5997CK - 欢迎您! (hezhilin.online) 这里有全部步骤&#xff0c;反正过了几天我也会忘记&#xff0c;不赘述了。 直接上拆的坑&#xff1a; 开启服务器端口后…

借书问题-2022年全国青少年信息素养大赛Python国赛第3题

[导读]&#xff1a;超平老师计划推出《全国青少年信息素养大赛Python编程真题解析》50讲&#xff0c;这是超平老师解读Python编程挑战赛真题系列的第5讲。 全国青少年信息素养大赛&#xff08;原全国青少年电子信息智能创新大赛&#xff09;是“世界机器人大会青少年机器人设计…

9.33UEC++、容器

1.定义&#xff1a; 2.TArray&#xff1a;快&#xff0c;小&#xff0c;高 &#xff1a;同质容器 &#xff1a;创建栈对象&#xff0c;不能创建堆对象 如何构建TArray&#xff1a; 获取方式&#xff1a; 实现方式&#xff1a;定义一个array容器数组&#xff0c;两种遍历方式…

跳跃游戏(力扣)贪心 JAVA

给定一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。 数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标。 示例 1&#xff1a; 输入&#xff1a;nums [2,3,1,1,4] 输出&#xff1a;true 解释&#xff1a;可以先跳 1 步…

【实用工具】(BeyondCompare+CppCheck对某次更新的文件进行静态检查)按文件清单复制文件到指定文件夹的辅助工具【2023.07.07】

摘要 开发一个功能提交了多次代码&#xff0c;现在需要比较最终的提交和某次提交的差异并进行静态代码检查。用到了BeyondCompare和CppCheck软件。但是有个问题&#xff0c;BeyondCompare不能把差异项导出&#xff0c;于是乎花了两个小时写了个辅助工具。 使用说明 第一步&…

校服选购定制小程序开发制作功能介绍

本次为各大校服服装定制生产老板介绍一下校服定制商城小程序系统的功能&#xff0c;为大家做参考。 校服定制小程序主要功能有&#xff1a; 1、每个学校校服定制信息独立&#xff0c;不同的学校打开小程序里面信息可以不同。 2、学校致家长的一封信。 3、学生信息录入和下单信息…

【VUE】项目设置超时6分钟,实际1分钟就超时了

一、背景&#xff1a;VUE项目中&#xff0c;前端接口请求设置6分钟&#xff0c;但实际在浏览器中1分钟就超时了 timeout: 6 * 60 * 1000二、经查&#xff1a;不是前端代码设置的问题&#xff0c;也不是浏览器的问题&#xff0c;而是nginx超时设置默认为60S&#xff0c;导致1分…

hexo个人博客搭建(二)butterfly主题配置

Butterfly主题安装文档(二)之主题配置 一、回顾安装butterfly主题 1、在hexo项目根目录下执行操作clone主题 git clone -b master https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly2、如果沒有 pug 以及 stylus 的渲染器&#xff0c;还需要下载&…

matlab语言的前世今生

matlab语言的算法库是用什么语言实现的? Matlab的算法库是用C和C++语言实现的。 Matlab本身是一个解释型的高级编程语言,但它的核心算法库是使用更底层的C和C++语言编写的。这些库提供了许多数值计算、统计分析、信号处理、图像处理、优化等常用算法函数,并以mex文件的形式集…

投票评选活动小程序v2-搜索功能和最新排序功能实现

投票评选活动小程序-搜索功能和最新排序功能实现 优秀人物评选活动&#xff0c;五四奖章评选、优秀工作者人物评选、劳动最光荣评选。通常需要一个搜索功能&#xff0c;用户通过输入名称或编号搜索对应的作品项目或者人物&#xff0c;进行投票。或者通过最新排序功能查看列表情…

pytorch超详细安装教程,Anaconda、PyTorch和PyCharm整套安装流程

本文介绍基于Anaconda环境以及PyCharm软件结合&#xff0c;安装PyTorch深度学习框架。 PyTorch深度学习框架详细安装教程 一、anaconda安装&#xff08;一&#xff09;下载&#xff08;二&#xff09;安装&#xff08;三&#xff09;配置环境变量&#xff08;四&#xff09;检查…

虚假人脸检测实验

虚假人脸检测实验 虚假人脸识别 数据集链接 链接&#xff1a;https://pan.baidu.com/s/1hDyJ91dAwI5j5GTR0hD_cA?pwd4cki 原理 ResNet-18是一种经典的CNN网络&#xff0c;是 Deep Residual Learning 团队在 2017 年提出的。它是为了解决 ImageNet 数据集上的图像分类任务而…

会话机制【Cookie 和 Session】,登陆页面的模拟实现

前言 小亭子正在努力的学习编程&#xff0c;接下来将开启JavaEE的学习~~ 分享的文章都是学习的笔记和感悟&#xff0c;如有不妥之处希望大佬们批评指正~~ 同时如果本文对你有帮助的话&#xff0c;烦请点赞关注支持一波, 感激不尽~~ 目录 前言 Cookie 和 Session 是什么 Cookie…

【计算机网络】计算机网络概述

&#x1f525; 本文由 程序喵正在路上 原创&#xff0c;CSDN首发&#xff01; &#x1f496; 系列专栏&#xff1a;计算机网络 &#x1f320; 首发时间&#xff1a;2023年7月8日 &#x1f98b; 欢迎关注&#x1f5b1;点赞&#x1f44d;收藏&#x1f31f;留言&#x1f43e; &…

基于Spring Boot的社区适龄青年征兵系统设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的社区适龄青年征兵系统设计与实现&#xff08;Javaspring bootMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 后端&#xff1a;Java springboot框架 …

BOM操作

JavaScript组成 BOM 浏览器对象模型 window对象 是一个全局对象&#xff0c;也就是JavaScript中的顶级对象 像document&#xff0c;alert() console.log() 都是window对象的属性&#xff0c; 基本的BOM的属性和方法都属于window对象 通过var定义在全局作用域中的变量&#x…