虚幻C+++基础 day3

常见的游戏机制

Actor机关门

  • 创建一个Actor类,添加两个静态网格与一个触发器

    • UBoxComponent
    • UStaticMeshComponent
  • 头文件:

    • #include “Components/BoxComponent.h”
    • #include “Components/StaticMeshComponent.h”

TriggerDoor.h

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

#pragma once

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

UCLASS()
class UEGAME_API ATriggerDoor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ATriggerDoor();

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	class UBoxComponent* TriggerBox;

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	class UStaticMeshComponent* TriggerMesh;

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	UStaticMeshComponent* DoorMesh;

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

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

};

TriggerDoor.cpp

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


#include "TriggerDoor.h"
#include "Components/BoxComponent.h"
#include "Components/StaticMeshComponent.h"
// Sets default values
ATriggerDoor::ATriggerDoor()
{
 	// 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;

	TriggerBox = CreateDefaultSubobject<UBoxComponent>(TEXT("TriggerBox"));
	RootComponent = TriggerBox;

	TriggerMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("TriggerMesh"));
	TriggerMesh->SetupAttachment(GetRootComponent());

	DoorMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorMesh"));
	DoorMesh->SetupAttachment(GetRootComponent());

}

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

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

}

设置材质上去

  • 直接采用场景里面的材质附着上去即可
    在这里插入图片描述

编写位置更新函数

  • 修好好位置后点击这个就可以实例同步到蓝图中
    在这里插入图片描述
    在这里插入图片描述

蓝图中的时间轴函数(Timeline)与UE多播委托机制

多播委托

  • 触发器中提供了两个与一个绑定宏用来判断组件是否被重叠,这两个函数是要参与UE反射系统的,我们需要使用UFUNCTION进行标明,UFUNCTION里面可以什么参数不要,我们称这个操作为多播委托
    • OnComponentBeginOverlap:事件,当某些内容开始与此组件重叠时调用,例如玩家进入触发器。
    • OnComponentEndOverlap:事件,当组件停止重叠时调用
  • AddDynamic宏:这个宏需要传入一个绑定函数,绑定函数参数格式如下UE源码,因为UE是把参数与参数类型都当做参数了,所以我们需要把逗号删除,一般是从开头数字乘2,从参数最后面开始选择,例如:SixParams(...),也就是6*2=12个参数,从函数尾部开始复制12个参数,注意UE4.23之后的版本可能参数不一样
/**
 * Delegate for notification of blocking collision against a specific component.  
 * NormalImpulse will be filled in for physics-simulating bodies, but will be zero for swept-component blocking collisions. 
 */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams( FComponentHitSignature, UPrimitiveComponent*, HitComponent, AActor*, OtherActor, UPrimitiveComponent*, OtherComp, FVector, NormalImpulse, const FHitResult&, Hit );
/** Delegate for notification of start of overlap with a specific component */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams( FComponentBeginOverlapSignature, UPrimitiveComponent*, OverlappedComponent, AActor*, OtherActor, UPrimitiveComponent*, OtherComp, int32, OtherBodyIndex, bool, bFromSweep, const FHitResult &, SweepResult);
/** Delegate for notification of end of overlap with a specific component */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams( FComponentEndOverlapSignature, UPrimitiveComponent*, OverlappedComponent, AActor*, OtherActor, UPrimitiveComponent*, OtherComp, int32, OtherBodyIndex);
/** Delegate for notification when a wake event is fired by physics*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FComponentWakeSignature, UPrimitiveComponent*, WakingComponent, FName, BoneName);
/** Delegate for notification when a sleep event is fired by physics*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FComponentSleepSignature, UPrimitiveComponent*, SleepingComponent, FName, BoneName);
/** Delegate for notification when collision settings change. */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FComponentCollisionSettingsChangedSignature, UPrimitiveComponent*, ChangedComponent);

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam( FComponentBeginCursorOverSignature, UPrimitiveComponent*, TouchedComponent );
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam( FComponentEndCursorOverSignature, UPrimitiveComponent*, TouchedComponent );
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FComponentOnClickedSignature, UPrimitiveComponent*, TouchedComponent , FKey, ButtonPressed);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FComponentOnReleasedSignature, UPrimitiveComponent*, TouchedComponent, FKey, ButtonReleased);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FComponentOnInputTouchBeginSignature, ETouchIndex::Type, FingerIndex, UPrimitiveComponent*, TouchedComponent );
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FComponentOnInputTouchEndSignature, ETouchIndex::Type, FingerIndex, UPrimitiveComponent*, TouchedComponent );
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FComponentBeginTouchOverSignature, ETouchIndex::Type, FingerIndex, UPrimitiveComponent*, TouchedComponent );
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FComponentEndTouchOverSignature, ETouchIndex::Type, FingerIndex, UPrimitiveComponent*, TouchedComponent );
  • 声明两个绑定函数
public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	//自定义AddDynamic绑定的触发器函数
	UFUNCTION()
	void OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
	UFUNCTION()
	void OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
};
  • 将多播委托进行绑定

// Called when the game starts or when spawned
void ATriggerDoor::BeginPlay()
{
	Super::BeginPlay();
	//绑定事件
	TriggerBox->OnComponentBeginOverlap.AddDynamic(this, &ATriggerDoor::OnOverlapBegin);
	TriggerBox->OnComponentEndOverlap.AddDynamic(this, &ATriggerDoor::OnOverlapEnd);
}

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

}

void ATriggerDoor::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
}

void ATriggerDoor::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
}

指定触发器只响应Pawn

	//设置TriggerBox碰撞的硬编码
	TriggerBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly);//设置碰撞类型
	TriggerBox->SetCollisionObjectType(ECollisionChannel::ECC_WorldStatic);//设置对象移动时其应视为某种物体
	TriggerBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);//设置所有的碰撞响应为忽略
	TriggerBox->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);//设置Pawn碰撞响应为重叠

TriggerDoor.h

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

#pragma once

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

UCLASS()
class UEGAME_API ATriggerDoor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ATriggerDoor();

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	class UBoxComponent* TriggerBox;

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	class UStaticMeshComponent* TriggerMesh;

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	UStaticMeshComponent* DoorMesh;

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

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

	//自定义AddDynamic绑定的触发器函数
	UFUNCTION()
	void OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
	UFUNCTION()
	void OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
};

TriggerDoor.cpp

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


#include "TriggerDoor.h"
#include "Components/BoxComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Characters/Player/MainPlayer.h"
// Sets default values
ATriggerDoor::ATriggerDoor()
{
 	// 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;

	TriggerBox = CreateDefaultSubobject<UBoxComponent>(TEXT("TriggerBox"));
	RootComponent = TriggerBox;

	TriggerBox->SetBoxExtent(FVector(60.f, 60.f, 30.f));

	//设置TriggerBox碰撞的硬编码
	TriggerBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly);//设置碰撞类型
	TriggerBox->SetCollisionObjectType(ECollisionChannel::ECC_WorldStatic);//设置对象移动时其应视为某种物体
	TriggerBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);//设置所有的碰撞响应为忽略
	TriggerBox->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);//设置Pawn碰撞响应为重叠

	TriggerMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("TriggerMesh"));
	TriggerMesh->SetupAttachment(GetRootComponent());

	DoorMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorMesh"));
	DoorMesh->SetupAttachment(GetRootComponent());

}

// Called when the game starts or when spawned
void ATriggerDoor::BeginPlay()
{
	Super::BeginPlay();
	
	TriggerBox->OnComponentBeginOverlap.AddDynamic(this, &ATriggerDoor::OnOverlapBegin);
	TriggerBox->OnComponentEndOverlap.AddDynamic(this, &ATriggerDoor::OnOverlapEnd);
}

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

}

void ATriggerDoor::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);
	if (Player)
	{

	}
}

void ATriggerDoor::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);
	if (Player)
	{

	}
}

BlueprintImplementableEvent参数的使用

  • 新建几个函数用来升起降下门,关闭打开门
  • BlueprintImplementableEvent:会把函数变成一个事件,把函数提升为事件后,就不能去初始化函数了,因为这个是在蓝图调用的事件
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

编写更新函数在蓝图中使用

  • 新建两个用于更新的函数在蓝图中调用,新建两个变量用来获取当前门与触发器的offset,用于更新函数中去进行位置移动
//获取门与触发器位置
UPROPERTY(BluerprintReadWrite,Category = "Trigger Door|Trigger Properties")
FVector InitTriggerLoction;
UPROPERTY(BluerprintReadWrite,Category = "Trigger Door|Door Properties")
FVector InitDoorLocation;
UFUNCTION(BlueprintCallable,Category="Triggerable Door | Triggerable Switch")
void UpdateTriggerLocation(FVector offset);
UFUNCTION(BlueprintCallabel,Category= "Triggerable Door | Door Switch")
void UpdateDoorLocation(FVector offset);
  • 在BeginPlay中初始化门和触发器的位置
// Called when the game starts or when spawned
void ATriggerDoor::BeginPlay()
{
	Super::BeginPlay();
	
	TriggerBox->OnComponentBeginOverlap.AddDynamic(this, &ATriggerDoor::OnOverlapBegin);
	TriggerBox->OnComponentEndOverlap.AddDynamic(this, &ATriggerDoor::OnOverlapEnd);

	//获取到门和触发器的位置
	InitTriggerLoction = TriggerMesh->GetComponentLocation();
	InitDoorLocation = DoorMesh->GetComponentLocation();
}
  • 然后去更新函数中更新门与触发器的位置
void ATriggerDoor::UpdateTriggerLocation(FVector offset)
{
	//新位置=初始位置+移动后的位置
	FVector newLocation = InitTriggerLoction + offset;
	TriggerMesh->SetWorldLocation(newLocation);
}

void ATriggerDoor::UpdateDoorLocation(FVector offset)
{
	//新位置=初始位置+移动后的位置
	FVector newLocation = InitDoorLocation + offset;
	DoorMesh->SetWorldLocation(newLocation);
}
  • 然后去蓝图中连接更新函数
    在这里插入图片描述

采用定时器关门

  • 采用定时器关门的两种思路:
    • 思路一:我们可以在人物踩上触发器的时候开启定时器进行关门,当人物离开定时器的时候就关闭定时器
    • 思路二:我们可以定义一个bool变量(默认为false)进行判断定时器是否开启,当人物踩上触发器的时候就让变量为true,当人物离开触发器的时候就为false也就不执行定时器
  • 定时器头文件:#include “TimerManager.h”
  • FTimerHandleCloseDoorTimerHandle: 创建一个时间句柄
  • SetTimer的六个重载函数
/**
	 * Sets a timer to call the given native function at a set interval.  If a timer is already set
	 * for this handle, it will replace the current timer.
	 *
	 * @param InOutHandle			If the passed-in handle refers to an existing timer, it will be cleared before the new timer is added. A new handle to the new timer is returned in either case.
	 * @param InObj					Object to call the timer function on.
	 * @param InTimerMethod			Method to call when timer fires.
	 * @param InRate				The amount of time between set and firing.  If <= 0.f, clears existing timers.
	 * @param InbLoop				true to keep firing at Rate intervals, false to fire only once.
	 * @param InFirstDelay			The time for the first iteration of a looping timer. If < 0.f inRate will be used.
	 */
	template< class UserClass >
	FORCEINLINE void SetTimer(FTimerHandle& InOutHandle, UserClass* InObj, typename FTimerDelegate::TUObjectMethodDelegate< UserClass >::FMethodPtr InTimerMethod, float InRate, bool InbLoop = false, float InFirstDelay = -1.f)
	{
		InternalSetTimer(InOutHandle, FTimerUnifiedDelegate( FTimerDelegate::CreateUObject(InObj, InTimerMethod) ), InRate, InbLoop, InFirstDelay);
	}
	template< class UserClass >
	FORCEINLINE void SetTimer(FTimerHandle& InOutHandle, UserClass* InObj, typename FTimerDelegate::TUObjectMethodDelegate_Const< UserClass >::FMethodPtr InTimerMethod, float InRate, bool InbLoop = false, float InFirstDelay = -1.f)
	{
		InternalSetTimer(InOutHandle, FTimerUnifiedDelegate( FTimerDelegate::CreateUObject(InObj, InTimerMethod) ), InRate, InbLoop, InFirstDelay);
	}

	/** Version that takes any generic delegate. */
	FORCEINLINE void SetTimer(FTimerHandle& InOutHandle, FTimerDelegate const& InDelegate, float InRate, bool InbLoop, float InFirstDelay = -1.f)
	{
		InternalSetTimer(InOutHandle, FTimerUnifiedDelegate(InDelegate), InRate, InbLoop, InFirstDelay);
	}
	/** Version that takes a dynamic delegate (e.g. for UFunctions). */
	FORCEINLINE void SetTimer(FTimerHandle& InOutHandle, FTimerDynamicDelegate const& InDynDelegate, float InRate, bool InbLoop, float InFirstDelay = -1.f)
	{
		InternalSetTimer(InOutHandle, FTimerUnifiedDelegate(InDynDelegate), InRate, InbLoop, InFirstDelay);
	}
	/*** Version that doesn't take a delegate */
	FORCEINLINE void SetTimer(FTimerHandle& InOutHandle, float InRate, bool InbLoop, float InFirstDelay = -1.f)
	{
		InternalSetTimer(InOutHandle, FTimerUnifiedDelegate(), InRate, InbLoop, InFirstDelay);
	}
	/** Version that takes a TFunction */
	FORCEINLINE void SetTimer(FTimerHandle& InOutHandle, TFunction<void(void)>&& Callback, float InRate, bool InbLoop, float InFirstDelay = -1.f )
	{
		InternalSetTimer(InOutHandle, FTimerUnifiedDelegate(MoveTemp(Callback)), InRate, InbLoop, InFirstDelay);
	}

思路一

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

#pragma once

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

UCLASS()
class UEGAME_API ATriggerDoor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ATriggerDoor();

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	class UBoxComponent* TriggerBox;

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	class UStaticMeshComponent* TriggerMesh;

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	UStaticMeshComponent* DoorMesh;

	//获取门与触发器位置
	UPROPERTY(BlueprintReadWrite,Category = "Trigger Door|Trigger Properties")
	FVector InitTriggerLoction;
	UPROPERTY(BlueprintReadWrite,Category = "Trigger Door|Door Properties")
	FVector InitDoorLocation;

	//延时时间
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trigger Door|Trigger Properties")
	float DelayTimer;

	//创建一个时间句柄
	FTimerHandle CloseDoorTimerHandle;

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

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

	//自定义AddDynamic绑定的触发器函数
	UFUNCTION()
	void OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
	UFUNCTION()
	void OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

	//将函数提升为事件
	UFUNCTION(BlueprintImplementableEvent, Category = "Triggerable Dorr|Trigger Switch")
	void RaiseTrigger();
	UFUNCTION(BlueprintImplementableEvent, Category = "Triggerable Dorr|Trigger Switch")
	void LowerTrigger();
	UFUNCTION(BlueprintImplementableEvent, Category = "Triggerable Dorr|Trigger Switch")
	void OpenDoor();
	UFUNCTION(BlueprintImplementableEvent, Category = "Triggerable Dorr|Trigger Switch")
	void CloseDoor();


	UFUNCTION(BlueprintCallable,Category="Triggerable Door | Triggerable Switch")
	void UpdateTriggerLocation(FVector offset);
	UFUNCTION(BlueprintCallable,Category= "Triggerable Door | Door Switch")
	void UpdateDoorLocation(FVector offset);
};
  • Trigger.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "TriggerDoor.h"
#include "Components/BoxComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Characters/Player/MainPlayer.h"
#include "TimerManager.h"

// Sets default values
ATriggerDoor::ATriggerDoor()
{
 	// 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;

	TriggerBox = CreateDefaultSubobject<UBoxComponent>(TEXT("TriggerBox"));
	RootComponent = TriggerBox;

	TriggerBox->SetBoxExtent(FVector(60.f, 60.f, 30.f));

	//设置TriggerBox碰撞的硬编码
	TriggerBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly);//设置碰撞类型
	TriggerBox->SetCollisionObjectType(ECollisionChannel::ECC_WorldStatic);//设置对象移动时其应视为某种物体
	TriggerBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);//设置所有的碰撞响应为忽略
	TriggerBox->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);//设置Pawn碰撞响应为重叠

	TriggerMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("TriggerMesh"));
	TriggerMesh->SetupAttachment(GetRootComponent());

	DoorMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorMesh"));
	DoorMesh->SetupAttachment(GetRootComponent());

	//延迟时间设为2秒
	DelayTimer = 2.0f;
	
}

// Called when the game starts or when spawned
void ATriggerDoor::BeginPlay()
{
	Super::BeginPlay();
	
	TriggerBox->OnComponentBeginOverlap.AddDynamic(this, &ATriggerDoor::OnOverlapBegin);
	TriggerBox->OnComponentEndOverlap.AddDynamic(this, &ATriggerDoor::OnOverlapEnd);

	//获取到门和触发器的位置
	InitTriggerLoction = TriggerMesh->GetComponentLocation();
	InitDoorLocation = DoorMesh->GetComponentLocation();
}

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

}

void ATriggerDoor::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);
	if (Player)
	{
		//清除定时器
		GetWorldTimerManager().ClearTimer(CloseDoorTimerHandle);
		OpenDoor();
		LowerTrigger();
	}
}

void ATriggerDoor::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);
	if (Player)
	{
		RaiseTrigger();
		
		//开启定时器进行关门
		GetWorldTimerManager().SetTimer(CloseDoorTimerHandle, this, &ATriggerDoor::CloseDoor, DelayTimer);
	}
}

void ATriggerDoor::UpdateTriggerLocation(FVector offset)
{
	//新位置=初始位置+移动后的位置
	FVector newLocation = InitTriggerLoction + offset;
	TriggerMesh->SetWorldLocation(newLocation);
}

void ATriggerDoor::UpdateDoorLocation(FVector offset)
{
	//新位置=初始位置+移动后的位置
	FVector newLocation = InitDoorLocation + offset;
	DoorMesh->SetWorldLocation(newLocation);
}

思路二

  • 定义一个bool变量用来检测角色是否踩到触发器
  • 我们采用Lambda表示来进行操作,使用Lambda表达式操作,SetTimer函数传入参数会不一样
GetWorldTimerManager().SetTimer(CloseDoorTimerHandle, FTimerDelegate::CreateLambda(CloseDoorLambda), DelayTimer, false);

在这里插入图片描述

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

#pragma once

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

UCLASS()
class UEGAME_API ATriggerDoor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ATriggerDoor();

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	class UBoxComponent* TriggerBox;

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	class UStaticMeshComponent* TriggerMesh;

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	UStaticMeshComponent* DoorMesh;

	//获取门与触发器位置
	UPROPERTY(BlueprintReadWrite,Category = "Trigger Door|Trigger Properties")
	FVector InitTriggerLoction;
	UPROPERTY(BlueprintReadWrite,Category = "Trigger Door|Door Properties")
	FVector InitDoorLocation;

	//延时时间
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trigger Door|Trigger Properties")
	float DelayTimer;

	
	bool bIsPlayerOnTrigger;

	//创建一个时间句柄
	FTimerHandle CloseDoorTimerHandle;


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

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

	//自定义AddDynamic绑定的触发器函数
	UFUNCTION()
	void OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
	UFUNCTION()
	void OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

	//将函数提升为事件
	UFUNCTION(BlueprintImplementableEvent, Category = "Triggerable Dorr|Trigger Switch")
	void RaiseTrigger();
	UFUNCTION(BlueprintImplementableEvent, Category = "Triggerable Dorr|Trigger Switch")
	void LowerTrigger();
	UFUNCTION(BlueprintImplementableEvent, Category = "Triggerable Dorr|Trigger Switch")
	void OpenDoor();
	UFUNCTION(BlueprintImplementableEvent, Category = "Triggerable Dorr|Trigger Switch")
	void CloseDoor();


	UFUNCTION(BlueprintCallable,Category="Triggerable Door | Triggerable Switch")
	void UpdateTriggerLocation(FVector offset);
	UFUNCTION(BlueprintCallable,Category= "Triggerable Door | Door Switch")
	void UpdateDoorLocation(FVector offset);
};
  • Tirgger.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "TriggerDoor.h"
#include "Components/BoxComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Characters/Player/MainPlayer.h"
#include "TimerManager.h"

// Sets default values
ATriggerDoor::ATriggerDoor()
{
 	// 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;

	TriggerBox = CreateDefaultSubobject<UBoxComponent>(TEXT("TriggerBox"));
	RootComponent = TriggerBox;

	TriggerBox->SetBoxExtent(FVector(60.f, 60.f, 30.f));

	//设置TriggerBox碰撞的硬编码
	TriggerBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly);//设置碰撞类型
	TriggerBox->SetCollisionObjectType(ECollisionChannel::ECC_WorldStatic);//设置对象移动时其应视为某种物体
	TriggerBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);//设置所有的碰撞响应为忽略
	TriggerBox->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);//设置Pawn碰撞响应为重叠

	TriggerMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("TriggerMesh"));
	TriggerMesh->SetupAttachment(GetRootComponent());

	DoorMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorMesh"));
	DoorMesh->SetupAttachment(GetRootComponent());

	//延迟时间设为2秒
	DelayTimer = 2.0f;
	bIsPlayerOnTrigger = false;
}

// Called when the game starts or when spawned
void ATriggerDoor::BeginPlay()
{
	Super::BeginPlay();
	
	TriggerBox->OnComponentBeginOverlap.AddDynamic(this, &ATriggerDoor::OnOverlapBegin);
	TriggerBox->OnComponentEndOverlap.AddDynamic(this, &ATriggerDoor::OnOverlapEnd);

	//获取到门和触发器的位置
	InitTriggerLoction = TriggerMesh->GetComponentLocation();
	InitDoorLocation = DoorMesh->GetComponentLocation();
}

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

}

void ATriggerDoor::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);
	if (Player)
	{
		//判断主角踩到触发器,就为真
		if (bIsPlayerOnTrigger == false)
		{
			bIsPlayerOnTrigger = true;
		}
		OpenDoor();
		LowerTrigger();
	}
}

void ATriggerDoor::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	AMainPlayer* Player = Cast<AMainPlayer>(OtherActor);
	//判断主角没踩到触发器,就为假
	if (bIsPlayerOnTrigger == true)
	{
		bIsPlayerOnTrigger = false;
	}
	//Lambda表达式
	auto CloseDoorLambda = [this]()
	{
		//主角没有踩到触发器就关门
		if (bIsPlayerOnTrigger == false)
		{
			CloseDoor();
		}
	};

	if(Player)
	{
		RaiseTrigger();
		//开启定时器进行关门
		GetWorldTimerManager().SetTimer(CloseDoorTimerHandle, FTimerDelegate::CreateLambda(CloseDoorLambda), DelayTimer, false);
	}
}

void ATriggerDoor::UpdateTriggerLocation(FVector offset)
{
	//新位置=初始位置+移动后的位置
	FVector newLocation = InitTriggerLoction + offset;
	TriggerMesh->SetWorldLocation(newLocation);
}

void ATriggerDoor::UpdateDoorLocation(FVector offset)
{
	//新位置=初始位置+移动后的位置
	FVector newLocation = InitDoorLocation + offset;
	DoorMesh->SetWorldLocation(newLocation);
}

Actor创建一个随机生成空间

  • 新建一个Actor类用来随机随机生成Actor,添加触发器与一个模版变量(专门用来存储AActor)
public:	
	// Sets default values for this actor's properties
	ASpawnVolume();

	//触发器
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
	class UBoxComponent* SpawnBox;

	//模版类,用来添加AActor
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Spawn Volume")
	TArray<TSubclassOf<AActor>> SpawnClass;

获取Actor随机位置与随机种类

  • 新建两个函数用来获取种类与位置
  • 头文件#include “Kismet/KismetMathLibrary.h” 中会有很多数学函数
  • 例如:RandomPointInBoundingBox:随机点在边界框
public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	//获取随机位置
	UFUNCTION(BlueprintCallable, Category = "Spawn Volume")
	FVector GetSpawnRandomLocation();

	UFUNCTION(BlueprintCallable, Category = "Spawn Volume")
	TSubclassOf<AActor> GetSpawnClass();
};
FVector ASpawnVolume::GetSpawnRandomLocation()
{
	FVector OriginPoint = SpawnBox->GetComponentLocation();
	FVector Extent = SpawnBox->GetScaledBoxExtent();
	
	return UKismetMathLibrary::RandomPointInBoundingBox(OriginPoint,Extent);
}

TSubclassOf<AActor> ASpawnVolume::GetSpawnClass()
{
	if (SpawnClass.Num() > 0)
	{
		int index = FMath::RandRange(0, SpawnClass.Num() - 1);
		return SpawnClass[index];
	}
	else
	{
		return nullptr;
	}
}

随机生成区域与蓝图本地事件机制

  • BlueprintPure参数:可以把函数变成纯净函数没有执行线
  • BlueprintNativeEvent参数一般要与BlueprintCallable一起使用,在蓝图中声明的本地事件使用BlueprintNativeEvent关键词进行标记。它告诉Unreal Engine系统该事件可以在C++中进行实现,并且可以在蓝图中被重写或使用。一旦在蓝图中声明了本地事件,就可以在C++中编写实现该事件的代码。通过继承蓝图类的C++类,可以对该本地事件进行重写或添加额外的逻辑。这样,当在蓝图中调用该本地事件时,将执行C++中实现的代码逻辑。总之,BlueprintNativeEvent关键词允许在Unreal Engine蓝图中声明本地事件,并通过C++进行实现和扩展。这样的设计模式提供了蓝图和C++之间的灵活交互,允许开发人员在保持蓝图的简洁性和可视化优势的同时,利用C++的强大功能来实现高级逻辑和性能优化
  • 在C++中实现一个蓝图本地事件时,通常需要在函数名后添加_Implementation作为后缀。例如,如果在蓝图中声明了一个本地事件函数MyEvent,那么在C++中实现它的函数名将是MyEvent_Implementation。这是因为在Unreal Engine中,蓝图本地事件的实现通常是通过虚函数重写机制来完成的。当蓝图中调用本地事件时,底层引擎会查找与事件函数同名的_Implementation函数,然后执行该函数中的代码逻辑。使用_Implementation作为后缀是一种约定,用于将原始的虚函数和其实现函数进行区分。这样做可以保持函数名的一致性,并且使代码更易于理解和维护。另外,_Implementation函数是在C++类中实现蓝图本地事件的默认方法。你也可以在自定义C++类中以其他的名字实现蓝图本地事件,只需在蓝图中指定相应的函数即可。
    总结起来,为了实现蓝图本地事件函数,需要在函数名后添加_Implementation作为后缀。这是为了区分虚函数和其实现函数,并保持代码的一致性和清晰度
  • GetWorld()->SpawnActor<AActor>(SpawnClasses, SpawnLocation, FRotator(0.0f)):生成Actor在世界中
  • SpawnActor:函数源码
	/** Templated version of SpawnActor that allows you to specify a class type via the template type */
	template< class T >
	T* SpawnActor( const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters() )
	{
		return CastChecked<T>(SpawnActor(T::StaticClass(), NULL, NULL, SpawnParameters),ECastCheckedType::NullAllowed);
	}

	/** Templated version of SpawnActor that allows you to specify location and rotation in addition to class type via the template type */
	template< class T >
	T* SpawnActor( FVector const& Location, FRotator const& Rotation, const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters() )
	{
		return CastChecked<T>(SpawnActor(T::StaticClass(), &Location, &Rotation, SpawnParameters),ECastCheckedType::NullAllowed);
	}
	
	/** Templated version of SpawnActor that allows you to specify the class type via parameter while the return type is a parent class of that type */
	template< class T >
	T* SpawnActor( UClass* Class, const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters() )
	{
		return CastChecked<T>(SpawnActor(Class, NULL, NULL, SpawnParameters),ECastCheckedType::NullAllowed);
	}

	/** 
	 *  Templated version of SpawnActor that allows you to specify the rotation and location in addition
	 *  class type via parameter while the return type is a parent class of that type 
	 */
	template< class T >
	T* SpawnActor( UClass* Class, FVector const& Location, FRotator const& Rotation, const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters() )
	{
		return CastChecked<T>(SpawnActor(Class, &Location, &Rotation, SpawnParameters),ECastCheckedType::NullAllowed);
	}
	/** 
	 *  Templated version of SpawnActor that allows you to specify whole Transform
	 *  class type via parameter while the return type is a parent class of that type 
	 */
	template< class T >
	T* SpawnActor(UClass* Class, FTransform const& Transform,const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters())
	{
		return CastChecked<T>(SpawnActor(Class, &Transform, SpawnParameters), ECastCheckedType::NullAllowed);
	}
SpawnVolume.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

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

UCLASS()
class UEGAME_API ASpawnVolume : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ASpawnVolume();

	//触发器
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
	class UBoxComponent* SpawnBox;

	//模版类,用来添加AActor的子类
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Spawn Volume")
	TArray<TSubclassOf<AActor>> SpawnClass;

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

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

	//获取随机位置
	UFUNCTION(BlueprintPure, Category = "Spawn Volume")
	FVector GetSpawnRandomLocation();

	UFUNCTION(BlueprintPure, Category = "Spawn Volume")
	TSubclassOf<AActor> GetSpawnClass();

	//蓝图本地事件机制
	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Spawn Volue")
	void SpawnActor(UClass* SpawnClasses, FVector SpawnLocation);
};

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


#include "SpawnVolume.h"
#include "Components/BoxComponent.h"
#include "Kismet/KismetMathLibrary.h"
// Sets default values
ASpawnVolume::ASpawnVolume()
{
 	// 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;

	SpawnBox = CreateDefaultSubobject<UBoxComponent>(TEXT("SpawnBox"));
	RootComponent = SpawnBox;

}

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

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

}

FVector ASpawnVolume::GetSpawnRandomLocation()
{
	FVector OriginPoint = SpawnBox->GetComponentLocation();
	FVector Extent = SpawnBox->GetScaledBoxExtent();
	return UKismetMathLibrary::RandomPointInBoundingBox(OriginPoint,Extent);
}

TSubclassOf<AActor> ASpawnVolume::GetSpawnClass()
{
	if (SpawnClass.Num() > 0)
	{
		int index = FMath::RandRange(0, SpawnClass.Num() - 1);
		return SpawnClass[index];
	}
	else
	{
		return 0;
	}
}
//生成Actor
void ASpawnVolume::SpawnActor_Implementation(UClass* SpawnClasses, FVector SpawnLocation)
{
	GetWorld()->SpawnActor<AActor>(SpawnClasses, SpawnLocation, FRotator(0.0f));
}
  • 蓝图中去调用写好的这个机制
    在这里插入图片描述
  • 运行结果
    在这里插入图片描述

Actor创建移动浮空平台

创建移动平台与EditWidget工具

  • 创建一个Actor类,这个Actor需要一个静态网格,两个变量(一个表示起点一个表示终点)
  • meta = (EditWidget = “true”):这是一个可选的元标记(Metadata),用于添加额外的属性信息。在这种情况下,MakeEditWidget 被设置为 “true”,这表示该属性可以在编辑器中作为一个小部件进行编辑。这通常用于自定义编辑器小部件以提供更直观的属性编辑体验。
public:	
	// Sets default values for this actor's properties
	AFloatingPlatform();
	UPROPERTY(VisibleAnywhere,BlueprintReadOnly)
	class UStaticMeshComponent* PlatformMesh;

	UPROPERTY(VisibleAnywhere, Category = "Floating Platform")
	FVector StartPoint{};

	UPROPERTY(EditAnywhere, Category = "Floating Platform", meta = (MakeEditWidget = "true"))
	FVector EndPoint{};

  • 运行结果
    在这里插入图片描述

使用VInterpTo进行插值

  • 新建一个变量PlatformSpeed用来设置上升速度
public:	
	// Sets default values for this actor's properties
	AFloatingPlatform();
	UPROPERTY(VisibleAnywhere,BlueprintReadOnly)
	class UStaticMeshComponent* PlatformMesh;

	UPROPERTY(VisibleAnywhere, Category = "Floating Platform")
	FVector StartPoint{};

	UPROPERTY(EditAnywhere, Category = "Floating Platform", meta = (MakeEditWidget = "true"))
	FVector EndPoint{};

	UPROPERTY(EditAnywhere, Category = "Floating Platform")
	float PlatformSpeed;
  • 使用VInterpTo进行移动
  • VInterpTo 源码
CORE_API FVector FMath::VInterpTo( const FVector& Current, const FVector& Target, float DeltaTime, float InterpSpeed )
{
	// If no interp speed, jump to target value
	if( InterpSpeed <= 0.f )
	{
		return Target;
	}

	// Distance to reach
	const FVector Dist = Target - Current;

	// If distance is too small, just set the desired location
	if( Dist.SizeSquared() < KINDA_SMALL_NUMBER )
	{
		return Target;
	}

	// Delta Move, Clamp so we do not over shoot.
	const FVector	DeltaMove = Dist * FMath::Clamp<float>(DeltaTime * InterpSpeed, 0.f, 1.f);

	return Current + DeltaMove;
}
  • VInterpConstantTo:匀速运动源码
CORE_API FVector FMath::VInterpConstantTo(const FVector Current, const FVector& Target, float DeltaTime, float InterpSpeed)
{
	const FVector Delta = Target - Current;
	const float DeltaM = Delta.Size();
	const float MaxStep = InterpSpeed * DeltaTime;

	if( DeltaM > MaxStep )
	{
		if( MaxStep > 0.f )
		{
			const FVector DeltaN = Delta / DeltaM;
			return Current + DeltaN * MaxStep;
		}
		else
		{
			return Current;
		}
	}

	return Target;
}
  • FloatingPlatform.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "FloatingPlatform.h"
#include "Components/StaticMeshComponent.h"
// Sets default values
AFloatingPlatform::AFloatingPlatform()
{
 	// 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;

	PlatformMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Platform"));
	RootComponent = PlatformMesh;

	PlatformSpeed = 2.f;
}

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

	//获取Actor的世界位置
	StartPoint = GetActorLocation();
	EndPoint += StartPoint;
}

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

	FVector CurrentLocation = GetActorLocation();
	FVector NewLocation = FMath::VInterpTo(CurrentLocation, EndPoint, DeltaTime, PlatformSpeed);
	SetActorLocation(NewLocation);//移动到新位置
}

使用定时器进行平台来回移动

  • 新建一个bool变量bInterping用来判断是否要开启定时器,新建一个DelayTime用来规定延时时间几秒后开启定时器,新键一个用来记录当前位置的变量Distance
  • 思路:bInterping默认为true,Distance获取到移动到目标点的位置,在移动过程中,新建一个NewDistace获取当前世界位置减去开始位置的值,判断Distance要是减去NewDistance的值大于等于一个很小的值,就说明到最终位置了,此时的bInterping为false了,该开启定时器返回起点,2秒后定时器中函数表达式bInterping为真,互换起点与终点位置,又进行新一轮平台移动
FloatingPlatform.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

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

UCLASS()
class UEGAME_API AFloatingPlatform : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AFloatingPlatform();
	UPROPERTY(VisibleAnywhere,BlueprintReadOnly)
	class UStaticMeshComponent* PlatformMesh;

	UPROPERTY(VisibleAnywhere, Category = "Floating Platform")
	FVector StartPoint{};

	UPROPERTY(EditAnywhere, Category = "Floating Platform", meta = (MakeEditWidget = "true"))
	FVector EndPoint{};

	UPROPERTY(EditAnywhere, Category = "Floating Platform")
	float PlatformSpeed;

	UPROPERTY(EditAnywhere, Category = "Floating Platform")
	float DelayTime;


	FTimerHandle InterpTimerHandle;
	bool bInterping;
	float Distance;
protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

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

};
FloatingPlatform.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "FloatingPlatform.h"
#include "Components/StaticMeshComponent.h"
#include "TimerManager.h"
// Sets default values
AFloatingPlatform::AFloatingPlatform()
{
 	// 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;

	PlatformMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("PlatformMesh"));
	RootComponent = PlatformMesh;

	PlatformSpeed = 200.f;
	DelayTime = 2.0f;
	bInterping = true;
}

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

	//获取Actor的世界位置
	StartPoint = GetActorLocation();
	Distance = EndPoint.Size();
	EndPoint += StartPoint;

}

// Called every frame
void AFloatingPlatform::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	if (bInterping)
	{
		FVector CurrentLocation = GetActorLocation();
		FVector NewLocation = FMath::VInterpConstantTo(CurrentLocation, EndPoint, DeltaTime, PlatformSpeed);
		SetActorLocation(NewLocation);//移动到新位置

		float NewDistance = (GetActorLocation() - StartPoint).Size();

		if (Distance - NewDistance <= 0.5f)
		{
			bInterping = !bInterping;

			//Lambda表达式
			auto ToggleInterpState = [this]()
			{
				bInterping = !bInterping;
			};
			
			//开启定时器
			GetWorldTimerManager().SetTimer(InterpTimerHandle, FTimerDelegate::CreateLambda(ToggleInterpState), DelayTime, false);

			//交换起点与终点
			FVector temp = StartPoint;
			StartPoint = EndPoint;
			EndPoint = temp;
		}
	}

}
  • 运行结果
    请添加图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Godot4实现游戏的多语言版本

要在Godot 4中实现多语言版本的游戏&#xff0c;您需要按照以下几个步骤来设置和管理游戏文本以及可能的其他资源&#xff0c;如图像或声音。以下是根据官方文档和详细教程整理的简明指南&#xff1a; 准备翻译文件&#xff1a; Godot支持使用.csv文件或.po文件进行国际化​​…

SPASS-交叉表分析

导入数据 修改变量测量类型 分析->描述统计->交叉表 表中显示行、列变量通过卡方检验给出的独立性检验结果。共使用了三种检验方法。上表各种检验方法显著水平sig.都远远小于0.05,所以有理由拒绝实验准备与评价结果是独立的假设&#xff0c;即认为实验准备这个评价指标是…

javaSE学习笔记(二)数组,类,对象,成员变量,匿名对象,构造方法,static,final,封装,继承,多态

目录 三、面向对象 1.概述 面向过程与面向对象 面向对象编程特点 面向对象三个基本特征 2.数组 数组定义格式 数组的初始化 动态初始化 静态初始化 数组的内存分配 Java中的内存分配 数组的内存分配 数组的角标 数组的基本操作 二维数组&#xff08;实际开发几乎…

VR全景技术,为养老院宣传推广带来全新变革

现如今&#xff0c;人口老龄化的现象加剧&#xff0c;养老服务行业也如雨后春笋般不断冒头&#xff0c;但是市面上各式的养老院被包装的五花八门&#xff0c;用户实际参访后却差强人意&#xff0c;如何更好的给父母挑选更为舒心的养老环境呢&#xff1f;可以利用720度VR全景技术…

JVM-垃圾回收

目录 1、GC过程 2、垃圾回收算法 2.1、标记-清除 2.2、标记-整理 2.3、复制 2.4、分代收集算法 3、TLAB 4、对象如何进入老年代 5、卡片标记 6、HotSpot垃圾回收器 6.1、年轻代垃圾回收器 6.2、老年代垃圾回收器 6.3、如何配置垃圾回收器 6.4、STW 7、CMS垃圾回收…

Qt 继承QAbstractTableModel实现自定义TableModel

1.简介 QAbstractTableModel是Qt框架中的一个抽象类&#xff0c;用于实现数据模型&#xff0c;用于在Qt的视图组件中展示和编辑表格数据。它提供了一些基本的接口和默认实现&#xff0c;可以方便地创建自定义的表格数据模型。 QAbstractTableModel的主要功能包括以下几点&…

吃啥大转盘

经常跟朋友出去吃饭&#xff0c;选择太困难了所以写了个简单的转盘&#xff0c;直接copy到txt文本然后把文件后缀改成html即可。 需要换食物直接在文件中找到 list 值按照格式替换一下即可。 效果&#xff1a; 代码块&#xff1a; <html><head><meta http-…

制作一个用户登录界面

Flask-WTF扩展使用Python类来表示web表单。表单类只是将表单的字段定义为类变量。 再次考虑到分离的问题&#xff0c;我将使用一个新的app/forms.py模块来存储我的web表单类。首先&#xff0c;让我们定义一个用户登录表单&#xff0c;它要求用户输入用户名和密码。表单还将包括…

Window 11中安装Rust编译环境和集成开发环境

https://blog.csdn.net/weixin_43882409/article/details/87616268是我参考的一篇文章。 下载 先到https://www.rust-lang.org/learn/get-started&#xff0c;下载64-Bit&#xff08;64位&#xff09;的rustup-init.exe文件。 使用其他方式进行安装的网址https://forge.rust…

Unity Mirror学习(二) Command属性使用

Command&#xff08;命令&#xff09;属性 1&#xff0c;修饰方法的&#xff0c;当在客户端调用此方法&#xff0c;它将在服务端运行&#xff08;我的理解&#xff1a;客户端命令服务端做某事&#xff1b;或者说&#xff1a;客户端向服务端发消息&#xff0c;消息方法&#xff…

(StateFlow ShareFlow) VS (Flow LiveData)

目录 数据流程 大致流程 切换线程 StateFlow & ShareFlow StateFlow 构建StateFlow &ShareFlow 在之前的Flow&#xff0c;collect函数浅析和仿Flow构建器创建数据流文章中我们探索了flow的简单使用及它的简单原理&#xff0c;但是生产过程中我们往往会借用这些基…

小程序订单中心path设置本次审核不通过,审核原因:小程序尚未发布,无法审核。

小程序尚未发布&#xff0c;无法审核。 先按照这篇文章把小程序审核通过&#xff0c;小程序版本审核未通过&#xff0c;需在开发者后台「版本管理—提交审核——小程序订单中心path」设置订单中心页path&#xff0c;请设置后再提交代码审核 小程序审核通过后&#xff0c;发布…

高效自学-网络安全(黑客技术)

一、网络安全应该怎么学&#xff1f; 1.计算机基础需要过关 这一步跟网安关系暂时不大&#xff0c;是进入it行业每个人都必须掌握的基础能力。 计算机网络计算机操作系统算法与数据架构数据库 Tips:不用非要钻研至非常精通&#xff0c;可以与学习其他课程同步进行。 2.渗透技…

Vue计算属性 computed

计算属性&#xff1a;常用于逻辑比较复杂的计算&#xff0c;根据已有的数据&#xff0c;计算出一个新的数据。计算属性具有缓存机制&#xff0c;复用性强&#xff0c;效率更高。 计算属性【简写方式】&#xff1a; <template><div>提现金额&#xff1a;<input …

【漏洞复现】XXL-JOB默认accessToken身份绕过远程命令执行漏洞

漏洞描述 xxl-job是一个中心式分布式的调度平台,调度中心和执行器解耦,执行器和业务代码耦合,代码的侵入性少,学习简单、开发简单、轻量级 XXL-JOB 是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线…

树莓派4B的测试记录(CPU、FFMPEG)

本文是用来记录树莓派 4B 的一些测试记录。 温度 下面记录中的风扇和大风扇是这样的&#xff1a; 为什么要用大风扇呢&#xff1f;因为小风扇在外壳上&#xff0c;气流通过外壳的珊格会有啸叫&#xff0c;声音不大但是很烦人&#xff0c;大风扇没这个问题&#xff0c;并且同样…

【计算机网络基础实验】实验二(补充内容)路由器的配置和静态路由

任务一 IP路由协议实现企业路由器通信 目录如下&#xff1a; 任务一 IP路由协议实现企业路由器通信一、实验目的&#xff1a;二、实验环境三、实验内容四、实验步骤1、路由器的基本配置&#xff08;1&#xff09;实验拓扑图&#xff08;2&#xff09;启动路由器&#xff08;3&a…

k8s-Pod控制器

一、Pod控制器 1.Pod控制器及其功用 Pod控制器&#xff0c;又称之为工作负载&#xff08;workload&#xff09;&#xff0c;是用于实现管理pod的中间层&#xff0c;确保pod资源符合预期的状态&#xff0c;pod的资源出现故障时&#xff0c;会尝试进行重启&#xff0c;当根据重启…

猫头虎博主第7期赠书活动:《计算机考研精炼1000题》

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

设计模式(3)-结构型模式

结构型模式 结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式&#xff0c;前者采用继承机制来组织接口和类&#xff0c;后者釆用组合或聚合来组合对象。 由于组合关系或聚合关系比继承关系耦合度低&#xff0c;满足“合成复用原则…