U
タブ 03カメラと入力6 レッスン — タップで開く
06 / 06 · 20 分
カメラと入力★ 練習課題

練習課題:指 1 本で遊べる

ここまでのプロジェクト

Assets/

Scenes/

Arena.unity

Ultimate vcam

Scripts/

CameraDirector.cs

Settings/

UI action map

everything from tab 01–03

すでにあるものこのレッスンで追加

練習課題2–3 時間

Arena に、キーボード・ゲームパッド・タッチの三つを単一の action map で動かす操作リグを備えましょう。壁を突き抜けない三人称カメラも一緒に設定します。手ブレがなく、攻撃が当たったときだけ小さく振動し、必殺技では見栄えの良いアングルへ切り替わります。

再利用するもの

Input Actions assetInput Actions アセットGenerated C# class自動生成の C# クラスCinemachineバーチャルカメラImpulse source / listenerカメラの振動Camera-relative movementカメラ相対の移動On-screen stick画面のスティックScreen.safeAreaセーフエリア
アニメーション図

三台の設備、ひとつの action map、ひとつのカメラリグ。

パート 1 — ひとつの action map ですべてをまかなう

一つの action に対して、三台の設備分のバインディングをまとめて付けます。if (touch) … else if (keyboard) … を書きそうになったら、そこで手を止めてください。action map はまさにそれを防ぐためにあります。

PlayerControls.inputactions — the map you must build
Map: Gameplay

  Move     Value / Vector2
     ├─ 2D Vector Composite  W A S D
     ├─ Gamepad              <Gamepad>/leftStick
     └─ On-Screen Stick      (bound from the UI prefab)

  Look     Value / Vector2
     ├─ Pointer delta        <Mouse>/delta
     ├─ Gamepad              <Gamepad>/rightStick
     └─ Touch drag           (right half of the screen)

  Jump     Button
     ├─ <Keyboard>/space     ├─ <Gamepad>/buttonSouth   └─ On-Screen Button

  Skill1 / Skill2 / Skill3   Button
     ├─ Q E R                ├─ West / North / buttonEast   └─ On-Screen Buttons

  Pause    Button
     ├─ <Keyboard>/escape    └─ <Gamepad>/start

Map: UI          ← enabled only while paused
  Navigate · Submit · Cancel
InputRouter.cs — the single place that reads input
using UnityEngine;
using UnityEngine.InputSystem;

public class InputRouter : MonoBehaviour
{
    private PlayerControls controls;

    public Vector2 Move { get; private set; }
    public Vector2 Look { get; private set; }
    public bool JumpQueued { get; private set; }

    [SerializeField] private SkillCaster caster;

    void Awake()
    {
        controls = new PlayerControls();
        var g = controls.Gameplay;

        g.Move.performed += c => Move = c.ReadValue<Vector2>();
        g.Move.canceled  += _ => Move = Vector2.zero;
        g.Look.performed += c => Look = c.ReadValue<Vector2>();
        g.Look.canceled  += _ => Look = Vector2.zero;

        g.Jump.performed   += _ => JumpQueued = true;
        g.Skill1.performed += _ => caster.TryCast(0);
        g.Skill2.performed += _ => caster.TryCast(1);
        g.Skill3.performed += _ => caster.TryCast(2);
        g.Pause.performed  += _ => TogglePause();
    }

    void OnEnable()  => controls.Enable();
    void OnDisable() => controls.Disable();
    void LateUpdate() => JumpQueued = false;

    void TogglePause()
    {
        bool paused = Time.timeScale > 0f;
        Time.timeScale = paused ? 0f : 1f;

        // swap which map is live
        if (paused) { controls.Gameplay.Disable(); controls.UI.Enable(); }
        else        { controls.UI.Disable(); controls.Gameplay.Enable(); }

        Cursor.lockState = paused ? CursorLockMode.None : CursorLockMode.Locked;
        Cursor.visible = paused;
    }
}

パート 2 — カメラリグを組み立てる

  • Third Person Follow の Cinemachine カメラ。Follow = Knight、LookAt は胸の高さに置いた CameraTarget にします。

  • Camera Collision Filter に Environment レイヤーを指定すれば、壁の手前でカメラが引き寄せられ、突き抜けません。

  • Damping は三軸とも 0.3。重みは感じられますが、酔うほどではありません。

  • 必殺技用に二台目のカメラ。普段は Priority 0、R を発動したときだけ 1.5 秒 20 へ引き上げます。

  • 二台のカメラに Impulse Listener、プレイヤーに Impulse Source を置き、攻撃を当てるたびに発生させます。

CameraDirector.cs
using System.Collections;
using UnityEngine;
using Unity.Cinemachine;

public class CameraDirector : MonoBehaviour
{
    [SerializeField] private CinemachineCamera gameplayCam;
    [SerializeField] private CinemachineCamera ultimateCam;
    [SerializeField] private CinemachineImpulseSource impulse;

    /// call from SkillCaster when a hit lands
    public void Shake(float strength = 0.35f) => impulse.GenerateImpulseWithForce(strength);

    /// call when the ultimate starts
    public void PlayUltimateShot(float seconds = 1.5f) => StartCoroutine(Cut(seconds));

    IEnumerator Cut(float seconds)
    {
        ultimateCam.Priority = 20;
        yield return new WaitForSeconds(seconds);
        ultimateCam.Priority = 0;
    }
}

パート 3 — タッチ操作のレイヤー

  • Canvas は Scale With Screen Size、基準解像度 1080 × 1920、Match 0.5 に設定します。

  • 左下に Move へバインドした On-Screen Stick、右下にスキルボタン三つとジャンプボタンを置きます。

  • ルートパネルに SafeAreaFitter を付け、すべての操作要素は中央ではなく角へアンカーします。

  • タッチ用 Canvas はデスクトップでは丸ごと無効化します。Awake で Application.isMobilePlatform を確認してください。

C# スクリプト
void Awake()
{
    bool mobile = Application.isMobilePlatform;

#if UNITY_EDITOR
    mobile = forceTouchInEditor;    // a bool you tick to test on desktop
#endif

    touchCanvas.SetActive(mobile);
}

できたことになる条件

  • ✓同じビルドが、コードに分岐を一つも足さずにキーボード・接続したゲームパッド・画面のタッチ操作のすべてに反応します。
  • ✓どのカメラアングルでも W は必ずカメラから遠ざかる方向へ歩きます。
  • ✓キャラを壁に近づけると、カメラは壁の内侧を見せる代わりに滑らかに手前へ寄ります。
  • ✓移動中にキャラが震えることはありません。カメラ処理は LateUpdate にあるからです。
  • ✓攻撃が当たるたびにカメラがわずかに揺れ、0.25 秒以内に収まります。
  • ✓必殺技では二台目のカメラへ切り替わり、終わったあとは自動でブレンドで戻ります。
  • ✓Esc で一時停止してカーソルが解放され、ゲームへの入力も止まります。キャラが歩き続けることはありません。
  • ✓プロジェクトの中に Input.GetAxis も Input.GetKey も一行残っていません。

狙うべき数値

項目目標値理由
カメラの Damping0.2–0.4 s0.2 未満だと飛び、0.5 を超えるとプレイヤーがキャラを見失います。
振動の長さ≤ 0.25 s長いとバグに見え、モバイルでは酔いの原因になります。
Pitch の制限-25° … 65°範囲を外れると、カメラが頭の上までひっくり返ります。
タッチ領域のサイズ≥ 44 ptこれより小さいと、実機では親指で何度も外します。

もっとやりたい人へ

ボーナス 1

感度スライダーと Y 軸反転のトグルを追加し、設定をプレイをまたいで保持するようにします。

ボーナス 2

操作中のデバイスを検知し、画面のキーヒントを Q/E/R とゲームパッドのアイコンで切り替えます。

ボーナス 3

カメラのロックオンを追加。マウスの中ボタンで最も近い敵を画面に収めます(Tab はすでにキャラ切り替えに使っています)。

ボーナス 4

Cinemachine の拡張を使い、プレイヤーが壁に近づいたらカメラをズームインさせます。