Unreal Quest1の課題をこなしながら学習していく。
今回はUnreal Quest1の1日目初級、動く床を作成する。
スイッチで動くようにした。
C++とMultiplayについても考える。
Blueprint Only
動く床
PressとReleaseのイベントはBPI(Blueprint Interface)で作成。
スイッチOnでPressイベント、スイッチOffでReleaseイベントが実行される。
タイムラインの中身。
スイッチを使わない場合、AutoPlayをONにして左側の実行ピンに何もつながなければ良い。
往復にかかる時間を設定するための処理。
Timeline Lengthをとってきているのは、Lengthが変わっても往復時間を正常にとれるようにするため。
スイッチ
スイッチの上に乗ると、ボタンが下に押し込まれていく。
完全に押し込まれたときPressイベントが動く床に伝わる。
スイッチから離れると、ボタンが元の位置に戻っていく。
完全に元に戻るとReleaseイベントが動く床に伝わる。
ボタンの押し込まれ具合で、マテリアルの発行度合いを変化。
PressとReleaseのイベントの際に色を変化させている。
マテリアルを動的に変化させるために、DMI(Dynamic Material Instance)とMPC(Material Parameter Collection)を使ってみた。
Dynamic Material Instance
アクターごとに、設定したマテリアルのマテリアルインスタンスを作成。
👇DMIを使ったスイッチのBlueprint
複数のマテリアルを持つ場合は下のように変更できる。
Meshがもとから持つマテリアルをDMIを作る場合は、SourceMaterialの部分にGetMaterialをつなげれば良い。
今回は、単純なマテリアルを使いたかったので下のマテリアルを設定した。
Material Parameter Collection
同じマテリアルは全て色が変わってしまう。
同じマテリアルをもつ複数のスイッチを作成した場合、ひとつのスイッチを押したら他のスイッチの色も同時に変化してしまう。
スイッチのマテリアルとして使うには不向き。
簡単な仕組み
・BPでMPCの数値を変化
・MPCの変化に応じてマテリアルが変化
👇MPCを使ったスイッチのBlueprint
BP and C++
👇質量をカウントして、しきい値を超えたときに押し込まれるように設計。
(現時点の方針)
・マテリアルに関すること全般はBP
・StaticMeshはDefault値だけC++
・BPで必要な調整をできるようにC++で設計
C++だとマテリアルの変更が大変。マテリアル変更の役に立つ関数や数値はC++で作成しておく。
StaticMeshはDefault値として単純なものを作っておく。基本的にエンジンコンテンツで。
機能に必要な調整は、BPで出来るようにしておく。StaticMeshに変更を加えたときなどに不具合が生じないようにする。スイッチの例でいうと、スイッチを踏んだ時に押し込まれる幅などをMeshを変えても調整できるようにしておく。
簡単な流れ
スイッチで別のアクター動かすときの簡単な流れをまとめる。
インターフェースで関数を作成。簡単。親クラスはUnreal Interface。
public:
UFUNCTION(BlueprintNativeEvent)
void PressEvent();
ヘッダファイル
アクターのRefを作成。
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Switch")
AActor* ActorRef;
トリガーとなるCollisionを作成。
UBoxComponentは前にclassを付ける必要あり。
//SwitchCPP.h
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
USceneComponent* Scene;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
class UBoxComponent* CollisionBox;
//SwitchCPP.cpp
ASwitchCPP::ASwitchCPP()
{
Scene = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
CollisionBox = CreateDefaultSubobject<UBoxComponent>(TEXT("Box Collision"));
Scene->SetupAttachment(GetRootComponent());
CollisionBox->SetupAttachment(Scene);
CollisionBox->SetBoxExtent(FVector(32.f, 32.f, 32.f));
CollisionBox->SetCollisionProfileName("Trigger");
}
OnComponentBeginOverlapのイベントを作成。
UFUNCTION()は必要。
//SwitchCPP.h
UFUNCTION()
void OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
//SwitchCPP.cpp
ASwitchCPP::ASwitchCPP()
{
CollisionBox->OnComponentBeginOverlap.AddDynamic(this, &ASwitchCPP::OnOverlapBegin);
}
ソースファイル
interfaceとBoxComponentをインクルード。
アクターがインターフェースを実装していることを確認して関数を実行。
#include "SwitchCPP.h"
#include <Components/BoxComponent.h>
#include "SwitchInterface.h"
void ASwitchCPP::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
ISwitchInterface* Interface = Cast<ISwitchInterface>(ActorRef);
if (Interface)
{
Interface->Execute_PressEvent(ActorRef);
}
}
動かすアクターでインターフェースを実装。includeとpublicの部分にインターフェースを記述。
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SwitchInterface.h"
#include "MovingFloor.generated.h"
UCLASS()
class UNREALQUEST1_API AMovingFloor : public AActor, public ISwitchInterface
{
GENERATED_BODY()
}
動かすアクターで処理を記述
// MovingFloor.h
public:
virtual void PressEvent_Implementation();
// MovingFloor.cpp
void AMovingFloor::PressEvent_Implementation()
{
//アクターを動かす処理を記述
}
動く床
MovingFloor
MovingFloor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Net/UnrealNetwork.h"
#include "Engine/Engine.h"
#include "SwitchInterface.h"
#include "MovingFloor.generated.h"
UCLASS()
class UNREALQUEST1_API AMovingFloor : public AActor, public ISwitchInterface
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMovingFloor();
/** Property replication */
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
protected:
// Components
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="MovingFloor|Components")
USceneComponent* Scene;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "MovingFloor|Components")
UStaticMeshComponent* Floor;
// Moving Floor Properties
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Replicated ,Category = "MovingFloor")
FVector FloorRelLoc;
UPROPERTY(BlueprintReadWrite, Category="MovingFloor")
bool bIsForward;
UPROPERTY(BlueprintReadWrite, Category = "MovingFloor")
FVector StartLoc;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MovingFloor", Meta = (MakeEditWidget = true))
FVector MaxMovement;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MovingFloor")
float Speed;
UPROPERTY(BlueprintReadOnly, Category = "MovingFloor")
float MoveAlpha;
// Implementable Events
UFUNCTION(BlueprintImplementableEvent, Category="MovingFloor")
void OnMaxDistance();
UFUNCTION(BlueprintImplementableEvent, Category = "MovingFloor")
void OnMinDistance();
UFUNCTION(BlueprintImplementableEvent, Category = "MovingFloor")
void OnPressEvent();
UFUNCTION(BlueprintImplementableEvent, Category = "MovingFloor")
void OnReleaseEvent();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Movement Flag
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MovingFloor")
bool bIsMoving = false;
// Switch Interface Functions
virtual void PressEvent_Implementation();
virtual void ReleaseEvent_Implementation();
};
MovingFloor.cpp
#include "MovingFloor.h"
#include <Engine/Engine.h>
// Sets default values
AMovingFloor::AMovingFloor()
{
// 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;
// Replicates ON
AMovingFloor::bReplicates = true;
// Scene
Scene = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
Scene->SetupAttachment(GetRootComponent());
// Floor
Floor = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StatecMeshComponent"));
Floor->SetupAttachment(Scene);
FString CubeName = "/Engine/BasicShapes/Cube";
Floor->SetStaticMesh(LoadObject<UStaticMesh>(NULL, *CubeName));
FloorRelLoc = FVector(0, 0, 9);
Floor->SetRelativeLocation(FloorRelLoc);
Floor->SetRelativeScale3D(FVector(2.0, 2.0, 0.175));
// Initial Setting
bIsForward = true;
StartLoc = FloorRelLoc;
Speed = 50;
}
//////////////////////////////////////////////////////////////////////////
// Replicated Properties
void AMovingFloor::GetLifetimeReplicatedProps(TArray <FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
//Replicate FloorRelLoc.
DOREPLIFETIME(AMovingFloor, FloorRelLoc);
}
// Called when the game starts or when spawned
void AMovingFloor::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AMovingFloor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (bIsMoving)
{
FVector Movement = FloorRelLoc - StartLoc;
float Distance = sqrt(std::pow(Movement.X, 2) + std::pow(Movement.Y, 2) + std::pow(Movement.Z, 2));
float MaxDistance = sqrt(std::pow(MaxMovement.X, 2) + std::pow(MaxMovement.Y, 2) + std::pow(MaxMovement.Z, 2));
float DeltaDistance = DeltaTime * Speed;
FVector UnitVec = MaxMovement / MaxDistance;
FVector DeltaMove = UnitVec * DeltaDistance;
MoveAlpha = FMath::Clamp(Distance / MaxDistance, 0, 1);
// Move
if (bIsForward)
{
FloorRelLoc += DeltaMove;
}
else
{
FloorRelLoc -= DeltaMove;
}
Floor->SetRelativeLocation(FloorRelLoc);
// Turn
if (Distance > MaxDistance)
{
bIsForward = false;
FloorRelLoc -= 3 * DeltaMove;
Floor->SetRelativeLocation(FloorRelLoc);
OnMaxDistance();
}
else if (Distance < DeltaDistance)
{
bIsForward = true;
FloorRelLoc += 3 * DeltaMove;
Floor->SetRelativeLocation(FloorRelLoc);
OnMinDistance();
}
}
}
// Called by SwitchInterface
void AMovingFloor::PressEvent_Implementation()
{
OnPressEvent();
bIsMoving = true;
}
void AMovingFloor::ReleaseEvent_Implementation()
{
OnReleaseEvent();
bIsMoving = false;
}
BP_MovingFloorCPP
動きはじめたら色が変化して、止まったら元の色に戻る。
初期値からの移動割合(MoveAlpha)を用いて、動的に色を変化させてみた。
スイッチ
SwitchCPP
スイッチのCollisionに接しているComponentの質量の合計が、一定の値を超えたらボタンが下に沈み込んでいく。
完全に押し込まれたらスイッチが起動して動く床に情報が伝わる。
SwitchCPP.h
#pragma once
//#include "SwitchInterface.h"
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SwitchCPP.generated.h"
UCLASS()
class UNREALQUEST1_API ASwitchCPP : public AActor //, public ISwitchInterface
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ASwitchCPP();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Components
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Switch|Components")
USceneComponent* Scene;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Switch|Components")
class UBoxComponent* CollisionBox;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Switch|Components")
UStaticMeshComponent* Base;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Switch|Components")
UStaticMeshComponent* Button;
TArray<UPrimitiveComponent*> OverlappingComponents;
UPROPERTY(BlueprintReadOnly)
int32 OverlappingNum;
// Mass Properties
UPROPERTY(BlueprintReadOnly, Category = "Switch|Mass")
float TotalMass;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Switch|Mass")
float ThreshMass;
// Button Move
UPROPERTY(BlueprintReadWrite, Category = "Switch|ButtonMove")
float MaxHeight;
UPROPERTY(BlueprintReadWrite, Category = "Switch|ButtonMove")
float MinHeight;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Switch|ButtonMove")
float Max;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Switch|ButtonMove")
float Speed;
UPROPERTY(BlueprintReadWrite, Category = "Switch|ButtonMove")
bool bIsUp;
UPROPERTY(BlueprintReadWrite, Category = "Switch|ButtonMove")
bool bIsMoving;
UPROPERTY(BlueprintReadOnly, Category = "Switch|ButtonMove")
float MoveAlpha;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Switch|ButtonMove")
FVector BtnRelLoc;
// Implementable Event
UFUNCTION(BlueprintImplementableEvent, Category = "Switch|ImplementableEvent")
void OnPressEvent();
UFUNCTION(BlueprintImplementableEvent, Category = "Switch|ImplementableEvent")
void OnReleaseEvent();
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Reference
UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category = "Switch|Ref")
TArray<AActor*> ActorRefs;
// Collision Function
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);
};
SwitchCPP.cpp
#include "SwitchCPP.h"
#include <Components/BoxComponent.h>
#include "SwitchInterface.h"
// Sets default values
ASwitchCPP::ASwitchCPP()
{
// 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;
// Component Properties
// Scene
Scene = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
Scene->SetupAttachment(GetRootComponent());
// Base
Base = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("BaseMesh"));
Base->SetupAttachment(Scene);
FString BaseName = "/Engine/BasicShapes/Cube";
Base->SetStaticMesh(LoadObject<UStaticMesh>(NULL, *BaseName));
Base->SetRelativeLocation(FVector(0, 0, 0));
Base->SetRelativeScale3D(FVector(1.5, 1.5, 0.1));
// Button
Button = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ButtonMesh"));
Button->SetupAttachment(Scene);
FString ButtonName = "/Engine/BasicShapes/Cube";
Button->SetStaticMesh(LoadObject<UStaticMesh>(NULL, *ButtonName));
Button->SetRelativeLocation(FVector(0, 0, 7));
Button->SetRelativeScale3D(FVector(1.3, 1.3, 0.07));
// CollisionBox
CollisionBox = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxCollision"));
CollisionBox->SetupAttachment(Button);
CollisionBox->SetCollisionProfileName("Trigger");
CollisionBox->SetRelativeLocation(FVector(0, 0, 100.f));
CollisionBox->SetBoxExtent(FVector(40.f, 40.f, 6.f));
// AddDynamic
CollisionBox->OnComponentBeginOverlap.AddDynamic(this, &ASwitchCPP::OnOverlapBegin);
CollisionBox->OnComponentEndOverlap.AddDynamic(this, &ASwitchCPP::OnOverlapEnd);
// ButtonMove Initial
Speed = 5;
Max = 5;
// Mass Initial
ThreshMass = 80;
}
// Called when the game starts or when spawned
void ASwitchCPP::BeginPlay()
{
Super::BeginPlay();
// ButtonMove Initial Setting
BtnRelLoc = Button->GetRelativeLocation();
MaxHeight = BtnRelLoc.Z;
MinHeight = BtnRelLoc.Z - Max;
bIsUp = false;
bIsMoving = false;
}
// Called every frame
void ASwitchCPP::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// ButtonMove
const float Val = DeltaTime * Speed;
MoveAlpha = (MaxHeight - BtnRelLoc.Z) / Max;
if (bIsMoving)
{
if (bIsUp)
{
BtnRelLoc.Z += Val;
}
else
{
BtnRelLoc.Z -= Val;
}
Button->SetRelativeLocation(BtnRelLoc);
}
if (BtnRelLoc.Z > MaxHeight)
{
bIsMoving = false;
bIsUp = false;
BtnRelLoc.Z = MaxHeight - Val;
// ReleaseEvent(Switch Interface Function)
for (AActor* ActorRef : ActorRefs)
{
ISwitchInterface* Interface = Cast<ISwitchInterface>(ActorRef);
if (Interface)
{
Interface->Execute_ReleaseEvent(ActorRef);
OnReleaseEvent();
}
}
}
else if (BtnRelLoc.Z < MinHeight)
{
bIsMoving = false;
bIsUp = true;
BtnRelLoc.Z = MinHeight + Val;
// PressEvent(Switch Interface Function)
for (AActor* ActorRef : ActorRefs)
{
ISwitchInterface* Interface = Cast<ISwitchInterface>(ActorRef);
if (Interface)
{
Interface->Execute_PressEvent(ActorRef);
OnPressEvent();
}
}
}
}
// Collision Function
void ASwitchCPP::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
TotalMass = 0.f;
CollisionBox->GetOverlappingComponents(OUT OverlappingComponents);
for (UPrimitiveComponent* OverlappingComponent : OverlappingComponents)
{
switch (OverlappingComponent->GetCollisionObjectType())
{
case ECC_Pawn:
TotalMass += 100;
break;
default:
TotalMass += OverlappingComponent->GetMass();
break;
}
}
if (TotalMass > ThreshMass)
{
// Button Move Down
bIsUp = false;
bIsMoving = true;
}
}
void ASwitchCPP::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
TotalMass = 0.f;
CollisionBox->GetOverlappingComponents(OUT OverlappingComponents);
for (UPrimitiveComponent* OverlappingComponent : OverlappingComponents)
{
switch (OverlappingComponent->GetCollisionObjectType())
{
case ECC_Pawn:
TotalMass += 100;
break;
default:
TotalMass += OverlappingComponent->GetMass();
break;
}
}
if (TotalMass <= ThreshMass)
{
// Button Move Up
bIsUp = true;
bIsMoving = true;
}
}
BP_SwitchCPP
インターフェース
SwitchInterface
親クラスはUnreal Interface。BPにおけるBPI(BlueprintInterface)と同じ役割。
SwitchInterface.h
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "SwitchInterface.generated.h"
// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class USwitchInterface : public UInterface
{
GENERATED_BODY()
};
/**
*
*/
class UNREALQUEST1_API ISwitchInterface
{
GENERATED_BODY()
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
UFUNCTION(BlueprintNativeEvent)
void PressEvent();
UFUNCTION(BlueprintNativeEvent)
void ReleaseEvent();
};
SwitchInterface.cpp
#include "SwitchInterface.h"
// Add default functionality here for any ISwitchInterface functions that are not pure virtual.
Multiplay
動く床をはじめから動かすと、ServerとClientログインのタイミングの差で動きがずれる。
C++では、動く床の場所の変数をReplicatedにすることで動きを揃えることはできた。
BPではタイムラインを使っていたためか揃えられなかった。
いろんな変数をReplicatedにしてみたがダメだった。
どうやらタイムラインのためのRelicate方法があるようだ。
👇BPのFloor。ServerとClientで動きがずれている。(上のアイコンで解消できた。)
👇C++で変数をReprecatedにするのは少し手間がかかる。
ヘッダファイル
UnrealNetwork.hをインクルードする。
#include "Net/UnrealNetwork.h"
コンストラクタの直下でGetLifetimereplicatedProps関数を宣言
/** Property replication */
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
Replicateしたい変数のプロパティの指定子(Specifier)を付け足す。
RepNotifyする場合は、ReplicatedUsing=変数名。
Replicatedの場合は、Replicatedと書けばよい。
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, ReplicatedUsing=OnRep_FloorRelLoc ,Category = "Components")
FVector FloorRelLoc;
RepNotifyの場合、OnRepの関数を作成。
// RepNotify
UFUNCTION()
void OnRep_FloorRelLoc();
ソースファイル
GetLifetimereplicatedProps関数の中身を書く。
下のコードの場合、クラス名はAMovingFloorで、変数名はFloorRelLoc。
この部分に自分が編集しているクラス名と、Reprecateしたい変数名を入れて修正。
//////////////////////////////////////////////////////////////////////////
// Replicated Properties
void AMovingFloor::GetLifetimeReplicatedProps(TArray <FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
//Replicate FloorRelLoc.
DOREPLIFETIME(AMovingFloor, FloorRelLoc);
}
ソースファイルでbReplicatesをtrueにする。
AMovingFloor::bReplicates = true;
OnRepの関数の中身を適当に作成。
知識・考察
これで一応変更できたけど、マテリアルの変更はBPで僕はやる。
header file
UPROPERTY(VisibleInstanceOnly)
UMaterialInterface* BlueMat = nullptr;
source file
FString BlueMatName = "/Game/LevelPrototyping/Materials/MI_Solid_Blue";
static ConstructorHelpers::FObjectFinder<UMaterialInterface> BlueMatAsset(*BlueMatName);
BlueMat = BlueMatAsset.Object;
Floor->SetMaterial(0, BlueMat);
Round Trip
to A and back
both ways
メタタグのとこ。
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = “MyCategory”, Meta = (MakeEditWidget = true))
print stringみたいなやつ。
#include <Engine/Engine.h>
GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, "Overlap End Function Called");
同じ文字にしてはいけない。BaseとButtonを両方ともStaticMeshにしたらエラー。
Scene = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
CollisionBox = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxCollision"));
Base = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("BaseMesh"));
Button = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ButtonMesh"));
実行順(たぶん)
C++のConstructor
↓
(UEのエディタ操作)
↓
BPのConstructor
↓
BPのBeginPlay
↓
C++のBeginPlay
方針
C++のConstructorでデフォルト値を設定。
UEのエディタでメッシュの差し替え、位置の調整など。
BPのConstructorで初期設定。
BPのBeginPlayで初期設定。
C++のBeginPlayでは、BP側でメッシュや位置などが調整されても不具合が起きないように、最終的な初期設定をする。
考察
BPのConstructorと、C++のConstructorは実行のタイミングが違う。
BPのConstructorの処理はUEのエディタで操作した後に実行されるが、C++のConstructorの処理はUEのエディタで操作する前に実行されているようだ。
したがって、C++のConstructorで設定したことはUEのエディタで書き換えが可能。BPのConstructorで設定したことはUEのエディタで書き換えても、不意にもとに戻ってしまう。
なので、C++のConstructorは書き換え可能なデフォルト値を、BPのConstructorは初期値を設定するのに適していると思われる。
閾値
👇単語説明。すごく詳しくて驚いた。Thresholdの説明なのに性癖の話までしている。
F12で調べ、コピペしたやつ。
void UPrimitiveComponent::GetOverlappingComponents(TArray<UPrimitiveComponent*>& OutOverlappingComponents) const
{
if (OverlappingComponents.Num() <= 12)
{
// TArray with fewer elements is faster than using a set (and having to allocate it).
OutOverlappingComponents.Reset(OverlappingComponents.Num());
for (const FOverlapInfo& OtherOverlap : OverlappingComponents)
{
UPrimitiveComponent* const OtherComp = OtherOverlap.OverlapInfo.Component.Get();
if (OtherComp)
{
OutOverlappingComponents.AddUnique(OtherComp);
}
}
}
else
{
// Fill set (unique)
TSet<UPrimitiveComponent*> OverlapSet;
GetOverlappingComponents(OverlapSet);
// Copy to array
OutOverlappingComponents.Reset(OverlapSet.Num());
for (UPrimitiveComponent* OtherOverlap : OverlapSet)
{
OutOverlappingComponents.Add(OtherOverlap);
}
}
}
TotalMass = 0.f;
CollisionBox->GetOverlappingComponents(OUT OverlappingComponents);
for (UPrimitiveComponent* PrimitiveComponent : OverlappingComponents)
{
TotalMass += PrimitiveComponent->GetMass();
}}
OverlappingComponentsの0番目の要素を取り出してIF文で使ったらエラー出た。
出てくるエラー。
// Template property, branch will be optimized out
if (AllocatorType::RequireRangeCheck)
{
checkf((Index >= 0) & (Index < ArrayNum),TEXT("Array index out of bounds: %i from an array of size %i"),Index,ArrayNum); // & for one branch
}
参考
👇マルチプレイで困ったら読む。
👇以前も参考にしたが、ときどき読み返すつもり。
👇C++のコリジョンについて
Unreal Interfaceについて
TotalMass by GetOverlappingComponents
Twitterしてます
ブログの更新をお知らせ
コメント