5.

UE5 On Component Hit イベント完全解説|物理衝突・Impact Normal・Hit Result

編集
この記事の要点
  • On Component Hit は物理シミュレーション中のコンポーネントが何かに衝突した瞬間に発火するイベント
  • 前提: Simulate Physics = true かつ Simulation Generates Hit Events = true
  • 受け取れる引数: Hit Component / Other Actor / Other Component / Normal Impulse / Hit Result
  • スイープ移動 (MoveComponent) で当たった場合の OnComponentHit はキャラクター系では Notify Rigid Body Collision の設定で分岐
  • 使い道: 弾の命中、爆発トリガー、衝突時パーティクル、ダメージ計算

On Component Hit とは

OnComponentHit は、物理シミュレーション中のコンポーネント (RigidBody) が他のオブジェクトと衝突した瞬間に呼ばれるイベントです。手榴弾の爆発、弾丸の命中、転がる物理オブジェクトの音再生など、衝突を契機としたゲームロジックの起点になります。

イベント発火の前提条件

設定必要な値場所
Simulate PhysicstrueComponent → Physics
Simulation Generates Hit EventstrueComponent → Collision
Collision EnabledQuery and PhysicsComponent → Collision
相手 Collision ResponseBlock相手側のチャンネル応答

Blueprint での使い方

  1. Components → 物理シミュレーションする Mesh を選択
  2. Details パネル右上の + Add EventOn Component Hit
  3. Event Graph に新しいイベントノードが追加される
  4. 必要に応じて Hit Result を分解して使用

イベントの引数

引数意味
HitComponentPrimitiveComponent自分のコンポーネント(衝突した側)
OtherActorActor衝突した相手アクター
OtherCompPrimitiveComponent相手のコンポーネント
NormalImpulseVector衝突時に加わった力 (運動量変化)
HitFHitResult衝突詳細(位置・法線・物理マテリアル等)

C++ で OnComponentHit にバインド

// Projectile.h
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Projectile.generated.h"

UCLASS()
class MYGAME_API AProjectile : public AActor
{
    GENERATED_BODY()

public:
    AProjectile();

    UPROPERTY(VisibleAnywhere)
    UStaticMeshComponent* Mesh;

    UPROPERTY(EditAnywhere, Category = "Damage")
    float BaseDamage = 50.0f;

protected:
    virtual void BeginPlay() override;

    UFUNCTION()
    void OnHit(UPrimitiveComponent* HitComp,
               AActor* OtherActor,
               UPrimitiveComponent* OtherComp,
               FVector NormalImpulse,
               const FHitResult& Hit);
};
// Projectile.cpp
AProjectile::AProjectile()
{
    Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
    RootComponent = Mesh;

    Mesh->SetSimulatePhysics(true);
    Mesh->SetNotifyRigidBodyCollision(true);  // ★ Hit Events 有効
    Mesh->SetCollisionProfileName(TEXT("PhysicsActor"));
}

void AProjectile::BeginPlay()
{
    Super::BeginPlay();
    Mesh->OnComponentHit.AddDynamic(this, &AProjectile::OnHit);
}

void AProjectile::OnHit(UPrimitiveComponent* HitComp,
                        AActor* OtherActor,
                        UPrimitiveComponent* OtherComp,
                        FVector NormalImpulse,
                        const FHitResult& Hit)
{
    if (!OtherActor || OtherActor == this) return;

    UE_LOG(LogTemp, Log, TEXT("Hit %s at %s, impulse=%s"),
        *OtherActor->GetName(),
        *Hit.ImpactPoint.ToString(),
        *NormalImpulse.ToString());

    // ダメージ計算 (運動量に比例)
    const float Damage = BaseDamage * (NormalImpulse.Size() / 10000.0f);
    UGameplayStatics::ApplyDamage(OtherActor, Damage, nullptr, this, nullptr);

    // 衝突パーティクル
    UGameplayStatics::SpawnEmitterAtLocation(
        GetWorld(), ImpactEffect, Hit.ImpactPoint, Hit.ImpactNormal.Rotation());

    // 自分は消滅
    Destroy();
}

Hit Result の主要フィールド

フィールド意味
ImpactPointVector衝突したワールド座標
ImpactNormalVector衝突面の法線ベクトル
LocationVector命中時の自分の位置
NormalVector進行方向の法線
BoneNameFName当たったボーン名 (SkeletalMesh)
PhysMaterialPhysicalMaterial当たった面の物理マテリアル
bBlockingHitboolブロック衝突か(Overlap でなく)

Notify Rigid Body Collision 設定の重要性

C++ では SetNotifyRigidBodyCollision(true)、Blueprint では Details → Collision → Simulation Generates Hit Events にチェックを入れないと、物理衝突しても OnComponentHit が呼ばれません

// 動的に切替
Mesh->SetNotifyRigidBodyCollision(true);   // = bGenerateHitEvents = true
Mesh->BodyInstance.bNotifyRigidBodyCollision = true;  // 内部フラグ

OnComponentHit vs OnActorHit

イベント対象用途
OnComponentHit個別の Primitive Component細かい制御(特定パーツのみ判定)
OnActorHitアクター全体大まかな衝突検知

Sweep Hit との違い

キャラ移動 (MoveComponent) や AddActorWorldOffset(Sweep=true) で動かしたときの衝突は、物理シミュレーションではなくスイープテストです。これも OnComponentHit を発火させるには NotifyRigidBodyCollision 相当の設定が必要です:

// Sweep 移動でも OnComponentHit を発火させる
FHitResult Hit;
SetActorLocation(NewLocation, /*bSweep=*/true, &Hit);

if (Hit.bBlockingHit)
{
    // 手動で同等の処理
    OnHit(GetRootComponent(),
          Hit.GetActor(),
          Hit.GetComponent(),
          FVector::ZeroVector,
          Hit);
}

サンプル: 弾の命中処理

[Blueprint: BP_Bullet]

Event BeginPlay
  → Set Notify Rigid Body Collision (Mesh, true)

Event On Component Hit (Mesh)
  ├ Other Actor → Cast to BP_Enemy
  │   └ Apply Damage (Damage=20)
  │
  ├ Spawn Emitter At Location
  │   Template:  P_Impact
  │   Location:  Hit Result → Impact Point
  │   Rotation:  Hit Result → Impact Normal → Make Rot From X
  │
  ├ Spawn Sound At Location (Hit Result → Impact Point)
  │
  └ Destroy Actor (Self)

サンプル: 衝突強度でダメージを変える

void AMyVehicle::OnHit(... FVector NormalImpulse, const FHitResult& Hit)
{
    const float Speed = GetVelocity().Size();         // cm/s
    const float ImpactMag = NormalImpulse.Size();

    // 5 m/s 未満は無視
    if (Speed < 500.0f) return;

    // ImpactMag が大きいほどダメージ
    const float Dmg = FMath::Clamp(ImpactMag / 5000.0f, 0.0f, 100.0f);

    if (AActor* Hit_ = Hit.GetActor())
    {
        UGameplayStatics::ApplyDamage(Hit_, Dmg, nullptr, this, nullptr);
    }

    // 物理マテリアルで音を変える
    if (Hit.PhysMaterial.IsValid())
    {
        UPhysicalMaterial* PM = Hit.PhysMaterial.Get();
        if (PM == MetalPhysMat)
            UGameplayStatics::PlaySoundAtLocation(this, MetalHitSound, Hit.ImpactPoint);
        else if (PM == WoodPhysMat)
            UGameplayStatics::PlaySoundAtLocation(this, WoodHitSound, Hit.ImpactPoint);
    }
}

よくあるトラブル

症状原因対処
イベントが発火しないNotify Rigid Body Collision OFFtrue に
キャラを撃っても無反応Capsule の Collision が OverlapBlock に変更 or 弾を Trace 方式に
高速の弾が貫通TunnelingCCD 有効化 or Projectile Component の Sweep 使う
同じ衝突で何度も発火跳ね返り中も呼ばれる一度処理したらフラグ立てる / Destroy
NormalImpulse が常にゼロ物理シミュレーションでないSimulate Physics 有効に

FAQ

Q: Projectile Movement Component の場合は?
A: UProjectileMovementComponentOnProjectileBounce / OnProjectileStop イベントを使うのが一般的。OnComponentHit も併用可能。

Q: ImpactNormal が思った方向と違う
A: ImpactNormal は衝突面の法線(壁の向き)です。弾の進行方向と反対側を向きます。粒子効果の向きには Impact Normal をそのまま使うのが正解。

Q: マルチプレイで二重ダメージになる
A: OnComponentHit はサーバとクライアント両方で発火します。HasAuthority() でサーバ側だけで ApplyDamage を実行してください。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. Event BeginPlay
  2. Event ActorBeginOverlap
  3. Event Tick
  4. on component begin overlap
  5. On Component Hit
  6. CameraBoom(Spring Arm)
  7. Get Player Character
  8. Nav Mesh Bounds Volume
  9. AI MoveTo
  10. Pawn
  11. Create Render Target 2D
  12. Take High Res Screenshotノード
  13. Sphere Reflection Capture
  14. Event Tickノード
  15. ウィジェットのキャンバスパネル
  16. DefaultSceneRoot
  17. FloatingPawnMovement
  18. Set World Rotation
  19. Event Any Damage
  20. Set World Rotation
  21. VInterp To
  22. Get Socket Transform

最近更新/作成されたページ