【UQ1】動く床・スイッチ【BP/C++】

Unreal Quest1の課題をこなしながら学習していく。
今回はUnreal Quest1の1日目初級、動く床を作成する。
スイッチで動くようにした。
C++とMultiplayについても考える。

UQ1Switch

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)を使ってみた。

UQ1SwitchBPOnly

Dynamic Material Instance

アクターごとに、設定したマテリアルのマテリアルインスタンスを作成。

👇DMIを使ったスイッチのBlueprint

BP_Switch posted by anonymous | blueprintUE | PasteBin For Unreal Engine
No description provided

複数のマテリアルを持つ場合は下のように変更できる。
Meshがもとから持つマテリアルをDMIを作る場合は、SourceMaterialの部分にGetMaterialをつなげれば良い。

ThirdPersonCharacterのConstructionScript

今回は、単純なマテリアルを使いたかったので下のマテリアルを設定した。

今回使用したSourceマテリアル

Material Parameter Collection

同じマテリアルは全て色が変わってしまう。
同じマテリアルをもつ複数のスイッチを作成した場合、ひとつのスイッチを押したら他のスイッチの色も同時に変化してしまう。
スイッチのマテリアルとして使うには不向き。

簡単な仕組み
・BPでMPCの数値を変化
・MPCの変化に応じてマテリアルが変化

Material Parameter Collection
MPCのDetailsパネル
マテリアル

👇MPCを使ったスイッチのBlueprint

BP_Switch_2 posted by anonymous | blueprintUE | PasteBin For Unreal Engine
No description provided

BP and C++

👇質量をカウントして、しきい値を超えたときに押し込まれるように設計。

UQ1SwitchMass

(現時点の方針)
・マテリアルに関すること全般は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)を用いて、動的に色を変化させてみた。

Event Graph

スイッチ

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

Construction Script

Event Graph

インターフェース

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方法があるようだ。

アイコンをポチるだけでReplicateできた。

👇BPのFloor。ServerとClientで動きがずれている。(上のアイコンで解消できた。)

UQ1FloorRep

👇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);

メタタグのとこ。

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の説明なのに性癖の話までしている。

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++ でシンプルなマルチプレイヤー ゲームを作成します。

👇以前も参考にしたが、ときどき読み返すつもり。

ネットワーキングの概要
マルチプレイヤーに対応するネットワーク ゲームを設定します。

👇C++のコリジョンについて

Unreal Engine 4 C++ Tutorial – How To Program Collision Triggers For Pickups & Checkpoints

Unreal Interfaceについて

Interface problem in 4.16
Okay, I finally got it to working, thank you so much for your help Iceey! I managed to used the UnrealInterface class, here is what I had to change based on my...

TotalMass by GetOverlappingComponents

Twitterしてます

ブログの更新をお知らせ

コメント

タイトルとURLをコピーしました