Sử dụng một thể hiện hoặc một lớp cho tài nguyên trò chơi (gỗ, sắt, vàng)


31

Vì vậy, tôi đang làm một trò chơi nơi bạn có thể gửi tàu đến các địa điểm để bán hoặc mua các tài nguyên như gỗ, sắt, vàng, v.v.

Bây giờ tôi đã tự hỏi làm thế nào các tài nguyên nên được tạo ra trong trò chơi de. Tôi đã đưa ra 2 lựa chọn

  1. Tạo một lớp cho mỗi tài nguyên:

    public class ResourceBase {
        private int value;
        // other base properties
    }
    
    public class Gold : ResourceBase {
         public Gold {
             this.value = 40 // or whatever
         }
    }
    
  2. Tạo các thể hiện của một lớp Resource

    public class Resource {
        string name;
        int value;
    
        public Resource(string name, int value) {
            this.name = name;
            this.value = value;
         }
     }
    
    // later on...
    
    Resource gold = new Resource("Gold",40);
    

Với tùy chọn thứ hai, có thể điền tài nguyên trò chơi từ tệp resource.json, tôi giống như cấu trúc đó.

Ý tưởng mới / mẫu thiết kế / cấu trúc luôn được chào đón!

EDIT: Nó hơi giống với ứng dụng đồng hành của Assassins Creed Black Flag. Xem thanh tài nguyên trên hình ảnh dưới đây

EDIT 2: Tôi đã thực hiện một số nghiên cứu thêm về "tải các mục / tài nguyên từ tệp JSON" và tìm thấy blog này: Sức mạnh của JSON trong Phát triển trò chơi - Các mục . Nó hiển thị tốt nhất của tùy chọn 1 và tùy chọn 2. Bạn vẫn có thể thêm chức năng cho từng tài nguyên :)

nhập mô tả hình ảnh ở đây


1
Minecraft có các lớp riêng biệt cho từng tài nguyên (không phải là bạn nên)
MCM Abbey

@MCM Abbey ohh là nguồn mở minecraft? Tôi muốn có một cái nhìn vào cấu trúc đó. Và thx để đề cập
MDijkstra

1
Không phải vậy, nhưng nó luôn bị khử
nhiễu

12
Đừng sử dụng một chuỗi như thế. Thật quá dễ dàng để gõ nhầm "vàng" với "glod" hoặc tương tự, và đó là một nỗi đau rất lớn để gỡ lỗi. Sử dụng một enumthay thế.
Nolonar

Minecraft không phải là nguồn mở về mặt kỹ thuật , nhưng nó gần như đã bị dịch ngược và khử nhiễu. Bạn không thể tìm thấy một nguồn khử nhiễu ở bất cứ đâu trên Internet, nhưng bạn có thể tải xuống và chạy quy trình để thực hiện nó từ files.minecraftforge.net (tải xuống MDK là thứ bạn muốn)
JamEngulfer

Câu trả lời:


51

Đi theo cách tiếp cận thứ hai, đơn giản là do bạn có thể giới thiệu các loại tài nguyên hoặc các mục mới bất cứ lúc nào mà không phải viết lại hoặc cập nhật mã ( phát triển theo hướng dữ liệu ).


Chỉnh sửa:

Để giải thích thêm một chút về lý do tại sao điều này nói chung là tốt, ngay cả khi bạn chắc chắn 100% một số giá trị sẽ không bao giờ thay đổi.

Hãy lấy ví dụ về trò chơi console được đề cập trong các bình luận, bởi vì điều này cung cấp một lý do rất chính đáng về lý do tại sao bạn không nên mã hóa bất cứ thứ gì, trừ khi bạn thực sự có (hoặc muốn), ví dụ như khóa mã hóa, tên riêng của bạn, v.v.

Khi phát hành một trò chơi trên bảng điều khiển, những trò chơi này thường phải trải qua quá trình xem xét, trong đó QA của nhà sản xuất bảng điều khiển sẽ kiểm tra trò chơi, chơi qua nó, tìm kiếm các vấn đề, v.v ... Điều này là bắt buộc và tốn rất nhiều tiền. Tôi nghĩ rằng tôi đã từng đọc rằng một bản phát hành có thể có giá 30.000-50.000 đô la chỉ cho chứng nhận.

Bây giờ hãy tưởng tượng bạn đang đẩy trò chơi của mình ra mắt, trả 30.000 đô la và chờ đợi. Và đột nhiên bạn nhận thấy rằng một số giá trị trò chơi của bạn bị phá vỡ theo nghĩa đen. Giả sử bạn có thể mua các thanh sắt với giá 50 vàng và bán chúng với giá 55 vàng.

Bạn làm nghề gì? Nếu bạn đã mã hóa nội dung đó, bạn sẽ phải tạo phiên bản cập nhật / phát hành mới, phiên bản này sẽ phải được xem xét lại một lần nữa, vì vậy, trường hợp xấu nhất bạn sẽ phải trả lại một lần nữa cho chứng nhận lại! Tôi cá là Ubisoft sẽ không phiền, nhưng đối với nhà phát triển game indie nhỏ của riêng bạn thì bỏ túi!

Nhưng hãy tưởng tượng trò chơi thỉnh thoảng sẽ kiểm tra các định nghĩa trò chơi được cập nhật (ví dụ ở dạng tệp JSON). Trò chơi của bạn có thể tải xuống và sử dụng phiên bản mới nhất bất cứ lúc nào mà không yêu cầu cập nhật mới. Bạn có thể khắc phục sự mất cân bằng / khai thác / lỗi bất cứ lúc nào mà không yêu cầu chứng nhận lại hoặc bất kỳ khoản tiền nào khác được trả. Chỉ cần một số quyết định thiết kế nhỏ, bạn không nghĩ là đáng giá, chỉ giúp bạn tiết kiệm được một khoản tiền gồm 5 chữ số! Điều đó thật tuyệt phải không? :)

Đừng hiểu lầm. Điều này cũng áp dụng cho các loại phần mềm và nền tảng khác. Việc tải xuống các tệp dữ liệu cập nhật nói chung dễ dàng hơn và dễ thực hiện hơn đối với người dùng chuẩn, bất kể đó là trò chơi hay một loại ứng dụng / công cụ nào đó. Hãy tưởng tượng một trò chơi được cài đặt trong Tệp chương trình trong Windows. Trình cập nhật của bạn cần quyền quản trị viên để sửa đổi bất cứ điều gì và bạn không thể sửa đổi các chương trình đang chạy. Với DDD, chương trình của bạn chỉ cần tải dữ liệu và sử dụng nó. Người chơi thậm chí có thể không nhận thấy đã có một bản cập nhật.


6
+1 cho DDD. Điều này rất phù hợp cho những thứ như tài nguyên.
Kromster nói hỗ trợ Monica

@Funnydutchman nếu câu trả lời của ai đó đã giúp bạn, thì theo thông lệ trên Stack Exchange sẽ nâng cao câu trả lời của họ bằng cách nhấp vào mũi tên phía trên số bên trái. Nếu câu trả lời giúp bạn nhiều nhất, bạn nên nhấp vào dấu chọn bên dưới mũi tên dưới cùng để chấp nhận câu trả lời của họ. Cả hai hành động đều mang lại cho người trả lời một phần thưởng danh tiếng và làm cho câu trả lời có thể sử dụng được hiển thị rõ hơn cho cộng đồng.
Nzall

2
Tôi không thấy cách bạn phải viết lại mã theo cách đầu tiên; tất cả những gì bạn làm là thêm một lớp (trừ khi tôi thiếu thứ gì đó).
SirPython

4
Và đây là sự khác biệt giữa mã hướng đối tượng và mã đối tượng bị ám ảnh. Chỉ vì bạn có thể tạo một hệ thống phân cấp đối tượng không nhất thiết là bạn nên làm.
corsiKa

2
@ AgustínLado Nếu tôi có một đô la cho mỗi lần khách hàng nói "Điều này sẽ không bao giờ thay đổi" và nó đã thay đổi, tôi có thể đưa cả hai chúng tôi đến một nhà hàng sang trọng cho bữa tối (mặc dù chúng tôi sẽ phải tiết kiệm một chút tiền boa, vì vậy có lẽ chỉ có một nhà hàng tầm thường ... vẫn đủ thời gian cho bữa tối cho hai người.)
corsiKa

29

Một nguyên tắc nhỏ là bạn sử dụng các lớp khác nhau khi các đối tượng yêu cầu mã và thể hiện khác nhau của cùng một lớp khi các đối tượng chỉ yêu cầu các giá trị khác nhau.

Khi các tài nguyên có các cơ chế trò chơi khác nhau, là duy nhất cho chúng, việc thể hiện chúng với các lớp là điều hợp lý. Ví dụ, khi bạn có Plutonium trong đó có một thời gian chu kỳ bán rã và Thỏ mà sinh sản theo dãy Fibonacci , sau đó nó có thể làm cho tinh thần để có một cơ sở đẳng cấp Resourcevới một phương pháp Updatethực hiện như một không-op và hai lớp có nguồn gốc HalfLifeResourceSelfGrowingResourceđó ghi đè phương thức đó để giảm / tăng giá trị (và cũng có thể bao gồm logic để chi phối những gì xảy ra khi bạn lưu trữ cả hai cạnh nhau).

Nhưng khi tài nguyên của bạn thực sự chỉ là những con số ngớ ngẩn, thì sẽ không có ý nghĩa gì khi thực hiện chúng với bất cứ thứ gì lạ mắt hơn một biến đơn giản.


Cảm ơn @Phillipp cho câu trả lời của bạn. Đây là những gì tôi đã suy nghĩ. Nhưng các tài nguyên sẽ không thực sự thay đổi về các thuộc tính / phương thức, vì vậy tôi sẽ đi với tùy chọn 2 ngay bây giờ
MDijkstra

14

Tôi muốn thêm có hai tùy chọn bổ sung:
Giao diện: bạn có thể xem xét nếu lớp tài nguyên sẽ chỉ là "lưu trữ" cho 5 số nguyên và mỗi thực thể khác sẽ có logic khác nhau về tài nguyên (ví dụ: chi tiêu / loot của người chơi, thành phố tạo ra nguồn). Trong trường hợp đó, bạn có thể không muốn một lớp nào cả - bạn chỉ muốn tiết lộ rằng một số thực thể có một biến mà người khác có thể tương tác với:

interface IHasResources {
  int Gold { get; set; }
  int Wood { get; set; }
  int Cloth { get; set; }
  // ....
}

class Player : IHasResources { }
class City: IHasResources { }

Ngoài ra, điều này có mặt trái là bạn có thể cướp phá một thành phố với mã chính xác giống như bạn sẽ cướp phá hạm đội của kẻ thù, thúc đẩy việc tái sử dụng mã.
Nhược điểm là, việc thực hiện giao diện như vậy có thể tỏ ra tẻ nhạt nếu nhiều lớp thực hiện nó, do đó bạn có thể sẽ sử dụng giao diện nhưng không phải là vấn đề ban đầu, giải quyết nó bằng tùy chọn 1 hoặc 2:

interface IHasResources { 
   IEnumerable<Resource> Resources { get; }
}

Trường hợp (với enum): trong một số trường hợp, nên sử dụng enum thay vì chuỗi khi xác định loại tài nguyên:

enum Resource {
   Gold, Wood, Cloth, //...
}

class ResourceStorage {
   public Resource ResourceType { get; set; }
   public int Value { get; set; }
}

điều này sẽ cung cấp một chút "an toàn", bởi vì IDE của bạn sẽ cung cấp cho bạn danh sách tất cả các tài nguyên hợp lệ khi gán nó sau khi viết dấu chấm ResourceType = Resource., ngoài ra còn có nhiều "thủ thuật" với các enum bạn có thể cần, như Loại rõ ràng hoặc thành phần / cờ mà tôi có thể tưởng tượng là hữu ích khi chế tạo:

[Flags]
enum Metal {
 Copper = 0x01/*01*/, Tin = 0x02/*10*/, Bronze = 0x03/*11*/
}
Metal alloy = Metal.Copper; //lets forget Value(s) for sake of simplicity
alloy |= Metal.Tin; //now add a bit of tin
Console.WriteLine(alloy); //will print "Bronze"


Đối với những gì tốt nhất - không có viên đạn bạc, nhưng cá nhân tôi sẽ nghiêng về bất kỳ hình thức nào, chúng có thể không lạ mắt như giao diện nhưng chúng đơn giản, không lộn xộn cây thừa kế và được hiểu rộng rãi.


2
Cảm ơn câu trả lời của bạn @wondra Tôi thích tùy chọn enum và cách bạn tạo ra đồng từ đồng và thiếc; p
MDijkstra

2
Tôi đã tham gia cộng đồng này một cách cụ thể để tôi có thể bình chọn giải pháp enum. Bạn cũng có thể xem xét việc sử dụng thuộc tính Mô tả enum để ánh xạ từ một số biểu diễn JSON của enum ( my-great-enum) sang một số biểu diễn C # của enum ( MyGreatEnum).
Dan Forbes

Rõ ràng tôi đã ra khỏi vòng lặp Java quá lâu. Kể từ khi nào nó được phép có khai báo trường cho các giao diện? Theo như tôi biết thì chúng tự động tĩnh và cuối cùng, và do đó không sử dụng nhiều cho các mục đích được mô tả trong câu hỏi.
M.Herzkamp

2
@ M.Herzkamp nó không phải là một trường, nó là một thuộc tính - và Java không hỗ trợ các thuộc tính. Câu hỏi được gắn thẻ C #, C # cho phép các thuộc tính trong giao diện - thuộc tính là các phương thức được đặt dưới tất cả. Tôi e ngại với chú thích cờ, không được hỗ trợ trong Java mặc dù tôi không hoàn toàn chắc chắn.
wonderra

Cám ơn giải thích rõ ràng! Tôi đã bỏ lỡ thẻ C # và mã trong câu hỏi trông rất giống Java: D
M.Herzkamp

2

Bạn nên sử dụng tùy chọn 1, có 3 ưu điểm:

  1. Việc khởi tạo tài nguyên trở nên dễ dàng hơn - thay vì phải truyền loại tài nguyên dưới dạng tham số, ví dụ, bạn sẽ làm new Gold(50)
  2. Nếu bạn cần đưa ra hành vi đặc biệt cho tài nguyên, bạn có thể bằng cách thay đổi lớp đó.
  3. Dễ dàng hơn để kiểm tra nếu tài nguyên là một loại nhất định - resource instanceof Gold

Tất cả các câu trả lời đều có ưu và nhược điểm, thật khó để chọn cái nào tốt hơn. Tôi đã thực hiện một chỉnh sửa cho câu hỏi, bạn nghĩ gì về cấu trúc đó?
MDijkstra

1

Nếu tài nguyên của bạn không có logic kinh doanh của riêng họ hoặc trường hợp đặc biệt với cách chúng tương tác với môi trường, thì các tài nguyên khác (ngoài giao dịch mà bạn đã bảo hiểm) hoặc trình phát sau đó, tùy chọn hai giúp dễ dàng thêm các tài khoản mới.

Nếu bạn cần xem xét các tình huống như điều gì sẽ xảy ra nếu bạn trộn hai tài nguyên để chế tạo, nếu tài nguyên có thể có các tính chất khác nhau rất lớn (một xô nước rất khác so với một cục than) hoặc các tương tác kinh tế phức tạp khác, thì cách tiếp cận 1 là linh hoạt hơn nhiều cho động cơ, nhưng không phải là dữ liệu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.