U
tab 03Camera & Điều khiển6 bài — chạm để mở
06 / 06 · 20 phút
Camera & Điều khiển★ bài tập

Bài tập: chơi được bằng một ngón tay

dự án của bạn tới lúc này

Assets/

Scenes/

Arena.unity

Ultimate vcam

Scripts/

CameraDirector.cs

Settings/

UI action map

everything from tab 01–03

đã có sẵnbài này thêm

bài tập2–3 giờ

Cho Arena một bộ điều khiển chạy được trên bàn phím, tay cầm và cảm ứng chỉ từ một action map — kèm camera góc nhìn thứ ba không xuyên tường, không giật, rung khi trúng đòn, và chuyển sang góc quay đẹp khi dùng chiêu cuối.

dùng lại những gì

Input Actions assetfile Input ActionsGenerated C# classclass tự sinhCinemachinecamera ảoImpulse source / listenerrung cameraCamera-relative movementđi theo hướng cameraOn-screen stickcần gạt ảoScreen.safeAreavùng an toàn
sơ đồ động

Ba thiết bị, một action map, một bộ camera.

Phần 1 — một action map cho tất cả

Mỗi action phải có binding cho cả ba thiết bị. Nếu thấy mình đang viết if (touch) … else if (keyboard) … thì dừng lại — action map sinh ra chính là để tránh chuyện đó.

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      (gán từ prefab UI)

  Look     Value / Vector2
     ├─ Pointer delta        <Mouse>/delta
     ├─ Gamepad              <Gamepad>/rightStick
     └─ Touch drag           (nửa phải màn hình)

  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          ← chỉ bật khi tạm dừng
  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;

        //đổi map đang bật
        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;
    }
}

Phần 2 — bộ camera

  • Một camera Cinemachine kiểu Third Person Follow, Follow = Knight, LookAt = CameraTarget đặt ngang ngực.

  • Camera Collision Filter chọn layer Environment, để nó tự thu vào khi gặp tường thay vì xuyên qua.

  • Damping 0.3 cho cả ba trục — đủ nặng tay, chưa tới mức lảo đảo.

  • Camera thứ hai cho chiêu cuối, bình thường Priority 0, khi bấm R thì nâng lên 20 trong 1,5 giây.

  • Impulse Listener trên cả hai camera, Impulse Source trên nhân vật, kích hoạt mỗi lần trúng đòn.

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;

    //gọi từ SkillCaster khi trúng đòn
    public void Shake(float strength = 0.35f) => impulse.GenerateImpulseWithForce(strength);

    //gọi khi bắt đầu chiêu cuối
    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;
    }
}

Phần 3 — lớp cảm ứng

  • Canvas đặt Scale With Screen Size, tham chiếu 1080 × 1920, Match 0.5.

  • Góc dưới trái: On-Screen Stick gán vào Move. Góc dưới phải: ba nút skill và nút nhảy.

  • SafeAreaFitter trên panel gốc, và mọi nút neo vào góc, đừng neo giữa.

  • Cả canvas cảm ứng bị tắt trên máy tính — kiểm tra Application.isMobilePlatform trong Awake.

C# script
void Awake()
{
    bool mobile = Application.isMobilePlatform;

#if UNITY_EDITOR
    mobile = forceTouchInEditor;    // tick để thử trên PC
#endif

    touchCanvas.SetActive(mobile);
}

Khi nào thì xong

  • ✓Cùng một bản build nhận cả bàn phím, tay cầm cắm vào, và nút cảm ứng — mà không có nhánh if nào trong code.
  • ✓Bấm W luôn là đi ra xa camera, ở mọi góc quay.
  • ✓Đi sát tường thì camera thu vào mượt mà thay vì để lộ phần bên trong bức tường.
  • ✓Nhân vật không rung khi di chuyển — code camera nằm trong LateUpdate.
  • ✓Mỗi cú trúng đòn camera rung nhẹ; rung hết trong vòng 0,25 giây.
  • ✓Chiêu cuối chuyển sang camera thứ hai rồi tự chuyển mượt về.
  • ✓Bấm Esc là tạm dừng, thả con trỏ, và input gameplay tắt — nhân vật không đi tiếp nữa.
  • ✓Trong dự án không còn dòng Input.GetAxis hay Input.GetKey nào.

Con số cần đạt

Chỉ sốMục tiêuVì sao
Độ trễ camera0.2–0.4 sDưới 0,2 là dính cứng, trên 0,5 là người chơi lạc mất nhân vật.
Thời gian rung≤ 0.25 sLâu hơn là tưởng lỗi, và trên mobile gây chóng mặt.
Giới hạn góc ngẩng / cúi-25° … 65°Ra ngoài khoảng đó camera lộn qua đầu.
Kích thước vùng chạm≥ 44 ptNhỏ hơn là ngón cái bấm trượt liên tục trên máy thật.

Muốn làm thêm

thêm 1

Thêm thanh trượt độ nhạy và nút đảo trục Y, giữ lại giữa các lần chơi.

thêm 2

Nhận diện thiết bị đang dùng và đổi gợi ý phím trên màn hình giữa Q/E/R và ký hiệu tay cầm.

thêm 3

Thêm camera khoá mục tiêu: bấm chuột giữa để ngắm vào kẻ địch gần nhất. (Phím Tab đã dùng để đổi nhân vật.)

thêm 4

Thu camera lại khi nhân vật đứng sát tường, dùng extension của Cinemachine.