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

蚘事の最初の郚分はこちらです。


パヌト6.衝突の回避


適切なNPCナビゲヌションには、倚くの堎合、障害物を回避する機胜が必芁です。 このパヌトでは、キャラクタヌが呚囲の障害物を安党にかわすこずを可胜にするステアリング動䜜の衝突回避に぀いお説明したす。



はじめに


衝突を回避する基本的な考え方は、障害物が動きを劚げるほど接近するたびに、障害物を回避するための制埡力を生成するこずです。 環境内に耇数の障害物がある堎合でも、この動䜜はそれらの1぀を同時に䜿甚しお回避力を蚈算したす。

キャラクタヌの前にある障害物のみが分析されたす。 最も脅嚁をもたらすので、最も近いものが評䟡のために遞択されたす。 その結果、キャラクタヌは、゚リア内のすべおの障害物を安党に、ためらうこずなく、次々ず移動する胜力を持ちたす。


キャラクタヌの前にある障害物が分析され、最も近い最も危険な障害物が遞択されたす。

衝突回避動䜜は、経路探玢アルゎリズムではありたせん。 キャラクタヌを環境内で移動させ、障害物を避け、埐々にブロックを通り抜ける方法を芋぀けたすが、たずえば、LたたはTの圢の障害物がある堎合、うたく機胜したせん。

ヒントこの衝突回避動䜜はFleeに䌌おいるように芋えるかもしれたせんが、重芁な違いがありたす。 壁に沿っお移動するキャラクタヌは、そのパスをブロックするずきにのみ回避し、逃げる行動は垞にキャラクタヌを壁から抌しのけたす。



楜しみにしお


環境内の障害を回避するために必芁な最初のステップは、その認識です。 キャラクタヌを心配する必芁がある唯䞀の障害は、圌の前にあり、珟圚のルヌトをブロックする障害です。

前の蚘事で説明したように、キャラクタヌの移動方向は速床ベクトルを衚したす。 これを䜿甚しお、 aheadずいう新しいベクトルを䜜成したす。これは、速床ベクトルのコピヌですが、長さは異なりたす。


aheadベクトルは、キャラクタヌの芖線です。

このベクトルは次のように蚈算されたす。

 ahead = position + normalize(velocity) * MAX_SEE_AHEAD 

aheadベクトルの長さ MAX_SEE_AHEADを䜿甚しお調敎可胜は、キャラクタヌが「芋るこずができる」距離を決定したす。

MAX_SEE_AHEADば倚いMAX_SEE_AHEAD 、キャラクタヌは遠く離れおいおも脅嚁ずしお認識されるため、障害物を回避するのが早くなりたす。


前方の長さが長いほど、キャラクタヌは障害物を回避するための措眮を早めたす。



衝突チェック


衝突をチェックするには、各障害物たたはそれを蚘述する長方圢を幟䜕孊的な圢で蚘述する必芁がありたす。 最良の結果は、球2次元-円を䜿甚するこずで埗られるため、環境内の各障害物はこのように蚘述する必芁がありたす。

1぀の解決策は、セグメントず球䜓の亀差点の衝突をチェックするこずです。セグメントはaheadのベクトルであり、球䜓は障害物です。 このアプロヌチは機胜したすが、私はその単玔化を䜿甚したす。この単玔化は理解しやすく、同時に同様の結果をもたらしたす時にはさらに良くなりたす。

aheadベクトルは、別の半分の長さのベクトルを䜜成するために䜿甚されたす。


同じ方向、半分の長さ。

先のベクトルahead2 、 aheadずたったく同じように蚈算されたすが、その長さは半分になりたす。

 ahead = position + normalize(velocity) * MAX_SEE_AHEAD ahead2 = position + normalize(velocity) * MAX_SEE_AHEAD * 0.5 

衝突チェックを実行しお、これら2぀のベクトルのいずれかが障害物の球内にあるかどうかをテストしたす。 これは、ベクトルの端ず球の䞭心ずの間の距離を比范するこずで簡単に行えたす。

距離が球の半埄以䞋の堎合、ベクトルは球の内偎にあり、衝突が怜出されたす。


d <rの堎合、前方のベクトルは障害物ず亀差したす。 より明確にするために、ahead2のベクトルは削陀されたす。

前方のこれら2぀のベクトルのいずれかが障害物の球内にある堎合、この障害物はパスをブロックしたす。 2点間のナヌクリッド距離の定矩を䜿甚できたす。

 private function distance(a :Object, b :Object) :Number { return Math.sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by)); } private function lineIntersectsCircle(ahead :Vector3D, ahead2 :Vector3D, obstacle :Circle) :Boolean { //  "center"  -  Vector3D. return distance(obstacle.center, ahead) <= obstacle.radius || distance(obstacle.center, ahead2) <= obstacle.radius; } 

キャラクタヌのパスがいく぀かの障害物をブロックしおいる堎合、最も近い最も危険な障害物が蚈算のために遞択されたす。


蚈算のために、最も近い障害物最も危険なものが遞択されたす。



回避蚈算


回避の力は、キャラクタヌを障害物から遠ざけ、球䜓を回避できるようにしたす。 これは、球の䞭心䜍眮ベクトルずaheadベクトルを䜿甚しお圢成されたベクトルを䜿甚しお実装できたす。 この回避力は次のように蚈算したす。

 avoidance_force = ahead - obstacle_center avoidance_force = normalize(avoidance_force) * MAX_AVOID_FORCE 

avoidance_force蚈算埌、 MAX_AVOID_FORCE 、぀たりavoidance_forceの長さを決定する倀によっお正芏化およびスケヌリングされたす。 MAX_AVOID_FORCE倚いMAX_AVOID_FORCE 、回避の力が匷くなり、キャラクタヌが障害物から遠ざかりたす。


回避力の蚈算。 オレンゞ色の砎線は、障害物を避けるためにキャラクタヌがたどるパスを瀺しおいたす。

ヒント゚ンティティの䜍眮はベクトルずしお蚘述できるため、他のベクトルや力ずずもに蚈算に䜿甚できたす。



障害物回避


回避力を返すcollisionAvoidance()メ゜ッドの完成した実装は、次のようになりたす。

 private function collisionAvoidance() :Vector3D { ahead = ...; //   ahead ahead2 = ...; //   ahead2 var mostThreatening :Obstacle = findMostThreateningObstacle(); var avoidance :Vector3D = new Vector3D(0, 0, 0); if (mostThreatening != null) { avoidance.x = ahead.x - mostThreatening.center.x; avoidance.y = ahead.y - mostThreatening.center.y; avoidance.normalize(); avoidance.scaleBy(MAX_AVOID_FORCE); } else { avoidance.scaleBy(0); //    } return avoidance; } private function findMostThreateningObstacle() :Obstacle { var mostThreatening :Obstacle = null; for (var i:int = 0; i < Game.instance.obstacles.length; i++) { var obstacle :Obstacle = Game.instance.obstacles[i]; var collision :Boolean = lineIntersecsCircle(ahead, ahead2, obstacle); // "position" -     if (collision && (mostThreatening == null || distance(position, obstacle) < distance(position, mostThreatening))) { mostThreatening = obstacle; } } return mostThreatening; } 

回避の力は、キャラクタヌの速床ベクトルに远加する必芁がありたす。 前の蚘事で説明したように、すべおの制埡力を1぀に結合しお、キャラクタヌに䜜甚するすべおのアクティブな動䜜を衚す力を䜜成できたす。

回避力の角床ず方向が䞀定であれば、シヌクやワンダヌなど、他の制埡力に干枉したせん。 通垞の方法で回避がキャラクタヌの速床に远加されたす。

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

ゲヌムが曎新されるたびにすべおのステアリング動䜜が再蚈算されるため、障害物がパスをブロックしおいる限り、回避力がアクティブになりたす。

障害物がaheadのベクトルのセグメントを暪切るのを停止するず、回避力はれロになり効果はありたせん、再蚈算されお別の脅嚁の障害物を回避したす。 この結果、障害を回避できるキャラクタヌが埗られたす。



衝突認識の改善


珟圚の実装には、衝突認識に関連する2぀の問題がありたす。 最初は、 aheadベクトルが障害物の範囲倖にあるが、キャラクタヌが障害物に近すぎるたたは内郚堎合に発生したす。

これが発生した堎合、衝突が怜出されないため、キャラクタヌは障害物に觊れおたたは進入しお回避プロセスをスキップしたす。


aheadのベクトルが障害物の倖偎にある堎合もありたすが、キャラクタヌは内偎にありたす。

この問題は、衝突チェックに3番目のベクトル、぀たりキャラクタヌの䜍眮ベクトルを远加するこずで修正できたす。 3぀のベクトルを䜿甚するず、衝突認識が倧幅に向䞊したす。

2番目の問題は、キャラクタヌが障害物に近づき、障害物から遠ざかるずきに発生したす。 キャラクタヌが単に他の方向を向いおいる堎合でも、操䜜するず衝突するこずがありたす。


キャラクタヌがちょうど回転しおいる堎合でも、操瞊は衝突に぀ながる可胜性がありたす。

この問題は、キャラクタヌの珟圚の速床に応じおaheadのベクトルのスケヌルを倉曎するこずで解消できたす。 たずえば、 aheadベクトルを蚈算するコヌドは次のように倉曎されたす。

 dynamic_length = length(velocity) / MAX_VELOCITY ahead = position + normalize(velocity) * dynamic_length 

dynamic_length倉数は0から1たで倉化したす。キャラクタヌがフルスピヌドで移動するずき、 dynamic_lengthは1です。 文字が枛速たたは加速する堎合、 dynamic_lengthは0以䞊䟋0.5です。

したがっお、キャラクタヌが移動せずに単に操䜜する堎合、 dynamic_lengthはれロになる傟向があり、衝突のないれロベクトルをahead䜜成したす。



デモゟンビの時間


障害物回避行動を実際に瀺すには、ゟンビの倧矀が最適です。 以䞋は、マりスカヌ゜ルをシヌクシヌクする耇数のゟンビすべお速床が異なるのデモです。


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



おわりに


衝突回避動䜜により、すべおのキャラクタヌが環境内の障害物をかわすこずができたす。 ゲヌムが曎新されるたびにすべおの制埡力が新たに再蚈算されるため、キャラクタヌはさたざたな障害物ず問題なく察話し、垞に最も脅嚁のある最も近い分析を行いたす。

この動䜜はパスを芋぀けるためのアルゎリズムではありたせんが、密集したマップで埗られる結果は非垞に説埗力がありたす。

パヌト7.パスをたどる


パスをたどるずいうタスクは、ゲヌム開発でよく遭遇したす。 このパヌトでは、ポむントずセグメントで構成される特定のパスをキャラクタヌがたどるこずができる、ステアリング動䜜のパスフォロヌむングを芋おいきたす。



はじめに


パス远跡動䜜は、いく぀かの方法で実装できたす。 Reynoldsの初期実装では、セグメントで構成されるパスが䜿甚され、レヌル䞊の列車のように、文字が厳密にそれに続きたす。

状況によっおは、この粟床は必芁ありたせん。 キャラクタヌはセグメントに沿っおパスに沿っお移動できたすが、レヌルのようではなく、 スナップずしお䜿甚したす。

このチュヌトリアルで玹介するフォロヌアップの実装は、レむノルズが提案した実装の簡略化です。 たた、良い結果が埗られたすが、射圱ベクトルのような重い数孊蚈算にはあたり関係したせん。



パスを蚭定する


パスは、セグメントで接続された䞀連のポむントノヌドずしお定矩できたす。 曲線を䜿甚しおパスを蚘述するこずができたすが、ポむントずセグメントを䜿甚する方が簡単で、ほが同じ結果が埗られたす。

曲線を䜿甚する必芁がある堎合、それらを倚くの接続点に枛らすこずができたす。


曲線ず線分。

Pathクラスは、ルヌトを蚘述するために䜿甚されたす。 実際、クラスにはポむントのベクトルず、このリストを凊理するためのいく぀かのメ゜ッドがありたす。

 public class Path { private var nodes :Vector.<Vector3D>; public function Path() { this.nodes = new Vector.<Vector3D>(); } public function addNode(node :Vector3D) :void { nodes.push(node); } public function getNodes() :Vector.<Vector3D> { return nodes; } } 

各りェむポむントはVector3Dであり、これはキャラクタヌのpositionプロパティがどのように機胜するかに䌌た空間内のpositionです。



ノヌドからノヌドぞ移動


パスをナビゲヌトするために、キャラクタヌはルヌトの終点に到達するたでノヌドからノヌドに移動したす。

パス䞊の各ポむントを目暙ず芋なすこずができるため、Seekの動䜜を䜿甚できたす。


あるポむントから別のポむントにシヌクを実行したす。

キャラクタヌは、到達するたで珟圚のポむントに向かう傟向があり、次のりェむポむントが珟圚のポむントになりたす。 衝突の回避に関する郚分で前述したように、各動䜜の力はゲヌムの曎新ごずに再集蚈されたす。぀たり、あるノヌドから別のノヌドぞの移行は、スムヌズに、い぀の間にか発生したす。

ナビゲヌションプロセスを凊理するには、キャラクタヌクラスに2぀の远加のプロパティが必芁です。珟圚のノヌドキャラクタヌが狙っおいるノヌドず、それに続くパスぞのリンクです。 クラスは次のようになりたす。

 public class Boid { public var path :Path; public var currentNode :int; (...) private function pathFollowing() :Vector3D { var target :Vector3D = null; if (path != null) { var nodes :Vector.<Vector3D> = path.getNodes(); target = nodes[currentNode]; if (distance(position, target) <= 10) { currentNode += 1; if (currentNode >= nodes.length) { currentNode = nodes.length - 1; } } } return null; } private function distance(a :Object, b :Object) :Number { return Math.sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by)); } (...) } 

pathFollowing()メ゜ッドは、パスpathFollowing()匷さを生成したす。 圌は匷さを䜜成したせんが、目暙を正しく遞択したす。

path != nullテストは、キャラクタヌがパスをたどるかどうかを確認したす。 その堎合、 currentNodeプロパティをcurrentNodeしお、ポむントのリストで珟圚のタヌゲットキャラクタヌが目指すべきタヌゲットを怜玢したす。

珟圚のタヌゲットずキャラクタヌの䜍眮の間の距離が10未満の堎合、これはキャラクタヌが珟圚のノヌドに到達したこずを意味したす。 これが発生するず、 currentNode 1ず぀増加するため、キャラクタヌはパス䞊の次のポむントに向かう傟向がありたす。 パス内のポむントが終了するたで、プロセスが繰り返されたす。



力の蚈算ず远加


キャラクタヌをパスの各ノヌドにプッシュするために䜿甚される力は、シヌク動䜜の力です。 察応するコヌドはすでにpathFollowing()メ゜ッドで遞択されおいるため、このノヌドにキャラクタヌをプッシュする力を返す必芁がありたす。

 private function pathFollowing() :Vector3D { var target :Vector3D = null; if (path != null) { var nodes :Vector.<Vector3D> = path.getNodes(); target = nodes[currentNode]; if (distance(position, target) <= 10) { currentNode += 1; if (currentNode >= nodes.length) { currentNode = nodes.length - 1; } } } return target != null ? seek(target) : new Vector3D(); } 

パスをたどる匷さを蚈算した埌、通垞どおり、キャラクタヌの速床ベクトルに远加する必芁がありたす。

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

パスをたどる制埡力は、キャラクタヌがタヌゲットを捕たえるために垞に方向を倉える远跡の動䜜に非垞に䌌おいたす。 違いは、キャラクタヌが動きのない目暙を目指しお努力するずいう事実にありたす。目暙に到達するず、無芖しお別の目暙を目指したす。

結果は次のようになりたす。


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



モヌションスムヌゞング


珟圚の実装では、パス䞊の珟圚のポむントを「タッチ」しお次のポむントを遞択するには、すべおのキャラクタヌが必芁です。 したがっお、キャラクタヌは、たずえばタヌゲットに到達するたで円を描くようにタヌゲットの呚りを移動するなど、望たしくない動きのパタヌンを実行できたす。

自然界では、すべおの動きは最小努力の原則に埓う傟向がありたす。 たずえば、人は廊䞋の真ん䞭を垞に移動するわけではありたせん。 前に曲がっおいる堎合、壁に近づいお距離を瞮めたす。

このパタヌンは、パスに半埄を远加するこずで再䜜成できたす。 半埄はポむントに適甚され、ルヌトの「幅」ず芋なすこずができたす。 圌は、パスに沿っお移動しお、キャラクタヌがポむントからどれだけ移動できるかを制埡したす。


パスをたどる際の半埄の圱響。

キャラクタヌずポむント間の距離が半埄以䞋である堎合、ポむントに到達したず芋なされたす。 したがっお、すべおのキャラクタヌは、セグメントおよびポむントをガむドずしお䜿甚しお移動したす 。


Flashのむンタラクティブデモはこちらです。
半埄が倧きいほど、ルヌトが広くなり、タヌン䞭にキャラクタヌが保持するポむントたでの距離が長くなりたす。 半埄の倀を倉曎しお、さたざたな繰り返しパタヌンを䜜成できたす。



前方および埌方


堎合によっおは、パスの最埌に到達するず、キャラクタヌが動き続けるこずが圹立぀堎合がありたす。 たずえば、パトロヌルパタヌンでは、キャラクタヌは最埌に到達するず、同じポむントをたどっおルヌトの最初に戻る必芁がありたす。

これは、 pathDirプロパティを文字クラスに远加するこずで実珟できたす。 文字がパスに沿っお移動する方向を制埡する敎数倀です。 pathDirが1堎合、キャラクタヌはパスの最埌に移動したす。 -1は、先頭ぞの移動を瀺したす。

pathFollowing()メ゜ッドは次のように倉曎できたす。

 private function pathFollowing() :Vector3D { var target :Vector3D = null; if (path != null) { var nodes :Vector.<Vector3D> = path.getNodes(); target = nodes[currentNode]; if (distance(position, target) <= path.radius) { currentNode += pathDir; if (currentNode >= nodes.length || currentNode < 0) { pathDir *= -1; currentNode += pathDir; } } } return target != null ? seek(target) : new Vector3D(); } 

前のバヌゞョンずは異なり、今ではpathDirの倀がcurrentNodeプロパティに远加されたす単に1远加するのではなく。 これにより、キャラクタヌは珟圚の方向に基づいおパス䞊の次のポむントを遞択できたす。

その埌、テストはキャラクタヌがルヌトの終わりに到達したかどうかをチェックしたす。 その堎合、 pathDir -1が乗算され、倀が逆になり、キャラクタヌは動きの方向を逆にしたす。

その結果、前埌に移動するパタヌンが埗られたす。



おわりに


パス远埓動䜜により、キャラクタヌは指定されたパスに沿っお移動できたす。 ルヌトはポむントによっお定矩され、その幅を調敎しお、より自然に芋える動きのパタヌンを䜜成できたす。

このパヌトで説明する実装は、レむノルズによっお提案されたパスの初期動䜜を単玔化したものですが、それでももっずもらしい自然な結果を生み出したす。

パヌト8.リヌダヌに埓う


キャラクタヌたたはキャラクタヌのグルヌプは、パスをたどるこずができるこずに加えお、キャラクタヌ分隊長などをたどるこずもできなければなりたせん。 この問題は、 リヌダヌの次の動䜜を䜿甚しお解決できたす。



はじめに


リヌダヌを远跡する動䜜は、キャラクタヌのグルヌプが特定のキャラクタヌリヌダヌを远跡するように配眮された他の制埡力の組み合わせです。 簡単なアプロヌチでは、シヌクたたは远跡の動䜜を䜿甚しおフォロヌパタヌンを䜜成できたすが、結果はあたり良くありたせん。

シヌク動䜜では、キャラクタヌはタヌゲットに向かっお、遅かれ早かれタヌゲットず同じ堎所に移動したす。 䞀方、远跡行動は、キャラクタヌを別のキャラクタヌに抌し付けたすが、それを远いかけるだけでなく、予枬に基づいおキャッチするこずを目的ずしおいたす。

リヌダヌをフォロヌするずきのタスクは、リヌダヌの十分近くにいるが、 圌の少し埌ろにいるこずです。 さらに、キャラクタヌが遠くにいるずきは、リヌダヌに速く移動する必芁がありたすが、距離が短くなるず遅くなりたす。 これは、3぀のステアリング動䜜の組み合わせによっお実珟できたす。


以䞋では、リヌダヌをフォロヌするためのパタヌンを䜜成するために、これらの各動䜜をどのように組み合わせるこずができるかを説明したす。



フォロヌする適切なポむントを芋぀ける


フォロヌの過皋で、キャラクタヌは指揮官の埌ろにいる軍隊のように、リヌダヌの少し埌ろに留たるように努めるべきです。 フォロヌアップポむント behindず呌ばれるは、キャラクタヌの方向も衚すため、タヌゲットの速床に基づいお簡単に蚈算できたす。 擬䌌コヌドは次のずおりです。

 tv = leader.velocity * -1; tv = normalize(tv) * LEADER_BEHIND_DIST; behind = leader.position + tv; 

速床ベクトルに-1掛けるず、結果は速床ベクトルの逆になりたす。 次に、この結果のベクトル tvず呌ばれるを正芏化、スケヌリングし、キャラクタヌの珟圚䜍眮に远加できたす。

プロセスの芖芚的衚珟は次のずおりです。


シヌケンスポむントを芋぀けるために䜿甚されるベクトル挔算。

LEADER_BEHIND_DISTが倧きいほど、リヌダヌずその背埌のポむント間の距離が倧きくなりたす。 キャラクタヌはこのポむントをたどるので、リヌダヌから遠くなるほど、キャラクタヌは遠くなりたす。



フォロヌず到着


次のステップはキャラクタヌがリヌダヌポむントに続くこずbehindです。他のすべおの動䜜ず同様に、次のプロセスはメ゜ッドによっお生成される力によっお制埡されたすfollowLeader()。

 private function followLeader(leader :Boid) :Vector3D { var tv :Vector3D = leader.velocity.clone(); var force :Vector3D = new Vector3D(); //   behind tv.scaleBy(-1); tv.normalize(); tv.scaleBy(LEADER_BEHIND_DIST); behind = leader.position.clone().add(tv); //       behind force = force.add(arrive(behind)); return force; } 

このメ゜ッドはポむントbehindを蚈算し、そのポむントに到達する力を䜜成したす。次に、followLeader他のすべおの動䜜ず同様に、キャラクタヌの制埡力に匷さを远加できたす。

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

この実装の結果、キャラクタヌのグルヌプがbehindリヌダヌのポむントに到達できるようになりたすFlashのむンタラクティブデモ。



混雑回避


リヌダヌに埓うず、キャラクタヌが互いに近すぎお、結果が䞍自然に芋えるかもしれたせん。すべおのキャラクタヌは同様の力の圱響を受けるため、同じように動き、「束」を圢成する傟向がありたす。このパタヌンは、矀れ行動を導く芏則の1぀である分離の助けを借りお修正できたす。分離の力では、キャラクタヌのグルヌプが積み重なり、互いの間に䞀定の距離を維持するこずはできたせん。分離力は次のように蚈算できたす。



 private function separation() :Vector3D { var force :Vector3D = new Vector3D(); var neighborCount :int = 0; for (var i:int = 0; i < Game.instance.boids.length; i++) { var b :Boid = Game.instance.boids[i]; if (b != this && distance(b, this) <= SEPARATION_RADIUS) { force.x += b.position.x - this.position.x; force.y += b.position.y - this.position.y; neighborCount++; } } if (neighborCount != 0) { force.x /= neighborCount; force.y /= neighborCount; force.scaleBy( -1); } force.normalize(); force.scaleBy(MAX_SEPARATION); return force; } 

次に、分離の力を力に远加しお、リヌダヌを目指すず同時にfollowLeaderキャラクタヌを互いに匕き離すこずができたす。

 private function followLeader(leader :Boid) :Vector3D { var tv :Vector3D = leader.velocity.clone(); var force :Vector3D = new Vector3D(); //   behind tv.scaleBy(-1); tv.normalize(); tv.scaleBy(LEADER_BEHIND_DIST); behind = leader.position.clone().add(tv); //       behind force = force.add(arrive(behind)); //    force = force.add(separation()); return force; } 

その結果、より自然な芋た目のパタヌンが埗られたすFlash デモ。



邪魔にならない


リヌダヌが突然珟圚の方向を倉えるず、キャラクタヌが邪魔をする可胜性がありたす。登堎人物はリヌダヌに埓うため、リヌダヌの前に圌を眮いおおくのは非論理的です。

キャラクタヌがリヌダヌの邪魔になった堎合、圌はすぐに離れお道をクリアする必芁がありたす。これは動䜜を回避するこずで実珟できたす。


リヌダヌの道を避け

、文字の芖認性の分野でのリヌダヌは、我々は衝突回避の行動の障害物を認識する方法ず䌌た抂念、䜿甚するかどうかをチェックするためにリヌダヌの珟圚の速床ず方向に基づいおを、我々はず呌ばれるその前にポむントを投圱しおいたすahead。aheadたずえば30、リヌダヌのポむントずキャラクタヌの間の距離が短い堎合、キャラクタヌはリヌダヌの芖野内にあり、移動するはずです。

ポむントはahead、behindポむントず同じ方法で蚈算されたす。違いは、速床ベクトルが反転しないこずです。

 tv = leader.velocity; tv = normalize(tv) * LEADER_BEHIND_DIST; ahead = leader.position + tv; 

followLeader()文字がスコヌプ内にあるかどうかを確認するためにメ゜ッドを倉曎する必芁がありたす。これが発生した堎合、倀force戻りevade(leader)倀、぀たりリヌダヌの䜍眮を回避する力が倉数に远加されたす連続を衚す

 private function followLeader(leader :Boid) :Vector3D { var tv :Vector3D = leader.velocity.clone(); var force :Vector3D = new Vector3D(); //   ahead tv.normalize(); tv.scaleBy(LEADER_BEHIND_DIST); ahead = leader.position.clone().add(tv); //   behind tv.scaleBy(-1); behind = leader.position.clone().add(tv); //       ,    //     . if (isOnLeaderSight(leader, ahead)) { force = force.add(evade(leader)); } //      behind force = force.add(arrive(behind, 50)); // 50 -   //    force = force.add(separation()); return force; } private function isOnLeaderSight(leader :Boid, leaderAhead :Vector3D) :Boolean { return distance(leaderAhead, this) <= LEADER_SIGHT_RADIUS || distance(leader.position, this) <= LEADER_SIGHT_RADIUS; } private function distance(a :Object, b :Object) :Number { return Math.sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by)); } 

すべおのキャラクタヌは、䞀床リヌダヌの芖界に入るず、珟圚の䜍眮を即座に回避したすFlash デモ。

ヒントリヌダヌをフォロヌする力は、いく぀かの力の組み合わせです。キャラクタヌが远埓の力の圱響を受ける堎合、実際には到着、分離、回避の力の圱響も同時に受けたす。



デモ


以䞋は、リヌダヌをフォロヌする動䜜を瀺すデモです。青い兵士リヌダヌはマりスカヌ゜ルに関連しお到着動䜜を実行し、緑の兵士はリヌダヌに埓いたす。

プレむダヌが画面をクリックするず、すべおの兵士がカヌ゜ルの方向に向きを倉えお撃ちたす。モンスタヌは数秒ごずに到着動䜜をランダムなポむントに適甚したす。


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


おわりに


リヌダヌの远埓動䜜により、キャラクタヌのグルヌプは特定の目暙をたどりながら、少し遅れをずるこずができたす。さらに、キャラクタヌは、リヌダヌの邪魔をしおいる堎合、リヌダヌの珟圚䜍眮を避けたす。

リヌダヌに埓う行動は、到着、回避、分離などの他のいく぀かの行動の組み合わせであるこずに泚意するこずが重芁です。単玔な動䜜を組み合わせお、非垞に耇雑なモヌションパタヌンを䜜成できるこずを瀺しおいたす。

パヌト9.キュヌ


郚屋がAI制埡の゚ンティティで満たされおいるゲヌムシヌンを想像しおください。䜕らかの理由で、圌らは郚屋を出おドアを通らなければなりたせん。ランダムにお互いを通り抜けるのではなく、䞁寧に䞊んでいるこずを教えたしょう。

チュヌトリアルのこの郚分では、矀衆を動かし、゚ンティティのチェヌンを䜜成するさたざたなアプロヌチで、キュヌキュヌの動䜜を理解したす。



はじめに


このチュヌトリアルのコンテキストでは、キュヌむングは、ある時点での到着を蟛抱匷く埅っおいるキャラクタヌの行を䜜成するプロセスです。行の最初の人が移動するず、他の人が圌を远いかけ、圌の埌ろにワゎンが䌞びおいる電車に䌌たパタヌンを䜜成したす。埅機䞭、キャラクタヌはキュヌから離れるこずはできたせん。

キュヌの動䜜を説明し、さたざたな実装を実蚌するには、「キュヌのあるシヌン」を䜿甚したデモが最適です。良い䟋は、AI制埡゚ンティティがドアから出お郚屋から出ようずする郚屋ですFlash デモ。

このシヌンは、前述の2぀の動䜜、シヌクず衝突回避を䜿甚しお䜜成されたした。

ドアは、2぀の長方圢の障害物で構成されおおり、その間に隙間出入口がありたす。キャラクタヌは、このドアの埌ろにあるポむントを探したす。そこに達するず、キャラクタヌは画面の䞋郚に移動したす。

キュヌの動䜜がなければ、シヌンは野av人の倧矀のように芋え、目的地に到着するのに苊劎したす。動䜜の実装埌、矀集は郚屋をスムヌズに離れ、列を䜜成したす。



楜しみにしお


キャラクタヌが䞊ぶ必芁がある最初の胜力は、誰かが圌の前にいるかどうかを調べる胜力です。この情報に基づいお、圌は移動を続けるか停止するかを決定できたす。

前の隣人の存圚を確認するより耇雑な方法が存圚するにもかかわらず、私はポむントずキャラクタヌの間の距離に基づいお単玔化された方法を䜿甚したす。このアプロヌチは、前方の障害物を確認するための衝突回避動䜜で䜿甚されたした。


前のポむントで隣人をチェックしたす。

名前のあるドットがキャラクタヌの前に投圱されaheadたす。この点ず隣接するキャラクタヌの間の距離が以䞋である堎合MAX_QUEUE_RADIUS、これは前に誰かがいおキャラクタヌが停止するこずを意味したす。

ポむントはahead次のように蚈算されたす擬䌌コヌド

 //  qa,  ahead    qa = normalize(velocity) * MAX_QUEUE_AHEAD; ahead = qa + position; 

文字の方向も䞎える速床は、ずMAX_QUEUE_AHEAD呌ばれる新しいベクトルを䜜成するために正芏化およびスケヌリングされたすqa。qaベクトルに远加するず、position結果はキャラクタヌMAX_QUEUE_AHEADからナニットの距離にあるキャラクタヌの前のポむントになりたす。

これはすべおメ゜ッドでラップできたすgetNeighborAhead()

 private function getNeighborAhead() :Boid { var i:int; var ret :Boid = null; var qa :Vector3D = velocity.clone(); qa.normalize(); qa.scaleBy(MAX_QUEUE_AHEAD); ahead = position.clone().add(qa); for (i = 0; i < Game.instance.boids.length; i++) { var neighbor :Boid = Game.instance.boids[i]; var d :Number = distance(ahead, neighbor.position); if (neighbour != this && d <= MAX_QUEUE_RADIUS) { ret = neighbor; break; } } return ret; } 

このメ゜ッドは、ポむントaheadず他のすべおの文字ずの間の距離をチェックし、最初の文字を返しMAX_QUEUE_AHEADたす。距離は以䞋です。文字が芋぀からない堎合、メ゜ッドはを返したすnull。



キュヌむングメ゜ッドの䜜成


他のすべおの動䜜ず同様に、キュヌむングパワヌは次のメ゜ッドで蚈算されqueue()たす。

 private function queue() :Vector3D { var neighbor :Boid = getNeighborAhead(); if (neighbor != null) { // TODO:  ,      } return new Vector3D(0, 0); } 

結果getNeighborAhead()は倉数に栌玍されたすneighbor。の堎合neighbor != null、先に誰かがいたす。それ以倖の堎合、パスは明確です。

方法queue()、䞊びに行動のすべおの他の方法は、方法自䜓に関連付けられた駆動力である力を返すべきです。それqueue()は倧きさなしで電力を返したすが、぀たり、効果はありたせん。これたでドアのあるシヌンのすべおのキャラクタヌ

のメ゜ッドupdate()は、次のようになりたす擬䌌コヌド。

 public function update():void { var doorway :Vector3D = getDoorwayPosition(); steering = seek(doorway); // seek   steering = steering + collisionAvoidance(); //   steering = steering + queue(); //      steering = truncate (steering, MAX_FORCE); steering = steering / mass; velocity = truncate (velocity + steering , MAX_SPEED); position = position + velocity; 

queue()れロのパワヌを返すため、キャラクタヌは行を䜜成せずに動き続けたす。隣人が先に発芋されたずき、圌らは䜕らかの行動を取る時です。



トラフィックの停止に関するいく぀かの蚀葉


ステアリング動䜜は垞に倉化する力に基づいおいるため、システム党䜓が非垞に動的になりたす。より倚くの力が䜿甚されるほど、特定の力のベクトルを特定しおキャンセルするこずがより難しくなりたす。

ステアリング動䜜に関するこの䞀連のチュヌトリアルで䜿甚される実装では、すべおの力が加算されたす。したがっお、力をキャンセルするには、力を再蚈算、倉換し、珟圚の制埡力のベクトルに远加し盎す必芁がありたす。

これは、キャラクタヌを停止させるために速床がキャンセルされる到着動䜜で発生するこずです。しかし、たずえば衝突回避、逃亡などの行動においお、いく぀かの力が䞀緒に䜜甚するずどうなりたすか

以䞋のセクションでは、キャラクタヌを止める方法に぀いお2぀のアむデアを提䟛したす。1぀目は、速床ベクトルに盎接䜜甚し、他のすべおの制埡力を無芖する「ハヌドストップ」アプロヌチを䜿甚したす。2番目は、ず呌ばれる力ベクトルを䜿甚しお、brake他のすべおの制埡力をキャンセルしたす。぀たり、キャラクタヌを匷制的に停止したす。



ストップモヌション「ハヌドストップ」


いく぀かの制埡力は、キャラクタヌの速床ベクトルに基づいおいたす。このベクトルが倉化するず、他のすべおの力に圱響したす。぀たり、再蚈算する必芁がありたす。「ハヌドストップ」のアむデアは非垞に単玔です。前にキャラクタヌがいる堎合、速床ベクトルを「トリミング」したす。

 private function queue() :Vector3D { var neighbor :Boid = getNeighborAhead(); if (neighbor != null) { velocity.scaleBy(0.3); } return new Vector3D(0, 0); } 

䞊蚘のコヌドでは、文字が前にある堎合、ベクトルスケヌルは珟圚の倀長さからvelocity枛少し30%たす。その結果、動きは倧幅に枛少したすが、キャラクタヌがブロッキングパスを離れるず最終的に通垞の倀に戻りたす。

これは、各曎新で動きがどのように蚈算されるかを分析するこずで理解しやすくなりたす

 velocity = truncate (velocity + steering , MAX_SPEED); position = position + velocity; 

力velocityが䜎䞋し続ける堎合、力にsteering基づいおいるため、力でこれが発生しvelocityたす。これは、極端に䜎い倀をもたらす悪埪環を䜜り出したすvelocity。そしお、キャラクタヌは動きを止めたす。

トリミングプロセスが完了するず、ゲヌムの曎新ごずにベクトルvelocityがわずかに増加し、匷床にも圱響しsteeringたす。埐々にいく぀かの曎新この意志ベクトルvelocityずsteeringその正垞倀に。

「ハヌドストップ」゜リュヌションは、次の結果を䜜成したすフラッシュデモ。

結果は非垞に劥圓ですが、それでも少し「機構的」に芋えたす。実際の矀集では、通垞、参加者の間に空きスペヌスはありたせん。



ストップモヌション制動力


動䜜を停止する2番目のアプロヌチでは、すべおのアクティブな制埡力が匷制的にキャンセルされるため、「機構的な」結果は少なくなりbrakeたす。

 private function queue() :Vector3D { var v :Vector3D = velocity.clone(); var brake :Vector3D = new Vector3D(); var neighbor :Boid = getNeighborAhead(); if (neighbor != null) { brake.x = -steering.x * 0.8; brake.y = -steering.y * 0.8; v.scaleBy( -1); brake = brake.add(v); } return brake; } 

brakeアクティブな各制埡力を再カりントおよび反転しお力を䜜成する代わりに、珟圚適甚されおいるすべおの制埡力を含むbrake珟圚のベクトルに基づいお蚈算steeringされたす。


抑制力の衚珟。

匷brake受信コンポヌネントxずy匷さはsteering、それらを描画し、芏暡でそれらを倉曎したす0.8。これはbrake、倧きさが80steeringで、反察方向を指しおいるこずを意味したす。

ヒント力の盎接䜿甚はsteering危険です。これqueue()がキャラクタヌに適甚される最初の動䜜である堎合、力steeringは「空」になりたす。したがっお、他のすべおの動䜜メ゜ッドqueue()を呌び出しおから、圌が完党か぀最終的な力を獲埗できるようにする必芁がありたすsteering。

匷さbrakeもキャラクタヌの速床を盞殺するはずです。これは、-velocity匷床を远加するこずによっお行われbrakeたす。この方法の埌queue()最終的な力を取り戻すこずができbrakeたす。

制動力を䜿甚した結果は次のようになりたす。


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



文字オヌバヌレむの削枛


すべおのキャラクタヌが空のスペヌスを埋めようずしおいるため、抑制のある゜リュヌションは「メカニズム」の結果ず比范しおより自然な結果を生み出したす。ただし、新しい問題が発生したす。文字が互いに重なり合っおいたす。

それを排陀するために、「ハヌドストップ」アプロヌチを少し修正したバヌゞョンでブレヌキ゜リュヌションを改善できたす。

 private function queue() :Vector3D { var v :Vector3D = velocity.clone(); var brake :Vector3D = new Vector3D(); var neighbor :Boid = getNeighborAhead(); if (neighbor != null) { brake.x = -steering.x * 0.8; brake.y = -steering.y * 0.8; v.scaleBy( -1); brake = brake.add(v); if (distance(position, neighbor.position) <= MAX_QUEUE_RADIUS) { velocity.scaleBy(0.3); } } return brake; } 

新しいテストは、最近傍をチェックするために䜿甚されたす。今回は、距離を枬定するためにポむントを䜿甚する代わりにahead、新しいテストは文字ベクトル間の距離をチェックしたすposition


MAX_QUEUE_RADIUSの半埄内で、前方ではなく䜍眮を䞭心ずする最近傍をチェックしたす。

この新しいテストは半埄の䞭にどんな最も近い隣人がいるかチェックしたすMAX_QUEUE_RADIUSが、今それはベクトルの䞭倮にありpositionたす。内郚に誰かがいる堎合、これは呚囲の領域がいっぱいになりすぎおおり、キャラクタヌが重なり始める可胜性があるこずを意味したす。

オヌバヌレむは、ベクトルvelocityを珟圚の倀の30に曎新するたびにスケヌリングするこずで削枛できたす。「ハヌドストップ」アプロヌチず同様に、ベクトルをトリミングするず、velocity動きが倧幅に枛少したす。

結果は「機構的」ではないように芋えたすが、キャラクタヌはただ戞口で亀差しおいるため、ただ䞍完党ですデモ Flashで。



分離を远加する


キャラクタヌは説埗力のある方法で戞口に到達しようずしたすが、通路が狭くなるずすべおの空きスペヌスを埋めようずしたすが、戞口では互いに近づきすぎたす。

この問題は、分離力を远加するこずで解決できたす。

 private function queue() :Vector3D { var v :Vector3D = velocity.clone(); var brake :Vector3D = new Vector3D(); var neighbor :Boid = getNeighborAhead(); if (neighbor != null) { brake.x = -steering.x * 0.8; brake.y = -steering.y * 0.8; v.scaleBy( -1); brake = brake.add(v); brake = brake.add(separation()); if (distance(position, neighbor.position) <= MAX_QUEUE_RADIUS) { velocity.scaleBy(0.3); } } return brake; } 

リヌダヌ远埓動䜜で以前䜿甚されおいた分離力がフォヌスに远加されbrake、キャラクタヌが停止するず同時に、互いに觊れないようにしたす。

その結果、戞口から抜け出そうずする信頌できる矀衆が埗られたす。


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


おわりに


キュヌの動䜜により、キャラクタヌは䞀列に䞊び、目的地に到着するたで蟛抱匷く埅぀こずができたす。䞊んでいる間、キャラクタヌは䜍眮を飛び越えお「チヌト」しようずしない。圌は自分の前に立っおいるキャラクタヌが動いたずきにのみ動きたす。

このチュヌトリアルに瀺されおいる戞口のシヌンは、この動䜜がいかに倚甚途でカスタマむズ可胜であるかを瀺しおいたす。わずかな倉曎でもたったく異なる結果が䜜成され、さたざたな状況に合わせお簡単に調敎できたす。この動䜜は、衝突回避など、他の動䜜ず組み合わせるこずもできたす。

この新しい振る舞いを楜しんでいただき、ゲヌムで感動的な矀衆を䜜成するためにそれを䜿甚するこずを願っおいたす

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


All Articles