Viết kịch bản và quay phim mà không cần luồng


12

Tôi đã vật lộn với cách triển khai kịch bản trong công cụ trò chơi của mình. Tôi chỉ có một vài yêu cầu: Nó phải trực quan, tôi không muốn viết một ngôn ngữ tùy chỉnh, trình phân tích cú pháp và trình thông dịch, và tôi không muốn sử dụng luồng. (Tôi chắc chắn có một giải pháp đơn giản hơn; tôi không cần rắc rối của nhiều luồng logic trò chơi.) Đây là một kịch bản ví dụ, trong Python (còn gọi là mã giả):

def dramatic_scene(actors):
    alice = actors["alice"]
    bob = actors["bob"]

    alice.walk_to(bob)
    if bob.can_see(alice):
        bob.say("Hello again!")
    else:
        alice.say("Excuse me, Bob?")

Đó là đoạn sử thi kể chuyện đặt ra vấn đề thực hiện. Tôi không thể đánh giá toàn bộ phương pháp cùng một lúc, vì walk_tomất thời gian trò chơi. Nếu nó trở lại ngay lập tức, Alice sẽ bắt đầu đi đến chỗ Bob và (trong cùng một khung) nói xin chào (hoặc được chào đón). Nhưng nếu walk_tomột cuộc gọi chặn trở lại khi cô ấy gặp Bob, thì trò chơi của tôi bị kẹt, bởi vì nó chặn cùng một chuỗi hành quyết sẽ khiến Alice bước đi.

Tôi đã cân nhắc việc biến mỗi chức năng thành một hành động - alice.walk_to(bob)sẽ đẩy một vật thể lên hàng đợi, thứ sẽ bật ra sau khi Alice đến Bob, bất cứ nơi nào anh ta ở. Điều đó bị phá vỡ một cách tinh tế hơn: ifchi nhánh được đánh giá ngay lập tức, vì vậy Bob có thể chào Alice ngay cả khi lưng anh quay về phía cô.

Làm thế nào để các công cụ / người khác xử lý kịch bản mà không tạo chủ đề? Tôi đang bắt đầu tìm kiếm ở các khu vực không phải là nhà phát triển trò chơi, như chuỗi hoạt hình jQuery, để tìm ý tưởng. Có vẻ như nên có một số mô hình tốt cho loại vấn đề này.


1
+1 cho câu hỏi nhưng đặc biệt là "Python (còn gọi là mã giả)" :)
Ricket

Python giống như có một siêu năng lực.
ojrac

Câu trả lời:


3

Cách một cái gì đó như Panda làm điều này là với các cuộc gọi lại. Thay vì chặn nó sẽ giống như

def dramatic_scene(actors):
    alice = actors["alice"]
    bob = actors["bob"]

    def cb():
        if bob.can_see(alice):
            bob.say("Hello again!")
        else:
            alice.say("Excuse me, Bob?")
    alice.walk_to(bob, cb)

Có một cuộc gọi lại hoàn chỉnh cho phép bạn xâu chuỗi các loại sự kiện này sâu sắc như bạn muốn.

EDIT: Ví dụ JavaScript vì có cú pháp tốt hơn cho kiểu này:

function dramatic_scene(actors) {
    var alice = actors.alice;
    var bob = actors.bob;
    alice.walk_to(bob, function() {
        if(bob.can_see(alice)) {
            bob.say('Hello again!');
        } else {
            alice.say('Excuse me, Bob?');
        }
     });
}

Nó hoạt động đủ cho +1, tôi chỉ nghĩ rằng nó sai khi thiết kế nội dung. Tôi sẵn sàng làm thêm một số công việc của mình để kịch bản trông đơn giản và rõ ràng.
ojrac

1
@ojrac: Thật ra cách này tốt hơn, bởi vì ở đây trong cùng một kịch bản, bạn có thể nói với nhiều diễn viên bắt đầu đi bộ cùng một lúc.
Bart van Heukelom

Ugh, điểm tốt.
ojrac

Tuy nhiên, để cải thiện khả năng đọc, định nghĩa gọi lại có thể được lồng bên trong lệnh gọi walk_to () hoặc đặt sau nó (cho cả hai đi: nếu ngôn ngữ hỗ trợ) để mã được gọi sau sẽ được nhìn thấy sau trong nguồn.
Bart van Heukelom

Vâng, Python không thực sự tuyệt vời cho loại cú pháp này không may. Nó trông đẹp hơn rất nhiều trong JavaScript (xem ở trên, không thể sử dụng định dạng mã ở đây).
coderanger

13

Thuật ngữ bạn muốn tìm kiếm ở đây là " coroutines " (và thường là từ khóa ngôn ngữ hoặc tên hàm là yield).

Coroutines là các thành phần chương trình tổng quát hóa các chương trình con để cho phép nhiều điểm vào để tạm dừng và tiếp tục thực hiện tại các vị trí nhất định.

Việc thực hiện sẽ phụ thuộc trước hết vào ngôn ngữ của bạn. Đối với một trò chơi, bạn muốn việc thực hiện càng nhẹ càng tốt (nhẹ hơn các luồng hoặc thậm chí là các sợi). Trang Wikipedia (được liên kết) có một số liên kết đến các triển khai ngôn ngữ cụ thể khác nhau.

Tôi nghe nói Lua đã tích hợp hỗ trợ cho coroutines. GameMonkey cũng vậy.

UnrealScript thực hiện điều này với cái mà nó gọi là "trạng thái" và "hàm tiềm ẩn".

Nếu bạn sử dụng C #, bạn có thể xem bài đăng trên blog này của Nick Gravelyn.

Ngoài ra, ý tưởng "chuỗi hoạt hình", trong khi không giống nhau, là một giải pháp khả thi cho cùng một vấn đề. Nick Gravelyn cũng có triển khai C # về việc này .


Bắt đẹp, Tetrad;)
Andrew Russell

Điều này thực sự tốt, nhưng tôi không chắc nó sẽ giúp tôi đạt được 100%. Có vẻ như coroutines cho phép bạn mang lại phương thức gọi, nhưng tôi muốn có một cách để mang lại từ tập lệnh Lua, tất cả các cách lên ngăn xếp thành mã C # mà không cần viết trong khi (walk_to ()! = Thực hiện) {ield}.
ojrac

.... đó là ngầm một điều kiện). Bạn thậm chí có thể có các hàm tiềm ẩn trả về đại biểu, vì vậy bạn có thể viết: yield return walk_to();trong tập lệnh của mình.
Andrew Russell

Hiệu suất trong C # là tuyệt vời, nhưng tôi đang tối ưu hóa giải pháp của mình cho kịch bản đơn giản, khó gây rối. Tôi sẽ có một thời gian dễ dàng hơn để giải thích các cuộc gọi lại hơn năng suất, vì vậy tôi sẽ chấp nhận câu trả lời khác. Tôi sẽ +2 nếu tôi có thể.
ojrac

1
Thông thường bạn không phải giải thích lệnh gọi lợi tức - chẳng hạn, bạn có thể gói nó trong hàm "walk_to".
Kylotan

3

không đi ren là thông minh.

Hầu hết các công cụ trò chơi hoạt động như một chuỗi các giai đoạn mô-đun với công cụ trong bộ nhớ điều khiển từng giai đoạn. Đối với 'ví dụ' của bạn, bạn thường có một giai đoạn AI trong đó các nhân vật đi bộ của bạn ở trạng thái không nên tìm kiếm kẻ thù để giết, giai đoạn hoạt hình nơi họ nên chạy hoạt hình X, giai đoạn vật lý (hoặc giai đoạn mô phỏng) nơi vị trí thực tế của họ được cập nhật, vv

trong ví dụ của bạn ở trên, 'alice' là một diễn viên gồm nhiều mảnh sống trong nhiều giai đoạn này, do đó, một diễn viên chặn.walk_to gọi (hoặc một coroutine bạn gọi tiếp theo () trên một khung hình) có thể không có ngữ cảnh phù hợp để đưa ra nhiều quyết định.

Thay vào đó, chức năng 'start_walk_to' có thể sẽ làm một cái gì đó như:

def start_cutscene_walk_to(actor,target):
    actor.ai.setbrain(cutscene_brain)
    actor.physics.nocoll = 1
    actor.anims.force_anim('walk')
    # etc.

Sau đó, vòng lặp chính của bạn chạy dấu tích ai, đánh dấu vật lý, đánh dấu hoạt hình và đánh dấu cắt cảnh của nó và đoạn cắt cảnh cập nhật trạng thái cho từng hệ thống con trong động cơ của bạn. Hệ thống cutscene chắc chắn phải theo dõi những gì từng đoạn cắt cảnh của nó đang làm, và một hệ thống điều khiển coroutine cho một cái gì đó tuyến tính và xác định như một custcene có thể có ý nghĩa.

Lý do cho tính mô-đun này là vì nó giữ cho mọi thứ tốt đẹp và đơn giản, và đối với một số hệ thống (như vật lý và AI), bạn cần biết trạng thái của mọi thứ cùng một lúc để giải quyết mọi thứ đúng cách và giữ cho trò chơi luôn ở trạng thái nhất quán .

hi vọng điêu nay co ich!


Tôi thích những gì bạn đang làm, nhưng tôi thực sự cảm thấy mạnh mẽ về giá trị của diễn viên.walk_to ([một diễn viên khác, một vị trí cố định hoặc thậm chí là một chức năng trả về một vị trí]). Mục tiêu của tôi là cung cấp các công cụ đơn giản, dễ hiểu và xử lý tất cả sự phức tạp khỏi phần tạo nội dung của trò chơi. Bạn cũng giúp tôi nhận ra rằng tất cả những gì tôi thực sự muốn là một cách để coi mỗi kịch bản là một máy trạng thái hữu hạn.
ojrac

rất vui vì tôi có thể giúp! Tôi cảm thấy câu trả lời của mình hơi lạc đề :) chắc chắn đồng ý với giá trị của một diễn viên.walk_to để hoàn thành mục tiêu của bạn, tôi mong muốn được nghe về việc thực hiện của bạn.
Aaron Brady

Có vẻ như tôi sẽ kết hợp các hàm gọi lại và các hàm chuỗi theo kiểu jQuery. Xem câu trả lời được chấp nhận;)
ojrac
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.