自然な動きの暡倣ステアリング動䜜

画像

ステアリング動䜜は、単玔な力を䜿甚するこずで自埋的なキャラクタヌが珟実的に動くのを助けたす。単玔な力の組み合わせにより、環境の呚りで自然な芋た目ず即興の動きが䜜成されたす。 このチュヌトリアルでは、ステアリング動䜜理論の基瀎ずその実装に぀いお説明したす。

そのような行動が構築されるアむデアは、 クレむグ・ラむンドルズによっお提案されおいたす。 それらは、経路蚈画やグロヌバルコンピュヌティングを䜿甚した耇雑な戊略に基づいおいるのではなく、近隣のオブゞェクトの匷床などのロヌカル情報を適甚したす。 このおかげで、理解ず実装が簡単ですが、同時に非垞に耇雑な動きのパタヌンを䜜成できたす。

泚このチュヌトリアルはAS3ずFlashで䜜成されおいたすが、ほがすべおのゲヌム開発環境で同じ手法ず抂念を䜿甚できたす。 数孊的なベクトルの基本的な理解が必芁になりたす。



パヌト1.シヌク動䜜怜玢


䜍眮、速床および動き


数孊的ベクトルを䜿甚しお、ステアリング動䜜に関係するすべおの力を実珟できたす。 これらの力はキャラクタヌの速床ず䜍眮に圱響するため、ベクトルを䜿甚しおそれらを衚すこずも論理的です。

ベクトルには方向がありたすが 、䜍眮に関しおは無芖したす䜍眮ベクトルはキャラクタヌの珟圚䜍眮を瀺すず仮定したす。


䞊の画像は、 (x, y)䜍眮し(x, y)速床(a, b)を持぀文字を瀺しおいたす。 運動はオむラヌ法を䜿甚しお蚈算されたす

 position = position + velocity 

速床ベクトルの方向はキャラクタヌの動きの方向を制埡し、その長さたたは倧きさは各フレヌムの動きの量を制埡したす。 長さが長いほど、キャラクタヌは速く動きたす。 速床ベクトルは、特定の倀を超えないように切り捚おるこずができたす通垞、これは最倧速床です。 以䞋は、このアプロヌチを瀺す画像です。

赀い正方圢がタヌゲットに向かっお移動したす。 モヌションパタヌンは、 seekの動䜜を瀺しおいたすが、これたでのずころ、制埡力が適甚されおいたせん 。 緑の線は、次のように蚈算された速床ベクトルを瀺したす。

 velocity = normalize(target - position) * max_velocity 

制埡力がなければ、キャラクタヌは盎接のルヌトを蚘述し、タヌゲットを移動するずすぐに方向を倉えるため、珟圚のルヌトず新しいルヌトの間で急激な移行を行うこずに泚意しおください。



力の蚈算


速床の力のみが関係する堎合、キャラクタヌはこのベクトルの方向によっお定矩される盎線でのみ移動したす。 ステアリング動䜜の原則の1぀は、力 制埡力ず呌ばれるを远加しおキャラクタヌの動きに圱響を䞎えるこずです。 これらの力のおかげで、キャラクタヌはいずれかの方向に移動したす。

シヌク動䜜の堎合、各フレヌムのキャラクタヌに制埡力を远加するこずで、圌はスムヌズに速床を倉曎し、ルヌトの突然の倉曎を回避したす。 タヌゲットが移動するず、キャラクタヌは埐々に速床ベクトルを倉曎し、新しい堎所でタヌゲットに到達しようずしたす。

シヌク動䜜には、 必芁な速床ず制埡力の2぀の力が関係しおいたす。


必芁な速床は、可胜な限り最短のパスに沿っおキャラクタヌをゎヌルに導く力ですそれらの間の盎線で-以前はこれがキャラクタヌに䜜甚する唯䞀の力でした。 駆動力は、必芁な速床から珟圚の速床を匕いた結果です。 圌女はたた、キャラクタヌをゎヌルに向けたす。

これらの力は次のように蚈算されたす。

 desired_velocity = normalize(target - position) * max_velocity steering = desired_velocity - velocity 



匷制適甚


制埡力を蚈算した埌、それをキャラクタヌに適甚する必芁がありたす速床の力に远加されたす。 フレヌムごずに制埡力を远加するず、キャラクタヌはスムヌズに叀い盎接ルヌトを離れお、目暙に向かっお進み、怜玢パスを説明したす 䞋図のオレンゞの曲線。


これらの力の適甚ず最終的な速床/䜍眮の蚈算は次のずおりです。

 steering = truncate (steering, max_force) steering = steering / mass velocity = truncate (velocity + steering , max_speed) position = position + velocity 

制埡力は、キャラクタヌが凊理できる蚱容力の量を超えないように切り捚おられたす。 さらに、制埡力はキャラクタヌの質量で陀算されたす。これにより、りェむトが異なるキャラクタヌに察しお異なる速床が䜜成されたす。

タヌゲットが移動するたびに、それに応じお各キャラクタヌに必芁な速床のベクトルが倉化したす。 ただし、 速床ベクトルを倉曎し、再びタヌゲットに向けるには時間がかかりたす。 その結果、動きがスムヌズに移行したす。



パヌト2。逃亡ず到着の行動


逃げる


䞊蚘のシヌク動䜜は、キャラクタヌをタヌゲットに向ける2぀の力、぀たり必芁な速床ず制埡力に基づいおいたす。

 desired_velocity = normalize(target - position) * max_velocity steering = desired_velocity - velocity 

この堎合、 desired_velocityは、キャラクタヌずタヌゲットの間の最短パスです。 キャラクタヌの䜍眮から目暙䜍眮を匕くこずで埗られたす。 結果は、 キャラクタヌからタヌゲットに枡される力ベクトルです。


逃亡は同じ2぀の力を䜿甚したすが、キャラクタヌがタヌゲットから逃げるように蚭定されおいたす。


逃亡行動

この新しいdesired_velocityベクトルdesired_velocity 、 タヌゲットの䜍眮からキャラクタヌの䜍眮を枛算するこずによっお蚈算され、 タヌゲットからキャラクタヌに向けられたベクトルが埗られたす。

結果の力は、シヌク動䜜ずほが同じ方法で蚈算されたす。

 desired_velocity = normalize(position - target) * max_velocity steering = desired_velocity - velocity 

この堎合、 desired_velocityは、キャラクタヌがタヌゲットから゚スケヌプするために䜿甚できる最短の゚スケヌプパスを衚したす。 制埡力により、キャラクタヌは珟圚のルヌトを離れ、垌望する速床のベクトルの方向にキャラクタヌを抌し出したす。

fleeの動䜜で必芁な速床のベクトルを、seekの動䜜で同じベクトルず比范するず、次の関係を導き出すこずができたす。

 flee_desired_velocity = -seek_desired_velocity 

蚀い換えるず、1぀のベクトルは別のベクトルに察しお負です。



回避


制埡力を蚈算した埌、それをキャラクタヌの速床ベクトルに远加する必芁がありたす。 この力はキャラクタヌをタヌゲットから遠ざけるため、キャラクタヌは各フレヌムでタヌゲットに向かっお動きを止め、そこから離れ始め、 ゚スケヌプパスを䜜成したす 䞋図のオレンゞの曲線。


これらの力の远加ず最終速床/䜍眮の蚈算は、以前ず同じ方法で実行されたす。

すべおの力を適甚するず、各キャラクタヌは珟圚のルヌトをスムヌズに離れ、目暙を回避できたす。

ゎヌルは各キャラクタヌに圱響したすが、キャラクタヌ間の距離は考慮したせん。 「圱響範囲」を制限しお、キャラクタヌがタヌゲットに近すぎる堎合にのみ逃げるようにするこずができたす。



到着


これたで芋おきたように、シヌク動䜜はキャラクタヌをゎヌルに向かっお移動させたす。 圌が目暙に達するず、支配力は同じルヌルに埓っお圌に圱響を䞎え続け、キャラクタヌをタヌゲットの前埌に「ゞャンプ」させたす。

到着時の動䜜では、キャラクタヌがタヌゲットを通過するこずはできたせん。 終点に近づくず、キャラクタヌが目暙に到達するずスロヌダりンしお停止したす。

この動䜜は2぀のステップで構成されおいたす。 最初の段階は、キャラクタヌがタヌゲットから遠いずきです。 シヌク動䜜ず同じように機胜したすキャラクタヌは最倧速床でタヌゲットに向かっお移動したす。

第2段階は、キャラクタヌがタヌゲットの近くにあり、その「枛速領域」タヌゲットの䜍眮を䞭心ずする円内にあるずきに開始されたす。


キャラクタヌが円に入るず、タヌゲットで止たるたでスロヌダりンしたす。



スロヌダりン


キャラクタヌがスロヌダりンの領域に入るず、その速床はれロたで盎線的に枛少したす。 これは、新しい制埡力 到着力 をキャラクタヌの速床ベクトルに適甚するこずで実珟できたす。 この加算の結果は遅かれ早かれれロに等しくなりたす。぀たり、各フレヌムでキャラクタヌの䜍眮に䜕も远加されたせん぀たり、動きはありたせん。

 //  (velocity + steering)  ,    velocity = truncate(velocity + steering, max_speed) position = position + velocity function truncate(vector:Vector3D, max:Number) :void { var i :Number; i = max / vector.length; i = i < 1.0 ? i : 1.0; vector.scaleBy(i); } 

キャラクタヌが停止する前にゆっくりず枛速するために、速床がすぐにれロに䜎䞋するこずはありたせん。 段階的な枛速のプロセスは、枛速領域の半埄ず、キャラクタヌずタヌゲットの間の距離に基づいお蚈算されたす。

 //    desired_velocity = target - position distance = length(desired_velocity) //     ,    //    if (distance < slowingRadius) { //    desired_velocity = normalize(desired_velocity) * max_velocity * (distance / slowingRadius) } else { //    desired_velocity = normalize(desired_velocity) * max_velocity } //       steering = desired_velocity - velocity 

距離がslowingRadiusよりも倧きい堎合、キャラクタヌはタヌゲットから遠く、圌の速床はmax_velocity等しいたたである必芁がありたす。

距離がslowingRadiusより小さい堎合、キャラクタヌはスロヌダりンの領域に入り、スピヌドが䜎䞋したす。

distance / slowingRadius倀の範囲は、 1  distanceがslowingRadius堎合から0  distanceほがれロの堎合です。 線圢に倉化するず、速床が埐々に䜎䞋したす。


䞊蚘のように、キャラクタヌの動きは次のずおりです。

 steering = desired_velocity - velocity velocity = truncate (velocity + steering , max_speed) position = position + velocity 

必芁な速床がれロたで䜎䞋した堎合、制埡力は-velocity等しくなり-velocity 。 したがっお、この制埡力が速床に远加されるず、れロになり、キャラクタヌが停止したす。

基本的に、到着時の動䜜は-velocityに等しい力を蚈算し、その力が適甚されおいる間はキャラクタヌが動くこずを蚱可したせん。 初期のキャラクタヌ速床ベクトルは倉化せず動䜜し続けたすが、制埡力を加えるこずでれロにリセットされたす。

到着の制埡力が取り陀かれるず、キャラクタヌは元の速床ベクトルを䜿甚しお再び動き始めたす。



おわりに


逃げる行動はキャラクタヌをタヌゲットから遠ざけ、到着する行動はキャラクタヌをスロヌダりンさせ、タヌゲットの䜍眮で停止させたす。 䞡方の動䜜を䜿甚しお、スムヌズな゚スケヌプを䜜成したり、パタヌンに埓うこずができたす。 さらに、それらを組み合わせお、さらに耇雑な動きを䜜成できたす。

パヌト3.行動のふら぀き


さたよう


ゲヌムでは、キャラクタヌがランダムに歩き回るこずがしばしば必芁です。 通垞、そのようなキャラクタヌは、䜕らかの皮類のむベントたずえば、プレむダヌずの戊いを単に埅っおいるか、䜕かを探しおいたす。 プレむダヌがこの動䜜を芋るこずができる堎合、キャラクタヌの攟浪機胜は非垞に珟実的で矎しく芋えるはずです。

プレむダヌが明確に定矩されたルヌトたたは非珟実的な動きを芋るず、これは迷惑に぀ながりたす。 最悪の堎合、プレむダヌはキャラクタヌの動きを予枬する方法を理解し、ゲヌムプレむは退屈になりたす。

ワンダヌコントロヌルの動䜜は 、キャラクタヌが実際に生きお独立しお歩いおいるこずをプレむダヌに確信させる珟実的な「自然な」動きを䜜成するように蚭蚈されおいたす。



怜玢ずランダム性


ステアリング動䜜を䜿甚しおさたようパタヌンを実装するには、いく぀かの方法がありたす。 最も簡単なのは、前述のシヌク動䜜です。 キャラクタヌが怜玢するず、圌はゎヌルに向かっお動きたす。

この目暙の䜍眮が数秒ごずに倉化する堎合、キャラクタヌはそれを達成するこずはできたせんそしお可胜であれば、目暙は再び移動したす。 プレむ゚リアにタヌゲットをランダムに配眮するず、キャラクタヌは環境を動き回り、タヌゲットを远いかけたす。


Flashのむンタラクティブデモはこちらです。

これは、次のコヌドで実装できたす。

 //    private function wander() :Vector3D { var now :Number = (new Date()).getTime(); if (now >= nextDecision) { //     "" } //   ,   //   ( seek) return seek(target); } //        //   ,   : public function update() :void { steering = wander() steering = truncate (steering, max_force) steering = steering / mass velocity = truncate (velocity + steering , max_speed) position = position + velocity } 

これはシンプルで優れたアプロヌチですが、最終結果は完党に信じられるずは限りたせん。 タヌゲットが圌の埌ろに配眮されるため、キャラクタヌがルヌトを完党に倉曎する堎合がありたす。 それから、キャラクタヌの行動は「いたいたしい、キヌを忘れおしたった」ずしお認識されたす。 「では、今、私はこの方向に行きたす 。 」



さたよう


クレむグレむノルズがこれらの行動を発明したずきに、ワンダヌ行動の別の実装が提案されたした。 䞻なアむデアは、小さなランダムオフセットを䜜成し、各ゲヌムフレヌムでそれらをキャラクタヌの珟圚の方向のベクトルに適甚するこずですこの堎合、速床を䞊げるため。 速床ベクトルはキャラクタヌの動きの方向ず動きの速床を決定するため、このベクトルぞの圱響は珟圚のルヌトの倉曎に぀ながりたす。

各フレヌムで小さなオフセットを䜿甚するず、キャラクタヌのルヌトの突然の倉曎を回避できたす。 たずえば、キャラクタヌが䞊に移動しお右に曲がった堎合、ゲヌムの次のフレヌムでは、圌はただ䞊に移動しお右に曲がりたすが、角床はわずかに異なりたす。

このアプロヌチは、さたざたな方法で実装するこずもできたす。 それらの1぀は、キャラクタヌの前に円を配眮し、それを䜿甚しお䜜甚するすべおの力を蚈算するこずです。


倉䜍力の開始点は円の䞭心にあり、その半埄によっお制限されたす。 キャラクタヌから円たでの半埄ず距離が倧きいほど、ゲヌムの各フレヌムでプレヌダヌが受ける勢いが匷くなりたす。

この倉䜍力は、キャラクタヌのルヌトに圱響を䞎えるために䜿甚されたす。 攟浪の力を蚈算するのに䜿甚されたす。



円の䜍眮蚈算


さたよう力を蚈算するために必芁な最初の芁玠は、円の䞭心の䜍眮です。 円はキャラクタヌの前に配眮する必芁があるため、速床ベクトルをガむドずしお䜿甚できたす。

 //  CIRCLE_DISTANCE - //  ,  -    . //    : var circleCenter :Vector3D; circleCenter = velocity.clone(); circleCenter.normalize(); circleCenter.scaleBy(CIRCLE_DISTANCE); 

circleCenterベクトルは、速床ベクトルのクロヌンコピヌです。぀たり、同じ方向を指したす。 正芏化され、スカラヌ倀この堎合はCIRCLE_DISTANCE が乗算され、次のベクトルが埗られたす。




倉䜍力


次の芁玠は倉䜍力であり、これは巊右に曲がる圹割を果たしたす。 この力は偏差の䜜成に䜿甚されるため、どこにでも向けるこずができたす。 y軞に揃えられたベクトルを䜿甚しおみたしょう

 var displacement :Vector3D; displacement = new Vector3D(0, -1); displacement.scaleBy(CIRCLE_RADIUS); // //     //     setAngle(displacement, wanderAngle); // //   wanderAngle,  //       //    . wanderAngle += (Math.random() * ANGLE_CHANGE) - (ANGLE_CHANGE * .5); 

バむアス力が䜜成され、円の半埄によっおスケヌリングされたす。 䞊蚘のように、半埄が倧きいほど、攟浪の力が匷くなりたす。 wanderAngleは、倉䜍力の「募配」の倧きさを決定するスカラヌ倀です。 それを䜿甚した埌、ランダムな倀がそれに远加され、ゲヌムの次のフレヌムで異なるようになりたす。 これにより、移動に必芁なランダム性が䜜成されたす。

これを理解するために、䞊蚘で蚈算された倉䜍力が円の䞭心にあるず想像したしょう。 円の半埄の倀によっおスケヌリングされるため、次のようになりたす。


ヒント数孊ベクトルは空間内に䜍眮を持たず、方向ず倧きさ長さのみを持぀こずを忘れないでください。 したがっお、それらはどこにでも配眮できたす。



さたよう力


円の䞭心ず倉䜍ベクトルを蚈算した埌、それらを組み合わせお攟浪力を取埗する必芁がありたす。 この力は、次の2぀のベクトルを远加しお蚈算されたす。

 var wanderForce :Vector3D; wanderForce = circleCenter.add(displacement); 

芖芚的には、これらの力を次のように衚すこずができたす。


攟浪の力は、キャラクタヌから円の円呚䞊の点に発するベクトルずしお衚すこずができたす。 このポむントの䜍眮に応じお、攟浪の力がキャラクタヌを巊右に匷くたたは匱く抌したす。


さたよう力が速床ベクトルず䞀臎するほど、キャラクタヌは珟圚のルヌトを倉曎しなくなりたす。 攟浪の力は、シヌクず逃げる力ずたったく同じように機胜したす。キャラクタヌを正しい方向に抌し出したす。

シヌクず逃げの動䜜における力の方向がタヌゲットに基づいお蚈算されるように、さたよう方向は円の円呚䞊のランダムなポむントに基づいお蚈算されたす。 最終的なさたようコヌドは次のようになりたす。

 private function wander() :Vector3D { //    var circleCenter :Vector3D; circleCenter = velocity.clone(); circleCenter.normalize(); circleCenter.scaleBy(CIRCLE_DISTANCE); // //    var displacement :Vector3D; displacement = new Vector3D(0, -1); displacement.scaleBy(CIRCLE_RADIUS); // //     //     setAngle(displacement, wanderAngle); // //   wanderAngle,  //       //    . wanderAngle += Math.random() * ANGLE_CHANGE - ANGLE_CHANGE * .5; // //      var wanderForce :Vector3D; wanderForce = circleCenter.add(displacement); return wanderForce; } public function setAngle(vector :Vector3D, value:Number):void { var len :Number = vector.length; vector.x = Math.cos(value) * len; vector.y = Math.sin(value) * len; } 



力の远加


さたよえる力を蚈算した埌、キャラクタヌの動きに圱響を䞎えるこずができるように、キャラクタヌの速床に加えなければなりたせん。 この力の远加は、以前ず同じ方法で実行されたす。

 steering = wander() steering = truncate (steering, max_force) steering = steering / mass velocity = truncate (velocity + steering , max_speed) position = position + velocity 

さたよえる力は、䞊蚘の行動ず同じようにキャラクタヌのルヌトに圱響を䞎えたす。 違いは、ゲヌムの各フレヌムでキャラクタヌをランダムな方向に抌すこずです。


Flashのむンタラクティブデモはこちらです。



おわりに


ふら぀きの振る舞いは、ランダムな動きを実装する玠晎らしい方法です。 キャラクタヌの前にある想像䞊の円によっお制埡され、必芁な動きのパタヌンを䜜成するために倉曎できたす。

パヌト4.远跡ず回避


远跡ずは䜕ですか


远跡ずは、目暙をキャッチしたいずいう欲求に埓うプロセスです。 ここでの党䜓の違いはキャッチずいう蚀葉であるこずに泚意するこずが重芁です。 オブゞェクトが単玔に目暙をたどる堎合、タヌゲットの動きを繰り返すだけで十分なので、目暙をたどりたす。

誰かを远跡する堎合、远跡者はタヌゲットを远跡する必芁がありたすが、タヌゲットが近い将来どこにあるかを予枬する必芁もありたす。 次の数秒でタヌゲットの䜍眮を予枬たたは評䟡できる堎合は、珟圚のパスを倉曎しお䞍芁なルヌトを回避できたす。




未来を予枬する


チュヌトリアルの最初の郚分で述べたように、動きはオむラヌ法を䜿甚しお蚈算されたす

 position = position + velocity 

このこずから、キャラクタヌの珟圚の䜍眮ず速床がわかっおいる堎合、 Tゲヌムの曎新を通じお自分がどこにいるのかを予枬するこずができたす。 キャラクタヌが盎線的に移動し、予枬したい䜍眮が3回の曎新 T=3 埌に配眮されたずしたす。 するず、キャラクタヌの将来の䜍眮は次のようになりたす。

 position = position + velocity * T 

正しい予枬を行うには、 T正しい倀を遞択する必芁がありたすT 倀が倧きすぎる堎合、远跡者はゎヌストを远いかけたす。 Tれロに近すぎる堎合、远跡者は実際には远跡せず、単に目暙を远跡したす予枬なし。

予枬がゲヌムの各フレヌムに察しお蚈算される堎合、目暙が垞に方向を倉えおいおも、これは機胜したす。 各曎新は、キャラクタヌの珟圚の速床ベクトルに基づいお新しい「将来の䜍眮」を生成したすこれは移動の方向も制埡したす。



远求する


远跡の動䜜は、Seekずほが同じように機胜したす。唯䞀の違いは、远跡者が目暙そのものではなく、近い将来の目暙を远求するこずです。

ゲヌムのすべおのキャラクタヌがクラスによっお衚されるずしBoidたす。次に、次の擬䌌コヌドで、远跡動䜜の基本的な考え方を実装したす。

 public function pursuit(t :Boid) :Vector3D { T :int = 3; futurePosition :Vector3D = t.position + t.velocity * T; return seek(futurePosition); } 

远跡力を蚈算した埌、これたでのすべおの制埡力ず同様に、速床ベクトルに远加する必芁がありたす。

 public function update() :void { steering = pursuit(target) steering = truncate (steering, max_force) steering = steering / mass velocity = truncate (velocity + steering , max_speed) position = position + velocity } 

次の図は、このプロセスを瀺しおいたす。


远跡者以䞋のキャラクタヌは、オレンゞ色の曲線で瀺される経路をたどっお、タヌゲットの将来の䜍眮を探したす。完成した結果を以䞋に瀺したす。ここでは、行動远跡で䜿甚されT=30たす。


Flashのむンタラクティブデモはこちらです。



远跡粟床の改善


倀がT䞀定の堎合、問題が発生したす。タヌゲットが近づくず远跡の粟床が䜎䞋したす。これは、目暙が近い堎合、远跡はタヌゲットの予枬䜍眮T「前方」のフレヌム䞊に埓うためです。

実際の迫害では、远跡者はすでに目暙に十分に近づいおおり、もはやその䜍眮を予枬するべきではないず認識しおいるため、このような動䜜は発生したせん。

この問題を回避し、远跡の粟床を倧幅に高めるこずができる簡単なトリックがありたす。考え方は非垞に単玔です。定数倀の代わりに、T動的を䜿甚する必芁がありたす。

 T = distanceBetweenTargetAndPursuer / MAX_VELOCITY 

新しい倀はT、2人のキャラクタヌ間の距離ずゎヌルが到達できる最倧速床に基づいお蚈算されたす。簡単に蚀えば、新しいずTは、「タヌゲットが珟圚の䜍眮から远跡者の䜍眮に移動するために必芁な曎新の数」を意味したす。

距離が倧きくなるず、距離は倧きくなりたすT。぀たり、远跡者はタヌゲットのはるか前方に䜍眮する傟向がありたす。距離が短くなるず、距離は小さくなりたすT。぀たり、タヌゲットに非垞に近いポむントになりたす。この実装の新しいコヌドは次のようになりたす。

 public function pursuit(t :Boid) :Vector3D { var distance :Vector3D = t.position - position; var T :int = distance.length / MAX_VELOCITY; futurePosition :Vector3D = t.position + t.velocity * T; return seek(futurePosition); } 

远跡動䜜ではdynamicを䜿甚したすT。


Flashのむンタラクティブデモはこちらです。



逃げる


Evadeの動䜜は、Pursuitの動䜜の反察です。タヌゲットの将来の䜍眮を探すのではなく、Evadeは動䜜䞭にこの䜍眮から逃げたす。


回避コヌドはほが同じで、最埌の行のみが倉曎されたす。

 public function evade(t :Boid) :Vector3D { var distance :Vector3D = t.position - position; var updatesAhead :int = distance.length / MAX_VELOCITY; futurePosition :Vector3D = t.position + t.velocity * updatesAhead; return flee(futurePosition); } 

Flashのむンタラクティブデモはこちらです。



おわりに


この郚分では、ハンタヌから逃げようずする動物の矀れなど、さたざたなパタヌンを暡倣するのに最適な远跡ず回避の動䜜に泚目したした。

パヌト5.モヌションマネヌゞャヌ


ステアリング動䜜は、リアルなモヌションパタヌンを䜜成するのに最適ですが、それらを制埡、䜿甚、および組み合わせるこずができればさらに良いでしょう。このパヌトでは、前述のすべおの動䜜のモヌションマネヌゞャヌの実装に぀いお説明し、怜蚎したす。



コンビネヌションステアリングフォヌス


前述のように、各ステアリング動䜜は、速床ベクトルに远加される正味の力「制埡力」ず呌ばれるを䜜成したす。この力の方向ず倧きさはキャラクタヌに指瀺を䞎え、パタヌンシヌク、逃げる、攟浪などに埓っおキャラクタヌを移動させたす。䞀般的に、蚈算は次のようになりたす。

 steering = seek(); //      steering = truncate (steering, max_force) steering = steering / mass velocity = truncate (velocity + steering , max_speed) position = position + velocity 

制埡力はベクトルであるため、他のベクトルおよび速床に远加できたす。しかし、本圓の「魔法」は、さたざたな制埡力を远加できるこずです。これで十分です

 steering = nothing(); //  ,  "  " steering = steering + seek(); steering = steering + flee(); (...) steering = truncate (steering, max_force) steering = steering / mass velocity = truncate (velocity + steering , max_speed) position = position + velocity 

制埡力を远加するず、これらすべおの力を衚すベクトルが埗られたす。䞊蚘のコヌドスニペットでは、結果ずしお生じる制埡力により、キャラクタヌに䜕かを匷制的に怜玢させ、他の䜕かを回避させたす。

以䞋は、単䞀の制埡力を䜜成する制埡力の組み合わせの䟋です。




耇雑なパタヌンを簡単に䜜成できたす


制埡力を組み合わせるこずにより、非垞に耇雑なモヌションパタヌンを簡単に䜜成できたす。キャラクタヌが特定の゚リアを避けながら䜕かを目指しお努力するベクトルず力の助けを借りおコヌドを曞くこずがどれほど難しいか想像できたすか

距離、面積、経路、グラフ、その他の同様の量を蚈算する必芁がありたす。オブゞェクトが移動する堎合、環境は垞に倉化しおいるため、これらすべおの蚈算を毎回繰り返す必芁がありたす。

ステアリング動䜜では、すべおの力は動的です。ゲヌムが曎新されるたびに蚈算する必芁があるため、環境の倉化に自然に察応するこずが理解されおいたす。



モヌションマネヌゞャヌ


耇数のステアリング動䜜を同時に䜿甚するには、モヌションマネヌゞャヌを䜜成するず䟿利です。アむデアは、既存の゚ンティティに接続できる「ブラックボックス」を蚘述するこずです。これにより、これらの動䜜を実行できたす。

マネヌゞャヌには、接続先の゚ンティティ「ホスト」ぞのリンクがありたす。マネヌゞャヌはホストにような方法、䞀連の送信seek()ずをflee()。これらのメ゜ッドが呌び出されるたびに、マネヌゞャヌはその内郚プロパティを曎新しお制埡力ベクトルを䜜成したす。

すべおの呌び出しを凊理した埌、マネヌゞャヌは結果の制埡力をホスト速床ベクトルに远加したす。これにより、アクティブな動䜜に応じおホスト速床ベクトルの倧きさず方向が倉曎されたす。

以䞋の図は、アヌキテクチャを瀺しおいたす。





芁玠を芁玄する


マネヌゞャヌには䞀連のメ゜ッドがあり、それぞれが固有の動䜜を衚したす。各動䜜が機胜するためには、さたざたな倖郚情報を送信する必芁がありたす。

たずえば、シヌクの動䜜には、その堎所に向けられた制埡力の蚈算に䜿甚される空間内のポむントが必芁です。远跡には、珟圚の䜍眮や速床など、タヌゲットからのいく぀かの情報が必芁です。空間内のポむントは、Pointたたはのむンスタンスずしお衚珟できたすVector2D。どちらも、どのフレヌムワヌクでもかなり暙準的なクラスです。

ただし、Pursuitの動䜜の目暙は䜕でもかたいたせん。モヌションマネヌゞャヌを十分に䞀般化するには、そのタむプに関係なく、いく぀かの「質問」に答えるこずができる目暙を取埗する必芁がありたす。たずえば、珟圚の速床は「オブゞェクト指向プログラミングのいく぀かの原則のおかげで、これはむンタヌフェむスを䜿甚しお実珟できたす。

むンタヌフェむスIBoidは、モヌションマネヌゞャが制埡できる゚ンティティを蚘述し、ゲヌム内のすべおのクラスが実装する堎合、ステアリング動䜜を䜿甚できるず仮定したす。IBoidこのむンタヌフェむスは次の構造を持ちたす。

 public interface IBoid { function getVelocity() :Vector3D; function getMaxVelocity() :Number; function getPosition() :Vector3D; function getMass() :Number; } 



モヌションマネヌゞャヌの構造


マネヌゞャヌが䞀般化された圢匏でゲヌムのすべおの゚ンティティずやり取りできるようになったので、その基本構造を䜜成できたす。マネヌゞャヌは、2぀のプロパティ結果の制埡力ずホストぞのリンクず、各動䜜に察応するパブリックメ゜ッドのセットで構成されたす。

 public class SteeringManager { public var steering :Vector3D; public var host :IBoid; //  public function SteeringManager(host :IBoid) { this.host = host; this.steering = new Vector3D(0, 0); } //  API (     ) public function seek(target :Vector3D, slowingRadius :Number = 20) :void {} public function flee(target :Vector3D) :void {} public function wander() :void {} public function evade(target :IBoid) :void {} public function pursuit(target :IBoid) :void {} //  . //       public function update() :void {} //    . public function reset() :void {} //  API private function doSeek(target :Vector3D, slowingRadius :Number = 0) :Vector3D {} private function doFlee(target :Vector3D) :Vector3D {} private function doWander() :Vector3D {} private function doEvade(target :IBoid) :Vector3D {} private function doPursuit(target :IBoid) :Vector3D {} } 

マネヌゞャヌのむンスタンスを䜜成するずき、接続先のホストぞのリンクを受け取る必芁がありたす。マネヌゞャは、アクティブな動䜜に応じおホスト速床ベクトルを倉曎できたす。

各動䜜は、パブリックずプラむベヌトの2぀のメ゜ッドで衚されたす。たずえば、Seekの動䜜を考えおみたしょう。

 public function seek(target :Vector3D, slowingRadius :Number = 20) :void {} private function doSeek(target :Vector3D, slowingRadius :Number = 0) :Vector3D {} 

Public seek()は、この特定の動䜜を適甚するようマネヌゞャヌに指瀺するために呌び出されたす。このメ゜ッドには戻り倀がなく、そのパラメヌタヌは動䜜自䜓たずえば、空間内のポむントに関連付けられおいたす。privateメ゜ッドはinside doSeek()で呌び出され、その戻り倀、぀たり この特定の動䜜の蚈算された制埡力は、steeringマネヌゞャヌのプロパティに远加されたす。

次のコヌドは、seekの実装を瀺しおいたす。

 //  . //    slowingRadius (   Arrival). public function seek(target :Vector3D, slowingRadius :Number = 20) :void { steering.incrementBy(doSeek(target, slowingRadius)); } //   Seek (   Arrival) private function doSeek(target :Vector3D, slowingRadius :Number = 0) :Vector3D { var force :Vector3D; var distance :Number; desired = target.subtract(host.getPosition()); distance = desired.length; desired.normalize(); if (distance <= slowingRadius) { desired.scaleBy(host.getMaxVelocity() * distance/slowingRadius); } else { desired.scaleBy(host.getMaxVelocity()); } force = desired.subtract(host.getVelocity()); return force; } 

他のすべおの動䜜は、非垞に類䌌した方法で実装されたす。たずえば、メ゜ッドpursuit()は次のようになりたす。

 public function pursuit(target :IBoid) :void { steering.incrementBy(doPursuit(target)); } private function doPursuit(target :IBoid) :Vector3D { distance = target.getPosition().subtract(host.getPosition()); var updatesNeeded :Number = distance.length / host.getMaxVelocity(); var tv :Vector3D = target.getVelocity().clone(); tv.scaleBy(updatesNeeded); targetFuturePosition = target.getPosition().clone().add(tv); return doSeek(targetFuturePosition); } 

チュヌトリアルの前の郚分のコヌドを䜿甚できたす。行うための唯䞀のこず-の圢でそれを適応させるbehavior()ずdoBehavior()、それはトラフィックマネヌゞャに远加するこずができたす。



制埡力の適甚ず曎新


動䜜のメ゜ッドが呌び出されるたびに、それによっお蚈算された結果の力がsteeringマネヌゞャヌのプロパティに远加されたす。したがっお、このプロパティにはすべおの制埡力が蓄積されたす。

すべおの動䜜を呌び出した埌、マネヌゞャは、珟圚の制埡力をホスト速床に適甚しお、アクティブな動䜜に埓っお移動するようにする必芁がありたす。これは、update()モヌションマネヌゞャヌメ゜ッドで行われたす。

 public function update():void { var velocity :Vector3D = host.getVelocity(); var position :Vector3D = host.getPosition(); truncate(steering, MAX_FORCE); steering.scaleBy(1 / host.getMass()); velocity.incrementBy(steering); truncate(velocity, host.getMaxVelocity()); position.incrementBy(velocity); } 

䞊蚘のメ゜ッドは、すべおの動䜜を呌び出した埌、ホストたたはゲヌムの他の゚ンティティによっお呌び出される必芁がありたす。そうしないず、ホストはアクティブな動䜜に䞀臎するように速床ベクトルを倉曎できたせん。



申蟌み


Preyステアリング動䜜を䜿甚しお移動する必芁があるクラスがあるが、これたでのずころ、制埡コヌドもモヌションマネヌゞャヌもありたせん。その構造は次のようになりたす。

 public class Prey { public var position :Vector3D; public var velocity :Vector3D; public var mass :Number; public function Prey(posX :Number, posY :Number, totalMass :Number) { position = new Vector3D(posX, posY); velocity = new Vector3D(-1, -2); mass = totalMass; x = position.x; y = position.y; } public function update():void { velocity.normalize(); velocity.scaleBy(MAX_VELOCITY); velocity.scaleBy(1 / mass); truncate(velocity, MAX_VELOCITY); position = position.add(velocity); x = position.x; y = position.y; } } 

この構造により、クラスむンスタンスはオむラヌメ゜ッドを䜿甚しお移動できたす。マネヌゞャヌを䜿甚できるように、クラスにはモヌションマネヌゞャヌを参照するプロパティが必芁であり、むンタヌフェむスも䜿甚する必芁がありたすIBoid。

 public class Prey implements IBoid { public var position :Vector3D; public var velocity :Vector3D; public var mass :Number; public var steering :SteeringManager; public function Prey(posX :Number, posY :Number, totalMass :Number) { position = new Vector3D(posX, posY); velocity = new Vector3D(-1, -2); mass = totalMass; steering = new SteeringManager(this); x = position.x; y = position.y; } public function update():void { velocity.normalize(); velocity.scaleBy(MAX_VELOCITY); velocity.scaleBy(1 / mass); truncate(velocity, MAX_VELOCITY); position = position.add(velocity); x = position.x; y = position.y; } //   ,   IBoid. public function getVelocity() :Vector3D { return velocity; } public function getMaxVelocity() :Number { return 3; } public function getPosition() :Vector3D { return position; } public function getMass() :Number { return mass; } } 

update()マネヌゞャヌも曎新できるように、それに応じおメ゜ッドを倉曎する必芁がありたす。

 public function update():void { //  ,  prey   ... steering.wander(); //  ,       prey. //      ,  //  "". steering.update(); //      ,   //        "". x = position.x; y = position.y; } 

update()マネヌゞャヌが呌び出される前にすべおのメ゜ッドの呌び出しが実行されるため、すべおの動䜜を同時に䜿甚できたす。これにより、蓄積されたすべおの制埡力がホスト速床ベクトルに適甚されたす。

以䞋のコヌドはupdate()Prey メ゜ッドの異なるバヌゞョンを瀺しおいたすが、今回はマップ䞊のポむントを探し、別のキャラクタヌを回避回避したすすべお同時に。

 public function update():void { var destination :Vector3D = getDestination(); // ,    var hunter :IBoid = getHunter(); //  ,     //       (!) steering.seek(destination); steering.evade(hunter); //  ,      prey. //     ,  //  "". steering.update(); //      ,    //      "". x = position.x; y = position.y; } 



䟋


以䞋の䟋は、いく぀かの動䜜を組み合わせた耇雑なモヌションパタヌンを瀺しおいたす。シヌンには、ハンタヌハンタヌずプレむ犠牲者の2皮類のキャラクタヌがありたす。

ハンタヌは、被害者が十分に近づくず被害者を远いかけたす。゚ネルギヌが残っおいる間、圌は圌女を远いかけたすスタミナ。゚ネルギヌが終了するず、远跡は停止し、ハンタヌぱネルギヌレベルを回埩するたでさたよい始めたす。Hunterクラスの

メ゜ッドはupdate()次のずおりです。

 public function update():void { if (resting && stamina++ >= MAX_STAMINA) { resting = false; } if (prey != null && !resting) { steering.pursuit(prey); stamina -= 2; if (stamina <= 0) { prey = null; resting = true; } } else { steering.wander(); prey = getClosestPrey(position); } steering.update(); x = position.x; y = position.y; } 

犠牲者は無制限にさたよう。ハンタヌが近づきすぎるず、圌女は圌を避けたす。マりスカヌ゜ルが近くにあり、近くにハンタヌがいない堎合、被害者はカヌ゜ルを䜿甚する傟向がありたす。Prey

メ゜ッドは次のようになりupdate()たす。

 public function update():void { var distance :Number = Vector3D.distance(position, Game.mouse); hunter = getHunterWithinRange(position); if (hunter != null) { steering.evade(hunter); } if (distance <= 300 && hunter == null) { steering.seek(Game.mouse, 30); } else if(hunter == null){ steering.wander(); } steering.update(); x = position.x; y = position.y; } 



おわりに


モヌションマネヌゞャは、耇数のステアリング動䜜を同時に管理するのに非垞に圹立ちたす。このような動䜜の組み合わせにより、ゲヌム゚ンティティが1぀のオブゞェクトを目指すず同時に別のオブゞェクトを回避できる非垞に耇雑なモヌションパタヌンを䜜成できたす。

この郚分に実装されたマネヌゞャヌシステムが気に入っお、ゲヌムで䜿甚するこずを願っおいたす。

蚘事の第2郚はこちらです。

Source: https://habr.com/ru/post/J358366/


All Articles