Tôi có thể làm gì để tránh cờ một lần và kiểm tra trong toàn bộ mã của mình?


17

Hãy xem xét một trò chơi bài, chẳng hạn như Hearthstone .

Có hàng trăm thẻ làm rất nhiều thứ, một số trong đó là duy nhất ngay cả với một thẻ! Chẳng hạn, có một thẻ (được gọi là Nozdormu) làm giảm lượt người chơi xuống chỉ còn 15 giây!

Khi bạn có nhiều hiệu ứng tiềm năng như vậy, làm thế nào để bạn tránh các số ma thuật và kiểm tra một lần trên toàn bộ mã của bạn? Làm cách nào để tránh phương thức "Check_Nozdormu_In_Play" trong lớp PlayerTurnTime? Và làm thế nào người ta có thể tổ chức mã sao cho khi bạn thêm nhiều hiệu ứng hơn nữa , bạn không cần phải cấu trúc lại các hệ thống cốt lõi để hỗ trợ những thứ mà trước đây họ chưa bao giờ phải hỗ trợ?


Đây thực sự là một vấn đề hiệu suất? Ý tôi là, bạn có thể làm rất nhiều thứ điên rồ với CPU hiện đại gần như không mất thời gian ..
Jari Komppa

11
Ai nói bất cứ điều gì về vấn đề hiệu suất? Vấn đề chính tôi sẽ thấy là liên tục cần điều chỉnh tất cả mã của bạn mỗi khi bạn tạo một thẻ mới.
jhocking

2
Vì vậy, thêm một ngôn ngữ kịch bản và kịch bản mỗi thẻ.
Jari Komppa

1
Không có thời gian để đưa ra câu trả lời thích hợp, nhưng thay vì kiểm tra ví dụ Nozdormu và điều chỉnh 15 giây bên trong mã lớp "PlayerTurnTime" để xử lý các lượt người chơi, bạn có thể mã hóa lớp "PlayerTurnTime" để gọi [lớp-, nếu bạn muốn ] chức năng được cung cấp từ bên ngoài tại các điểm cụ thể. Sau đó, mã thẻ Nozdormu (và tất cả các thẻ khác cần ảnh hưởng đến cùng một mức độ) có thể thực hiện một chức năng cho điều chỉnh đó và đưa chức năng đó vào lớp PlayerTurnTime khi cần. Có thể hữu ích khi đọc về mẫu chiến lược và nội dung phụ thuộc từ cuốn sách Mẫu thiết kế cổ điển
Peteris

2
Tại một số điểm tôi phải tự hỏi liệu việc thêm kiểm tra đặc biệt vào các bit mã có liên quan có phải giải pháp đơn giản nhất không.
user253751

Câu trả lời:


12

Bạn đã xem xét các hệ thống thành phần thực thể và chiến lược nhắn tin sự kiện?

Các hiệu ứng trạng thái phải là các thành phần thuộc loại nào đó có thể áp dụng các hiệu ứng liên tục của chúng trong phương thức OnCreate (), hết hiệu lực của chúng trong OnRemond () và đăng ký các thông báo sự kiện trò chơi để áp dụng các hiệu ứng xảy ra như một phản ứng với điều gì đó xảy ra.

Nếu hiệu ứng liên tục có điều kiện (kéo dài trong lượt X, nhưng chỉ áp dụng trong một số trường hợp nhất định), bạn có thể cần kiểm tra các điều kiện đó ở các giai đoạn khác nhau.

Sau đó, bạn chỉ cần đảm bảo rằng trò chơi của bạn không có số ma thuật mặc định. Hãy chắc chắn rằng tất cả mọi thứ có thể thay đổi là một biến điều khiển dữ liệu thay vì mặc định được mã hóa cứng với các biến được sử dụng cho bất kỳ trường hợp ngoại lệ nào.

Theo cách này, bạn không bao giờ giả định chiều dài lần lượt sẽ là bao nhiêu. Đây luôn là một biến được kiểm tra liên tục có thể được thay đổi bởi bất kỳ hiệu ứng nào và có thể hoàn tác sau đó bởi hiệu ứng khi hết hạn. Bạn không bao giờ kiểm tra ngoại lệ trước khi mặc định số ma thuật của bạn.


2
"Hãy chắc chắn rằng tất cả mọi thứ có thể thay đổi là một biến điều khiển dữ liệu thay vì mặc định được mã hóa cứng với các biến được sử dụng cho bất kỳ trường hợp ngoại lệ nào." - Ồ, tôi khá là thích điều đó. Điều đó giúp ích rất nhiều, tôi nghĩ vậy!
Sable Dreamer

Bạn có thể nói rõ hơn về "áp dụng hiệu ứng bền bỉ của chúng" không? Sẽ không đăng ký TurnStarted và sau đó thay đổi giá trị Độ dài làm cho mã không thể truy cập được và thậm chí tệ hơn tạo ra kết quả không nhất quán (khi tương tác giữa các hiệu ứng tương tự)?
wonderra

Chỉ dành cho các thuê bao sẽ giả định bất kỳ khoảng thời gian nhất định. Bạn phải làm mẫu cẩn thận. Có thể tốt khi có thời gian bật hiện tại khác với thời gian bật của người chơi. PTT sẽ được kiểm tra để tạo ra một ngã rẽ mới. CTT có thể được kiểm tra bằng thẻ. Nếu một hiệu ứng sẽ tăng thời gian hiện tại, giao diện người dùng hẹn giờ sẽ tự nhiên tuân theo nếu nó không trạng thái.
RobStone

Để trả lời tốt hơn câu hỏi. Không có gì khác lưu trữ thời gian lần lượt hoặc bất cứ điều gì dựa trên điều đó. Luôn luôn kiểm tra nó.
RobStone

11

RobStone đang đi đúng hướng, nhưng tôi muốn giải thích vì đây chính xác là những gì tôi đã làm khi tôi viết Dungeon Ho!, Một Roguelike có hệ thống hiệu ứng rất phức tạp cho vũ khí và phép thuật.

Mỗi thẻ nên có một tập hợp các hiệu ứng kèm theo, được xác định theo cách nó có thể chỉ ra hiệu ứng đó là gì, mục tiêu của nó là gì, như thế nào và trong bao lâu. Ví dụ, hiệu ứng "gây sát thương cho đối thủ" có thể trông giống như thế này;

Effect type: deal damage (enumeration, string, what-have-you)
Effect amount: 20
Source: my weapon
Target: opponent
Effect Cost: 20
Cost Type: Mana

Sau đó, khi hiệu ứng kích hoạt, có một thói quen chung xử lý việc xử lý hiệu ứng. Giống như một thằng ngốc, tôi đã sử dụng một tuyên bố trường hợp / chuyển đổi lớn:

switch (effect_type)
{
     case DAMAGE:

     break;
}

Nhưng một cách tốt hơn và nhiều mô-đun hơn để làm điều đó là thông qua đa hình. Tạo một lớp Hiệu ứng bao bọc tất cả các dữ liệu này, tạo một lớp con cho từng loại hiệu ứng và sau đó lớp đó ghi đè lên một phương thức onExecute () dành riêng cho lớp.

class Effect
{
    Object source;
    int amount;

    public void onExecute(Object target)
    {
          // Do nothing
    }
}

class DamageEffect extends Effect
{
    public void onExecute(Object target)
    {
          target.health -= amount;
    }
}

Vì vậy, chúng ta sẽ có một lớp Hiệu ứng cơ bản, sau đó là lớp DamageEffect với phương thức onExecute (), vì vậy, trong mã xử lý của chúng ta, chúng ta sẽ đi;

Effect effect = card.getActiveEffect();

effect.onExecute();

Cách để đối phó với việc biết những gì đang chơi là tạo một Vector / Array / danh sách được liên kết / v.v. của các hiệu ứng hoạt động (thuộc loại Hiệu ứng, lớp cơ sở) được gắn vào bất kỳ đối tượng nào (bao gồm cả sân chơi / "trò chơi"), vì vậy thay vì phải kiểm tra xem có hiệu ứng cụ thể nào không, bạn chỉ cần lặp qua tất cả các hiệu ứng được đính kèm các đối tượng và để cho họ thực thi. Nếu một hiệu ứng không được gắn vào một đối tượng, thì nó không hoạt động.

Effect effect;

for (int o = 0; o < objects.length; o++)
{
    for (int e = 0; e < objects[o].effects.length; e++)
    {
         effect = objects[o].effects[e];

         effect.onExecute();
    }
}

Đây chính xác là cách tôi đã thực hiện nó. Cái hay ở đây là về cơ bản bạn có một hệ thống điều khiển dữ liệu và bạn có thể điều chỉnh logic khá dễ dàng trên cơ sở mỗi hiệu ứng. Thông thường, bạn sẽ phải thực hiện một số điều kiện kiểm tra trong logic thực thi của hiệu ứng, nhưng nó vẫn mạch lạc hơn rất nhiều vì các kiểm tra này chỉ dành cho hiệu ứng được đề cập.
manabreak

1

Tôi sẽ đưa ra một số gợi ý. Một số trong số họ mâu thuẫn với nhau. Nhưng có lẽ một số hữu ích.

Xem xét danh sách so với cờ

Bạn có thể lặp đi lặp lại trên toàn thế giới và kiểm tra một lá cờ trên mỗi mục để quyết định có nên làm điều đó không. Hoặc bạn có thể giữ một danh sách chỉ những vật phẩm nên làm cờ.

Xem xét danh sách và liệt kê

Bạn có thể tiếp tục thêm các trường boolean vào lớp vật phẩm của mình, isAThis và isAThat. Hoặc bạn có thể có một danh sách các chuỗi hoặc các phần tử enum, như {Hồi isAThis,, is ishathathat} hoặc {IS_A_THIS, IS_A_THAT}. Bằng cách đó, bạn có thể thêm những cái mới trong bảng liệt kê (hoặc hằng chuỗi) mà không cần thêm các trường. Không có gì thực sự sai khi thêm trường ...

Xem xét các con trỏ hàm

Thay vì một danh sách các cờ hoặc enum, có thể có một danh sách các hành động để thực thi cho mục đó trong các bối cảnh khác nhau. (Thực thể-ish)

Xem xét các đối tượng

Một số người thích cách tiếp cận dựa trên dữ liệu, hoặc theo kịch bản hoặc thực thể thành phần. Nhưng hệ thống phân cấp đối tượng lỗi thời cũng đáng xem xét. Lớp cơ sở cần phải chấp nhận các hành động, như trò chơi bài này cho B B-pha hoặc bất cứ điều gì. Sau đó, mỗi loại thẻ có thể ghi đè và trả lời khi thích hợp. Có lẽ cũng có một đối tượng người chơi và đối tượng trò chơi, vì vậy trò chơi có thể làm những việc như, nếu (người chơi-> is ALLowedToPlay ()) {thực hiện chơi trò}.

Xem xét khả năng gỡ lỗi

Một điều thú vị về một đống trường cờ là bạn có thể kiểm tra và in ra mọi trạng thái của mọi mục theo cùng một cách. Nếu trạng thái được biểu thị bằng các loại khác nhau, hoặc các túi của các thành phần hoặc con trỏ hàm hoặc nằm trong các danh sách khác nhau, có thể không đủ để chỉ nhìn vào các trường của mặt hàng. Đó là tất cả sự đánh đổi.

Cuối cùng, tái cấu trúc: Xem xét các bài kiểm tra đơn vị

Cho dù bạn có khái quát bao nhiêu kiến ​​trúc của mình, bạn sẽ có thể tưởng tượng những thứ mà nó không bao gồm. Sau đó, bạn sẽ phải cấu trúc lại. Có thể một chút, có thể rất nhiều.

Một cách để làm cho điều này an toàn hơn là với một bài kiểm tra đơn vị. Bằng cách đó bạn có thể tự tin rằng mặc dù bạn đã sắp xếp lại những thứ bên dưới (có thể rất nhiều!) Chức năng hiện có vẫn hoạt động. Mỗi bài kiểm tra đơn vị trông, như thế này:

void test1()
{
   Game game;
   game.addThis();
   game.setupThat(); // use primary or backdoor API to get game to known state

   game.playCard(something something).

   int x = game.getSomeInternalState;
   assertEquals(“did it do what we wanted?”, x, 23); // fail if x isn’t 23
}

Như bạn có thể thấy, giữ cho các lệnh gọi API cấp cao nhất đó trong trò chơi (hoặc trình phát, thẻ, & c) ổn định là chìa khóa cho chiến lược thử nghiệm đơn vị.


0

Thay vì nghĩ riêng từng thẻ, hãy bắt đầu suy nghĩ về các loại hiệu ứng và thẻ chứa một hoặc nhiều loại này. Ví dụ: để tính lượng thời gian trong một lượt, bạn có thể lặp qua tất cả các thẻ đang chơi và kiểm tra danh mục "thao tác thời gian bật" của mỗi thẻ có chứa danh mục đó. Mỗi thẻ sau đó tăng hoặc ghi đè thời lượng lần lượt dựa trên các quy tắc bạn đã quyết định.

Đây thực chất là một hệ thống thành phần nhỏ, trong đó mỗi đối tượng "thẻ" chỉ đơn giản là một thùng chứa cho một loạt các thành phần hiệu ứng.


Vì các thẻ - và các thẻ trong tương lai cũng vậy - có thể làm bất cứ điều gì, tôi mong muốn mỗi thẻ sẽ mang một tập lệnh. Tuy nhiên, tôi khá chắc chắn rằng đây không phải là vấn đề hiệu suất thực sự ..
Jari Komppa

4
theo các bình luận chính: Không ai (ngoài bạn) đã nói bất cứ điều gì về các vấn đề hiệu suất. Đối với kịch bản đầy đủ như là một thay thế, hãy giải thích điều đó trong một câu trả lời.
jhocking
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.