YUKA AI Engine
YUKAとは
Yukaは、JavaScriptで書かれたオープンソースのゲームAIライブラリです。Three.jsのようなWebGLフレームワークと組み合わせて使うことで、3Dゲームやシミュレーションに高度なAI処理を簡単に取り入れられます。以下のような機能が特徴です。
-
キャラクターAI(ステアリングビヘイビア)
NPC(Non-Player Character)やエージェントが自然に動き回る「ステアリング行動」を手軽に実装できます。たとえば「敵を追いかける」「障害物を避ける」「グループ行動(群れ、隊列)」などを組み合わせるだけで、リアルな挙動が実現しやすくなります。 -
パスファインディング
A*やDijkstraなどの経路探索アルゴリズムを備え、タイルマップやナビゲーションメッシュなどのデータ構造と連携させることで、キャラクターがステージ内のルートを自動で計算して移動できます。 -
ステートマシン/ゴール思考AI
NPCの「状態管理」や「複数の目標の中から優先度をつけて行動する」ような高度な意思決定AIを構築できます。大規模なゲームAIを整理しやすくなるのが利点です。 -
メッセージング/記憶システム
AI同士でメッセージ(通信)をやりとりしたり、NPCが周囲を“認識・記憶”するロジックを組み込むことも可能です。 -
衝突判定や空間管理
軸揃えバウンディングボックス(AABB)やBoundingSphereなどのクラスを持ち、衝突検知や空間分割に役立つ機能を多数提供します。
なぜThree.jsと組み合わせるのか?
Three.jsは主に「3D表現(レンダリング)」を担うライブラリで、視覚的な描画に優れています。一方、YukaはAIやパスファインディング、衝突判定などのゲームロジック部分に特化しているため、両者を組み合わせると「ビジュアル面はThree.js、AIやゲーム内の振る舞いをYukaが担当」という形で分担でき、開発効率が上がります。
どんなときに役立つか
-
敵や味方が自然に動き回る動作を入れたい
- 例:ターゲットを追跡(Pursuit)しながら、近すぎる仲間とは一定距離を保つ(Separation)
- 例:迷路や入り組んだマップで目的地までの経路をA*アルゴリズムで自動的に導き出す
-
NPCの行動を複雑にしたい
- 状態機械(StateMachine)やゴール思考(Think, Goal)の仕組みを用いて、NPCが状況によって行動を切り替えるシステムを構築
-
大規模シーンでの衝突判定や近接判定を効率化したい
- AABBやBVHといった空間分割・バウンディングボリュームを使い、フレームレートを維持しながら多数のエージェントを扱える
YUKAクラス
幾何/数値系(コリジョン判定や空間構造など)
AABB
- 概要: Axis Aligned Bounding Box(軸方向に揃ったバウンディングボックス)。
- 用途: オブジェクト同士の衝突判定や「点が箱の中にあるか」などのチェック。ワールド空間で箱形の境界を持たせたいときに使う。
BoundingSphere
- 概要: 球形のバウンディングボリューム。
- 用途: 衝突判定や、球体範囲内に他オブジェクトが入ったかどうかを簡易に判定したいとき。
OBB
- 概要: Oriented Bounding Box(任意の向きに対応できるバウンディングボックス)。
- 用途: 物体の回転が多い場合でも、より正確に境界を捉えるための衝突判定など。
BVH / BVHNode
- 概要: Bounding Volume Hierarchy(階層的なバウンディング構造)。
- 用途: シーン内の多数のオブジェクトに対する衝突判定やレイキャストを効率化するための空間分割データ構造。大規模シーン最適化に使える。
Cell / CellSpacePartitioning
- 概要: 空間をセルに区切って管理する仕組み。
- 用途: エージェント同士の近傍検索や衝突判定を最適化したい場合に、マップをセル単位に分割して使う。
Plane
- 概要: 数学的な平面を表すクラス。
- 用途: ライティングや衝突判定などで、点がある平面のどちら側にあるか、平面と交差するか、などを計算するとき。
Ray
- 概要: レイ(始点と方向ベクトルを持つ無限線)。
- 用途: レイキャスト(クリックピッキングや視線判定、壁にぶつかるかなどの判定)に使用。
LineSegment
- 概要: 線分を定義するクラス。
- 用途: 2点間の衝突検知や、視線処理などで「無限線ではなく区間が必要」な場合に使う。
Polygon / Polyhedron
- 概要: ポリゴン面や多面体。NavMeshの構成要素として使われることが多い。
- 用途: 静的なジオメトリの定義、ナビゲーションメッシュや当たり判定の構築など。
MeshGeometry
- 概要: メッシュの頂点やインデックス情報を保持するクラス(Yuka内での簡易ジオメトリ表現)。
- 用途: 3Dモデルやカスタム形状を扱うときに使うことがあるが、Three.jsのジオメトリとは別の用途(AIでの衝突判定など)で用いる。
MathUtils / Matrix3 / Matrix4 / Vector3 / Quaternion
- 概要: 行列やベクトル、クォータニオンなど3D数学に必要な基本クラスや、ユーティリティ関数群。
- 用途: 変換行列(回転、平行移動、スケーリング)や座標変換など、3D計算のあらゆる場面。
SAT
- 概要: Separating Axis Theorem(分離軸定理)を用いた衝突判定のための実装。
- 用途: 凸形状同士の衝突判定を行いたいときなどに内部的に使われる。
ステアリング行動系(移動AI)
Yukaには「SteeringBehavior」を継承したクラスが多数あり、それぞれ“こう動く”というキャラクターAIの振る舞いをまとめています。Vehicle(MovingEntityを継承)にこれらのBehaviorを付与し、SteeringManagerを通じて合成・制御できます。
AlignmentBehavior
- 概要: 群れなどの集団AIにおいて、「周囲の仲間と向きを合わせる」動きを実現。
- 用途: フロック(Boids系)や集団行動シミュレーションで、仲間と同じ方向を向きやすくする。
CohesionBehavior
- 概要: 周囲の仲間の中心へ近づこうとする(まとまり)。
- 用途: フロックで分散しすぎないようにまとまるAIを実装するとき。
SeparationBehavior
- 概要: 仲間や障害物と一定距離を保とうとする(離れようとする)。
- 用途: フロックで密集しすぎないようにする。
SeekBehavior
- 概要: 指定ターゲットに向かって最短で近づくシンプルな移動。
- 用途: 何らかの目標地点へ向かう最も基本的なステアリング。
FleeBehavior
- 概要: 指定ターゲットから逃げるように移動。
- 用途: プレイヤーや敵から離れる動きを実装したい場合など。
ArriveBehavior
- 概要: 目標地点へ近づきつつ減速し、最終的に停止するような動き。
- 用途: 目標地点を通り過ぎずにピタッと止まりたいときに使う(滑らかな到着)。
WanderBehavior
- 概要: ランダムにさまよいながら移動する振る舞い。
- 用途: NPCが自然に歩き回っているような表現。
PursuitBehavior / EvadeBehavior
- 概要: Pursuitは動くターゲットを追跡、Evadeは追跡から逃げる動き。
- 用途: 敵がプレイヤーを追いかける、あるいは逃げ回る、といったAIロジック。
OffsetPursuitBehavior
- 概要: ターゲットから“オフセットされた位置”を保ちながら追跡する。
- 用途: 車列や隊列を組んで移動するときに、先頭を追いかける形で隊列を維持する。
InterposeBehavior
- 概要: 2つの対象の間に割って入り、仲介するような位置取りをする。
- 用途: 2体の間に割り込む防御キャラなどを実装したい場合。
ObstacleAvoidanceBehavior
- 概要: 進行方向にある障害物を自動的に回避する。
- 用途: シンプルな障害物回避が必要な移動AIに。
FollowPathBehavior
- 概要: あらかじめ設定した経路(Path)に沿って移動する。
- 用途: 決まったルート巡回のNPCなど。
SteeringBehavior / SteeringManager
- 概要: すべてのステアリング系クラスの基底/管理クラス。Vehicleに複数のステアリング行動を足し合わせて使う。
- 用途: 複数の行動(例: Seek + ObstacleAvoidanceなど)を合成し、最終的な移動ベクトルを求める。
パスファインディング/経路探索系
AStar
- 概要: A* アルゴリズム(ヒューリスティック付きの最短経路探索)。
- 用途: オープンワールドやタイルマップなどで、最短ルートを効率的に求めたいとき。
BFS
- 概要: 幅優先探索(Breadth-First Search)。
- 用途: 均一コストマップなど、単純な探索で最短経路を見つけたい場合。
DFS
- 概要: 深さ優先探索(Depth-First Search)。
- 用途: 木やグラフの探索時にすべてをなめるなど、汎用的なパス探索。
Dijkstra
- 概要: 辺のコストがさまざまな場合に対応可能な最短経路アルゴリズム(A*のヒューリスティックなし版)。
- 用途: コストがバラついており、確実な最短経路が必要なとき。
Graph / GraphUtils / Edge / Node
- 概要: グラフや頂点、辺を表すクラス。経路探索アルゴリズムが利用する。
- 用途: ノード・エッジベースのパスファインディング、AIネットワークなどを構築するとき。
HeuristicPolicyXXXX
- 概要: A*などで使われるヒューリスティック関数の実装(例: ユークリッド距離、マンハッタン距離など)。
- 用途: 地形に合わせて最適なヒューリスティックを選ぶ。
NavMesh / NavMeshLoader / NavEdge / NavNode
- 概要: ナビゲーションメッシュを扱い、ポリゴン同士の経路探索を行う仕組み。
- 用途: タイルマップよりもポリゴンベースの経路探索が有効な大型3Dステージなどで、キャラクターが走行できる範囲を定義して最短ルートを計算。
ゴール指向AI / ステートマシンなど
State / StateMachine
- 概要: キャラクターの状態管理用クラス(Finite State Machine)。
- 用途: 敵AIが「巡回中→プレイヤー発見→攻撃中→…」のように状態遷移する場合。
Goal / CompositeGoal / GoalEvaluator / Think
- 概要: ゴール指向のAIを作るための仕組み。キャラクターが複数のゴール(目的)を評価し、サブゴールを積み重ねながら行動を決定する。
- 用途: RPGのNPCが「探索する」「攻撃する」「逃げる」などの複数方針を持ち、状況によって優先度を判断して自律的に行動を切り替える。
Task / TaskQueue
- 概要: 非同期タスクやスケジューリングを扱う仕組み。
- 用途: AI行動を複数段階に分けて処理したい、あるいは順番待ちタスクを管理したい場合。
Trigger / TriggerRegion / RectangularTriggerRegion / SphericalTriggerRegion
- 概要: 「ある条件下で起動するトリガー(ギミック)」を表現するクラス群。位置範囲でトリガーを有効にできる。
- 用途: 指定の範囲にプレイヤーが入ったときにイベントを発動する仕組みなど。
メッセージング、エンティティ管理など
EntityManager
- 概要: ゲーム上のエンティティ(GameEntity)全般を管理。ID( UUID )との対応などを行う。
- 用途: 複数NPCやアイテムを一括で管理したり、検索を簡単にするために用いる。
GameEntity
- 概要: Yuka内での最も基本的なエンティティ。位置・回転・スケールなどを持ち、メッセージ処理や更新ロジックを備える。
-
用途: ゲーム内のキャラ・オブジェクトを表現。Three.jsの
Object3D
に相当する概念をAI側で扱うイメージ。
MovingEntity / Vehicle
- 概要: 移動/速度/質量などのパラメータを持つエンティティ。ステアリングを適用できる。
- 用途: プレイヤー、NPCなど“動き”のあるキャラクターを表現する場合に使う。
EventDispatcher
- 概要: イベントを購読/発行する仕組み。
- 用途: エンティティ間のイベント通知(衝突、ステート変化など)を処理する。
MessageDispatcher / Telegram
- 概要: 送受信(ディスパッチ)されるメッセージ(Telegram)と、それを制御するDispatcher。
- 用途: AI間のやり取りなどで「一定時間後に届くメッセージ」や「即時メッセージ」を送信・受信する仕組み。
MemoryRecord / MemorySystem
- 概要: エンティティが他のオブジェクトをいつどのように知覚したかを記録するシステム。
- 用途: NPCの“見失う”時間や、味方を認識しているかなどの知覚と記憶を実装したい場合に使う。
Logger
- 概要: ログ出力を管理するためのユーティリティ。
- 用途: デバッグ時に出力レベルを切り替えたりする。
Regulator
- 概要: 「1秒間に何回まで更新する」など、更新頻度を調整する仕組み。
- 用途: AIの高頻度更新を制御し、パフォーマンスを安定させたい場合。
Time
- 概要: フレームごとのΔt計測などを行う時間管理。
- 用途: 物理やステアリングの「経過時間」による移動量の計算や、固定フレームレートでのAI更新など。
ファジィ論理系
ファジィセットやファジィルールを組み合わせて「曖昧な条件」でAIの意思決定をする仕組みです。
FuzzySet / FuzzyTerm / FuzzyCompositeTerm
-
概要: ファジィ集合やファジィ用語を扱う基底クラス。
FuzzyAND
/FuzzyOR
など複合演算子もある。 - 用途: 「遠い/近い」「そこそこ近い」といった曖昧な評価を定量化し、その上で意思決定したい場合。
FuzzyModule
- 概要: ファジィ変数・ルール・演算をまとめて管理するモジュール。
- 用途: キャラの状態をファジィ論理で評価し、行動を決めたいときにフレームワークとして使う。
FuzzyRule / FuzzyVariable / FuzzySet(各種)
- 概要: ファジィルール(if-then)、ファジィ変数(曖昧な概念の変数化)、三角・台形・ガウスなど様々なファジィ集合。
- 用途: 例:敵との距離が「近い」かつHPが「少ない」→「逃げる」など、曖昧さを含む条件分岐を作りたい場合。
FuzzyAND / FuzzyOR / FuzzyFAIRLY / FuzzyVERY
- 概要: ファジィ論理の「AND/OR」「より控えめに(FAIRLY)」「より強く(VERY)」を表すヘッジ(Hedge)演算子。
- 用途: 複数条件を掛け合わせる際のファジィ演算子として利用。
クラスまとめ
- AABB / BoundingSphere などの境界ボリューム: 衝突や視界判定用
- AlignmentBehavior / ArriveBehavior などのステアリング行動: キャラクターAIの移動ロジック
- AStar / BFS: グラフベースの経路探索
- BVH / BVHNode: 階層的な衝突判定最適化
- Cell / CellSpacePartitioning: 空間を分割して近傍検索を効率化
- CohesionBehavior: 群れ行動での「まとまり」を促すステアリング行動。
- CompositeGoal: サブゴールを階層的に管理する複合ゴール。複雑なAI行動を整理。
- ConvexHull: 点群から凸包を計算するユーティリティ。衝突形状などに役立つ。
- Corridor: NavMeshのポータル列を管理し、Funnelアルゴリズムで最短経路を生成。
-
CostTable: ノード間の移動コスト表。
NavMesh
などで特殊地形コストを扱う際に利用。 - DFS: 深さ優先探索。グラフを深く辿ってゴールを探す。
- Dijkstra: ヒューリスティックを使わない最短経路探索。コスト付きグラフで確実な最短パスを見つける。
- Edge: グラフの辺を表す。コストや接続情報を保持。
- EntityManager: ゲームエンティティを一元管理。追加・削除・更新がまとめてできる。
- EvadeBehavior: ターゲットから逃げるステアリング行動。Pursuitの逆。
- EventDispatcher: イベントを発行/購読する仕組み。AIやゲームロジックの疎結合化に便利。
- FleeBehavior: ターゲット位置から離れるステアリング行動。
- FollowPathBehavior: Pathのウェイポイントをたどり移動するステアリング行動。
- FuzzyAND / FuzzyOR: ファジィ論理で複数条件をAND/ORで合成し、メンバーシップ値の最小/最大を取る。
- FuzzyCompositeTerm: 複数のFuzzyTermをまとめるための基底クラス。
- FuzzyFAIRLY / FuzzyVERY: メンバーシップ値を緩和or強調するヘッジ演算。
- FuzzyModule: ファジィ変数やルールを一括管理し、fuzzify→defuzzifyで推論を行う中心的存在。
- FuzzyRule: 前件(Antecedent)→後件(Consequent)というファジィルールを表す。
- FuzzySet / FuzzyTerm: 各ファジィ用語のメンバーシップ関数を定義し、論理演算に使う。
-
FuzzyVariable: 単一の物理量を複数のファジィセット(用語)で管理する変数。
FuzzyModule
と連携して使う。 - GameEntity: 3D空間でのエンティティを表す基本クラス。カスタマイズしてNPCなどを実装。
- Goal: ゴール指向AIでの単一目標を表し、状態(INACTIVE, ACTIVE, COMPLETED, FAILED)を管理。
- GoalEvaluator: ゴール候補の「望ましさ(desirability)」を計算し、最も適切なゴールを設定する仕組み。
- Graph: ノードとエッジの集合を管理。パスファインディング(A*, Dijkstraなど)に使用。
- GraphUtils: グリッドからグラフを作るなど、グラフ操作のユーティリティ関数を提供。
- HalfEdge: ポリゴンメッシュの半稜線データ構造。PolygonやNavMesh内部で用いられる低レベルクラス。
- HeuristicPolicyDijkstra: A*のヒューリスティックを常に0にし、実質Dijkstraにするポリシー。
- HeuristicPolicyEuclid: A*のヒューリスティックにユークリッド距離を使用。一般的に多用。
- HeuristicPolicyEuclidSquared: A*のヒューリスティックにユークリッド距離の二乗を使用。sqrtを省き高速化狙い。
- HeuristicPolicyManhattan: A*のヒューリスティックにマンハッタン距離を使用。格子移動に適している。
- InterposeBehavior: 2つのエージェントの間に割り込むステアリング行動。
- LeftSCurveFuzzySet / LeftShoulderFuzzySet: ファジィ集合のS字/肩型で、低い値付近を中心にしたメンバーシップを定義。
- LineSegment: 2点を結ぶ有限線分を扱い、点との最接近点などを計算。
- Logger: ログ出力を制御し、デバッグメッセージをレベル別に出力。
- MathUtils: 乱数やクランプ、UUID生成などの便利関数。
- Matrix3 / Matrix4: 3×3, 4×4行列を定義し、ベクトルへの変換等に使用。回転や平行移動を行列演算で扱う。
- MemoryRecord / MemorySystem: NPCが他エンティティを認識・記憶するための仕組み。最後の目撃時間や位置を保持し、時間とともにアップデートする。
- MeshGeometry: Yuka独自のジオメトリクラス。頂点やインデックスを管理。Three.jsで可視化するには変換が必要。
- MessageDispatcher: エンティティ間のメッセージを送受信・遅延配達する仕組み。
- MovingEntity: 速度や質量を持った可動エンティティ。ステアリング行動を適用できる。
- NavEdge: NavMesh内部でポリゴン間のつながりを表すエッジ。
- NavMesh: ポリゴンを組み合わせて通行可能領域を定義し、経路探索を可能にするクラス。
-
NavMeshLoader: glTF(.glb)等でエクスポートされたナビメッシュを読み込み、
NavMesh
を生成。 -
NavNode / Node: ナビゲーションやグラフで使用されるノード。
NavNode
は3D座標を持つ。 - NormalDistFuzzySet: 正規分布形状のファジィ集合。頂点(peak)と標準偏差に基づくガウス型メンバーシップ。
- OBB: 任意の向きに対応したバウンディングボックス。回転に強い衝突判定を可能にする。
- ObstacleAvoidanceBehavior: 進路上の障害物を回避する。
- OffsetPursuitBehavior: リーダーからオフセットした位置を追従(隊列移動)。
- OnPathBehavior: 定義済みパス(線)から外れないようにする。
- Path: ウェイポイントの集合で道筋を表す。
- Plane: 平面の法線と距離を管理し、点との距離計算を行う。
- Polygon: 2D平面上の多角形を定義。NavMeshや衝突で使用。
- Polyhedron: 複数のPolygonで構成される3D多面体。中心や頂点一覧を計算可能。
- PriorityQueue: 優先度付きキュー。A*等のアルゴリズムで使われる。
- PursuitBehavior: 動くターゲットを先読みして追跡するステアリング行動。
- Quaternion: 3D回転を表し、エージェントの向き制御やスムーズな回転合成に使う。
- Ray: 原点と方向ベクトルを持つレイ。衝突判定や視線判定で利用。
- RectangularTriggerRegion: 矩形領域のトリガー範囲。中に入るかどうかの判定に使う。
- Regulator: 一定の更新頻度を維持するための仕組み。AIロジックを間引いて実行する。
- RightSCurveFuzzySet: ファジィ集合のS字型(右寄り)。ある境界までは1、そこからS字で0に近づく。
- RightShoulderFuzzySet: 右肩型ファジィ集合。高い値側がメンバーシップ1で、左に行くほど減る。
- SAT: 分離軸定理を用いた凸形状の衝突判定。OBBや凸メッシュの衝突に利用。
- SeekBehavior: ターゲット地点に向かって全速力で近づくステアリング行動。
- SeparationBehavior: 仲間と一定距離を保つように離れるステアリング行動(群れの分散要素)。
- SingletonFuzzySet: ある一点でのみメンバーシップ1になるファジィ集合。
- Smoother: 過去サンプルの移動平均で値を滑らかにするユーティリティ。
- SphericalTriggerRegion: 球形のトリガー領域。半径で範囲を判定してイベント発火に使う。
-
State, StateMachine: NPCの状態管理(FSM)。
enter/execute/exit
のライフサイクルを実装して制御。 - SteeringBehavior, SteeringManager: ステアリング行動の基底・管理クラス。複数行動を合成しNPCの移動を制御。
- Task, TaskQueue: 重い処理や非同期処理をタスク化し、キューで順次実行。フレームレートへの影響を抑える。
-
Telegram: メッセージ通信のデータ。
MessageDispatcher
から送受信される。 - Think: Goal指向AIのトップレベル。GoalEvaluatorで複数行動を比較し、最適ゴールを選択。
- Time: フレーム間隔などを管理し、固定ステップ更新にも対応できる。
- TriangularFuzzySet: 三角形形状のファジィ集合。手軽に「中程度」の概念などを表す。
- Trigger, TriggerRegion: 領域への進入を検知し、イベントを発火する仕組み。矩形や球など各種リージョンがある。
-
Vector3: 3Dベクトル。加減算・正規化などの演算を提供。Three.jsの
Vector3
と概念的には類似。 -
Vehicle: 移動可能なエンティティ(
MovingEntity
派生)。ステアリング行動を付与して自然な移動を実現。 - Vision: NPCの視野範囲と障害物判定を行うクラス。視野角や最大距離などで「見えるかどうか」をチェック。
- WanderBehavior: ステアリング行動の一種で、ランダムにさまよい移動させる。
まとめ
- Yukaは、3Dゲームやシミュレーションにおける「AI」や「物理的衝突判定・空間管理」を補助する豊富なクラスを提供しています。
- Three.js はレンダリング側を担い、Yuka はエージェントの行動やパスファインディング、衝突判定などの裏方ロジックを担う、という役割分担が典型的です。
- ステアリング系(Seek, Flee, Arrive など)は、MovingEntity/Vehicleに付与して自然な移動や集団行動を実装。
- パスファインディング系(AStar, NavMesh など)は、マップ上の経路計算に利用。
- ゴール/ステートマシン系(Think, Goal, StateMachine など)は、NPCの高度な意思決定に使います。
- それらをまとめるためのEntityManagerやMessageDispatcher、MemorySystemなどもあり、AIロジックを包括的に扱えます。
これらを状況に応じて組み合わせることで、Three.jsによるビジュアル面とYukaのAI/ロジック面を連携させたゲームやシミュレーションを効率的に作成できます。ぜひプロジェクトの要件に合わせ、上記クラスを使い分けてみてください。
使い方
Three.jsとYukaを組み合わせて各クラスをどのように使うのかを示すサンプルコードを交えながら解説します。
AABB
Axis-Aligned Bounding Box(軸揃えバウンディングボックス)
主に衝突判定や視界判定などに利用します。軸に沿っているため計算が単純で高速。
使いどころ
- オブジェクトの境界ボックスを簡単に定義・比較して、衝突判定や包含判定を行う。
- 三次元空間での「点がAABB内にあるか」「AABB同士が重なっているか」を判定。
Three.jsとの連携例
- Three.jsのオブジェクトの
position
やgeometry.boundingBox
などを参照しながら、YukaのAABBを設定。 - 毎フレームでオブジェクトの位置を反映してAABBを更新し、他のAABBと衝突判定を行う。
サンプルコード
import * as THREE from 'three';
import { AABB, Vector3 } from 'yuka';
// Three.jsでBoxGeometryを用意
const boxGeometry = new THREE.BoxGeometry(1, 1, 1);
const boxMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial);
// シーンやカメラ、レンダラーの初期化を省略
// YukaのAABBを作成
const aabb = new AABB(
new Vector3(-0.5, -0.5, -0.5),
new Vector3( 0.5, 0.5, 0.5)
);
// 毎フレーム更新でAABBをオブジェクト位置に合わせる例
function update() {
// Three.js上のオブジェクトワールド行列などを取得
const worldPos = new THREE.Vector3();
boxMesh.getWorldPosition(worldPos);
// AABBの中心を移動させたい場合、
// AABB自体をMatrix4で変換するメソッド: aabb.applyMatrix4(threeMatrix) も利用可
// ここでは簡易的に min/max を移動させる
const offset = worldPos; // Vector3として扱う
aabb.min.set(-0.5 + offset.x, -0.5 + offset.y, -0.5 + offset.z);
aabb.max.set( 0.5 + offset.x, 0.5 + offset.y, 0.5 + offset.z);
// 他のAABBとの衝突判定
// if ( aabb.intersectsAABB(otherAABB) ) { ... }
requestAnimationFrame(update);
renderer.render(scene, camera);
}
update();
AlignmentBehavior
群れや集団行動のステアリング行動の一つ
近隣のエージェント(Vehicle)と同じ向きを取るようにステアリング力を発生させる。
使いどころ
- Boids系のアルゴリズムで「整列(Alignment)」を表現する。
- 鳥や魚の群れ、敵の隊列など、周囲の向きと揃えたい場面。
Three.jsとの連携例
- Three.jsで表示するNPCモデルをYukaの
Vehicle
と対応付け。 - SteeringManagerに
AlignmentBehavior
を追加し、自然な集団行動を表現する。
サンプルコード
import * as THREE from 'three';
import { Vehicle, SteeringManager, AlignmentBehavior } from 'yuka';
const scene = new THREE.Scene();
// YukaのVehicleを定義
const vehicle = new Vehicle();
vehicle.position.set(0, 0, 0);
// 周辺の仲間を想定する配列
const neighbors = [ /* 他のVehicleの配列 */ ];
// ステアリング行動を管理するSteeringManager
const steeringManager = new SteeringManager(vehicle);
// AlignmentBehaviorを追加
const alignment = new AlignmentBehavior();
// このビヘイビアは「仲間のVehicleリスト」を参照して向きを合わせる
// neighborsをVehicle内のneighborsに設定するか、もしくはクラス拡張で参照させるケース多し
steeringManager.add(alignment);
// Three.jsの描画用メッシュ
const geometry = new THREE.SphereGeometry(0.2, 16, 16);
const material = new THREE.MeshNormalMaterial();
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// 毎フレーム更新でYukaのステアリングを計算→Three.jsに反映
function update() {
const delta = 0.016; // 仮に1/60秒
steeringManager.calculate(delta);
// vehicle.update()すると、Vehicleのpositionやvelocityが更新される
vehicle.update(delta);
// Three.jsのメッシュに位置と向きを反映
mesh.position.copy(vehicle.position);
requestAnimationFrame(update);
renderer.render(scene, camera);
}
update();
実際には
neighbors
配列を更新しつつ、AlignmentBehavior
が参照できるようにします。AlignmentBehavior
は近くのVehicleたちの平均的なheading(向き)ベクトルを計算して自分の向きを合わせる力を生み出します。
ArriveBehavior
目標地点に向かい、最終的に減速して止まるステアリング行動
SeekBehavior
との違いは、到着(Arrive)時に速度を落として停止する点。
使いどころ
- NPCが指定地点に向かう際、到着時にスムーズに停止させたい場合。
サンプルコード
import * as THREE from 'three';
import { Vehicle, SteeringManager, ArriveBehavior, Vector3 } from 'yuka';
const scene = new THREE.Scene();
// 目的地
const target = new Vector3(5, 0, 0);
// Vehicle
const vehicle = new Vehicle();
vehicle.position.set(0, 0, 0);
// Arriveビヘイビア
const arrive = new ArriveBehavior(target);
arrive.deceleration = 3; // 減速の度合い(大きいほど早く減速)
// SteeringManagerに追加
const steeringManager = new SteeringManager(vehicle);
steeringManager.add(arrive);
// Three.jsメッシュ作成
const geometry = new THREE.SphereGeometry(0.2, 16, 16);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
function update() {
const delta = 0.016;
steeringManager.calculate(delta);
vehicle.update(delta);
mesh.position.copy(vehicle.position);
requestAnimationFrame(update);
renderer.render(scene, camera);
}
update();
このコードでは、
target
((5,0,0)
の位置)に向かって進み、近づくと減速→最終的に停止します。
AStar
A*アルゴリズム:経路探索の代表例
ヒューリスティックを使い最短ルートを効率的に探索。
使いどころ
- タイルマップやグラフ構造上で最短経路を素早く見つける。
- 大規模マップでもDijkstraより高速に動く場合が多い。
簡単なコード例(タイルグラフ想定)
import { AStar, Graph, HeuristicPolicyEuclid } from 'yuka';
// グラフを用意(ノードとエッジを設定)
const graph = new Graph(/* 無向 or 有向, ノード数など */);
// ...ノード追加、エッジ追加など
const startNodeIndex = 0;
const targetNodeIndex = 5;
// AStarインスタンスを生成
const aStar = new AStar(graph, startNodeIndex, targetNodeIndex);
aStar.heuristic = HeuristicPolicyEuclid; // ユークリッド距離をヒューリスティックに設定
// 検索実行
aStar.search();
// 最短経路を取得(ノードのインデックス列)
if (aStar.found) {
const path = aStar.getPath();
console.log('最短経路:', path);
} else {
console.log('道が見つかりませんでした');
}
Three.js側で、このノード列を順に移動していくようにキャラクターを動かすと、A*ベースのパス移動が実現できます。
BFS
幅優先探索(Breadth-First Search)
コストが均一のグリッドや単純な構造で最短経路を見つける時に便利。
簡単なサンプル
import { BFS, Graph } from 'yuka';
// グラフ構築(例として小規模)
const graph = new Graph();
const nodeA = graph.addNode(/* index=0 */);
const nodeB = graph.addNode(/* index=1 */);
// ... node追加
// エッジを設定 graph.addEdge(0, 1, 1), etc.
const startIndex = 0;
const goalIndex = 3;
// BFSインスタンス
const bfs = new BFS(graph, startIndex, goalIndex);
// 検索
bfs.search();
if (bfs.found) {
const path = bfs.getPath();
console.log('BFSで見つかったパス:', path);
}
複雑なコストがある場合はDijkstraやA*が向いていますが、コスト均一ならBFSもシンプルで高速です。
BoundingSphere
球形の境界ボリューム
衝突判定や距離判定を簡易に行うために使用。
使いどころ
- 中心と半径のみで定義されるため、判定が高速。
- エージェントの「視界」や「当たり判定」を球で単純化することが多い。
サンプルコード
import * as THREE from 'three';
import { BoundingSphere, Vector3 } from 'yuka';
const sphereMesh = new THREE.Mesh(
new THREE.SphereGeometry(1, 16, 16),
new THREE.MeshBasicMaterial({ color: 0x0000ff, wireframe: true })
);
scene.add(sphereMesh);
const boundingSphere = new BoundingSphere(
new Vector3(0, 0, 0), // 中心
1 // 半径
);
// 毎フレームで更新する場合
function update() {
// Meshのワールド座標を取得
const worldPos = new THREE.Vector3();
sphereMesh.getWorldPosition(worldPos);
boundingSphere.center.copy(worldPos);
// 大きさが変わるなら boundingSphere.radius = <someValue>;
// 他のBoundingSphereとの衝突判定
// if ( boundingSphere.intersectsBoundingSphere(otherSphere) ) { ... }
requestAnimationFrame(update);
renderer.render(scene, camera);
}
update();
BVH / BVHNode
Bounding Volume Hierarchy
多数の物体を階層的にまとめ、衝突判定やレイの当たり判定を高速化する仕組み。
使いどころ
- 大量のオブジェクトを扱うシーンで、一つずつ衝突判定すると負荷が大きいので、BVHで空間的に分割し高速化。
サンプルコード(概念的)
import { BVH, BVHNode, AABB, Vector3 } from 'yuka';
// シーン中の複数オブジェクトのAABBをBVHに登録
const bvh = new BVH();
// ダミーでオブジェクト10個分
for (let i = 0; i < 10; i++) {
const min = new Vector3(i, i, i);
const max = new Vector3(i+1, i+1, i+1);
const aabb = new AABB(min, max);
// BVHNodeを作成
const node = new BVHNode();
node.aabb.copy(aabb);
bvh.insert(node);
}
// BVHを構築
bvh.build();
// レイなどによるヒット判定
// bvh.intersectRay(ray, callback) みたいにヒットチェックをする
実際の実装はやや複雑で、個別のオブジェクトをBVHにどう関連づけるかなど工夫が必要です。
Cell
空間を区画するセルを表すクラス
例えばマップをグリッド状に分割して管理するときに用いられます。
使いどころ
- 「どのセルにどのエージェントがいるか」を記録するための単位。
- 衝突判定や近傍探索を高速化する一部として使われる。
サンプルイメージ
import { Cell } from 'yuka';
// 1つのセルを作る(通常はCellSpacePartitioningで自動生成される)
const cell = new Cell();
// セルに含まれるエンティティを追加
cell.addEntity(someEntity);
// セルから削除
cell.removeEntity(someEntity);
// セル内のエンティティ一覧
console.log(cell.entities); // Array of entities
単体で使うより、後述の
CellSpacePartitioning
と組み合わせて使用するのが一般的です。
CellSpacePartitioning
空間を複数のCellに区切って管理し、近傍検索を効率化
大量のエージェントがいる場合、全エージェント同士を比較するのではなく、同じCellや近隣Cellにいるエージェントだけチェックする。
使いどころ
- AI同士の近接チェックや衝突判定を効率化できる。
- 広大なワールドでもパフォーマンスを保てる。
サンプルコード
import { CellSpacePartitioning, Vector3, GameEntity } from 'yuka';
// ワールドを X, Y, Z方向に区切る大きさを決める
const width = 100;
const height = 1; // 2D的に使うなら高さを1にするなど
const depth = 100;
// セルの数(例として10x10)を指定
const cellsX = 10;
const cellsY = 1;
const cellsZ = 10;
// CellSpacePartitioningを初期化
const partition = new CellSpacePartitioning(width, height, depth, cellsX, cellsY, cellsZ);
// エンティティをシーンに追加&パーティションにも登録
for (let i = 0; i < 50; i++) {
const entity = new GameEntity();
entity.position.set(Math.random() * 100, 0, Math.random() * 100);
// パーティションに登録
partition.addEntity(entity);
}
// 毎フレーム、エンティティが動いたら位置に応じてパーティションを更新
function updateEntities() {
for (const entity of partition.entities) {
// 動いたと仮定して適当な移動
entity.position.x += (Math.random() - 0.5) * 0.1;
entity.position.z += (Math.random() - 0.5) * 0.1;
// パーティション上のセル情報を再計算
partition.updateEntity(entity);
}
}
// 近くのエンティティを探したい時
function findNeighbors(entity, queryRadius) {
const neighbors = [];
partition.querySphere(entity.position, queryRadius, neighbors);
return neighbors;
}
querySphere
などで特定範囲内にいるエンティティだけを高速に取得できます。これはステアリングビヘイビアのSeparation
などで近隣エージェントを探すときに役立ちます。
CohesionBehavior
概要
群れ(フロック)や集団行動をするエージェントが、「周囲の仲間と固まろうとする(集団の重心へ近づく)」ためのステアリング行動です。SeparationやAlignmentと組み合わせると「群れっぽい自然な動き」を実装できます。
使いどころ
- Boidsやフロックアルゴリズムで、「ばらばらにならない」動きを出したいとき。
- 複数のNPCが散らばらずに、ある程度まとまった配置で動いて欲しい場合。
サンプルコード
import * as THREE from 'three';
import {
Vehicle, SteeringManager, CohesionBehavior
} from 'yuka';
const scene = new THREE.Scene();
// エージェント(Vehicle)たちを用意
const vehicles = [];
for (let i = 0; i < 5; i++) {
const vehicle = new Vehicle();
vehicle.position.set(Math.random() * 10, 0, Math.random() * 10);
vehicles.push(vehicle);
// Three.js表示用
const mesh = new THREE.Mesh(
new THREE.SphereGeometry(0.2, 16, 16),
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
);
mesh.position.copy(vehicle.position);
scene.add(mesh);
// SteeringManagerを設定
const steeringManager = new SteeringManager(vehicle);
// CohesionBehaviorを追加
// この挙動では周囲の仲間(neighbors)を参照する必要があるため、Vehicleのneighborsが設定されている想定
const cohesion = new CohesionBehavior();
steeringManager.add(cohesion);
// Vehicleにメッシュを紐づけておく (あくまでサンプル的)
vehicle.userData = { mesh, steeringManager };
}
// 毎フレーム更新
function animate() {
requestAnimationFrame(animate);
const delta = 0.016;
// 近隣の仲間リストを更新(簡易版)
for (const v1 of vehicles) {
v1.neighbors.length = 0; // クリア
for (const v2 of vehicles) {
if (v1 !== v2) {
const dist = v1.position.distanceTo(v2.position);
if (dist < 5) {
v1.neighbors.push(v2);
}
}
}
}
// ステアリング計算→Vehicleを更新
for (const v of vehicles) {
const { steeringManager } = v.userData;
steeringManager.calculate(delta);
v.update(delta);
// Three.jsメッシュに反映
v.userData.mesh.position.copy(v.position);
}
renderer.render(scene, camera);
}
animate();
CohesionBehaviorは、仲間との重心を求め、その位置に近づくためのステアリング力を発生させます。実際にはSeparationやAlignmentも同時に使うと自然な群れになります。
CompositeGoal
概要
複数のサブゴール(Goal)を管理する「複合ゴール」の基底クラスです。サブゴールのリストを持ち、順番に実行したり、並列的に状態管理を行います。特にThink
クラスなどで使われています。
使いどころ
- キャラクターAIの行動計画を階層的に構築するとき。
- 例:大目標「拠点を防衛する」の下に「敵を倒す」「パトロールする」などのサブゴールがある…といった複雑なAI行動パターン。
サンプルコード
import {
CompositeGoal, Goal, GoalEvaluator, GameEntity
} from 'yuka';
// 自作のサブゴール例
class MoveToGoal extends Goal {
constructor(owner, target) {
super(owner);
this.target = target;
}
activate() {
this.status = Goal.STATUS.ACTIVE;
// 実際にはArriveBehaviorやSeekBehaviorなどをセットアップ
}
execute() {
// 目標地点に移動しているかどうかを確認
if (this.owner.position.distanceTo(this.target) < 0.1) {
this.status = Goal.STATUS.COMPLETED;
}
}
terminate() {
// 終了処理(例: SteeringManagerからArriveBehaviorを外すなど)
}
}
// 複合ゴール
class MyCompositeGoal extends CompositeGoal {
constructor(owner) {
super(owner);
}
activate() {
this.status = Goal.STATUS.ACTIVE;
// サブゴールを追加
this.addSubgoal(new MoveToGoal(this.owner, { x: 10, y: 0, z: 10 }));
this.addSubgoal(new MoveToGoal(this.owner, { x: 5, y: 0, z: -5 }));
}
execute() {
// subgoalsを順番に実行する
const status = this.executeSubgoals();
this.status = status;
}
}
// 使い方イメージ
const entity = new GameEntity();
const mainGoal = new MyCompositeGoal(entity);
// 毎フレーム
function update() {
// goalがINACTIVEならactivate
mainGoal.activateIfInactive();
// 実行
mainGoal.execute();
// ...Three.js描画など
}
CompositeGoal
はsubgoals
配列を持ち、executeSubgoals()
で現在のサブゴールを順次実行/終了していきます。これによりAI行動を階層化でき、複雑な振る舞いを整理しやすくなります。
ConvexHull
概要
与えられた点集合の凸包(Convex Hull)を計算するクラスです。YukaではConvexHull
が(バウンディングボリュームなどの計算に)利用できます。
使いどころ
- オブジェクトの頂点から凸包を求め、衝突判定などに利用。
- 3Dモデルを単純化してAI用のコリジョン形状にしたりする。
サンプルコード
import { ConvexHull, Vector3 } from 'yuka';
// 点群データを用意
const points = [
new Vector3(0, 0, 0),
new Vector3(1, 0, 0),
new Vector3(1, 1, 0),
new Vector3(0, 1, 1),
new Vector3(0.5, 0.5, 2)
];
// ConvexHullを生成
const hull = new ConvexHull().fromPoints(points);
// hull.facesに凸包を構成する面(Polygon)が登録される
console.log('Convex Hull face count:', hull.faces.length);
// Three.jsで可視化するなら hull.faces から頂点を取り出し、Wireframeなどで表示可能
ConvexHull
は3D空間の点群からポリゴンの集合として最小の凸包を作ります。内部的にはさまざまな演算が行われますが、使い方はfromPoints()
するだけです。
Corridor
概要
ナビゲーションメッシュなどで生成される「回廊(ポータル)の連続」を表し、その中を最短経路として通すための仕組み。特にPathfindingで使われる「Funnelアルゴリズム(ポータルを徐々に絞ってコリドールを通す)」を実装するときに活用されます。
使いどころ
- NavMeshで見つけた領域間の経路を、連続するポリゴン間の“ポータル”として保持し、最適な経路を求める。
- 「狭い通路」を抜ける経路などをスムーズに計算する。
サンプルコード
import { Corridor, Vector3 } from 'yuka';
// Corridorインスタンス
const corridor = new Corridor();
// ポータルの左頂点(left)と右頂点(right)をpushしていく
corridor.push(new Vector3(0,0,0), new Vector3(1,0,0));
corridor.push(new Vector3(1,0,2), new Vector3(2,0,2));
corridor.push(new Vector3(2,0,3), new Vector3(3,0,3));
// corridor内の最適な経路(waypoints)を生成
const waypoints = corridor.generate();
console.log('Waypoints:', waypoints);
Corridor
はNavMesh.findPath()
の結果などをもとに、各ポリゴン間のポータル情報を追加し、generate()
によって最短ルートのウェイポイントを抽出します。Three.js側ではこれらウェイポイントを利用してキャラクターを動かすことができます。
CostTable
概要
ナビゲーションメッシュ(NavMesh
)の各ノード間のコストを管理するテーブル。通常はNavMesh
が内部的に扱い、ポリゴン間の移動コスト(距離や通過難度など)を格納します。
使いどころ
- ノード間の移動に追加のコストがある場合、それを
CostTable
にセットしてA*等で使う。 - 水に入るとコストが高いなど、特殊な地形コストを設定したい場合。
サンプルコード
import { CostTable } from 'yuka';
// CostTableを生成
const costTable = new CostTable();
// navmeshの全ノード数に合わせてinitするなど
// costTable.init(navMesh);
// 個別にコストを設定 (from, to, cost)
costTable.set(0, 1, 1.0); // ノード0->1のコストを1
costTable.set(1, 2, 2.5); // ノード1->2のコストを2.5
// 取得
const cost = costTable.get(1, 2);
console.log('Cost from 1->2:', cost);
// クリア
// costTable.clear();
通常、NavMesh
のcreateCostTable()
などで自動的に作成される場合があります。自前で使うときは「(from, to)の組み合わせでコストを入れる」というシンプルな仕組みです。
DFS
概要
深さ優先探索(Depth-First Search)アルゴリズムのクラスです。ノードを枝分かれしながら深くまで探索してゴールを探す。
使いどころ
- グラフ上で単純にゴールが存在するか、全体を探索したい場合。
- 木構造や迷路探索などにも使えますが、最短経路探索としては必ずしも最適解を見つけられない点に注意。
サンプルコード
import { Graph, DFS } from 'yuka';
// グラフ準備
const graph = new Graph();
// ノード追加
const nodeA = graph.addNode(); // index 0
const nodeB = graph.addNode(); // index 1
const nodeC = graph.addNode(); // index 2
// エッジ追加
graph.addEdge(0, 1, 1);
graph.addEdge(0, 2, 1);
graph.addEdge(1, 2, 1);
const source = 0;
const target = 2;
// DFS
const dfs = new DFS(graph, source, target);
dfs.search();
if (dfs.found) {
const path = dfs.getPath(); // ノードインデックスの配列
console.log('Found path by DFS:', path);
} else {
console.log('No path found');
}
DFSはすべてのノードを深く探索し、ゴール発見時に終了します。最短経路である保証はありませんが、木構造や特定の探索目的にはシンプルに使えます。
Dijkstra
概要
辺にコストがあるグラフで、最短経路を求めるためのアルゴリズム(A*のヒューリスティックなし版)。すべての頂点までの最短距離を求められます。
使いどころ
- コストの異なるグラフ上で確実に最短経路を求めたい場合。
- Aのヒューリスティックを使わないので汎用的。だけどAより遅い場合も。
サンプルコード
import { Graph, Dijkstra } from 'yuka';
// グラフ定義
const graph = new Graph();
for (let i = 0; i < 4; i++) {
graph.addNode();
}
// エッジ (from, to, cost)
graph.addEdge(0, 1, 2);
graph.addEdge(1, 2, 5);
graph.addEdge(2, 3, 1);
graph.addEdge(0, 2, 10);
const source = 0;
const target = 3;
// Dijkstra
const dijkstra = new Dijkstra(graph, source, target);
dijkstra.search();
if (dijkstra.found) {
const path = dijkstra.getPath();
console.log('Dijkstra path:', path);
}
getPath()
でノードインデックス配列が取得できるので、Three.jsのキャラクターをこの順に動かせば最短経路移動ができます。
Edge
概要
グラフ内の「辺」を表すクラス。Graph
にノードとともにEdge
を追加し、重み付けされた経路を構築します。
使いどころ
- A*, Dijkstraなどの探索アルゴリズムで利用するグラフの「接続情報」に該当。
- たとえば
Graph.addEdge(from, to, cost)
で自動生成されるEdgeを内部的に保持しています。
サンプルコード
import { Graph, Edge } from 'yuka';
// Graphを作成
const graph = new Graph();
// ノード追加
graph.addNode(); // index=0
graph.addNode(); // index=1
// Edgeを作成
const edge = new Edge(0, 1, 2); // ノード0->1, コスト=2
// グラフに手動で追加する場合
graph.addEdgeInstance(edge);
// 取得
const edgesFrom0 = graph.getEdgesOfNode(0);
edgesFrom0.forEach(e => {
console.log(`Edge from ${e.from} to ${e.to}, cost=${e.cost}`);
});
基本的にGraph.addEdge()
を呼ぶと内部でEdge
が作られます。必要に応じて直接Edgeを生成することも可能です。
EntityManager
概要
Yukaのゲームエンティティ(GameEntity
)を一括管理するクラス。登録・検索・削除などを行い、メッセージディスパッチや更新ループなどに利用できます。
使いどころ
- 多数のエンティティ(NPC、アイテム、トリガーなど)を一元管理して、
update()
を一括呼び出ししたい時。 - それぞれのエンティティをUUIDで識別し、シリアライズやメッセージングに活用。
サンプルコード
import { EntityManager, GameEntity } from 'yuka';
// EntityManager
const entityManager = new EntityManager();
// Entity作成
class NPC extends GameEntity {
update(delta) {
super.update(delta);
// 自分なりのAIロジック
}
}
const npc1 = new NPC();
const npc2 = new NPC();
entityManager.add(npc1);
entityManager.add(npc2);
// 毎フレーム
function update() {
const delta = 0.016;
// 全エンティティを更新
entityManager.update(delta);
// ...
}
EntityManager
を使うと、新たなエンティティ追加・削除が簡単になります。またnpc1.uuid
などで固有識別IDを持つため、メッセージ送信やシリアライズ/デシリアライズもやりやすくなります。
EvadeBehavior
概要
ターゲット(MovingEntity)から「逃げる」ためのステアリング行動。Pursuitの逆で、ターゲットの将来位置を予測しつつ、そこから離れようとします。
使いどころ
- 敵に追われるNPCが逃げ回るAIを作りたい場合。
- ターゲットが近づくほど加速して離れたい、といった逃走行動のベースに。
サンプルコード
import * as THREE from 'three';
import {
Vehicle, SteeringManager, EvadeBehavior
} from 'yuka';
// 追跡者
const pursuer = new Vehicle();
pursuer.position.set(5, 0, 0);
// 逃げる側
const evader = new Vehicle();
evader.position.set(0, 0, 0);
// EvadeBehavior
const evade = new EvadeBehavior(pursuer);
const steeringManager = new SteeringManager(evader);
steeringManager.add(evade);
// Three.jsメッシュ
const pursuerMesh = new THREE.Mesh(
new THREE.SphereGeometry(0.2, 16, 16),
new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
scene.add(pursuerMesh);
const evaderMesh = new THREE.Mesh(
new THREE.SphereGeometry(0.2, 16, 16),
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
);
scene.add(evaderMesh);
function animate() {
requestAnimationFrame(animate);
const delta = 0.016;
// pursuerに適当な移動
pursuer.position.x += 0.01;
// evaderの逃げステアリング
steeringManager.calculate(delta);
evader.update(delta);
pursuerMesh.position.copy(pursuer.position);
evaderMesh.position.copy(evader.position);
renderer.render(scene, camera);
}
animate();
EvadeBehavior
は近づいてくる相手から将来の位置を予測しつつ離れる動きをします。ObstacleAvoidanceBehaviorなどとの併用も容易です。
EventDispatcher
概要
カスタムイベントを発行・購読する仕組み。ゲーム内で何らかのイベント(衝突した、HPが減った等)をトリガーにハンドラを呼ぶのに利用できます。
使いどころ
- 複数オブジェクト間で疎結合にイベント伝達したいとき。
- YukaのAIロジック上で、状態変化に応じてイベントを発火し、リスナーが反応する形を取りやすい。
サンプルコード
import { EventDispatcher } from 'yuka';
// イベントディスパッチャ
const dispatcher = new EventDispatcher();
// リスナー登録
dispatcher.addEventListener('testEvent', (event) => {
console.log('Received testEvent with data:', event.data);
});
// イベント発行
dispatcher.dispatch({ type: 'testEvent', data: { msg: 'Hello' } });
// リスナー削除
const callback = (event) => {};
dispatcher.addEventListener('something', callback);
dispatcher.removeEventListener('something', callback);
EventDispatcher
は複数のイベント種別に対してリスナーを登録し、dispatch()
で全リスナーに通知します。Three.jsのEventDispatcher
とも似た構造です。
FleeBehavior
概要
ターゲット(他のVehicle
など)からできるだけ離れようとするステアリング行動です。ターゲットが近づいてくると、その方向とは反対にステアリング力を発生させます。
使いどころ
- 敵から逃げ回るNPCを表現。
- プレイヤーが接近したら離れる動作など。
サンプルコード
import * as THREE from 'three';
import {
Vehicle, SteeringManager, FleeBehavior, Vector3
} from 'yuka';
// 逃げる側
const fleeVehicle = new Vehicle();
fleeVehicle.position.set(0, 0, 0);
// 相手(追跡者)
const pursuer = new Vehicle();
pursuer.position.set(5, 0, 0);
// FleeBehaviorを作成(ターゲットをpursuerに設定)
const flee = new FleeBehavior(pursuer.position);
// SteeringManagerに登録
const steeringManager = new SteeringManager(fleeVehicle);
steeringManager.add(flee);
// Three.jsで描画
const fleeMesh = new THREE.Mesh(
new THREE.SphereGeometry(0.2, 16, 16),
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
);
scene.add(fleeMesh);
const pursuerMesh = new THREE.Mesh(
new THREE.SphereGeometry(0.2, 16, 16),
new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
scene.add(pursuerMesh);
function animate() {
requestAnimationFrame(animate);
const delta = 0.016;
// 追跡者が何らかの移動をする想定
pursuer.position.x -= 0.01; // 例: 近づいてくる
// fleeVehicleのステアリング計算
steeringManager.calculate(delta);
fleeVehicle.update(delta);
// Three.jsメッシュ位置更新
fleeMesh.position.copy(fleeVehicle.position);
pursuerMesh.position.copy(pursuer.position);
renderer.render(scene, camera);
}
animate();
FleeBehavior
はターゲットのposition
(Vector3)を参照します。ターゲットが近づくと反対方向に逃げる力が働きます。
FollowPathBehavior
概要
Path
クラスで定義されたウェイポイントに沿って進むステアリング行動です。
ウェイポイントの順番に「Seek」し、必要に応じて自動で次のウェイポイントへ切り替えます。
使いどころ
- NPCが決められた経路をパトロールしたい場合。
- レースゲームで車がコースライン(ウェイポイント)に従うように走る。
サンプルコード
import * as THREE from 'three';
import {
Vehicle, SteeringManager, FollowPathBehavior, Path, Vector3
} from 'yuka';
// Pathの定義
const path = new Path();
path.add(new Vector3(0, 0, 0));
path.add(new Vector3(5, 0, 2));
path.add(new Vector3(10, 0, 0));
path.loop = true; // 最後まで行ったら最初に戻る
// Vehicle
const vehicle = new Vehicle();
vehicle.position.set(0, 0, 0);
// FollowPathBehavior
const followPathBehavior = new FollowPathBehavior(path);
followPathBehavior.nextWaypointDistance = 0.5; // ウェイポイント切り替え距離
// SteeringManager
const steeringManager = new SteeringManager(vehicle);
steeringManager.add(followPathBehavior);
// Three.jsで表示
const vehicleMesh = new THREE.Mesh(
new THREE.SphereGeometry(0.2, 16, 16),
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
);
scene.add(vehicleMesh);
// Path可視化
const pathLineGeom = new THREE.BufferGeometry().setFromPoints(path._waypoints);
const pathLineMat = new THREE.LineBasicMaterial({ color: 0xffff00 });
const pathLine = new THREE.Line(pathLineGeom, pathLineMat);
scene.add(pathLine);
function animate() {
requestAnimationFrame(animate);
const delta = 0.016;
steeringManager.calculate(delta);
vehicle.update(delta);
vehicleMesh.position.copy(vehicle.position);
renderer.render(scene, camera);
}
animate();
FollowPathBehavior
は順次ウェイポイントに向かう動きを自動実行し、目標地点に近づくと次のウェイポイントへ切り替えます。
FuzzyAND
概要
ファジィ論理における「AND」演算子。複数のファジィ集合(またはファジィ用語)のメンバーシップ値を最低値を取る形で合成します(AかつB、という曖昧演算)。
使いどころ
- 例:「敵が近い」と「味方が少ない」をファジィでANDするときなど。
- ファジィモジュール内で複数条件を掛け合わせる際に使用。
サンプルコード(概念的)
import {
FuzzySet, FuzzyTerm, FuzzyAND, FuzzyModule, FuzzyRule
} from 'yuka';
// 適当なFuzzyTerm(例: 左肩セットや三角形セットなど)
const termA = /* FuzzyTerm for "Near" */;
const termB = /* FuzzyTerm for "LowHP" */;
// FuzzyAND(複数Term)
const fuzzyAnd = new FuzzyAND(termA, termB);
// fuzzyAnd.getDegreeOfMembership() で
// termAとtermBのメンバーシップ値の最小値が取得される
console.log('AND membership:', fuzzyAnd.getDegreeOfMembership());
FuzzyAND
はFuzzyCompositeTerm
を継承しており、複数のファジィ用語をまとめて扱います。ルールベースの中で使われることが多いです。
FuzzyCompositeTerm
概要
ファジィ演算(AND, OR, FAIRLY, VERYなど)によって複数のファジィ用語をまとめた「複合的なファジィ用語」を表す基底クラス。FuzzyAND
やFuzzyOR
などはこれを継承しています。
使いどころ
- ファジィルール内で「(Near AND LowHP)」「(Far OR MediumHP)」などの結合を行う。
サンプルコード
import {
FuzzyCompositeTerm, FuzzyTerm
} from 'yuka';
// 自作のFuzzyCompositeTerm例(AND)
class MyFuzzyAND extends FuzzyCompositeTerm {
getDegreeOfMembership() {
let minValue = 1;
for (const term of this.terms) {
const val = term.getDegreeOfMembership();
if (val < minValue) {
minValue = val;
}
}
return minValue;
}
}
// 使い方
const term1 = /* あるFuzzyTerm */;
const term2 = /* あるFuzzyTerm */;
const myAnd = new MyFuzzyAND([term1, term2]);
console.log('result membership:', myAnd.getDegreeOfMembership());
一般的には直接FuzzyCompositeTerm
を継承して自作するより、FuzzyAND
やFuzzyOR
など既存クラスを使います。
FuzzyFAIRLY
概要
ファジィヘッジ(Hedge)の一種で、対象のメンバーシップ値を「控えめ」にする(平方根などによって減少させる)演算子。「ちょっと近い」「やや大きい」などの表現を作るために使われる。
使いどころ
- ファジィルールで「FAIRLY」(控えめに) 条件を強調/緩和したいとき。
- 例:「ほんの少し '近い' と感じる」とか「やや '危険'」など。
サンプルコード
import {
FuzzyTerm, FuzzyFAIRLY, SingletonFuzzySet
} from 'yuka';
// 'Near'セット(メンバーシップ1の範囲が狭い)
const nearSet = new SingletonFuzzySet(50); // 例: 中心50
// そのセットをFAIRLYで包む
const fairlyNear = new FuzzyFAIRLY(nearSet);
// メンバーシップ値を取得
console.log('Fairly near membership:', fairlyNear.getDegreeOfMembership());
FuzzyFAIRLY
は元のメンバーシップ値を0.5乗(√)するなどの実装がデフォルトになっています。(上限は1.0)
FuzzyModule
概要
ファジィ変数やファジィルールを管理するモジュール。メンバーシップ関数を登録し、fuzzify()
やdefuzzify()
を通じて数値の入力→出力を行います。
使いどころ
- 「入力(距離、速度など)」をファジィ変数に変換→「ファジィルール」で意思決定→「デファジィ」して最終的な数値に落とすまでの一連の流れを実装するとき。
- キャラクターAIで複雑な状況判断を行いたい場合に便利。
サンプルコード
import {
FuzzyModule, FuzzyVariable, FuzzySet, LeftShoulderFuzzySet, FuzzyRule,
FuzzyAND, FuzzyOR, FuzzyVERY
} from 'yuka';
const module = new FuzzyModule();
// ファジィ変数を追加
const distanceVar = new FuzzyVariable();
distanceVar.add(new LeftShoulderFuzzySet(0, 0, 50), 'CLOSE');
distanceVar.add(new LeftShoulderFuzzySet(0, 50, 100), 'MEDIUM');
// ...他のセット追加
// モジュールに登録
module.addFLV('Distance', distanceVar);
// ファジィルールを作成 (例: IF Distance is CLOSE THEN "Danger" のような)
const closeTerm = distanceVar.flvs['CLOSE']; // 取得例
// Consequent部のファジィ用語(危険度など)を別のFuzzyVariableに設定したりする
// ルール例 (IF distance=CLOSE THEN ...)
const rule = new FuzzyRule(closeTerm, /* 何らかのConsequentTerm */);
module.addRule(rule);
// fuzzify
module.fuzzify('Distance', 30);
// defuzzify (Centroid法 or MaxAv法など)
const result = module.defuzzify('SomeOutputVar', 'MAXAV');
console.log('Defuzzified result:', result);
FuzzyModule
に複数のFuzzyVariable、FuzzyRuleを登録し、fuzzify()
→defuzzify()
で入力値→出力値まで導けます。
FuzzyOR
概要
ファジィ論理の「OR」演算子。複数ファジィ用語のメンバーシップ値の最大値を取る合成方法です。
使いどころ
- 例:「Close OR Medium」の条件のとき、どちらかが大きいほどメンバーシップを高く評価する。
サンプルコード
import {
FuzzySet, FuzzyOR
} from 'yuka';
// あるFuzzyTerm2つ
const closeTerm = /* FuzzySet for 'Close' */;
const mediumTerm = /* FuzzySet for 'Medium' */;
// FuzzyOR
const closeOrMedium = new FuzzyOR(closeTerm, mediumTerm);
// メンバーシップ = どちらか高い方
console.log('OR membership:', closeOrMedium.getDegreeOfMembership());
FuzzyRule
概要
ファジィルール「IF (前件:Antecedent) THEN (後件:Consequent)」を表すクラス。複数のFuzzyTerm(AND, ORなど)を組み合わせた前件→出力の後件を処理します。
使いどころ
- ファジィ推論で「IF 敵が近い AND HPが少ない THEN 逃げる意欲を高める」などを定義するとき。
サンプルコード
import {
FuzzyModule, FuzzyRule, FuzzyAND, FuzzyTerm
} from 'yuka';
// FuzzyModuleを用意
const fuzzyModule = new FuzzyModule();
// 例: 2つのFuzzyTermをANDしたもの
const nearAndLowHP = new FuzzyAND(/* nearTerm */, /* lowHPTerm */);
// 後件 (例: 'Escape'というFuzzyTerm)
const escapeTerm = /* FuzzyTerm for 'Escape' */;
// ルール
const rule = new FuzzyRule(nearAndLowHP, escapeTerm);
// モジュールに登録
fuzzyModule.addRule(rule);
// 以降、fuzzify/defuzzifyによってルールの結果が反映される
FuzzyRule
は前件(複合Term)のメンバーシップ値を求め、Consequent(後件)にアップデートを行います。最終的にはfuzzyModule.defuzzify()
で数値結果を得ます。
FuzzySet
概要
ファジィ集合の基底クラス。三角形や台形、肩型(Shoulder)など特定のメンバーシップ関数を持つ派生クラスが多数あります。
使いどころ
- 入力値に対しメンバーシップ値(0~1)を計算するための関数を定義。
-
FuzzyVariable.add()
で追加し、名前を紐づけて管理する。
サンプルコード
import {
FuzzySet, TriangularFuzzySet
} from 'yuka';
// TriangularFuzzySet(左端=0, 中央=50, 右端=100)
const nearSet = new TriangularFuzzySet(0, 50, 100);
// 入力値 x=25 についてメンバーシップを計算
const membership = nearSet.computeDegreeOfMembership(25);
console.log('membership:', membership); // 0.5 くらいになる
FuzzySet
自体は抽象ですが、TriangularFuzzySet
やLeftShoulderFuzzySet
など、具体的な形状のメンバーシップ関数を継承クラスが多数用意されています。
FuzzyTerm
概要
ファジィの用語(単語)を抽象化した基底クラス。単独のFuzzySet
や複合演算された用語(FuzzyAND
, FuzzyOR
など)を「Term」として扱います。
使いどころ
- ルールの前件や後件を表す抽象的な「Term」。
- 単一のFuzzySetか、複合運算で生成したFuzzyCompositeTermも「FuzzyTerm」として扱う。
サンプルコード
import {
FuzzyTerm, TriangularFuzzySet
} from 'yuka';
// TriangularFuzzySetをFuzzyTermとして扱う例
const nearTerm = new TriangularFuzzySet(0, 50, 100);
// nearTermはFuzzyTermのインスタンス扱いができる
console.log(nearTerm.getDegreeOfMembership()); // 0(初期)
nearTerm.computeDegreeOfMembership(25);
他にもFuzzyAND
やFuzzyOR
などもFuzzyTerm
の一種として利用されます。
FuzzyVariable
概要
ファジィ変数(FlV: Fuzzy Linguistic Variable)。複数のファジィセット(用語)を「名前付き」で管理し、fuzzify()
/defuzzify()
を行うために必要となる単位。FuzzyModule
に登録して使う。
使いどころ
- 一つの物理量(例: 距離distance, 速度speed など)を複数のファジィセット(Near, Medium, Far...)で表す。
- 入力値をメンバーシップ値に変換する際のキーとなる。
サンプルコード
import {
FuzzyVariable, LeftShoulderFuzzySet, RightShoulderFuzzySet, TriangularFuzzySet
} from 'yuka';
const distanceVar = new FuzzyVariable();
// Near(0~0~20)
distanceVar.add(new LeftShoulderFuzzySet(0, 0, 20), 'Near');
// Medium(0~20~40)
distanceVar.add(new TriangularFuzzySet(0, 20, 40), 'Medium');
// Far(20~100~100)
distanceVar.add(new RightShoulderFuzzySet(20, 100, 100), 'Far');
// fuzzify(入力: 10)
distanceVar.fuzzify(10);
console.log(distanceVar.flvs['Near'].degreeOfMembership); // ~0.5
console.log(distanceVar.flvs['Medium'].degreeOfMembership); // ~0.5
console.log(distanceVar.flvs['Far'].degreeOfMembership); // 0
FuzzyVERY
概要
ファジィヘッジの一種で、「VERY」はメンバーシップ値を二乗するなどで強調する演算。「さらに近い」「すごく大きい」といった意味を持たせたい場合に使う。
使いどころ
- ファジィルールで「VERY Close」などのように、条件を強めたいときに利用。
サンプルコード
import { FuzzyTerm, FuzzyVERY, TriangularFuzzySet } from 'yuka';
// TriangularFuzzySet
const closeSet = new TriangularFuzzySet(0, 25, 50);
// VERYをかける
const veryClose = new FuzzyVERY(closeSet);
// 入力値(10)に対するメンバーシップ計算
closeSet.computeDegreeOfMembership(10); // ~0.4 と仮定
veryClose.getDegreeOfMembership(); // ~0.16 (二乗で小さくなる)
FuzzyVERY
はFuzzyFAIRLY
の逆方向(強調)になります。
GameEntity
概要
Yukaの最も基本的なエンティティクラス。位置、回転、スケールなどの3D情報を持ち、メッセージ受信や更新処理などのメソッドを備えています。キャラクターやオブジェクトを表現するときに、これを継承してカスタムできる。
使いどころ
- 自作AIやNPCのベースクラスとして利用。
- Three.jsの
Object3D
と同様の概念だが、AIロジックに特化している。
サンプルコード
import * as THREE from 'three';
import { GameEntity } from 'yuka';
class MyNPC extends GameEntity {
constructor() {
super();
this.name = 'MyNPC';
}
// 毎フレーム呼ばれる更新処理
update(delta) {
super.update(delta);
// 例: 自己位置を少し動かす
this.position.x += 0.01 * delta;
// AIステートマシンやステアリング行動があればここに処理を追加
}
// メッセージ受信時の処理
handleMessage(telegram) {
console.log('Received message:', telegram.message);
return true; // メッセージを処理した場合
}
}
// Three.jsで表示するメッシュ
const npcMesh = new THREE.Mesh(
new THREE.BoxGeometry(1,1,1),
new THREE.MeshBasicMaterial({color: 0xff0000})
);
scene.add(npcMesh);
// MyNPCインスタンス生成
const myNPC = new MyNPC();
myNPC.position.set(0, 0, 0); // Yukaの座標
function animate() {
requestAnimationFrame(animate);
const delta = 0.016;
myNPC.update(delta);
// Three.jsメッシュにGameEntityのpositionを反映
npcMesh.position.copy(myNPC.position);
renderer.render(scene, camera);
}
animate();
GameEntity
はposition
、rotation
、scale
などを持ち、カスタムのAI挙動を簡単に埋め込めます。update()
やhandleMessage()
を継承先で必要に応じて実装します。
Goal
概要
Yukaのゴール指向AIにおいて、1つの目標や行動単位を表すクラス。status
(INACTIVE, ACTIVE, COMPLETED, FAILED)を持ち、activate()
→execute()
→terminate()
のライフサイクルで処理。
使いどころ
- NPCが「特定の行動をとる」際のステップをまとめる。
- 例:「敵を倒す」「アイテムを拾う」などの単一目標。
サンプルコード
import { Goal, GameEntity } from 'yuka';
class AttackGoal extends Goal {
constructor(owner) {
super(owner);
}
activate() {
this.status = Goal.STATUS.ACTIVE;
console.log('AttackGoal activated');
}
execute() {
// 攻撃動作をする
console.log('Attacking...');
// 攻撃完了とみなしたらCOMPLETEDに
this.status = Goal.STATUS.COMPLETED;
}
terminate() {
console.log('AttackGoal terminated');
}
}
const myEntity = new GameEntity();
const myAttackGoal = new AttackGoal(myEntity);
// 毎フレーム
function update() {
// ゴールがINACTIVEならactivate
myAttackGoal.activateIfInactive();
// 毎フレーム execute
myAttackGoal.execute();
// COMPLETED or FAILED なら terminate が呼ばれる場合あり
}
Goal
は単体でも使えますが、複数のゴールをまとめるCompositeGoal
やThink
クラスと組み合わせることで強力になります。
GoalEvaluator
概要
複数のゴール候補を持つときに、「どのゴールを選ぶか」の**望ましさ(desirability)**を計算するクラス。Think
クラスなどのトップレベルで呼び出し、最もスコアの高いゴールを設定します。
使いどころ
- AIが「攻撃するか、回復するか、探索するか」などの複数候補から最適な行動を選ぶロジックを組む。
-
calculateDesirability()
で「今はどのくらい攻撃したいか」を数値化し、setGoal()
で目標を設定。
サンプルコード
import { GoalEvaluator, Goal } from 'yuka';
class AttackGoalEvaluator extends GoalEvaluator {
constructor(characterBias) {
super(characterBias);
}
calculateDesirability(owner) {
// 例: HPが低いほど攻撃を避ける、敵が近いほど攻撃したいなど
const distanceToEnemy = owner.position.distanceTo(owner.enemy.position);
let desirability = 1 / distanceToEnemy;
desirability *= this.characterBias; // キャラ好み補正
return desirability;
}
setGoal(owner) {
// AttackGoalを設定
owner.brain.addSubgoal(new AttackGoal(owner));
}
}
GoalEvaluatorはThink
やCompositeGoal
の一部として動き、calculateDesirability()
でスコアを出し、setGoal()
で具体的なGoalを作成します。
Graph
概要
ノードとエッジによって構成されるグラフを表すクラス。A*やDijkstraなどの経路探索で使用。各Node
はインデックスを持ち、Edge
はfrom
, to
, cost
などを管理します。
使いどころ
- タイルマップやウェイポイントシステムなどをノード&エッジで表現し、パスファインディングに利用。
-
AStar
,Dijkstra
などを実行する際に必須のデータ構造。
サンプルコード
import { Graph } from 'yuka';
const graph = new Graph();
// ノード追加
for (let i = 0; i < 4; i++) {
graph.addNode();
}
// エッジ追加 (from, to, cost)
graph.addEdge(0, 1, 1);
graph.addEdge(1, 2, 2);
graph.addEdge(2, 3, 1);
graph.addEdge(0, 2, 4);
console.log('Nodes:', graph.nodes.length);
console.log('Edges from node0:', graph.getEdgesOfNode(0));
Graph
は有向/無向を選択可能。探索アルゴリズムにかけるときは、ノード数やエッジ数をしっかり設定しておく必要があります。
GraphUtils
概要
グラフの構築や変換など便利関数を集めたユーティリティ。例えばグリッド(タイルマップ)からGraphを作るなどに役立つ機能を提供します。
使いどころ
- 2D/3Dのグリッドマップを自動的にノード・エッジに変換するような場面。
-
buildGridGraph()
などの便利メソッドを使う場合もある(Yukaのバージョンやリリースによる)。
サンプルコードイメージ
import { Graph, GraphUtils } from 'yuka';
// 例えば2Dグリッド 3x3をGraphに変換するユーティリティ関数があると想定
const width = 3, height = 3;
const graph = new Graph();
GraphUtils.buildGridGraph(graph, width, height);
// これで width*heightのノードが追加され、隣接するマス同士にエッジを張る
console.log('Created grid graph', graph.nodes.length, 'nodes');
(※バージョンによっては GraphUtils
が提供する機能が異なる場合があります)
HalfEdge
概要
ポリゴンメッシュを半稜線構造(Half-Edge)で表す仕組みの一部。各ポリゴン(Polygon
)が持つ半稜線がprev
/next
やtwin
を参照しあい、メッシュを効率的に操作できます。
使いどころ
-
Polygon
内部で頂点間を結ぶエッジを扱うときに利用。ナビメッシュや凸包計算で多用。 - 通常は直接使う機会は少なく、
Polygon
やNavMesh
が内部で処理に使う。
サンプルコード
import { HalfEdge, Vector3 } from 'yuka';
// HalfEdgeを直接生成する例(通常はPolygon.fromContour()で自動生成される)
const he1 = new HalfEdge(new Vector3(0,0,0));
const he2 = new HalfEdge(new Vector3(1,0,0));
const he3 = new HalfEdge(new Vector3(1,1,0));
// next/prevのリンク
he1.next = he2;
he2.next = he3;
he3.next = he1;
he2.prev = he1;
he3.prev = he2;
he1.prev = he3;
HalfEdge
は tail()
, head()
で頂点参照したり、twin
で隣接ポリゴン側の半稜線を繋いだりします。多角形やポリゴンメッシュを操作するための低レベルクラスです。
HeuristicPolicyDijkstra
概要
A探索においてヒューリスティックを常に0とし、結果的にDijkstraと同じ挙動にするためのポリシー。
AStar#heuristic = HeuristicPolicyDijkstra;
と設定すれば、Aはヒューリスティックなしで動作。
使いどころ
- A*をヒューリスティックなしで使い、確実な最短経路を得る(ただし計算は遅め)。
-
Dijkstra
クラスを使わず、AStar
クラスのみで統一したい場合などに便利。
サンプルコード
import { AStar, HeuristicPolicyDijkstra, Graph } from 'yuka';
// Graph定義など省略
const graph = new Graph();
// AStar
const source = 0;
const target = 5;
const aStar = new AStar(graph, source, target);
// ヒューリスティックをDijkstraにする (常に0を返す)
aStar.heuristic = HeuristicPolicyDijkstra;
aStar.search();
if (aStar.found) {
const path = aStar.getPath();
console.log('Dijkstra-like path:', path);
}
HeuristicPolicyEuclid
概要
A*探索のヒューリスティックとしてユークリッド距離(直線距離)を使用するポリシー。
使いどころ
- 3D空間において「直線距離が実際の移動コストに近い」ならこれが有効。
- 一般的な最短路探索では最も多用されるヒューリスティックの一つ。
サンプルコード
import { AStar, HeuristicPolicyEuclid, Graph } from 'yuka';
// Graph定義
const graph = new Graph();
// ...ノード/エッジ追加
const aStar = new AStar(graph, 0, 5);
aStar.heuristic = HeuristicPolicyEuclid; // ユークリッド距離
aStar.search();
if (aStar.found) {
console.log('AStar path with Euclid heuristic:', aStar.getPath());
}
HeuristicPolicyEuclid.calculate(graph, sourceNodeIndex, targetNodeIndex)
が2点の座標を取り、ユークリッド距離を返します。
HeuristicPolicyEuclidSquared
概要
A探索のヒューリスティックとしてユークリッド距離の2乗を使うポリシー。平方根計算を省き高速化できる半面、ヒューリスティックとして適切かどうかを検討する必要がある。
ただし2乗を使うと実コスト以上にならない条件を満たすために注意が必要(Aが最適性を失う可能性あり)。
使いどころ
- スピードが重要で、比較的誤差が問題にならない場合に使うことがある。
- 2乗した分、検索挙動が若干変わる(オーバーなヒューリスティックになる恐れ)。
サンプルコード
import { AStar, HeuristicPolicyEuclidSquared, Graph } from 'yuka';
const aStar = new AStar(graph, 0, 5);
aStar.heuristic = HeuristicPolicyEuclidSquared;
// sqrtを計算しない分高速かもしれない
aStar.search();
if (aStar.found) {
const path = aStar.getPath();
console.log('EuclidSquared path:', path);
}
HeuristicPolicyManhattan
概要
A*探索のヒューリスティックとしてマンハッタン距離(|x1 - x2| + |y1 - y2| + |z1 - z2|)を使うポリシー。タイルベースなど軸整合した移動で効果的。
使いどころ
- 2Dグリッドや軸方向への移動が主な場合に適切。
- 3Dでも軸揃えの移動なら有効だが、対角移動があると誤差が生じる。
サンプルコード
import { AStar, HeuristicPolicyManhattan, Graph } from 'yuka';
const aStar = new AStar(graph, 0, 5);
aStar.heuristic = HeuristicPolicyManhattan;
aStar.search();
if (aStar.found) {
console.log('Manhattan path:', aStar.getPath());
}
マンハッタン距離は格子状マップ(上下左右のみ移動)などで推定が正確になります。
InterposeBehavior
概要
2つのエージェント(MovingEntity)の間に割り込むように移動するステアリング行動です。例えば味方2人が戦っている間に仲裁に入る動きや、サッカーのディフェンダーがボール保持者とパス先の間に立ちふさがる行動を実装できます。
使いどころ
- 2つのターゲットの「中間地点」に向かって移動し、それを制止したり妨害したりしたいとき。
- ターン制ではなくリアルタイムに「常に2人の間を取り持つ」ような動きをステアリングで表現できる。
サンプルコード
import * as THREE from 'three';
import {
Vehicle, SteeringManager, InterposeBehavior
} from 'yuka';
// 2つのターゲット
const agent1 = new Vehicle();
agent1.position.set(5, 0, 0);
const agent2 = new Vehicle();
agent2.position.set(-5, 0, 0);
// 間に入るVehicle
const interposer = new Vehicle();
interposer.position.set(0, 0, 0);
// InterposeBehaviorを設定
// decelerationは減速度合い(ArriveBehaviorと似たパラメータ)
const interposeBehavior = new InterposeBehavior(agent1, agent2, 3);
// SteeringManagerに追加
const steeringManager = new SteeringManager(interposer);
steeringManager.add(interposeBehavior);
// Three.jsメッシュ
const agent1Mesh = new THREE.Mesh(
new THREE.SphereGeometry(0.3),
new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
scene.add(agent1Mesh);
const agent2Mesh = new THREE.Mesh(
new THREE.SphereGeometry(0.3),
new THREE.MeshBasicMaterial({ color: 0x0000ff })
);
scene.add(agent2Mesh);
const interposerMesh = new THREE.Mesh(
new THREE.SphereGeometry(0.3),
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
);
scene.add(interposerMesh);
function animate() {
requestAnimationFrame(animate);
const delta = 0.016;
// agent1/2を適当に動かす例
agent1.position.x += 0.01;
agent2.position.x -= 0.01;
// InterposeBehavior計算
steeringManager.calculate(delta);
interposer.update(delta);
// Three.jsに反映
agent1Mesh.position.copy(agent1.position);
agent2Mesh.position.copy(agent2.position);
interposerMesh.position.copy(interposer.position);
renderer.render(scene, camera);
}
animate();
LeftSCurveFuzzySet
概要
S字型(S-curve)の左寄りファジィ集合。0付近では急激に立ち上がり、右方向に行くにつれてメンバーシップが緩やかに1に近づく形状([0, 1]の間でスムーズに上がるS字カーブ)を想定します。
使いどころ
- 「小さい値がほぼ0で、ある程度進むとすぐに1に近づく」ようなファジィ変数を定義したい場合。
サンプルコード
import {
LeftSCurveFuzzySet
} from 'yuka';
// left=0, midpoint=50, right=100 の例
// 0付近ではメンバーシップ0, だんだんS字で1に近づく
const leftSCurve = new LeftSCurveFuzzySet(0, 50, 100);
// 任意の入力値 x=25 に対するメンバーシップを計算
const membership = leftSCurve.computeDegreeOfMembership(25);
console.log('Membership for x=25:', membership);
LeftShoulderFuzzySet
概要
ファジィ集合のうち「左肩」型。左側でメンバーシップが1を保ち、右に行くにしたがって0になる肩型です。
例:「非常に低い温度」を表すときなど、低い側はしばらく1で、ある値を超えると徐々に0になる。
使いどころ
- 「0~10まではほぼ1、10~20で線形に0へ落ちる」など。
- ファジィ変数で最小側を一気に表現する際に便利。
サンプルコード
import {
LeftShoulderFuzzySet
} from 'yuka';
// LeftShoulderFuzzySet(left, midpoint, right)
const coldSet = new LeftShoulderFuzzySet(0, 10, 20);
// => 0~10はメンバーシップ1, 20以降は0, 10-20で段階的に下がる
console.log(coldSet.computeDegreeOfMembership(5)); // ~1
console.log(coldSet.computeDegreeOfMembership(15)); // ~0.5
console.log(coldSet.computeDegreeOfMembership(25)); // 0
LineSegment
概要
2つの3D座標(Vector3)を結ぶ「線分」を表すクラス。最近接点を求めたり、点との最短距離計算などに利用可能。
使いどころ
- キャラクターが通る直線経路を簡易的に定義したい場合や、レイと似た処理だが「有限区間」で扱いたいとき。
- 交差判定や視線処理などで「無限レイ」ではなく区間限定が欲しい場合に使う。
サンプルコード
import { LineSegment, Vector3 } from 'yuka';
// Start/End
const seg = new LineSegment(
new Vector3(0, 0, 0),
new Vector3(5, 0, 0)
);
// ある点との最近接点
const point = new Vector3(2, 3, 0);
const closest = new Vector3();
seg.closestPointToPoint(point, true, closest);
console.log('Closest point on segment to (2,3,0):', closest);
このようにclampToLine
フラグをtrueにすると、「線分外に出る場合」も端点にクランプされます。
Logger
概要
ログ出力を管理するシンプルなクラス。ログレベルをLOG
, WARN
, ERROR
, SILENT
に設定して出力を制御できます。
使いどころ
- ライブラリ内部やAIロジックのデバッグメッセージの出力制御。
- Consoleへの出力を抑制したい場合など。
サンプルコード
import { Logger } from 'yuka';
Logger.setLevel(Logger.LEVEL.LOG); // LOG, WARN, ERROR, SILENT
Logger.log('This is a log message'); // 通常ログ
Logger.warn('This is a warning'); // 警告
Logger.error('This is an error'); // エラー
setLevel(Logger.LEVEL.SILENT)
とすればすべてのログが出力されなくなります。
MathUtils
概要
Yukaの数学ユーティリティ関数集。ランダム生成やクランプ、面積計算など多彩な機能を含みます。
使いどころ
-
randFloat()
,randInt()
,clamp()
,generateUUID()
など補助関数が必要なとき。 - 例: AIでランダムに行動を選択したい場合や数値制限をかける場合に使用。
サンプルコード
import { MathUtils } from 'yuka';
// 乱数
const randomFloat = MathUtils.randFloat(-1, 1);
const randomInt = MathUtils.randInt(0, 10);
// クランプ
let value = 15;
value = MathUtils.clamp(value, 0, 10); // => 10
// UUID
const uuid = MathUtils.generateUUID();
console.log('UUID:', uuid);
Matrix3
概要
3×3行列を表すクラス。2D変換や3Dの回転行列などで用いられます。OBB
(Oriented Bounding Box)の回転行列として使われることが多い。
使いどころ
- 3D中の回転だけを扱う場合(拡縮含まない)や、2D変換用に使う場合。
- OBBの向き行列を定義して衝突判定に利用するなど。
サンプルコード
import { Matrix3, Vector3 } from 'yuka';
// 単位行列
const m = new Matrix3();
m.identity();
// 90度回転(Z軸基準)を手動で設定する例
m.set(
0, -1, 0,
1, 0, 0,
0, 0, 1
);
// ベクトルを回転させる
const v = new Vector3(1, 0, 0);
v.applyMatrix3(m); // => (0,1,0)
console.log(v);
YukaにはMatrix3#fromEuler()
などのヘルパーはありませんが、直接 set()
などで要素を操作可能です。
Matrix4
概要
4×4行列を表すクラス。3D空間での「平行移動+回転+拡縮」をまとめて扱う。Three.jsのMatrix4
と似た概念。
使いどころ
-
GameEntity
のworldMatrixを適用するなど、オブジェクトをワールド空間に変換する際に使う。 - レイやAABBをこの行列で変換するメソッド (
applyMatrix4()
) がある。
サンプルコード
import { Matrix4, Vector3 } from 'yuka';
// 単位行列
const m = new Matrix4();
m.identity();
// 例: 平行移動を設定
m.elements[12] = 5; // x方向に5
m.elements[13] = 2; // y方向に2
m.elements[14] = -3; // z方向に-3
// ベクトルに適用
const v = new Vector3(1, 1, 1);
v.applyMatrix4(m); // => (6, 3, -2)
console.log(v);
Three.jsと併用する場合は、Three.jsのMatrix4と同様の並びだと考えていいですが、混在して使うときは注意が必要です(互換性はおおむねあります)。
MemoryRecord
概要
MemorySystem
と連動し、「ある対象をいつ見たか」「どの位置で見たか」「どのくらい危険か」といった記録を一件ごとに保持するデータクラス。NPCの知覚・記憶の一部。
使いどころ
- NPCが他のエージェントやアイテムを「最近いつ発見したか」「位置はどこだったか」を覚えるのに利用。
- ステルスゲームなどで、敵NPCが「数秒前に見たプレイヤー位置」を追跡するときに活用。
サンプルコード
import { MemoryRecord, Vector3 } from 'yuka';
const record = new MemoryRecord();
// 例: 対象となるGameEntityの参照を保存
record.entity = /* someEntity */;
// 最終目撃場所
record.lastSensedPosition = new Vector3(10, 0, 5);
// いつ最後に見たか(秒単位)
record.timeLastSensed = performance.now() / 1000; // 例
console.log('MemoryRecord:', record);
MemorySystem
概要
NPCが周囲のエンティティを認識・記憶する仕組みを管理するクラス。内部でMemoryRecord
をIDに紐付けて保持します。NPCの視野検知や聴覚検知を行って、その情報を時間とともに更新・忘却できるようにすることが目的。
使いどころ
- 「NPCが一定時間見失うとプレイヤー位置を忘れる」などのAI実装。
-
Vision
クラスと組み合わせて、見える範囲内のエンティティをMemorySystem
に登録し、しばらく経つと忘却。
サンプルコード
import { MemorySystem, GameEntity, Vector3 } from 'yuka';
class MyNPC extends GameEntity {
constructor() {
super();
this.memorySystem = new MemorySystem(this);
}
update(delta) {
super.update(delta);
// MemorySystemの更新
this.memorySystem.update(delta);
}
}
// NPC生成
const npc = new MyNPC();
// 何かのきっかけ(視線判定)で別のエンティティを「目撃」した場合
function onEntitySpotted(spottedEntity) {
// MemorySystemに記録
npc.memorySystem.updateRecord(spottedEntity, /* some position */, /* time */);
}
// MemorySystemは内部にMemoryRecordを作成し、最後に見た時間や位置を保存
MemorySystem
はupdate()
で「記憶の鮮度」を管理し、一定時間見えないと「認識できなくなる」処理を実装したりできます。NPCの高度な知覚/意思決定に生かせる仕組みです。
MeshGeometry
概要
YukaのMeshGeometry
は、頂点やインデックス、法線などのジオメトリ情報を保持するクラスです。Three.jsのBufferGeometry
に近い概念ですが、主にYuka内でAIや物理的な計算に使われることを想定しています。
使いどころ
- 衝突判定やパスファインディングなどで独自のメッシュデータを扱う場合に役立つ。
- Three.jsのジオメトリとは直接互換性がないが、頂点情報の受け渡しを行うことで連携可能。
サンプルコード
import * as THREE from 'three';
import { MeshGeometry, Vector3 } from 'yuka';
// YukaのMeshGeometryを作成
const meshGeo = new MeshGeometry();
// 頂点を追加(例:三角形)
meshGeo.vertices.push(
new Vector3(0, 0, 0),
new Vector3(1, 0, 0),
new Vector3(0, 1, 0)
);
// インデックス(三角形の場合 [0,1,2] )
meshGeo.indices.push(0, 1, 2);
// Three.jsで同じ頂点を使ってみる
const threeGeometry = new THREE.BufferGeometry();
const positions = new Float32Array([
0, 0, 0,
1, 0, 0,
0, 1, 0
]);
threeGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
threeGeometry.setIndex([0, 1, 2]);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const mesh = new THREE.Mesh(threeGeometry, material);
scene.add(mesh);
MeshGeometry
はYukaの内部AIや衝突計算等に使用できます。Three.jsへ可視化する場合は別途BufferGeometry
等に変換が必要です。
MessageDispatcher
概要
エンティティ間のメッセージ(通信)をディスパッチ(送信・配信)する仕組みを提供します。Telegram
とセットで、NPC同士のやり取りや遅延付きメッセージを実現できます。
使いどころ
- AI同士が「一定時間後にメッセージを送る」などのイベント駆動のやり取りを実装したい時。
- 独自のイベントシステムとして使う。
サンプルコード
import { MessageDispatcher, Telegram, GameEntity } from 'yuka';
const dispatcher = new MessageDispatcher();
class MyEntity extends GameEntity {
handleMessage(telegram) {
console.log(`Entity ${this.name} received message:`, telegram.message);
return true;
}
}
const sender = new MyEntity();
sender.name = 'Sender';
const receiver = new MyEntity();
receiver.name = 'Receiver';
// 送信
dispatcher.dispatch(new Telegram(
sender, // 送信元
receiver, // 送信先
'HelloWorld', // メッセージ本文
0, // 遅延(ミリ秒)
{ someData: 123 } // 任意のカスタムデータ
));
上記の例では、dispatch()
直後にreceiver.handleMessage()
が呼ばれ、メッセージが受理されます。遅延を設定すれば一定時間後に配達される仕組みもサポートしています。
MovingEntity
概要
GameEntity
を継承したクラスで、速度(velocity)や質量(mass)など物理的パラメータを持つ「動くエージェント」。ステアリング行動を付与できるようになります。
使いどころ
- NPCやプレイヤーキャラなど、動きを伴うオブジェクトを表現したい場合。
-
SteeringBehavior
を適用して自然な移動や回避・追跡を実装。
サンプルコード
import * as THREE from 'three';
import { MovingEntity, SteeringManager, SeekBehavior, Vector3 } from 'yuka';
class MyMovingEntity extends MovingEntity {
update(delta) {
super.update(delta);
// 追加ロジックがあればここに
}
}
// 移動するターゲット位置
const targetPosition = new Vector3(5, 0, 0);
// MovingEntityインスタンス
const entity = new MyMovingEntity();
entity.position.set(0, 0, 0);
entity.maxSpeed = 2; // 最高速度
// ステアリング行動
const seek = new SeekBehavior(targetPosition);
const steeringManager = new SteeringManager(entity);
steeringManager.add(seek);
// Three.jsメッシュ
const mesh = new THREE.Mesh(
new THREE.SphereGeometry(0.3, 16, 16),
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
);
scene.add(mesh);
function animate() {
requestAnimationFrame(animate);
const delta = 0.016;
steeringManager.calculate(delta);
entity.update(delta);
mesh.position.copy(entity.position);
renderer.render(scene, camera);
}
animate();
MovingEntity
はVehicle
の親クラスとしても使われる。ステアリング行動を付与することで自然な移動を実装できます。
NavEdge
概要
ナビゲーションメッシュ(NavMesh)内の「辺(ポリゴン同士のつながり)」を表すクラスです。各NavEdge
には辺の両端、対応するポリゴン情報などが格納されます。
使いどころ
- ナビゲーションメッシュのポリゴン間を繋ぐ境界を定義し、経路探索に利用。
- 通常は
NavMesh
内で自動的に作られるので、手動で使うケースは少ない。
サンプルコード
import { NavEdge } from 'yuka';
// NavMesh生成時にポリゴン間の接続としてNavEdgeが使用される
const edge = new NavEdge();
edge.from = 0; // ナビゲーションノード(ポリゴン)ID
edge.to = 1; // 接続先ノードID
edge.cost = 1.5; // 移動コストなど
console.log(edge);
NavMesh
概要
ポリゴン(Polygon
)からなるナビゲーションメッシュを構築し、キャラクターの経路探索や移動制限を行うためのクラス。内部でNavEdge
やNavNode
を扱い、A*などで経路を探せます。
使いどころ
- 広大な3Dステージで「歩行可能な床面」や「壁」をポリゴンとして定義し、キャラクターが通れる道筋を計算。
-
findPath()
メソッドでA*を実行し、ポータル縮小(Funnel)でスムーズな経路を得られる。
サンプルコード
import { NavMesh, Polygon, Vector3 } from 'yuka';
// ナビメッシュインスタンス
const navMesh = new NavMesh();
// ポリゴン(凸形状)を追加
const poly1 = new Polygon().fromContour([
new Vector3(0,0,0), new Vector3(5,0,0), new Vector3(5,0,5), new Vector3(0,0,5)
]);
const poly2 = new Polygon().fromContour([
new Vector3(5,0,0), new Vector3(10,0,0), new Vector3(10,0,5), new Vector3(5,0,5)
]);
// ...
// NavMeshに設定
navMesh.fromPolygons([poly1, poly2 /* ... */]);
// 経路探索
const from = new Vector3(1, 0, 1);
const to = new Vector3(9, 0, 4);
const path = navMesh.findPath(from, to);
console.log('Found path:', path); // Vector3[] の配列
NavMesh
ではmergeConvexRegions
などのオプションもあり、複数ポリゴンを自動結合して扱いやすくしたりできます。結果はCorridor
を経て最終的にウェイポイントとして抽出されます。
NavMeshLoader
概要
gltf形式(.glbなど)でエクスポートされたナビゲーションメッシュを読み込むクラス。parse()
またはload()
メソッドを用い、NavMesh
インスタンスを生成します。
使いどころ
- 外部ツール(Blenderなど)で作成・エクスポートしたナビメッシュをYukaで使用したい場合。
- glbファイルをダウンロードして経路探索に組み込む。
サンプルコード
import { NavMeshLoader } from 'yuka';
const loader = new NavMeshLoader();
loader.load('path/to/navmesh.glb').then((navMesh) => {
console.log('NavMesh loaded:', navMesh);
// 例:経路探索
const from = new Vector3(1,0,1);
const to = new Vector3(9,0,4);
const path = navMesh.findPath(from, to);
console.log('Path:', path);
}).catch((err) => {
console.error('Failed to load navmesh:', err);
});
.glb
内にナビメッシュ用のデータ(ポリゴン座標)が埋め込まれている前提で、NavMeshLoader
がパースを行いNavMesh
オブジェクトとして返します。
NavNode
概要
ナビゲーショングラフ(NavMeshなど)で使用されるノード。Node
を継承し、3D座標(position
)とユーザーデータを保持します。
使いどころ
- NavMeshの各ポリゴンがノードとして表現されるとき、またはウェイポイントとしてノード管理する場合。
- 経路探索や隣接関係を管理するための要素。
サンプルコード
import { NavNode, Vector3 } from 'yuka';
// NavNode(index, position, userData)
const node = new NavNode(0, new Vector3(5,0,5), { regionID: 2 });
console.log('Node index:', node.index);
console.log('Position:', node.position);
console.log('UserData:', node.userData);
Node
概要
グラフにおけるノード(頂点)を表す基本クラス。index
を持ち、NavNode
など派生クラスで使われる。
使いどころ
- A*やDijkstraなどのグラフ探索で一般的に使用。
- 単独で使うより
Graph.addNode()
やNavNode
などの形で使われる事が多い。
サンプルコード
import { Node } from 'yuka';
const nodeA = new Node(0);
const nodeB = new Node(1);
console.log('nodeA index:', nodeA.index);
NormalDistFuzzySet
概要
正規分布(ガウス分布)形状のファジィ集合を表すクラス。left
, midpoint
, right
に加え、standardDeviation
を指定して山型のメンバーシップを定義します。
使いどころ
- 「中間がもっとも高い確度で、左右に行くほど緩やかに0に近づく」ようなファジィ評価をしたい場合。
- 例:プレイヤーがちょうど中間距離にいる時を最大にし、近すぎる/遠すぎる時はメンバーシップを低くする表現など。
サンプルコード
import { NormalDistFuzzySet } from 'yuka';
// NormalDistFuzzySet(left, midpoint, right, standardDeviation)
const normalSet = new NormalDistFuzzySet(0, 50, 100, 15);
// 入力値 x=50付近で最大メンバーシップ
console.log('membership at x=50:', normalSet.computeDegreeOfMembership(50));
// x=20やx=80付近ではやや低い値になる
OBB
概要
Oriented Bounding Box(任意の向きに対応するバウンディングボックス)を表すクラス。中心座標(center)、ハーフサイズ(halfSizes)、回転行列(rotation: Matrix3
)を保持し、衝突判定や囲み判定で用いられます。
使いどころ
- 物体が回転している場合でも、より正確なバウンディングボックス判定を行いたいとき(AABBだと回転に対応しづらい)。
-
intersectsOBB()
,intersectsAABB()
などの判定メソッドを使い、衝突や視界遮蔽などを計算。
サンプルコード
import { OBB, Vector3, Matrix3 } from 'yuka';
// OBB(center, halfSizes, rotation)
const obb = new OBB(
new Vector3(0, 0, 0), // 中心
new Vector3(1, 2, 1), // ハーフサイズ (幅2, 高さ4, 奥行2)
new Matrix3().identity() // 回転行列 (ここでは回転なし)
);
// 何らかの回転を設定(例:Z軸90度)
obb.rotation.set(
0, -1, 0,
1, 0, 0,
0, 0, 1
);
// 他のOBBやAABBと衝突するか確認
// if (obb.intersectsOBB(otherOBB)) { ... }
これにより、回転しているボックス形状の衝突判定が簡単に行えます。rotation
はMatrix3
で行列を設定する必要があります。
ObstacleAvoidanceBehavior
概要
進行方向にある障害物を自動的に回避するステアリング行動です。Vehicleの前方に「検出ボックス」を設定し、そこに障害物がある場合は向きを変えて回避します。
使いどころ
- NPCや車などが壁やオブジェクトに衝突しないようにする。
- シンプルな回避ロジックをステアリング行動に組み込むだけで実装できる。
サンプルコード
import * as THREE from 'three';
import {
Vehicle, SteeringManager, ObstacleAvoidanceBehavior, GameEntity, Vector3
} from 'yuka';
// Three.js準備
const scene = new THREE.Scene();
// カメラ、レンダラーなど省略
// Vehicle生成
const vehicle = new Vehicle();
vehicle.position.set(0, 0, 0);
// 障害物を想定するGameEntityを用意
const obstacleEntity = new GameEntity();
obstacleEntity.position.set(5, 0, 0); // 前方に障害物
// 障害物のメッシュ(Three.js)を作成
const obstacleMesh = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
obstacleMesh.position.copy(obstacleEntity.position);
scene.add(obstacleMesh);
// ObstacleAvoidanceBehaviorに渡すため、配列で保持
const obstacles = [ obstacleEntity ];
// ステアリングマネージャー
const steeringManager = new SteeringManager(vehicle);
// ObstacleAvoidanceBehaviorを追加
const obstacleAvoidance = new ObstacleAvoidanceBehavior(obstacles);
obstacleAvoidance.brakingWeight = 0.2; // 回避時の減速度合い
obstacleAvoidance.dBoxMinLength = 4; // 検出ボックスの最小サイズ
steeringManager.add(obstacleAvoidance);
// Vehicleを描画するSphere
const vehicleMesh = new THREE.Mesh(
new THREE.SphereGeometry(0.3, 16, 16),
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
);
scene.add(vehicleMesh);
// アニメーションループ
function animate() {
requestAnimationFrame(animate);
const delta = 0.016; // 仮に60FPS
steeringManager.calculate(delta);
vehicle.update(delta);
// Three.js側に反映
vehicleMesh.position.copy(vehicle.position);
renderer.render(scene, camera);
}
animate();
この例では、vehicle
が前方にあるobstacleEntity
を避けるようなステアリングが働きます。実際には他のステアリング(Seek, Wanderなど)と組み合わせて利用すると自然な動きになります。
OffsetPursuitBehavior
概要
ターゲットVehicleの位置+オフセット座標を追従するステアリング行動です。隊列や隊形を組む場合に便利です。
使いどころ
- リーダーVehicleを先頭に、数メートル横/後方などに並走するフォロワーを実装したい時。
- フォーメーション移動で、一定の隊列を維持しながら進む場合に役立つ。
サンプルコード
import * as THREE from 'three';
import {
Vehicle, SteeringManager, OffsetPursuitBehavior, Vector3
} from 'yuka';
// リーダーVehicle
const leader = new Vehicle();
leader.position.set(0, 0, 0);
// リーダーVehicleを描画するメッシュ
const leaderMesh = new THREE.Mesh(
new THREE.SphereGeometry(0.3, 16, 16),
new THREE.MeshBasicMaterial({ color: 0x0000ff })
);
scene.add(leaderMesh);
// フォロワーVehicle
const follower = new Vehicle();
follower.position.set(-2, 0, 0);
// ステアリングマネージャー
const steeringManager = new SteeringManager(follower);
// OffsetPursuitBehaviorを設定(リーダーを追随)
const offset = new Vector3(-1, 0, 0); // リーダーから見て左or後ろ方向のオフセット
const offsetPursuit = new OffsetPursuitBehavior(leader, offset);
// 行動を登録
steeringManager.add(offsetPursuit);
// Three.jsで表示する
const followerMesh = new THREE.Mesh(
new THREE.SphereGeometry(0.3, 16, 16),
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
);
scene.add(followerMesh);
function animate() {
requestAnimationFrame(animate);
const delta = 0.016;
// リーダーに何らかの移動(例: 適当に進む)
leader.position.x += 0.01;
// フォロワーのステアリング計算
steeringManager.calculate(delta);
follower.update(delta);
// Three.jsに反映
leaderMesh.position.copy(leader.position);
followerMesh.position.copy(follower.position);
renderer.render(scene, camera);
}
animate();
このコードでは、フォロワーは常にリーダーのoffset
分だけ離れた位置を維持しようと動きます。他のステアリング行動(ObstacleAvoidanceBehaviorなど)も併用可能です。
OnPathBehavior
概要
指定したパス(経路)から大きく外れないように移動するステアリング行動です。
たとえば車が道路からはみ出さないように走るイメージで、道に沿って移動させる場合に活用できます。
使いどころ
- プレイヤーやNPCが予め用意した線形のパス(道)から逸脱しすぎないようにする。
- レースゲームのCPU車など、コースアウトを自動的に防ぐために使える。
サンプルコード
import * as THREE from 'three';
import {
Vehicle, SteeringManager, OnPathBehavior, Path, Vector3
} from 'yuka';
// パスを定義
const path = new Path();
// パス上のウェイポイントを追加
path.add(new Vector3(0, 0, 0));
path.add(new Vector3(5, 0, 2));
path.add(new Vector3(10, 0, 0));
path.loop = false; // ループするならtrue
// Three.jsでパスの可視化
const pathLineGeometry = new THREE.BufferGeometry().setFromPoints(path._waypoints);
const pathLineMaterial = new THREE.LineBasicMaterial({ color: 0xffff00 });
const pathLine = new THREE.Line(pathLineGeometry, pathLineMaterial);
scene.add(pathLine);
// Vehicle
const vehicle = new Vehicle();
vehicle.position.set(0, 0, 0);
// OnPathBehaviorを設定
const onPathBehavior = new OnPathBehavior(path);
// パスからどのくらい離れたら戻すか(半径)
onPathBehavior.radius = 1.0;
const steeringManager = new SteeringManager(vehicle);
steeringManager.add(onPathBehavior);
// Three.js表示用メッシュ
const vehicleMesh = new THREE.Mesh(
new THREE.SphereGeometry(0.3, 16, 16),
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
);
scene.add(vehicleMesh);
function animate() {
requestAnimationFrame(animate);
const delta = 0.016;
steeringManager.calculate(delta);
vehicle.update(delta);
vehicleMesh.position.copy(vehicle.position);
renderer.render(scene, camera);
}
animate();
OnPathBehavior
は「パスに沿う」だけで自動的に前進はしません。SeekBehavior
やFollowPathBehavior
などと組み合わせることで、より自然にパスに沿って進めます。
Path
概要
Yukaで複数のウェイポイントを管理し、その間を移動するための経路を表すクラス。
FollowPathBehavior
やOnPathBehavior
などで利用されます。
使いどころ
- キャラクターが順番にウェイポイントをたどって移動する巡回ルート。
-
loop
フラグをtrueにすれば、最後のウェイポイントの後に最初へ戻るループが作れる。
サンプルコード
import { Path, Vector3 } from 'yuka';
// Pathインスタンスを生成
const path = new Path();
// ウェイポイント追加
path.add(new Vector3(0, 0, 0));
path.add(new Vector3(5, 0, 2));
path.add(new Vector3(10, 0, 0));
// ループにしたい場合
path.loop = true;
// 次のウェイポイントを取得
console.log('Current:', path.current()); // 最初のウェイポイント
path.advance(); // 次に進む
console.log('Next:', path.current());
// Three.jsで可視化するときは path._waypoints をLineにして描画するなど
このPath
をFollowPathBehavior
などに渡してやると、「順番にポイントを巡回する」動きを実現できます。
Plane
概要
幾何学的な平面を定義するクラスで、法線ベクトルと距離によって表現されます。点が平面のどちら側にあるかなどの計算で利用します。
使いどころ
- コリジョン: レイが平面と交差する位置を計算する。
- 3Dオブジェクトと平面の交差判定など。
サンプルコード
import { Plane, Vector3 } from 'yuka';
// 平面を定義(法線: (0,1,0), 距離0 -> y=0 の平面)
const plane = new Plane(new Vector3(0, 1, 0), 0);
// ある点が平面より上か下かをチェック
const point = new Vector3(0, 5, 0);
const dist = plane.distanceToPoint(point);
if (dist > 0) {
console.log('Point is above the plane');
} else if (dist < 0) {
console.log('Point is below the plane');
} else {
console.log('Point is exactly on the plane');
}
Three.jsのPlane
とは別物ですが、同様の用途(平面判定や距離計算など)に使えます。
Polygon
概要
平面上の多角形を表すクラス。NavMesh
で使われるポリゴンや衝突処理などに活用できます。
使いどころ
- ナビゲーションメッシュなどでの各ポリゴン領域を表す。
- 3Dモデルの一部をポリゴンとして扱い、内部テストや辺の情報を得る。
サンプルコード
import { Polygon, Vector3 } from 'yuka';
// ポリゴンを作成し、頂点をCCW順で設定
const polygon = new Polygon();
const points = [
new Vector3(0, 0, 0),
new Vector3(2, 0, 0),
new Vector3(2, 0, 2),
new Vector3(0, 0, 2)
];
polygon.fromContour(points);
// ポリゴンが凸かどうか
const isConvex = polygon.convex();
console.log('Convex:', isConvex);
// 中心(重心)を計算
polygon.computeCentroid();
console.log('Centroid:', polygon.centroid);
// 点がポリゴン内に含まれるか
const testPoint = new Vector3(1, 0, 1);
const contained = polygon.contains(testPoint);
console.log('Contains:', contained);
Polygon
はplane
やedge
、centroid
など内部的に管理し、幾何計算や包含判定などが行えます。
Polyhedron
概要
多面体(複数のPolygonで構成される立体)を表すクラス。ナビゲーションやコリジョン等で使えます。
使いどころ
- AABBなどの形状をPolyhedron化するなど、オブジェクトの形状を抽象化して扱う場合。
- 例えばカスタムの凸メッシュをPolyhedronとして扱い、衝突判定やナビゲーションに応用する。
サンプルコード
import { Polyhedron, Polygon, Vector3 } from 'yuka';
// 立方体を構成する例
const polyhedron = new Polyhedron();
// 6面の四角形をPolygonとして定義
const face1 = new Polygon().fromContour([
new Vector3(-1, -1, 1),
new Vector3( 1, -1, 1),
new Vector3( 1, 1, 1),
new Vector3(-1, 1, 1)
]);
// ...他の5面も同様に定義
// polyhedron.facesにpush
polyhedron.faces.push(face1 /*, face2, face3, ...*/);
// 顔の情報から頂点などを再計算
polyhedron.computeUniqueVertices();
polyhedron.computeUniqueEdges();
polyhedron.computeCentroid();
console.log('Vertices:', polyhedron.vertices.length);
console.log('Edges:', polyhedron.edges.length);
console.log('Centroid:', polyhedron.centroid);
Polyhedron
を通じて、3D空間の形状をポリゴン面の集合として扱えます。例えばナビゲーションメッシュの一部やCollision形状として活用できます。
PriorityQueue
概要
優先度付きキューを実装するクラス。DijkstraやA*などの経路探索アルゴリズムが内部的に利用します。
使いどころ
- AStar、DijkstraなどのOpenリスト管理に使われる。
- 自作で他の優先度ベース処理(タスクスケジューリングなど)に流用するのも可。
サンプルコード
import { PriorityQueue } from 'yuka';
const pq = new PriorityQueue();
// 要素をプッシュ(要素, 優先度)
pq.push('taskA', 2);
pq.push('taskB', 1);
pq.push('taskC', 3);
// 取り出し
while (pq.size() > 0) {
const element = pq.pop(); // 優先度が最も低い(または高い)順に取り出す
console.log(element);
}
YukaのPriorityQueue
は「小さいスコアが高優先度」として扱うことが多いので、pop()時に最も優先度が低い(スコアが小さい)要素が返ります。
PursuitBehavior
概要
動くターゲットを追跡するステアリング行動です。ターゲットの現在の速度・位置から「先読み」して追いかけるため、静止ターゲットに対するSeekBehavior
よりも賢い追跡が可能。
使いどころ
- 敵がプレイヤーキャラを追跡するとき。
- スポーツゲームで相手プレイヤーをマークするAIなど。
サンプルコード
import * as THREE from 'three';
import {
Vehicle, SteeringManager, PursuitBehavior
} from 'yuka';
// ターゲットVehicle
const target = new Vehicle();
target.position.set(5, 0, 0);
// PursuitするVehicle
const pursuer = new Vehicle();
pursuer.position.set(0, 0, 0);
// ステアリングを設定
const pursuit = new PursuitBehavior(target);
const steeringManager = new SteeringManager(pursuer);
steeringManager.add(pursuit);
// Three.jsでメッシュ
const targetMesh = new THREE.Mesh(
new THREE.SphereGeometry(0.2, 16, 16),
new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
scene.add(targetMesh);
const pursuerMesh = new THREE.Mesh(
new THREE.SphereGeometry(0.2, 16, 16),
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
);
scene.add(pursuerMesh);
function animate() {
requestAnimationFrame(animate);
const delta = 0.016;
// ターゲットに適当な移動を与える(例: x方向に動く)
target.position.x += 0.01;
// ステアリング計算→更新
steeringManager.calculate(delta);
pursuer.update(delta);
// Three.jsに反映
targetMesh.position.copy(target.position);
pursuerMesh.position.copy(pursuer.position);
renderer.render(scene, camera);
}
animate();
Pursuitは「ターゲットが将来どこにいるか」を推定しながら進むため、直線的なSeekBehavior
よりも追いつきやすくなります。
Quaternion
概要
3D回転を扱うための四元数クラスです。ゲームAIでオブジェクトを回転させる時などに役立ちます。Three.jsにも同様のTHREE.Quaternion
がありますが、Yuka側のQuaternion
も独立して用意されています。
使いどころ
- エージェントの向き(回転)を表現・合成する時に、ジンバルロックを回避したスムーズな回転が可能。
-
lookAt()
やrotateTo()
などで角度制限付きの回転制御を行える。
サンプルコード
import { Quaternion, Vector3 } from 'yuka';
// 単位クォータニオン
const q1 = new Quaternion(); // (0,0,0,1)
// オイラー角からセット(YXZオーダー)
q1.fromEuler(Math.PI * 0.5, 0, 0); // x軸に90度回転
// もうひとつ作って合成
const q2 = new Quaternion();
q2.fromEuler(0, Math.PI * 0.5, 0); // y軸に90度回転
// 掛け合わせる
const qCombined = new Quaternion().multiplyQuaternions(q1, q2);
// => x軸90度→y軸90度の回転が合成されたもの
// ベクトルに適用
const v = new Vector3(1, 0, 0);
v.applyQuaternion(qCombined); // => 結果が (0, 0, -1) など
console.log('Rotated vector:', v);
Yuka.Quaternion
もmultiply()
, slerp()
, inverse()
など多彩なメソッドを備えています。Three.jsとの混在利用に注意が必要ですが、数学的機能として同等に使うことができます。
Ray
概要
3D空間内でのレイ(光線)を表すクラス。原点origin
と方向direction
の2つのVector3
で構成され、衝突判定や視線判定でよく使われます。
使いどころ
- 「このレイが物体と交差するか?」「どこで交差するか?」などを判定したいとき。
- Three.jsの
Raycaster
と似た概念ですが、Yuka用としてAABBやBoundingSphere、BVHとの交差を計算するメソッドがあります。
サンプルコード
import * as THREE from 'three';
import { Ray, Vector3 } from 'yuka';
// YukaのRay
const ray = new Ray(
new Vector3(0, 0, 0), // origin
new Vector3(1, 0, 0) // direction(単位ベクトルが望ましい)
);
// AABBなどとの交差をチェック
// const intersection = ray.intersectAABB(aabb, new Vector3());
// if (intersection) {
// console.log('Ray intersects AABB at:', intersection);
// }
// Three.js側で視覚的にRayを描画する例
const rayGeometry = new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(0,0,0),
new THREE.Vector3(5,0,0), // 適当に長さ5
]);
const rayMaterial = new THREE.LineBasicMaterial({ color: 0xff0000 });
const rayLine = new THREE.Line(rayGeometry, rayMaterial);
scene.add(rayLine);
Ray
には intersectAABB()
, intersectBoundingSphere()
, intersectPlane()
, intersectBVH()
など、便利な衝突検出メソッドが揃っています。Three.jsのRaycaster
とは別物ですが、用途は似ています。
RectangularTriggerRegion
概要
矩形(四角形)の領域を表すトリガーリージョン。キャラクター(GameEntity
)やプレイヤーがこの四角形の範囲に入ったかどうかを判定するための仕組みです。
使いどころ
- ゲーム中で「特定のエリアに入ったらイベント発火」など、四角形領域で起動するトリガーを実装したい時。
-
Trigger
と組み合わせて、領域に入った瞬間に何かしらのAI処理を行う。
サンプルコード
import { RectangularTriggerRegion, Vector3 } from 'yuka';
// 中心(center)と幅(width), 高さ(height), 奥行(depth)などから生成
const region = new RectangularTriggerRegion(
new Vector3(0, 0, 0), // 中心
10, // width
1, // height (2D的に使うなら薄くする)
5 // depth
);
// あるエンティティが領域内にいるかどうか
const entityPos = new Vector3(3, 0, 2);
const isInside = region.contains(entityPos);
console.log('Is entity inside rectangular region?', isInside);
RectangularTriggerRegion
のcontains(position)
がtrue
を返せば、そのオブジェクトは領域内です。これをTrigger
と合わせ、領域への侵入を検知してイベントを発火する形が一般的です。
Regulator
概要
一定の更新頻度を保つための仕組み。例えば「AIの思考は1秒に数回だけ行う」「高負荷な計算を毎フレームではなく一定間隔で実行」といった調整が可能。
使いどころ
- 「フレームが高FPSでもAI処理は1秒に10回程度に抑えたい」など、パフォーマンスを安定させる。
- NPCのステート更新頻度や視界判定の頻度を制御。
サンプルコード
import { Regulator } from 'yuka';
// 1秒あたり2回の更新(つまり0.5秒おき)
const regulator = new Regulator(2);
function update() {
requestAnimationFrame(update);
// レンダリングは常にする
renderer.render(scene, camera);
// しかしAIなどの重い処理はregulatorで間引く
if (regulator.ready()) {
// AI更新処理(例えばMemorySystem.update()やGoalEvaluatorなど)
console.log('Update AI logic...');
}
}
update();
regulator.ready()
がtrue
を返すタイミングのみロジックを実行することで、計算負荷を制限できます。
RightSCurveFuzzySet
概要
ファジィ集合のS字型(右寄り)を表すクラスです。左側ではメンバーシップが1に近く、右に行くに従いS字カーブで0へ落ちていく。
使いどころ
- 「ある閾値以下はほぼ常に1とみなし、それを超えると急激に0へ近づく」ようなファジィ評価をしたいケース。
- 例:プレイヤーのHPが高い側をS字で定義する場合など。
サンプルコード
import { RightSCurveFuzzySet } from 'yuka';
// (left, midpoint, right)
const rightSCurve = new RightSCurveFuzzySet(0, 50, 100);
// x=10など小さい値はまだ1に近いメンバーシップを持ち、
// x=80,90と大きくなるとS字カーブで0に近づく
console.log(rightSCurve.computeDegreeOfMembership(10));
console.log(rightSCurve.computeDegreeOfMembership(80));
5. RightShoulderFuzzySet
概要
ファジィ集合の右肩型。右側でメンバーシップが1を維持し、左方向へ向かうにつれ0に落ちる肩型です。
使いどころ
- 「ある値以上はほぼ1、それ未満は徐々に0になる」表現をしたいときに便利。
-
LeftShoulderFuzzySet
の逆パターン。
サンプルコード
import { RightShoulderFuzzySet } from 'yuka';
// (left, midpoint, right)
const highSet = new RightShoulderFuzzySet(50, 70, 100);
// x=80,90,100あたりはメンバーシップ1近く
// x=50以下は0に近い
console.log('membership at x=60:', highSet.computeDegreeOfMembership(60));
console.log('membership at x=90:', highSet.computeDegreeOfMembership(90));
SAT
概要
Separating Axis Theorem(分離軸定理)を用いた衝突判定のためのユーティリティ。凸形状同士が分離する軸があるかどうかをチェックし、衝突の有無を判定します。
使いどころ
- OBBやPolyhedronなどの凸形状同士の衝突判定に利用される。
- 直接使う機会は少ないが、内部アルゴリズムとして扱われることが多い。
サンプルコード(概念例)
import { SAT, OBB } from 'yuka';
// 2つのOBBをSATで衝突判定
const obb1 = new OBB();
const obb2 = new OBB();
// ... center, rotation, halfSize等を設定
const isColliding = SAT.intersectsOBB(obb1, obb2);
console.log('Collision:', isColliding);
ほかにも SAT.intersectsPolyhedron()
などのメソッドで凸多面体間の衝突を判定できる場合があります。
SeekBehavior
概要
目標点に最短で近づくステアリング行動です。ArriveBehavior
との違いは減速せず常に最大速度で向かう点。
使いどころ
- NPCがターゲット座標を追いかけて移動したい場合。
- シンプルな到達行動(Stoppingが不要なシーンなど)。
サンプルコード
import * as THREE from 'three';
import { Vehicle, SteeringManager, SeekBehavior, Vector3 } from 'yuka';
// Vehicle
const vehicle = new Vehicle();
vehicle.position.set(0, 0, 0);
// 目標地点
const target = new Vector3(10, 0, 5);
// SeekBehavior
const seek = new SeekBehavior(target);
// ステアリングマネージャー
const steeringManager = new SteeringManager(vehicle);
steeringManager.add(seek);
// Three.jsメッシュ
const mesh = new THREE.Mesh(
new THREE.SphereGeometry(0.2),
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
);
scene.add(mesh);
function animate() {
requestAnimationFrame(animate);
const delta = 0.016;
steeringManager.calculate(delta);
vehicle.update(delta);
mesh.position.copy(vehicle.position);
renderer.render(scene, camera);
}
animate();
常に最大速度でターゲットへ向かうため、近づいても停止せず通り過ぎる挙動になります。
SeparationBehavior
概要
群れや集団で、仲間から一定距離を保ち離れるように働くステアリング行動。Alignment・Cohesionと組み合わせるとBoidsに近い挙動を実現できます。
使いどころ
- フロック行動で「ぶつからないように散らばる」要素を加えたい場合。
- NPC同士が重ならずに自然に避け合うアニメーションをしたい場合。
サンプルコード
import * as THREE from 'three';
import { Vehicle, SteeringManager, SeparationBehavior } from 'yuka';
// 複数のVehicle
const vehicles = [];
for (let i = 0; i < 5; i++) {
const v = new Vehicle();
v.position.set(Math.random()*5, 0, Math.random()*5);
vehicles.push(v);
const mesh = new THREE.Mesh(
new THREE.SphereGeometry(0.2),
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
);
scene.add(mesh);
v.userData = { mesh };
}
// SeparationBehaviorを各Vehicleに設定
for (let v of vehicles) {
const steeringManager = new SteeringManager(v);
const separation = new SeparationBehavior();
steeringManager.add(separation);
v.userData.steeringManager = steeringManager;
}
// 毎フレーム neighborsを更新してステアリング計算
function animate() {
requestAnimationFrame(animate);
const delta = 0.016;
// 近くの仲間をneighborに登録するなど
for (let v1 of vehicles) {
v1.neighbors.length = 0;
for (let v2 of vehicles) {
if (v1 !== v2) {
const dist = v1.position.distanceTo(v2.position);
if (dist < 3) {
v1.neighbors.push(v2);
}
}
}
}
// ステアリング
for (let v of vehicles) {
v.userData.steeringManager.calculate(delta);
v.update(delta);
v.userData.mesh.position.copy(v.position);
}
renderer.render(scene, camera);
}
animate();
SeparationBehavior
はvehicle.neighbors
を参照し、近すぎる仲間から離れる力を生成します。
SingletonFuzzySet
概要
ファジィ集合のうち「ある一点」でのみメンバーシップが最大(=1)になる集合。他の値は0。三角形や台形ではなく「離散的」なファジィ集合の一種。
使いどころ
- 「ピンポイントの値」に対してのみメンバーシップ1とし、それ以外は0という定義。
- ファジィというより「ほぼクリスプ(離散)に近い使い方」をしたい場合。
サンプルコード
import { SingletonFuzzySet } from 'yuka';
// 例:中心値50のみメンバーシップ1、それ以外は0
const singleton = new SingletonFuzzySet(50);
// 入力値をテスト
console.log('membership at x=49:', singleton.computeDegreeOfMembership(49)); // => 0
console.log('membership at x=50:', singleton.computeDegreeOfMembership(50)); // => 1
console.log('membership at x=51:', singleton.computeDegreeOfMembership(51)); // => 0
Smoother
概要
スカラー値やベクトル値を移動平均でスムーズにするユーティリティ。たとえばNPCの向きや速度などに急激な変化を出したくない際、サンプルを一定数蓄えて平均をとり平滑化します。
使いどころ
- ステアリング結果やセンサーノイズを減らすために、過去数フレームの値を平均化して滑らかにする。
- フォロワーカメラなどの補間にも応用可能。
サンプルコード (スカラー版)
import { Smoother } from 'yuka';
const sampleSize = 10;
const smoother = new Smoother(sampleSize);
// 毎フレーム、新しい値を追加&取得
function update(speed) {
const smoothedValue = smoother.update(speed);
console.log('Smoothed speed:', smoothedValue);
}
Vector3版の場合はSmoother<Vector3>
を使います(Yukaにはジェネリクス的な仕組みでSmootherを使えるバージョンがあります)。
SphericalTriggerRegion
概要
球形のトリガーリージョンを表すクラス。RectangularTriggerRegion
と同様、領域に進入したかどうかを判定するが、球体の範囲で行います。
使いどころ
- 「半径R以内に入ったらイベント発火」など、円(球)形のゾーンでトリガーを作りたい場合。
- 敵の索敵範囲やアイテム取得範囲などを球形で表現。
サンプルコード
import { SphericalTriggerRegion, Vector3 } from 'yuka';
// 中心と半径
const sphericalRegion = new SphericalTriggerRegion(new Vector3(0,0,0), 5);
// あるエンティティの位置
const entityPos = new Vector3(3, 0, 4);
if (sphericalRegion.contains(entityPos)) {
console.log('Inside spherical trigger region!');
}
トリガーの使い方としてはRectangularTriggerRegion
と同様にTrigger
クラスで領域を設定し、update()
でcontains()
をチェックしてイベントを起動する流れが一般的です。
State
概要
キャラクターAIなどで使われる**状態(ステート)**を表すクラス。単一状態ごとにenter()
, execute()
, exit()
のライフサイクルを持ちます。
使いどころ
- 敵AIが「巡回」「追跡」「攻撃」といったステートを切り替えるときに使用。
-
StateMachine
とセットで活用。
サンプルコード
import { State, GameEntity } from 'yuka';
class PatrolState extends State {
enter(owner) {
console.log(`${owner.name} enters PatrolState`);
}
execute(owner) {
console.log(`${owner.name} is patrolling...`);
// パトロールのロジック:例えばWaypointsを巡回
}
exit(owner) {
console.log(`${owner.name} exits PatrolState`);
}
}
// キャラエンティティ例
class MyNPC extends GameEntity {
constructor() {
super();
this.name = 'NPC1';
}
}
owner
にはStateMachine
が持つ所有者(MyNPC
など)が渡されます。
StateMachine
概要
複数のState
を管理し、現在の状態を切り替えながらAIを動かすフレームワーク。changeState()
, update()
などのメソッドを持ちます。
使いどころ
- 敵AIが「巡回中→プレイヤー発見→追跡→攻撃→…」のように状態遷移を管理する。
- 大小さまざまなFSM(Finite State Machine)を実装。
サンプルコード
import { StateMachine } from 'yuka';
// PatrolState と AttackState などを用意
class PatrolState { /* enter/execute/exit */ }
class AttackState { /* enter/execute/exit */ }
// NPC
class MyNPC extends GameEntity {
constructor() {
super();
this.stateMachine = new StateMachine(this);
}
update(delta) {
super.update(delta);
this.stateMachine.update(); // 現在のステートの execute が呼ばれる
}
}
// インスタンス
const npc = new MyNPC();
npc.name = 'NPC1';
const patrolState = new PatrolState();
const attackState = new AttackState();
// 初期ステートを設定
npc.stateMachine.changeState(patrolState);
// 条件に応じて
// npc.stateMachine.changeState(attackState);
各ステートはenter()
, execute()
, exit()
を定義し、StateMachine.update()
でexecute()
が呼ばれます。
SteeringBehavior
概要
1つのステアリング行動を表す抽象クラス。calculate(vehicle, force, delta)
などを実装し、移動ベクトルを算出します。SeekBehavior
, FleeBehavior
などがこれを継承。
使いどころ
-
SteeringManager
を介してVehicle
に適用され、NPCの挙動を作る。 - 単独で使うことは少なく、各派生クラス(SeekBehaviorなど)として利用。
サンプルコード
import { SteeringBehavior, Vector3 } from 'yuka';
class MyCustomBehavior extends SteeringBehavior {
calculate(vehicle, force, delta) {
// カスタムのステアリングロジック
// 例えば単純に x軸方向へ押す
force.set(1, 0, 0);
return force;
}
}
このように継承クラスでcalculate()
をオーバーライドする形でカスタムステアリングを実現します。
SteeringManager
概要
複数のSteeringBehavior
をまとめ、それらを合成した最終的なステアリング力をVehicle
に適用するクラス。add()
, remove()
, clear()
などで行動を管理。
使いどころ
-
SeekBehavior
+ObstacleAvoidanceBehavior
など、複数のステアリングを同時に使用して自然な合成結果を得る。 - ステアリングを加重合成して最終の移動方向を決定。
サンプルコード
import { Vehicle, SteeringManager, SeekBehavior, FleeBehavior } from 'yuka';
// Vehicle
const vehicle = new Vehicle();
vehicle.maxSpeed = 5;
// SteeringManager
const steeringManager = new SteeringManager(vehicle);
// Behaviors
const seek = new SeekBehavior(targetPos);
const flee = new FleeBehavior(enemyPos);
// 追加
steeringManager.add(seek);
steeringManager.add(flee);
// 毎フレーム
function update(delta) {
// 合成されたステアリング力を計算
steeringManager.calculate(delta);
// Vehicleを更新
vehicle.update(delta);
}
複数の行動を「重み(weight)」付きで加算して最終移動ベクトルを得ます。
Task
概要
非同期タスクや段階的な処理を表すクラス。キューに入れて順番に処理したいロジックをまとめ、TaskQueue
で管理できます。
使いどころ
- 「複数のジョブを順番に処理する」「長い処理を少しずつ実行する」など。
- AIで大きな計算を分割し、フレーム毎に進捗させる用途。
サンプルコード
import { Task } from 'yuka';
class MyTask extends Task {
execute() {
console.log('Long operation...');
// 重い処理を行う
this.finished = true; // 完了時
}
}
// 使用例
const task = new MyTask();
task.execute();
TaskQueue
概要
Task
を複数登録し、キューとして順に実行する仕組み。
実行済みタスクを削除したり、次のタスクへ移ったりできる。
使いどころ
- 大きな処理を細切れタスクに分けて、フレーム毎に少しずつ消化し、ゲームのフレームレートを落とさないようにする。
- ネットワーク処理やAI学習などをバックグラウンドで実行する。
サンプルコード
import { TaskQueue } from 'yuka';
const queue = new TaskQueue();
class MyTask extends Task {
execute() {
console.log('Task running...');
this.finished = true;
}
}
queue.enqueue(new MyTask());
queue.enqueue(new MyTask());
// 毎フレーム: キューをチェック
function update() {
queue.update(); // 先頭タスクのexecuteを呼び出し、終了なら次へ
requestAnimationFrame(update);
}
update();
queue.update()
が未完了タスクを1つ実行し、finished
ならデキューします。タスクの分割が細かいほどフレーム単位で処理を分散できます。
Telegram
概要
メッセージ(封筒)の実体を表すクラス。MessageDispatcher
によりエンティティ間で送受信され、遅延メッセージなどにも対応可能。
使いどころ
-
sender
,receiver
,message
,delay
,data
を持ち、NPC同士の通信に利用。 - 例えば「数秒後に攻撃命令を送る」「アイテム生成のリクエスト」などを実装できる。
サンプルコード
import { Telegram, MessageDispatcher } from 'yuka';
const dispatcher = new MessageDispatcher();
const telegram = new Telegram(
senderEntity,
receiverEntity,
'HELLO',
1000, // 1秒後に配信
{ value: 123 }
);
// ディスパッチ
dispatcher.dispatch(telegram);
受信側のhandleMessage(telegram)
が呼ばれ、メッセージを処理する仕組みです。
Think
概要
「GoalベースAI」のトップレベルゴールとして、複数のGoalEvaluator
を管理し、「どのゴールを優先するか」を決定するクラス。CompositeGoal
を継承しています。
使いどころ
- NPCが「今は探索したい? 攻撃したい? 逃げたい?」など複数の戦略を評価し、最も望ましい戦略のGoalを採用。
-
arbitrate()
メソッドでGoalEvaluatorを評価し、新しいゴールをセットする。
サンプルコード
import { Think, GoalEvaluator } from 'yuka';
// GoalEvaluator例
class AttackEvaluator extends GoalEvaluator {
calculateDesirability(owner) {
// HPや敵との距離から攻撃の望ましさを計算
return someValue;
}
setGoal(owner) {
// 攻撃ゴールをownerのgoalサブゴールに追加
}
}
class MyThink extends Think {
constructor(owner) {
super(owner);
// evaluator追加
}
// or override activate/execute
}
// NPC
class MyNPC extends GameEntity {
constructor() {
super();
this.brain = new MyThink(this);
}
update(delta) {
super.update(delta);
this.brain.execute();
}
}
Think
内部で複数のGoalEvaluator
を持ち、arbitrate()
するときに一番スコアの高いゴールを生成して設定します。
Time
概要
フレームごとの時間計測や、固定Δtでのシミュレーション制御を可能にするクラス。update()
を呼ぶとdelta
などが更新されます。
使いどころ
- 「実時間経過」に基づいて移動量などを計算する場合や、固定ステップで物理シミュレーションを行う場合。
-
enableFixedDelta()
,setFixedDelta(value)
でフレームレートに依存しないステップ制御ができる。
サンプルコード
import { Time } from 'yuka';
const time = new Time();
// 毎フレーム
function animate() {
requestAnimationFrame(animate);
time.update(); // 内部でdeltaやelapsedを更新
const delta = time.getDelta(); // 秒
// NPCや物理などの更新にdeltaを使う
renderer.render(scene, camera);
}
animate();
time.getDelta()
はフレーム間隔(秒)。time.getElapsed()
で累計経過時間を得られます。
TriangularFuzzySet
概要
ファジィ集合の三角形形状。left
, midpoint
, right
を指定し、山型の単純なメンバーシップ関数を表す。
使いどころ
- 「近い」「中くらい」「遠い」などを三角で定義し、ファジィロジックに利用する。
-
FuzzyModule
/FuzzyVariable
に登録して、fuzzify/defuzzifyを行う。
サンプルコード
import { TriangularFuzzySet } from 'yuka';
// TriangularFuzzySet(left, midpoint, right)
const nearSet = new TriangularFuzzySet(0, 50, 100);
// x=0~50でメンバーシップが増え、50を最大値1、50~100で減少し0になる
console.log('membership at 25:', nearSet.computeDegreeOfMembership(25)); // ~0.5
Trigger
概要
トリガーの基底クラス。TriggerRegion
と組み合わせて、「特定エリアや条件で起動するイベント」の仕組みを提供する。
Trigger#update()
でエンティティが範囲内にいるかをチェックし、onTriggerEnter()
などを呼ぶ形。
使いどころ
- NPCやプレイヤーが「ある地点に踏み込んだらイベント発火」など、ゲーム内スクリプト・ギミックを組む。
-
RectangularTriggerRegion
やSphericalTriggerRegion
を割り当てて使う。
サンプルコード
import { Trigger, RectangularTriggerRegion, Vector3 } from 'yuka';
class DoorTrigger extends Trigger {
constructor() {
super();
this.region = new RectangularTriggerRegion(new Vector3(5,0,5), 2,1,2);
}
// エンティティが範囲に入った
onTriggerEnter(entity) {
console.log('Entity entered the door region');
// ドアを開ける等
}
// update()内で region.contains(entity.position) をチェックする仕組み
}
TriggerRegion
概要
トリガーの領域を表す基底クラス。RectangularTriggerRegion
, SphericalTriggerRegion
などの派生がある。contains(position)
で内部判定を行う。
使いどころ
- 領域型トリガーにおいて、どのような形状で範囲を定義するかを抽象化して扱う。
サンプルコード
import { TriggerRegion } from 'yuka';
class MyCustomRegion extends TriggerRegion {
constructor(center, radius) {
super();
this.center = center;
this.radius = radius;
}
contains(position) {
return position.distanceTo(this.center) < this.radius;
}
}
Vector3
概要
3Dベクトルを表すクラス。x,y,zを持ち、加減算、スカラー乗算、正規化など多彩な操作が可能。Three.jsのTHREE.Vector3
と似たインターフェース。
使いどころ
- 位置・速度・力など3Dベクトルを扱うあらゆる場面。
- Three.jsと混用する際は注意が必要(YukaのVector3 vs THREE.Vector3)。
サンプルコード
import { Vector3 } from 'yuka';
// 作成
const v1 = new Vector3(1,2,3);
const v2 = new Vector3(4,5,6);
// 演算
v1.add(v2); // => (5, 7, 9)
v1.multiplyScalar(2); // => (10, 14, 18)
v1.normalize(); // 正規化
Yuka.Vector3
をThree.jsと一緒に使う場合、必要に応じてデータをコピーしたり、toArray()
などでやりとりします。
Vehicle
概要
MovingEntity
の派生クラスで、ステアリング行動に最適化された実装。SteeringManager
を直接持つことも多い(ただし、YukaではVehicle
はSteeringManager
を自動保持しない設計)。
使いどころ
- NPCや移動体の実装で、速度や向き、ステアリングを適用するのが
Vehicle
の典型パターン。 - 例:
vehicle.maxSpeed
,vehicle.update(delta)
などで自然に移動するAIを実現。
サンプルコード
import { Vehicle, SteeringManager, SeekBehavior, Vector3 } from 'yuka';
const vehicle = new Vehicle();
vehicle.position.set(0,0,0);
vehicle.maxSpeed = 5;
const steeringManager = new SteeringManager(vehicle);
const seek = new SeekBehavior(new Vector3(10,0,10));
steeringManager.add(seek);
function animate() {
requestAnimationFrame(animate);
const delta = 0.016;
steeringManager.calculate(delta);
vehicle.update(delta);
// Three.jsメッシュに反映
}
animate();
Vision
概要
NPCの視野判定を行うためのクラス。指定の角度や距離内でターゲットが見えるかどうかを判定し、障害物がないかラインオブサイトテストも可能。
使いどころ
- ステルスゲームなどで「敵の視野コーンに入ったら見つかる」といった仕組みを簡単に実装。
-
Vision
にはangle
,range
,lineOfSightTest()
などがあり、障害物との衝突を考慮した見える/見えないを判定。
サンプルコード
import { Vision, GameEntity, Ray, Vector3 } from 'yuka';
// NPC
class Guard extends GameEntity {
constructor() {
super();
this.vision = new Vision(this);
this.vision.range = 10;
this.vision.angle = Math.PI / 4; // 45度
}
update(delta) {
super.update(delta);
// 周囲のターゲットに対して vision.visible(target) をチェック
}
}
// 視線判定
const guard = new Guard();
const intruder = new GameEntity();
intruder.position.set(5, 0, 0);
const canSee = guard.vision.visible(intruder);
if (canSee) {
console.log('Guard sees the intruder!');
}
内部でlineOfSightTest()
が呼ばれ、障害物や角度チェックを行います。
WanderBehavior
概要
ランダムにさまよい歩くようなステアリング行動。一定の円周上でターゲット方向を変え続け、自然な揺らぎを持って動作します。
使いどころ
- NPCが適当にぶらぶら動く挙動を付けたい場合に便利。
- アイドル状態の動きや、動物がフラフラ移動する感じを出せる。
サンプルコード
import * as THREE from 'three';
import { Vehicle, SteeringManager, WanderBehavior } from 'yuka';
const vehicle = new Vehicle();
vehicle.position.set(0,0,0);
const wander = new WanderBehavior();
// パラメータ (wanderRadius, wanderDistance, wanderJitterなど) 調整可能
const steeringManager = new SteeringManager(vehicle);
steeringManager.add(wander);
// Three.js表示
const mesh = new THREE.Mesh(
new THREE.SphereGeometry(0.2),
new THREE.MeshBasicMaterial({ color: 0xff00ff })
);
scene.add(mesh);
function animate() {
requestAnimationFrame(animate);
const delta = 0.016;
steeringManager.calculate(delta);
vehicle.update(delta);
mesh.position.copy(vehicle.position);
renderer.render(scene, camera);
}
animate();
時間経過とともに移動方向が少しずつランダムに変わり、「ふらふら」移動をするようになります。
ファジィ論理
1. ファジィセットとファジィルールの基本的な活用場面
Yuka には ファジィ論理(Fuzzy Logic) を扱うクラスがあり、NPC の知的な行動を作る際に役立ちます。特に 「明確な判断基準がない、または曖昧な概念を処理する必要がある」 場面で有効です。
以下のような状況では、ファジィ論理が適しています。
- 敵の AI(攻撃・防御の判断)
- キャラクターの移動速度調整
- 戦闘中の意思決定(回避するか攻撃するか)
- プレイヤーの行動に応じた NPC の態度変化
- エネルギー管理(スタミナ、HP、MP の調整)
2. 具体的な活用例
(1) NPC の攻撃・防御の判断
状況:
敵 AI がプレイヤーに対して攻撃するか、防御するかを決定する際に、単純に 「HPが50以下なら防御」 のようなルールではなく、もっと自然な動きをさせたい。
ファジィセットの定義例:
- 「HPが低い(0~40%)」
- 「HPが中程度(30~70%)」
- 「HPが高い(60~100%)」
- 「プレイヤーとの距離が近い(0~10m)」
- 「プレイヤーとの距離が遠い(8m~20m)」
ファジィルール:
- もし HP が低い かつ 距離が近い ならば 回避行動を取る
- もし HP が中程度 かつ 距離が近い ならば 攻撃する
- もし HP が高い ならば 積極的に攻撃する
- もし HP が低い かつ 距離が遠い ならば 逃げる
(2) キャラクターの移動速度調整
状況:
NPC がプレイヤーを追跡する際、距離に応じて速度を調整したい。
ファジィセットの定義例:
- 「距離が近い(0~5m)」
- 「距離が中程度(3m~10m)」
- 「距離が遠い(8m~20m)」
ファジィルール:
- もし 距離が近い ならば 速度を遅くする(歩く)
- もし 距離が中程度 ならば 速度を中程度にする(ジョギング)
- もし 距離が遠い ならば 速度を速くする(走る)
このルールを使うことで、NPC の動きが急に変化せず、よりスムーズな追跡 AI を実装できます。
(3) NPC の感情・態度変化
状況:
プレイヤーが NPC に攻撃したり、助けたりすると、NPC の態度が変化する。
ファジィセットの定義例:
- 「プレイヤーの好感度(0~100%)」
- 「プレイヤーの攻撃頻度(0~10回)」
- 「プレイヤーの助けた回数(0~5回)」
ファジィルール:
- もし 好感度が低い ならば NPC は警戒する
- もし 好感度が高い ならば NPC は協力的になる
- もし 攻撃頻度が高い ならば NPC は敵対する
- もし 助けた回数が多い ならば NPC は友好的になる
このようなルールを使うことで、NPC がプレイヤーに対して自然に感情を持つようなシステムが作れます。
3. Yuka でファジィロジックを実装する方法
Yuka には FuzzyModule(ファジィモジュール) というクラスがあり、これを使ってファジィルールを実装できます。
実装の流れ
- FuzzyModule を作成
- ファジィセット(FuzzySet)を定義
- ファジィルール(FuzzyRule)を追加
- 推論(Inference)を実行し、結果を取得
例えば、HP に応じた回避行動を設定する場合は以下のように実装できます。
import { FuzzyModule, FuzzyVariable, FuzzyRule, FuzzyTerm } from 'yuka';
// 1. ファジィモジュールを作成
const fuzzyModule = new FuzzyModule();
// 2. ファジィ変数を定義(HP)
const hp = fuzzyModule.createFLV('HP');
const lowHP = hp.addLeftShoulderSet('Low', 0, 30, 60);
const mediumHP = hp.addTriangleSet('Medium', 30, 60, 90);
const highHP = hp.addRightShoulderSet('High', 60, 90, 100);
// 3. ファジィ変数を定義(回避率)
const evadeChance = fuzzyModule.createFLV('EvadeChance');
const lowEvade = evadeChance.addLeftShoulderSet('Low', 0, 30, 60);
const mediumEvade = evadeChance.addTriangleSet('Medium', 30, 60, 90);
const highEvade = evadeChance.addRightShoulderSet('High', 60, 90, 100);
// 4. ルールを設定
const rule1 = new FuzzyRule(lowHP, highEvade);
const rule2 = new FuzzyRule(mediumHP, mediumEvade);
const rule3 = new FuzzyRule(highHP, lowEvade);
fuzzyModule.addRule(rule1);
fuzzyModule.addRule(rule2);
fuzzyModule.addRule(rule3);
// 5. 現在のHPに基づいて回避率を計算
fuzzyModule.fuzzify('HP', 40);
const evadeResult = fuzzyModule.defuzzify('EvadeChance');
console.log(`回避率: ${evadeResult}`);
4. まとめ
Three.js と Yuka を使ったゲームでファジィセットやファジィルールを活用する場面は、主に NPC の AI 行動、キャラクターの移動、感情の変化などの曖昧な判断が必要な場面 です。
具体的には:
- NPC の攻撃・防御の判断
- キャラクターの移動速度調整
- NPC の感情・態度変化
- プレイヤーの行動に応じた状況判断
Yuka の FuzzyModule を使うと、簡単にファジィロジックを組み込むことができ、よりリアルな NPC の知的行動 を作ることが可能です。
ゲームの難易度調整や自然な動きを作るのにも役立つので、ぜひ活用してみてください!