Dự án tham khảo về cây xanh

*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 xanh là một trải nghiệm tham khảo nơi người chơi trồng và tưới hạt giống, để họ có thể sau này thu hoạch và bán những cây trồng thu được.

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

Dự án tập trung vào những 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ải nghiệm trên Roblox. Ở những nơi thích hợp, bạn sẽ tìm thấy ghi chú về các sự đánh đổi, thỏa hiệp và lý do của các lựa chọn thực hiện khác nhau, để bạn có thể đưa ra quyết định tốt nhất cho những trải nghiệm của riêng bạn.

Nhận tệp

  1. Điều hướng đến trang trải nghiệm Cây xanh.
  2. Nhấp vào nút Chỉnh sửa trong Studio.

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

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

  • Dữ liệu phiên và khả năng duy trì dữ liệu người chơi
  • Quản lý góc nhìn UI
  • Kết nối máy chủ - khách
  • Trải nghiệm người dùng lần đầu (FTUE)
  • Mua bán tiền tệ cứng và mềm

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

  • Tùy chỉnh một khu vực trong địa điểm gắn liền 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 sau các nhân vật
  • Phát hiện xem nhân vật đang ở phần nào của thế giới

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

Cấu trúc dự án

Quyết định đầu tiên khi tạo một trải nghiệm là xác định cách cấu trúc dự án, bao gồm chủ yếu là nơi để đặ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 mã cả 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 các thể hiện trong mô hình dữ liệu.

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, thay đổi hoặc phá hủy những thể hiện này 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 rỗng, trong đó 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 con 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ải nghiệm. Càng có nhiều thể hiện được đặt vào ReplicatedFirst, thời gian chờ đợi để sao chép chúng trước khi mã trong ReplicatedFirst có thể chạy càng lâu.

  • Trong thư mục Instances có GUI cho màn hình tải.
  • Trong thư mục Source có mã cho màn hình tải và mã cần thiết để chờ đợi phần còn lại của trải nghiệm 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ò là một thùng chứa cho tất cả các thể hiện mà cần truy cập cả phía 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 dự án sử dụng.
  • Trong thư mục Instances có một loạt các thể hiện được sản xuất sẵn.
  • Trong thư mục Source có tất cả mã không cần thiết cho quy trình tải mà cần phải có thể truy cập từ cả phía máy khách và máy chủ.
ServerScriptService

Chứa một Script đóng vai trò là điểm vào cho tất cả mã phía máy chủ trong dự án.

ServerStorage

Đóng vai trò là một thùng chứa cho tất cả các thể hiện không cần sao chép đến máy khách.

  • Trong thư mục Instances có một mô hình Farm mẫu. Một bản sao của mô hình này được đặt trong Workspace khi người chơi tham gia trải nghiệm, 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 riêng 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ải nghiệm. 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 ModuleScripts có thể tái sử dụng có thể được nhập vào toàn bộ mã nguồn. ModuleScripts có thể tái sử dụng nhưng chúng không thực thi tự động; chúng cần được nhập 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ải nghiệm, tạo ra nhiều điểm vào.

Đối với trò chơi nhỏ Cây xanh, 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ã máy khách, và một Script duy nhất là điểm vào cho tất cả mã 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 lớn hơn về thứ tự trong đó các hệ thống được thực thi.

Danh sách sau mô tả những sự đánh đổi của cả hai phương pháp:

  • Một Script duy nhất và một LocalScript duy nhất bao gồm mã máy chủ và máy khách tương ứng.
  • Kiểm soát tốt hơn về thứ tự trong đó 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 kịch bản duy nhất.
  • Có thể chuyển các đối tượng qua tham chiếu giữa các hệ thống.

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

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ác lớp khác.

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

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

Máy chủ

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

Hệ thốngMô tả
Mạng
  • Tạo tất cả các thể hiện RemoteEventRemoteFunction.
  • Tiết lộ các phương thức để gửi và lắng nghe tin nhắn từ máy khách.
  • Kiểm tra kiểu cho các tham số nhận từ máy khách tại thời gian chạy.
PlayerDataServer
  • Lưu trữ 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 thể đến máy khách.
  • Tiết lộ 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.
  • Tiết lộ một phương thức để bán cây thu hoạch.
CollisionGroupManager
  • Gán các mô hình nhân vật người chơi vào 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 tải cây.
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ải nghiệm.
  • 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.
  • Tiết lộ 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.
Thùng chứa đối tượng người chơi
  • Tạo nhiều đối tượng liên quan đến tuổi thọ của một người chơi và cung cấp phương thức để truy xuất chúng.
TagPlayers
  • Thêm 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 mỗi giai đoạn và chờ nó 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 tái sinh được tạm ngừ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 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.
  • Tiết lộ các phương thức để gửi và lắng nghe tin nhắn từ và đến máy chủ.
  • Thực hiện kiểm tra kiểu tham số tại runtime.
  • Chạy pcall() trên các hàm từ xa.
PlayerDataClient
  • Lưu trữ dữ liệu của người chơi địa phương trong bộ nhớ.
  • Tiết lộ 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
  • Tiết lộ 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
  • Tiết lộ các phương thức để thay đổi WalkSpeed hoặc JumpHeight của một nhân vật thông qua các hệ số nhân để tránh xung đột khi thay đổi các giá trị này từ nhiều nơi.
FarmManagerClient
  • Lắng nghe sự kiện cụ thể về việc các thẻ CollectionService đang được áp dụng cho các thể hiện và tạo "thành phần" bổ sung hành vi cho 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ị phá hủy khi nó bị xóa; động thái này được sử dụng cho các lời nhắc CTA trong trang trại và nhiều lớp khác truyền đạt trạng thái trang trại cho người chơi.
UISetup
  • Khởi tạo tất cả các lớp UI.
  • 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 tốc độ WalkSpeed khi một nhân vật người chơi ở 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ải nghiệm Roblox bao gồm 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 bản cập nhật tới máy khách.

Trong dự án này, giao tiếp giữa máy khách và máy chủ được giữ càng tổng quát càng tốt bằng cách giới hạn việc sử dụng các đối tượng RemoteEventRemoteFunction nhằm 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à vẫn tồn tại giữa các phiên lưu trữ. 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ì gửi một UpdateCoins RemoteEvent đặt riêng để nói cho máy khách biết họ có bao nhiêu đồng xu, bạn có thể gọi câu lệnh sau và để cho máy khách đăng ký thông qua sự kiện PlayerDataClient.updated .


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

Tất nhiên, điều này chỉ có í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 tồn tại giữa các phiên, nhưng điều này áp dụng cho một số trường hợp đáng ngạc nhiên trong dự án, bao gồm:

  • Giai đoạn FTUE hiện tại
  • Kho đồ của người chơi
  • Số lượng xu mà người chơi đang có
  • Trạng thái 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 các 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 duy trì bất kỳ con đường mã nào để sao chép trạng thái liên quan đến một đối tượng. Một lợi ích khác là quá trình sao chép này được thực hiện 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 ra ở 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 gán là cha của mô hình dữ liệu sẽ sao chép một cách nguyên tử cùng với chính thể hiện đó. Điều này giúp tránh cần viết mã để "chờ" dữ liệu bổ sung được sao chép qua một RemoteEvent hoặc StringValue.

Bạn cũng có thể đọc trực tiếp thuộc tính từ mô hình dữ liệu, từ cả máy khách hoặc máy chủ, bằng phương thức GetAttribute() và đăng ký các thay đổi bằng cách sử dụng GetAttributeChangedSignal(). Trong dự án Cây xanh, tiếp cận này được sử dụng cho, bên cạnh những thứ khác, để sao chép trạng thái hiện tại của cây đế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 dụng 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ủ để báo hiệu cho máy khách rằng một chậu nhất định có thể nhận được một cây.

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

Đối với những 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 đó cho phép việc truyền tải các yêu cầu từ máy khách và nhận phản hồi từ máy chủ.

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

  • Tưới 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 riêng có thể làm tăng độ phức tạp của dự án, mặc dù điều này đã được tránh càng nhiều càng tốt, đặc biệt là đối với 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 xanh, giống như các thể hiện trên Roblox, có thể được tạo và bị 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 đối với lập trình hướng đối tượng với một số thay đổi để hỗ trợ cho kiểm tra kiểu chặt chẽ.

Khởi tạo

Nhiều lớp trong dự án gắn liền với 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 trong Roblox sử dụng Instance.new().

Mô hình 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ụ điển hình là BeamBetween tạo ra một đối tượng Beam giữa hai đối tượng Attachment nhất định và giữ cho những gắn kết đó định hướng để tia luôn hướng lên. Những thể hiện này có thể được sao chép từ một phiên bản được sản xuất 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 lớp đó.

Thay vì tạo những 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 được sản xuất 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 những thể hiện này và tạo chúng từ đầu trong các hàm new() của lớp, điều này sẽ khiến cho việc chỉnh sửa các đối tượng trở nên khá khó khăn và khiến cho việc đọc hiểu trở nên khó khăn hơn. Ngoài ra, 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ù thừa kế là có thể trong Luau bằng cách sử dụng các bảng, 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.

Để xem ví dụ về điều này, hãy xem lớp CloseButton 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() được viết thường với chữ d cho tính nhất quán của kiểu 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 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 bất kỳ tham chiếu nào đến thể hiện hoặc các kết nối với thể hiện đó.

Singleton

Singleton, như tên gọi cho thấy, là các lớp mà chỉ một đối tượng có thể tồn tại. Chúng tương đương với Các dịch vụ của Roblox. Thay vì lưu trữ một tham chiếu đến đối tượng singleton và chuyển nó xung quanh trong mã Luau, Cây xanh 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 liên tục 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 cùng một ModuleScript.

Singleton được phân biệt với các lớp có thể hình thành mới bằng thực tế rằng 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ì 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 chặt chẽ

Luau hỗ trợ kiểu dần dần có nghĩa là bạn có thể thêm đị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 kịch bản. Đây là tùy chọn ít cho phép nhất đối với công cụ Phân tích Kịch bản của Roblox và do đó có khả năng 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 xác định để tạo các lớp trong Lua đã được ghi chép đầy đủ, 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())

Cách này hiệu quả nhưng không 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. Ngoài ra, giả định được đưa ra trong cú pháp lớp Lua là khi định nghĩa một phương thức trên self là nó sẽ luôn là một thể hiện của lớp đó; đây không phải là giả định mà bộ suy diễn kiểu có thể đưa ra.

Để hỗ trợ cho việc suy diễn kiểu chặt chẽ, dự án Cây xanh sử dụng 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ách này 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 trình khởi tạo. Điều này tạo ra gánh nặng bảo trì, nhưng sẽ có cảnh báo nếu hai định nghĩa không đồng bộ với nhau.
  • 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 với 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 bộ bảo vệ 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 bộ 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 bộ bảo vệ này với kiểu rõ ràng được ép.


--!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 cấu trúc DataModel

Trong một số trường hợp, mã nguồn cần phải duyệt qua cấu trúc 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 xuất hiện 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 cấu trúc cây mô hình dữ liệu 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 có tên getInstance để duyệt qua các cấu trúc cây mô hình dữ liệu mà ép 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 bộ kiểm tra 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 xanh bao gồm nhiều giao diện người dùng 2D phức tạp và đơn giản. Những giao diện này bao gồm các mục hiển thị không tương tác như bộ đếm 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 một cách lỏng lẻo giao diện UI trên Roblox với DOM HTML, bởi vì nó là một cấu trúc 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 giao diện Roblox được chia thành hai thực tiễn chính là mệnh lệnhkhai báo.

Cách tiếp cậnLợi thế 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ỳ cấu trúc thể hiện nào khác trên Roblox. Cấu trúc giao diện được tạo ra trước thời gian chạy trong Studio và thêm vào mô hình dữ liệu, thường được thêm trực tiếp vào StarterGui. Sau đó, tại thời gian chạy, mã thao tác các phần cụ thể của giao diện để phản ánh trạng thái mà người tạo yêu cầu.

Cách tiếp cận này có một số lợi ích. Bạn có thể tạo giao diện 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 việc tạo giao diện. Bởi vì mã giao diện 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 đơn giản trở nên dễ dàng để thực hiện.

Một nhược điểm đáng chú ý là, khi các cách tiếp cận giao diện mệnh lệnh yêu cầu trạng thái phả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ó khăn để tìm và gỡ lỗi. Thường thì sẽ xuất hiện các lỗi khi phát triển mã giao diện mệnh lệnh, đặc biệt khi trạng thái và giao diện không còn đồng bộ với nhau do nhiều cập nhật tương tác theo thứ tự không như mong đợi.

Một thách thức khác đối với các cách tiếp cận mệnh lệnh là việc khó khăn hơn trong việc phân chia giao diện thành các thành phần có ý nghĩa mà 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 được khai báo tại thời điểm chỉnh sửa, các mẫu phổ biến có thể bị 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 đượ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 ích 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 của bạn trông như thế nào. Điều này làm cho việc xác định và giải quyết lỗi dễ dàng hơn rất nhiều.

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

Cây xanh sử dụng một cách tiếp cận mệnh lệnh dưới quan điể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 thể hiệu quả hơn về cách giao diện được tạo ra và thao tác trên Roblox. Điều này sẽ không thể có được với cách tiếp cận khai báo. Một số cấu trúc giao diện người dùng và logic 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 nhằm tránh một bẫy phổ biến trong thiết kế giao diện UI mệnh lệnh.

Kiến trúc cấp cao

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

Lớp và thành phần

Trong Cây xanh, tất cả các cấu trúc UI đều là một Layer hoặc một Component.

  • Layer được định nghĩa là một lớp nhóm cấp cao bao gồm các cấu trúc UI được sản xuất 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 chính nó. Ví dụ về lớp là menu kho đồ hoặc số lượng xu trong hiển thị lên đầu.
  • Component là một phần tử UI 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 được sản xuất 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ề danh sách các mặt hàng.

Quản lý góc nhìn

Một vấn đề quản lý giao diện phổ biến là quản lý góc nhìn. Dự án này có một loạt các menu và các mục HUD, một số trong số đó 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 bật là cần thiết.

Cây xanh 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 UI nên hoặc không nên hiển thị. Tất cả các lớp UI trong trải nghiệm được phân loại là HUD hoặc Menu và trạng thái hiển thị của chúng được quản lý theo các quy tắc sau:

  • Trạng thái được bật của các lớp MenuHUD có thể được chuyển đổi.
  • Các lớp HUD được bật chỉ được hiển thị nếu không có Menu nào đang được bật.
  • Các lớp Menu được bậ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 bật, nó sẽ được chèn vào đầu ngăn xếp và được hiển thị. Khi một lớp Menu bị tắt, nó sẽ được xóa khỏi ngăn xếp và lớp Menu được tiếp theo trong hàng đợi sẽ đượ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 các 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ẽ lại hiển thị menu cũ.

Các singleton lớp UI tự đăng ký với UIHandler và được cung cấp một tín hiệu sẽ phát khi trạng thái hiển thị của nó nên thay đổi.

Đọc thêm

Từ cái nhìn tổng quan đầy đủ về dự án Cây xanh, 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.

  • Mô hình Máy khách-Máy chủ — Một cái nhìn tổng quan về mô hình máy khách-máy chủ trong Roblox.
  • Luau — Thông tin chi tiết về Luau, ngôn ngữ lập trình được Roblox tạo ra xuất phát từ Lua 5.1.
  • Sự kiện từ xa và Callbacks — Tất cả về các sự kiện mạng từ xa và callbacks để giao tiếp qua ranh giới máy khách-máy chủ.
  • Giao diện Người dùng (UI) — Thông tin chi tiết về các đối tượng giao diện người dùng và thiết kế trên Roblox.
©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.