
プレイヤーキャラクター:Part 2
Tutorial
Beginner
+0XP
60 mins
(192)
Unity Technologies

さて、ゲームのプレイヤーキャラクターの開発に時間をかけたので、キャラクターの動きを制御するカスタムスクリプトを作成できますね。
このチュートリアルでは、以下の作業を行います。
- Unity で新規スクリプトからアセットを作成する
- 生成されたデフォルトのスクリプトを触ってみる
- キャラクターの位置を変更するために使用できるベクトルの作成
- キャラクターに垂直および水平方向の動きと回転を適用する
- 変更した内容が Unity の物理演算システムで動作することを確認する
完了したら、John Lemon は足音を忍ばせてお化け屋敷を進んでいくことができるようになります。
1. プレイヤーキャラクターの続き
前回のチュートリアルでは、JohnLemon のプレハブをアニメーション化するシステムと、物理演算システムでキャラクターを動作させるコンポーネントを追加して、作業を開始しました。では、最初のスクリプトを使用してカスタムのコンポーネントを作成しましょう!
スクリプトとは何?
スクリプトとは、コンピューターに対する一連の指示を含んだテキスト文書のことです。これらの指示は、一般的にコードと呼ばれています。指示はコンピュータが理解できるように書かれており、この場合は C# (C Sharp)と呼ばれるプログラミング言語を使用します。
C# では、命令の書き方や使われる言葉の一部を定義しています。幸いなことに、使用されている言語は英語と同様に C# でも似たような意味を持つことが多いです。例えば、C# で最初に出てくる単語は "using" です。これは、それが書かれているスクリプトがどこか他の場所のコードを使っていることを意味します。もう一つの例は "public" です。これは何かにアクセスできるということを意味しています。ここでは例が多すぎて一つずつ説明していくことはできませんが、このチュートリアルでは、それらが出てきたときに、それぞれの例について触れていきます。
このプロジェクトで作成するすべてのスクリプトは、 MonoBehaviour からの派生形式になります。MonoBehaviour は、コンポーネントと同様にゲームオブジェクトにアタッチできる特殊なタイプのスクリプトです。これは、自分で書くことができるコンポーネントの特別なケースだからです。
スクリプトはプレハブと若干の類似点があります。
- スクリプトは、プレハブと同じようにアセットとして作成されます。
- スクリプトをコンポーネントとしてゲームオブジェクトに追加すると、実際にそのスクリプトがインスタンス化されます。これは、プレハブをシーンに追加すると、そのプレハブがインスタンス化されるのと同じです。
しかし、多くの点でスクリプトは大きく異なります。スクリプトに進んで、詳しく触れてみましょう!
2. 最初のスクリプトを作成する (PlayerMovement)
まず、新しいスクリプトをアセットとして作成します。
1.Project ウィンドウで Assets > Scripts の順に選択しフォルダーを見つけます。フォルダー上を右クリックして、 Create > C# Script の順に選択します。スクリプトに "PlayerMovement" という名前を付けます。
注意: コンポーネントとして使用するスクリプトは、スクリプト自体の クラス名 と同じ名前をアセットに付ける必要があります。Unity がスクリプトファイルを作成するとき、アセットに最初に付けられた名前と一致するクラス名がファイルに付けられます。ただし、アセット名を変更しても、クラス名は変わりません。
2.スクリプトを選択し、Inspector ウィンドウを確認します。以下のコードが表示されるはずです。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
// Start は最初のフレーム更新の前に呼び出される
void Start()
{
}
// Update はフレームごとに 1 回呼び出される
void Update()
{
}
}
「public class PlayerMovement」 で始まる行を見つけます。これがクラス名を定義するものです。スクリプトに PlayerMovement と表示されていない場合は、スクリプトアセットを削除して、PlayerMovement という名前の新しいアセットを作成します。
3.これでスクリプトアセットが作成できたので、編集するためにそのスクリプトを開きます。アセットをダブルクリックするか、Inspector ウィンドウの "Open…" ボタンをクリックします。
スクリプトの編集は Unity の中では行われません。その代わり、スクリプトは Visual Studio と呼ばれる別のプログラムで開きます。それを開くと、スクリプトを編集できるようになります。
デフォルトのスクリプトに触れてみる
Visual Studio で見られるようになったデフォルトのスクリプトを一つずつ見ていきましょう。
1.最初の 3 行のコードは以下のようになります。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
これらは using ディレクティブ と呼びます。これにより、他の場所で実装されたコードを使用することができます。例えば、次の行の "MonoBehaviour" というコードは "using UnityEngine " がないと使えません。ほとんどの場合、Unity にデフォルトで含まれている using ディレクティブで十分なので、これらについて心配する必要はありません。
2.次の行、
public class PlayerMovement : MonoBehaviour
これは クラス宣言 の始まりになります。クラスはオブジェクトと呼ばれるインスタンスの仕様書のようなものです。クラスがインスタンス化されると(例えば、スクリプトをコンポーネントとしてゲームオブジェクトにアタッチするなど)、そのインスタンスは オブジェクト と呼ばれます。オブジェクトはコードの構成要素であり、これまでに行った作業の中にも存在しています。
すでに触れたクラスには、Animator や Rigidbody、GameObject、Transform などがあります。これらのクラスはすでにゲームに存在しています。別の方向からクラスについて考えてみると、クラスが工場建物であるということです。工場は材料を受け取り、材料にいくつかのことを行い、その後製品を生産します。他のコード要素を使って、この工場の例えに戻ります。
3.次の行は、開き波括弧/角括弧です: {
このチュートリアルシリーズでは、それらを波括弧と呼びます。波括弧はコードブロック を定義するため、C# 言語には欠かせない要素です。コードブロックは、開き波括弧と閉じ波括弧の間に存在するコード行のことです。波括弧は常にペアで存在しなければなりません。
クラス宣言のコードブロックについては、閉じ波括弧が一番下にありますが、クラス宣言の中にはさらに 2 つのコードブロックがあります。クラス宣言に含まれる 2 つのコードブロックはインデント(字下げ)されています。インデントは技術的に見て必要ありませんが、コードブロックの開始位置と終了位置を定義するのに非常に役立ちます。
4.次の行、
// Start は最初のフレーム更新の前に呼び出されるこのように 2 つのスラッシュが前に挿入されているテキストは、 コメントと呼ばれます。コメントについては、コンピュータから完全に無視されたいものですよね。ほとんどの場合、コメントはその周りのコードについて何かを説明するためのラベルのようなものとして機能しています。この場合、コメントはその下で何が宣言されているかを非常に簡潔に説明しています。
5.コメントの後は、最初のメソッド宣言の始まりです。
void Start()工場の例えに戻りましょう。クラスが工場であれば、メソッドはその工場内の機械です。メソッドは、データを取り込み、操作を実行してから、データを出力(または返す)できます。
すべてのメソッド宣言は同じ形式です。
- 最初に 戻り値 の型を指定します。これは、メソッドが終了したときに渡されるデータの種類です。この例では、戻り値の型は void です。これは C# の特別なキーワードで、メソッドは文字通り何も返しません。
- 戻り値の型の後には メソッド名 が付きます。この場合は Start です。
- 名前の後には一対の開きと閉じの波括弧/角括弧 () があります。このチュートリアルシリーズでは、それらを丸括弧と呼びます。これらの丸括弧の中で、メソッドはどのようなデータを取り入れるかを宣言することができます。 これらのデータは パラメーターと呼びます。丸括弧の間に何もないので、宣言されたパラメーターはありません。
これら 3 つの情報 (戻り値の型、名前、パラメーター) がメソッドの signature(シグネチャ)を形成します。ほとんどの場合、メソッドには任意の signature(シグネチャ)を付けることができます。ただし、MonoBehaviour クラスでは、特定のシグネチャを持つ必要がある特別なメソッドを使うことができます。これらの特別なメソッドはコードから呼び出す必要はなく、代わりに Unity から特定のタイミングで呼び出されます。Start は特別なメソッドの一例です。GameObjectの起動時にすぐに呼び出されますが、通常はシーンが開始すると同時に呼び出されます。そのため、セットアップのよう一回だけ行う処理に最適です。
6.Start メソッドのシグネチャの後にコードブロックがあります。これは、メソッドが呼び出されるたびに実行されるすべてのコードを定義します。メソッドを呼び出すことは、それをどのように使うかということです。これは後で自分で行います。工場の例えに戻ると、メソッドの宣言は工場内のマシンがどのように動作するかを示すものであり、メソッドを呼び出すことは実際にそのマシンを利用することになります。
メソッドのシグネチャ宣言とそれに続くコードブロックは、共に メソッド定義 として知られています。メソッド宣言とメソッド定義という用語は C# の話をしているときによく使われます。なぜなら、それらは同時に起こるからです。この違いは他の言語(C++ など)では本当に重要です。
7.さて、そろそろクラスで書かれているものの順序について話をする時が来ました。工場と機械の例えで言えば、工場のどこに機械があるかは問題ではありませんが、機械がどのような順番で作業を行うかについては非常に重要です。
C# では、クラス内でメソッドがどの順番で宣言されているかは重要ではありませんが、メソッドがその操作を実行する順番は非常に重要です。
8.スクリプトの最後の 2 つの部分は、別のコメントであり、別のメソッドの定義です。
Update メソッドも MonoBehaviour の特別なメソッドです。このメソッドは何かが画面にレンダリングされる前に、フレームごとに呼び出されます。
これで、デフォルトスクリプトの一部について基本的な理解ができたので、このスクリプトをゲームに合わせてカスタマイズしましょう。
3. 水平軸と垂直軸の変数を作成する
PlayerMovement のスクリプトは、ユーザーの入力を受けて、それをキャラクターの動きに変換する必要があります。
まず必要なことは、Unity の入力システムからデータを取得することです。スクリプトは常に入力に何が起こっているかをチェックする必要があり、Update はフレームごとに呼び出されるので、そこで入力をチェックするのは理にかなっています。しかし、具体的には何をチェックする必要があるのでしょうか?
矢印キー や WASD を使ってキャラクターを動かすのは理にかなっているので、スクリプトはキーボードの特定のキーの値を確認する必要があります。それぞれのキーが個別に保持されているかどうかを確認し、そのキャラクターが何をすべきか(またはしないか)を決めることができますが、これをもう少し簡単にする別の方法があります。
Unity には、名前で見つけることができるさまざまなボタンと軸を定義する インプットマネージャー があります。たとえば、AD キーや左右キーで表される Horizontal(水平)という軸があります。それをチェックすることで、プレイヤーのコンピューターがキャラクターを左右に動かすかどうかを判断することができるのです。
変数を作成するコードを書く
始めましょう!Update メソッドの波括弧内に次の行を追加します。
float horizontal = Input.GetAxis ("Horizontal");
以下のようになっているはずです。
// Update はフレームごとに 1 回呼び出される
void Update()
{
float horizontal = Input.GetAxis ("Horizontal");
}
この新しい行は何をするの?
要するに、新しいコード行は、「新たに horizontal と言う名前の float 型変数を作成し、それにこのメソッド(Input.GetAxis メソッド)の呼び出し結果を代入する。」とコンピュータに伝えているのです。
クラスが工場でメソッドがそれらの工場の中にある機械だとすると、変数は工場の中にあるものが入っている箱になります。つまり、変数とはデータを保存するための手段なのです。保存する必要があるデータは、水平方向の軸入力の値です。Unity では、入力軸は -1 から 1 の間の数値を返します。このタイプのデータは float と呼ばれます。float は小数点以下の桁数を表します。
このコード行には、いくつかの重要なシンタックス(構文)があります。
- C#で 等号 とは、右にあるもの(メソッドの結果)を左にある変数(新しく作成された float 変数)に代入することを意味します。
- Input と GetAxis の間のピリオドにより、コンピューターは前のオブジェクトの内部にアクセスすることができます (GetAxis は Input の中のメソッドなので、Input から GetAxis への取得にはピリオドが使用されます)。
- C# のコードは ステートメント と呼ばれるもので構成されています。各ステートメントは、コンピュータへの指示を一つ以上含むことができ、文のように考えることができます。セミコロン はステートメントの終わりを示します。そのため、文の最後にあるピリオド ( 日本語の場合は読点 ) と同じように機能します。
4. どうやってその変数を作成したの?
コードが何をしているかをもう少し詳しく見ていきましょう。
Input というクラスは入力を取得するメソッドをいくつか持ち、その中の GetAxis と呼ばれるメソッドを見つけます。次に、名前の後に丸括弧を付けて、そのメソッドを呼び出します。ただし、Start や Update とは異なり、GetAxis にはタスクを実行するために必要なデータの一部であるパラメーターがあります。具体的には、GetAxis は値を取得する軸の名前を必要とします。ここでは水平軸の値を見つけようとしているので、それをパラメーターとして指定しました。
この情報のデータ型は string 型と呼ばれています。これは、単語や文章などの文字列を指します。Horizontal という単語の前後に引用符を付けることで、それが文字列のように処理するようにコンピュータに指示しています。
コンピュータが軸の値を取得したら、その値をどこかに保存する必要があります。メソッド呼び出しの左側に、horizontal という新しい float 変数を導入し、GetAxis から見つけた値と同じ値を設定します。
次に、もう 1 行コードを追加して、 Vertical という軸の値を見つけ、その値を vertical という変数に格納します。
Update メソッドは以下のようになります。
// Update はフレームごとに 1 回呼び出される
void Update()
{
float horizontal = Input.GetAxis ("Horizontal");
float vertical = Input.GetAxis ("Vertical");
}
これで、水平軸と垂直軸の両方の値が得られました。次のステップでは、それらを ベクトル に結合して位置を変えられるようにします。
5. ベクトル の作成
Unity では、3D 空間を 3 つの座標によって表現し、それらを合わせてベクトルとしています。Unity でベクトルを表現するデータ型は Vector3 と呼ばれています。これはゲームオブジェクトの位置を表すものなので、その位置の変更を表す Vector3 を作成する必要があります。ユーザーの入力が表す動きは、このクラスの基本であり、他のことにも使う必要があるかもしれません。
これを念頭に置いて、作成する変数のスコープを考えることが重要です。
変数のスコープとは、その変数が使用できるコードの領域のことです。通常、これは宣言されているコードブロックと同じくらい簡単です。例えば、先ほど宣言した float 変数(水平と垂直)の両方が Update メソッド全体のスコープに入っています。これは、それらの変数が宣言されたコードブロックだからです。これらは、 Update メソッドがローカル であると言われています。
複数の異なるメソッドで変数を使用したい場合は、メソッドのスコープ外に変数を作成することができます。代わりに、これらはクラスがローカルなものになります。
メソッド定義の上に、以下の行を追加します。
Vector3 m_Movement;
スクリプトは以下のようになります。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
Vector3 m_Movement;
// Start は最初のフレーム更新の前に呼び出される
void Start()
{
}
// Update はフレームごとに 1 回呼び出される
void Update()
{
float horizontal = Input.GetAxis ("Horizontal");
float vertical = Input.GetAxis ("Vertical");
}
}
新しいコード行は、コンピューターに m_Movement という Vector3 変数を作成するように指示しています。この変数は PlayerMovement クラスの好きなところに使うことができます。
命名規則
しかし、名前の先頭の m_ は何を意味しているのでしょうか?これは 命名規則 と呼ばれるものの一部です。命名規則は、特定のオブジェクトまたはオブジェクトのクラスを識別するために使用されます。このプロジェクトでは、Unity の内部命名規則を使用します。すべての変数の先頭は小文字で始まり、スペースを空けずに次の語は大文字で始まります。これは キャメルケース(camelCase)と呼ばれます。
この例外は、 非公開メンバ変数で、接頭辞が m_ で始まり、すべての単語が大文字で始まるものです。これは パスカルケース(PascalCase)と呼ばれます。メンバ変数とは、特定のメソッドではなくクラスに属する変数のことです。非公開メンバ変数の m_ 部分は、それらが「メンバ」変数であることに由来します。
6. 変数の値を設定する
キャラクターの動きを格納する変数ができたので、その値を設定する必要があります。これはフレームごとに変更される可能性があるので、フレームごとに設定する必要があります。設定は Update メソッドで行う必要があります。
Update メソッド内に、horizontal と vertical の変数を作成した後、以下の行を追加します。
m_Movement.Set(horizontal, 0f, vertical);
3D 空間のベクトルには 3 つの値があります。この Set メソッドはそれぞれに値を割り当てます。ベクトルの各座標に 1 つずつ、3 つのパラメーターがあります。これで、移動ベクトルの x 軸は水平入力、y 軸は 0、z 軸は垂直入力の値が設定されました。2 番目のパラメーターの 0 の後には f があり、これはコンピューターに数値を float として扱うように指示しています。
ここで、小さな問題を解決する必要があります。移動ベクトルは、最大値 1 になりうる 2 つの数値で構成されています。両方の値が 1 の場合、ベクトルの長さ(そのマグニチュードとして知られている)は 1 より大きくなります。これはピタゴラスの定理によって表された三角形の辺の関係です。
これは、キャラクターが 1 つの軸に沿って移動するよりも斜め方向に速く移動することを意味します。そうならないようにするためには、移動ベクトルが常に同じ値の大きさにする必要があります。正規化することでそれを行うことができますよ。ベクトルの正規化とは、ベクトルの方向を同じにして、その大きさを 1 に変えることを意味します。
前に書いた行の下に以下のスクリプトを追加して、ベクトル自体にメソッドを呼び出すようにします。
m_Movement.Normalize ();
スクリプト全体は以下のようになります。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
Vector3 m_Movement;
// Start は最初のフレーム更新の前に呼び出される
void Start()
{
}
// Update はフレームごとに 1 回呼び出される
void Update()
{
float horizontal = Input.GetAxis ("Horizontal");
float vertical = Input.GetAxis ("Vertical");
m_Movement.Set(horizontal, 0f, vertical);
m_Movement.Normalize ();
}
}
この例では、入力を取得してから移動ベクトルを設定するまでの間にギャップがあります。これは重要なことではなく、ただきちんと整理されているように見せるためです。
7. Animator コンポーネントのセットアップ
これで移動ベクトルが作成できましたね。他にもフレームごとにやらなければならない作業があります。コンピュータに以下のことを指示する必要があります。
- キャラクターが歩いているかどうかを Animator コンポーネントに伝える
- プレイヤーの入力からキャラクターの回転を取得(動きを取得する方法と似ている)
- キャラクターに動きと回転を適用する
まずは Animator コンポーネントから始めましょう。
プレイヤーの入力の有無を確認する
プレイヤーの入力がある場合は、キャラクターは歩いているはずで、ない場合はアイドル状態になります。
1.手始めに、水平入力があるかどうかを判断するコード行書いておく必要があります。移動ベクトルを正規化する行の後に次のコードを追加します。
bool hasHorizontalInput = !Mathf.Approximately (horizontal, 0f);ここでは、 bool 変数 (true または false のいずれかになるもの) を作成し、hasHorizontalInput を呼び出します。次に、それをメソッドの戻り値と等しく設定します。このメソッドは Approximate と呼ばれ、Mathf クラスに由来しています。2 つの float パラメータを受け取り、bool を返します。2 つの float がほぼ等しい場合は true、そうでない場合は false を返します。ですから、このシナリオでは、 horizontal(水平方向)の変数がほぼゼロの場合、メソッドは true を返します。
待ってください!この行には、これまで遭遇したことのない他の文字があります。メソッド呼び出しの前にある感嘆符です。これは 論理否定演算子 で、bool を反転し、true を false に、false を true に設定します。つまり、水平がほぼ 0 になっていないときに hasHorizontalInput が true に設定されます。言い換えれば、hasHorizontalInput は水平がゼロ以外の場合には true となります。
2.気になるのは水平軸だけではありませんが。垂直軸にも同様の線を追加します。
bool hasVerticalInput = !Mathf.Approximately (vertical, 0f);
この線は全く同じことを行っていますが、垂直軸に対してです。
3.これで、軸の入力がいつ取得されるのかが分かったので、それらを 1 つの bool に結合する必要があります。以下のコード行を追加します。
bool isWalking = hasHorizontalInput || hasVerticalInput;
この行は、isWalking という別の新しい bool 変数を作成しています。これは hasHorizontalInput または hasVerticalInput に設定されています。2 本の縦線は、 論理和演算子 です。これは両側の bool 値を比較しています。どちらか一方、または両方が true であれば、それは true と等しくなり、そうでなければ false に等しくなります。つまり、hasHorizontalInput または hasVerticalInput が true の場合は isWalking が true で、そうでない場合は false になります。
8. Animator コンポーネントへの参照を格納する変数を作成する
次に、先ほど作成した bool 値を使用して、キャラクターが歩くべきかどうかを Animator コンポーネントに伝える必要があります。これを行うには、Animator コンポーネントにアクセスする必要があります。
でも、ちょっと待ってください。Input や Mathf メソッドを呼び出すのに特別なことをする必要がないのに、なぜ Animator コンポーネントへのアクセスに何か特別なことをする必要があるのですか?これは、Input と Mathf のメソッドが静的であるためです。
静的メソッド とは、クラスのインスタンスではなく、クラスの 型 に対して呼び出されるメソッドのことです。入力はよりグローバルな概念なので、軸の値を決めるために Input クラスの単一インスタンスを持つ必要はありません。そのため、これらの値を取得するメソッドは静的なものになっています。同様に、Mathf クラスはヘルパーメソッド(別のメソッドがそのタスクを実行するのを助けるメソッド)でいっぱいで、Mathf の特定インスタンスのための特定のデータを含まないので、それらのメソッドも静的になっています。
ただし、m_Movement 変数を考えてみましょう。Vector3 の特定のインスタンスに固有の値を設定する必要があったので、これらのメソッドは静的なものではありませんでした。覚えておくべき重要なことは、static(静的)メソッドは型名を使って呼ばれ、non-static(非静的)メソッド(または 「インスタンス」メソッド)はインスタンス名を使って呼ばれるということです。
9. Animator コンポーネントへの参照を取得する
Animator コンポーネントにアクセスするには、そのコンポーネントへの参照が必要です。これは GetComponent と呼ばれるメソッドを使用して取得します。この参照は、単一のメソッドだけでなく、クラス全体で使用されます。メンバ変数として(動きベクトルのように)保持することは理にかなっているので、そのスコープはクラスに対してローカルになります。
クラスの先頭、移動ベクトル宣言の上ですがクラス宣言の下に、以下の行を追加します。
Animator m_Animator;
スクリプトは以下のようになります。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
Animator m_Animator;
Vector3 m_Movement;
void Start ()
{
}
void Update ()
{
float horizontal = Input.GetAxis ("Horizontal");
float vertical = Input.GetAxis ("Vertical");
m_Movement.Set(horizontal, 0f, vertical);
m_Movement.Normalize ();
bool hasHorizontalInput = !Mathf.Approximately (horizontal, 0f);
bool hasVerticalInput = !Mathf.Approximately (vertical, 0f);
bool isWalking = hasHorizontalInput || hasVerticalInput;
}
}
注意:この例では、Start メソッドと Update メソッドの上にあるコメントが削除されています。必要に応じてこれを行うこともできますが、その必要はありません。コメントがコードに影響を与えることはありません。
10. Animator コンポーネントへの参照を設定する
Animator コンポーネントへの参照を格納するための変数を作成しましたが、今のところこの変数には何も設定されておらず、空です。C# ではこのように変数が空の場合、その値が null になると言われています。何かへの参照があっても、どのオブジェクトにも参照を持たない場合はいつでも、その参照は null になります。
ここでは null 参照は不要なので、参照を適切に設定する必要があります。また、どのメソッドでも Animator コンポーネントにアクセスできるようにする必要があるので、できるだけ早く参照を設定することが重要です。
MonoBehaviour で呼び出される最も初期のメソッドのひとつは、このチュートリアルの前半で触れた Start メソッド です。そのため、そこに参照を設定することは完全に理にかなっています。Start メソッドに以下の行を追加します。
m_Animator = GetComponent<Animator> ();
メソッドは以下のようになります。
void Start ()
{
m_Animator = GetComponent<Animator>();
}
このコード行では、よく知られたシンタックスと新しいシンタックスを使用しています。
- 割り当てる変数は左側にあります。
- 右側にメソッドの名前があります(ただし、その前には何も書いていません)。
- Animator の周りには、以前に出てきた丸括弧の前に山形の角括弧があります。
- 行の末尾はセミコロンで終わります。
この参照の意味は?
コードに触れてみましょう。
まず、GetComponent の前にクラスがないのはなぜでしょう?以前それを追加したときは、他のオブジェクトのメソッドにアクセスしていました(例えば、移動ベクトルの Normalize メソッド)。しかし、GetComponent は既にアクセスできるものです。これは MonoBehaviour の一部であり、クラスは MonoBehaviour なので、GetComponent にもアクセスすることができます。
次は、山括弧です。GetComponent メソッドは ジェネリックであるため、これらが追加されました。ジェネリックメソッドは、2 つの異なるパラメーターセット(標準のパラメーター と 型パラメーター)を持つメソッドです。山括弧の間に指定されたパラメーターは、型パラメーターです。
このシナリオでは、GetComponent は、探しているコンポーネントの 型を知る必要があります。Animator コンポーネントを探しますが、型パラメーターは Animator になります。コード行は "「Animator」型のコンポーネントへの参照を取得し、それを m_Animator という変数に代入する"と述べています。
isWalking Animator パラメーターを設定する
これで Animator コンポーネントへの参照ができたので、それを使って、前回のチュートリアルで作成した IsWalking Animator パラメーターを設定することができます。
Update メソッドで isWalking 変数を作成した下に、以下の新しいコードの行を追加します。
m_Animator.SetBool ("IsWalking", isWalking);このコードは、先ほど設定した Animator コンポーネント参照を使用して SetBool メソッド を呼び出しています。最初のパラメーターは値を設定したい Animator パラメーターの名前、2 番目のパラメーターは設定したい値です。最初のパラメーターのスペルと大文字と小文字は完全に一致させることが重要です。そうしないと、メソッドはどの Animator パラメーターの値を設定するかわからなくなります。
スクリプトは以下のようになります。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
Animator m_Animator;
Vector3 m_Movement;
void Start ()
{
m_Animator = GetComponent<Animator>();
}
void Update ()
{
float horizontal = Input.GetAxis ("Horizontal");
float vertical = Input.GetAxis ("Vertical");
m_Movement.Set(horizontal, 0f, vertical);
m_Movement.Normalize ();
bool hasHorizontalInput = !Mathf.Approximately (horizontal, 0f);
bool hasVerticalInput = !Mathf.Approximately (vertical, 0f);
bool isWalking = hasHorizontalInput || hasVerticalInput;
m_Animator.SetBool("IsWalking", isWalking);
}
}
以上です!Animator パラメーターの値を設定しました。
11. キャラクターの回転を作成する
次の作業「キャラクターの回転を作成する」はフレームごとに見ていきましょう。
このゲームでは、キャラクターは前方にしか歩けないので、動きと同じ方向を向いている必要があります。しかし、 John Lemon が望みの方向に素早く反転してしまうと、実に妙な感じになってしまうので、少しスローにする必要があります。でも、どのくらい遅くすればいいですか?
その質問の答えを出すための簡単な方法は、キャラクターがどのくらいの速さで回転すべきかを考えることです。
このスピードを設定するには、新しい変数を作成する必要があります。
turnSpeed 変数を作成する
以下の行をクラスの先頭、Animator メンバ変数の上に追加します。
public float turnSpeed;
コードを触ってみましょう。
変数宣言の前に public という単語を追加しました。Unityでは、public メンバ変数は Inspector ウィンドウに表示されるため、微調整することができます。
また、camelCase が使われています(PascalCase ではなく、接頭辞が m_ の PascalCase を使用)。これは、変数が public であり、Unity の命名規則では public メンバ変数にこの形式を使用しているためです。命名規則は非常に便利ですが、技術的な理由はありません。
キャラクターの前方ベクトルを計算する
キャラクターは動きの方向を向く必要があることを覚えておいてください。すべての Transform コンポーネントは 前方ベクトル を有しています。ですから、適切な中間ステップはキャラクターの前方ベクトルの値を何にしたいかを計算することでしょう。
Update メソッドの一番下に以下の行を追加します。
Vector3 desiredForward = Vector3.RotateTowards (transform.forward, m_Movement, turnSpeed * Time.deltaTime, 0f);かなり長くて複雑に見えるコード行ですが、使い慣れた多くのコードが含まれています。分かりやすく説明しましょう。
- このコードでは、 desiredForward という Vector3 変数を作成します。
- これは Vector3 クラスの静的メソッドである RotateTowards というメソッドの戻り値に設定します。RotateTowards は 4 つのパラメーターを受け取ります。最初の2つは Vector3 で、それぞれ回転させているベクトルの位置と方向を表しています。
- コードは、 transform.forward から始まり、 m_Movement 変数を目指します。transform.forward は、Transform コンポーネントにアクセスし、その前方ベクトルを取得するためのショートカットです。
- 次の 2 つのパラメーターは、開始ベクトルとターゲットベクトルの間の変化量です。最初に角度(ラジアン単位)の変化、次に大きさの変化です。このコードは、 turnSpeed * Time.deltaTime で角度を、マグニチュードを 0 で変更します。
Time.deltaTime は前のフレームからの時間です(フレーム間の時間と考えることもできます)。じゃあ、どうして turnSpeed の値を増やす必要があるのですか?
更新はフレームごとに呼び出されます。ゲームが 60 フレーム/秒で実行されている場合、このメソッドは 1 秒間に 60 回呼び出されます。60 フレームの間に 1 秒間あなたが望む変化が得られるように、それぞれの呼び出しには非常に小さな変化があるでしょう。でも毎秒 30 フレームで動かすゲームの場合はどうなりますか?メソッド呼び出しの半分だけが同時に行われるため、半分だけ回転が行われることになります。1 秒あたりのフレーム数がキャラクターの回転速度に影響するのは避けたいですよね。
フレームごとの変更ではなく、1 秒ごとの変更を処理する場合はどうでしょうか?それで処理がかなり楽になりますね。これを行うには、1 秒間に必要な変化を 1 フレームにかかった時間で乗算する必要があります。それは、まさにこのコードが行うことです。
12. turnSpeed 変数を調整する
turnSpeed 変数は 1 秒間にキャラクターを回転させる角度をラジアン単位で指定します。次に、これに Time.deltaTime を掛けて、キャラクターがこのフレームを回転させるべき量を求めます。ラジアンは角度の異なる測定です。それらは度数に似ていますが、より自然な測定です。円(360度に相当)のラジアンは 2π ラジアンなので、約 6 になります。キャラクターは常に最短で周るようになるため、キャラクターが周れるのは最大でも 3 ラジアンくらいです。
これを考えると turnSpeed が 3 の場合は、キャラクターが完全に振り向くのに約 1 秒かかることになります。それは実際にはかなり遅いですね。回転速度を 6 にすると、振り向くのに約 0.5 秒くらいかかることになります。これでもかなり遅いです。20 の値を試して、どんな感じか見てみましょう。必要に応じて、これは後からでも変更できます。
クラスの先頭にある turnSpeed 変数を宣言する行を以下のように変更します。
public float turnSpeed = 20f;これで、キャラクターの向きのベクトルができました!
回転の作成と保存
次に、ベクトルを使って回転を取得し、保存しておくと、どこでも使えるようになります。移動ベクトルと同じように保存しますので、そこに変数を宣言することは理にかなっています。
m_Movement という Vector3 を宣言している行の下に、以下の行を追加します。
Quaternion m_Rotation = Quaternion.identity;Quaternions は回転を保存する方法で、回転を 3D ベクトルとして保存する際のいくつかの問題を回避することができます。このチュートリアルでは、それらについて詳しく触れていません。回転を保存する方法であると知ることが今必要なすべてです。
Quaternion に、必要な Quaternion.identity.all のデフォルト値を指定しました。通常、特定のメソッドの一部ではなく、クラスの一部である変数(メンバ変数)は、クラスのインスタンスが作成されたときにデフォルト値に設定されます。例えば、Vector3 のデフォルト値は、x、y、z がすべて 0 に設定されています。クォータニオンについても同様です。ただし、ゼロベクトルは(移動なし)意味を成しますが、ゼロクォータニオンはそれほど意味がありません。Quaternion.identity のこの値を設定することは、単に回転しない値を与えるだけであり、理にかなったデフォルト値です。
これで回転変数の作成が終わったので回転を設定できるようになりました。desiredForward 変数の作成の下に、以下の行を追加します。
m_Rotation = Quaternion.LookRotation (desiredForward);
この行は単に LookRotation メソッドを呼び出し、指定されたパラメーターの方向を向いた回転を作成しています。
スクリプトは以下のようになります。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float turnSpeed = 20f;
Animator m_Animator;
Vector3 m_Movement;
Quaternion m_Rotation = Quaternion.identity;
void Start ()
{
m_Animator = GetComponent<Animator>();
}
void Update ()
{
float horizontal = Input.GetAxis ("Horizontal");
float vertical = Input.GetAxis ("Vertical");
m_Movement.Set(horizontal, 0f, vertical);
m_Movement.Normalize ();
bool hasHorizontalInput = !Mathf.Approximately (horizontal, 0f);
bool hasVerticalInput = !Mathf.Approximately (vertical, 0f);
bool isWalking = hasHorizontalInput || hasVerticalInput;
m_Animator.SetBool("IsWalking", isWalking);
Vector3 desiredForward = Vector3.RotateTowards (transform.forward, m_Movement, turnSpeed * Time.deltaTime, 0f);
m_Rotation = Quaternion.LookRotation (desiredForward);
}
}
13. キャラクターに動きと回転を適用する
あと少しで完成ですね!最後のステップでは、動きと回転をキャラクターに適用していきます。これを行う方法はたくさんありますが、キャラクターは物理演算システムの一部である必要があるので、他のテクニックを使用する代わりにリジッドボディを動かす必要があります。
これを行うには、Rigidbody コンポーネントへの参照が必要です。Animator コンポーネントで行ったのと全く同じように取得することができます。
1.Animator 変数宣言の直後に以下の行をスクリプトに追加します。
Rigidbody m_Rigidbody;
2.Animator 変数の参照を設定した直後に、もう 1 行コードを追加します。
m_Rigidbody = GetComponent<Rigidbody> ();さあここで、リジッドボディへの参照ができたのでアニメーションキャラクターを動かすための具体的な内容について考えてみましょう。キャラクターには楽しい Walk アニメーションが含まれており、それにはルートモーションを使用するのが良いでしょう。ただし、アニメーションには回転がありません。Update メソッドでリジッドボディを回転させようとすると、アニメーションでオーバーライドされる可能性があります(これはキャラクターが回転が必要な時に回転しない可能性がある)。
実際に必要なのは、アニメーションのルートモーションの一部ですが、すべてではありません。具体的には、回転ではなく動きを適用する必要があります。では、アニメーターからルートモーションの適用方法を変更するにはどうすればいいのでしょうか?幸いにも、MonoBehaviour には、アニメーターからルートモーションの適用方法を変更するための特別なメソッドが用意されています。
Update メソッドの下に、新しいメソッドを宣言します。
void OnAnimatorMove ()
{
}
この方法では、好きなだけルートモーションを適用できます。つまり、動きと回転を別々に適用できるということです。
14. 動き
まずは動きから始めましょう。新しい OnAnimatorMove メソッドに以下の行を追加します。
m_Rigidbody.MovePosition (m_Rigidbody.position + m_Movement * m_Animator.deltaPosition.magnitude);これもかなり複雑そうなコード行ですが、ここであなたにとって全く目新しいコードはほとんどありません。
まず、Rigidbody コンポーネントの MovePosition メソッドを呼び出すために Rigidbody コンポーネントへの参照を使用し、単一のパラメーター、つまり新しい位置に渡します。新しい位置はリジッドボディの現在の位置から始まり、そこにアニメーターの deltaPosition の大きさを掛け合わせることにより移動ベクトルに変更を加えています。それは何を意味するのでしょう?
アニメーターの deltaPosition は、このフレームに適用されていた場合のルートモーションによる位置の移動量です。その大きさ(ベクトルの長さ)を取得し、実際にキャラクターに動いて欲しい方向にある移動ベクトルを掛け合わせます。
15. 回転
次に、回転を適用します。OnAnimatorMove メソッドの MovePosition の呼び出しのすぐ下に以下の行を追加します。
m_Rigidbody.MoveRotation (m_Rotation);これは、回転に適用されることを除いては、MovePosition の呼び出しと非常に似ています。今回は回転に変更を加えているわけではなく、直接回転を設定しているだけです。
それが最初のスクリプトの最後の行のコードです!ただし、もう一つ調整してほしいことがあります。
16. Update メソッドの変更
前回のチュートリアルでは、Update ループ(レンダリングに使用)と FixedUpdate ループ(物理演算を実行)について学びました。物理演算とアニメーションの間の衝突を避けるために、アニメーターが物理演算のループと同期して実行されるようにしました。ただし、ここでは OnAnimatorMove を使ってルートモーションをオーバーライドしています。つまり、OnAnimatorMove は Update メソッドのようなレンダリングではなく、実際には物理演算に合わせて呼び出されることになります。
移動ベクトルと回転は Update で設定します。OnAnimatorMove が最初に呼ばれた場合は、値が設定されていないクォータニオンは全く意味を成さないので、問題が発生します。
OnAnimatorMove に合わせて移動ベクトルと回転が適切に設定されるようにするには、Update メソッドを以下のように FixedUpdate メソッドに変更します。
void FixedUpdate ()
これも Unity が自動的に呼び出す特別なメソッドですが、こちらは物理演算に合わせています。レンダリングされたフレームの前に呼び出されるのではなく、物理システムが発生した衝突やその他の相互作用を解決する前に FixedUpdate が呼び出されます。デフォルトでは 1 秒に 50 回正確に呼び出されます。
以上です!完成したスクリプトは以下のようになります。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float turnSpeed = 20f;
Animator m_Animator;
Rigidbody m_Rigidbody;
Vector3 m_Movement;
Quaternion m_Rotation = Quaternion.identity;
void Start ()
{
m_Animator = GetComponent<Animator> ();
m_Rigidbody = GetComponent<Rigidbody> ();
}
void FixedUpdate ()
{
float horizontal = Input.GetAxis ("Horizontal");
float vertical = Input.GetAxis ("Vertical");
m_Movement.Set(horizontal, 0f, vertical);
m_Movement.Normalize ();
bool hasHorizontalInput = !Mathf.Approximately (horizontal, 0f);
bool hasVerticalInput = !Mathf.Approximately (vertical, 0f);
bool isWalking = hasHorizontalInput || hasVerticalInput;
m_Animator.SetBool ("IsWalking", isWalking);
Vector3 desiredForward = Vector3.RotateTowards (transform.forward, m_Movement, turnSpeed * Time.deltaTime, 0f);
m_Rotation = Quaternion.LookRotation (desiredForward);
}
void OnAnimatorMove ()
{
m_Rigidbody.MovePosition (m_Rigidbody.position + m_Movement * m_Animator.deltaPosition.magnitude);
m_Rigidbody.MoveRotation (m_Rotation);
}
}
重要:C# ではクラス内でメソッドを宣言する順番は重要ではないため、メソッドの配置が少し異なる場合があります。
スクリプトを保存したら、自分を褒めてあげてください!あとは Unity に戻って、テストするだけです。
17. 変更内容のテスト
JohnLemon に PlayerMovement のスクリプトを追加する
Unity に戻ったら、スクリプトをコンポーネントとして JohnLemon に追加する必要があります。
スクリプトコンポーネントの追加は、Inspector ウィンドウの Add Component ボタンをクリックして行うことができますが、別の方法で行うこともできます。
1.JohnLemon のゲームオブジェクトを選択します。
2.Inspector ウィンドウで、Edit をクリックしてプレハブを編集する画面 Prefab モードに移行します。
3.Project ウィンドウで、 Assets > Scripts の順に選択し、 PlayerMovement スクリプトを見つけます。
4.PlayerMovement スクリプトを Project ウィンドウから Inspector ウィンドウにドラッグします。
5.Prefab モードが Auto Save(自動保存)に設定されていない場合は、Save ボタンをクリックします。
6.戻る矢印をクリックして、プレハブのパンくずリストに沿って戻ります。
7.MainScene がロードされていない場合は、Project ウィンドウに移動し、 Assets > Scenes の順に選択し、フォルダ内の Scene Asset をダブルクリックしてロードします。
8.シーンで JohnLemon のゲームオブジェクトを選択すると、プレハブに指定したすべてのコンポーネントと設定が含まれていることがわかります。
18. Game ビューの設定を調整する
プレハブをテストする前に、Game ビューの設定の一部を調整する必要があるかもしれません。高解像度のモニターをお持ちの場合は、Unity が自動的にGame ウィンドウをスケーリングしてパフォーマンスを向上させます。これは、Game ウィンドウのスケールを設定することで行います。
シーンは現在、パフォーマンスが気になるほど複雑ではないので、この問題がある場合は今すぐ修正しましょう。
1.Game Window タブを選択します。Game ウィンドウが上部でスケール(拡大縮小)されているかどうかを確認できます。
2.現在 Free Aspect と表示されているアスペクト比のドロップダウンをクリックします。
3.Low Resolution Aspect Ratios チェックボックスを無効にしてから、Scale スライダーを 1 倍に変更します。このボックスを無効にできない場合(またはすでに無効になっている場合)は心配する必要はありません。これは完成したゲームには影響しませんし、モニターに合わせて自動的に設定されます。
これで、Game ウィンドウが正常に表示されたので、JohnLemon のテストができるようになりました!再生ボタンをクリックして開始し、矢印キーを使用して John Lemon を移動させます。
注意 コンパイラーのエラーメッセージが表示されても、慌てないでください!戻って、コードを例と比較して注意深くチェックしてください。 特に、はじめてスクリプトを作成したばかりのときは間違いが起こりやすいです。修正をすべて保存してから、再生モードでもう一度テストしてください。
19. まとめ
このチュートリアルでは、最初のスクリプトを書き、Unity におけるコーディングの主要コンセプトとなる概念に触れました。コーディングの経験がなくても、簡単に全てを理解してくれた方、よくやった!と思います。ですが、初めての試みで何かに苦労したとしても心配しないでください。 これらの概念を快適に感じるためには、ある程度の時間と練習が必要ですから。
次のチュートリアルでは、JohnLemon がどこか不気味な雰囲気の場所から脱出できるように、ゲームの環境と照明を作成します。