切换武器

基础内容

Posted by AIaimuti on May 4, 2020


今天要做的是武器拾取的切换,一般游戏是拾取了武器到背包或者装备栏,目前是按E拾取武器销毁,并前面拾取的武器。

切换武器

在上次拾取武器的类Weapon上,进行一些改动,主要改动为两方面
1.对主角Man关于获取武器相关信息的改动
2.对Wdeapon的触发和离开触发相关内容的修改

对主角Man的改动准备

这里定义了一个AItem的指针,用于触发时指针指向待拾取的武器;
OnInteract是与待拾取武器互动的函数;
SetWeapon记录当前主角的武器持有的武器,再次拾取会覆盖前一个武器,由EquiqedWeapon变量保存当前武器。
EquiqedWeapon之所以写在protected里是因为写在public里十分麻烦,还需要很多其他操作

public:
	//触发时指针指向待拾取的武器
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
		class AItem* ActiveOverlapItem;
	//与待拾取武器互动的函数,按E调用
	UFUNCTION()
		void OnInteract();
	//设置武器,返回被捡去信息
	void SetWeapon(class AWeapon *Weapon);

protected:
	//记录当前武器的变量
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
		class AWeapon* EquiqedWeapon;

函数实现

OnInteract与按键E绑定,按下E时,将ActiveOverlapItem转化为Weapon;
然后调用Weapon中的Equip函数,this就指代Weapon,装备后,清空指针ActiveOverlapItem。
这里的ActiveOverlapItem值是Weapon触发函数里给的

void AMan::OnInteract()
{
	//这里的ActiveOverlapItem值是Weapon触发函数里给的
	AWeapon *Weapon = Cast<AWeapon>(ActiveOverlapItem);
	if (Weapon)
	{	
		Weapon->Equip(this);
		ActiveOverlapItem = nullptr;
	}
}
void AMan::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	/////////////省略其它内容////////////////
	//与武器互动,绑定按键E
	PlayerInputComponent->BindAction("Interact", IE_Released, this, &AMan::OnInteract);
}

SetWeapon则是用来装备和替换武器,EquiqedWeapon来记录当前持有的武器信息;
拾取时如果有武器,则销毁当前武器,放入新的武器,这是武器自身去调用的

void AMan::SetWeapon(AWeapon * Weapon)
{
	//如果已经有武器,则销毁当前武器
	if (EquiqedWeapon)
	{
		EquiqedWeapon->Destroy();
	}
	//装备新的武器
	EquiqedWeapon = Weapon;
}

对Weapon类的改动准备

这里加入了离开触发的函数的覆写和武器装备的声音,以及装备武器的函数

//离开触发的函数
virtual void OnOverlapEnd_Item(UPrimitiveComponent* OverlappedComponent,
	AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)override;
//武器装备时的声音
UPROPERTY(EditAnywhere, BlueprintReadOnly)
	USoundCue *SoundEquiped;

void Equip(class AMan *Man);

Weapon类切换武器函数实现

Weapon.cpp添加头文件:
//声音控制
#include “Sound/SoundCue.h”
//播放声音用的
#include “Kismet/GameplayStatics.h”
//用于打印的
#include “Engine/Engine.h”

这里首先修改了OnOverlapBegin_Item函数;
当拿到主角实例时,先对ActiveOverlapItem进行一些逻辑处理;
ActiveOverlapItem是一个指向待拾取武器的指针,但当同时接触多个武器时ActiveOverlapItem会有一些混乱;
因此,在这里当遇到待拾取武器时,先让ActiveOverlapItem指向遇到的第一个武器;
这样使逻辑简化,再接触其它物体时会忽略,这样就只能捡起一把碰到的武器

然后使用了TArray类,TArray是UE4的一个数组模版;
TArray <FStringFormatArg> FormatArray;中的FStringFormatArg类型,会将输入的字符串变成索引的形式以方便运用;
FormatArray获取组件名称放在索引{0};
使用AddOnScreenDebugMessage将FormatArray索引{0}的内容显示在屏幕上

void AWeapon::OnOverlapBegin_Item(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
	Super::OnOverlapBegin_Item(OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex, bFromSweep, SweepResult);
	if (OtherActor)
	{
		//拿到碰撞触发的实例
		AMan * Man = Cast<AMan>(OtherActor);
		//对实例装备武器
		if (Man)
		{
			//这里首先修改了OnOverlapBegin_Item函数;
			//当拿到主角实例时,先对ActiveOverlapItem进行一些逻辑处理;
			//ActiveOverlapItem是一个指向待拾取武器的指针,但当同时接触多个武器时ActiveOverlapItem会有一些混乱;
			//因此,在这里当遇到待拾取武器时,先让ActiveOverlapItem指向Man自身的武器;
			//这样使逻辑简化,再接触其它物体时会忽略,这样就只能捡起一把碰到的武器
			if (Man->ActiveOverlapItem == nullptr)
			{
				Man->ActiveOverlapItem = this;
			}
			//显示在屏幕上的相关配置
			//定义FStringFormatArg类的FormatArray
			TArray <FStringFormatArg> FormatArray;
			//FormatArray获取组件名称放在索引{0}
			FormatArray.Add(GetName());
			//使用AddOnScreenDebugMessage将FormatArray索引{0}的内容显示在屏幕上
			GEngine->AddOnScreenDebugMessage(-1, 100, FColor::Blue,
				FString::Format(TEXT("Press E to equip {0}"),FormatArray), true, FVector2D(4,4));

			//Equip(Man);
		}
	}
}

当离开第一个释放ActiveOverlapItem指针,使其为空;
这里也有问题,当我们同时接触两个物体时,离开了第一个物体但没离开第二个物体时,
ActiveOverlapItem也会为空,这会导致第二个物体捡不到;
这里为了使逻辑简单,先这样写

void AWeapon::OnOverlapEnd_Item(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex)
{
	Super::OnOverlapEnd_Item(OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex);
	if (OtherActor)
	{
		AMan *Man = Cast<AMan>(OtherActor);
		//这里也有问题,当我们同时接触两个物体时,离开了第一个物体但没离开第二个物体时,
		//ActiveOverlapItem也会为空,这会导致第二个物体捡不到
		if (Man->ActiveOverlapItem == this)
		{
			Man->ActiveOverlapItem = nullptr;
		}
		//消除屏幕上的文字
		GEngine->ClearOnScreenDebugMessages();
	}
}

装备函数,将武器对于所有通道的碰撞设置为忽略并关闭模拟物理 这里较上一次添加了解绑定碰撞触发离开函数,避免一直调用触发函数功能 然后播放装备声音,设置武器

void AWeapon::Equip(AMan * Man)
{
	if (Man)
	{
		//把武器对于所有通道的碰撞设置为忽略
		Mesh->SetCollisionResponseToAllChannels(ECR_Ignore);
		//设置武器模拟物理为false
		Mesh->SetSimulatePhysics(false);
		//把武器附着在主角身上的函数,三个参数
		//第一个是获取Pawn的网格,第二个是附着的方式,第三个是插槽的名字
		//FAttachmentTransformRules有四种方式
		//保持相对位置KeepRelativeTransform; 保持世界位置KeepWorldTransform; 
		//附着不包括缩放SnapToTargetNotIncludingScale; 附着包括缩放SnapToTargetIncludingScale;
		AttachToComponent(Man->GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, "WeaponSocket");
		Rotate = false;
		//关闭粒子特效
		ParticleComp->SetActive(false);
		//解绑定碰撞触发函数,避免一直调用触发函数功能
		SphereComp->OnComponentBeginOverlap.RemoveDynamic(this, &AItem::OnOverlapBegin_Item);
		//解绑定碰撞触发离开函数,避免一直调用触发函数功能
		SphereComp->OnComponentEndOverlap.RemoveDynamic(this, &AItem::OnOverlapEnd_Item);
		GEngine->ClearOnScreenDebugMessages();
		//播放装备声音
		if (SoundEquiped)
		{
			UGameplayStatics::PlaySound2D(this, SoundEquiped);
		}
		//装备武器
		Man->SetWeapon(this);
	}
}

整体思路就是主角碰到武器,把武器用ActiveOverlapItem临时记下,
如果按E则调用OnInteract函数与Weapon互动,调用Weapon的Equip函数;
Equip函数调用主角的SetWeapon函数,告诉主角自己被装备了,记录下当前装备的武器。