Lập trình chức năng thuần túy và trạng thái trò chơi


12

Có một kỹ thuật chung để xử lý trạng thái (nói chung) trong ngôn ngữ lập trình chức năng không? Có nhiều giải pháp trong mọi ngôn ngữ lập trình (chức năng) để xử lý trạng thái toàn cầu, nhưng tôi muốn tránh điều này càng nhiều càng tốt.

Tất cả các trạng thái trong một cách chức năng thuần túy là các tham số chức năng. Vì vậy, tôi cần đặt toàn bộ trạng thái trò chơi (một hashmap khổng lồ với thế giới, người chơi, vị trí, điểm số, tài sản, kẻ thù, ...)) làm tham số cho tất cả các chức năng muốn thao túng thế giới trên một đầu vào hoặc kích hoạt nhất định . Bản thân chức năng chọn thông tin có liên quan từ blob của gamestate, làm một cái gì đó với nó, thao túng gamestate và trả lại gamestate. Nhưng điều này có vẻ như một giải pháp mans nghèo cho vấn đề. Nếu tôi đặt toàn bộ gamestate vào tất cả các chức năng, sẽ không có lợi cho tôi trái ngược với các biến toàn cục hoặc cách tiếp cận bắt buộc.

Tôi chỉ có thể đưa thông tin liên quan vào các hàm và trả về các hành động sẽ được thực hiện cho đầu vào đã cho. Và một chức năng duy nhất áp dụng tất cả các hành động cho gamestate. Nhưng hầu hết các chức năng cần rất nhiều thông tin "có liên quan". move()cần vị trí đối tượng, vận tốc, bản đồ cho sự va chạm, vị trí của tất cả kẻ thù, sức khỏe hiện tại, ... Vì vậy, cách tiếp cận này dường như cũng không hiệu quả.

Vì vậy, câu hỏi của tôi là làm thế nào để tôi xử lý số lượng lớn trạng thái trong một ngôn ngữ lập trình chức năng - đặc biệt là để phát triển trò chơi?

EDIT: Có một số khung trò chơi để xây dựng trò chơi trong Clojure. Có cách tiếp cận để giải quyết vấn đề này một phần là xâu chuỗi tất cả các vật thể trong trò chơi dưới dạng "thực thể" và đặt nó vào một cái túi lớn. Một chức năng chính Gigant đang nắm giữ màn hình và các đối tượng và xử lý các sự kiện ( :on-key-down, :on-init, ...) cho các tổ chức này và chạy vòng lặp hiển thị chính. Nhưng đây không phải là giải pháp sạch mà tôi đang tìm kiếm.


Tôi đã suy nghĩ về loại điều này trong một thời gian; đối với tôi, nó không phải là đầu vào đó là vấn đề độc đáo, như bạn vẫn phải thức ăn chăn nuôi (khoảng) các yếu tố cùng một chức năng trong chương trình không hoạt động. Không, đó là đầu ra (và các bản cập nhật liên quan tiếp theo) là vấn đề. Một số tham số đầu vào của bạn nên được kết hợp; vì move(), có lẽ bạn nên chuyển qua đối tượng 'hiện tại' (hoặc một định danh cho nó), cộng với thế giới mà nó đang di chuyển, và chỉ lấy được vị trí và vận tốc hiện tại ... đầu ra là toàn bộ thế giới vật lý, hoặc ít nhất là một danh sách các đối tượng thay đổi.
Clockwork-Muse

lợi thế của chức năng thuần túy là các nguyên mẫu chức năng cho thấy tất cả các phụ thuộc mà chương trình của bạn có.
tp1

3
IMO, ngôn ngữ chức năng rất kém phù hợp để viết trò chơi. Đây là một trong nhiều vấn đề mà bạn sẽ cần phải giải quyết. Các trò chơi đòi hỏi kiểm soát hiệu suất rất chính xác và hiếm khi có sự đồng thời tốt, do cách thức không thể đoán trước được các sự kiện xảy ra một cách tự nhiên. Các ngôn ngữ chức năng (thuần túy) đáng chú ý là có thể song song hóa và khó tối ưu hóa. Một trò chơi là CỨNG để viết, và tôi khuyên bạn chỉ nên thực hiện nó bằng một ngôn ngữ điển hình, trước khi tiếp nhận một thứ gì đó phức tạp (lập trình chức năng).
Casey Kuball

Câu trả lời:


7

Tác dụng phụ và trạng thái trong các ngôn ngữ lập trình chức năng là một vấn đề rộng lớn hơn trong khoa học máy tính. Trong trường hợp bạn đã không gặp họ trước đó, có lẽ có một cái nhìn tại monads . Mặc dù được cảnh báo: họ là một khái niệm khá tiên tiến và hầu hết những người tôi biết (bao gồm cả tôi) đấu tranh để nắm bắt chúng. Có rất nhiều, rất nhiều hướng dẫn trực tuyến, với các cách tiếp cận và yêu cầu kiến ​​thức khác nhau. Cá nhân, tôi thích nhất của Eric Lippert.

Tôi là một lập trình viên C # không có lập trình chức năng của nền tảng nào. Điều này là gì monad mà tôi tiếp tục nghe về nó, và nó có ích gì cho tôi?

Eric Lippert trên Monads

Một số điều cần xem xét, mặc dù:

  • Bạn có khăng khăng sử dụng một ngôn ngữ chức năng thuần túy? Nếu bạn thành thạo cả lập trình chức năng và phát triển trò chơi, có lẽ bạn có thể làm được. (Mặc dù tôi muốn biết liệu những lợi ích có đáng để nỗ lực hay không.)
  • Sẽ không tốt hơn nếu chỉ sử dụng phương pháp chức năng khi cần thiết? Nếu bạn đang sử dụng ngôn ngữ hướng đối tượng (hoặc, nhiều khả năng là ngôn ngữ đa mô hình), không có gì ngăn bạn sử dụng kiểu chức năng để triển khai các phần thu lợi từ nó. (Kiểu như MapReduce, có lẽ vậy?)

Một số suy nghĩ cuối cùng:

  • Xử lý song song: Trong khi trò chơi làm sử dụng nó rất nhiều, AFAIK nhất của nó đã xảy ra trên GPU anyway.
  • Không quốc tịch: Đột biến trạng thái là một phần không thể thiếu của trò chơi. Cố gắng loại bỏ chúng có thể chỉ làm phức tạp các vấn đề không cần thiết.
  • Có thể nhìn vào cách ngôn ngữ chức năng F # chơi với hệ sinh thái hướng đối tượng của .NET, nếu bạn quan tâm.

Nói chung, tôi nghĩ ngay cả khi nó có thể thú vị về mặt học thuật, tôi nghi ngờ phương pháp này là thiết thực và đáng để nỗ lực.


Tại sao đăng bình luận về một chủ đề bạn không có kinh nghiệm? Một ý kiến ​​đến từ những người bị mắc kẹt trong một mô hình suy nghĩ.
Anthony Raimondo

4

Tôi đã viết một vài trò chơi bằng cách sử dụng F # (đa mô hình, không tinh khiết, ngôn ngữ đầu tiên chức năng), với các cách tiếp cận từ OOP đến FRP . Đây là một câu hỏi rộng, nhưng tôi sẽ làm hết sức mình.

Có một kỹ thuật chung để xử lý trạng thái (nói chung) trong ngôn ngữ lập trình chức năng không?

Cách ưa thích của tôi là có một loại bất biến đại diện cho toàn bộ trò chơi State. Sau đó tôi có một tham chiếu có thể thay đổi đến hiện tại Stateđược cập nhật mỗi đánh dấu. Điều này không hoàn toàn thuần túy, nhưng nó giữ khả năng biến đổi giới hạn ở một nơi.

Nếu tôi đặt toàn bộ gamestate vào tất cả các chức năng, sẽ không có lợi cho tôi trái ngược với các biến toàn cục hoặc cách tiếp cận bắt buộc.

Không đúng. Bởi vì Stateloại này là bất biến, bạn không thể có bất kỳ thành phần cũ nào làm thay đổi thế giới theo những cách không xác định. Điều này khắc phục vấn đề lớn nhất với GameObjectcách tiếp cận (phổ biến bởi Unity): thật khó để kiểm soát thứ tự các Updatecuộc gọi.

Và không giống như sử dụng toàn cầu, nó có thể dễ dàng kiểm tra đơn vị và song song hóa,

Bạn cũng nên viết các hàm trợ giúp nhận các thuộc tính phụ của trạng thái để phân tích vấn đề.

Ví dụ:

let updateSpaceShip ship = 
  {
    ship with 
      Position = ship.Position + ship.Velocity
  }

let update state = 
  { 
    state with 
      SpaceShips = state.SpaceShips |> map updateSpaceShip 
  }

Ở đây updatehành động trên toàn bộ nhà nước, nhưng updateSpaceShipchỉ hành động trên một cá nhân SpaceShiptrong sự cô lập.

Tôi chỉ có thể đưa thông tin liên quan vào các hàm và trả về các hành động sẽ được thực hiện cho đầu vào đã cho.

Đề nghị của tôi sẽ là tạo ra một Inputloại giữ các trạng thái bàn phím, chuột, trò chơi, v.v. Sau đó, bạn có thể viết một hàm mất một StateInputtrả về tiếp theo State:

let applyInput input state = 
  // Something

Để cho bạn biết làm thế nào điều này khớp với nhau, toàn bộ trò chơi có thể trông giống như thế này:

let mutable state = initialState ()

// Game loop
while true do
  // Apply user input to the world
  let input = collectInput ()

  state <- applyInput input state

  // Game logic
  let dt = computeDeltaTime ()

  state <- update dt state

  // Draw
  render state

Vì vậy, câu hỏi của tôi là làm thế nào để tôi xử lý số lượng lớn trạng thái trong một ngôn ngữ lập trình chức năng - đặc biệt là để phát triển trò chơi?

Đối với các trò chơi đơn giản, bạn có thể sử dụng cách tiếp cận ở trên (chức năng trợ giúp). Đối với một cái gì đó phức tạp hơn, bạn có thể muốn thử " ống kính ".

Trái với một số ý kiến ​​ở đây, tôi rất muốn đề xuất viết trò chơi bằng ngôn ngữ lập trình chức năng (không trong sạch), hoặc ít nhất là thử nó! Tôi đã tìm thấy nó làm cho việc lặp lại nhanh hơn, cơ sở mã nhỏ hơn và các lỗi ít phổ biến hơn.

Tôi cũng không nghĩ rằng bạn cần phải học các đơn vị để viết trò chơi bằng ngôn ngữ FP (không trong sạch). Điều này là do mã của bạn rất có thể sẽ bị chặn và luồng đơn.

Điều này đặc biệt đúng nếu bạn đang viết một trò chơi nhiều người chơi. Sau đó, mọi thứ sẽ trở nên dễ dàng hơn nhiều với cách tiếp cận này vì nó cho phép bạn tuần tự hóa trạng thái trò chơi một cách tầm thường và gửi nó qua mạng.

Về lý do tại sao nhiều trò chơi không được viết theo cách này ... tôi không thể nói. Tuy nhiên, điều này đúng trong tất cả các lĩnh vực lập trình (ngoại trừ tài chính), vì vậy tôi sẽ không sử dụng điều này như một lập luận rằng các ngôn ngữ chức năng không phù hợp với lập trình trò chơi.

Cũng đáng đọc là Retrogames hoàn toàn chức năng .


1

Những gì bạn đang tìm kiếm là phát triển trò chơi FRP.

Một số video giới thiệu:

100% có thể và tốt hơn là làm cho logic trò chơi cốt lõi theo cách hoàn toàn có chức năng, toàn bộ ngành công nghiệp chỉ đơn giản là phía sau, bị mắc kẹt trong một mô hình suy nghĩ.

Nó có thể làm điều đó trong Unity là tốt.

Để trả lời câu hỏi, một trạng thái trò chơi mới sẽ được cập nhật / tạo ra mỗi khi có thứ gì đó di chuyển, như carmack nói trong bài nói chuyện của anh ta, đó không phải là vấn đề. Việc giảm mạnh chi phí nhận thức xuất phát từ một kiến ​​trúc hoàn toàn có chức năng, có khả năng bảo trì cao, vượt xa các hiệu suất đạt được, nếu nó tồn tại.

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.