環境:Unity 2019.2.13f1
カメラは平行移動と中心座標を回転する。キャラクターは一定の距離が開くと中心座標に向かって走る。
カメラのコード
クラス名はCameraController2で、前回のコードを差し替える形で作成。
キャラクターとカメラ
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraController2 : MonoBehaviour
{
public Vector3 cameraRotation = new Vector3();
Vector3 currentCamRotation = Vector3.zero;
public float armLength = 4.0f;
public float speed = 2.0f;
public Vector3 target = new Vector3();
Vector3 currentLookAtPos = new Vector3();
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
// ゲームパッド右スティックからの値を加算する
cameraRotation += new Vector3(-Input.GetAxis("Vertical2"), -Input.GetAxis("Horizontal2"), 0) * 0.05f;
// X軸回転の制限
cameraRotation.x = Mathf.Clamp(cameraRotation.x, 30 * Mathf.Deg2Rad, 75 * Mathf.Deg2Rad);
// 遅延用の角度との差分をとる
Vector3 diff = cameraRotation - currentCamRotation;
currentCamRotation += WrapAngle(diff) * 0.2f;
// 角度からベクトルを計算する
Vector3 craneVec = new Vector3
(
Mathf.Cos(currentCamRotation.x) * Mathf.Cos(currentCamRotation.y),
Mathf.Sin(currentCamRotation.x),
Mathf.Cos(currentCamRotation.x) * Mathf.Sin(currentCamRotation.y)
);
// ゲームパッドの左スティックのベクトルを取得する
Vector3 direction = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
// カメラのY軸角度から回転行列を生成する
Quaternion rot = Quaternion.Euler(0, (cameraRotation.y * Mathf.Rad2Deg + 90), 0);
// 逆行列を生成する
Matrix4x4 m = Matrix4x4.TRS(Vector3.zero, rot, Vector3.one);
Matrix4x4 inv = m.inverse;
// 回転行列をかけたベクトルに変換する
direction = inv.MultiplyVector(direction);
target += direction * direction.magnitude * Time.deltaTime * speed;
// 注視点の座標
Vector3 lookAtPos = target + new Vector3(0, 0, 0);
currentLookAtPos += (lookAtPos - currentLookAtPos) * 0.2f;
// カメラの座標を更新する
this.transform.position = currentLookAtPos + craneVec * armLength;
// プレイヤーの座標にカメラを向ける
this.transform.LookAt(currentLookAtPos);
}
// 角度を0~360°に収める関数
Vector3 WrapAngle(Vector3 vector)
{
vector.x %= Mathf.PI * 2;
vector.y %= Mathf.PI * 2;
vector.z %= Mathf.PI * 2;
return vector;
}
}
プレイヤーのコード
カメラの中心座標を目標にキャラクターが追従していくコード。PlayerController2というクラスで作成。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// ステート
public enum StateType
{
Idle,
Follow,
}
public class PlayerController2 : MonoBehaviour
{
public float rotationSpeed = 360.0f;
public float moveSpeed = 1.0f;
GameObject camera; // 外部のオブジェクトを参照する
Animator animator;
Vector3 pos = new Vector3();
Vector3 currentPos = new Vector3();
// アニメーションのブレンドに使う変数
float speed;
// 追従フラグ
bool isFollow;
private StateType state;
// Start is called before the first frame update
void Start()
{
camera = GameObject.FindGameObjectWithTag("MainCamera");
animator = this.GetComponentInChildren<Animator>();
}
// Update is called once per frame
void Update()
{
// 他のオブジェクトのスクリプトを読み込む(スクリプトはクラス扱い)
CameraController2 cameraScript = camera.GetComponent<CameraController2>();
// Slerpは球面線形補間、Vector3.Angleは二点間の角度(degree)を返す
Vector3 forward = Vector3.Slerp(this.transform.forward, (cameraScript.target - pos), rotationSpeed * Time.deltaTime / Vector3.Angle(this.transform.forward, (cameraScript.target - pos)));
// 目標との距離
float distance = (pos - cameraScript.target).magnitude;
// ステートマシン
switch(state)
{
// 待機
case StateType.Idle:
// 距離が離れていたら追いかけるフラグを立てる
if (distance > 2.0f)
{
state = StateType.Follow;
}
else
{
speed -= 2.0f * Time.deltaTime;
if (speed <= 0) speed = 0;
}
break;
// 追跡
case StateType.Follow:
// ベクトル方向へ向かせる
transform.LookAt(pos + forward);
// 減速
if (distance < 1.5f)
{
speed -= 1.0f * Time.deltaTime;
if (speed <= 0) speed = 0;
}
// 加速
else
{
speed += 0.5f * Time.deltaTime;
if (speed > 1.0f) speed = 1.0f;
}
pos += this.transform.forward * speed * moveSpeed * Time.deltaTime;
this.transform.position = pos;
// 立ち止まるフラグ
if (distance < 0.5f)
state = StateType.Idle;
break;
}
// アニメーターコントローラへ値を渡す(最大が1になるベクトルの長さ)
animator.SetFloat("Blend", speed);
}
}