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

Camera bám theo

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

Assets/

Scenes/

Arena.unity

Knight

Scripts/

CharacterSwitcher.csSwapCamera.cs→FollowCamera.cs

đã có sẵnbài này thêmbài này thay thế

Mở Arena.unity ra. SwapCamera ở tab 01 đã bám theo nhân vật đang chọn, nhưng nó dính cứng, nó nhìn vào chân, và nó xuyên tường thoải mái. Tab này dựng lại nó qua ba bước, và đây là bước một.

Camera dán cứng vào nhân vật cho cảm giác rẻ tiền. Camera đuổi theo với độ trễ nhẹ mới ra chất game.

sơ đồ động

Nhân vật đi trước, camera bắt kịp trong vài khung hình sau đó.

Script bám theo

gỡ SwapCamera ra trước
using UnityEngine;

public class FollowCamera : MonoBehaviour
{
    [SerializeField] private Transform target;
    [SerializeField] private Vector3 offset = new Vector3(0f, 6f, -8f);
    [SerializeField] private float smoothTime = 0.15f;
    [SerializeField] private Vector3 lookAtOffset = new Vector3(0f, 1.5f, 0f);

    private Vector3 velocity;      // SmoothDamp giữ trạng thái ở đây

    void LateUpdate()
    {
        if (target == null) return;

        Vector3 wanted = target.position + offset;
        transform.position = Vector3.SmoothDamp(transform.position, wanted, ref velocity, smoothTime);
        transform.LookAt(target.position + lookAtOffset);
    }
}

Dùng Lerp hay SmoothDamp?

✓ Vector3.Lerp(a, b, t * dt)

Đủ dùng, hơi phụ thuộc tốc độ khung hình

Dễ đọc, nhưng độ mượt thay đổi chút theo tốc độ khung hình.

✓ Vector3.SmoothDamp(...)

Chuẩn hơn, có quán tính

Giống lò xo, không phụ thuộc tốc độ khung hình, và smoothTime tính bằng giây thật.

Vẫn phải bám nhân vật đang chọn

Gán cứng target vào Knight là hỏng luôn phím 2 và 3. Hãy hỏi bộ chuyển nhân vật, đúng như SwapCamera vẫn làm.

C# script
[SerializeField] private CharacterSwitcher switcher;

void LateUpdate()
{
    Transform target = switcher.ActiveCharacter;    // không gán cứng
    Vector3 wanted = target.position + offset;

    transform.position = Vector3.SmoothDamp(transform.position, wanted, ref velocity, smoothTime);
    transform.LookAt(target.position + lookAtOffset);
}

Vì sao luôn dùng LateUpdate

  • Unity chạy các hàm Update theo thứ tự không xác định. Camera của bạn có thể chạy trước khi nhân vật di chuyển.

  • LateUpdate chạy sau khi mọi Update đã xong, nên camera luôn thấy vị trí cuối cùng của khung hình đó.

  • Dấu hiệu làm sai: nhân vật rung nhẹ một hai pixel khi di chuyển.

Nhìn trước — thấy đường phía trước

Dịch camera nhẹ về hướng đang đi giúp người chơi thấy sớm thứ phía trước. Chỉ tốn ba dòng.

C# script
[SerializeField] private float lookAhead = 2.5f;
private CharacterController targetCC;

void LateUpdate()
{
    Vector3 ahead = targetCC.velocity.normalized * lookAhead;
    ahead.y = 0f;

    Vector3 wanted = target.position + offset + ahead;
    transform.position = Vector3.SmoothDamp(transform.position, wanted, ref velocity, smoothTime);
    transform.LookAt(target.position + lookAtOffset);
}

Chống camera xuyên tường

add before assigning the position
[SerializeField] private LayerMask obstacles;

Vector3 pivot = target.position + lookAtOffset;
Vector3 dir = (wanted - pivot).normalized;
float dist = Vector3.Distance(pivot, wanted);

// có bức tường nào giữa nhân vật và chỗ camera muốn đứng không?
if (Physics.SphereCast(pivot, 0.3f, dir, out RaycastHit hit, dist, obstacles))
    wanted = hit.point - dir * 0.25f;      // kéo camera ra trước tường

Lỗi hay gặp

  • smoothTime bằng 0 thì dính cứng y như SwapCamera cũ; trên 0.5 giây thì camera tụt lại xa tới mức nhân vật chạy ra khỏi màn hình. Giữ trong khoảng 0.1 tới 0.25 giây rồi chỉnh dần.

  • Khai báo velocity bên trong LateUpdate là reset SmoothDamp mỗi khung hình, chuyển động thành nhún nhảy và sai. Nó phải là một field, vì SmoothDamp giữ trạng thái ở đó giữa các khung hình.

  • Main Camera bị kéo vào làm con của nhân vật trong Hierarchy sẽ thừa hưởng mọi phép xoay của cha và giằng co với script này. Để camera ở gốc scene và để script lo phần bám theo.