Dự án tham khảo về cây trồng

*Nội dung này được dịch bằng AI (Beta) và có thể có lỗi. Để xem trang này bằng tiếng Anh, hãy nhấp vào đây.

Cây trồng là một trò chơi tham khảo nơi người chơi trồng và tưới nước cho hạt giống, để họ có thể thu hoạch và bán cây trồng sau này.

Biểu ngữ dự án Cây trồng

Dự án tập trung vào các trường hợp sử dụng phổ biến mà bạn có thể gặp phải khi phát triển một trò chơi trên Roblox. Khi có thể, bạn sẽ tìm thấy các ghi chú về sự đánh đổi, thỏa hiệp và lý do của các lựa chọn triển khai khác nhau, để bạn có thể đưa ra quyết định tốt nhất cho các trò chơi của riêng mình.

Nhận tệp

  1. Điều hướng đến trang trò chơi Cây trồng.
  2. Nhấp vào nút Chỉnh sửa trong Studio.

Các trường hợp sử dụng

Cây trồng bao gồm các trường hợp sử dụng sau:

  • Dữ liệu phiên và dữ liệu người chơi
  • Quản lý giao diện người dùng
  • Mạng lưới máy khách-máy chủ
  • Trải nghiệm người dùng lần đầu (FTUE)
  • Mua hàng bằng tiền tệ cứng và mềm

Ngoài ra, dự án này giải quyết các vấn đề hẹp hơn có thể áp dụng cho nhiều trò chơi, bao gồm:

  • Tùy chỉnh một khu vực trong nơi mà liên kết với một người chơi
  • Quản lý tốc độ di chuyển của nhân vật người chơi
  • Tạo một đối tượng theo dõi các nhân vật
  • Phát hiện phần nào của thế giới mà một nhân vật đang ở

Lưu ý rằng có một số trường hợp sử dụng trong trò chơi này quá nhỏ, quá ngách, hoặc không thể hiện một giải pháp cho một thách thức thiết kế thú vị; những điều này không được đề cập.

Cấu trúc dự án

Quyết định đầu tiên khi tạo một trò chơi là quyết định cách cấu trúc dự án, chủ yếu bao gồm việc đặt các thể hiện cụ thể trong mô hình dữ liệu và cách tổ chức và cấu trúc các điểm vào cho cả mã máy khách và máy chủ.

Mô hình dữ liệu

Bảng sau mô tả các dịch vụ chứa mà các thể hiện trong mô hình dữ liệu được đặt vào.

Dịch vụCác loại thể hiện
Workspace

Chứa các mô hình tĩnh đại diện cho thế giới 3D, cụ thể là các phần của thế giới không thuộc về bất kỳ người chơi nào. Bạn không cần phải tạo, sửa đổi hoặc phá hủy các thể hiện này một cách động tại thời gian chạy, vì vậy việc để chúng ở đây là chấp nhận được.

Cũng có một Folder trống, nơi các mô hình trang trại của người chơi sẽ được thêm vào tại thời gian chạy.

Lighting

Các hiệu ứng khí quyển và ánh sáng.

ReplicatedFirst

Chứa tập hợp nhỏ nhất có thể của các thể hiện cần thiết để hiển thị màn hình tải và khởi tạo trò chơi. Càng nhiều thể hiện được đặt trong ReplicatedFirst, thời gian chờ để chúng sao chép trước khi mã trong ReplicatedFirst có thể chạy sẽ càng lâu.

  • Trong thư mục Instances có GUI màn hình tải.
  • Trong thư mục Source có mã màn hình tải và mã cần thiết để chờ phần còn lại của trò chơi tải. start LocalScript là điểm vào cho tất cả mã phía máy khách trong dự án.
ReplicatedStorage

Đóng vai trò như một container lưu trữ cho tất cả các thể hiện mà cần truy cập từ cả máy khách và máy chủ.

  • Trong thư mục Dependencies có một số thư viện bên thứ ba được sử dụng bởi dự án.
  • Trong thư mục Instances có một loạt các thể hiện được chế tạo sẵn.
  • Trong thư mục Source có tất cả mã không cần thiết cho quá trình tải mà cần phải truy cập từ cả máy khách và máy chủ.
ServerScriptService

Chứa một Script phục vụ như điểm vào cho tất cả mã phía máy chủ trong dự án.

ServerStorage

Đóng vai trò như một container lưu trữ cho tất cả các thể hiện không cần phải sao chép đến máy khách.

  • Trong thư mục Instances có một mô hình mẫu Farm. Một bản sao của mô hình này được đặt trong Workspace khi người chơi tham gia trò chơi, nơi nó sẽ được sao chép đến tất cả người chơi.
  • Trong thư mục Source có tất cả mã chỉ dành cho máy chủ.
SoundService

Chứa các đối tượng Sound được sử dụng cho các hiệu ứng âm thanh trong trò chơi. Dưới SoundService, các đối tượng Sound này không có vị trí và không được mô phỏng trong không gian 3D.

Điểm vào

Hầu hết các dự án tổ chức mã bên trong các ModuleScripts có thể tái sử dụng mà có thể được nhập khẩu trên toàn bộ mã nguồn. ModuleScripts có thể tái sử dụng nhưng chúng không thực thi một mình; chúng cần được nhập khẩu bởi một Script hoặc LocalScript. Nhiều dự án Roblox sẽ có một số lượng lớn các đối tượng ScriptLocalScript, mỗi đối tượng liên quan đến một hành vi hoặc hệ thống cụ thể trong trò chơi, tạo ra nhiều điểm vào.

Đối với microgame Cây trồng, một cách tiếp cận khác được thực hiện thông qua một LocalScript duy nhất là điểm vào cho tất cả mã phía máy khách, và một Script duy nhất là điểm vào cho tất cả mã phía máy chủ. Cách tiếp cận đúng cho dự án của bạn phụ thuộc vào yêu cầu của bạn, nhưng một điểm vào duy nhất cung cấp kiểm soát tốt hơn về thứ tự mà các hệ thống được thực thi.

Các danh sách sau mô tả sự đánh đổi của cả hai cách tiếp cận:

  • Một Script duy nhất và một LocalScript duy nhất bao phủ mã máy chủ và máy khách tương ứng.
  • Kiểm soát tốt hơn về thứ tự mà các hệ thống khác nhau được khởi động vì tất cả mã được khởi tạo từ một script duy nhất.
  • Có thể truyền các đối tượng theo tham chiếu giữa các hệ thống.

Kiến trúc hệ thống cấp cao

Các hệ thống cấp cao trong dự án được chi tiết dưới đây. Một số hệ thống này phức tạp hơn nhiều so với những hệ thống khác, và trong nhiều trường hợp, chức năng của chúng được trừu tượng hóa qua một hệ thống phân cấp của các lớp khác.

Sơ đồ kiến trúc hệ thống dự án Cây trồng

Mỗi hệ thống này là một "singleton", vì nó là một lớp không thể khởi tạo mà thay vào đó được khởi tạo bởi script start của máy khách hoặc máy chủ liên quan. Bạn có thể đọc thêm về mẫu singleton sau trong hướng dẫn này.

Máy chủ

Các hệ thống sau đây liên quan đến máy chủ.

Hệ thốngMô tả
Mạng
  • Tạo tất cả các thể hiện RemoteEventRemoteFunction.
  • Cung cấp các phương thức để gửi và lắng nghe tin nhắn từ máy khách.
  • Xác thực kiểu cho các tham số nhận được từ máy khách tại thời gian chạy.
PlayerDataServer
  • Lưu và tải dữ liệu người chơi bền vững bằng cách sử dụng DataStoreService.
  • Lưu trữ dữ liệu người chơi trong bộ nhớ và sao chép các biến đổi đến máy khách.
  • Cung cấp các tín hiệu và phương thức để đăng ký, truy vấn và cập nhật dữ liệu người chơi.
Thị trường
  • Xử lý các giao dịch tiền tệ mềm từ máy khách.
  • Cung cấp một phương thức để bán cây trồng đã thu hoạch.
CollisionGroupManager
  • Gán các mô hình nhân vật người chơi vào các nhóm va chạm.
  • Cấu hình các nhóm va chạm để các nhân vật người chơi không va chạm với xe kéo cây trồng.
FarmManagerServer
  • Tạo lại mô hình trang trại của người chơi từ dữ liệu người chơi của họ khi họ tham gia trò chơi.
  • Xóa mô hình trang trại khi một người chơi rời đi.
  • Cập nhật dữ liệu người chơi khi trang trại của một người chơi bị thay đổi.
  • Cung cấp một phương thức để truy cập lớp Farm liên quan đến một người chơi nhất định.
PlayerObjectsContainer
  • Tạo các đối tượng khác nhau liên quan đến vòng đời của một người chơi và cung cấp một phương thức để truy xuất chúng.
TagPlayers
  • Thêm các thẻ CollectionService vào tất cả các đối tượng người chơi và nhân vật.
FtueManagerServer
  • Trong FTUE, thực hiện từng giai đoạn và chờ cho đến khi hoàn thành.
CharacterSpawner
  • Tái sinh các nhân vật khi họ chết. Lưu ý rằng Players.CharacterAutoLoads đã bị vô hiệu hóa để việc sinh ra bị tạm dừng cho đến khi dữ liệu của người chơi đã được tải.

Máy khách

Các hệ thống sau đây liên quan đến máy khách.

Hệ thốngMô tả
Mạng
  • Chờ máy chủ tạo tất cả các thể hiện RemoteEventRemoteFunction.
  • Cung cấp các phương thức để gửi và lắng nghe tin nhắn đến và từ máy chủ.
  • Thực thi xác thực kiểu tham số tại thời gian chạy.
  • Chạy pcall() trên các hàm từ xa.
PlayerDataClient
  • Lưu trữ dữ liệu của người chơi cục bộ trong bộ nhớ.
  • Cung cấp các phương thức và tín hiệu để truy vấn và đăng ký các thay đổi trong dữ liệu người chơi.
MarketClient
  • Cung cấp một phương thức để yêu cầu máy chủ mua một mặt hàng bằng tiền tệ mềm.
LocalWalkJumpManager
  • Cung cấp các phương thức để sửa đổi WalkSpeed hoặc JumpHeight của một nhân vật thông qua các hệ số để tránh xung đột khi sửa đổi các giá trị này từ nhiều nơi.
FarmManagerClient
  • Lắng nghe các thẻ CollectionService cụ thể được áp dụng cho các thể hiện và tạo "thành phần" thêm hành vi vào các thể hiện này. Một "thành phần" đề cập đến một lớp được tạo ra khi một thẻ CollectionService được thêm vào một thể hiện và bị xóa khi nó bị loại bỏ; những điều này được sử dụng cho các lời nhắc CTA trong trang trại và các lớp khác nhau truyền đạt trạng thái trang trại đến người chơi.
UISetup
  • Khởi tạo tất cả các lớp giao diện người dùng.
  • Cấu hình một số lớp chỉ hiển thị trong các phần vật lý của thế giới.
  • Kết nối một hiệu ứng camera đặc biệt cho khi các menu được kích hoạt.
FtueManagerClient
  • Cấu hình các giai đoạn FTUE trên máy khách.
CharacterSprint
  • Sử dụng LocalWalkJumpManager để tăng WalkSpeed khi một nhân vật người chơi ở bên ngoài trang trại của họ.

Giao tiếp giữa máy khách và máy chủ

Hầu hết các trò chơi Roblox liên quan đến một số yếu tố giao tiếp giữa máy khách và máy chủ. Điều này có thể bao gồm việc máy khách yêu cầu máy chủ thực hiện một hành động nhất định và máy chủ sao chép các cập nhật đến máy khách.

Trong dự án này, giao tiếp giữa máy khách và máy chủ được giữ ở mức tổng quát nhất có thể bằng cách giới hạn việc sử dụng các đối tượng RemoteEventRemoteFunction để giảm bớt số lượng quy tắc đặc biệt cần theo dõi. Dự án này sử dụng các phương thức sau, theo thứ tự ưu tiên:

Sao chép thông qua hệ thống dữ liệu người chơi

hệ thống dữ liệu người chơi cho phép dữ liệu được liên kết với người chơi mà duy trì giữa các phiên lưu. Hệ thống này cung cấp sao chép từ máy khách đến máy chủ và một tập hợp các API có thể được sử dụng để truy vấn dữ liệu và đăng ký các thay đổi, làm cho nó lý tưởng cho việc sao chép các thay đổi trạng thái người chơi từ máy chủ đến máy khách.

Ví dụ, thay vì kích hoạt một UpdateCoins RemoteEvent tùy chỉnh để nói cho máy khách biết họ có bao nhiêu đồng xu, bạn có thể gọi cái sau và để máy khách đăng ký nó thông qua sự kiện PlayerDataClient.updated.

PlayerDataServer.setValue(player, "coins", 5)

Tất nhiên, điều này chỉ hữu ích cho việc sao chép từ máy chủ đến máy khách và cho các giá trị mà bạn muốn duy trì giữa các phiên, nhưng điều này áp dụng cho một số lượng đáng ngạc nhiên các trường hợp trong dự án, bao gồm:

  • Giai đoạn FTUE hiện tại
  • Kho đồ của người chơi
  • Số lượng đồng xu mà người chơi có
  • Trạng thái của trang trại của người chơi

Sao chép thông qua thuộc tính

Trong các tình huống mà máy chủ cần sao chép một giá trị tùy chỉnh đến máy khách cụ thể cho một Instance nhất định, bạn có thể sử dụng thuộc tính. Roblox tự động sao chép các giá trị thuộc tính, vì vậy bạn không cần phải duy trì bất kỳ đường dẫn mã nào để sao chép trạng thái liên quan đến một đối tượng. Một lợi thế khác là việc sao chép này xảy ra cùng với chính thể hiện.

Điều này đặc biệt hữu ích cho các thể hiện được tạo tại thời gian chạy, vì các thuộc tính được đặt trên một thể hiện mới trước khi nó được làm cha của mô hình dữ liệu sẽ sao chép nguyên tử cùng với chính thể hiện. Điều này tránh bất kỳ nhu cầu viết mã để "chờ" dữ liệu bổ sung được sao chép thông qua một RemoteEvent hoặc StringValue.

Bạn cũng có thể đọc trực tiếp các thuộc tính từ mô hình dữ liệu, từ cả máy khách hoặc máy chủ, với phương thức GetAttribute() và đăng ký các thay đổi với phương thức GetAttributeChangedSignal(). Trong dự án Cây trồng, cách tiếp cận này được sử dụng cho, giữa những thứ khác, sao chép trạng thái hiện tại của cây trồng đến máy khách.

Sao chép thông qua thẻ

CollectionService cho phép bạn áp dụng một thẻ chuỗi cho một Instance. Điều này hữu ích cho việc phân loại các thể hiện và sao chép phân loại đó đến máy khách.

Ví dụ, thẻ CanPlant được áp dụng trên máy chủ để chỉ ra cho máy khách rằng một chậu nhất định có thể nhận một cây.

Gửi tin nhắn trực tiếp qua mô-đun mạng

Đối với các tình huống mà không có tùy chọn nào trước đó áp dụng, bạn có thể sử dụng các cuộc gọi mạng tùy chỉnh thông qua mô-đun Mạng. Đây là tùy chọn duy nhất trong dự án cho phép giao tiếp từ máy khách đến máy chủ và do đó hữu ích nhất cho việc truyền tải yêu cầu từ máy khách và nhận phản hồi từ máy chủ.

Cây trồng sử dụng các cuộc gọi mạng trực tiếp cho nhiều yêu cầu từ máy khách, bao gồm:

  • Tưới nước cho một cây
  • Trồng một hạt giống
  • Mua một mặt hàng

Nhược điểm của cách tiếp cận này là mỗi tin nhắn riêng lẻ yêu cầu một số cấu hình tùy chỉnh có thể làm tăng độ phức tạp của dự án, mặc dù điều này đã được tránh bất cứ khi nào có thể, đặc biệt là cho giao tiếp từ máy chủ đến máy khách.

Các lớp và singleton

Các lớp trong dự án Cây trồng, giống như các thể hiện trên Roblox, có thể được tạo ra và bị phá hủy. Cú pháp lớp của nó được lấy cảm hứng từ cách tiếp cận Lua thông thường cho lập trình hướng đối tượng với một số thay đổi để hỗ trợ kiểm tra kiểu nghiêm ngặt.

Khởi tạo

Nhiều lớp trong dự án liên quan đến một hoặc nhiều Instances. Các đối tượng của một lớp nhất định được tạo ra bằng cách sử dụng phương thức new(), nhất quán với cách các thể hiện được tạo ra trong Roblox bằng cách sử dụng Instance.new().

Mẫu này thường được sử dụng cho các đối tượng mà lớp có một đại diện vật lý trong mô hình dữ liệu, và lớp mở rộng chức năng của nó. Một ví dụ tốt là BeamBetween tạo ra một đối tượng Beam giữa hai đối tượng Attachment nhất định và giữ cho các gắn kết đó hướng lên trên. Những thể hiện này có thể được sao chép từ một phiên bản chế tạo sẵn trong ReplicatedStorage hoặc được truyền vào new() như một đối số và được lưu trữ bên trong đối tượng dưới self.

Các thể hiện tương ứng

Như đã đề cập ở trên, nhiều lớp trong dự án này có một đại diện mô hình dữ liệu, một thể hiện tương ứng với lớp và được thao tác bởi nó.

Thay vì tạo các thể hiện này khi một đối tượng lớp được khởi tạo, mã thường chọn Clone() một phiên bản chế tạo sẵn của Instance được lưu trữ dưới ReplicatedStorage hoặc ServerStorage. Mặc dù có thể tuần tự hóa các thuộc tính của các thể hiện này và tạo chúng từ đầu trong các hàm new() của lớp, việc làm như vậy sẽ khiến việc chỉnh sửa các đối tượng trở nên rất khó khăn và làm cho chúng khó hiểu hơn cho người đọc. Thêm vào đó, việc sao chép một thể hiện thường là một thao tác nhanh hơn so với việc tạo một thể hiện mới và tùy chỉnh các thuộc tính của nó tại thời gian chạy.

Thành phần

Mặc dù kế thừa là có thể trong Luau bằng cách sử dụng metatables, dự án chọn cho phép các lớp mở rộng lẫn nhau thông qua thành phần. Khi kết hợp các lớp thông qua thành phần, đối tượng "con" được khởi tạo trong phương thức new() của lớp và được bao gồm như một thành viên dưới self.

Để có một ví dụ về điều này trong hành động, hãy xem lớp CloseButton bao bọc lớp Button.

Dọn dẹp

Tương tự như cách một Instance có thể bị phá hủy bằng phương thức Destroy(), các lớp có thể được khởi tạo cũng có thể bị phá hủy. Phương thức hủy cho các lớp dự án là destroy() với chữ cái thường d để duy trì tính nhất quán camelCase trong các phương thức của mã nguồn, cũng như để phân biệt giữa các lớp của dự án và các thể hiện Roblox.

Vai trò của phương thức destroy() là để phá hủy bất kỳ thể hiện nào được tạo ra bởi đối tượng, ngắt kết nối bất kỳ kết nối nào, và gọi destroy() trên bất kỳ đối tượng con nào. Điều này đặc biệt quan trọng đối với các kết nối vì các thể hiện có kết nối hoạt động không được dọn dẹp bởi bộ thu gom rác Luau, ngay cả khi không còn tham chiếu nào đến thể hiện hoặc kết nối với thể hiện.

Singleton

Singleton, như tên gọi của nó, là các lớp mà chỉ một đối tượng có thể tồn tại. Chúng là tương đương của dự án với Dịch vụ của Roblox. Thay vì lưu trữ một tham chiếu đến đối tượng singleton và truyền nó xung quanh trong mã Luau, Cây trồng tận dụng thực tế rằng việc yêu cầu một ModuleScript sẽ lưu trữ giá trị trả về của nó. Điều này có nghĩa là việc yêu cầu cùng một ModuleScript singleton từ các nơi khác nhau sẽ nhất quán cung cấp cùng một đối tượng trả về. Ngoại lệ duy nhất cho quy tắc này sẽ là nếu các môi trường khác nhau (máy khách hoặc máy chủ) truy cập ModuleScript.

Singleton được phân biệt với các lớp có thể khởi tạo bởi thực tế là chúng không có phương thức new(). Thay vào đó, đối tượng cùng với các phương thức và trạng thái của nó được trả về trực tiếp thông qua ModuleScript. Vì các singleton không được khởi tạo, cú pháp self không được sử dụng và các phương thức được gọi bằng dấu chấm (.) thay vì dấu hai chấm (:).

Kiểm tra kiểu nghiêm ngặt

Luau hỗ trợ kiểu dần dần có nghĩa là bạn có thể thêm các định nghĩa kiểu tùy chọn cho một số hoặc tất cả mã của bạn. Trong dự án này, kiểm tra kiểu strict được sử dụng cho mọi script. Đây là tùy chọn ít cho phép nhất cho công cụ Phân tích Script của Roblox và do đó có khả năng cao nhất để phát hiện lỗi kiểu trước khi chạy.

Cú pháp lớp có kiểu

Cách tiếp cận đã được thiết lập để tạo các lớp trong Lua được tài liệu tốt, tuy nhiên nó không phù hợp với kiểu mạnh của Luau. Trong Luau, cách tiếp cận đơn giản nhất để lấy kiểu của một lớp là phương thức typeof():

type ClassType = typeof(Class.new())

Điều này hoạt động nhưng không rất hữu ích khi lớp của bạn được khởi tạo với các giá trị chỉ tồn tại tại thời gian chạy, ví dụ như các đối tượng Player. Thêm vào đó, giả định được thực hiện trong cú pháp lớp Lua thông thường là việc khai báo một phương thức trên lớp self sẽ luôn là một thể hiện của lớp đó; đây không phải là giả định mà công cụ suy diễn kiểu có thể thực hiện.

Để hỗ trợ suy diễn kiểu nghiêm ngặt, dự án Cây trồng sử dụng một giải pháp khác với cú pháp lớp Lua thông thường theo nhiều cách, một số có thể cảm thấy không trực quan:

  • Định nghĩa của self được sao chép, cả trong khai báo kiểu và trong hàm khởi tạo. Điều này tạo ra một gánh nặng bảo trì, nhưng các cảnh báo sẽ được đánh dấu nếu hai định nghĩa không đồng bộ với nhau.
  • Các phương thức lớp được khai báo bằng dấu chấm, vì vậy self có thể được khai báo rõ ràng là kiểu ClassType. Các phương thức vẫn có thể được gọi bằng dấu hai chấm như mong đợi.
--!strict
local MyClass = {}
MyClass.__index = MyClass
export type ClassType = typeof(setmetatable(
{} :: {
property: number,
},
MyClass
))
function MyClass.new(property: number): ClassType
local self = {
property = property,
}
setmetatable(self, MyClass)
return self
end
function MyClass.addOne(self: ClassType)
self.property += 1
end
return MyClass

Ép kiểu sau các điều kiện logic

Tại thời điểm viết, kiểu của một giá trị không được thu hẹp sau một câu lệnh điều kiện bảo vệ. Ví dụ, sau điều kiện bảo vệ dưới đây, kiểu của optionalParameter không được thu hẹp thành number.

--!strict
local function foo(optionalParameter: number?)
if not optionalParameter then
return
end
print(optionalParameter + 1)
end

Để giảm thiểu điều này, các biến mới được tạo ra sau các điều kiện bảo vệ với kiểu của chúng được ép kiểu rõ ràng.

--!strict
local function foo(optionalParameter: number?)
if not optionalParameter then
return
end
local parameter = optionalParameter :: number
print(parameter + 1)
end

Duyệt qua các phân cấp DataModel

Trong một số trường hợp, mã nguồn cần duyệt qua phân cấp mô hình dữ liệu của một cây các đối tượng được tạo ra tại thời gian chạy. Điều này đặt ra một thách thức thú vị cho việc kiểm tra kiểu. Tại thời điểm viết, không thể định nghĩa một phân cấp mô hình dữ liệu tổng quát như một kiểu. Do đó, có những trường hợp mà thông tin kiểu duy nhất có sẵn cho một cấu trúc mô hình dữ liệu là kiểu của thể hiện cấp cao nhất.

Một cách tiếp cận cho thách thức này là ép kiểu thành any và sau đó tinh chỉnh. Ví dụ:

local function enableVendor(vendor: Model)
local zonePart: BasePart = (vendor :: any).ZonePart
end

Vấn đề với cách tiếp cận này là nó ảnh hưởng đến khả năng đọc. Thay vào đó, dự án sử dụng một mô-đun tổng quát gọi là getInstance để duyệt qua các phân cấp mô hình dữ liệu mà ép kiểu thành any bên trong.

local function enableVendor(vendor: Model)
local zonePart: BasePart = getInstance(vendor, "ZonePart")
end

Khi sự hiểu biết của công cụ kiểu về mô hình dữ liệu phát triển, có thể rằng các mẫu như thế này sẽ không còn cần thiết nữa.

Giao diện người dùng

Cây trồng bao gồm một loạt các giao diện người dùng 2D phức tạp và đơn giản. Những điều này bao gồm các mục hiển thị không tương tác như bộ đếm đồng xu và các menu tương tác phức tạp như cửa hàng.

Cách tiếp cận UI

Bạn có thể so sánh lỏng lẻo giao diện người dùng Roblox UI với DOM HTML, vì nó là một phân cấp các đối tượng mô tả những gì người dùng nên thấy. Các cách tiếp cận để tạo và cập nhật một giao diện người dùng Roblox được chia thành các thực hành mệnh lệnhkhai báo.

Cách tiếp cậnƯu điểm và nhược điểm
Mệnh lệnh

Trong cách tiếp cận mệnh lệnh, giao diện người dùng được coi như bất kỳ phân cấp thể hiện nào khác trên Roblox. Cấu trúc giao diện người dùng được tạo ra trước thời gian chạy trong Studio và được thêm vào mô hình dữ liệu, thường là trực tiếp trong StarterGui. Sau đó, tại thời gian chạy, mã thao tác các phần cụ thể của giao diện người dùng để phản ánh trạng thái mà người tạo yêu cầu.

Cách tiếp cận này đi kèm với một số lợi thế. Bạn có thể tạo giao diện người dùng từ đầu trong Studio và lưu trữ nó trong mô hình dữ liệu. Đây là một trải nghiệm chỉnh sửa đơn giản và trực quan có thể tăng tốc độ tạo giao diện người dùng. Bởi vì mã giao diện người dùng mệnh lệnh chỉ quan tâm đến những gì cần thay đổi, nó cũng làm cho các thay đổi giao diện người dùng đơn giản dễ thực hiện.

Một nhược điểm đáng chú ý là, vì các cách tiếp cận giao diện người dùng mệnh lệnh yêu cầu trạng thái được thực hiện thủ công dưới dạng các biến đổi, các đại diện phức tạp của trạng thái có thể trở nên rất khó tìm và gỡ lỗi. Thường thì các lỗi xuất hiện khi phát triển mã giao diện người dùng mệnh lệnh, đặc biệt khi trạng thái và giao diện người dùng trở nên không đồng bộ do nhiều cập nhật tương tác theo một thứ tự không mong đợi.

Một thách thức khác với các cách tiếp cận mệnh lệnh là khó khăn hơn để phân chia giao diện người dùng thành các thành phần có ý nghĩa có thể được khai báo một lần và tái sử dụng. Bởi vì toàn bộ cây giao diện người dùng được khai báo tại thời gian chỉnh sửa, các mẫu chung có thể được lặp lại ở nhiều phần khác nhau của mô hình dữ liệu.

Khai báo

Trong cách tiếp cận khai báo, trạng thái mong muốn của các thể hiện giao diện người dùng được khai báo rõ ràng, và việc thực hiện hiệu quả của trạng thái này được trừu tượng hóa bởi các thư viện như Roact hoặc Fusion.

Lợi thế của cách tiếp cận này là việc thực hiện trạng thái trở nên đơn giản và bạn chỉ cần mô tả những gì bạn muốn giao diện người dùng của mình trông như thế nào. Điều này làm cho việc xác định và giải quyết các lỗi dễ dàng hơn đáng kể.

Nhược điểm chính là phải khai báo toàn bộ cây giao diện người dùng trong mã. Các thư viện như Roact và Fusion có cú pháp để làm điều này dễ dàng hơn, nhưng nó vẫn là một quá trình tốn thời gian và trải nghiệm chỉnh sửa ít trực quan hơn khi kết hợp giao diện người dùng.

Cây trồng sử dụng một cách tiếp cận mệnh lệnh với quan niệm rằng việc hiển thị các biến đổi trực tiếp cung cấp một cái nhìn tổng quan hiệu quả hơn về cách giao diện người dùng được tạo ra và thao tác trên Roblox. Điều này sẽ không thể thực hiện được với một cách tiếp cận khai báo. Một số cấu trúc và logic giao diện người dùng lặp lại cũng được trừu tượng hóa thành các thành phần có thể tái sử dụng để tránh một cạm bẫy phổ biến trong thiết kế giao diện người dùng mệnh lệnh.

Kiến trúc cấp cao

Sơ đồ kiến trúc UI dự án Cây trồng

Lớp và thành phần

Trong Cây trồng, tất cả các cấu trúc giao diện người dùng đều là một Layer hoặc một Component.

  • Layer được định nghĩa là một singleton nhóm cấp cao bao bọc các cấu trúc giao diện người dùng chế tạo sẵn trong ReplicatedStorage. Một lớp có thể chứa một số thành phần, hoặc nó có thể bao gồm logic của riêng nó hoàn toàn. Ví dụ về các lớp là menu kho hoặc chỉ số số đồng xu trong hiển thị đầu.
  • Component là một phần tử giao diện người dùng có thể tái sử dụng. Khi một đối tượng thành phần mới được khởi tạo, nó sao chép một mẫu chế tạo sẵn từ ReplicatedStorage. Các thành phần có thể chứa các thành phần khác. Ví dụ về các thành phần là một lớp nút tổng quát hoặc khái niệm về một danh sách các mục.

Quản lý chế độ xem

Một vấn đề quản lý giao diện người dùng phổ biến là quản lý chế độ xem. Dự án này có một loạt các menu và các mục HUD, một số trong đó lắng nghe đầu vào của người dùng, và việc quản lý cẩn thận khi nào chúng hiển thị hoặc được kích hoạt là cần thiết.

Cây trồng tiếp cận vấn đề này với hệ thống UIHandler của nó, quản lý khi nào một lớp giao diện người dùng nên hoặc không nên hiển thị. Tất cả các lớp giao diện người dùng trong trò chơi được phân loại là HUD hoặc Menu và tính khả dụng của chúng được quản lý bởi các quy tắc sau:

  • Trạng thái được kích hoạt của các lớp MenuHUD có thể được chuyển đổi.
  • Các lớp HUD được kích hoạt chỉ được hiển thị nếu không có lớp Menu nào được kích hoạt.
  • Các lớp Menu được kích hoạt được lưu trữ trong một ngăn xếp, và chỉ một lớp Menu được hiển thị tại một thời điểm. Khi một lớp Menu được kích hoạt, nó được chèn vào đầu ngăn xếp và hiển thị. Khi một lớp Menu bị vô hiệu hóa, nó được loại bỏ khỏi ngăn xếp và lớp Menu tiếp theo được kích hoạt trong hàng đợi được hiển thị.

Cách tiếp cận này rất trực quan vì nó cho phép điều hướng menu với lịch sử. Nếu một menu được mở từ một menu khác, việc đóng menu mới sẽ hiển thị lại menu cũ.

Các singleton lớp giao diện người dùng đăng ký với UIHandler và được cung cấp một tín hiệu được kích hoạt khi tính khả dụng của nó nên thay đổi.

Đọc thêm

Từ cái nhìn tổng quan chi tiết về dự án Cây trồng, bạn có thể muốn khám phá các hướng dẫn sau đây đi sâu hơn vào các khái niệm và chủ đề liên quan.

©2026 Roblox Corporation. Roblox, logo Roblox và Powering Imagination là các nhãn hiệu đã đăng ký và chưa đăng ký của chúng tôi tại Hoa Kỳ và các quốc gia khác.