BLOGブログ

2019.01.23UE4UE/ Blueprint

[UE4]PhysicsCollisionHandlerで衝突を扱う

執筆バージョン: Unreal Engine 4.21

こんにちは。アシスタントエンジニアの小倉です。
今回は、PhysicsCollisionHandlerを用いた物体の衝突の取り扱いについて紹介します。

PhysicsCollisionHandlerとは

PhysicsCollisionHandlerは、UE4における物理を用いた全ての衝突を検出し、独自の衝突後挙動を作成するためのクラスです。

UE4での物理による衝突は、OnActorHitやOnActorを用いる方法がよく使われます。この方法では、衝突後の挙動はActor側に実装することになりますが、PhysicsCollisionHandlerでは衝突時の情報(衝突したAcotrのペアや衝突の強さなど)を用いてPhysicsCollisionHandlerに対して実装を行います。

基本的にはOnAcotrHitなどを使っても同じことが実現できますが、PhysicsCollisionHandlerを使うと、既存のActorに衝突の挙動を追加するなども可能になります。

PhysicsCollisionHandlerを作成する

まず、C++でPhysicsCollisionHandlerを作成します。ここでは、クラス名をMyPhysicsCollisionHandlerとしました。

#include “CoreMinimal.h”
#include “Engine/EngineTypes.h”
#include “PhysicsEngine/PhysicsCollisionHandler.h”
#include “MyPhysicsCollisionHandler.generated.h”

USTRUCT(BlueprintType)
struct FMyRigidBodyContactInfo
{
GENERATED_BODY()

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PhysicsCollisionHandlerTest)
FVector ContactPosition;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PhysicsCollisionHandlerTest)
FVector ContactNormal;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PhysicsCollisionHandlerTest)
float ContactPenetration;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PhysicsCollisionHandlerTest)
class UPhysicalMaterial* PhysMaterial0;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PhysicsCollisionHandlerTest)
class UPhysicalMaterial* PhysMaterial1;
};

USTRUCT(BlueprintType)
struct FMyCollisionImpactData
{
GENERATED_BODY()

/** all the contact points in the collision*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PhysicsCollisionHandlerTest)
TArray ContactInfos;

/** the total impulse applied as the two objects push against each other*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PhysicsCollisionHandlerTest)
FVector TotalNormalImpulse;

/** the total counterimpulse applied of the two objects sliding against each other*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PhysicsCollisionHandlerTest)
FVector TotalFrictionImpulse;
};

UCLASS()
class BLOGTEST421_API UMyPhysicsCollisionHandler : public UPhysicsCollisionHandler
{
GENERATED_BODY()

public:
/** Gives game-specific ability to handle and filter all physics collisions in one place. This is a good place to play sounds and spawn effects, as it does not require special object-specific code. */
virtual void HandlePhysicsCollisions_AssumesLocked(TArray& PendingCollisionNotifies) override;

UFUNCTION(BlueprintImplementableEvent, Category = PhysicsCollisionHandlerTest)
void HandlePhysicsCollisionPair(class AActor* actor0, class AActor* actor1, FMyCollisionImpactData contactInfo);
};

void UMyPhysicsCollisionHandler::HandlePhysicsCollisions_AssumesLocked(TArray& PendingCollisionNotifies)
{
for (const auto& notify : PendingCollisionNotifies)
{
AActor* actor0 = notify.Info0.Actor.Get();
AActor* actor1 = notify.Info1.Actor.Get();
FCollisionImpactData impactData = notify.RigidCollisionData;

TArray myContactInfos;
for (auto info : impactData.ContactInfos)
{
FMyRigidBodyContactInfo myContactInfo;
myContactInfo.ContactNormal = info.ContactNormal;
myContactInfo.ContactPenetration = info.ContactPenetration;
myContactInfo.ContactPosition = info.ContactPosition;
myContactInfo.PhysMaterial0 = info.PhysMaterial[0];
myContactInfo.PhysMaterial1 = info.PhysMaterial[1];
myContactInfos.Add(myContactInfo);
}

FMyCollisionImpactData myImpactData;
myImpactData.ContactInfos = myContactInfos;
myImpactData.TotalFrictionImpulse = impactData.TotalFrictionImpulse;
myImpactData.TotalNormalImpulse = impactData.TotalNormalImpulse;

HandlePhysicsCollisionPair(actor0, actor1, myImpactData);
}
}

 

次に、MyPhysicsCollisionHandlerを継承したBlueprintを作成します。ここでは、クラス名をBP_MyPhysicsCollisionHandlerとしました。

作成したBlueprintにHandlePhysicsCollisionPairを以下のように実装しました。

HandlePhysicsCollisionPairは、衝突したActorのペアに対して呼び出されるイベントです。

上記の実装では、衝撃の強さが指定したしきい値より大きいとき、衝突位置に爆発のパーティクルをスポーンします。

 

最後に、作成したBP_MyPhysicsCollisionHandlerをプロジェクトに設定します。

[Edit][Project Settings…]を順に選択して、[General Settings][Physics Collision Handler Class]に、BP_MyPhysicsCollisionHandlerを設定します。

物体の衝突を行うレベルを作成する

レベル上にSphereを配置し、Simulate PhysicsとSimulation Generates Hit Eventsにチェックを入れます。

Simulateでゲームを開始すると、次のようになります。

PhysicsCollisionHandlerに実装した通りの挙動を行っていることが確認できました。