プレイヤー、敵、またはオブジェクトに何らかのダメージを与えるゲームの開発を行っている場合、これらのオブジェクトに何かがヒットしたかどうか、ヒットした場合は何がヒットしたのか、インパクト ポイントはどこかなど、検知されたヒットに関する情報を判断しなければならない状況が発生します。 コリジョンの発生時には OnHit イベント によってこういった情報が提供されます。このデータを使用して、ヘルスに影響を及ぼしたり、オブジェクトを破壊したり、その他のゲームプレイ関連のアクションを実行したりなど、ゲーム内のアクションを実行できます。 このチュートリアルでは、OnComponentHit と 関数 イベント を使ってアクタにダメージを与え、アクタのメッシュ マテリアルを変更することでこれを表現します。また、このイベントはヒット位置に衝撃を適用してアクタをプッシュし、発射物によってヒットを受けたエフェクトをシミュレートして、ヒット位置に力を加えます。
実装方法を選んでください。
Blueprints
C++
プロジェクトの設定
まず、[Games (ゲーム)] > [First Person (ファースト パーソン)] > [Blueprint (ブループリント)] から、「OnHit」という名前の新しいプロジェクトを作成します。
メッシュ マテリアルを作成する
コンテンツ ブラウザ に移動して、「LevelPrototyping/Materials」フォルダを見つけます。
MI_SolidBlue を選択して複製 (Ctrl + D) し、複製した新しいアセットの名前を「MI_Solid_Red」に変更します。
このアセットをダブルクリックして開き、[Base Color (ベース カラー)] を選択して赤色に設定します。
アセットを 保存 します。
キューブ アクタを作成する
コンテンツ ブラウザ に移動して、「LevelPrototyping/Meshes」フォルダを見つけます。
SM_ChamferCube スタティックメッシュを 右クリック して、[Asset Actions (アセットアクション)] > [Create Blueprint Using This... (これを使用してブループリントを作成する…)] を選択します。
ブループリントに「BP_Cube」と名前を付けます。
新しいブループリントの保存場所のフォルダを必ず選択してください。
[Details (詳細)] パネルの [Physics (物理)] > [Simulate Physics (物理シミュレーション)] を「True」に設定します。
[Details] パネルで [Events (イベント)] > [On Component Hit] をクリックします。
これによって イベント グラフ が開き、新しいノードが加わります。
Other Actor ピンを 左クリック してドラッグし、Cast To FirstPersonProjectile ノードを検索して追加します。
FirstPersonProjectile
Hit ピンからドラッグして、Break Hit Result ノードを追加します。
Break Hit Result ノードの Hit Actor ピンからドラッグして、Apply Point Damage ノードを追加します。
Apply Point Damage ノードの Base Damage を「100」に設定し、Damage Type Class を「Damage Type」に設定します。
Apply Point Damage ノードの残りのワイヤーを接続します。
As BP First Person Projectile ピンを Damage Causer ピンにつなげます。
Impact point ピンを Hit from Direction ピンにつなげます。
Hit ピンを Hit Info ピンにつなげます。
コンパイル して 保存 します。
ダメージ機能を実装する
発射物がキューブに衝突した際にキューブにダメージを与える機能を作成したので、次は、キューブがダメージを受けた際にキューブのマテリアルを設定し、一定期間後にメッシュを元のマテリアルにリセットする関数を実装する必要があります。
[My Blueprint (マイ ブループリント)] > [Functions (関数)] に移動して、[Add (追加)] (+) をクリックして「OnTakeDamage」という名前の新しい関数を作成します。
[Components (コンポーネント)] タブで、StaticMesh コンポーネント をクリックしてイベント グラフにドラッグします。
Static Mesh ピンをクリックしてドラッグし、アクション メニューから [Set Material] を選択して、その Material パラメータを先ほど作成した MI_Solid_Red マテリアル アセットに変更します。
コンパイル して 保存 します。
[My Blueprint] > [Functions] に移動して、[Add] (+) をクリックして「Damage Reset」という名前の新しい関数を作成します。
[Components (コンポーネント)] タブで、StaticMesh コンポーネント をクリックしてイベント グラフにドラッグします。
Static Mesh ピンをクリックしてドラッグし、アクション メニューから [Set Material] を選択して、その Material パラメータを MI_Solid_Blue マテリアル アセットに変更します。
Apply Point Damage ノードの実行ピンをクリックしてドラッグし、On Take Damage 関数を検索して選択します。
On Take Damage ノードの実行ピンをクリックしてドラッグし、Delay を検索して選択します。
Delay ノードの Completed 実行ピンをクリックしてドラッグし、Damage Reset を検索して選択します。
コンパイル して 保存 します。
完成したブループリント
レベルを設定する
BP_Cube を コンテンツ ブラウザ からレベルにドラッグします。
アウトライナー の [Simulated Cubes (シミュレートされたキューブ)] に移動して、すべての SM_ChauferCubes を選択して右クリックし、[Replace Selected Actors with (選択したアクタを置換)] > [BP_Cube] を選択します。
[Play (プレイ)] をクリックしてエディタで再生し、マウスの左ボタンを使ってキューブに向けて発射物を撃ちます。
エディタでプレイしている場合は、撃った発射物がキューブにヒットした際に、キューブがダメージを受けてそのメッシュ マテリアルが変更され、キューブのヒット位置に衝撃が適用されて、キューブが発射物の反対方向に動くことがわかります。
適用される力の大きさは FirstPersonProjectile ブループリント内で定義されます。このブループリントでは、Event Hit ノードを使って発射物が実際に何かにヒットしたことを判断します。
「Content/FirstPersonBP/Blueprints」フォルダにある FirstPersonProjectile ブループリントを開きます。
このブループリントのスクリプトでは、ヒットしたオブジェクトが物理シミュレーションを実行 (キューブ ブループリントで true に設定) しているかどうかを確認します。物理シミュレーションを実行している場合はヒット位置に衝撃を加えます (衝撃の大きさは緑色のボックスで定義)。この値を調整することで、ヒットしたときに加えられる衝撃の大きさを増減させることができます。
プロジェクトの設定
まず、[Games (ゲーム)] > [First Person (ファースト パーソン)] > [C++] から、「OnHit」という名前の新しいプロジェクトを作成します。
メッシュ マテリアルを作成する
コンテンツ ブラウザ に移動して、「LevelPrototyping/Materials」フォルダを見つけます。
MI_SolidBlue を選択して複製 ( Ctrl + D ) し、複製した新しいアセットの名前を「MI_Solid_Red」に変更します。
このアセットをダブルクリックして開き、[Base Color (ベース カラー)] を選択して赤色に設定します。
アセットを 保存 します。
キューブ アクタを作成する
エディタ で、[+ Add (+ 追加)] > [Open C++Class... (新規 C++ クラス...)] をクリックし、親クラスとして アクタ を選択して、クラスに「Cube」と名前を付けます。
「
cube.h
」ファイルで以下のクラス デフォルトを宣言します。UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) class UStaticMeshComponent* CubeMesh; UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) UMaterialInstance* CubeMaterial; UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) UMaterialInstance* DamagedCubeMaterial; FTimerHandle DamageTimer; UFUNCTION() void OnComponentHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
次に、「
cube.cpp
」ファイルで以下のクラス ライブラリを宣言します。#include "Kismet/GameplayStatics.h" #include "OnHitProjectile.h"
キューブ コンストラクタに移動して、以下の機能を実装します。
ACube::ACube() { CubeMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CubeMesh")); DamagedCubeMaterial = CreateDefaultSubobject<UMaterialInstance>(TEXT("DamageMaterial")); CubeMaterial = CreateDefaultSubobject<UMaterialInstance>(TEXT("CubeMaterial")); CubeMesh->SetSimulatePhysics(true); }
ダメージ機能を実装する
キューブを作成したので、次は、メッシュがダメージを受けた際にメッシュのマテリアルを設定し、一定期間後にメッシュを元のマテリアルにリセットする関数を実装する必要があります。
「
cube.h
」ファイルで以下のコードを宣言します。void OnTakeDamage(); void ResetDamage();
「
cube.cpp
」ファイルに移動して、ACube::BeginPlay
関数のために以下を実装します。void ACube::BeginPlay() { CubeMesh->OnComponentHit.AddDynamic(this, &ACube::OnComponentHit); }
ACube::OnTakeDamage
関数を実装します。void ACube::OnTakeDamage() { CubeMesh->SetMaterial(0, DamagedCubeMaterial); GetWorld()->GetTimerManager().SetTimer(DamageTimer, this, &ACube::ResetDamage, 1.5f, false); }
次に
ACube::ResetDamage
関数を実装します。void ACube::ResetDamage() { CubeMesh->SetMaterial(0,CubeMaterial); }
最後に、
ACube::OnComponentHit
関数に移動して以下のコードを実装します。void ACube::OnComponentHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit) { if (AOnHitProjectile* HitActor = Cast<AOnHitProjectile>(OtherActor)) { UGameplayStatics::ApplyDamage(this, 20.0f, nullptr, OtherActor, UDamageType::StaticClass()); OnTakeDamage(); } }
コードをコンパイルします。
エディタで「C++ Classes」 > 「OnHitCPP」フォルダに移動して、Cube アクタ を右クリックして [Create Blueprint class based on Cube (キューブに基づいてブループリント クラスを作成)] を選択します。
[Components (コンポーネント)] タブで Cube メッシュ を選択し、[Details (詳細)] パネル > [Static Mesh (スタティックメッシュ)] で SM_ChamferCube アセットを選択します。
BP_Cube のクラス デフォルトで、[Cube Material (キューブ マテリアル)] を MI_Solid_Blue アセットに、[Damaged Cube Material (ダメージ キューブ マテリアル)] を MI_Solid_Red アセットに設定します。
コンパイル して 保存 します。
CubeActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Cube.generated.h"
UCLASS()
class ONHIT_API ACube : public AActor
{
GENERATED_BODY()
public:
// アクタのプロパティにデフォルト値を設定します
ACube();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
class UStaticMeshComponent* CubeMesh;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
UMaterialInstance* CubeMaterial;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
UMaterialInstance* DamagedCubeMaterial;
FTimerHandle DamageTimer;
void OnTakeDamage();
void ResetDamage();
UFUNCTION()
void OnComponentHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
public:
// 毎フレーム呼び出されます
virtual void Tick(float DeltaTime) override;
};
CubeActor.cpp
#include "Cube.h"
#include "Kismet/GameplayStatics.h"
#include "OnHitProjectile.h"
// デフォルト値を設定します
ACube::ACube()
{
// 毎フレーム Tick() を呼び出すようにこのアクタを設定します。必要なければ、パフォーマンスを向上させるためにこれをオフにすることができます。
PrimaryActorTick.bCanEverTick = true;
CubeMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CubeMesh"));
DamagedCubeMaterial = CreateDefaultSubobject<UMaterialInstance>(TEXT("DamageMaterial"));
CubeMaterial = CreateDefaultSubobject<UMaterialInstance>(TEXT("CubeMaterial"));
CubeMesh->SetSimulatePhysics(true);
}
// ゲーム開始時またはスポーン時にコールされます
void ACube::BeginPlay()
{
Super::BeginPlay();
CubeMesh->OnComponentHit.AddDynamic(this, &ACube::OnComponentHit);
}
void ACube::OnTakeDamage()
{
CubeMesh->SetMaterial(0, DamagedCubeMaterial);
GetWorld()->GetTimerManager().SetTimer(DamageTimer, this, &ACube::ResetDamage, 1.5f, false);
}
void ACube::ResetDamage()
{
CubeMesh->SetMaterial(0,CubeMaterial);
}
void ACube::OnComponentHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
if (AOnHitProjectile* HitActor = Cast<AOnHitProjectile>(OtherActor))
{
UGameplayStatics::ApplyDamage(this, 20.0f, nullptr, OtherActor, UDamageType::StaticClass());
OnTakeDamage();
}
}
// 毎フレーム呼び出されます
void ACube::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
レベルを設定する
BP_Cube を コンテンツ ブラウザ からレベルにドラッグします。
アウトライナー の [Simulated Cubes (シミュレートされたキューブ)] に移動して、すべての SM_ChauferCubes を選択して右クリックし、[Replace Selected Actors with (選択したアクタを置換)] > [BP_Cube] を選択します。
[Play (プレイ)] をクリックしてエディタで再生し、マウスの左ボタンを使ってキューブに向けて発射物を撃ちます。
エディタでプレイしている場合は、撃った発射物がキューブにヒットした際に、キューブがダメージを受けてそのメッシュ マテリアルが変更され、そのヒット位置に衝撃が適用されて、キューブが発射物の反対方向に動くことがわかります。
適用される力の大きさは「
OnHitProjectile.cpp
」ファイル内で定義されます。このファイルでは、OnHit 関数を使って発射物が実際に何かにヒットしたことを判断します。void AOnHitCPPProjectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit) { // 衝撃を追加し、物理に当たった場合のみ発射物を破壊します。 if ((OtherActor != nullptr) && (OtherActor != this) && (OtherComp != nullptr) && OtherComp->IsSimulatingPhysics()) { OtherComp->AddImpulseAtLocation(GetVelocity() * 100.0f, GetActorLocation()); Destroy(); } }