Kết nối mạng cho các trò chơi Chiến lược thời gian thực


16

Tôi đang phát triển một trò chơi chiến lược thời gian thực cho một khóa học về khoa học máy tính. Một trong những khía cạnh khó khăn hơn của nó dường như là kết nối và đồng bộ hóa máy khách-máy chủ. Tôi đã đọc về chủ đề này (bao gồm 1500 cung thủ ), nhưng tôi đã quyết định thực hiện một cách tiếp cận máy khách-máy chủ trái ngược với các mô hình khác (ví dụ qua mạng LAN).

Trò chơi chiến lược thời gian thực này đi kèm với một số vấn đề. Rất may, mọi hành động người chơi thực hiện đều mang tính quyết định. Tuy nhiên, có những sự kiện xảy ra vào khoảng thời gian theo lịch trình. Chẳng hạn, trò chơi được tạo thành từ các ô xếp và khi người chơi lấy một ô, 'mức năng lượng', một giá trị trên ô đó, sẽ tăng thêm một giây sau khi chơi. Đây là một lời giải thích rất nhanh mà nên biện minh cho trường hợp sử dụng của tôi.

Ngay bây giờ tôi đang làm các máy khách mỏng, chỉ gửi các gói đến máy chủ và chờ phản hồi. Tuy nhiên, có một số vấn đề.

Khi các trò chơi giữa những người chơi phát triển thành trò chơi kết thúc, thường có hơn 50 sự kiện mỗi giây (do các sự kiện đã lên lịch, được giải thích trước đó, chồng chất) và lỗi đồng bộ hóa bắt đầu hiển thị sau đó. Vấn đề lớn nhất của tôi là ngay cả một sai lệch nhỏ về trạng thái giữa các khách hàng cũng có thể có nghĩa là các quyết định khác nhau của khách hàng, đưa bóng tuyết vào các trò chơi hoàn toàn riêng biệt. Một vấn đề khác (không quan trọng như bây giờ) là có độ trễ và người ta phải chờ vài mili giây, thậm chí vài giây sau khi họ di chuyển để xem kết quả.

Tôi đang tự hỏi những chiến lược và thuật toán nào tôi có thể sử dụng để làm cho việc này dễ dàng hơn, nhanh hơn và thú vị hơn cho người dùng cuối. Điều này đặc biệt thú vị với số lượng sự kiện cao mỗi giây, cùng với nhiều người chơi mỗi trò chơi.

TL; DR tạo RTS với> 50 sự kiện mỗi giây, làm cách nào để đồng bộ hóa máy khách?


Bạn có thể thực hiện những gì Eve-online làm và "làm chậm" thời gian để cho phép mọi thứ xử lý đúng cách.
Ryan Erb

3
Đây là một liên kết bắt buộc với mô hình máy khách / máy chủ của hành tinh hủy diệt hành tinh: forrestthewoods.ghost.io/iêu Đây là một thay thế cho mô hình bước chân dường như đang hoạt động rất tốt đối với họ.
DallonF

Xem xét giảm số lượng sự kiện bằng cách gửi một bản cập nhật cho tất cả các ô đã lấy thay vì các bản cập nhật cho mỗi ô hoặc, như Ilmari đã trả lời, bằng cách phân cấp các hành động không phải của người chơi.
Lilienthal

Câu trả lời:


13

Mục tiêu của bạn là đồng bộ hóa 50 sự kiện mỗi giây trong âm thanh thời gian thực với tôi như nó không thực tế. Đây là lý do tại sao cách tiếp cận khóa bước được nói đến trong bài viết 1500 cung thủ , tốt, được nói về!

Trong một câu: Cách duy nhất để đồng bộ hóa quá nhiều mục trong thời gian quá ngắn qua mạng quá chậm là KHÔNG đồng bộ hóa quá nhiều mục trong thời gian quá ngắn qua mạng quá chậm, nhưng thay vào đó, tiến trình xác định trạng thái trên tất cả các máy khách và chỉ đồng bộ hóa nhu cầu trần (đầu vào của người dùng).


6

mọi hành động người chơi thực hiện đều mang tính quyết định, tuy nhiên, có những sự kiện xảy ra vào các khoảng thời gian theo lịch trình

Tôi nghĩ có vấn đề của bạn; trò chơi của bạn chỉ nên có một dòng thời gian (đối với những thứ ảnh hưởng đến trò chơi). Bạn nói rằng một số thứ nhất định tăng trưởng với tốc độ X mỗi giây ; tìm hiểu có bao nhiêu trò chơi bước trong một giây và chuyển đổi đó để một tỷ lệ X mỗi bước trò chơi Y . Sau đó, mặc dù trò chơi có thể chậm lại, mọi thứ vẫn mang tính quyết định.

Có trò chơi chạy độc lập với thời gian thực có những lợi thế khác:

  • bạn có thể điểm chuẩn bằng cách chạy nó càng nhanh càng tốt
  • bạn có thể gỡ lỗi bằng cách làm chậm trò chơi để xem các sự kiện thoáng qua và như đã đề cập
  • Trò chơi vẫn mang tính quyết định, điều rất quan trọng đối với nhiều người chơi.

Bạn cũng đã đề cập rằng bạn gặp phải sự cố khi có> 50 sự kiện hoặc có độ trễ lên tới vài giây. Tỷ lệ này nhỏ hơn nhiều so với kịch bản được mô tả trong 1500 cung thủ , vì vậy hãy xem liệu bạn có thể lập hồ sơ cho trò chơi của mình không và tìm ra nơi chậm lại.


1
+1: Dựa trên khung là lựa chọn đúng đắn, không dựa trên thời gian. Bạn có thể cố gắng giữ N khung hình mỗi giây, tất nhiên. Một cú hích nhẹ là tốt hơn so với một desync đầy đủ.
PatrickB

@PatrickB: Tôi thấy rằng nhiều trò chơi sử dụng thời gian "mô phỏng" không gắn với khung hình video. World of Warcraft chỉ cập nhật những thứ như mana cứ sau 100ms và Pháo đài Lùn mặc định là 10 tích tắc trên mỗi khung hình video.
Vịt mướp

@Mooing Duck: Nhận xét của tôi là dành riêng cho RTS. Đối với một cái gì đó mà các lỗi nhỏ có thể được chấp nhận và sửa chữa sau này (ví dụ MMORPG, FPS), thì việc sử dụng các giá trị liên tục không chỉ tốt mà còn quan trọng. Tuy nhiên, mô phỏng xác định phải được đồng bộ hóa trên nhiều máy? Dán vào khung.
PatrickB

4

Đầu tiên, để giải quyết vấn đề với các sự kiện được lên lịch, đừng phát sóng các sự kiện khi chúng xảy ra , nhưng khi chúng được lên lịch ban đầu. Nghĩa là, thay vì gửi tin nhắn "tăng năng lượng của ô ( x , y )" mỗi giây, chỉ cần gửi một tin nhắn có nội dung "tăng năng lượng của ô ( x , y ) một lần mỗi giây cho đến khi đầy bị gián đoạn ". Mỗi khách hàng sau đó chịu trách nhiệm lên lịch cập nhật cục bộ.

Trên thực tế, bạn có thể thực hiện nguyên tắc này hơn nữa và chỉ truyền các hành động của người chơi : mọi thứ khác có thể được tính toán cục bộ bởi mỗi khách hàng (và máy chủ, nếu cần).

(Tất nhiên, đôi khi bạn cũng có thể truyền tổng kiểm trạng thái trò chơi, để phát hiện bất kỳ sự đồng bộ hóa ngẫu nhiên nào và có một số cơ chế để đồng bộ hóa lại máy khách nếu điều đó xảy ra, ví dụ: bằng cách gửi lại tất cả dữ liệu trò chơi từ bản sao có thẩm quyền của máy chủ cho khách hàng Nhưng điều này hy vọng sẽ là một sự kiện hiếm gặp, chỉ gặp phải trong thử nghiệm hoặc trong các sự cố hiếm gặp.)


Thứ hai, để giữ cho máy khách được đồng bộ hóa, hãy đảm bảo rằng trò chơi của bạn mang tính quyết định. Các câu trả lời khác đã cung cấp lời khuyên tốt cho việc này, nhưng hãy để tôi bao gồm một bản tóm tắt ngắn gọn về những việc cần làm:

  • Làm cho trò chơi của bạn theo lượt nội bộ, với mỗi lượt chơi hoặc "đánh dấu", giả sử, 1/50 giây. (Trên thực tế, bạn có thể thoát được 1/10 giây hoặc lâu hơn.) Bất kỳ hành động nào của người chơi xảy ra trong một lượt chơi nên được coi là đồng thời. Tất cả các tin nhắn, ít nhất là từ máy chủ đến máy khách, phải được gắn thẻ số lần lượt để mỗi khách hàng biết được sự kiện nào xảy ra trên mỗi sự kiện.

    Vì trò chơi của bạn đang sử dụng kiến ​​trúc máy khách-máy chủ, bạn có thể để máy chủ đóng vai trò là trọng tài cuối cùng của những gì xảy ra trong mỗi lượt, điều này giúp đơn giản hóa một số điều. Tuy nhiên, xin lưu ý rằng điều đó có nghĩa là khách hàng cũng phải xác nhận lại hành động của chính họ từ máy chủ: nếu khách hàng gửi tin nhắn có nội dung "Tôi di chuyển đơn vị X một ô sang trái" và câu trả lời của máy chủ không nói gì về đơn vị X di chuyển, khách hàng phải cho rằng điều đó đã không xảy ra và có thể hủy bỏ bất kỳ hoạt hình chuyển động dự đoán nào mà họ có thể đã bắt đầu chơi.

  • Xác định một trật tự nhất quán cho các sự kiện "đồng thời" xảy ra trên cùng một lượt, để mỗi máy khách sẽ thực hiện chúng theo cùng một thứ tự. Thứ tự này có thể là bất cứ điều gì, miễn là nó mang tính quyết định và giống nhau cho tất cả các máy khách (và máy chủ).

    Ví dụ: trước tiên bạn có thể tăng tất cả các tài nguyên (có thể được thực hiện cùng một lúc, nếu sự tăng trưởng tài nguyên trong một ô này không thể can thiệp vào tài nguyên đó), sau đó di chuyển từng đơn vị của người chơi theo trình tự được xác định trước, sau đó di chuyển các đơn vị NPC. Để công bằng cho người chơi, bạn có thể muốn thay đổi thứ tự di chuyển đơn vị giữa các lượt, để mỗi người chơi được đi trước thường xuyên như nhau; điều này là tốt, miễn là nó được thực hiện một cách xác định (ví dụ dựa trên số lần lượt).

  • Nếu bạn đang sử dụng toán học dấu phẩy động, hãy đảm bảo rằng bạn đang sử dụng nó trong chế độ IEEE nghiêm ngặt. Điều này có thể làm mọi thứ chậm lại một chút, nhưng đó là một cái giá nhỏ để trả cho sự nhất quán giữa các khách hàng. Ngoài ra, hãy đảm bảo không làm tròn ngẫu nhiên xảy ra trong quá trình liên lạc (ví dụ: máy khách truyền giá trị làm tròn đến máy chủ, nhưng vẫn sử dụng giá trị không có nội bộ bên trong). Như đã lưu ý ở trên, có một giao thức để phát hiện và phục hồi từ quá trình đồng bộ hóa cũng là một ý tưởng tốt, chỉ trong trường hợp.


1
Ngoài ra, đồng bộ hóa RNG để bắt đầu và chỉ lấy từ RNG được đồng bộ hóa khi máy chủ báo cho bạn biết. Starcraft1 đã có một lỗi trong một thời gian dài khi hạt giống RNG không được lưu trong các lần phát lại, do đó, replay sẽ dần đi chệch khỏi các trò chơi thực tế.
Vịt mướp

1
@MooingDuck: Điểm tốt. Trên thực tế, tôi khuyên bạn nên truyền hạt giống RNG hiện tại ở mỗi lượt, để đồng bộ hóa RNG ngay lập tức được phát hiện. Ngoài ra, nếu mã UI của bạn cần bất kỳ sự ngẫu nhiên nào, đừng lấy nó từ cùng một ví dụ RNG như được sử dụng cho logic trò chơi.
Ilmari Karonen

3

Bạn nên làm cho logic trò chơi của mình hoàn toàn độc lập với thời gian thực và về cơ bản làm cho nó trở thành trò chơi theo lượt. Bằng cách đó, bạn biết chính xác bật "thay đổi năng lượng gạch xảy ra". Trong trường hợp của bạn, mỗi lượt chỉ là 1/50 giây.

Bằng cách đó, bạn chỉ phải lo lắng về đầu vào của người chơi, mọi thứ khác được quản lý theo logic trò chơi và hoàn toàn giống nhau trên tất cả các máy khách. Ngay cả khi trò chơi bị đình trệ một lúc, do độ trễ Net hoặc tính toán quá phức tạp, các sự kiện vẫn diễn ra đồng bộ cho tất cả mọi người.


1

Trước hết, bạn phải hiểu rằng PC float / double math IS KHÔNG mang tính quyết định, trừ khi bạn chỉ định sử dụng nghiêm ngặt IEEE-754 cho tính toán của mình (sẽ chậm)

Sau đó, đây là cách tôi sẽ triển khai nó: máy khách kết nối với máy chủ và sắp xếp thời gian (chăm sóc độ trễ ping!) (Để chơi trò chơi dài có thể cần phải đồng bộ lại dấu thời gian / lượt)

bây giờ, mỗi khi khách hàng thực hiện một hành động, nó bao gồm dấu thời gian / lượt và tùy thuộc vào máy chủ để từ chối dấu thời gian / lượt xấu. Sau đó, máy chủ gửi lại hành động cho khách hàng và mỗi khi một lượt "bị đóng" (hay còn gọi là máy chủ sẽ không chấp nhận lần lượt / dấu thời gian quá cũ), máy chủ sẽ gửi và hành động kết thúc cho khách hàng.

Khách hàng sẽ có 2 "thế giới": một là đồng bộ hóa với lượt cuối, cái còn lại được tính bắt đầu từ lượt cuối, thêm hành động đến vào hàng đợi, cho đến khi bật / dấu thời gian của máy khách hiện tại.

bởi vì máy chủ sẽ chấp nhận một hành động hơi cũ, khách hàng có thể thêm hành động của mình trực tiếp vào hàng đợi, do đó thời gian khứ hồi qua mạng sẽ bị ẩn, ít nhất là cho hành động của chính bạn.

điều cuối cùng là xếp hàng nhiều hành động hơn để bạn có thể điền vào gói MTU, gây ra ít chi phí protocoll hơn; một ý tưởng hay là làm điều đó trên máy chủ, vì vậy mọi sự kiện kết thúc đều có hành động trên hàng đợi.

Tôi sử dụng algoritm này trong một trò chơi bắn súng thời gian thực và hoạt động tốt (với một khách hàng không có hành động riêng của mình, nhưng với ping máy chủ thấp đến 20 / 50ms), thì mọi máy chủ quay đầu X đều gửi một "tất cả" bản đồ khách hàng "gói, để sửa các giá trị bị trôi.


Các vấn đề toán học dấu phẩy động thường có thể tránh được như vậy - trong RTS, bạn thường có thể dễ dàng thực hiện mô phỏng và di chuyển với số nguyên / điểm cố định và chỉ sử dụng dấu phẩy động cho lớp hiển thị không ảnh hưởng đến hành vi trò chơi.
Peteris

Với số nguyên là khó để làm gạch ngang, trừ khi nó là một bảng bát giác. Không có acceleretion hw cho điểm cố định, vì vậy nó có thể chậm hơn so với phao IEEE754
Lesto
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.