導入:世界を「壊せる」ようにするということ

ゲームの没入感を高める最大の要素の一つは、プレイヤーの行動に対する「世界の反応」です。特にアクションゲームにおいて、置かれている木箱を壊せるか、ただの背景かという違いは、プレイ体験の密度に直結します。 今回は、UE5 の新世代物理エンジン「Chaos」を活用し、単に消えるだけではない、物理的にリアリティのある破壊表現の実装に挑戦しました。

導入:世界を「壊せる」ようにするということ

Chaos 破砕:Geometry Collection の活用

破壊可能なオブジェクト(ADestructibleCrate)の実装では、UE5 の Geometry Collection を使用しました。 ヘルスが 0 になった瞬間に Static Mesh を非表示にし、事前に破砕設定を施した Geometry Collection をシミュレート物理として出現させます。ここで重要なのは、単に物理を ON にするだけでなく、「確実に粉々にする」ための力(Impulse)を加えることです。

Source Code
// ダメージを受けて破壊される際の処理(C++)
void ADestructibleCrate::DestroyCrate() {
  if (bIsDestroyed) return;
  bIsDestroyed = true;

  // 通常のメッシュを隠し、破砕用コンポーネントを表示
  CrateMesh->SetVisibility(false);
  GeometryCollection->SetVisibility(true, true);
  GeometryCollection->SetSimulatePhysics(true);

  // 指定した半径内に巨大なダメージを与え、意図的に粉砕を「誘発」させる
  UGameplayStatics::ApplyRadialDamage(
      GetWorld(), 1000000.0f, GetActorLocation(), 50.0f, nullptr, {}, this
  );
  
  // さらに上方へ突き上げるようなインパルスを加え、飛び散り方を演出
  GeometryCollection->AddImpulse(FVector(0, 0, 500.0f), NAME_None, true);
}

動的なギミック:移動プラットフォームの制御

一方で、ピラミッド探索を奥深くするために「移動リフト」も実装しました。 これは単純な `Tick` による座標更新ですが、工夫したのは「誤差の蓄積への対策」です。単に速度を掛けて進ませるだけでは、浮動小数点の計算誤差で数時間後には位置がズレてしまいます。周期ごとに計算上の「開始地点」をリセットすることで、安定した往復運動を実現しました。

Source Code
// Tick内での移動制御
void AMovingPlatform::Tick(float DeltaTime) {
  FVector CurrentLocation = GetActorLocation();
  CurrentLocation += (MoveVelocity * DeltaTime);
  SetActorLocation(CurrentLocation);

  float DistanceMoved = FVector::Dist(StartLocation, CurrentLocation);
  if (DistanceMoved >= MoveDistance) {
    // 境界を越えたら、基準点を正確に移動させて誤差をリセット
    FVector MoveDirection = MoveVelocity.GetSafeNormal();
    StartLocation = StartLocation + (MoveDirection * MoveDistance);
    SetActorLocation(StartLocation);

    // 進行方向を反転
    MoveVelocity = -MoveVelocity;
  }
}

今後の展望:ゲームプレイと演出の融合

今回の実装により、世界に対する攻撃(破壊)と、世界の中での移動(リフト)という二つの基本要素が揃いました。 今後はこれらを組み合わせ、特定のスイッチを押すと扉が開く、あるいは特定のオブジェクトを破壊すると道が拓けるといった、よりパズル要素の強いゲームプレイへと拡張していく予定です。UE5 の C++ 開発は、エンジンの深層まで手が届くため、独自のロジックを組む楽しさを再認識しています。