创建一个Actor类,添加两个静态网格与一个触发器
头文件:
// 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;
};
// 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);
}
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)
{
}
//设置TriggerBox碰撞的硬编码
TriggerBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly);//设置碰撞类型
TriggerBox->SetCollisionObjectType(ECollisionChannel::ECC_WorldStatic);//设置对象移动时其应视为某种物体
TriggerBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);//设置所有的碰撞响应为忽略
TriggerBox->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);//设置Pawn碰撞响应为重叠
// 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);
};
// 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)
{
}
}
//获取门与触发器位置
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);
// 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);
}
FTimerHandle
CloseDoorTimerHandle: 创建一个时间句柄/**
* 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);
}
// 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);
};
// 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);
}
GetWorldTimerManager().SetTimer(CloseDoorTimerHandle, FTimerDelegate::CreateLambda(CloseDoorLambda), DelayTimer, false);
// 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);
};
// 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);
}
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;
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;
}
}
在蓝图中声明的本地事件使用BlueprintNativeEvent关键词进行标记
。它告诉Unreal Engine系统该事件可以在C++中进行实现
,并且可以在蓝图中被重写或使用
。一旦在蓝图中声明了本地事件,就可以在C++中编写实现该事件的代码。通过继承蓝图类的C++类,可以对该本地事件进行重写或添加额外的逻辑。这样,当在蓝图中调用该本地事件时,将执行C++中实现的代码逻辑。总之,BlueprintNativeEvent关键词允许在Unreal Engine蓝图中声明本地事件,并通过C++进行实现和扩展。这样的设计模式提供了蓝图和C++之间的灵活交互,允许开发人员在保持蓝图的简洁性和可视化优势的同时,利用C++的强大功能来实现高级逻辑和性能优化
这是因为在Unreal Engine中,蓝图本地事件的实现通常是通过虚函数重写机制来完成的。当蓝图中调用本地事件时,底层引擎会查找与事件函数同名的_Implementation函数,然后执行该函数中的代码逻辑。使用_Implementation作为后缀是一种约定,用于将原始的虚函数和其实现函数进行区分。
这样做可以保持函数名的一致性,并且使代码更易于理解和维护。另外,_Implementation函数是在C++类中实现蓝图本地事件的默认方法。你也可以在自定义C++类中以其他的名字实现蓝图本地事件,只需在蓝图中指定相应的函数即可。总结起来,为了实现蓝图本地事件函数,需要在函数名后添加_Implementation作为后缀。这是为了区分虚函数和其实现函数,并保持代码的一致性和清晰度
GetWorld()
->SpawnActor
<AActor>(SpawnClasses, SpawnLocation, FRotator(0.0f)):生成Actor在世界中 /** 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);
}
// 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);
};
// 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));
}
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{};
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;
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;
}
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;
}
// 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);//移动到新位置
}
// 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;
};
// 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;
}
}
}