f:id:tsubaki_t1:20170706223650g:plain

学生のUnityプロジェクトでよく見るのが、主人公キャラクターが壁から落下したり、壁を貫通して移動したりする光景です。

それは「そういったゲーム」なら兎も角、RPGの移動パートのような「キャラクターを移動させる」事が目的の場合は出来れば避けて欲しい所です。

今回はキャラクターが「想定したステージ上」から落下しないように動かす手っ取り早い方法を紹介します。

  • 意図しない場所を歩かないキャラクター
  • 落下せずキャラクターを移動させるのはNavMeshが楽
    • キャラクターはNavMeshAgent.Moveで動かす
    • NavMeshをベイクする
  • Unity 5.6から動的なNavMesh構築が可能に
    • LocalNavMeshBuilderでキャラクターの周辺のNavMeshを生成
  • ジャンプ・落下は特殊アクション扱い
  • 追加情報

意図しない場所を歩かないキャラクター

RPGのようなゲームにおいて、キャラクターが想定外の位置に移動することは好ましくないです。例えば道中にボスが居ても無視して進められたら、ゲームは興ざめです。(最近ソレが意図的に出来るゲーム多い印象ではありますが)

また何もない空間を歩かれるのも困り物なので、基本的に地面の上を歩くことが望ましいです。

f:id:tsubaki_t1:20170706225006j:plain

落下せずキャラクターを移動させるのはNavMeshが楽

さて、地面判定はPhysics系の命令でColliderとの距離もしくは接地判定を行うのが楽ですが、キャラクターがジャンプしないのであればNavMeshを設定してしまうのが楽です。

NavMeshを設定しNavMeshAgentで移動させている場合、agent.updatePositionをfalseに設定しない限り、NavMeshで指定した範囲から抜け出すことは出来ません。キャラクターに指定の範囲移動させるには便利な機能です。

http://tsubakit1.sakura.ne.jp/images/20131104223224785s.jpg

他のアイディアとしては、足元にSphereCastを落とす…や、壁をColliderで囲む(囲み損ねるとソコから落下する)等が考えられます。

キャラクターはNavMeshAgent.Moveで動かす

ここでのキャラクターは基本的にNavMeshAgent.Moveで移動します。例えばコードはこんな感じです。

GetAxisで移動を持ってきてAgentに渡すだけの簡単なお仕事です。

gist.github.com

なお、この設定ではNavMeshAgentに設定されてる移動用パラメータはほぼ使いません。SpeedもAngular SpeedもAccelerationも0で良いです。

f:id:tsubaki_t1:20170706234813j:plain

ついでにはObstacle Avoidanceすらも0もNoneで良いです。何せキャラクターは直接動かすので。
ちなみにNavMeshAgent同士が接触する際、どちらかでもObstacle AvoidanceをLow以上せっていされてるなら、接触してキャラクターが重なるのを回避してくれます(要するに押し出せます)

f:id:tsubaki_t1:20170706235032g:plain

この手法はNavmeshAgentだけでは不十分で、必ずNavMeshで移動範囲を定義してやる必要があります。

NavMeshAgentが移動するにはNavMeshを事前にベイクする必要があります。

NavMeshをベイクするにはNavigation Staticにチェックが入っている事が大前提で、かつNavigationウィンドウでシーン毎にベイクしておく事が必要です。

f:id:tsubaki_t1:20170706230649j:plain

要するに、途中で変化するようなステージには使いにくかった所があります。やるならNavMeshとグラフィックは別にして、NavMeshObstacleで穴を空ける的な事をする必要がありました。

tsubakit1.hateblo.jp

Unity 5.6から動的なNavMesh構築が可能に

実際、以前はNavMeshは静的な物でしたがUnity 5.6から動的にNavMeshを構築が可能になったので、それを使用して「地形が変化するステージ」で「マップから落ちない」ように移動するのを、「手っ取り早く」実現してみます。

 

まずはNavigationのウィンドウにあるリンクを移動して、NavmeshのHLAPIを入手・導入します。DLするファイルのバージョンは、とりあえず最新なら多分良さそうです。

f:id:tsubaki_t1:20170706231808j:plain

f:id:tsubaki_t1:20170706231932j:plain

f:id:tsubaki_t1:20170706232046j:plain

LocalNavMeshBuilderでキャラクターの周辺のNavMeshを生成

NavMeshを動的に作るHLAPIは幾つかありますが、今回は「キャラクターの周辺だけNavMeshを作る」LocalNavMeshBuilderを使ってみます。

f:id:tsubaki_t1:20170706232649g:plain

  1. 適当なGameObjectにLocalNavMeshBulderを追加し、Trackedに動かすキャラクターを指定します。
  2. 地面になるオブジェクトにNavMeshSourceTagを設定します

これで動的にNavMeshがキャラクターの周辺に構築されて、NavMeshAgent経由で地面に落下せず動かすことが期待出来ます。

f:id:tsubaki_t1:20170706232941j:plain

f:id:tsubaki_t1:20170706232954j:plain

なお、初期設定80平方メートルとかなってますが、今回の用途だとキャラクターが5平方メートルくらいでも十分です。
大きくなるとビルドに時間がかかりますので、出来る限り小さくしたい所です。但し、小さいとNavMeshの更新頻度が上がるので、その辺りは調整を考える必要がありそうです。

 

今回は移動するキャラクターが一体のみなのでLocalNavMeshBuilderを使用しましたが、もしプレイヤー周辺以外にもNavMeshAgentで動くキャラクターが居る場合、NavMeshSurfaceで指定範囲のNavMeshを構築する方が安いかもしれません。

ジャンプ・落下は特殊アクション扱い

この手法で問題になるのはジャンプや落下等の扱いです。基本的にNavMesh上を動き回るので、そこから外れたジャンプ・自由落下等は出来ません。ついでに言えば、OffMeshLinkも使えません。

そういった物は、この手法では「指定場所の特別なアクション」のような形で扱います。

イメージとしてはコレが近くて、

  1. キャラクターが指定範囲に入ったらagent.updatePositionをfalse
  2. キャラクターの移動を(アニメーションやTween、MatchTargetで)行う
  3. 移動完了後、WarpでNavMeshAgentの位置を移動
  4. agent.updatePositionをtrueで移動を再開

といった感じです。

f:id:tsubaki_t1:20170706234136j:plain

追加情報

旧版

tsubakit1.hateblo.jp

特別なアクションをカットシーンで…

tsubakit1.hateblo.jp

NavMesh以外で動く物との連携

tsubakit1.hateblo.jp

単純な移動周り

tsubakit1.hateblo.jpNavMeshの云々

【Unite 2017 Tokyo】Navmesh完全マスターへの道 from UnityTechnologiesJapan

www.slideshare.net

今日の一言

やべぇ、タブレットPCで絵書くの超楽しい。ペイントわっほい

f:id:tsubaki_t1:20170707000306j:plain