3Dアクションゲーム編
Lesson1 プレイヤーを実装しよう
ゲームに必須なプレイヤーを実装します。プレイヤーの実装を通して移動やジャンプ、当たり判定やオブジェクト同士の関連付けなどの基本的な処理に挑戦しましょう。
1-0
1-0 このパートで作るゲームについて
1
はじめに
この「3Dアクションゲーム編」ではUnityの基本操作を中心に勉強していきます。Unityの操作や基本的なスクリプトの書き方を学習していきましょう。
どんなゲームができるかイメージできるように、アクションゲーム編のメインページに完成品のゲームをアップしています。まずは遊んでみてください。

1-1
1-1 新規プロジェクトを作る
1
新規プロジェクト
それでは3Dアクションゲームを作るためのプロジェクトを追加します。
Unity Hubのプロジェクトから「新しいプロジェクト」ボタンをクリックしてください。

今回作るのは3Dゲームですので、コアは「3D」を選択します。
上部からバージョンを変更できます。画像とバージョンが違うかもしれませんが、Lesson0でダウンロードした最新のバージョンで構いません。
プロジェクト名は自分がわかりやすい名前にしてください。名前を日本語にすると予期しない動作を起こす可能性があるので、英語にするようにしましょう。
選択できたら「プロジェクトを作成」ボタンをクリックしてください。

2
素材のインポート
ここで選んだ「2D」「3D」はあくまでUnityの編集モードなので、後から変更できます!それぞれ2Dゲームや3Dゲーム作成に適した設定に自動で設定してくれます。
モードによる具体的な違いは公式マニュアルを参照してください。
Unity Tips!

しばらく待つとUnityの画面が開きます。この画面を中心にゲームを作っていくことになります。

次にゲームに使用する素材をまとめたパッケージをインポートします。
配布所(https://drive.google.com/file/d/1CWTRhDRR5bXsEAvWAdU49cLXy_nFcP3s/view?usp=share_link)からUnityのパッケージをダウンロードしてください。
「UC_Run_Package.unitypackage」をダウンロードできたら、パッケージをUnityの下部にあるProjectウィンドウへドラッグ&ドロップします。

全てにチェックが入った状態で「Import」を選択します。

3
メインシーンへ
切り替える
素材のインポートができたら、画面下部のProjectウィンドウにModelとSpriteフォルダが追加されます。
Scenesフォルダを選択して「Main」をダブルクリックしてください。画面的にはあまり変わっていませんが気にしないでください。


4
画面の解像度を
変更
次にゲームを再生した時の画面サイズ(解像度)を設定しておきましょう。初期状態ではスクリーンの大きさに合わせて自動でゲームの画面サイズが変わるのですが、今回は作りやすいように16:10に設定しようと思います。
画面中央にある「Game」タブを選択してください。

Free Aspect を選択するとプルダウンメニューが開きます。16:10 Aspect を選択してください。

ここで選んだサイズはあくまでエディタ上でのサイズで、ビルド(出力)した際の画面サイズは別の場所で設定します。とりあえず今はあまり気にしなくてOK!(詳細はLesson6-5)
Unity Tips!

これで制作の準備が完了しました。
次からはメインとなるアクションゲームの部分を作っていきます。
1-2
1-2 地面とプレイヤーを追加
1
Unityの画面の
見方
制作を始める前にUnityの画面について少し説明しておきます。

① ヒエラルキー … ゲーム空間上にあるオブジェクトの一覧
(プレイヤーやカメラ、ギミックなど)
② インスペクター … 選択中のオブジェクトの情報が表示される
③ プロジェクト … ゲームに使用する素材の一覧
④ シーン … オブジェクトを配置していくゲーム空間
それぞれをヒエラルキーウィンドウ、インスペクターウィンドウ、プロジェクトウィンドウ、シーンビューなどと呼ぶこともあります。使っているうちに覚えると思いますが、どのウィンドウも超重要なので場所と用途を確認しておいてください。
以降の解説では各ウィンドウを「ヒエラルキー」や「インスペクター」などと呼称します。
2
箱を作る
各ウィンドウの位置は自由に変更できるので、人によっては配置が違うこともあります。解説ではデフォルトの配置を使用していますが、使いやすい配置に動かしても構いません。
右上のボタンでウィンドウ配置を変更、リセットすることもできます。

Unity Tips!

長くなりましたが、ようやくメインのアクションゲーム制作に入ります。
早速プレイヤーを追加したいところですが、今のゲーム空間上には地面がないので先に地面を追加しましょう。
ヒエラルキー(Hierarchy)にある+ボタンをクリックするとゲーム空間上にオブジェクトを追加できます。
「3D Object」→「Cube」を選択してゲーム空間上に箱を追加してみてください。

シーン(Scene)に白い箱が表示されました。
Unityではこのように簡単な箱や球を作成することができます。

ヒエラルキー内を右クリック、または上部にある「GameObject」をクリックしてもゲーム空間上にオブジェクトを追加することができます。好きな方法で構いません。
Unity Tips!


3
箱を動かす
左上のヒエラルキーに「Cube」の項目が追加されているのでクリックしてみてください。
すると右側のインスペクター(Inspector)に選択したCubeの情報が表示されます。

k2Engineと同じようにUnityでもPosition(座標)、Rotation(回転)、Scale(大きさ)でオブジェクトを扱います。
試しにCubeを動かしてみましょう。
インスペクター内のPositionに好きな値を入力してみてください。

UnityにおいてXは左右、Yは上下、Zは奥行きを表します。Unreal EngineではZが上下になっているのでややこしいですが、間違えないように注意しましょう。
座標、回転、大きさの編集はシーン内でもできます。シーン内のボタンを選択して移動モードにしてから、箱を自由に動かしてみてください。3dsMaxと似た感覚で動かせると思います。
シーン内のカメラも操作できますので同時に練習してみましょう。
マウスの右を押しながらマウスを動かすと、カメラを回転できます。マウスのホイール操作でカメラの画角を変更できます。マウスの中(真ん中、ホイールの部分)を押しながらマウスを動かすとカメラを移動できます。

4
箱を配置する

移動ツールの切り替えはショートカットキーでも行えます。
一番上のシーンビュー操作ボタンをQキーとして、移動はWキー、回転はEキー…と順番に割り振られています。
Unityには便利なショートカットがたくさんあります。ここでは紹介しきれませんが、ぜひ色々調べてみてください。
Unity Tips!

箱を使ってUnityの操作方法に一通り慣れたら、この箱を地面として使える位置に設定しましょう。
Cubeを選択した状態でヒエラルキー内の座標、回転、大きさを設定してください。
Positionは全て0、Rotationは全て0、ScaleのXは10、Yは1、Zは10です。

オブジェクトの名前もヒエラルキーから変えることができます。わかりやすいように「Ground」に変更しておきましょう。


5
プレイヤーを
配置する
地面ができたのでプレイヤーとなるユニティちゃんを配置します。
プロジェクトから「Assets」→「Model」→「unity-chan!」
→「Unity-chan! Model」→「Art」→「Models」
と選択して、ユニティちゃんのモデルをシーンにドラッグ&ドロップしてください。


すると、シーン内にユニティちゃんの3Dモデルが追加されます。
ヒエラルキーから追加されたユニティちゃんのオブジェクトを選択して、地面と同じように座標を調整してください。
PositionはXは0、Yは0.5、Zは0です。

6
ゲームの
プレビュー
地面と床がゲーム実行時にどのように見えるか確認してみましょう。
中央上部にある三角のボタンを押すと、ゲームが実行されます。
今は何の動きもありませんが、ユニティちゃんと地面がゲーム空間に存在することを確認しておいてください。

ゲーム画面を確認できたら、もう一度再生ボタンを押してゲームを終了してください。
Unityはテストプレイしやすいようにゲームの実行中でもゲームの編集ができます。便利な機能ですが、この変更はゲームを終了すると元に戻ってしまう一時的な変更です。
「ゲームを編集したのに、実は裏でゲームが実行中で終了すると元に戻ってしまった!」ということが割と起こるので、悲劇を避けるためにも確認が終わったゲームはちゃんと終了する癖をつけることをオススメします。

評価テスト
1-3
1-3 プレイヤーの移動
【評価テスト】
1
フォルダの作成
このままではユニティちゃんを眺めるだけの画面なので、まずはユニティちゃんが動けるようにしましょう。
新しくAssets内に「Script」フォルダを作成しましょう。
プロジェクト内の適当な空間を右クリックして「Create」→「Folder」と選択してください。作成したフォルダの名前を「Script」に変更しましょう。
プロジェクト内で右クリックして「Create」という操作は今後も頻繁に使いますので、覚えておいてください。



名前を間違えた際は作成したフォルダを右クリックして「Rename」を選択することで変更できます。

2
スクリプトの作成
次はScriptフォルダ内に新しいスクリプトを作成します。
作成したScriptフォルダをダブルクリックして開き、先ほどと同じように適当な空間を右クリックして「Create」→「C# Script」を選択してください。
作成したスクリプトの名前は「Player」にしておいてください。

作成したスクリプトをダブルクリックするとVisual Studioが開きます。


3
移動のコードを
書く
ここからはVisual Studioを使ってC# Scriptを記述していきます。Lesson0でも解説した通りC#はC++と共通している部分の多い言語なので、身構えずにやっていきましょう。
今回はユニティちゃんがWASDキーで動けるようにします。下記の赤い部分のコードを入力してください。
※ Wixの文字コードの問題なのか、ここからコピペすると体裁が崩れるので注意
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float MoveSpeed = 0.01f;
void Start()
{
}
void Update()
{
// 移動速度を初期化
Vector3 move = Vector3.zero;
// 前後移動
if (Input.GetKey(KeyCode.W))
{
move.z += MoveSpeed;
}
if (Input.GetKey(KeyCode.S))
{
move.z += -MoveSpeed;
}
// 左右移動
if (Input.GetKey(KeyCode.D))
{
move.x += MoveSpeed;
}
if (Input.GetKey(KeyCode.A))
{
move.x += -MoveSpeed;
}
// 移動させる
transform.position += move;
}
}
4
スクリプトの
アタッチ
「Visual StudioでInputなどの予測変換が出ない!」という方は「UnityとVisual Studioの関連付けについて」を参考にしてください。
今後プログラムをたくさん書いていく上で予測変換が出ないとかなり辛いですので、早めに直しましょう。
Unity Error!

プログラムが書けたらCtrl+Sで保存してください。保存しないとUnity側に反映されないので、保存する癖をつけましょう。
保存できたらUnityに戻ってください。
【プログラムの解説】
・k2EngineでPlayer.hとPlayer.cppのように2つのファイルがありましたが、UnityではPlayer.csのように1つのファイルを扱います。戸惑うかもしれませんが少しずつ慣れていきましょう。
・Update関数はk2Engineと同じように毎フレーム自動で呼ばれる関数です。ほぼ同じものという認識で構いません。
・Input.GetKey関数でキーが押されているか取得することができます。引数にはキーの番号を入力しますが、KeyCode.(キー名)で取得することができます。
・transform.position で自身のワールド座標を取得したり、変更することができます。
さて、せっかく書いたスクリプトですが、ユニティちゃんにアタッチしないと意味がありません。
Playerスクリプトをヒエラルキーのユニティちゃんにドラッグ&ドロップしてアタッチします。

ユニティちゃんを選択した状態で「Add Component」からPlayerと検索してアタッチすることもできます。やりやすい方法で構いません。


Unity Tips!

スクリプトをアタッチしようとして Can't add script というエラーが出た人は、プロジェクト内でスクリプトの名前をRenameした人だと思います。
プロジェクトで表示されている名前と実際のクラス名が異なるとエラーが出てしまうので、クラス名を修正してください。


Unity Error!

ユニティちゃんにPlayerスクリプトをアタッチできたら、上部の再生ボタンを押してWASDキーで移動できることを確認してみてください。
ユニティちゃんの移動速度を表す変数MoveSpeedはpublicに設定されています。
publicに設定した変数はインスペクター内に表示され、いつでも自由に変更することができます。ゲームを微調整する時にも便利なので覚えておいてください。


1-4
1
重力を加える
異なるオブジェクトにスクリプトをアタッチ、または複数アタッチしてしまった場合は、右上にある三つの点のボタンをクリックした後「Remove Component」を選択してください。

ちなみに「スクリプト」はクラスを格納した設計図で、それをオブジェクトにアタッチすることで実体を持った「コンポーネント」になります(ややこしいので無理に理解しなくてもOK)
Unity Tips!

ゲームの実行時に「All compiler errors have to be fixed before you can enter playmode!」というエラーが表示されてゲームを開始できない場合、プログラムのどこかに問題があります。

その場合、Consoleタブを開くとエラーが起きている個所と内容を確認することができます。
エラー文をダブルクリックしてみてください。

エラー文をダブルクリックするとVisual Studioに切り替わり、エラーが起きている個所を確認することができます。今回は文末に「;」が抜けていたようです。

エラーが起きた時は慌てず、どこでどういったエラーが起きているか確認するようにしましょう。ゲームの実行中にエラーが起きた場合も同様です。
エラーについてはLessonEX「よくあるエラー」にもまとめてあります。参考にしてください。
Unity Error!

1-4 重力と当たり判定
さて、移動のスクリプトを書いたことでユニティちゃんが移動できるようになりましたが、足場からはみ出しても落下しません。

このままでは物理的におかしいので、ユニティちゃんに重力を加えてみましょう。
ユニティちゃんのインスペクターから「Add Component」を選択して「Rigidbody」を検索してアタッチしてください。

このRigidbodyがアタッチされたオブジェクトには重力が適用されます。ゲームを実行してどうなるか確認してみてください。
ユニティちゃんが落下するようになっています。

2
当たり判定を
つける
確かに重力は適用されましたが、ユニティちゃんが地面を貫通して落下してしまいました。
ユニティちゃんが床を貫通する原因は、ユニティちゃんに当たり判定が設定されていないからです。

Unityには様々な形のCollider(コライダー)があります。コライダーをアタッチすることで当たり判定を取ることができます。

ちなみに地面(Cube)には最初からBox Colliderがアタッチされています。
後はユニティちゃんにコライダーをアタッチすれば、双方で当たり判定が取れます。

Unity Tips!

プレイヤーにアタッチするコライダーとしてメジャーなのはCapsule Colliderになります。名前通り薬のカプセルのような形をしたコライダーです。
なぜCapsule Colliderが適しているのかというと、Box Colliderと比べて小さな段差に引っかからないから、というのが大きな理由になります。
それではRigidbodyの時と同じように、Capsule Colliderをアタッチしてみましょう。

3
当たり判定の調整
4
回転の制限
コライダーのサイズや位置がモデルと噛み合っていないので、以下のように設定してみてください。ユニティちゃんを覆うように当たり判定が設定されます。
Center : X=0 Y=0.8 Z=0
Radius : 0.3
Height : 1.6


ユニティちゃんにRigidbody(重力)とCollider(当たり判定)をアタッチしたことで物理的な動作ができるようになりました。
実際にプレイして、「ユニティちゃんが地面に着地すること」と「地面からはみ出ると落下すること」を確認してみてください。
これで重力と当たり判定は完成…としたいところですが、実は少し不完全です。
しばらく移動すると…

ユニティちゃんが倒れてしまいました。このままだと起き上がれない亀のようで悲しいので、ユニティちゃんの回転に制限をかけましょう。
Y軸周りの回転は後で実装するので、X軸とZ軸周りの回転だけできないようにします。
Rigidbodyを選択してConstraintsのFreeze Rotationから、X軸とZ軸の回転を制限してください。

Freeze Rotationのチェックが入っている軸周りの回転は起こらなくなります(スクリプトで回転させることは可能)
1-5
※ このTIPSの内容はやや上級者向けのため、難しい人は飛ばして構いません
インスペクターでCubeの情報を見た時、何か既視感を感じた人はいるでしょうか。

座標や回転を格納する変数があって、Mesh Rendrerという絵描きさんがいて…
この状況は、k2Engineで行ってきたゲームの作り方と酷似しているのです。

Unityであったとしても、モノを描画するときには座標(描画する場所)とRenderer(絵描きさん)が必要であるという点は変わりません。k2Engineと違うのは「座標の更新や描画をUnity側が勝手に行ってくれる」という点だけです。
そして、今回のレッスンで新しいコンポーネントを追加しましたが、これをC++で書くとこうなります。

元々k2EngineはUnityを参考に作られています。
k2Engineを使って作ったゲームとUnityで作ったゲームのプログラムを見比べて、共通している個所を探すと、より双方への理解が深まるでしょう。
Unity Tips!

1-5 進行方向に回転する
次はユニティちゃんが進行方向に回転するようにしましょう。
Playerスクリプトを開いて、Updateの最後に赤い部分のコードを追加してください。
~中略~
// 移動させる
transform.position += move;
// 回転
if (move.sqrMagnitude > 0.0f)
{
transform.rotation = Quaternion.LookRotation(move.normalized);
}
}
}
【プログラムの解説】
・Vector3.sqrMagnitudeはベクトルの長さを返す関数で、k2Engineで今まで使ってきたVector3.Lengthと同じように使えます。
ここではプレイヤーが移動しようとしているかどうかをベクトルの長さを用いて判定しています。「移動用ベクトルの大きさが0より大きい」ということは「移動しようとしている」ということなので、回転処理を実行しているわけです。
・Quaternion.LookRotationは引数にしたベクトルの方向に回転するクォータニオンを作成する関数です。この処理では移動しようとしている方向のベクトルを引数として渡すことで、進行方向へ回転するようにしています。
スクリプトの変更をCtrl+Sで保存するのを忘れないようにしてください。
保存できたらゲームを実行して、ユニティちゃんが進行方向に回転することを確認してください。

評価テスト
1-6
1-6 ジャンプの実装
1
上方向に
力を加える
次はユニティちゃんがジャンプできるようにしましょう。少し難しい内容ですが、超重要なプログラムがたくさん出てくるパートなので心して(?)挑んでください。
RigidbodyにはAddForceという関数があり、引数に設定したベクトル分の力をオブジェクトに加えることができます。サッカーボールを蹴る瞬間にボールに大きな力を加えるようなイメージです。

今回はスぺースキーが押された瞬間にユニティちゃんに上方向へ強い力を加えることでジャンプしてみましょう。AddForceを実行するためには自分(プレイヤー)にアタッチされているRigidbodyにアクセスする必要があります。
Playerスクリプトを開いて赤い部分のコードを追加してください。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float MoveSpeed = 0.01f;
public float JumpPower = 6.0f;
Rigidbody m_rigidBody;
void Start()
{
// 自分にアタッチされているRigidBodyを取得する
m_rigidBody = GetComponent<Rigidbody>();
}
~中略~
// 回転
if (move.sqrMagnitude > 0.0f)
{
transform.rotation = Quaternion.LookRotation(move.normalized);
}
// ジャンプ
if (Input.GetKeyDown(KeyCode.Space))
{
m_rigidBody.AddForce(new Vector3(0.0f, JumpPower, 0.0f),
ForceMode.VelocityChange);
}
}
}
書いてもらったプログラムには重要な処理がたくさん含まれています。一気に全て理解するのは難しいかもしれませんが、少しずつ覚えていってください。
【プログラムの解説】
・publicがついていない変数m_rigidBodyはprivateな変数として扱われます。
privete Rigidbody m_rigidBody と宣言しても構いません。privateなので外部からこの変数にアクセスできません。
この変数から自分にアタッチされているRigidbodyにアクセスします。
今までC++をたくさん書いてきた方は Rigidbody m_rigidBody; にアクセスするというと「ん?」と思ったかもしれません。C#ではRigidbody* m_rigidBody; のように書くのではなく*抜きでインスタンスを格納する変数を宣言します。これがポインタみたいなものと思ってください(厳密には違いますが…)
今はよくわからないかもしれませんが、この教材でたくさん使うのでゆっくり慣れていきましょう。

・Start関数もk2Engineと同じようにUpdateが実行される前に1度だけ実行されます。オブジェクトの初期化や取得を行うのに適しています。
・GetComponent関数は対象となるゲームオブジェクトからコンポーネントを取得する関数です。恐らくUnityを理解する上で 最初の関門になる関数 かつ 一番使う関数 になると思われます。
逆に言うとこれを理解できればUnityでゲームを作れるようになります。この教材でもたくさん使うので、頑張って覚えていきましょう。

・GetKeyDown関数は"押された瞬間"のみtrueを返す関数です。
移動に使用したGetKey関数は押されている間常にtrueを返す点で違いがあります。
・AddForce関数の第一引数には力を加える方向を渡します。その場で変数を生成する方法が今までと少し違うので注意してください。
第二引数には力を加えるモードを渡します。今回は瞬間的に力を加えるモードです。今はあまり気にしなくて構いません(このサイトが詳しく解説してます)
スクリプトの変更を保存するのを忘れないようにしてください。
保存できたらゲームを実行して、スぺースキーでユニティちゃんがジャンプすることを確認してみてください。

2
ジャンプの
条件を考える
これでジャンプできるようになりました…が、空中でスぺースキーを連打してみてください。ユニティちゃんが空中でも無限にジャンプできてしまいます。このままではゲームが成り立ちません。ジャンプに制限を実装しましょう。
2段ジャンプなどは今回実装しないので、まずユニティちゃんがジャンプできる条件は何か考えてみてください。
こう言われて「ユニティちゃんが地面に触れている時」と答えた方は惜しいです。
正確には「ユニティちゃんの足元が地面に触れている時」ジャンプできるという処理が正しいです。
ただ「地面に触れていたら」という判定だと、例えば壁や階段に横から触れていてもジャンプできてしまい、完全なジャンプ制限になりません。



屁理屈のような話で混乱したかもしれません。ただプログラム的には壁や階段も「地面」なのです。なので「ユニティちゃんの"足元が"地面に触れている時」というように、触れている場所を指定する必要があります。
この問題を解決する方法は色々あるのですが、今回は「ユニティちゃんの足元に接地判定用の当たり判定を追加する」方法をとります。
「接地判定用の当たり判定が地面に触れていたら」ジャンプできるようにしましょう。
3
接地判定用
オブジェクト作成

長くなりましたが、まずは接地判定用のオブジェクトを追加しましょう。なぜ新しいオブジェクトを作るのかは後でわかるので、今は気にしないでください。
ヒエラルキー内の+ボタンをクリックして「Create Empty」を選択してください。

当たり判定用の空オブジェクトが追加されました。空オブジェクトは座標や回転などの基礎的なデータを持っているだけの透明なオブジェクトです。
オブジェクトの名前は自分がわかりやすいもので構いません。この解説では「GroundCheck」という名前にしておきます。座標は後で調整するので今はどこでもOKです。

4
接地判定の
当たり判定を追加
このままでは当たり判定としては機能しないので、コライダーを追加しましょう。
今回はSphere Colliderを使用します。Add Componentから検索してSphere Colliderをアタッチしてください。


Sphere Colliderのパラメータを調整してください。
Is Triggerにチェックを入れ、Radius(半径)を0.1に設定します。

Is Triggerにチェックを入れたコライダーは物理的接触が起こらなくなります。しかし「衝突しているかどうか」を取得することは可能です。
例えばマップの移動ポイントやアイテムの取得判定で、触れたプレイヤーが弾かれると困りますよね?衝突しているかどうかは判定したいけど物理的に衝突してほしくないという時にはIs Triggerにチェックを入れましょう。

5
親子関係を構築
Is Triggerにチェックが入っていないコライダー同士の衝突は貫通せず、どちらか(または両方)のコライダーのIs Triggerにチェックが入っている場合は貫通します。重要なことなので覚えておいてください。
これで接地判定用オブジェクトの準備はできましたが、今のままではプレイヤーについてきてくれません。接地判定用オブジェクトは常にプレイヤーの足元にないと困ります。
ここでプログラムを書いてプレイヤーへの追尾を実装してもよいのですが、Unityにはもっと簡単な方法があります。
ヒエラルキー内で接地判定用オブジェクト「GroundCheck」をユニティちゃんにドラッグ&ドロップしてください。


ドラッグ&ドロップしたことで接地判定用オブジェクトがユニティちゃんの下に移動しました。
この状態を「親子関係」といいます。今はGroundCheckがunitychanの子になった状態です。
子になったオブジェクトは親オブジェクトの座標・回転・大きさが変化すると追従するように変化します。例えば親オブジェクトが右に移動すれば、全ての子オブジェクトも同じように右に移動します。しかし、子が移動しても親の座標は変化しません。

接地判定用オブジェクトの座標を全て0に設定してください。オブジェクトがユニティちゃんの足元に移動します。

試しにユニティちゃんがジャンプしている時のGroundCheckの位置を確認してみると、しっかり足元にあることが確認できます。これで接地判定オブジェクトの準備ができました。
実際に確かめたい人はユニティちゃんのジャンプ中に上部の一時停止ボタンをクリックして、シーンから確認してみてください。Ctrl+Shift+Pでもゲームを一時停止できます。
ちなみに、再生ボタンの一番右のボタンでゲームを1フレームずつ実行(コマ送り)にすることもできます。Ctrl+Alt+Pでも可能です。

Unity Tips!


6
接地判定の
スクリプト作成
それでは接地判定をするプログラムを書いていきます。
Playerスクリプトを作った時を参考に、新しいスクリプト「GroundChecker」を作成してください。

GroundCheckerを開いて、以下のプログラムを入力してください。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GroundChecker : MonoBehaviour
{
// 接地しているかを格納する変数
bool m_isGround;
// 地面に触れているかを返す関数
public bool GetIsGround()
{
return m_isGround;
}
// 毎フレーム最初に接地判定をリセットする
private void FixedUpdate()
{
m_isGround = false;
}
// 自身に何かが衝突している間呼ばれる
private void OnTriggerStay(Collider other)
{
// 地面のタグが付いたオブジェクトに衝突している
if (other.CompareTag("Ground"))
{
m_isGround = true;
}
}
}
6
タグを設定
【プログラムの解説】
・publicにした関数は外部から呼ぶことが可能です。この辺りはC++と同じです。
今回は接地しているかどうかのフラグを返す関数を作成しています。
・FixedUpdate関数は一定間隔で呼ばれる関数です。下記のOnTriggerStay関数やUpdate関数よりも早く呼ばれるという特徴もあります。
・OnTriggerStay関数は、このスクリプトがアタッチされたオブジェクトのコライダー(Is Triggerにチェックが入っている)に何かが衝突している間毎フレーム呼ばれる関数です。
CompareTagはゲームオブジェクトの関数で、オブジェクトについているタグが引数と一致していた場合にtrueを返す関数です。
「タグ」はそれぞれのオブジェクトに設定でき、プレイヤーや敵、地面などを区別する時に使います。

例えばスライム、ゴブリン、ドラゴンなど様々なモンスターがいたとしましょう。
モンスターそれぞれに同じ「Enemy」というタグをつけることで、まとめて「敵」として分類することができるようになります。これによって「自分がEnemyタグのついた当たり判定に触れたら、ダメージを受ける」といった処理を作ることができます。
「スライムに触れたらダメージを受ける」「ゴブリンに触れたらダメージを受ける」…といった実装をするより、タグで判定する方がずっと簡単で効率的です。何より後から新しいモンスターを追加したとしても、Enemyタグをつけるだけで同じように処理できる点が便利です。
タグはUnityでの開発を効率化する便利なツールなので、少しずつ覚えていくようにしましょう。

Unity Tips!

今回は地面に「Ground」というタグをつけてみましょう。
インスペクター上部にあるTagをクリックして「Add Tag」を選択してください。

+ボタンをクリックして新しいタグ「Ground」を追加してください。タグの検索は名前が完全一致しないといけないので、間違えないように注意してください。

地面のオブジェクトを選択して、Groundタグを設定してください。


7
ジャンプ処理に
条件を設定する
8
接地スクリプトを
アタッチする
あと少しです。Playerスクリプトを改造して、接地判定ができるようにしましょう。GroundCheckerのGetIsGround関数を呼び出して、ユニティちゃんが接地しているかどうかを取得します。
Playerスクリプトに赤い部分のコードを追加してください。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float MoveSpeed = 0.01f;
public float JumpPower = 6.0f;
Rigidbody m_rigidBody;
// 接地判定用スクリプト
public GroundChecker Ground_Checker;
void Start()
{
// 自分にアタッチされているRigidBodyを取得する
m_rigidBody = GetComponent<Rigidbody>();
}
~中略~
// 回転
if (move.sqrMagnitude > 0.0f)
{
transform.rotation = Quaternion.LookRotation(move.normalized);
}
// ジャンプ
if (Input.GetKeyDown(KeyCode.Space) && Ground_Checker.GetIsGround())
{
m_rigidBody.AddForce(new Vector3(0.0f, JumpPower, 0.0f),
ForceMode.VelocityChange);
}
}
}
スクリプトを保存したら、Unityに戻ってください。
ユニティちゃんにPlayerをアタッチした時と同じように、接地判定用オブジェクトにGroundCheckerをアタッチしてください。

9
プレイヤーと接地
判定を繋げる
先ほど書いたプログラムで public GroundChecker Ground_Checker; という宣言をしていました。以前説明した通り、publicにした変数はインスペクターに表示されます。
GroundCheckerとPlayerを関連付けしましょう。GroundCheckを新しく増えたGround_Checkerにドラッグ&ドロップしてください。これで2つのコンポーネントが繋がりました。


長くなりましたがようやく接地判定の完成です。ゲームを実行して、空中での無限ジャンプができなくなっていることを確認してみてください。
ジャンプの実装だけでUnityにおいて重要なことをたくさん説明しました。オブジェクトに力を加える関数、タグや親子関係、コンポーネントの取得やIsTriggerの使い方など、どれもゲームを作る上で欠かせないことばかりです。
一度に全てを覚えるのは難しいですが、いずれも今後のレッスンでもちょくちょく出てくる内容なので、わからなくなったときはこのレッスンを見返してみてください。
1-7
1-7 プレイヤーのアニメーション
1
アニメーション
フォルダを作成
プレイヤーの実装もあと少しです。ユニティちゃんがTポーズで動き回るのは悲しいので、プレイヤーのアニメーションを実装しましょう。
新しいフォルダ「Animation」を作成してください。フォルダの作り方は1-3を参考にしてください。

2
Animatorを作成
Animationフォルダ内にAnimator Controllerを作成してください。

3
Animatorを
プレイヤーに設定
Animator Controllerには自分のわかりやすい名前をつけておいてください。
(解説では「PlayerAnimation」にしています)
作成したAnimator Controllerをヒエラルキー内のユニティちゃんにドラッグ&ドロップしてください。
ユニティちゃんのAnimatorに自動で設定されます。


4
アニメーション
ステートを作成
ここからはAnimator Controllerの設定をしていきます。Animator Controllerをダブルクリックしてください。アニメーターが開きます。

まずは右側のBase Layer内を右クリックして「Create State」→「Empty 」を選択してください。

ステートもオブジェクトと同じようにインスペクターでパラメータを管理することができます。
作成したステートの名前を「Idle」にしてください。

Idleと同じように「Run」「Jump」のステートも作成してください。ステートの場所は自分が見やすい場所で構いません。

5
アニメーションを
設定
次は各ステートにアニメーションを設定します。
Motion横の+ボタンをクリックしてアニメーションを選択してください。同梱されているアニメーションの中には似た名前のものもあるので注意しましょう。
【設定するアニメーション】
Idle →「WAIT00」
Run →「RUN00-F」
Jump →「JUMP00B」



6
パラメータを作成
ステートの遷移条件となるパラメータを設定します。パラメータにもBoolやFloatといった馴染みのある型が使えるので、用途に応じて選んでください。今回はBool型のみ使用します。
Bool型の「MoveFlag」「JumpFlag」というパラメータを追加してください。


7
アニメーションを
繋げる
ステートとパラメータの設定が終わりましたが、これだけではアニメーションは切り替わりません。それぞれのステートが遷移する条件を設定する必要があります。
例えばMoveFlagがfalseの時はIdleアニメーションを実行して、MoveFlagがtrueの時はRunアニメーションを実行する、といった感じです。
各ステートを繋げる時は、遷移前のステートを右クリックして「Make Transition」を選択します。すると遷移前のステートから矢印が伸びるので、遷移先のステートをクリックすることで矢印を繋げられます。
IdleステートからRunステートに繋がる遷移を作ってください。


8
遷移条件を設定
新しく伸びた矢印をクリックするとインスペクターにパラメータが表示されます。
インスペクターでは「アニメーションが終了してから遷移するか」「どのくらいの時間をかけて遷移するか(補間時間)」といったアニメーションの設定や、「パラメータがどうなったら遷移するか」といった遷移条件を設定することができます。
この矢印を各ステートに伸ばしていきます。6本あるのでややこしいですが、特に遷移条件に注意して設定してください。
【IdleとRun】


【IdleとJump】
Idle→Jumpは他と少し違うので注意してください。
Jump→Idleは遷移条件が2つあります。


【JumpとRun】
Jump→Runは遷移条件が2つあります。


9
スクリプトで
パラメータを操作
Has Exit Timeにチェックが入っていると、アニメーションがExit Timeのところへ進むまで遷移を待ってくれます。例えばExit Timeを1にしておくと、アニメーションが一通り終わるまで遷移を待ってくれるようになります。今回は必要ないのでチェックを外しています。
Transition Durationの値は遷移を補間する時間です。簡単に言うと「アニメーションを滑らかに切り替えるためにかける時間」です。値を0にするとアニメーションがパキッと切り替わるようになります。
これでアニメーターの設定は完了です。
後はスクリプト内でパラメータを操作するだけでアニメーションが切り替わります。
Playerスクリプト内でアニメーションを管理したいところですが、移動やジャンプの処理と混ざるとわかりにくくなるので関数で分割しています。
長いですが以下のように入力してください。微妙に変わっている部分もあるので、自身のない人は一度消して最初から打ち直すことをオススメします。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float MoveSpeed = 0.01f;
public float JumpPower = 6.0f;
Rigidbody m_rigidBody;
Animator m_playerAnimator;
// 接地判定用スクリプト
public GroundChecker Ground_Checker;
bool m_moveFlag, m_jumpFlag, m_airFlag;
void Start()
{
// 自分にアタッチされているRigidBodyを取得する
m_rigidBody = GetComponent<Rigidbody>();
// 自分にアタッチされているAnimatorを取得する
m_playerAnimator = GetComponent<Animator>();
}
void Update()
{
// 移動やジャンプ
Action();
// アニメーション
Animation();
}
private void Action()
{
// 移動速度を初期化
Vector3 move = Vector3.zero;
// 前後移動
if (Input.GetKey(KeyCode.W))
{
move.z += MoveSpeed;
}
if (Input.GetKey(KeyCode.S))
{
move.z += -MoveSpeed;
}
// 左右移動
if (Input.GetKey(KeyCode.D))
{
move.x += MoveSpeed;
}
if (Input.GetKey(KeyCode.A))
{
move.x += -MoveSpeed;
}
// 移動させる
transform.position += move;
// 移動フラグの更新
if (move.sqrMagnitude == 0.0f)
{
m_moveFlag = false;
}
else
{
m_moveFlag = true;
}
// 回転
if (move.sqrMagnitude > 0.0f)
{
transform.rotation = Quaternion.LookRotation(move.normalized);
}
// ジャンプ
if (Input.GetKeyDown(KeyCode.Space) && Ground_Checker.GetIsGround())
{
m_rigidBody.AddForce(new Vector3(0.0f, JumpPower, 0.0f),
ForceMode.VelocityChange);
m_jumpFlag = true;
}
}
private void Animation()
{
// 移動フラグ
m_playerAnimator.SetBool("MoveFlag", m_moveFlag);
// ジャンプフラグ
if(m_jumpFlag && m_airFlag == false &&
Ground_Checker.GetIsGround() == false)
{
m_airFlag = true;
m_playerAnimator.SetBool("JumpFlag", true);
}
if(m_jumpFlag && m_airFlag &&
Ground_Checker.GetIsGround())
{
m_airFlag = false;
m_jumpFlag = false;
m_playerAnimator.SetBool("JumpFlag", false);
}
}
}
【プログラムの解説】
・Start関数内で自身にアタッチされているAnimatorを取得しています。取得している対象が違うだけでRigidbodyの時と同じです。
重ね重ね言いますがGetComponentは超重要関数ですので、少しずつでも使い方を覚えていきましょう。
・Animatorの関数にSetBoolやSetFloatなどがあります。この関数で先ほど設定したパラメータ(MoveFlagなど)を操作することができます。
第一引数に操作したいパラメータの名前、第二引数に値を入力します。
ジャンプ関連でちょっとややこしい処理を挟んでいますが、これはジャンプした瞬間に接地していると判定されてジャンプアニメーションがキャンセルされないように、一度地面から離れてから再度地面に触れたらJumpFlagをfalseにするという処理を行っています。無理に理解しなくてもOKです。
これでアニメーション処理は完成です。
長かったですがこれでユニティちゃんが走ってジャンプできるようになりました。ゲームを実行して確認してみてください。
次のレッスンでは追尾・回転するカメラを実装しましょう。


まとめ
・Unityでのゲーム制作において重要なウィンドウ
① ヒエラルキー … ゲーム空間上に存在するオブジェクトの一覧
② インスペクター … 選択中のオブジェクトの情報が表示される
③ プロジェクト … ゲームで使用する素材置き場
④ シーン … オブジェクトを配置するゲーム空間
・Unityのスクリプトではオブジェクトの座標を transform.position で取得できる
・public をつけた変数はインスペクターに表示され、値を調整することができる
移動速度やジャンプ力など、後から微調整をするような値に設定すると便利
・「Add Component」でオブジェクトに様々なコンポーネントを追加できる
Rigidbodyをアタッチすることでオブジェクトに物理演算を適用したり、Colliderをアタッチすることでオブジェクトに当たり判定を追加したりできる
・コンポーネントを取得するための関数 GetComponent (超重要関数!)
・ヒエラルキー内でオブジェクトを親オブジェクトにしたいオブジェクトにドラッグ&ドロップすることで、親子関係をつくることができる
親オブジェクトが動くと子オブジェクトは追従するが、子オブジェクトが動いても親オブジェクトは動かない
・Unityにはオブジェクトの種類を区別するためのタグという機能が存在する
評価テスト