入門書を2周3周してようやくUnityが体に馴染んできたので、練習がてら汎用性の高そうな2Dアクションゲーム向きの「静止(アイドリング)・歩く・ジャンプ(降下アニメーションあり)」ができるプレイヤーを作ってみようと思います(注:ロックバスターは打てません)。
プレイヤーの基本的な動きには和尚本でおなじみUnity2Dの超人気書籍「Unityの寺子屋(以下、和尚本2)」のサイドビューアクションのスクリプトを使用し、主にこの記事ではアニメーションでアレンジを加えてます。
なお書籍内のスクリプトは著作物なので部分的に引用するに留めます。和尚本2が手元にある前提で話を進めますのでその点ご了承ください。
完成形はこちら
先にこの記事の完成系をお見せしておきます。
スプライト(画像)変えれば色んなゲームで使えそうですよね!プレイヤーの動きのタイプ・操作感としては「ロックマン」っぽい感じ(あくまでっぽい)。
方向キー入力した瞬間から最高速度で移動速度は常に一定、空中でも地上と同じように左右に移動できるのがポイントです。
今回使用したアセット(無料)
ロックマンの画像をパ◎るわけにはいかないので、それっぽいアセットを使いました。
スクリプトのサンプルはありませんが、プレイヤーだけでなく敵キャラやステージのスプライトも一通り揃っているのでこのアセットだけでもそこそこゲームが作れてしまいそうです。
今回は対応しませんがはしごの登り降り等のスプライトも含まれているので、さらに細かいアニメーションを加えようと思った時も活用できますね。
準備
本題に入る前にまずは準備から。
Playerゲームオブジェクトと動作確認用の簡易なステージ(床)を用意します。
床を適当に作成
とりあえず床がないと確認しようがないので先に適当に作っておきましょう。Boxcollider2Dコンポーネントをあててblockレイヤーに所属さえさせればどんなものでも◎。もちろん和尚本のステージを使ってもOKです。
自分はアセットに含まれていたスプライトを使ってこんな感じで用意しました。
Playerゲームオブジェクト作成
インポートしたSunny Landアセットのスプライトをシーンビューにドラッグ&ドロップしてプレイヤーゲームオブジェクトを作成します(和尚本読者なら説明不要ですね)。
注意点としては、今回使用したアセットは画像サイズがかなり小さいので、スプライトを選択してImport SettingsからPixels Per Unitを変更するかゲームオブジェクトのScaleを変更した方がいい感じになるかなと思います。自分はPixels Per Unitを100から「15」に変更して使用しました。
Pivotをbottomにするのも忘れずに。centerのままだと接地判定用のLineCastが地面(blockレイヤー)に届かずジャンプできません。
設定を変更したら静止状態のスプライト(player-idel-1)をシーンビューにドラッグ&ドロップしてPlayerゲームオブジェクトを作成します。名前も変えておくと良いと思います。自分は「Player」としました。
和尚本スクリプトをアタッチ
Playerゲームオブジェクトを作成したらに和尚本2のサイドビューアクションのPlayerManager.csスクリプトをアタッチしましょう。
Rigidbody2D、Collerider2Dコンポーネントのアタッチも忘れずに。Rigidbody2Dの設定はお好みで。コライダーは以下のような感じで設定しました。
どうでもいい話ですが個人的にFreeze Rotation Z固定するの忘れがちです。
↓よくこうなる。
キーボード入力で操作できて以下のようになっていればひとまずプレイヤーの準備は完了です。この状態にアニメーションを追加していきます。
「静止」と「歩く」のアニメーションを作成
さて、ようやくここからが本題です。
和尚本2でもプレイヤーは歩くスプライトアニメーション(walk@Player)が適用されていますが、プレイヤーが静止しているときと歩く時でアニメーションは共通でした(静止している時も歩くアニメが実行)。
これを静止(アイドリング)状態と歩くアニメーションをそれぞれ別に用意して適用していきます。
静止(アイドリング)状態のアニメーション追加
まずは静止状態のアニメーションクリップを作成します。
Playerゲームオブジェクトを選択した状態でメニューの「Window」>「Animation」>「Animation」からアニメーションクリップ作成画面を開き「Create」で作成します。クリップ名は今回は「idle@Player」としました。
あとはplayer-idel-1〜4の4枚のスプライトをクリップにドラッグ&ドロップしてアニメーションの間隔を適当に調整すればOKです。注意点としては最後のスプライトをもう一つ追加することを忘れないようにすることくらいでしょうか。
こうすることで1枚目が0〜0.1秒まで、2枚目が0.1〜0.2秒まで、3枚目が0.2〜0.3秒まで、4枚目が0.3〜0.4秒まで再生されまた0秒目に戻り1枚目…という感じでアニメーションします(最後のスプライトを続けないと0.3秒になった瞬間0秒のアニメーションに戻ってしまうので4枚目が省かれたような状態になってしまいます)。
↑こんな感じになっていればOKです!
同様に歩くアニメーションも作成
同様の手順で「歩く」アニメーションクリップも作成します。
同じゲームオブジェクトに新しいクリップを追加を追加したいときは左上のクリップ名(今だとidle@Player)が表示されている部分をクリックして「Create New Clip」を押します。
クリップ名は「walk@Player」としました。
あとは先ほどと同じように今度はplayer-run-1〜6のスプライトを使用してクリップを作成しましょう。
静止・歩くアニメーションをPlayerに反映
idle@Playerとwalk@Playerアニメーションクリップが作成できたら一先ずここまでのアニメーション同士の関係性・切り替え条件を設定して、作成した静止と歩くアニメーションをプレイヤーに反映していきましょう。
改めて書き出すまでもないですが今回は以下のような仕様にしていきます。
<切り替え条件を整理>
- 何も入力していないときは静止(idle@Player)アニメーションを再生
- 左右いずれかのキーが入力されているときは歩く(walk@Player)アニメーションを再生
- 左右のキーが離されたらwalk@Playerを終了。idle@Playerに戻る
アニメーターコントローラーの設定(静止・歩く)
アニメーターコントローラーを開くと以下のような状態になっているはずです(配置はちょっと変えてます)。
この状態だと「Entry」から「idle@Player」へのトランジションしかないので、プレイヤーは静止中も歩いてる時も変わらずidle@Playerが再生され続けます。
今回はidle@Playerとwalk@Playerを相互に行き来できるようにしたいので「idel@Player→walk@Player」「walk@Player→idle@Player」の2つのトランジションを作成します。
歩くアニメーションの開始・終了をスクリプトで切り替えるためにトランジションに当てるパラメータを作成します。今回はbool型のパラメータを使用します。名前は「isMove」としました。
パラメータを作成したらトランジションの設定をしていきましょう。
idle@Playerからwalk@Playerに伸びるドランジションは以下のように設定します。Conditions(アニメーション開始条件)に「isMove=true」を追加、Has Exit Timeのチェックを外しTransision Durationsも0にします(Has Exit TimeとTransision Durationsの設定は今後の全てのトランジションで同様の設定にします)。
続いてwalk@Playerからidle@Playerのトランジションを設定します。違う部分はConditionsの「isMove=false」だけです。
アニメーターコントローラーの設定(静止・歩く)はひとまずこれで終わり!
スクリプト修正(静止・歩く)
アニメーションの準備が出来たらあとは任意のタイミングでisMoveがtrue、falseに切り替わるようにスクリプトを修正します(追加しているのは2行)。
・・・中略・・・
void Update () {
・・・中略・・・
if (!usingButtons)
{
float x = Input.GetAxisRaw ("Horizontal");
if (x == 0)
{
moveDirection = MOVE_DIR.STOP;
animator.SetBool("isMove", false);
}
else
{
if (x < 0)
{
moveDirection = MOVE_DIR.LEFT;
}
else
{
moveDirection = MOVE_DIR.RIGHT;
}
animator.SetBool("isMove", true);
}
}
if (Input.GetKeyDown ("space"))
{
PushJumpButton ();
}
}
・・・以下略・・・
Updateメソッド内でキーの入力状況を監視しているので、左右のキー入力がない状態ではisMoveをfalse、ある場合はisMoveをtrueにしアニメーションを切り替えるようにしています。
以下のようにジャンプしているしてないに関わらず、左右に移動してなければidle@Player、左右に移動しているときはwalk@Playerアニメーションが再生されていればOKです。
だいぶそれっぽくなってきました!
これでアレンジの2/3は終了。最後に仕上げとしてジャンプと降下のアニメーションを追加していきます。
「ジャンプ」と「降下」アニメーションを追加
アニメーションが切り分ける条件は後ほど整理するとして、とりあえずジャンプ中と降下中のアニメーションクリップを作成してアニメーターコントローラーの設定まで済ませましょう。
アニメーションクリップ追加(ジャンプ・降下)
アニメーションクリップの作り方は静止や歩きと同様なので詳細は割愛。
ジャンプ用のアニメーションクリップにはplayer-jump-01を使用します。クリップ名は「jump@Player」とします。
降下用にはplayer-jump-02を使用します。こちらのクリップ名は「fall@Player」とします。
アニメーターコントローラー修正(ジャンプ・ 降下)
jump@Playerとfall@Playerが追加されているのでトランジションを以下のように追加しましょう(ちょっと配置変えてます)。
isMoveを追加したときと同じようにisJump、isFallというbool型のパラメータを追加で用意します。これはそれぞれジャンプ中か降下中かの判定用に使用します。
パラメータを用意したらこれまた先ほどと同様にトランジションの設定をしていきます。Any State→jump@Playerのトランジションは以下のようにします。条件の部分以外は先ほどと同様ですね。
あとはConditions以外の部分は同じです。各トランジションに以下のようにConditionsを追加しましょう。
これでアニメーションクリップとアニメーターコントローラーの設定は終了です。
「ジャンプ」「降下」アニメーションをプレイヤーに反映
最後にジャンプと降下のアニメーションを追加していきますが、その前にどういう条件でアニメーションが切り替えるのかを整理しておきます。
<切り替え条件を整理>
- ジャンプ可能な状態でジャンプボタンが押されたらジャンプ(jump@Player)アニメーションを再生
- ジャンプ中にプレイヤーのY方向の速度が0より小さくなったらジャンプアニメーションを終了。同時に降下(fall@Player)アニメーションを再生
- ジャンプ中または降下中にblockレイヤーに接したらそれぞれのアニメーションを終了しデフォルト(idle@Player)アニメーションを再生
こんな感じで良さそうです。
以上を踏まえてスクリプトを修正していきます。
スクリプト修正(ジャンプ・降下)
以下のように追記します。
・・・中略・・・
void Update () {
canJump = // ・・・以下略・・・
// ジャンプ・降下アニメーション切り替え
if (canJump && rbody.velocity.y == 0)
{
if (animator.GetBool("isJump")) animator.SetBool("isJump", false);
if (animator.GetBool("isFall")) animator.SetBool("isFall", false);
}
else if (!canJump && rbody.velocity.y < 0)
{
if (animator.GetBool("isJump")) animator.SetBool("isJump", false);
if (!animator.GetBool("isFall")) animator.SetBool("isFall", true);
}
・・・中略・・・
// ジャンプボタンを押した
public void PushJumpButton ()
{
if (canJump)
{
goJump = true;
animator.SetBool("isJump", true);
}
}
・・・以下略・・・
基本的には先ほど整理した通りなのですが、12行目の条件に「rbody.velocity.y < 0」を加えているのはジャンプ直後にLinecastがblockレイヤーに触れてジャンプアニメーションが終了してしまうのを防ぐためです。
つまりプレイヤーのY方向の速度が0以外のときはY方向の速度がマイナス(つまり落下中)のときのみisJumpをFalseにしジャンプアニメーションを終了しています。
完成!
ということで完成です!
こんな感じでちょっとしたステージを作るだけでもだいぶゲームっぽくなりますよね。
正直もう少しシンプルにできそうだなーという気もしますが、とりあえず今回はこんなかんじで。
結構汎用性もありそうなものが作れたし、いい勉強にもなりました。アニメーション周りもちょっと苦手意識あったんですがだいぶ慣れてきた気がします。
ちなみに今回はロックマンっぽいにチャレンジしましたが、マリオっぽいだとおもちゃラボさんの以下の記事がとても参考になります。難易度はこちら(マリオ)の方がグッと高くなりますが、とっても勉強になるので一度読んでみることをおすすめします。
Unityでマリオっぽいゲームを作るのに必要な5つのこと – おもちゃラボ
またそのうち+アルファ的な動作(ダッシュ、スライディング、はしごの上り下り、..etc)の追加なんかにも挑戦してみたいですね。
とりあえず今回はこんなところで!