paint-brush
Cách tạo bộ điều khiển nhân vật 2D trong Unity: Phần 1từ tác giả@deniskondratev
544 lượt đọc
544 lượt đọc

Cách tạo bộ điều khiển nhân vật 2D trong Unity: Phần 1

từ tác giả Denis Kondratev15m2024/04/22
Read on Terminal Reader

dài quá đọc không nổi

Bài viết này đi sâu vào quá trình tạo bộ điều khiển ký tự 2D trong Unity, tập trung vào việc tích hợp phương thức `Slide` mới cho `Rigidbody2D` giúp đơn giản hóa việc di chuyển nhân vật. Nó bao gồm việc thiết lập, hành vi vật lý, xử lý va chạm và hạn chế di chuyển, cung cấp hướng dẫn cơ bản để phát triển bộ điều khiển nhân vật có thể được điều chỉnh và mở rộng cho nhiều trò chơi nền tảng 2D khác nhau.
featured image - Cách tạo bộ điều khiển nhân vật 2D trong Unity: Phần 1
Denis Kondratev HackerNoon profile picture
0-item
1-item
2-item


Một trong những yếu tố chính của bất kỳ nền tảng 2D nào là nhân vật chính. Cách nó di chuyển và điều khiển định hình đáng kể bầu không khí của trò chơi — cho dù đó là một trò chơi cổ điển ấm cúng hay một trò chơi sát thủ năng động. Do đó, việc tạo Bộ điều khiển nhân vật là giai đoạn đầu quan trọng trong quá trình phát triển một nền tảng.


Trong bài viết này, chúng tôi sẽ xem xét kỹ lưỡng quá trình tạo nhân vật từ đầu, dạy nó di chuyển theo cấp độ trong khi tuân thủ các định luật vật lý. Ngay cả khi bạn đã có kinh nghiệm tạo Bộ điều khiển nhân vật, bạn sẽ muốn tìm hiểu về những cải tiến trong Unity 2023. Thật ngạc nhiên, một phương pháp Slide được chờ đợi từ lâu đã được thêm vào thành phần Rigidbody2D , giúp đơn giản hóa đáng kể việc viết bộ điều khiển nhân vật bằng cách cho phép sử dụng Rigidbody2D ở chế độ Kinematic hiệu quả hơn. Trước đây, tất cả chức năng này phải được thực hiện thủ công.


Nếu bạn không chỉ muốn đọc bài viết mà còn muốn thử thực tế, tôi khuyên bạn nên tải xuống mẫu cấp độ từ kho lưu trữ GitHub Thợ săn kho báu , nơi đã bao gồm các nội dung cần thiết và cấp độ sẵn sàng để kiểm tra nhân vật của bạn.


Đặt nền tảng cho tính cách của chúng ta

Đối với nền tảng của chúng tôi, chúng tôi đã đặt ra một quy tắc rằng trò chơi sẽ chỉ có bề mặt dọc và ngang, và lực hấp dẫn sẽ hướng thẳng xuống dưới. Điều này giúp đơn giản hóa đáng kể việc tạo nền tảng ở giai đoạn đầu, đặc biệt nếu bạn không muốn đi sâu vào toán học vectơ.


Trong tương lai, tôi có thể đi chệch khỏi những quy tắc này trong dự án Thợ săn kho báu của mình, nơi tôi khám phá việc tạo ra cơ chế cho nền tảng 2D trên Unity. Nhưng đó sẽ là chủ đề của một bài viết khác.


Dựa trên quyết định của chúng tôi rằng chuyển động sẽ được thực hiện dọc theo các bề mặt nằm ngang, phần đế của nhân vật của chúng tôi sẽ có hình chữ nhật. Việc sử dụng các bề mặt nghiêng sẽ đòi hỏi phải phát triển một máy va chạm hình viên nang và các cơ chế bổ sung như trượt.


Đầu tiên, tạo một đối tượng trống trên hiện trường và đặt tên là Thuyền trưởng - đây sẽ là nhân vật chính của chúng ta. Thêm các thành phần Rigidbody2DBoxCollider2D vào đối tượng. Đặt loại Rigidbody2D thành Kinematic để chúng ta có thể điều khiển chuyển động của nhân vật trong khi vẫn sử dụng các khả năng vật lý tích hợp của Unity. Ngoài ra, hãy khóa chuyển động xoay của nhân vật dọc theo trục Z bằng cách kích hoạt tùy chọn Freeze Rotation Z.


Thiết lập của bạn sẽ giống như hình minh họa bên dưới.


Bây giờ hãy thêm diện mạo cho đội trưởng của chúng ta. Tìm kết cấu trong Assets/Textures/Treasure Hunters/Captain Clown Nose/Sprites/Captain Clown Nose/Captain Clown Nose with Sword/09-Idle Sword/Idle Sword 01.png và đặt giá trị Pixel Per Unit của nó thành 32. Chúng ta sẽ thường xuyên làm như vậy sử dụng giá trị này trong khóa học này vì họa tiết của chúng tôi được tạo với độ phân giải này. Đặt Chế độ Sprite thành Đơn và để thuận tiện, hãy đặt Pivot thành Bottom. Đừng quên áp dụng các thay đổi bằng cách nhấp vào nút Áp dụng. Hãy chắc chắn rằng tất cả các cài đặt được thực hiện chính xác.



Trong bài viết này, chúng tôi sẽ không đề cập đến hoạt hình nhân vật, vì vậy hiện tại, chúng tôi sẽ chỉ sử dụng một nhân vật. Trong đối tượng Captain, tạo một đối tượng lồng nhau có tên là Appearance và thêm thành phần Sprite Renderer vào nó, chỉ định sprite đã được cấu hình trước đó.



Khi tôi phóng to hình ảnh của thuyền trưởng, tôi nhận thấy nó khá mờ do cài đặt sprite không chính xác. Để khắc phục điều này, hãy chọn kết cấu Idle Sword 01 trong cửa sổ Project và đặt Chế độ lọc thành Điểm (không có bộ lọc). Bây giờ hình ảnh trông đẹp hơn nhiều.


Thiết lập máy va chạm ký tự

Hiện tại, vị trí của máy va chạm và hình ảnh thuyền trưởng của chúng tôi không khớp với nhau, và máy va chạm hóa ra quá lớn so với nhu cầu của chúng tôi.

Hãy khắc phục điều này và cũng định vị chính xác trục xoay của nhân vật ở phía dưới. Chúng tôi sẽ áp dụng quy tắc này cho tất cả các đồ vật trong trò chơi để tạo điều kiện thuận lợi cho việc sắp xếp chúng ở cùng cấp độ bất kể kích thước.


Để dễ dàng quản lý quy trình này, hãy sử dụng Chuyển đổi vị trí tay cầm công cụ với bộ Pivot, như hiển thị bên dưới.


Bước tiếp theo là điều chỉnh máy va chạm của anh hùng của chúng ta sao cho điểm xoay của nó nằm chính xác ở giữa phía dưới. Kích thước của máy va chạm phải khớp chính xác với kích thước của nhân vật. Điều chỉnh các thông số Offset và Size của máy va chạm, cũng như vị trí của đối tượng Appearance lồng nhau để đạt được độ chính xác cần thiết.


Đặc biệt chú ý đến tham số Offset.X của bộ va chạm: giá trị của nó phải hoàn toàn bằng 0. Điều này sẽ đảm bảo vị trí đối xứng của bộ va chạm so với tâm của đối tượng, điều này cực kỳ quan trọng đối với các lần xoay ký tự tiếp theo sang trái và phải, trong đó giá trị của Transform.Scale.X được thay đổi thành -1 và 1. Máy va chạm phải giữ nguyên vị trí và xoay hình ảnh sẽ trông tự nhiên.


Giới thiệu về hành vi thể chất của nhân vật

Đầu tiên và quan trọng nhất, điều cần thiết là phải dạy các nhân vật — dù là anh hùng chính, NPC hay kẻ thù — cách tương tác với thế giới vật chất. Ví dụ: các nhân vật phải có thể đi trên một bề mặt phẳng, nhảy khỏi bề mặt đó và chịu tác dụng của trọng lực, kéo họ trở lại mặt đất.


Unity có một công cụ vật lý tích hợp để điều khiển chuyển động của các vật thể, xử lý các va chạm và thêm tác động của ngoại lực lên vật thể. Tất cả điều này được thực hiện bằng cách sử dụng thành phần Rigidbody2D . Tuy nhiên, đối với các nhân vật, sẽ rất hữu ích nếu có một công cụ linh hoạt hơn tương tác với thế giới vật chất đồng thời giúp nhà phát triển có nhiều quyền kiểm soát hơn đối với hành vi của đối tượng.


Như tôi đã đề cập trước đó, trong các phiên bản trước của Unity, các nhà phát triển phải tự mình triển khai tất cả logic này. Tuy nhiên, trong Unity 2023, một phương pháp Slide mới đã được thêm vào thành phần Rigidbody2D , cho phép điều khiển linh hoạt đối tượng vật lý đồng thời cung cấp tất cả thông tin cần thiết về chuyển động được thực hiện.


Hãy bắt đầu bằng cách tạo một lớp CharacterBody , lớp này sẽ chứa logic cơ bản để di chuyển các nhân vật trong thế giới vật chất. Lớp này sẽ sử dụng Rigidbody2D ở chế độ Kinematic mà chúng tôi đã thêm vào nhân vật của mình. Vì vậy, điều đầu tiên là thêm một tham chiếu đến thành phần này.


 public class CharacterBody : MonoBehaviour { [SerializeField] private Rigidbody2D _rigidbody; }


Đôi khi, đối với động lực chuyển động của nhân vật, cần phải có trọng lực tác động mạnh hơn bình thường. Để đạt được điều này, chúng ta sẽ thêm hệ số ảnh hưởng trọng lực có giá trị ban đầu là 1 và hệ số này không thể nhỏ hơn 0.


 [Min(0)] [field: SerializeField] public float GravityFactor { get; private set; } = 1f;


Chúng ta cũng cần xác định những vật thể nào sẽ được coi là bề mặt không thể vượt qua. Để làm điều này, chúng tôi sẽ tạo một trường cho phép chúng tôi chỉ định các lớp cần thiết.


 [SerializeField] private LayerMask _solidLayers;


Chúng tôi sẽ giới hạn tốc độ của nhân vật để ngăn họ phát triển tốc độ quá cao, chẳng hạn như do một số ảnh hưởng bên ngoài, bao gồm cả trọng lực. Chúng tôi đặt giá trị ban đầu là 30 và giới hạn khả năng đặt giá trị nhỏ hơn 0 trong trình kiểm tra.


 [Min(0)] [SerializeField] private float _maxSpeed = 30;


Khi di chuyển dọc theo một bề mặt, chúng tôi muốn nhân vật luôn bám vào bề mặt đó nếu khoảng cách giữa chúng đủ nhỏ.


 [Min(0)] [SerializeField] private float _surfaceAnchor = 0.01f;


Mặc dù chúng tôi đã quyết định rằng các bề mặt trong trò chơi của mình sẽ chỉ nằm ngang hoặc thẳng đứng, nhưng để đề phòng, chúng tôi sẽ chỉ định góc dốc tối đa của bề mặt mà nhân vật có thể đứng ổn định, với giá trị ban đầu là 45°.


 [Range(0, 90)] [SerializeField] private float _maxSlop = 45f;


Thông qua thanh tra, tôi cũng muốn xem tốc độ hiện tại của nhân vật và trạng thái của họ, vì vậy tôi sẽ thêm hai trường với thuộc tính SerializeField .


 [SerializeField] private Vector2 _velocity; [field: SerializeField] public CharacterState State { get; private set; }


Có, ở đây tôi đã giới thiệu một thực thể mới nhưng chưa được xác định CharacterState . Chúng ta sẽ thảo luận thêm về vấn đề này.


Trạng thái ký tự

Để đơn giản hóa việc phát triển nền tảng của chúng ta, hãy xác định chỉ hai trạng thái nhân vật chính.

Đầu tiên là Grounded , trạng thái mà nhân vật đứng vững trên bề mặt. Ở trạng thái này, nhân vật có thể tự do di chuyển dọc theo bề mặt và nhảy khỏi nó.


Thứ hai là Airborne , một trạng thái rơi tự do, trong đó nhân vật ở trên không. Hành vi của nhân vật ở trạng thái này có thể khác nhau tùy thuộc vào đặc điểm cụ thể của người chơi nền tảng. Trong trường hợp tổng quát gần với thực tế, nhân vật di chuyển dưới tác động của động lượng ban đầu và không thể ảnh hưởng đến hành vi của nó. Tuy nhiên, trong platformer, vật lý thường được đơn giản hóa theo hướng thuận tiện và tính năng động trong lối chơi: ví dụ, trong nhiều trò chơi, ngay cả khi rơi tự do, chúng ta có thể điều khiển chuyển động ngang của nhân vật. Trong trường hợp của chúng tôi, điều này cũng có thể thực hiện được, cũng như cơ chế phổ biến của bước nhảy đôi, cho phép thực hiện thêm một bước nhảy trên không.


Hãy thể hiện các trạng thái của nhân vật của chúng ta trong mã:


 /// <summary> /// Describes the state of <see cref="CharacterBody"/>. /// </summary> public enum CharacterState { /// <summary> /// The character stays steady on the ground and can move freely along it. /// </summary> Grounded, /// <summary> /// The character is in a state of free fall. /// </summary> Airborne }


Điều đáng chú ý là có thể có nhiều trạng thái hơn. Ví dụ: nếu trò chơi bao gồm các bề mặt dốc, nhân vật có thể ở trạng thái trượt, không thể tự do di chuyển sang phải hoặc sang trái nhưng có thể trượt xuống dốc. Ở trạng thái như vậy, nhân vật cũng có thể nhảy, đẩy ra khỏi con dốc nhưng chỉ theo hướng dốc. Một trường hợp khác có thể xảy ra là trượt dọc theo một bức tường thẳng đứng, nơi ảnh hưởng của trọng lực bị suy yếu và nhân vật có thể bị đẩy theo chiều ngang.


Giới hạn tốc độ di chuyển

Chúng tôi đã xác định trường riêng _velocity , nhưng chúng tôi cần có thể lấy và đặt giá trị này từ bên ngoài đồng thời giới hạn tốc độ tối đa của nhân vật. Điều này đòi hỏi phải so sánh vectơ vận tốc đã cho với tốc độ tối đa cho phép.


Điều này có thể được thực hiện bằng cách tính độ dài của vectơ vận tốc, hoặc theo thuật ngữ toán học, độ lớn của nó. Cấu trúc Vector2 đã chứa thuộc tính magnitude cho phép chúng ta thực hiện việc này. Như vậy, nếu độ lớn của vectơ truyền vượt quá tốc độ tối đa cho phép thì chúng ta nên giữ nguyên hướng của vectơ nhưng hạn chế độ lớn của nó. Để làm điều này, chúng tôi nhân _maxSpeed với vectơ vận tốc chuẩn hóa (vectơ chuẩn hóa là vectơ có cùng hướng nhưng có độ lớn bằng 1).


Đây là những gì nó trông giống như trong mã:


 public Vector2 Velocity { get => _velocity; set => _velocity = value.magnitude > _maxSpeed ? value.normalized * _maxSpeed : value; }


Bây giờ chúng ta hãy xem xét kỹ hơn cách tính độ lớn của vectơ. Nó được xác định bởi công thức:



Tính căn bậc hai là một thao tác tốn nhiều tài nguyên. Mặc dù trong hầu hết các trường hợp tốc độ sẽ không vượt quá mức tối đa nhưng chúng ta vẫn phải thực hiện việc so sánh này ít nhất một lần trong mỗi chu kỳ. Tuy nhiên, chúng ta có thể đơn giản hóa đáng kể thao tác này nếu so sánh bình phương độ lớn của vectơ với bình phương tốc độ cực đại.


Đối với điều này, chúng tôi giới thiệu một trường bổ sung để lưu trữ bình phương của tốc độ tối đa và chúng tôi tính toán nó một lần trong phương pháp Awake :


 private float _sqrMaxSpeed; private void Awake() { _sqrMaxSpeed = _maxSpeed * _maxSpeed; }


Bây giờ việc thiết lập tốc độ có thể được thực hiện tối ưu hơn:


 public Vector2 Velocity { get => _velocity; set => _velocity = value.sqrMagnitude > _sqrMaxSpeed ? value.normalized * _maxSpeed : value; }


Vì vậy, chúng tôi tránh được những tính toán không cần thiết và cải thiện hiệu suất xử lý tốc độ di chuyển của nhân vật.


Phương pháp chuyển động của cơ thể cứng nhắc

Như tôi đã đề cập trước đó, Unity đã thêm một phương thức Slide() mới, điều này sẽ đơn giản hóa rất nhiều việc phát triển CharacterBody của chúng tôi. Tuy nhiên, trước khi sử dụng phương pháp này, cần xác định quy luật di chuyển của vật thể trong không gian. Hành vi này được thiết lập bởi cấu trúc Rigidbody2D.SlideMovement .


Hãy giới thiệu trường mới _slideMovement và đặt giá trị cho trường đó.


 private Rigidbody2D.SlideMovement _slideMovement; private void Awake() { _sqrMaxSpeed = _maxSpeed * _maxSpeed; _slideMovement = CreateSlideMovement(); } private Rigidbody2D.SlideMovement CreateSlideMovement() { return new Rigidbody2D.SlideMovement { maxIterations = 3, surfaceSlideAngle = 90, gravitySlipAngle = 90, surfaceUp = Vector2.up, surfaceAnchor = Vector2.down * _surfaceAnchor, gravity = Vector2.zero, layerMask = _solidLayers, useLayerMask = true, }; }



Điều quan trọng là phải giải thích rằng maxIterations xác định số lần một vật thể có thể thay đổi hướng do va chạm. Ví dụ: nếu nhân vật ở trên không cạnh một bức tường và người chơi cố gắng di chuyển nó sang bên phải trong khi trọng lực tác động lên nó. Do đó, đối với mỗi lệnh gọi phương thức Slide() , một vectơ vận tốc hướng sang phải và hướng xuống sẽ được đặt. Khi va vào tường, vectơ chuyển động được tính toán lại và vật sẽ tiếp tục chuyển động đi xuống.


Trong tình huống như vậy, nếu giá trị maxIterations được đặt thành 1, đối tượng sẽ va vào tường, dừng lại và mắc kẹt ở đó.


Các giá trị cho maxIterationslayerMask đã được xác định trước đó. Để biết thêm thông tin chi tiết về các trường khác, hãy xem tài liệu cấu trúc chính thức .


Cuối cùng là di chuyển nhân vật

Bây giờ mọi thứ đã sẵn sàng để Thuyền trưởng di chuyển. Chúng tôi sẽ thực hiện việc này trong FixedUpdate — một lệnh gọi lại trong Unity được thiết kế để xử lý vật lý. Trong vài năm qua, nhóm Unity đã cải thiện đáng kể khả năng xử lý vật lý 2D. Hiện tại, việc xử lý có thể được thực hiện trong lệnh gọi lại Update hoặc thậm chí bằng cách tự mình gọi phương thức được yêu cầu.


Tuy nhiên, trong ví dụ này, chúng tôi sẽ sử dụng phương pháp FixedUpdate truyền thống và đã được chứng minh. Trước khi tiếp tục, cần nói vài lời về giá trị của Time.fixedDeltaTime .


Để đảm bảo khả năng dự đoán về mặt vật lý của trò chơi, việc mô phỏng được thực hiện lặp đi lặp lại trong những khoảng thời gian cố định. Điều này đảm bảo rằng những thay đổi về FPS hoặc độ trễ không ảnh hưởng đến hành vi của đối tượng.


Vào đầu mỗi chu kỳ, chúng ta sẽ tính đến tác dụng của trọng lực lên vật. Vì trọng lực được cho bởi vectơ gia tốc rơi tự do nên ta có thể tính sự thay đổi vận tốc Δv của vật theo thời gian Δt theo công thức:




trong đó a là gia tốc không đổi của vật. Trong trường hợp của chúng tôi, đó là gia tốc do trọng lực, xét đến hệ số mà chúng tôi đã giới thiệu — Physics2D.gravity * GravityFactor . Do đó, Δv có thể được tính như sau:


 Time.fixedDeltaTime * GravityFactor * Physics2D.gravity


Kết quả cuối cùng, nơi chúng tôi thay đổi vận tốc, trông như thế này:


 Velocity += Time.fixedDeltaTime * GravityFactor * Physics2D.gravity;


Bây giờ chúng ta có thể thực hiện chuyển động cơ thể cứng nhắc của nhân vật:


 var slideResults = _rigidbody.Slide( _velocity, Time.fixedDeltaTime, _slideMovement);


Biến slideResults là một giá trị của cấu trúc SlideResults và lưu trữ kết quả của chuyển động. Các trường chính của kết quả này đối với chúng tôi là slideHit , kết quả của sự va chạm với bề mặt trong quá trình di chuyển và surfaceHit - kết quả của việc ném xuống, sẽ giúp xác định xem nhân vật có đang đứng trên một bề mặt ổn định hay không.


Xử lý va chạm

Khi va chạm với các bề mặt, điều quan trọng là phải hạn chế tốc độ của nhân vật hướng về bề mặt đó. Một ví dụ đơn giản là nếu nhân vật đang đứng yên trên mặt đất, họ không nên tiếp tục tăng tốc dưới tác động của trọng lực. Vào cuối mỗi chu kỳ, tốc độ của chúng sẽ bằng không. Tương tự, khi di chuyển lên trên và chạm trần nhà, nhân vật sẽ mất toàn bộ tốc độ thẳng đứng và bắt đầu di chuyển xuống dưới.


Kết quả của các va chạm, slideHitsurfaceHit , được biểu thị bằng các giá trị của cấu trúc RaycastHit2D , bao gồm bình thường của bề mặt va chạm.


Giới hạn tốc độ có thể được tính bằng cách lấy chính vectơ vận tốc trừ đi hình chiếu của vectơ vận tốc ban đầu lên pháp tuyến va chạm. Điều này được thực hiện bằng cách sử dụng tích số chấm . Hãy viết một phương thức sẽ thực hiện thao tác này:


 private static Vector2 ClipVector(Vector2 vector, Vector2 hitNormal) { return vector - Vector2.Dot(vector, hitNormal) * hitNormal; }


Bây giờ hãy tích hợp phương pháp này vào FixedUpdate của chúng tôi. Ở đây, đối với surfaceHit , chúng tôi sẽ chỉ giới hạn tốc độ nếu nó hướng xuống dưới, vì thao tác xác định xem vật thể có ở trên bề mặt hay không luôn được thực hiện để kiểm tra sự tiếp xúc với mặt đất.


 private void FixedUpdate() { Velocity += Time.fixedDeltaTime * GravityFactor * Physics2D.gravity; var slideResults = _rigidbody.Slide( _velocity, Time.fixedDeltaTime, _slideMovement); if (slideResults.slideHit) { _velocity = ClipVector(_velocity, slideResults.slideHit.normal); } if (_velocity.y <= 0 && slideResults.surfaceHit) { var surfaceHit = slideResults.surfaceHit; _velocity = ClipVector(_velocity, surfaceHit.normal); } }


Việc triển khai này cho phép quản lý chính xác chuyển động của nhân vật, tránh gia tốc không mong muốn khi va chạm với nhiều bề mặt khác nhau và giữ cho chuyển động của nhân vật trong trò chơi có thể đoán trước và mượt mà.


Xác định trạng thái của nhân vật

Vào cuối mỗi chu kỳ, cần xác định xem nhân vật đang ở trên một bề mặt rắn (Trạng thái nối đất) hay rơi tự do (hoặc, như chúng tôi đã định nghĩa, rơi có kiểm soát—Trạng thái trên không).


Để coi nhân vật ở trạng thái Nối đất, về cơ bản, tốc độ dọc của chúng phải bằng 0 hoặc âm, tốc độ này chúng tôi xác định bằng giá trị của _velocity.y .


Một tiêu chí quan trọng khác là sự hiện diện của bề mặt dưới chân nhân vật, mà chúng tôi xác định được từ kết quả chuyển động của Rigidbody, cụ thể là thông qua sự hiện diện của surfaceHit .


Yếu tố thứ ba là góc nghiêng của bề mặt, được chúng tôi phân tích dựa trên pháp tuyến của bề mặt này, tức là giá trị của surfaceHit.normal . Cần phải so sánh góc này với _maxSlop - góc tối đa có thể có của bề mặt mà nhân vật có thể đứng vững.


Đối với một bề mặt hoàn toàn thẳng đứng, pháp tuyến sẽ hoàn toàn nằm ngang, tức là giá trị vectơ của nó sẽ là (1, 0) hoặc (-1, 0). Đối với bề mặt nằm ngang, giá trị bình thường sẽ là (0, 1). Góc nghiêng càng nhỏ thì giá trị y càng lớn. Đối với góc alpha , giá trị này có thể được tính như sau:



Vì góc của chúng ta được tính bằng độ và hàm $\cos$ yêu cầu radian nên công thức được chuyển thành:



Để làm điều này, hãy giới thiệu một trường mới và tính toán nó bằng phương pháp Awake .


 private float _minGroundVertical; private void Awake() { _minGroundVertical = Mathf.Cos(_maxSlop * Mathf.PI / 180f); //... }


Bây giờ hãy cập nhật mã của chúng tôi trong FixedUpdate , kiểm tra tất cả các điều kiện trên.


 if (_velocity.y <= 0 && slideResults.surfaceHit) { var surfaceHit = slideResults.surfaceHit; Velocity = ClipVector(_velocity, surfaceHit.normal); State = surfaceHit.normal.y >= _minGroundVertical ? CharacterState.Grounded : CharacterState.Airborne; } else { State = CharacterState.Airborne; }


Logic này sẽ cho phép chúng tôi xác định chính xác thời điểm nhân vật ở trên mặt đất và phản ứng chính xác với những thay đổi trong trạng thái của họ.


Thêm CharacterBody vào Thuyền trưởng

Bây giờ thành phần CharacterBody của chúng ta đã sẵn sàng, bước cuối cùng là thêm nó vào Captain. Trong cảnh, chọn đối tượng Captain và thêm thành phần CharacterBody vào nó.

Đừng quên cấu hình Rigidbody như trong hình minh họa ở trên. Đặt Hệ số trọng lực thành 3 và chọn tùy chọn Mặc định cho Lớp rắn.


Bây giờ bạn có thể bắt đầu trò chơi và thử nghiệm cài đặt các giá trị khác nhau cho Vận tốc để quan sát cách nhân vật của chúng ta di chuyển xung quanh khung cảnh.

Kết thúc bây giờ

Tất nhiên, chúng ta vẫn cần thêm điều khiển nhân vật. Tuy nhiên, bài viết này đã khá dài nên tôi sẽ trình bày chi tiết cách điều khiển nhân vật bằng Hệ thống đầu vào mới trong bài viết tiếp theo: "Tạo Bộ điều khiển nhân vật 2D trong Unity: Phần 2."


Bạn có thể tải xuống dự án hoàn chỉnh được mô tả trong bài viết này tại đây: Thợ săn kho báu và kiểm tra mọi thứ trong thực tế nếu bạn gặp bất kỳ khó khăn nào. Phát triển bộ điều khiển nhân vật là một khía cạnh quan trọng trong việc tạo ra nền tảng 2D, vì nó quyết định sự phát triển hơn nữa của trò chơi. Nó ảnh hưởng đến mức độ dễ dàng thêm các tính năng mới vào hành vi của anh hùng chính hoặc kẻ thù. Vì vậy, điều rất quan trọng là phải hiểu những điều cơ bản để có thể phát triển trò chơi của riêng mình một cách độc lập.