UE5.1.1C++从0开始(6.两个额外的魔法弹:瞬移魔法弹和黑洞魔法弹)

news2024/11/28 5:32:42

做完这两个功能总共花费了一个下午加一个晚上的时间,瞬移魔法弹难度较低,黑洞魔法可能我的理解有误导致消耗时间较长,我会在下面把踩的坑写出来。

加上这个作业,我们一共做了三个魔法子弹了。同时那个老师也说我们可以写一个父类出来,我这里就写了个父类,然后更改了魔法子弹(最初的那个子弹)的父类,让它继承我们写的一个父类。

父类的代码如下:

父类代码与最初的那个魔法子弹并没有什么区别,最多就是我把一些之前在蓝图上实现的功能通过C++实现了出来,我会在代码中用中文注释标出我哪里使用了C++的代码来实现教程内的蓝图。

SFatherMagicProjectile.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SFatherMagicProjectile.generated.h"

UCLASS()
class ACTIONROGUELIKE_API ASFatherMagicProjectile : public AActor
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere)
	class USphereComponent * SphereComp;

	UPROPERTY(EditAnywhere)
	class UProjectileMovementComponent * MovementComp;

	UPROPERTY(EditAnywhere)
	class UParticleSystemComponent * ParticleComp;
	
public:	
	// Sets default values for this actor's properties
	ASFatherMagicProjectile();

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

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	UFUNCTION()
	virtual void OnComponentHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, FHitResult Hit);

};

SFatherMagicProjectile.cpp

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


#include "SFatherMagicProjectile.h"
#include "Components/SphereComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Particles/ParticleSystemComponent.h"

// Sets default values
ASFatherMagicProjectile::ASFatherMagicProjectile()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	FScriptDelegate OnHit;
	OnHit.BindUFunction(this, STATIC_FUNCTION_FNAME(TEXT("ASFatherMagicProjectile::OnComponentHit")));

	SphereComp = CreateDefaultSubobject<USphereComponent>(TEXT("Sphere Comp"));
	SphereComp->SetCollisionProfileName("Projectile");
	SphereComp->SetSphereRadius(10.0f, true);
	SphereComp->OnComponentHit.Add(OnHit);
	RootComponent = SphereComp;

	MovementComp = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("Movement Comp"));
	MovementComp->InitialSpeed = 1000.0f;
	MovementComp->bRotationFollowsVelocity = true;
	MovementComp->bInitialVelocityInLocalSpace = true;
	MovementComp->ProjectileGravityScale = 0.0f;

	ParticleComp = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("Particle Comp"));
	ParticleComp->SetupAttachment(SphereComp);

}

// Called when the game starts or when spawned
//此处是beginplay事件,我们在蓝图内最开始判断是否忽略instigator,此处做相同处理,先获取instigator对象,然后进行忽略
void ASFatherMagicProjectile::BeginPlay()
{
	Super::BeginPlay();

	APawn* Instigator_01 = AActor::GetInstigator();

	SphereComp->IgnoreActorWhenMoving(Instigator_01, true);

	
	
}

// Called every frame
void ASFatherMagicProjectile::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

//此处处理是判断是否有除了instigator以外的碰撞产生,如果有,那么就消除自身
void ASFatherMagicProjectile::OnComponentHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, FHitResult Hit)
{
	APawn* Instigator_01 = AActor::GetInstigator();

	if (Instigator_01!=OtherActor)
	{
		GetWorld()->DestroyActor(this);
		DrawDebugSphere(GetWorld(), Hit.ImpactPoint, 10.0f, 16, FColor::Red, false, 2.0f, 0U, 1.0f);
	}
}




可以发现,这里的代码基本上和我们在MagicProjectile上写的一致,所以当我们的MagicProjectile将父类修改成这个的时候,就不需要写任何的代码了。

更改后的SMagicProjectile代码如下:

SMagicProjectile.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SFatherMagicProjectile.h"
#include "SMagicProjectTile.generated.h"

UCLASS()
class ACTIONROGUELIKE_API ASMagicProjectTile : public ASFatherMagicProjectile//此处更改父类
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ASMagicProjectTile();

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

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	
};

SMagicProjectile.cpp

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


#include "SMagicProjectTile.h"
#include "Components/SphereComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Particles/ParticleSystemComponent.h"

// Sets default values
ASMagicProjectTile::ASMagicProjectTile()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;



}

// Called when the game starts or when spawned
void ASMagicProjectTile::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void ASMagicProjectTile::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}




瞬移魔法弹:难度较低,主要是time Handel的使用以及如何去移动我们的人物的问题。还记得我们前面在生成魔法子弹对象的时候么?我们是不是设置了子弹的instigator这个属性?那么我们就可以通过这个属性获取到我们的主角,最后通过setActorLocation()这个函数实现人物位置的变化。

代码如下:

SDashProjectile.h

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

#pragma once

#include "CoreMinimal.h"
#include "SFatherMagicProjectile.h"
#include "SDashProjectile.generated.h"

/**
 * 
 */
UCLASS()
class ACTIONROGUELIKE_API ASDashProjectile : public ASFatherMagicProjectile
{
	GENERATED_BODY()

        //定义一个timerhandle
protected:
	FTimerHandle TimerHandle_Dash;

public:
	ASDashProjectile();

protected:
	virtual void BeginPlay()override;

	//重写我们的受击函数
	void OnComponentHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, FHitResult Hit) ;
	//新写一个时间到了要触发的函数
	UFUNCTION()
	void TimeElasped();
};

SDashProjectile.cpp

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


#include "SDashProjectile.h"

ASDashProjectile::ASDashProjectile()
{
	PrimaryActorTick.bCanEverTick = true;


}

void ASDashProjectile::BeginPlay()
{
	Super::BeginPlay();
    
    //生成这个类的对象的一瞬间,我们就开始计时,等到了两秒之后就调用TimeElasped()函数
	GetWorldTimerManager().SetTimer(TimerHandle_Dash, this, &ASDashProjectile::TimeElasped,2.0f);
}

void ASDashProjectile::OnComponentHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, FHitResult Hit)
{
    //我们还是需要父类的代码,毕竟当子弹打到物体的时候是需要消失的
	Super::OnComponentHit(HitComponent, OtherActor, OtherComp, NormalImpulse, Hit);
	//一旦在倒计时结束之间打到了物体,那么这个倒计时就没用了,我们就需要把这个倒计时给删了,也就是停止倒计时
	GetWorldTimerManager().ClearTimer(TimerHandle_Dash);
	//停止倒计时之后,我们需要设置玩家的位置,也就是平常所说的瞬移
	this->GetInstigator()->SetActorLocation(Hit.ImpactPoint);
}

//这个函数是当两秒到了之后调用的函数
void ASDashProjectile::TimeElasped()
{
    //进行瞬移操作
	this->GetInstigator()->SetActorLocation(this->GetActorLocation());
    //删除自身
	GetWorld()->DestroyActor(this);
}

写到这里我并没有遇到什么太大的问题,毕竟所用到的知识教程里面都提到过了。

后面的黑洞魔法弹的实现功能,我遇到了点问题,我最开始的思路是在子弹上面增加一个径向力组件,然后每帧都fire Impulse一次,但是实际上的表现非常糟糕,于是我去ue官方的文档去查是否有什么函数能持续给模拟物理的组件施加一个持续的力的函数(提一嘴,UE的中文区的文档写的非常糟糕,一直都是这样,不知道中文区的人们每天在忙什么。。。b站的官号有些视频是比较好的,查东西的优先级我是:b站>CSDN>UE文档,不过这个教程的纯代码的分享我没在CSDN上面看到,有一个哥们写了这个功能,但是他没放代码。。。)最后找到了一个函数AddRadioForce()这个函数并不是给我们的子弹用的,而是给我们子弹影响范围内的物体用的。具体使用方法看代码,我会写注释

SBlackWholeProjectile.h:

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

#pragma once

#include "CoreMinimal.h"
#include "SFatherMagicProjectile.h"
#include "SBlackWholeProjectile.generated.h"

/**
 * 
 */
UCLASS()
class ACTIONROGUELIKE_API ASBlackWholeProjectile : public ASFatherMagicProjectile
{
	GENERATED_BODY()
	//设置一个timerhandle,两秒后消除我们的这个黑洞子弹	
	FTimerHandle TimeHandle;
	//设置一个队列,队列的类型是组件引用,存储的是所有重叠的组件
	TArray<UPrimitiveComponent*> OverlapComp;



public:
	ASBlackWholeProjectile();
	

protected:
	virtual void BeginPlay()override;

protected:
	virtual void Tick(float DeltaTime)override;
	//重写受击函数
	void OnComponentHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, FHitResult Hit);
	//增加一个两秒后调用的函数
	UFUNCTION()
	void TimeElapsed();
};

SBlackMagicProjectile.cpp

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


#include "SBlackWholeProjectile.h"
#include "PhysicsEngine/RadialForceComponent.h"
#include "SFatherMagicProjectile.h"
#include "Components/SphereComponent.h"
#include "Components/PrimitiveComponent.h"

ASBlackWholeProjectile::ASBlackWholeProjectile()
{
	PrimaryActorTick.bCanEverTick = true;
	//此处更改父类的SohereComp组件,使用另一个碰撞检测配置文件BlackWhole,这个碰撞检测的配置文件是除了摄像机和不可视的组件是忽略外,其它的全是重叠,也就是overlap
	SphereComp->SetCollisionProfileName("BlackWhole");
    //更改组件半径
	SphereComp->SetSphereRadius(500.0f, true);
	
}

void ASBlackWholeProjectile::BeginPlay()
{
	Super::BeginPlay();
	//事件开始的时候开始计时
	GetWorldTimerManager().SetTimer(TimeHandle, this, &ASBlackWholeProjectile::TimeElapsed, 2.0f);
	
}

void ASBlackWholeProjectile::Tick(float DeltaTime)
{
	//每帧都获取和SphereComp重叠的组件并全部丢到列表里面
	SphereComp->GetOverlappingComponents(OverlapComp);
	//队列表里面的组件进行遍历
	for (int32 i = 0 ; i<OverlapComp.Num();i++)
	{
        //获取组件
		UPrimitiveComponent* PrimComp = OverlapComp[i];
        //如果组件存在而且组件的模拟物理属性为真的话
		if (PrimComp&&PrimComp->IsSimulatingPhysics())
		{
            //初始化径向力
			const float SphereRadius = SphereComp->GetScaledSphereRadius();
			const float ForseStrength = -2000.0f;
			
            //增加径向力,力的方向是子弹的位置,半径和力的大小是上面提到的两行代码,力的衰减是RIF常量,冲量速度变更为true
			PrimComp->AddRadialForce(GetActorLocation(), SphereRadius, ForseStrength, ERadialImpulseFalloff::RIF_Constant, true);
		}
	}
}

//重写的受击函数,debug用
void ASBlackWholeProjectile::OnComponentHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, FHitResult Hit)
{
    
	FString Text = FString::Printf(TEXT("Fired"));
	DrawDebugString(GetWorld(), Hit.ImpactPoint, Text, nullptr, FColor::Blue, 2.0f, true, 1.0f);
}

//两秒后调用的函数
void ASBlackWholeProjectile::TimeElapsed()
{
	GetWorld()->DestroyActor(this);
}

大伙对AddRadialForce()函数中的ERadialImpulseFalloff::RIF_Constant可能不太熟悉,那么看这个截图可能能理解一点

请添加图片描述

我们可以看到,径向力组件的属性里面有一个衰减,各位去看一下这个衰减对应的英文,是Falloff,就是ERadialImpulseFalloff,RIF_Constant也就是RIF常量。说白了这个参数就是设置这个属性的。

各位也能发现,我们其实不是给子弹加一个负向的力,而是给子弹覆盖范围的所有物体施加一个负向的力。这个是我踩的一个思维的坑,施加力的对象与我的认知是相反的。

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

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

相关文章

js中各种运算符

文章目录 扩展运算符&#xff1a;...逻辑运算符&#xff08;&& 或 || &#xff09;第一、&& (逻辑与)运算&#xff0c;看一个简单的例子&#xff1a;第二、|| (逻辑或)运算&#xff0c;看一个简单的例子&#xff1a;三、&& (逻辑与) 和||&#xff08;逻…

JMX vs JFR:谁才是最强大的JVM监控利器?

大家好&#xff0c;我是小米&#xff01;今天我们来聊一聊JVM监控系统&#xff0c;特别是关于JMX和JFR的使用。你是否有过在线上应用出现性能问题时&#xff0c;无法准确获取关键指标的困扰呢&#xff1f;那么&#xff0c;不妨听听我给大家带来的解决方案。 什么是JMX 首先&a…

什么是CTF?打CTF的意义是什么?(附网络安全入门教程)

什么是CTF? CTF在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式。它起源于1996年DEFCON全球黑客大会&#xff0c;以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式。发展至今&#xff0c;已经成为全球范围网络安全圈流行的竞赛形式&#xff0…

使用mkdocs快速部署上线静态站点到Github

背景 mkdocs是一种方便地生成站点的工具&#xff0c;相比hugo、hexo等个人博客而言&#xff0c;mkdocs更加简便、轻量级&#xff0c;可以快速帮助部署上线类似个人技术本&#xff08;notebook&#xff0c;wiki&#xff09;之类的站点。并且支持默认支持站点内搜索&#xff0c;…

网络安全自学路线

很多人上来就说想学习黑客&#xff0c;但是连方向都没搞清楚就开始学习&#xff0c;最终也只是会无疾而终&#xff01;黑客是一个大的概念&#xff0c;里面包含了许多方向&#xff0c;不同的方向需要学习的内容也不一样。 算上从学校开始学习&#xff0c;已经在网安这条路上走…

基于ensp的mpls vpn跨域optionA方案(分公司之间不能互访但是能访问总部场景)

目录 一 实验拓扑 二 实验要求 三 实验分析 四 实验配置说明 4.1完成正常ip地址配置&#xff0c;ISP内部使用ospf协议和mpls ldp协议 4.2 PE和CE之间起ebgp邻居 4.3 PE和PE之间起mpbgp邻居 4.4 PE和CE、ASBR之间设置VRF来进行RT值的过滤&#xff0c;选择合适的RT值来完…

网络编程:TCP socket

文章目录 阅读前导 服务端定义日志框架成员属性服务端框架 初始化服务器创建套接字绑定开启监听 运行服务器netstat 工具获取连接和通信准备通信逻辑 单进程服务端函数&#xff08;version1&#xff09;telent 工具测试 多进程服务端&#xff08;version2&#xff09;创建子进程…

手把手教你学习IEC104协议和编程实现 十四-文件操作之目录操作

文件服务也算是最后一个系统的大章节了。早期的IEC104软件仅实现了基本的通讯,也就是我们常说的3遥,对于定值和文件的操作都很少提及。 首先我们先要看看文件都包含那些。 文件包含的内容 故障录波文件 故障录波文件我们单独开一章来讲解。包含通讯及文件的格式 历史数据…

多核系统内存假共享问题

多核处理器&#xff08;SMP&#xff09;系统中&#xff0c; 每一个处理器都有一个本地高速缓存。内存系统必须保证高速缓存的一致性。当不同处理器上的线程修改驻留再同一高速缓存中的变量时就会发生假共享(false sharing)&#xff0c;结果导致高速缓存无效&#xff0c;并强制更…

长期封闭环境中与积极情绪相关的肠道菌群

谷禾健康 近年来&#xff0c;随着微生物群-肠-脑轴研究的深入&#xff0c;越来越多证据表明&#xff0c;微生物群-肠-脑轴在调节大脑功能方面发挥着关键作用&#xff0c;尤其是在情绪处理和行为方面。然而&#xff0c;肠道微生物群与长期封闭环境中的心理变化之间的相关性仍知之…

小魔推助力实体行业短视频创作,打爆UGC素人流量!

最近不止一次的和大家讲到UGC的玩法&#xff0c;相信很多朋友也有了一定的认知&#xff0c;UGC简单来说&#xff0c;就是通过用户来生产原创内容&#xff0c;通过实体店的门头、环境、特色服务等相关内容&#xff0c;并且带有门店地址发布&#xff0c;就可以提升实体商家的POI热…

tensorrt

【TensorRT】动态batch进行推理_tensorrt 动态batch_there2belief的博客-CSDN博客 https://www.cnblogs.com/harrymore/p/15387117.html 1.动态batch 2.tensor内存管理 3.

《河北教育》期刊简介及投稿邮箱

《河北教育》期刊简介及投稿邮箱 主管单位:河北省教育厅 主办单位:河北省教育宣传中心 国际刊号ISSN&#xff1a;1004-6208&#xff1b;国内刊号CN&#xff1a;13-1036/G4&#xff1b;邮发代号&#xff1a;18-67 出版周期&#xff1a;月刊 出版地&#xff1a;河北省石家庄…

pytorch中backward()函数与gradient 参数详解

矩阵乘法的例子1 以下例来说明backward中参数gradient的作用 注意在本文中表示矩阵乘法&#xff0c;*表示对应元素相乘 求A求偏导 试运行代码1 import torch# [1,2][2*3][1,3] A torch.tensor([[1., 2.]], requires_gradTrue) B torch.tensor([[10., 20., 30.], [100., 20…

『赠书活动 | 第五期』《人工智能数学基础》

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 『赠书活动 &#xff5c; 第五期』 本期书籍&#xff1a;《人工智能数学基础》 赠书规则&#xff1a;评论区&#xff1a;点赞&#xff5c;收藏&#xff5c;留言 评论…

DAP-seq技术鉴定葡萄MADS-box转录因子VvMADS28的靶基因,揭示葡萄胚珠发育的调控机制

葡萄是一种营养丰富、美味可口的水果&#xff0c;深受世界各地消费者的喜爱。近年来&#xff0c;无籽葡萄也越来越受大家的欢迎&#xff0c;因此&#xff0c;无籽葡萄品种的培育成为一个重要的育种目标&#xff0c;而了解葡萄种子发育的分子遗传调控机制对于无籽栽培品种的培育…

Kyligence Zen 产品体验-好用的指标平台

文章目录 一、Kyligence Zen概念一、BI发展历史1.以报表为核心的IT响应式服务2.以宽表为核心的自助可视化分析3.以指标为核心的可视化分析、增强分析 二、什么是Kyligence Zen&#xff1f;1.官网的介绍2.个人的理解 二、产品体验一、创建账号二、指标1.直接创建指标2.导入指标数…

postgresql集群编译安装

postgreSQL集群部署1环境准备&#xff08;三台服务器全部执行&#xff09; 2.1.1 准备三台虚拟机 服务器名称 服务器IP 描述 Pgsql-0 xxx.xxx.xxx.xxx master节点 Pgsql-1 xxx.xxx.xxx.xxx slave1节点 Pgsql-2 xxx.xxx.xxx.xxx slave2节点 2.1.2 安装编译需要的相…

ANR实战案例 - 通用方法总结

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 文章目录 系列文章目录前言一、业务耗时1.登录Dialog优化2.子线程更新通知栏 二、频繁调用1.底部Tab资源初始…

【LLM系列之LLaMA】LLaMA: Open and Efficient Foundation Language Models

论文题目&#xff1a;《LLaMA: Open and Efficient Foundation Language Models》 论文链接&#xff1a;https://arxiv.org/pdf/2302.13971.pdf github链接&#xff1a;https://github.com/facebookresearch/llama/tree/main huggingface链接&#xff1a;https://huggingface.c…