So sánh giữa CHARATER CONTROLLER và RIGID BODY trong Unity3D (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. Nếu Character controller không được chăm chút, game đó 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. Nhưng để tạo một Character controller tốt cũng không phải chuyện đơn giản. Khi 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, 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. Tôi hy vọng sau khi đọc xong series này, các bạn sẽ có hiểu biết rõ hơn về Character controller và Rigid Body trong Unity để có thể chọn ra cách xử lý phù hợp nhất với 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 controller và scripting 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: SimpleMove và Move.
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, tôi đã tăng tốc vector di chuyển của mình về một hướng với tốc độ nhanh hơn bình thường để tạo ra hiệu ứng lướt. Vector lướt này sẽ chịu ảnh hưởng của 2 biến: khoảng cách lướt và lực cản. Chúng ta cần phải có một lực cản nếu không player sẽ lao đi mãi không ngừng. Lực cản này là mô phỏng lại của hiệu ứng ma sát trong thực tế (ma sát với không khí, mặt đất...). Lực cản có thể 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 dùng một Vector3 để biểu diễn lực cản, qua đó có thể 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 vào player của mình, chúng ta chỉ cần thêm đoạn code sau đây vào 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
Trên đây là một vài ví dụ về cách các bạn có thể sử dụng Character Controller để tạo ra chuyển động trong game Unity của mình. Như các bạn có thể thấy, Character Controller cho chúng ta rất nhiều sự tự do để tùy chỉnh chuyển động của mình. Nhưng mặt trái của chuyện đó 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ư nhảy hay trọng lực.
Và đến đây cũng là kết thúc của phần 1 series này. Trong phần 2 của series, chúng ta cũng sẽ thực hiện một vài chuyển động cơ bản trong Unity3D như trên, nhưng bằng cách sử dụng thành phần RigidBody và phân tích một chút về những ưu nhược điểm của cách thực hiện này. Mong các bạn sẽ đón đọc.
Xin cảm ơn các bạn đã dành thời gian để đọc đến tận đây, và hẹn gặp lại các bạn trong phần 2 nhé!
BÌNH LUẬN