Unity5で剣の攻撃とエフェクトを作ってみる

Unity5で見下ろし型のガン・シューティングを作っていますが、近接攻撃も作ってみたいので、実験用プロジェクトを作成しました。

剣を振る動作をどうやって実現するか

Unityでは、剣の攻撃のアニメーションを作る方法として大きく3つ考えられます。

  1. Blenderなどモデリングソフトでアニメーションを用意する。
  2. Mecanimで、Transformを修正してアニメーションを作成する。
  3. C#スクリプトで、Transformを全部操作する。

1つのアニメーションを作成する場合は、3の方法で問題ありませんが、アニメーションの数が多くなるとそれをすべてコードで書くのはかなり面倒なので、近接攻撃のアニメーションのようにいろいろ増えそうな場合は2の方法が簡単です。デザイナー・モデラーがいるプロジェクトの場合は、1の方法が最適です。

今回は頭から胴に斬るアニメーションと、胴を横に斬るアニメーションを2つ用意し、将来的にはもっと増やしたいので、2の方法を選択しました。以下は縦に剣を振るアニメーションです。

A02

剣にアニメーションを付けたら、それはPrafabにして、PlayerにEmpty Object(この場合EquipPointという名前)を用意してそこに持たせるようにします。

Empty Objectを用意して、そこに剣を装備させる
Empty Objectを用意して、そこに剣を装備させる

ゲームが開始したときに、PlayerBehaviour C#スクリプトのStartメソッドで、Sword Prefabをインスタンス化して、sword.transform.setParent(親オブジェクト, false);で、先ほどのEquipPointに装備させます。

あとは、マウスの左クリックで、縦のスイング、右クリックで、横のスイングのアニメーションを呼び出すようにしました。このStartメソッドのインスタンス化する方法とアニメーションを呼び出す方法は、よく使われるコードなので、お約束として覚えてしまえばいいです。

 

 

マウスカーソルの方向にプレイヤーを向ける

Playerオブジェクトの向きを変更するのは、transform.LookAt(向きベクトル);です。これはメソッド名からすぐにイメージできると思います。難しいのは向きベクトルを求めるところです。

  1. 仮の地面(tmpPlane)を用意する
  2. カメラからマウスカーソルへの光線(Ray)を用意する。
  3. 光線を仮の地面(tmpPlane)へ照射(Raycast)して、カメラからその地面までの光線の長さを求める。
  4. 光線の長さがわかったので、その点(point)を求める。
  5. Playerの高さを考慮して、LookAtで向きを変更する。

上記のように、レイキャストで求める必要があります。一度この仕組みをしっかり学べば理解しやすいです。しかし私も、昔ながらの物理エンジンがないゲームライブラリなどでは、光線の概念がないため、はじめて学習したときは結構理解するのが大変でした。

パーティクルの作成

パーティクルは、項目数が多いため、誰かが作ったものを参考にして、自分でいろいろパラメータを修正しながら作って慣れていくしかありません。

小さな四角形が落ちるエフェクト
小さな四角形が落ちるエフェクト
003
パーティクルは

パーティクルの基本は、Duration, Start Lifetime, Start Speedです。Durationは、この秒数、パーティクルを放射します。この例の場合は、0.15秒つまりかなり身近時間の放射です。Start Lifetimeは、生存期間で、放射したものが2秒生き続けます。2秒すぎると消えます。Start Speedは、そのまま速度で、これを速くすれば、遠くまで飛ばせます。

EmissionのRateは、量です。この場合1秒に100個ぐらいパーティクルを放射します。エフェクトを選択すると、Sceneビューに以下のGUIが表示されて、この部分でパーティクルの起動、ポーズ、停止ができます。また一番下に現在生成されているパーティクル数も表示されるのでいろいろデバッグに役立ちます。

006

Gravity Modifier: 2.5と指定しているので、重力の影響を受けて地面に落ちるようになります。これが0だと、横に飛び続けます。

004

上記では、Collisionを有効にして、[World]をターゲットにしているので、地面に当たった場合に貫通しないようになります。このパーティクルでは、Voxelモデルを倒したときのパーティクルにしたいため、Render Modeをメッシュにして、Materialを新規に作成(Shader: Sprites/Default)して割り当てています。通常のパーティクルの場合は、これをする必要ありません。

005

作成したパーティクルはPrefab化しておきます。

剣で斬られたら、パーティクルを生成する

敵オブジェクト(Enemy)に、Enemyスクリプトを割り当てて、Swordが衝突したら、InstantiateでParticleSystemを生成します。あとは、ParticleSystem.startLifeTimeで、開始からの生存秒数が取得できるので、この秒数後にDestroyメソッドを呼び出して、Particleを破棄しています。

 

剣の軌跡

Trail Rendererを使えば簡単に剣の軌跡を作れます。Swordプレハブに、Empty GameObjectを子要素として追加して、名前をTrailにしています。これにTrail Rendererコンポーネントを追加し、各種設定を行います。

007

このプロジェクトの配布

Unity5.4で作成しました。

http://hajimete-program.com/games/SwordAttack_and_DeathEffect.zip

まとめ

プレイヤーをマウスカーソルに向けて、近接武器である剣を振るようにして、ダメージの視覚化としてパーティクルを生成するようにしました。また斧やハンマーにしたときに簡単にモーションを追加できるようにMecanimでアニメーションを作成しました。

 

Unity Create a Game Series (E16. weapon effects)をやってみました。

今回は、弾を発射した際に、薬莢(やっきょう)を飛ぶようにして、また銃口が光る(マズルフラッシュ)ようにしました。プログラミングや作業は簡単ですが細かい作業なので、どこに何かあるかを繰り返し作業をして覚える必要があります。

また小さなパフォーマンスチューニングですが、薬きょうは、フロアと障害物以外には衝突しないようにしました。

001

Unity Create a Game Series (E14. game over)をやってみました。

Unity Create a Game Series (E14. game over)をやってみました。

今回はゲームオーバー画面をuGUIで作成し、”Play again”ボタンで、シーンをリロードする仕組みを作りました。これは今までミニゲームを作ったりした際に経験していたので簡単でした。

ゲームオーバーシーンを表示
ゲームオーバーシーンを表示

この回で、基本的なゲームの仕組みは揃いました。これから先の目次を見ると、それぞれの機能を磨き上げる工程になるので、おそらくそこまで難しいものではないと思われるので頑張って進んでいきます。

  • パーティクル追加
  • 敵ウェーブの修正
  • リコイル・リロード
  • 武器の追加
  • UI改良、
  • オーディオ追加
  • メニュー

あとは、ライフバー、アイテム、インベントリーあたりを学べばさらに良くなりそうです。

このプロジェクトの配布

http://hajimete-program.com/games/Unity Create a Game Series (E14. game over).zip

 

 

Unity Create a Game Series (E12. finishing the map generator)をやってみました。

引き続き、「Unity Create a Game Series」をやりマップの自動生成の作業が完了し、第7回までに作ったトップダウンシューティングゲームに組み込みました。

マップ自動生成をゲームに組み込んだところ
マップ自動生成をゲームに組み込んだところ

今回の第12回は、障害物に高低差を付け、グラデーションを付けれるようにしました。高低差はScaleで変更できるのが容易に想像できますが、Materialをプログラムから操作するのは少ないので、このチュートリアルで扱えてよかったです。また、今回は、Game.unityシーンに、MapGeneratorのオブジェクトをコピーペーストしてゲームに反映させました。MapGenerator.unityはシーンとして独立して保存しているのでいろいろ修正が可能です。

002
Unityエディターで、各マップを設定できるようにしている

MapGeneratorスクリプトには、Mapクラスを作り、その配列をUnityエディターから操作できるようにしたので、ちょっとしたマップの修正を検証するのが簡単になりました。

マップ自動生成の基礎プログラミングを手に入れた

様々なパラメータを修正できる
様々なパラメータを修正できる

今回まで一番の収穫は、プロシージャル・ジェネレーションの基礎を学習できたことです。ローグライク系ゲームのように、フロアを自動生成するような仕組みに作り替えたりはまだできませんが、それをするためのメッシュ生成や配列操作などは学べました。

このプロジェクトの配布

http://hajimete-program.com/games/Unity Create a Game Series (E12. finishing the map generator).zip

Unity5でNavMeshAgentを使った敵の体当たりと攻撃方法

Sebastian LagueさんのYoutube動画チュートリアル「Unity Create a Game Series (E06. enemy attacks)」でUnityのナビゲーションの使い方がわかりましたが、いくつか計算方法が理解できなかったので、今回も小さなプロジェクトを作成して、再学習しました。

敵がプレイヤーに移動する方法

これはUnityのナビゲーションを使えば簡単です。

敵オブジェクトにNavMeshAgentコンポーネントをインスペクターで持たせて、プログラム上では、agent = GetComponent<NavMeshAgent>();で変数に保持して、agent.SetDestination(座標);で、敵が障害物を交わしながら移動するようになります。

ですがColliderを無効にした場合には、プレイヤーの中心座標が、ナビゲーションの到達位置なので、プレイヤーに埋まってしまいます。

002
Colliderが無効なため、敵が埋まってしまう例

もちろんColliderを有効にしている場合は正しく衝突します。ただゲームでは、体当たりする敵もいますが、プレイヤーの至近距離まで来て、武器や魔法を振り回すような敵もいます。このような場合、何らかの方法で一定の距離を保つ計算が必要になります。

一定の距離を保つ方法

先ほどのソースコードでは、ターゲットの中心位置なので、ターゲットに埋まってしまいました。なので衝突判定の半径を考慮すればよいです。

  1. 敵がプレイヤーに向かう方向を求める
  2. ターゲットの位置から、(ターゲットの衝突判定の半径+敵自身の衝突判定の半径)*向かう方向を引く

紫の敵が、赤のプレイヤーに接触せずにとどまる
紫の敵が、赤のプレイヤーに接触せずにとどまる

この仕組みは、Unityに限らずシューティングゲームやもっと単純なゲームでもオフセット計算などでよく使われます。つまりターゲット座標-offsetをしているだけです。

敵が攻撃する方法

先ほどやりかたで敵が一定距離まで近づくようにできました。敵が攻撃する方法も同様で、さらに手前の範囲で攻撃を繰り出すようにします。基本的な考え方は、現在のプレイヤーと敵の距離 < 攻撃開始の距離 が成り立ったら攻撃を開始するようにします。

平方根を回避してパフォーマンスをよくする

数学では、二点ABの長さを求めるには、三平方の定理によりA*A+B*B=C*Cの平方根を求めます。先ほどの「距離A<距離B」のように2つの長さを求めて比較する場合は、2回平方根の処理をしなければなりません。しかし、小数点の平方根を求める処理は重たい処理なため出来れば避けたいです。そのためのテクニックとしては、「距離A<距離B」の比較を「距離A*距離A < 距離B*距離B」として、二乗のままで計算します。

現在のプレイヤーと敵の距離の二乗 < 攻撃開始の距離の二乗

このテクニックを使うためにUnityではsqrMagnitudeが用意されていて、ベクトル型(Vector3)から二乗の値が簡単に取得できます。

この二乗のまま計算するテクニックを知っていないと、.sqrMagnitudeが出てきたり、Mathf.Pow(長さ、2)が出てくる意味が理解できません。これはパフォーマンスのためであって、本来は2つの長さの比較をしたいだけです。

このプロジェクトの配布

http://hajimete-program.com/games/Topdown Shooting A02 enemy attacks.zip

修正案

Sebastian Lagueさんの仕組みは、Unityだけではなくいろんなゲームで使える方法です。ですがUnityの場合ゲームオブジェクトの子要素を用意し、そこにColliderを持たせ、IsTriggerにより、トリガーイベントで、衝突判定が可能になります。この方法はUnityに依存しますが、これで攻撃判定をすると簡単にできそうな気がします。

まとめ

計算方法はわかってしまえば大したことはありませんが、以下のようなコードが出てきたときに方向を求めているのが分からず、なぜ.normalizedがあるのかなどをきにしていました。

またソースコードでいろいろ試す際に、衝突の半径で変数を使わず数字をハードコーディングしたり、半径の変数を削除したりすることで、理解を深めることができました。

Unity5でTPSやトップダウンで弾を打つ方法

Unityは物理演算を使った方法や使わない方法で、物体を移動または回転させられるので初心者からするといろんな方法がありすぎて、人によって書き方が違い学ぶのが大変です。

今回は、先日試してみたSebastian Lagueさんの動画チュートリアル「Create a Game (Unity 5)」から、プレイヤーがマウスカーソルに向いて、弾を打つというロジックのみを切り出して1つのプロジェクトにしてみました。

移動方法

移動方法は、Input.GetAxisRawでキー入力を受け付けて、ローカル変数moveVelocityに値を保持します。

実際に移動する部分は、FixedUpdateメソッド内になります。AddForceもありますが、MovePositionを使うと補完されるのでなめらかに処理ができるそうです。

プレイヤーの向き

プレイヤーをマウスカーソルに向かせるには、以下の手順が必要になります。

  1. カメラからマウスカーソルへの光線を用意する。
  2. 地面に上記光線を投射(Raycast)して、地面に当たった時の、光線の距離(長さ)を求める
  3. 光線と長さから、ワールド座標上の地面のポイントを求める。
  4. そのままプレイヤーの向きを地面のポイントに向けると、下を向いてしまうのでプレイヤー自身のY座標を使う。
  5. プレイヤーを4で求めたポイントに向ける(LookAt)

また配布するソースコードでは、デバッグ情報として、光線Rayを黄色で表示、プレイヤーから地面のポイントへの向きベクトルを黒線で表示、プレイヤーの高さに補正したベクトル(つまり今向いているところ)を灰色で表示できるようにしました。これらはDebug.DrawLine(開始ポイント、終了ポイント)関数で実装しています。

001

Raycastで地面の座標を求めるだけでは下を向いてしまうので、高さをプレイヤーの高さに修正するのが大切です。(青色Z軸=画像では灰色の線)向きが正しくなったのであとは、その方向に弾を飛ばすだけです。

LookAtでプレイヤーの向きが変わるということは、青軸Z軸が変化したことです。Vector3.forward(向き)で取得できます。ちなみにVector3.upで緑軸Y軸、Vector3.rightで赤軸X軸です。

002

弾の発射口(マズル・Muzzle)をプレイヤーオブジェクトの子要素に用意すれば、当たり前ですが、この発射口はプレイヤーに追随して同じ向きに回転します。そのため、弾を発射するときには、このマズルの位置と回転情報で、インスタンス生成をします。

 

弾が飛ぶ仕組み

弾はそのまま重力を無視して飛ぶようにしています。そのためmuzzle.rotationで設定した初期の向きが、弾の向きになるので、その方向に飛び続けるようにします。ソースコードとしては、Vector3.forward(向き)に速度を加えるだけなので簡単です。

 

このプロジェクトの配布

http://hajimete-program.com/games/Topdown Shooting A01 Shot.zip

まとめ

今回は、プレイヤーの向きをマウスカーソルに向けて、弾を発射させました。単純にRaycastで向きを変更するとプレイヤーが地面を向いてしまうので、Y座標を補正する方法も学びました。ゲームの動画チュートリアルではいろんなゲームロジックが混在して複雑になるので、機能の的を絞って、簡単なプロジェクトを作成すると理解が深まります。

 

Unity5でJointを使ってみます。

Unityの物理エンジンには、RigidBody(剛体)間をつなげるジョイントという仕組みがあります。これを使うことでバネやドアのヒンジの仕組みを再現できます。またプログラミングは不要で、オブジェクトにジョイントを追加して、繋げたいオブジェクトを指定するだけです。

001

Unityの標準のジョイントは以下4つがあります。

  • Fixed Joint – 固定でつなげる
  • Hinge Joint – ドアのヒンジのようにつなげる
  • Sprint Joint – バネのようにつなげる
  • Configurable Joint – 上記3つを内包して、カスタマイズできる

まずは、Fiexed Joint, Hinge Joint, Spring Jointでいろいろ試してみて、もっと複雑なものが必要になったらConfigurable Jointを試せばよいでしょう。

A04

このプロジェクトの配布

http://hajimete-program/games/SandboxJoint.zip