UE C++学习(二)之 委托,Gamemode基础类,不同Pawn切换,映射,伤害TakeDamage等

目录

DECLARE_DELEGATE 委托

GameMode创建基础类

添加基础移动逻辑

 PlayerController切换pawn

新项目设置版权

创建文件在新的文件夹里面

UE的映射

角色摄像机控制

获取玩家速度

更改移动组件的父类

返回给定组件的所有者

计算角色的移动

判断变量是否有效

伤害函数TakeDamage

ApplyDamage


DECLARE_DELEGATE 委托

委托可以存储对 具有任意类的特定签名的方法 的引用,并在必要时调用此方法。委托在蓝图中叫EventDispatchers

(1)DELARE_DELEGATE:是最简单的版本,带有一个参数DelegateName 是我们可以分配给委托的名称,只能在C++中使用

(2)DECLARE_DYNAMIC_DELEGATE:这个委托的不同之处在于,可以做蓝图中使用

(3)DECLARE_DYNAMIC_MULTICAST_DELEGATE:多播,分别用蓝图的形式编写,可以编写多个客户端,也可以使用C++

声明两个委托:

//动态委托:以蓝图提供,具有两个参数
//UE中所有代表队名称用 F 开头
//给这个委托命名为FOnColoredChanged,下一个参数是 参数的类型(比如说参数是color,那么类型就是FLinearColor),下一个参数就是 第一个参数的名称 Color,下一个是 第二个参数的类型(比如说要输出 Actor的名称,那么类型就是FString),后面以此类推
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnColoredChanged,const FLinearColor& ,Color,const FString&, Name);

//非动态委托:以C++提供 ,具有一个参数
//命名为FOnTimerFinished,参数类型是指向参与者的指针,就是返回当前actor的指针
DECLARE_MULTICAST_DELEGATE_OneParam(FOnTimerFinished,AActor*);

创建两个委托变量

	//动态委托需要提供给蓝图
	UPROPERTY(BlueprintAssignable)
	FOnColoredChanged OnColorChanged;

	FOnTimerFinished OnTimerFinished;

执行这两个多播委托:Broadcast   单播用ExecuteIfBound

		//使用Broadcast 调用委托
		OnColorChanged.Broadcast(NewColor, GetName());

		OnTimerFinished.Broadcast(this);

绑定委托:动态多播用AddDynamic,非动态多播用AddUobject,单播用BindUObject

	//创建两个函数,用于订阅委托
	//参数类型要和委托类型一样
	//绑定动态委托必须要用UFUNCTION标记
	UFUNCTION()
	void OnColorChanged1(const FLinearColor& Color, const FString& Name);

	void OnTimerFinished1(AActor* Actor);
				//绑定委托:第一个参数是指向对象的指针,第二个是 改对象函数的引用
				// 动态委托绑定:AddDynamic:
				//当委托被触发时,会调用OnColorChanged1函数
				Geometry->OnColorChanged.AddDynamic(this, &ABaseGeometryHubActor::OnColorChanged1);
				//非动态委托绑定:AddUObject
				Geometry->OnTimerFinished.AddUObject(this, &ABaseGeometryHubActor::OnTimerFinished1);

在蓝图中的使用

GameMode创建基础类

StaticClass

	DefaultPawnClass = ADefaultPawn::StaticClass();
	PlayerControllerClass = APlayerController::StaticClass();
	PlayerStateClass = APlayerState::StaticClass();
	GameStateClass = AGameStateBase::StaticClass();
	HUDClass = AHUD::StaticClass();
	GameSessionClass = AGameSession::StaticClass();
	SpectatorClass = ASpectatorPawn::StaticClass();
	ReplaySpectatorPlayerControllerClass = APlayerController::StaticClass();
	ServerStatReplicatorClass = AServerStatReplicator::StaticClass();

添加基础移动逻辑

SceneComponent组件

Static组件,Camera组件

FVector初始为0可以用FVector::ZeroVector

	UPROPERTY(VisibleAnywhere)
	USceneComponent* SceneComponent;

    UPROPERTY(VisibleAnywhere)
	UStaticMeshComponent* StaticMeshComponent;

	UPROPERTY(VisibleAnywhere)
	UCameraComponent* CameraComponent;

    UPROPERTY(EditAnyWhere)
	float Velocity = 300.0f;

	FVector VelocityVector = FVector::ZeroVector;

	void MoveForward(float Amount);
	void MoveRight(float Amount);

初始化组件:CreateDefaultSubobject

将组件附加到别的组件中:SetupAttachment()

获取根组件:GetRootComponent()

	SceneComponent = CreateDefaultSubobject<USceneComponent>("SceneComponent");
	SetRootComponent(SceneComponent);

    StaticMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>("StaticMeshComponent");
	CameraComponent = CreateDefaultSubobject<UCameraComponent>("CameraComponent");

	//使用SetupAttachment将组件附加到根组件中,可以使用GetRootComponent()获取根组件
	StaticMeshComponent->SetupAttachment(SceneComponent);
	CameraComponent->SetupAttachment(GetRootComponent());

控制器有一个指向它所控制的pawn的指针,可以通过控制器将输入传递给pawn

绑定按键输入的函数:PlayerInputComponent->BindAxis()

void ASandboxPawn::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	if (!VelocityVector.IsZero())
	{
		const FVector NewLocation = GetActorLocation() + Velocity * DeltaTime * VelocityVector;
		SetActorLocation(NewLocation);
		VelocityVector = FVector::ZeroVector;
	}
}

// Called to bind functionality to input
void ASandboxPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	if (PlayerInputComponent)
	{
		PlayerInputComponent->BindAxis("MoveForward", this, &ASandboxPawn::MoveForward);
		PlayerInputComponent->BindAxis("MoveRight", this, &ASandboxPawn::MoveRight);
	}

}

void ASandboxPawn::MoveForward(float Amount)
{
	UE_LOG(LogTemp, Display, TEXT("MoveForwart: %f"), Amount);
	VelocityVector.X = Amount;
}

void ASandboxPawn::MoveRight(float Amount)
{
	UE_LOG(LogTemp, Display, TEXT("MoveRIght: %f"), Amount);
	VelocityVector.Y = Amount;
}

 PlayerController切换pawn

在PlayerController中添加按键输入,有个SetupInputComponent()

	virtual void SetupInputComponent() override;

使用BindAction或者BindAxis绑定按键

void ASandboxPlayerController::SetupInputComponent()
{
	Super::SetupInputComponent();


	//BindAction:绑定按键::蓝图映射名称、EInputEvent(负责控件发生的事件类型)
	if (InputComponent)
	{
		InputComponent->BindAction("ChangePawn", IE_Pressed, this, &ASandboxPlayerController::ChangePawn);

	}

}

Possess:用于切换Pawn的控制权

UGameplayStatic类:包含各种静态函数,该类的所有功能都可以做蓝图中使用

	UPROPERTY()
	TArray<AActor*>Pawns;

	int32 CurrentPawnIndex = 0;
void ASandboxPlayerController::BeginPlay()
{
	Super::BeginPlay();

	//UGameplayStatics类:包含各种静态函数,该类的所有功能都可以做蓝图中使用,
	UGameplayStatics::GetAllActorsOfClass(GetWorld(), ASandboxPawn::StaticClass(), Pawns);

}

void ASandboxPlayerController::ChangePawn()
{
	if (Pawns.Num() <= 1) return;

	ASandboxPawn* CurrentPawn = Cast<ASandboxPawn>(Pawns[CurrentPawnIndex]);

	CurrentPawnIndex = (CurrentPawnIndex + 1) % Pawns.Num();
	if (!CurrentPawn) return;

	UE_LOG(LogTemp, Display, TEXT("ChangePawn"));

	//切换pawn :Possess
	Possess(CurrentPawn);
}

PossessedBy,UnPossessed:当Pawn被切换控制权时调用

	//切换控制权 
	virtual void PossessedBy(AController* NewController) override;
	virtual void UnPossessed() override;

新项目设置版权

创建文件在新的文件夹里面

需要在构建文件Build.cs里面加上路径 :

UE的映射

UE的映射分为动作映射和轴映射,动作映射是一种离散映射,当一次收到一个事件:按下或者释放一个按钮。轴映射来自控件的值不断变化。

轴映射:添加移动,在charactor的SetupPlayerInputComponent函数中,绑定轴映射,AddMovementInput函数在每个Charactor中都有,实际上就是获取移动组件。

#include "Components/InputComponent.h"


void ASTUBaseCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	PlayerInputComponent->BindAxis("MoveForward", this, &ASTUBaseCharacter::MoveForward);
	PlayerInputComponent->BindAxis("MoveRight", this, &ASTUBaseCharacter::MoveRight);

}

void ASTUBaseCharacter::MoveForward(float Amount)
{
	AddMovementInput(GetActorForwardVector(), Amount);
}

void ASTUBaseCharacter::MoveRight(float Amount)
{
	AddMovementInput(GetActorRightVector(), Amount);
}

动作映射:添加跳跃

void ASTUBaseCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);


    //Jump函数是Character自带的
	PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ASTUBaseCharacter::Jump);

}

角色摄像机控制

添加相机组件和弹簧笔组件:UCameraComponent

//前向声明
class UCameraComponent;


	UPROPERTY(VisibleAnyWhere, BlueprintReadWrite, Category = "Components")
	UCameraComponent* CameraComponent;

初始化组件:

#include "Components/InputComponent.h"

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


	CameraComponent = CreateDefaultSubobject<UCameraComponent>("CameraComponent");
	CameraComponent->SetupAttachment(GetRootComponent());
}

添加旋转:

#include "Components/InputComponent.h"

// Called to bind functionality to input
void ASTUBaseCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	//相机旋转
	PlayerInputComponent->BindAxis("LookUp", this, &ASTUBaseCharacter::LookUp);
	PlayerInputComponent->BindAxis("TurnAround", this, &ASTUBaseCharacter::TurnAround);

	//如果是使用controller的旋转,可以直接用接口,这个和上面是一样的,都能够实现逻辑
	//PlayerInputComponent->BindAxis("LookUp", this, &ASTUBaseCharacter::AddControllerPitchInput);
	//PlayerInputComponent->BindAxis("TurnAround", this, &ASTUBaseCharacter::AddControllerYawInput);

}


void ASTUBaseCharacter::LookUp(float Amount)
{
	AddControllerPitchInput(Amount);
}

void ASTUBaseCharacter::TurnAround(float Amount)
{
	AddControllerYawInput(Amount);
}

但是这样设置只有水平的旋转,如果要上下旋转的话还需要使用pawn控制旋转

AddControllerPitchInput实际上是使用的PlayerController的旋转,而它的旋转速度在下面设置

如果要实现围绕角色当前所在点进行旋转的话,需要使用弹簧臂。

添加弹簧臂

//前向声明
class USpringArmComponent;



	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Components")
	USpringArmComponent* SpringArmComponent;

初始化:

#include "Components/InputComponent.h"
#include "GameFramework/SpringArmComponent.h"

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

	SpringArmComponent = CreateDefaultSubobject<USpringArmComponent>("SpringArmComponent");
	SpringArmComponent->SetupAttachment(GetRootComponent());

    //刚刚的设置也可以在代码中使用pawn的旋转
    SpringArmComponent->bUsePawnControlRotation = true;

    //将相机的父组件改为弹簧臂
	CameraComponent = CreateDefaultSubobject<UCameraComponent>("CameraComponent");
	CameraComponent->SetupAttachment(SpringArmComponent);
}

获取玩家速度

GetVelocity();

//判断速度是否为0
GetVelocity().IsZero();

更改移动组件的父类

创建一个CharacterMovement类,在Character类中使用带有参数的构造函数,

UCLASS()
class SHOOTTHEMUP_API ASTUBaseCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	//一种带有参数的特殊构造函数,将初始化对象传递到改构造函数中,改对象的类型为FObjectInitializer
	ASTUBaseCharacter(const FObjectInitializer& ObjInit);

在CPP文件中调用父类的构造,传递要初始化的对象

ObjInit.SetDefaultSubobjectClass<要继承的新类>(要修改的组件名字)

//必须要调用父类的构造函数,然后传递我们的初始化对象
//调用模板函数SetDefaultSubobjectClass,以要替换的子对象的类作为模板参数
ASTUBaseCharacter::ASTUBaseCharacter(const FObjectInitializer& ObjInit)
	: Super(ObjInit.SetDefaultSubobjectClass<USTUCharacterMovementComponent>(ACharacter::CharacterMovementComponentName))
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

}

返回给定组件的所有者

GetPawnOwner()

	//创建一个指向角色的指针,GetPawnOwner()返回指向给定组件所有者的指针,然后将指针转换为ASTUBaseCharacter
	const ASTUBaseCharacter* Player = Cast<ASTUBaseCharacter>(GetPawnOwner());

计算角色的移动

归一化:GetSafeNormal()

点乘:FVector::DotProduct()

余弦:FMath::Acos()

交叉乘积:FVector::CrossProduct()

反余弦:FMath::RadiansToDegrees()

符号化:FMath::Sign()

float ASTUBaseCharacter::GetMovementDirection() const
{
	if (GetVelocity().IsZero())
	return 0;
	const FVector VelocityNormal = GetVelocity().GetSafeNormal();
	const float AngleBetween = FMath::Acos(FVector::DotProduct(GetActorForwardVector(), VelocityNormal));
	const FVector CrossProduct = FVector::CrossProduct(GetActorForwardVector(), VelocityNormal);


	return FMath::RadiansToDegrees(AngleBetween) * FMath::Sign(CrossProduct.Z);
}

判断变量是否有效

Check:如果无效的话,运行时会跳转到报错位置

	check(HealthComponent);
	check(HealthTextComponent);

伤害函数TakeDamage

	//ue自带的伤害函数,是蓝图的ApplyDamage的底层实现函数
	TakeDamage(0.1,FDamageEvent{}, Controller, this);

分为三种类型OnTakeAnyDamage,OnTakePointDamage,OnTakeRadialDamage

是通过委托来实现的

DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FiveParams( FTakeAnyDamageSignature, AActor, OnTakeAnyDamage, AActor*, DamagedActor, float, Damage, const class UDamageType*, DamageType, class AController*, InstigatedBy, AActor*, DamageCauser );

实现逻辑:

创建一个函数用于绑定委托 参数要和委托一样

	UFUNCTION()
	void OnTakeAnyDamageComponent(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);

动态绑定委托:这里使用的是第一种伤害类型

OnTakeAnyDamage.AddDynamic()

	//获取owner
	AActor* ComponentOwner = GetOwner();
	if (ComponentOwner)
	{
		//OnTakeAnyDamage,OnTakePointDamage,OnTakeRadialDamage有三种类型,这里使用第一种
		ComponentOwner->OnTakeAnyDamage.AddDynamic(this, &USTUHealthComponent::OnTakeAnyDamageComponent);
	}

ApplyDamage

ApplyDamage:需要用到GameplayStatics类

DrawDebugSphere:绘制调试球形

TSubclassOf:

//TSubclassOf:生成一个DamgeType类型的变量,在蓝图外部赋值	
UPROPERTY(EditAnywhere, BlueprintReadWrite)
		TSubclassOf<UDamageType> DamageType;

#include "DrawDebugHelpers.h"
//从world中获得所以的actor时
#include "Kismet/GameplayStatics.h"



	//绘制球形检测:指向世界的指针,球的中心点,半径,球段数,球颜色
	DrawDebugSphere(GetWorld(), GetActorLocation(), Radius, 24, SphereColor);

	//DoFullDamage:为false时 边缘到中心伤害是增加的,true时,伤害是一样的
	UGameplayStatics::ApplyRadialDamage(GetWorld(), Damage, GetActorLocation(), Radius, DamageType, {}, this, nullptr, DoFullDamage);

绑定委托:

AddDynamic:绑定动态委托

IsA<>():IsA<>():强制转换,将DamageType强制转换为USTUFirDamageType或者USTUIceDamageType类型

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

	Health = MaxHealth;

	//获取owner
	AActor* ComponentOwner = GetOwner();
	if (ComponentOwner)
	{
		//OnTakeAnyDamage,OnTakePointDamage,OnTakeRadialDamage有三种类型,这里使用第一种
		ComponentOwner->OnTakeAnyDamage.AddDynamic(this, &USTUHealthComponent::OnTakeAnyDamageComponent);
	}

}

void USTUHealthComponent::OnTakeAnyDamageComponent(AActor* DamagedActor, float Damage, const UDamageType* DamageType, AController* InstigatedBy, AActor* DamageCauser)
{
	Health -= Damage;

	UE_LOG(HealthComponentLog, Display, TEXT("Damage:%f"), Damage);

	if (DamageType)
	{
		//IsA<>():强制转换,将DamageType强制转换为USTUFirDamageType或者USTUIceDamageType类型
		if (DamageType->IsA<USTUFirDamageType>()) 
		{
			UE_LOG(HealthComponentLog, Display, TEXT("Fire"));
		}
		else if (DamageType->IsA<USTUIceDamageType>())
		{
			UE_LOG(HealthComponentLog, Display, TEXT("Ice"));
		}
	}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值