+4

So sánh giữa CHARACTER CONTROLLER và RIGIDBODY trong Unity 3D (Phần 1)

Lời mở đầu

Không chỉ trong Unity mà dù trong bất kỳ engine nào, Character controller cũng là một trong những yếu tố tác động nhiều nhất đến trải nghiệm người dùng khi chơi game của bạn. Nếu bạn không chăm chút cho nó, game của bạn gần như sẽ cầm chắc thất bại cho dù các khía cạnh khác được đầu tư thế nào. Tuy nhiên để tạo một Character controller tốt cũng không phải việc đơn giản. Khi muốn tạo một character controller, sẽ có rất nhiều thứ bạn cần phải để ý đến, từ việc xử lý input của người chơi cho đến việc biến những input đó thành chuyển động trong game của mình

Một trong những câu hỏi đầu tiên bạn nên hỏi chính mình khi nghĩ đến việc tạo chuyển động cho game của mình đó là: Mình nên sử dụng Character controller hay Rigid body?

Trong series này, tôi và chúng ta sẽ thử phân tích và so sánh một chút về hai cách tiếp cận này bằng cách làm một vài chuyển động chạy nhảy đơn giản cho nhân vật. Hi vọng sau series này, các bạn sẽ có một sự thấu hiểu tốt hơn về Character controller và Rigid body trong Unity, qua đó chọn được phương pháp phù hợp nhất cho game của mình.

Cách sử dụng Character controller

Setup

Hãy tạo một project với một test scene như bình thường, project của bạn nên có cấu trúc thế này để dễ quản lý hơn.

Sau đó hãy tạo ra một scene tùy ý mình (lưu ý là cần có mặt sàn) để test chuyển động của nhân vật của chúng ta trong đó. Ví dụ như ở đây tôi đã tạo một scene tên Character controller có cầu thang để thực hiện những chuyển động nhảy và dùng một khối con nhộng đơn giản làm nhân vật để điều khiển.

Sceen của bạn nên có dạng thế này, hãy sử dụng các parent gameObject để dễ kiểm soát scence của mình hơn.

Character Controller

Character controller là một component trong Unity mà bạn có thể gán vào gameObject bạn muốn điều khiển. Tác dụng của nó là di chuyển gameObject trong môi trường scence. Nó không chịu tác động bởi hay ảnh hưởng gì đến những tính năng vật lý.

Character controller còn đi kèm với một Capsule Collider. Trước khi bắt đầu, bạn nên đọc qua về hướng dẫn của Unity về Character controllerscripting API thì sẽ tốt hơn.

Trong ví dụ này, tôi sử dụng nhưng paramenter mặc định nhưng bạn có thể tự do điều chỉnh chúng cho phù hợp với game của mình.

Nguyên tắc cốt lõi của Character Controller là nó cung cấp một Collier cơ bản mà không đi kèm tính chất vật lý nào. Cơ bản thì bạn sẽ di chuyển player của mình giống với khi sử dụng Transforms, nhưng bạn không thể đi xuyên qua các collider. Lợi thế của phương pháp này là chúng ta có thể kiểm soát chuyển động của player của mình một cách tốt hơn. Nhưng mặt trái của chuyện đó là bạn sẽ gần như phải code mọi thứ.

Character Controller có hai phương pháp dùng để di chuyển player: SimpleMoveMove.

SimpleMove nhận tốc độ làm tham số và sẽ di chuyển player dựa vào đó. Player sẽ chịu tác động của trọng lực, đó là hiệu ứng vật lý duy nhất mà Character controller cung cấp. Điểm trừ là phương pháp này sẽ bỏ qua tốc độ theo trục Y.

Move sẽ yêu cầu bạn xử lý nhiều hơn nhưng bạn sẽ ít bị hạn chế hơn. Nó nhận tham số là chuyển động tuyệt đối, vì thế bạn sẽ phải tự xử lý trọng lực bằng code của mình. Tuy tốn công hơn nhưng nó là phương pháp tôi hay sử dụng vì SimpleMove không thể kiểm soát chuyển động theo trục Y.

Những chuyển động cơ bản

Hãy thử implement một vài chuyển động đơn giản. Đầu tiên, hãy tạo một script C# mới có tên "Character" và gán nó vào player của chúng ta. Sau đó, chúng ta sẽ cần một biến public để có thể điều chỉnh tốc độ trực tiếp từ editor và một biến private để lưu trữ thông tin về Character controller của mình.

Trong hàm Update, chúng ta nhận những input, rồi lưu chúng vào một vector3. Sau đó chúng ta gọi hàm Move và chuyền vào nó vector của mình, nhân với tốc độ và DeltaTime để chuyển động của player không phụ thuộc vào framerate. Thế là xong, chúng ta đã có chuyển động cơ bản của mình.

public class Character : MonoBehaviour
{ 
private CharacterController _controller;

void Start()
    {
        _controller = GetComponent<CharacterController>();
    }
    
void Update()
    {
    Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    _controller.Move(move * Time.deltaTime * Speed);
    }
    
}

Trọng lực

Hãy thêm trọng lực vào để tăng tính chân thực cho chuyển động nào. Đầu tiên chúng ta sẽ cần một biến tượng trưng cho trọng lực (hoặc bạn có thể dùng biến mặc định của Unity - Physics.gravity.y) và một biến private để lưu tốc độ của player.

Sau đó chúng ta chỉ cần thêm trọng lực vào tốc độ của player với update và thực hiện tốc độ đó với hàm Move

_velocity.y += Gravity * Time.deltaTime;
_controller.Move(_velocity * Time.deltaTime);

Nếu chạy thử đoạn code này, bạn có thể sẽ nhận ra trọng lực của chúng ta đang hơi lạ. Đó là bởi vì kể cả khi player đã chạm đất, vận tốc vẫn tăng bởi ảnh hưởng của trọng lực. Để xử lý vấn đề này, chúng ta có thể reset vận tốc y về 0 khi player đã chạm đất.

CharacterController đã có sẵn một biến để xác định player đã chạm đất hay chưa, nhưng nó khá thiếu ổn định nên tôi quyết định sẽ tự mình xử lý vấn đề này. Tôi thích sử dụng method CheckSphere trong class Physics. Method này sẽ trả lại giá trị true nếu sphere của chúng ta va chạm với collider nào đó. Hãy tạo một gameObject trống ở giữa player của chúng ta làm tâm của sphere và dùng một biến để điều chỉnh bán kính của sphere đó. Bằng cách này chúng ta có thể điều chỉnh được bán kính của va chạm từ editor.

 _isGrounded = Physics.CheckSphere(_groundChecker.position, GroundDistance, Ground, QueryTriggerInteraction.Ignore);
        if (_isGrounded && _velocity.y < 0)
            _velocity.y = 0f;

Ở đây, _isGrounded là một biến bool được tạo ra trong class và _groundChecker là tâm của sphere collider. GroundDistance là bán kính của sphere và Ground là layer chứa mặt sàn.

Nhảy

Thực hiện một cú nhảy thực ra là việc khá dễ. Khi nút nhảy được bấm và player đang ở trên mặt đất, chúng ta sẽ thay đổi tốc độ theo trục Y của player. Chúng ta có thể sử dụng JumpForce để kiểm soát cú nhảy theo công thức đơn giản này:

if (Input.GetButtonDown("Jump") && _isGrounded)
    _velocity.y += JumpForce;

hoặc nếu các bạn thích phức tạp một chút để chúng ta có thể kiểm soát cú nhảy của mình tốt hơn, bao gồm cả độ cao của cú nhảy.

if (Input.GetButtonDown("Jump") && _isGrounded)
    _velocity.y += Mathf.Sqrt(JumpHeight * -2f * Gravity);

Lướt và phanh

Một ví dụ nữa về chuyển động mà bạn có thể sử dụng trong game của mình, đó là lướt. Khi bạn bấm nút lướt, player sẽ di chuyển về một hướng nhất định với tốc độ nhanh hơn bình thường. Giống như với hành động nhảy, bạn có thể dùng cách trực tiếp hoặc dùng một thuật toán để kiểm soát được tốt hơn. Thuật toán ở đây sẽ phức tạp hơn một chút và sau một vài thử nghiệm tôi đã thấy đây là cách tốt nhất.

 if (Input.GetButtonDown("Dash"))
        {
            Debug.Log("Dash");
            _velocity += Vector3.Scale(transform.forward, 
                                       DashDistance * new Vector3((Mathf.Log(1f / (Time.deltaTime * Drag.x + 1)) / -Time.deltaTime), 
                                                                  0, 
                                                                  (Mathf.Log(1f / (Time.deltaTime * Drag.z + 1)) / -Time.deltaTime)));
        }

Trong đoạn code này chúng ta sẽ tăng vector di chuyển của player về một hướng nhất định. Vector lướt sẽ phụ thuộc vào hai biến: khoảng cách lướt và lực cản. Chúng ta cần có lực cản nếu không thì player sẽ lao đi mãi mãi. Lực cản này là để mô phỏng lại lực ma sát trong thực tế (ma sát với không khí hoặc mặt đất). Lực cản là một giá trị có giá trị nằm giữa 0 và vô cùng, với 0 là mức hoàn toàn không có lực cản. Ở đây tôi đã chọn một Vector3 để biểu diễn lực cản, qua đó kiểm soát được lực cản theo cả ba trục. Cuối cùng, để áp dụng lực cản này lên player của chúng ta, chỉ cần thêm đoạn code này vào dưới hàm Update:

_velocity.x /= 1 + Drag.x * Time.deltaTime;
_velocity.y /= 1 + Drag.y * Time.deltaTime;
_velocity.z /= 1 + Drag.z * Time.deltaTime;

Kết luận về Character controller

Sau khi thử thực hiện một vài chuyển động cơ bản trong Unity bằng cách sử dụng Character controller, chắc các bạn cũng đã nhận ra ưu và nhược điểm của phương pháp này. Character controller cho chúng ta rất nhiều sự tự do để tùy chỉnh những chuyển động của mình, nhưng mặt trái của việc đó là chúng ta sẽ phải code gần như mọi thứ, bao gồm cả những chuyển động cơ bản nhất như trọng lực hay nhảy.

Phần kết luận này cũng là phần kết của phần 1 series này. Trong phần 2, chúng ta cũng sẽ thực hiện một vài chuyển động cơ bản trong Unity, nhưng bằng cách sử dụng Rigid body. Sau đó chúng ta sẽ cùng nhận xét và đưa ra so sánh về những ưu nhược điểm của hai cách tiếp cận này.

Xin cảm ơn các bạn đã dành thời gian để đọc bài viết này và hẹn gặp lại các bạn trong phần 2!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí