学生のUnityプロジェクトでよく見るのが、主人公キャラクターが壁から落下したり、壁を貫通して移動したりする光景です。
それは「そういったゲーム」なら兎も角、RPGの移動パートのような「キャラクターを移動させる」事が目的の場合は出来れば避けて欲しい所です。
今回はキャラクターが「想定したステージ上」から落下しないように動かす手っ取り早い方法を紹介します。
意図しない場所を歩かないキャラクター
RPGのようなゲームにおいて、キャラクターが想定外の位置に移動することは好ましくないです。例えば道中にボスが居ても無視して進められたら、ゲームは興ざめです。(最近ソレが意図的に出来るゲーム多い印象ではありますが)
また何もない空間を歩かれるのも困り物なので、基本的に地面の上を歩くことが望ましいです。
落下せずキャラクターを移動させるのはNavMeshが楽
さて、地面判定はPhysics系の命令でColliderとの距離もしくは接地判定を行うのが楽ですが、キャラクターがジャンプしないのであればNavMeshを設定してしまうのが楽です。
NavMeshを設定しNavMeshAgentで移動させている場合、agent.updatePositionをfalseに設定しない限り、NavMeshで指定した範囲から抜け出すことは出来ません。キャラクターに指定の範囲移動させるには便利な機能です。
他のアイディアとしては、足元にSphereCastを落とす…や、壁をColliderで囲む(囲み損ねるとソコから落下する)等が考えられます。
キャラクターはNavMeshAgent.Moveで動かす
ここでのキャラクターは基本的にNavMeshAgent.Moveで移動します。例えばコードはこんな感じです。
GetAxisで移動を持ってきてAgentに渡すだけの簡単なお仕事です。
なお、この設定ではNavMeshAgentに設定されてる移動用パラメータはほぼ使いません。SpeedもAngular SpeedもAccelerationも0で良いです。
ついでにはObstacle Avoidanceすらも0もNoneで良いです。何せキャラクターは直接動かすので。
ちなみにNavMeshAgent同士が接触する際、どちらかでもObstacle AvoidanceをLow以上せっていされてるなら、接触してキャラクターが重なるのを回避してくれます(要するに押し出せます)
NavMeshをベイクする
この手法はNavmeshAgentだけでは不十分で、必ずNavMeshで移動範囲を定義してやる必要があります。
NavMeshAgentが移動するにはNavMeshを事前にベイクする必要があります。
NavMeshをベイクするにはNavigation Staticにチェックが入っている事が大前提で、かつNavigationウィンドウでシーン毎にベイクしておく事が必要です。
要するに、途中で変化するようなステージには使いにくかった所があります。やるならNavMeshとグラフィックは別にして、NavMeshObstacleで穴を空ける的な事をする必要がありました。
Unity 5.6から動的なNavMesh構築が可能に
実際、以前はNavMeshは静的な物でしたがUnity 5.6から動的にNavMeshを構築が可能になったので、それを使用して「地形が変化するステージ」で「マップから落ちない」ように移動するのを、「手っ取り早く」実現してみます。
まずはNavigationのウィンドウにあるリンクを移動して、NavmeshのHLAPIを入手・導入します。DLするファイルのバージョンは、とりあえず最新なら多分良さそうです。
LocalNavMeshBuilderでキャラクターの周辺のNavMeshを生成
NavMeshを動的に作るHLAPIは幾つかありますが、今回は「キャラクターの周辺だけNavMeshを作る」LocalNavMeshBuilderを使ってみます。
- 適当なGameObjectにLocalNavMeshBulderを追加し、Trackedに動かすキャラクターを指定します。
- 地面になるオブジェクトにNavMeshSourceTagを設定します
これで動的にNavMeshがキャラクターの周辺に構築されて、NavMeshAgent経由で地面に落下せず動かすことが期待出来ます。
なお、初期設定80平方メートルとかなってますが、今回の用途だとキャラクターが5平方メートルくらいでも十分です。
大きくなるとビルドに時間がかかりますので、出来る限り小さくしたい所です。但し、小さいとNavMeshの更新頻度が上がるので、その辺りは調整を考える必要がありそうです。
今回は移動するキャラクターが一体のみなのでLocalNavMeshBuilderを使用しましたが、もしプレイヤー周辺以外にもNavMeshAgentで動くキャラクターが居る場合、NavMeshSurfaceで指定範囲のNavMeshを構築する方が安いかもしれません。
ジャンプ・落下は特殊アクション扱い
この手法で問題になるのはジャンプや落下等の扱いです。基本的にNavMesh上を動き回るので、そこから外れたジャンプ・自由落下等は出来ません。ついでに言えば、OffMeshLinkも使えません。
そういった物は、この手法では「指定場所の特別なアクション」のような形で扱います。
イメージとしてはコレが近くて、
- キャラクターが指定範囲に入ったらagent.updatePositionをfalse
- キャラクターの移動を(アニメーションやTween、MatchTargetで)行う
- 移動完了後、WarpでNavMeshAgentの位置を移動
- agent.updatePositionをtrueで移動を再開
といった感じです。
TimelineとNavmeshの組み合わせ pic.twitter.com/l4dISADioG
— 椿 (@tsubaki_t1) 2017年4月23日
追加情報
旧版
特別なアクションをカットシーンで…
NavMesh以外で動く物との連携
単純な移動周り
tsubakit1.hateblo.jpNavMeshの云々
今日の一言
やべぇ、タブレットPCで絵書くの超楽しい。ペイントわっほい