Làm cách nào để phát triển một trò chơi thành phần thực thể trong trò chơi theo lượt?


9

Cho đến nay, các hệ thống thành phần thực thể mà tôi đã sử dụng đã hoạt động chủ yếu như các tác phẩm nghệ thuật của Java:

  • Tất cả dữ liệu trong các thành phần
  • Các hệ thống độc lập không trạng thái (ít nhất là ở mức độ mà chúng không yêu cầu đầu vào khi khởi tạo) lặp lại qua từng thực thể chỉ chứa các thành phần mà hệ thống cụ thể quan tâm
  • Tất cả các hệ thống xử lý các thực thể của chúng một tích tắc, sau đó toàn bộ sự việc bắt đầu lại.

Bây giờ tôi đang cố gắng áp dụng điều này cho một trò chơi theo lượt lần đầu tiên, với vô số sự kiện và phản hồi phải xảy ra theo thứ tự được đặt tương ứng với nhau, trước khi trò chơi có thể tiếp tục. Một ví dụ:

Người chơi A nhận sát thương từ một thanh kiếm. Để đáp lại điều này, áo giáp của A đá vào và làm giảm thiệt hại. Tốc độ di chuyển của A cũng bị hạ thấp do hậu quả của việc yếu đi.

  • Các thiệt hại gây ra là những gì đặt ra toàn bộ tương tác
  • Giáp phải được tính toán và áp dụng cho sát thương nhận vào trước khi sát thương được áp dụng cho người chơi
  • Giảm tốc độ di chuyển không thể được áp dụng cho một đơn vị cho đến khi thiệt hại thực sự được xử lý, vì nó phụ thuộc vào lượng sát thương cuối cùng.

Các sự kiện cũng có thể kích hoạt các sự kiện khác. Giảm sát thương kiếm bằng áo giáp có thể khiến thanh kiếm vỡ ra (điều này phải diễn ra trước khi hoàn thành việc giảm sát thương), điều này có thể gây ra các sự kiện bổ sung để đáp ứng với nó, về cơ bản là đánh giá đệ quy các sự kiện.

Nói chung, điều này dường như dẫn đến một vài vấn đề:

  1. Rất nhiều chu trình xử lý bị lãng phí: Hầu hết các hệ thống (tiết kiệm cho những thứ luôn chạy, như kết xuất) đơn giản là không có gì đáng để làm khi nó không "đến lượt" hoạt động và dành phần lớn thời gian chờ đợi trò chơi vào một trạng thái làm việc hợp lệ. Điều này tạo ra mọi hệ thống như vậy với các kiểm tra tiếp tục tăng kích thước, càng nhiều trạng thái được thêm vào trò chơi.
  2. Để tìm hiểu xem một hệ thống có thể xử lý các thực thể có trong trò chơi hay không, họ cần một số cách để theo dõi các trạng thái thực thể / hệ thống không liên quan khác (hệ thống chịu trách nhiệm xử lý thiệt hại cần biết liệu áo giáp có được áp dụng hay không). Điều này có thể làm rối loạn các hệ thống với nhiều trách nhiệm hoặc tạo ra nhu cầu cho các hệ thống bổ sung không có mục đích nào khác ngoài việc quét bộ sưu tập thực thể sau mỗi chu kỳ xử lý và liên lạc với một nhóm người nghe bằng cách nói với chúng khi nào đó có thể làm gì đó.

Hai điểm trên cho rằng các hệ thống hoạt động trên cùng một tập hợp các thực thể, kết thúc việc thay đổi trạng thái bằng cách sử dụng các cờ trong các thành phần của chúng.

Một cách khác để giải quyết nó là thêm / xóa các thành phần (hoặc tạo các thực thể hoàn toàn mới) do một hệ thống duy nhất hoạt động để tiến triển trạng thái trò chơi. Điều này có nghĩa là bất cứ khi nào một hệ thống thực sự có một thực thể phù hợp, nó sẽ biết rằng nó được phép xử lý nó.

Tuy nhiên, điều này làm cho các hệ thống chịu trách nhiệm kích hoạt các hệ thống tiếp theo, gây khó khăn cho lý do về hành vi của chương trình vì các lỗi sẽ không hiển thị do kết quả của một tương tác hệ thống. Việc thêm các hệ thống mới cũng trở nên khó khăn hơn vì chúng không thể được thực hiện mà không biết chính xác chúng ảnh hưởng đến các hệ thống khác như thế nào (và các hệ thống trước đó có thể phải được sửa đổi để kích hoạt các trạng thái mà hệ thống mới quan tâm), đánh bại mục đích có các hệ thống riêng biệt với một nhiệm vụ duy nhất.

Đây có phải là thứ tôi sẽ phải sống cùng không? Mỗi ví dụ ECS duy nhất tôi từng thấy là thời gian thực và thật dễ dàng để thấy cách vòng lặp một trò chơi này hoạt động trong các trường hợp như vậy. Và tôi vẫn cần nó để kết xuất, nó dường như thực sự không phù hợp với các hệ thống tạm dừng hầu hết các khía cạnh của chính nó mỗi khi có điều gì đó xảy ra.

Có một số mẫu thiết kế để di chuyển trạng thái trò chơi về phía trước phù hợp cho việc này hay tôi chỉ nên di chuyển tất cả logic ra khỏi vòng lặp và thay vào đó chỉ kích hoạt nó khi cần thiết?


Bạn không thực sự muốn thăm dò ý kiến ​​cho một sự kiện sẽ xảy ra. Một sự kiện chỉ xảy ra khi nó xảy ra. Không phải Artemis cho phép các hệ thống giao tiếp với nhau sao?
Sidar

Nó có, nhưng chỉ bằng cách ghép chúng bằng các phương pháp.
Aeris130

Câu trả lời:


3

Lời khuyên của tôi ở đây đến từ kinh nghiệm trong quá khứ về một dự án RPG, nơi chúng tôi đã sử dụng một hệ thống thành phần. Tôi sẽ nói rằng tôi ghét làm việc trong mã gameside đó vì đó là mã spaghetti. Vì vậy, tôi không đưa ra nhiều câu trả lời ở đây, chỉ là một viễn cảnh:

Logic mà bạn mô tả để xử lý sát thương kiếm cho người chơi ... có vẻ như một hệ thống sẽ chịu trách nhiệm cho tất cả điều đó.

Ở đâu đó, có một hàm HandWeaponHit (). Nó sẽ truy cập ArmorComponent của thực thể người chơi để lấy bộ giáp liên quan. Nó sẽ truy cập WeaponComponent của thực thể vũ khí tấn công để phá vỡ vũ khí. Sau khi tính toán sát thương cuối cùng, nó sẽ chạm vào MovementComponent để người chơi đạt được tốc độ giảm.

Đối với các chu trình xử lý bị lãng phí ... Chỉ nên kích hoạt HandWeaponHit () khi cần thiết (khi phát hiện ra thanh kiếm đâm).

Có lẽ điểm tôi đang cố gắng thực hiện là: chắc chắn bạn muốn có một vị trí trong mã nơi bạn có thể đặt điểm dừng, đánh nó và sau đó tiến hành bước qua tất cả logic được cho là chạy khi xảy ra va chạm với thanh kiếm. Nói cách khác, logic không nên nằm rải rác trong các hàm tick () của nhiều hệ thống.


Làm theo cách này sẽ làm cho baloon hàm hit () khi có thêm hành vi được thêm vào. Giả sử có một kẻ thù rơi xuống cười mỗi khi một thanh kiếm đâm vào mục tiêu (bất kỳ mục tiêu nào) trong tầm nhìn của nó. Liệu HandWeaponHit có thực sự chịu trách nhiệm cho việc kích hoạt đó không?
Aeris130

1
Bạn có một chuỗi chiến đấu được bao bọc chặt chẽ nên có, đánh có trách nhiệm kích hoạt hiệu ứng. Không phải mọi thứ phải được chia thành các hệ thống nhỏ, hãy để hệ thống này xử lý việc này vì đây thực sự là "Hệ thống chiến đấu" của bạn và nó xử lý ... Chiến đấu ...
Patrick Hughes

3

Đó là một câu hỏi cũ nhưng bây giờ tôi đang phải đối mặt với những kẻ tầm thường với trò chơi do tôi tự làm trong khi học ECS, do đó, một số người cần thiết. Hy vọng rằng nó sẽ kết thúc trong một cuộc thảo luận hoặc ít nhất là một vài bình luận.

Tôi không chắc liệu nó có vi phạm các khái niệm ECS hay không, nhưng nếu:

  • Thêm một EventBus để cho phép Hệ thống phát hành / đăng ký vào các đối tượng sự kiện (thực tế là dữ liệu thuần túy, nhưng không phải là một thành phần tôi đoán)
  • Tạo thành phần cho từng trạng thái trung gian

Thí dụ:

  • UserInputSystem thực hiện một sự kiện Tấn công với [Thông tin về DamageDealerEntity, DamageReceiverEntity, Skill / Weapon được sử dụng]
  • CombatSystem được đăng ký vào nó và tính toán cơ hội trốn tránh cho DamageReceiver. Nếu trốn tránh thất bại thì nó kích hoạt sự kiện Thiệt hại với cùng tham số
  • DamageSystem được đăng ký vào sự kiện đó và do đó được kích hoạt
  • DamageSystem sử dụng Sức mạnh, sát thương BaseWeapon, loại của nó, v.v. và ghi nó vào một IncomDamageComponent mới với [DamageDealerEntity, FinalOut gửiDamage, DamageType] và gắn nó vào Thực thể / Thực thể nhận sát thương
  • DamageSystem kích hoạt Out gửiDamageCalculated
  • ArmorSystem được kích hoạt bởi nó, chọn một Thực thể người nhận hoặc tìm kiếm theo khía cạnh In chuẩnDamage này trong Thực thể để chọn In chuẩnDamageComponent (lần cuối có thể tốt hơn cho nhiều đòn tấn công có lây lan) và tính toán giáp và sát thương được áp dụng cho nó. Tùy chọn kích hoạt các sự kiện cho thanh kiếm vỡ
  • ArmorSystems loại bỏ IncomeDamageComponent trong mỗi thực thể và thay thế nó bằng DamageReceuredComponent bằng các số được tính toán cuối cùng sẽ ảnh hưởng đến HP và Giảm tốc độ từ vết thương
  • ArmorSystems gửi một sự kiện In chuẩnDamageCalculated
  • Hệ thống tốc độ được đăng ký và tính toán lại tốc độ
  • HealthSystem được đăng ký và giảm HP thực tế
  • Vân vân
  • Bằng cách nào đó dọn dẹp

Ưu điểm:

  • Hệ thống kích hoạt lẫn nhau cung cấp dữ liệu trung gian cho các sự kiện chuỗi phức tạp
  • Phân tách bởi EventBus

Nhược điểm:

  • Tôi cảm thấy rằng tôi pha trộn hai cách để vượt qua mọi thứ: trong các sự kiện của người tham gia sự kiện và trong các Thành phần tạm thời. nó có thể là một nơi yếu Về lý thuyết để giữ mọi thứ đồng nhất, tôi có thể kích hoạt chỉ các sự kiện không có dữ liệu để Hệ thống tìm thấy các tham số ngụ ý trong các thành phần của Thực thể theo khía cạnh ... Không chắc là nó có ổn không
  • Không chắc chắn làm thế nào để biết liệu tất cả các Hệ thống có khả năng quan tâm đã xử lý In chuẩnDamageCalculated để nó có thể được dọn sạch và để lần lượt tiếp theo xảy ra. Có thể một số loại kiểm tra lại trong CombatSystem ...

2

Đăng giải pháp cuối cùng tôi đã giải quyết, tương tự như của Yakovlev.

Về cơ bản, tôi đã kết thúc việc sử dụng một hệ thống sự kiện vì tôi thấy nó rất trực quan để theo logic của nó qua các lượt. Hệ thống cuối cùng chịu trách nhiệm cho các đơn vị trong trò chơi tuân thủ logic theo lượt (người chơi, quái vật và bất cứ thứ gì chúng có thể tương tác), các nhiệm vụ thời gian thực như kết xuất và bỏ phiếu đầu vào được đặt ở nơi khác.

Các hệ thống thực hiện một phương thức onEvent lấy một sự kiện và một thực thể làm đầu vào, báo hiệu rằng thực thể đó đã nhận được sự kiện đó. Mọi hệ thống cũng đăng ký các sự kiện và thực thể với một bộ thành phần cụ thể. Điểm tương tác duy nhất có sẵn cho các hệ thống là singleton trình quản lý thực thể, được sử dụng để gửi các sự kiện đến các thực thể và để truy xuất các thành phần từ một thực thể cụ thể.

Khi người quản lý thực thể nhận được một sự kiện kết hợp với thực thể mà nó được gửi đến, nó sẽ đặt sự kiện ở phía sau hàng đợi. Trong khi có các sự kiện trong hàng đợi, sự kiện quan trọng nhất được truy xuất và gửi đến mọi hệ thống vừa đăng ký sự kiện và quan tâm đến bộ thành phần của thực thể nhận sự kiện. Các hệ thống đó có thể lần lượt xử lý các thành phần của thực thể, cũng như gửi các sự kiện bổ sung cho người quản lý.

Ví dụ: Người chơi nhận sát thương, vì vậy thực thể người chơi được gửi một sự kiện thiệt hại. DamageSystem đăng ký các sự kiện thiệt hại được gửi đến bất kỳ thực thể nào có thành phần sức khỏe và có phương thức onEvent (thực thể, sự kiện) làm giảm sức khỏe trong thành phần thực thể theo số lượng được chỉ định trong sự kiện.

Điều này giúp dễ dàng chèn một hệ thống áo giáp đăng ký các sự kiện thiệt hại được gửi đến các thực thể có thành phần áo giáp. Phương pháp onEvent của nó giúp giảm sát thương trong trường hợp bằng lượng giáp trong thành phần. Điều này có nghĩa là chỉ định thứ tự các hệ thống nhận các sự kiện tác động đến logic trò chơi, vì hệ thống áo giáp phải xử lý sự kiện thiệt hại trước khi hệ thống thiệt hại hoạt động.

Tuy nhiên, đôi khi một hệ thống phải bước ra ngoài thực thể nhận. Để tiếp tục với phản hồi của tôi với Eric Undersander, việc thêm một hệ thống truy cập vào bản đồ trò chơi và tìm kiếm các thực thể với FallsDownLaughingComponent trong không gian x của thực thể nhận sát thương, và sau đó gửi FallDownLaughingEvent cho họ. Hệ thống này sẽ phải được lên lịch để nhận sự kiện sau hệ thống thiệt hại, nếu sự kiện thiệt hại chưa bị hủy bỏ tại thời điểm đó, thiệt hại đã được xử lý.

Một vấn đề nảy sinh là làm thế nào để đảm bảo phản hồi - các sự kiện được xử lý theo thứ tự chúng được gửi, với điều kiện một số phản hồi có thể sinh ra các phản hồi bổ sung. Thí dụ:

Người chơi di chuyển, khiến một sự kiện chuyển động được gửi đến thực thể người chơi và được hệ thống chuyển động nhặt lên.

Trong hàng đợi: Phong trào

Nếu chuyển động được cho phép, hệ thống sẽ điều chỉnh vị trí của người chơi. Nếu không (người chơi đã cố gắng di chuyển vào một chướng ngại vật), nó đánh dấu sự kiện là đã hủy, khiến người quản lý thực thể loại bỏ nó thay vì gửi nó đến các hệ thống tiếp theo. Ở cuối danh sách các hệ thống quan tâm đến sự kiện này là TurnFinishedSystem, xác nhận rằng người chơi đã dành lượt của mình để di chuyển nhân vật, và đến lượt anh ta / cô ta đã kết thúc. Điều này dẫn đến một sự kiện TurnOver được gửi đến thực thể người chơi và được xếp hàng.

Trong hàng đợi: TurnOver

Bây giờ nói rằng người chơi bước vào một cái bẫy, gây ra thiệt hại. Hệ thống bẫy nhận được thông báo chuyển động trước TurnFinishedSystem, vì vậy sự kiện thiệt hại được gửi trước tiên. Bây giờ, hàng đợi thay thế trông như thế này:

Trong hàng đợi: Damage, TurnOver

Tất cả đều ổn cho đến nay, sự kiện thiệt hại sẽ được xử lý, và sau đó đến lượt kết thúc. Tuy nhiên, điều gì sẽ xảy ra nếu các sự kiện bổ sung được gửi dưới dạng phản ứng với thiệt hại? Bây giờ hàng đợi sự kiện sẽ trông như sau:

Trong hàng đợi: Damage, TurnOver, FeedbackToDamage

Nói cách khác, lượt chơi sẽ kết thúc trước khi bất kỳ phản hồi nào về thiệt hại được xử lý.

Để giải quyết vấn đề này, tôi đã kết thúc bằng hai phương thức gửi sự kiện: gửi (sự kiện, thực thể) và trả lời (sự kiện, eventToRonymousTo, thực thể).

Mọi sự kiện đều ghi lại các sự kiện trước đó trong chuỗi phản hồi và bất cứ khi nào phương thức answer () được sử dụng, sự kiện được phản hồi (và mọi sự kiện trong chuỗi phản hồi của nó) sẽ kết thúc ở đầu chuỗi trong sự kiện được sử dụng để có trách nhiệm với. Sự kiện phong trào nội bộ không có sự kiện như vậy. Các phản ứng thiệt hại tiếp theo có sự kiện di chuyển trong danh sách của nó.

Trên hết, một mảng có độ dài thay đổi được sử dụng để chứa nhiều hàng đợi sự kiện. Bất cứ khi nào người quản lý nhận được một sự kiện, sự kiện sẽ được thêm vào hàng đợi tại một chỉ mục trong mảng phù hợp với số lượng sự kiện trong chuỗi phản hồi. Do đó, sự kiện di chuyển ban đầu được thêm vào hàng đợi tại [0] và thiệt hại, cũng như các sự kiện TurnOver được thêm vào một hàng đợi riêng biệt tại [1] vì cả hai đều được gửi dưới dạng phản hồi cho chuyển động.

Khi các phản hồi về sự kiện thiệt hại được gửi đi, những sự kiện đó sẽ chứa cả chính sự kiện thiệt hại, cũng như chuyển động, đưa chúng vào hàng đợi tại chỉ mục [2]. Miễn là chỉ mục [n] có các sự kiện trong hàng đợi, các sự kiện đó sẽ được xử lý trước khi chuyển sang [n-1]. Điều này đưa ra một thứ tự xử lý:

Di chuyển -> Thiệt hại [1] -> Phản hồiDamage [2] -> [2] trống -> TurnOver [1] -> [1] trống -> [0] trống

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.