#プレイヤーの移動、アニメーションの遷移、カメラの追従
#3D空間を走り回り、弾を飛ばして敵を倒すゲームを作ります。
#事前に準備するもの
プレイヤー(待機アニメーション、走るアニメーション、攻撃アニメーション)
敵キャラ(走るアニメーション、攻撃アニメーション)
キャラクター素材の作り方はこちら♪
気に入ったエフェクト素材をインポートして下さい。
#ここから本編スタートです
チェックをつけたら右下のApplyを押して確定しましょう。
さていよいよプログラミング!!とその前に
#ここでちょっとC#の解説をします
C#の解説はこちらにまとめておいたので読んで下さい♪
#それでは実際にスクリプトを作っていきましょう。
①今回はプレイヤーが前後左右に動く、
②動いてない時は待機アニメーション、動いている時は走っているアニメーションに切り替わる
③重力をかける
を実装したいと思います。
まず、Playerと名前をつけたスクリプトを開くと、初期状態では以下のようになります。
因みにスクリプト内にある//はコメントを表していて、//より右側はスクリプトとは関係のない文字となります。
例えば、 「//!”#$$%%」 と無茶苦茶に書いてもそれによってバグることはありません。
多くの場合、スクリプトの命令が何を示しているのか、メモ的に説明を加えます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
それではまず、使う変数を宣言します
入力する命令は以下の通りです。
変数を宣言する部分に入れてみましょう。
private CharacterController characterController;
private Vector3 velocity;
public float walkSpeed;
private Animator animator;
実際に入力すると以下のようになります。
public class Player : MonoBehaviour
{
//変数を入れる場所、ここから
private CharacterController characterController;//※1
private Vector3 velocity;//※1
public float walkSpeed;//※1
private Animator animator;//※2
//変数を入れる場所、ここまで
#それでは一つ一つ説明していきます。説明は折りたたんであるので、ちゃんと理解したい人は▶をクリックして下さい。
**private CharacterController characterController;**
Unityではプレイヤーの移動をさせる際にCharacterControllerというものをつけると便利です。 これをつけるとプレイヤーに地面との接触判定をつけることができ、更には段差や坂などを自動で 判定して上り下りできるようになります。 プレイヤーの移動はこのCharacterControllerを操作するので定義しておきます。 定義をしておかないと後々数値をとって操作することができません。 因みにprivateというのはスクリプトを貼り付けたオブジェクトのみで使う変数で、他のオブジェクトでも 変数を使う場合にはpublicにします。 ここではCharacterControllerの値の名前はcharacterControllerにして この変数はプレイヤーでしか、使わないよーって言っています。 characterControllerの部分はあだ名をつけているだけなのでどんな名前でも大丈夫です。**private Vector3 velocity;**
Vector3というのは3次元ベクトルのことです。分かりやすく言うと方向です。 例えばこの後プレイヤーの移動をするようにするのですが上と左を同時に押したらちょうど 上と左の間に進むと思います。特に3Dの世界ではどっちを向いていてどっちに進むのか常に意識してもらわないと どこに進んで良いのかわからなくなってしまいます。 なので方向をしっかりと数値として取るわけです。 velocityの部分はどんな名前でも大丈夫です。**public float walkSpeed;**
これは移動するスピードを決めています。 因みにプレイヤーでしか使わなそうなのに、何故publicにしているかと言うと、publicにしておくと 数値をUnityの画面で設定できるようになるからです。 移動スピードはゲームをプレイしてみて感覚で変えていきたいところなので、改変しやすいようにpublicに しています。**private Animator animator;**
これは先程設定したAnimatorControllerの変数の値を後々このスクリプトでコントロールするので定義しています。 アニメーションが切り替わるタイミングをスクリプトで制御するのにAnimatorControllerの変数を使うので、定義しておく 必要があります。 もちろん、animatorの部分はどんな名前でも大丈夫です。続いてゲーム開始時、一度だけ実行する部分を入力していきます。
// Use this for initialization
void Start ()
{
//ゲーム開始時一度だけ実行される部分、ここから
characterController = GetComponent<CharacterController>(); //※1
animator = GetComponent<Animator>(); //※2
//ゲーム開始時一度だけ実行される部分、ここまで
}
**characterController = GetComponent();**
変数を入れる場所で定義したコンポーネントは定義するだけでは使えず、値を取得する必要があります。 なので、ここでは定義したcharacterControllerにCharacterControllerの値を代入しています。 コンポーネントの取得の仕方は 定義した変数の名前 = GetComponent<コンポーネントの名前>(); です。 コンポーネントの操作をスクリプトで制御する場合には ①変数の定義 ②変数に値を代入 ③実際に操作 という流れを踏みます。#animator = GetComponent();
同様に定義したanimatorにAnimatorコンポーネントの値を入れています。
続いてゲーム開始後定期的に実行される部分を入力していきます。
// Update is called once per frame
void FixedUpdate ()//Updateではなく、FixedUpdateにする、※1
{
//ゲーム開始後定期的に実行される部分、ここから
if (characterController.isGrounded)//※1
{
velocity = new Vector3(Input.GetAxis("Horizontal"),0f,Input.GetAxis("Vertical"));//※1
if (velocity.magnitude > 0.1f)//※1
{
animator.SetBool("isRunning", true);//※2
transform.LookAt(transform.position + velocity);//※1
}
else//※2
{
animator.SetBool("isRunning", false);//※2
}
}
velocity.y += Physics.gravity.y * Time.deltaTime;//※1
characterController.Move(velocity * walkSpeed * Time.deltaTime);//※1
//ゲーム開始後定期的に実行される部分、ここまで
}
**void FixedUpdate ()**
いきなりUpdateがFixedUpdateになっているので混乱を呼ぶかもしれませんね。 実はvoid Updateは定期に実行される(1フレームごとに一回実行)のですが、その1フレームの中で実行される タイミングはランダムです。void FixedUpdateは実行されるタイミングが毎回同じになります。 キャラクターの移動の様に常に同じ様に実行されてほしい時にはvoid FixedUpdateにしたほうが動作が安定 するので変えています。**if (characterController.isGrounded)**
これはcharacterControllerが地面に接地している時の判定を取っています。 これを入れないと空中でも歩けるようになってしまいます。**velocity = new Vector3(Input.GetAxis("Horizontal"),0f,Input.GetAxis("Vertical"));**
これはvelocityという変数にベクトルの値を代入しています。 左右キー(Aキー,Dキー)が押されたらxが-1、+1上下キー(Wキー,Sキー)が押されたらzが+1、-1になります。**if (velocity.magnitude > 0.1f)**
もしvelocityのmagnitudeが0.1より大きいなら…という条件文です。 magnitudeというのはx*x + y*y + z*zの平方根の値です。 要するにmagnitudeが0.1より大きい状態というのは上下左右のいずれかが押されている状態を指すので、 上下左右が押されている間はと言い換えることも出来ます。 何故わざわざこのようなことをしているかと言うと、上下左右のいずれかが押されている間はと入力するよりも より詳細に値を認識させることが出来るため、なめらかな動きを実現できるからです。**animator.SetBool("isRunning", true);**
AnimatorControllerで設定したBool型の変数(値がtrue(真)、false(偽)の2値のみの変数)であるisRunningをtrueにしています。 これにより、待機アニメーションから走るアニメーションに移行しています。**transform.LookAt(transform.position + velocity);**
プレイヤーの向きを押されたキーの方向に変えています。 現在の位置から押されたキーの方向に力を加えることで現在の位置を基準にして押された方向に向きを変えています。**else**
if文の{}のあとにつけることで「…でなければ」という命令を付け加えることが出来る。 この場合は上下左右キーが押されていなければとなります。**animator.SetBool("isRunning", false);**
isRunningをfalseにしています。**velocity.y += Physics.gravity.y * Time.deltaTime;**
velocityのyに重力を経過時間(Time.deltaTime)の分だけかけ続けています。 Physicsは重力のことでUnityで予め設定されています。**characterController.Move(velocity * walkSpeed * Time.deltaTime);**
characterControllerをvelocity * walkSpeed * Time.deltaTime分だけ動かしています。 要するに設定したwalkSpeedの分だけ上下左右が押されている間動かし続けています。#完成形のスクリプト
※1はプレイヤーの移動、※2はアニメーションの制御に関係しています
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
private CharacterController characterController;//※1
private Vector3 velocity;//※1
public float walkSpeed;//※1
private Animator animator;//※2
void Start()//ゲーム開始時に一度だけ実行する内容
{
characterController = GetComponent<CharacterController>();//※1
animator = GetComponent<Animator>();//※2
}
void FixedUpdate()//ゲーム開始時に何度も実行する内容
{
if (characterController.isGrounded)//※1
{
velocity = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));//※1
if (velocity.magnitude > 0.1f)//※1
{
animator.SetBool("isRunning", true);//※2
transform.LookAt(transform.position + velocity);//※1
}
else//※2
{
animator.SetBool("isRunning", false);//※2
}
}
velocity.y += Physics.gravity.y * Time.deltaTime;//※1
characterController.Move(velocity * walkSpeed * Time.deltaTime);//※1
}
}
```
![スライド91.PNG](https://qiita-image-store.s3.amazonaws.com/0/214359/4b1e13d3-1bc1-4b19-9fac-b0ec74bbfec2.png)
![スライド92.PNG](https://qiita-image-store.s3.amazonaws.com/0/214359/fda9604e-0539-0fec-7f01-9bc74a713487.png)
![スライド93.PNG](https://qiita-image-store.s3.amazonaws.com/0/214359/d69ce49f-7ff7-3dcc-b8cc-b2623541eeac.png)
![スライド94.PNG](https://qiita-image-store.s3.amazonaws.com/0/214359/5012519b-b115-7d59-8f9a-084c41606a75.png)
#CameraFollowPlayerのスクリプトを作成します。
スクリプト内容は以下の通りです。
説明はコメントに記述してありますが、
Playerをtargetとしてカメラとの相対座標を求めていて、
常にカメラがPlayerから相対座標分離れたところに来るようにしています。
``````csharp:CameraFollowPlayer.cs
using UnityEngine;
using System.Collections;
public class CameraFollowPlayer : MonoBehaviour
{
public Transform target;//ターゲットへの参照
private Vector3 offset;//相対座標
void Start()
{
offset = GetComponent<Transform>().position - target.position;//自分自身とtargetとの相対距離を求める
}
void Update()
{
GetComponent<Transform>().position = target.position + offset;// 自分自身の座標に、targetの座標に相対座標を足した値を設定する
}
}
```
![スライド95.PNG](https://qiita-image-store.s3.amazonaws.com/0/214359/fe0589b5-b86f-0b58-1b95-fba8be3a1ff6.png)
![スライド96.PNG](https://qiita-image-store.s3.amazonaws.com/0/214359/d336debc-0a19-f803-d988-47e6e1ce265a.png)
#テストプレイをしてみると…
![PlayerRunning.gif](https://qiita-image-store.s3.amazonaws.com/0/214359/6a01c68f-4312-c22a-74d0-507daa086ecf.gif)
ステージが真っ白なんで分かりにくいですが3D空間を走り回っています。
#次のチュートリアルは[こちら](https://qiita.com/Getabako/items/839cfa286a8a156898e1)
[ゲタバコ先生のホームページ遊びに来てね♪](http://getabako.wp.xdomain.jp/)