タイトル: On Component Hit
SEOタイトル: UE5 On Component Hit イベント完全解説|物理衝突・Impact Normal・Hit Result
| この記事の要点 |
|
On Component Hit とは
OnComponentHit は、物理シミュレーション中のコンポーネント (RigidBody) が他のオブジェクトと衝突した瞬間に呼ばれるイベントです。手榴弾の爆発、弾丸の命中、転がる物理オブジェクトの音再生など、衝突を契機としたゲームロジックの起点になります。
イベント発火の前提条件
| 設定 | 必要な値 | 場所 |
|---|---|---|
| Simulate Physics | true | Component → Physics |
| Simulation Generates Hit Events | true | Component → Collision |
| Collision Enabled | Query and Physics | Component → Collision |
| 相手 Collision Response | Block | 相手側のチャンネル応答 |
Blueprint での使い方
- Components → 物理シミュレーションする Mesh を選択
- Details パネル右上の + Add Event → On Component Hit
- Event Graph に新しいイベントノードが追加される
- 必要に応じて Hit Result を分解して使用
イベントの引数
| 引数 | 型 | 意味 |
|---|---|---|
| HitComponent | PrimitiveComponent | 自分のコンポーネント(衝突した側) |
| OtherActor | Actor | 衝突した相手アクター |
| OtherComp | PrimitiveComponent | 相手のコンポーネント |
| NormalImpulse | Vector | 衝突時に加わった力 (運動量変化) |
| Hit | FHitResult | 衝突詳細(位置・法線・物理マテリアル等) |
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(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 の主要フィールド
| フィールド | 型 | 意味 |
|---|---|---|
| ImpactPoint | Vector | 衝突したワールド座標 |
| ImpactNormal | Vector | 衝突面の法線ベクトル |
| Location | Vector | 命中時の自分の位置 |
| Normal | Vector | 進行方向の法線 |
| BoneName | FName | 当たったボーン名 (SkeletalMesh) |
| PhysMaterial | PhysicalMaterial | 当たった面の物理マテリアル |
| bBlockingHit | bool | ブロック衝突か(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 OFF | true に |
| キャラを撃っても無反応 | Capsule の Collision が Overlap | Block に変更 or 弾を Trace 方式に |
| 高速の弾が貫通 | Tunneling | CCD 有効化 or Projectile Component の Sweep 使う |
| 同じ衝突で何度も発火 | 跳ね返り中も呼ばれる | 一度処理したらフラグ立てる / Destroy |
| NormalImpulse が常にゼロ | 物理シミュレーションでない | Simulate Physics 有効に |
FAQ
Q: Projectile Movement Component の場合は?
A: UProjectileMovementComponent の OnProjectileBounce / OnProjectileStop イベントを使うのが一般的。OnComponentHit も併用可能。
Q: ImpactNormal が思った方向と違う
A: ImpactNormal は衝突面の法線(壁の向き)です。弾の進行方向と反対側を向きます。粒子効果の向きには Impact Normal をそのまま使うのが正解。
Q: マルチプレイで二重ダメージになる
A: OnComponentHit はサーバとクライアント両方で発火します。HasAuthority() でサーバ側だけで ApplyDamage を実行してください。