从QuickDraw项目入门,制作一个简单的2D小游戏6

一.NPC的攻击
利用广播和动态多播委托实现,在GM_Duel.h中声明一个动态委托

//声明动态多播委托
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDrawPhaseStart);

再在GM_Duel类中定义一个动态多播的实例

public:
	//定义动态委托实例
	UPROPERTY(BlueprintAssignable)
	FDrawPhaseStart DrawPhaseStart;

然后在GM_Duel.cpp中,在Draw阶段开始时触发广播

void AGM_Duel::Tick(float DeltaTime)
{
	this->ElapsedTime += DeltaTime;
	//GEngine->AddOnScreenDebugMessage(-1, 1.0f, FColor::Green, FString::Printf(TEXT("%f"), this->ElapsedTime));
	switch (this->Phase)
	{
	case E_Phase::Intro:
	{
		//在intro阶段,等两个精灵滑动结束后,切换到wait阶段,然后延迟一段时间转进draw阶段
		if (this->ElapsedTime >= this->StartTime)
		{
			this->Phase = E_Phase::Wait;
			FLatentActionInfo LatentAction(0, FMath::Rand(), TEXT("TransWaitToDraw"), this);
			UKismetSystemLibrary::Delay(this, FMath::FRandRange(MinDrawStartTime, MaxDrawStartTime), LatentAction);
		}
		break;
	}
		

	case E_Phase::Wait:
	{
		
		break;
	}
		

	case E_Phase::Draw:
	{
		//GEngine->AddOnScreenDebugMessage(-1, 1.0f, FColor::Green, FString::Printf(TEXT("draw %i"), this->Phase));
		if (this->Exclamation != NULL)
		{
			//假如指针有效,将一个指向Exclamation类的指针指向他,然后设置可见性为true
			AExclamation* tem = Cast<AExclamation>(this->Exclamation);
			tem->SetVisibility(true);
		}
		//广播事件,让Samurai_NPC类知道
		DrawPhaseStart.Broadcast();
		break;
	}

	case E_Phase::Finished:
	{
		//GEngine->AddOnScreenDebugMessage(-1, 1.0f, FColor::Green, FString::Printf(TEXT("Finished %i"), this->Phase));
		break;
	}

	default:
	{
		break;
	}
	}

}

接下来开始对Samurai_NPC类进行一些代码实现,在Samurai_NPC.h中

#pragma once

#include "CoreMinimal.h"
#include "Samurai.h"
#include "Samurai_NPC.generated.h"

/**
 * 
 */
UCLASS()
class QUICKDRAW_API ASamurai_NPC : public ASamurai
{
	GENERATED_BODY()
	
public:
	//攻击延迟的最小值
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float MinAttackDelay = 0.3;

	//攻击延迟的最大值
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float MaxAttackDelay = 1.1;

	virtual void BeginPlay() override;

	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

	//攻击,简单的调用父类函数,UFUNCTION宏为了能让延迟函数识别到
	UFUNCTION()
	virtual void Attack() override;

	//延迟调用攻击,另外写作一个函数,要有UFUNCTION宏,否则接受不到广播事件
	UFUNCTION()
	void NPCAttackDelay();
};

在GM_Duel.cpp里实现如下

#include "GM_Duel.h"
#include "Kismet/GameplayStatics.h"
#include "Samurai_NPC.h"

void ASamurai_NPC::BeginPlay()
{
	Super::BeginPlay();

	//订阅事件
	if (this->GameMode)
	{
		GameMode->DrawPhaseStart.AddDynamic(this, &ASamurai_NPC::NPCAttackDelay);
		
	}
}

void ASamurai_NPC::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	//解除订阅
	if (GameMode)
	{
		GameMode->DrawPhaseStart.RemoveDynamic(this, &ASamurai_NPC::Attack);
	}

	Super::EndPlay(EndPlayReason);
}

void ASamurai_NPC::Attack()
{
	//GEngine->AddOnScreenDebugMessage(-1, 2.0, FColor::Green, FString("NPC Attack"));
	Super::Attack();
}

void ASamurai_NPC::NPCAttackDelay()
{
	//延迟调用攻击
	FLatentActionInfo LatentAction(0, FMath::Rand(), TEXT("Attack"), this);
	UKismetSystemLibrary::Delay(this, FMath::FRandRange(MinAttackDelay, MaxAttackDelay), LatentAction);
}

到此为止,转到虚幻编辑器中,选择被打倒时的玩家精灵姿态

再选择NPC武士精灵的攻击姿态,调整攻击滑动的结束位置

至此就完成了这块的功能编写

二.重置
在攻击成功后希望能够重置游戏状态,以此循环。在Samurai.h中,声明重置武士类状态的函数,并定义闲置状态精灵的变量

public:
	//闲置状态的精灵
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<UPaperSprite> IdleSprite;

	//重置武士状态
	void Reset();

在Samurai.cpp中实现重置状态的函数

void ASamurai::Reset()
{
	//将叉精灵置为不可见
	this->SetCrossVisbility(false);
	//复原攻击键按下状态
	this->isAttackPressed = false;
	//切换精灵至闲置状态
	SpriteComponent->SetSprite(IdleSprite);
	//调用滑动入场函数,如同游戏开始那样
	this->SlideIn();
}

在GM_Duel.h中,为GM_Duel类添加类成员和函数

public:	
	//重置游戏状态
	void Reset();
    
    //定时器
	FTimerHandle ResetTimerHandle;

在GM_Duel.cpp中实现重置状态函数,并在成功攻击后使用定时器,延迟一段时间调用它

void AGM_Duel::AttackSuccessfully(bool isPlayer)
{
	ASamurai_Player* SamuraiPlayer = Cast<ASamurai_Player>(UGameplayStatics::GetActorOfClass(this->GetWorld(), ASamurai_Player::StaticClass()));
	ASamurai_NPC* SamuraiNPC = Cast<ASamurai_NPC>(UGameplayStatics::GetActorOfClass(this->GetWorld(), ASamurai_NPC::StaticClass()));
	if (isPlayer)
	{
		SamuraiNPC->Defeated();
	}
	else
	{
		SamuraiPlayer->Defeated();
	}

	//因为攻击判定成功了,因此将感叹号置为不可见状态
	if (this->Exclamation != NULL)
	{
		AExclamation* tem = Cast<AExclamation>(this->Exclamation);
		tem->SetVisibility(false);
	}

	//使用定时器,延时3秒调用Reset
	FTimerManager& TimerManager = this->GetWorldTimerManager();
	TimerManager.SetTimer(ResetTimerHandle, this, &AGM_Duel::Reset, 3.0f, false);
}

void AGM_Duel::Reset()
{
	//重置游戏阶段
	this->Phase = E_Phase::Intro;
	ASamurai_Player* SamuraiPlayer = Cast<ASamurai_Player>(UGameplayStatics::GetActorOfClass(this->GetWorld(), ASamurai_Player::StaticClass()));
	ASamurai_NPC* SamuraiNPC = Cast<ASamurai_NPC>(UGameplayStatics::GetActorOfClass(this->GetWorld(), ASamurai_NPC::StaticClass()));
	//调用武士状态重置
	SamuraiPlayer->Reset();
	SamuraiNPC->Reset();
}

之后回到虚幻编辑器中,选择武士闲置状态精灵即可

posted @ 2025-03-21 17:21  SuzumiyaYui  阅读(44)  评论(0)    收藏  举报