Unity3d。 可視性と可聴性のゾーンの実現

こんにちは、ハブラ。



今日は、プロジェクトに可視性と可聴性のシステムを実装する方法についてお話します。 ゲームCommandosに似たものが判明しました。

いくつかのスクリーンショット。



より多くの可視性。







なるほど



オブジェクトを表示するにはどうすればよいですか? リストするオプションは多数あります。 コライダーによる可視性、レンダーテクスチャによる画像の解析、ポイントごとのレイなどのオプションがあります。 この例では、点ごとの光線のオプションを検討します。

したがって、可視性システムのポイントを持つ基本クラスが必要です。

public abstract class UnitBase : GameObjectBase { public List<Transform> visiblePoints; ... }
      
      





今。 可視性システムの要件を策定する必要があります。

明らかに、ユニット(それが何であるかは関係ありません。友人、敵、またはビデオ監視カメラ)は、視野角と、オブジェクトを区別する距離を持たなければなりません。 近視と遠視も可能です。 また、キャラクターは、例えば、眼鏡をかけてさらに見ることができます。 それは何かを取り、サイドビジョンで少し広く見ることができます。 したがって、ターゲット、目、範囲、視野角のパラメーターが必要です。

  public static bool IsVisibleUnit<T>(T unit, Transform from, float angle, float distance, LayerMask mask) where T : UnitBase { bool result = false; if (unit != null) { foreach (Transform visiblePoint in unit.visiblePoints) { if (IsVisibleObject(from, visiblePoint.position, unit.gameObject, angle, distance, mask)) { result = true; break; } } } return result; }
      
      





LayerMask-光線に必要です。 たとえば、弾丸は窓には反応しますが、視覚には反応しません。 フェンスや他のオブジェクトでも同じことが言えます。 その結果、可視性は不必要または透明なオブジェクトを無視します。 そして、葉の中に隠れて、目に見えるポイントのリストをリセットできます。 ゲーム自体のロジックに応じて、ポイントのリストを拡大または縮小することもできます。 たとえば、カモフラージュなどです。



  public static bool IsVisibleObject(Transform from, Vector3 point, GameObject target, float angle, float distance, LayerMask mask) { bool result = false; if (IsAvailablePoint(from, point, angle, distance)) { Vector3 direction = (point - from.position); Ray ray = new Ray(from.position, direction); RaycastHit hit; if (Physics.Raycast(ray, out hit, distance, mask.value)) { if (hit.collider.gameObject == target) { result = true; } } } return result; }
      
      







ここでは、条件(hit.collider.gameObject == target)に注意する価値があります。 このように見える理由については、使用例で明らかになります。 システムを目的のユニットでポイズニングし、実際に見えるかどうかを確認します。 それが私たちの望みです。



  public static bool IsAvailablePoint(Transform from, Vector3 point, float angle, float distance) { bool result = false; if (from != null && Vector3.Distance(from.position, point) <= distance) { Vector3 direction = (point - from.position); float dot = Vector3.Dot(from.forward, direction.normalized); if (dot < 1) { float angleRadians = Mathf.Acos(dot); float angleDeg = angleRadians * Mathf.Rad2Deg; result = (angleDeg <= angle); } else { result = true; } } return result; }
      
      





大きさや角度の変換にトリックを使用しませんでした。 スカラー積を見つけます。 1の場合、ポイントは一方向に向けられ、角度は省略できます。それ以外の場合は、角度を取得して制限を確認します。



そして今、私もあなたを聞くことができます



可聴システムを使用すると、物事がはるかに簡単になります。 サウンドについては、許容可能なすべてのユニットを反復処理し(実装方法は省略します。主なことは、現在のゾーンまたはロケーション全体に一定数のユニットが存在することです)、ノイズポイント、その半径、この場合はノイズのタイプも転送します。 たとえば、ショットの音と落下する石aiの音の反応は異なります。 実装方法に依存します。

  public virtual void ApplyNoise(Vector3 target, float radius, NoiseType type) { List<AIHearlingBase> aiObjects = AIManager.Instance.GetAIObjects<AIHearlingBase>(); foreach (AIHearlingBase ai in aiObjects) { if(Vector3.Distance(ai.unit.Position, target) <= ai.hearingRadius + radius) { ai.ApplyHearling(target, type); } } }
      
      





可聴システムの準備ができました。 どうぞ



可視性の例



さて、例はありません。

利用可能なユニットを取得する方法が必要になります。 これが本質的にエントリポイントになります。

  public virtual List<T> GetVisibleUnits<T>(Comparer<T> comparer) where T : UnitBase { List<T> result = new List<T>(); foreach (T unit in UnitsManager.Instance.GetUnits<T>()) { if (unit != null && unit != this.unit && unit.enabled && comparer(unit) && IsVisibleUnit(unit)) { result.Add(unit); } } return result; } public virtual bool IsVisibleUnit<T>(T unit) where T : UnitBase { bool result = ViewUtility.IsVisibleUnit(unit, CurrentEyes, visibleAngle, visibleDistance, visibleMask); // SENSORS if (!result) { foreach (AISensorBase sensor in sensors) { if (sensor != null) { if (sensor.DetectTarget<T>(unit)) { result = true; break; } } } } // END SENSORS return result; }
      
      





GetVisibleUnitsは、表示されているすべてのユニットを返すメソッドであり、すぐにロジックがそれを処理します。

注意する価値があります。 この例では、目と考えられるセンサーがあります。 センサーは好きなように機能します。たとえば、匂いなどによって、衛星や監視カメラからデータを受信します。 この例ではそれらを考慮しません。



オプショナル



既存のシステムでは、さらにメッシュを生成します。 そのため、コマンドーのように、敵がどこを見ているのかを判断できます。 以下はメッシュ生成コードです(タスクが現在のものの範囲外である地形のトポロジーを繰り返します)。

 public class FragmentMeshCreator : MeshCreatorBase { public virtual void Create(float angle, float distance, float step = 10f) { List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); List<Vector2> uvs = new List<Vector2>(); Vector3 right = ViewUtility.GetRotation(Vector3.forward, angle) * distance; Vector3 left = ViewUtility.GetRotation(Vector3.forward, angle) * distance; Vector3 from = left; vertices.Add(Vector3.zero); vertices.Add(from); uvs.Add(Vector2.one * 0.5f); uvs.Add(Vector2.one); int triangleIdx = 3; for (float angleStep = -angle; angleStep < angle; angleStep += step) { Vector3 to = ViewUtility.GetRotation(Vector3.forward, angleStep) * distance; //   from = to; vertices.Add(from); uvs.Add(Vector2.one); triangles.Add(triangleIdx - 1); triangles.Add(triangleIdx); triangles.Add(0); triangleIdx++; } vertices.Add(right); uvs.Add(Vector2.one); Mesh mesh = new Mesh(); mesh.name = "FragmentArea"; mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.uv = uvs.ToArray(); mesh.RecalculateNormals(); myMeshFilter.mesh = mesh; } } // public static Vector3 GetRotation(Vector3 forward, float angle) { float rad = angle * Mathf.Deg2Rad; Vector3 result = new Vector3(forward.x * Mathf.Cos(rad) + forward.z * Mathf.Sin(rad), 0, forward.z * Mathf.Cos(rad) - forward.x * Mathf.Sin(rad)); return result; }
      
      





コードの詳細については説明しません。 これは通常のメッシュ生成アルゴリズムです。

終わりに近づいています。

考慮されないものもあります(エディターのセンサーまたはギズモ)が、それらを個別に実装することは難しくありません。 この記事があなたの助けになるか、いくつかのアイデアを投げることを願っています。



参照資料



Unityスクリプト




All Articles