Unityのメッシュ変形での範囲攻撃の作り方
前回までの記事でPlayer→敵。敵→Playerへの攻撃が可能となった。前回までの記事↓
TPSのキャラ移動
ジャンプ
ダッシュ実装
自作キャラを動き回らせる
剣を振る方法
剣コンボ
足が浮く対策
剣の軌跡の作り方
燃える剣の必殺技
ステータス管理
ダメージ判定・処理
HPゲージ
敵の攻撃
ただ、問題点として、Player、敵、剣のコライダーの大きさにもよるが、剣を振ってもダメージが入らない場合がある。
剣を振るとなると、速い速度で剣のゲームオブジェクトを動かす必要がある。剣のコライダーが敵ゲームオブジェクトに侵入したか確認する処理と処理の間に剣が通過した場合はダメージ処理が行われない。
ダメージ処理すり抜けを防止する方法として、前の剣の位置と今の剣の位置で囲んだ部分をメッシュ変形をして表現し範囲攻撃を実現する方法があるので紹介していく。
Unityのメッシュ変形での範囲攻撃の作り方
メッシュとはメッシュの描写
メッシュ変形で範囲攻撃作成
目次にもどる
メッシュとは
メッシュとは三角形のポリゴンが集まって形成した面を指す。メッシュには様々な情報が含まれており、それはMeshクラスにより管理されている。その中で特に重要なのがVertexとtrianglesである。
Vertexは頂点の位置を示す。この頂点が囲まれた範囲がメッシュとなる。頂点が2つでは線ができるだけで面にならずメッシュとはならない。よって3つの頂点で作った三角形が最小単位のメッシュである。
trianglesは頂点をつなぐ順番を示す。頂点をつなぐことではじめて面ができメッシュを作ることができる。時計周りにした面が表となる。例えば面を組み合わせれば厚みのある立方体のメッシュも作れるが、一つだけ頂点をつなぐ順番を反時計回りにしたりしてしまうと、面の向きがおかしくなってしまうので注意が必要。全て時計周りで統一することが大切だ。
目次にもどる
メッシュの描写
作ったメッシュを表示するためにはMeshRenderer(メッシュレンダラー)というメッシュを表示するためのコンポーネントが必要となる。以下の2種が存在する。MeshRenderer
頂点の位置が移動しないメッシュ。
SkinnedMeshRenderer
頂点の位置が移動するメッシュ。
Meshインスタンス→MeshFilterにメッシュを渡すようにしてやれば、自動的にMeshFilter→MeshRendererにメッシュが渡される仕組みとなっている。
試しにゲームオブジェクトGubeを作成し、コンポーネントを見てみよう。最初からCube(MeshFilter)とMeshRendererがあり、この2つによってCubeが形つくられている。
目次にもどる
メッシュ変形で範囲攻撃作成
実際にメッシュを変形させて剣を振った際の範囲攻撃を作っていく。まずはメッシュの面を形成するための頂点位置を決める必要がある。Cubeのゲームオブジェクトを4つ作成した(名前はkenkonpon、kenkonpon2、kensaki、kensaki2とした)。見づらくないようサイズを小さくして、用意してPlayerの剣の刀身の先に2つ、根本に2つ、微妙に位置をずらして配置する(kensaki、kensaki2が剣よりだいぶ先にあるが、個人的な剣攻撃のとどく範囲を広くする措置。この辺りはお好みで)。
4つのゲームオブジュクトはPlayerの剣の子要素にして、剣を振った際に追従して動くようにする。これらのゲームオブジェクトの位置をスクリプトでメッシュ作成する際の頂点の位置とする。
4つのゲームオブジェクトとは別にCubeのゲームオブジェクトを1つ作成しAtariと名前をつけ剣の子要素とする(このゲームオブジェクトがメッシュを作り出すことになる)。Atariに以下のスクリプトをアタッチする。
クラス定義前に[RequireComponent(typeof(MeshFilter), typeof(MeshCollider))]としている。これでこのゲームオブジェクトにMeshFilterとMeshColliderを自動的に追加することができる。これらをAddComponentするのを忘れるのを防止できる。
Awakeメソッド(Startメソッドより早く呼び出される)では、このゲームオブジェクトを親子関係から外している。親子関係だとPlayerのゲームオブジェクトを原点とするため処理がおかしくなる。最初から親子関係にしないのもアリだが、後々のプレハブ化を考えると子要素にしたかったために、このような仕様にしている。
後は、位置を(0,0,0)に移動させている。これも後々の処理を行う際に原点(0,0,0)にこのゲームオブジェクトがあったほうが位置計算の都合がいいため行っている。
Startメソッドでは、自動追加したMeshFilter、MeshColliderを取得し変数に代入している。
更にMeshColliderについてはconvex、isTrigger、sharedMeshについて操作を行っている。
meshCollider.convex = true;で他のメッシュコライダと衝突できるようになるが、meshCollider.isTrigger = true;とすることでトリガー判定となる。
meshCollider.sharedMesh = mesh;とし、衝突を検出するために使用するメッシュを自動追加したMeshFilter()のmeshにしている。
LateUpdateメソッド(計算が終わった後に毎フレーム呼ばれる)ではICharaAttack Attacktimeuketori = AttackChara.GetComponent
この処理については前の記事を見ていないとわからないと思うので詳しく知りたい人は確認してほしい。
敵の攻撃
ICharaAttackを宣言した別スクリプトで、攻撃を行う際AnimationClipで剣の振り始めと振り終わりでメソッド呼び出しを行っており、bool型の変数Attacktimeを剣の振り始めでtrue、剣の振り終わりでfalseとしている。
Attacktimeの値をとることで、現在剣を振っているかそうでないかがわかる。if (Attacktime)としてtrueであれば中身のCreateMesh();が処理され、CreateMeshメソッドが実行されることになる。
CreateMeshメソッドではその名の通り、メッシュを作っている。
最初にメッシュとリストをクリアにしている。その上で以下のように頂点の位置リストに入れている。
OldMotoPositionとOldSakiPositionについては前フレームの頂点の位置を記憶したものである。残りの4つが現在の頂点の位置となる。現在の頂点の位置を4つ用意したのはコライダーに厚みをつけるためである(厚みがないコライダーでは接触時にエラーが出る場合がある)。この6つの頂点を使ってメッシュを作っていくことになる。
このリストに入れた順番に頂点に以下のように番号が振られる(0から振られるので注意しよう)。
OldMotoPosition…0
OldSakiPosition…1
MotoPosition.position…2
SakiPosition.position…3
MotoPosition2.position…4
SakiPosition2.position…5
続いて以下のように頂点をつなぐ順番を決め、面を作っていく。
慣れは必要だが、できるメッシュの立方体を想像しあらゆる方向から見て時計周りに面を描くようにしよう。
後は、リストを配列に変換し、mesh.RecalculateBounds()でメッシュの包含範囲を自動的に計算する。
mesh.RecalculateNormals();で法線の再計算を行う。法線とはポリゴンの面の表方向に伸びるベクトルである。これでメッシュの表裏が判別できる。
これで6つの頂点による立方体メッシュが完成した。
最後にMotoPositionとSakiPositionについて、OldMotoPositionとOldSakiPositionに記憶し、次の処理に備える。
このメソッドは剣を振っている間は毎フレーム呼ばれるので、剣が移動していくのに従って頂点が移動しメッシュ変形していき、メッシュコライダーによる攻撃範囲が剣に追従して変化する。
Atariのゲームオブジェクトに青のマテリアルをつけ、再生を行いヒエラルキー欄でAtariを選択してシーンビューを見ると以下のように見え、コライダーの範囲を確認することができる。
後はAtariのゲームオブジェクトに以下のPlayerAttackもアタッチする(以前の記事で作成したようなコライダーが侵入したらダメージ処理を行うスクリプト)。
これで、敵ゲームオブジェクトのコライダーに、メッシュコライダーが侵入すればダメージ処理スクリプトが呼ばれるようになる。
続きの記事は以下。
ビーム攻撃