การเขียนสคริปต์และ Cinematics โดยไม่มีเธรด


12

ฉันได้รับการดิ้นรนกับวิธีการใช้สคริปต์ในเครื่องมือเกมของฉัน ฉันมีข้อกำหนดเพียงไม่กี่ข้อเท่านั้น: มันควรจะเป็นสัญชาตญาณฉันไม่ต้องการเขียนภาษาที่กำหนดเองตัวแยกวิเคราะห์และล่ามและฉันไม่ต้องการใช้เธรด (ฉันแน่ใจว่ามีวิธีแก้ปัญหาที่ง่ายกว่า; ฉันไม่ต้องวุ่นวายกับหัวข้อเกมลอจิกหลายตัว) นี่เป็นสคริปต์ตัวอย่างใน Python (หรือที่รู้จักกันในนามของปลอม):

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?")

การเล่าเรื่องชิ้นเอกครั้งนี้ก่อให้เกิดปัญหาในการนำไปปฏิบัติ ฉันไม่สามารถประเมินวิธีการทั้งหมดในคราวเดียวเพราะwalk_toใช้เวลาเล่นเกม ถ้ามันกลับมาทันทีอลิซก็จะเริ่มเดินขึ้นไปหาบ๊อบและ (ในเฟรมเดียวกัน) พูดทักทาย (หรือได้รับการต้อนรับ) แต่ถ้าwalk_toเป็นการบล็อกการโทรที่ส่งคืนเมื่อเธอไปถึงบ็อบเกมของฉันจะติดขัดเพราะมันบล็อกการดำเนินการของเธรดเดียวกันที่ทำให้อลิซเดินได้

ฉันคิดว่าการทำให้แต่ละฟังก์ชั่นalice.walk_to(bob)จัดคิวแอคชั่น - จะผลักวัตถุไปยังคิวซึ่งจะหลุดออกหลังจากอลิซมาถึงบ๊อบไม่ว่าเขาจะอยู่ที่ไหน มันหักอย่างละเอียดมากขึ้น: ifสาขาจะถูกประเมินทันทีดังนั้นบ๊อบอาจทักทายอลิซแม้ว่าหลังของเขาจะหันไปหาเธอก็ตาม

เอ็นจิน / บุคคลอื่น ๆ จัดการสคริปต์อย่างไรโดยไม่สร้างเธรด ฉันเริ่มมองหาในพื้นที่ที่ไม่ใช่เกม - dev เช่น jQuery แอนิเมชันโซ่สำหรับแนวคิด ดูเหมือนว่าควรมีรูปแบบที่ดีสำหรับปัญหาประเภทนี้


1
+1 สำหรับคำถาม แต่โดยเฉพาะอย่างยิ่งสำหรับ "Python (aka pseudocode)" :)
Ricket

Python เปรียบเสมือนมหาอำนาจ
ojrac

คำตอบ:


3

วิธีที่แพนด้าทำเช่นนี้คือการโทรกลับ แทนที่จะปิดกั้นมันจะเป็นสิ่งที่ต้องการ

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)

การมีการเรียกกลับที่สมบูรณ์ช่วยให้คุณสามารถเชื่อมโยงเหตุการณ์ประเภทนี้ได้ลึกเท่าที่คุณต้องการ

แก้ไข: ตัวอย่าง JavaScript เนื่องจากมีไวยากรณ์ที่ดีกว่าสำหรับสไตล์นี้:

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?');
        }
     });
}

มันทำงานได้ดีสำหรับ +1 ฉันแค่คิดว่ามันผิดสำหรับการออกแบบเนื้อหา ฉันยินดีที่จะทำงานพิเศษในตอนท้ายของฉันเพื่อให้สคริปต์ดูเรียบง่ายและชัดเจน
ojrac

1
@ojrac: ที่จริงแล้ววิธีนี้ดีกว่าเพราะที่นี่ในสคริปต์เดียวกันคุณสามารถบอกนักแสดงหลายคนให้เริ่มเดินในเวลาเดียวกัน
Bart van Heukelom

ฮึจุดดี
ojrac

อย่างไรก็ตามเพื่อปรับปรุงความสามารถในการอ่านได้นิยามการเรียกกลับสามารถซ้อนกันในการโทร walk_to () หรือวางหลังจากนั้น (สำหรับทั้งสองไป: หากภาษารองรับ) ดังนั้นโค้ดที่ถูกเรียกในภายหลังจะเห็นในภายหลังในแหล่งที่มา
Bart van Heukelom

ใช่ Python ไม่ได้ยอดเยี่ยมสำหรับไวยากรณ์ประเภทนี้ มันดูดีกว่ามากใน JavaScript (ดูด้านบนไม่สามารถใช้การจัดรูปแบบรหัสที่นี่)
coderanger

13

คำที่คุณต้องการค้นหาที่นี่คือ " coroutines " (และมักจะเป็นคำหลักภาษาหรือชื่อฟังก์ชั่นyield)

Coroutines เป็นส่วนประกอบของโปรแกรมที่ทำให้รูทีนย่อยทั่วไปอนุญาตให้มีจุดเข้าใช้งานหลายจุดสำหรับการระงับและการดำเนินการทำงานต่อในบางตำแหน่ง

การติดตั้งจะขึ้นอยู่กับภาษาของคุณเป็นอันดับแรก สำหรับเกมที่คุณต้องการให้การนำไปใช้นั้นมีน้ำหนักเบาที่สุด (เบากว่าเธรดหรือแม้แต่เส้นใย) หน้า Wikipedia (ลิงค์) มีลิงค์เชื่อมโยงไปยังการใช้งานเฉพาะภาษาต่างๆ

ฉันได้ยินว่า Lua มีการสนับสนุนในตัวสำหรับ coroutines GameMonkey ก็เช่นกัน

UnrealScript ใช้สิ่งนี้กับสิ่งที่เรียกว่า "สถานะ" และ "ฟังก์ชันแฝง"

ถ้าคุณใช้ C # คุณสามารถดูโพสต์บล็อกนี้โดย Nick Gravelyn

นอกจากนี้ความคิด "เชนภาพเคลื่อนไหว" ในขณะที่ไม่เหมือนกันก็เป็นวิธีแก้ปัญหาที่ใช้การได้สำหรับปัญหาเดียวกัน Nick Gravelyn มีการนำ C # มาใช้ด้วย


จับได้ดี, Tetrad;)
Andrew Russell

นี่เป็นสิ่งที่ดีจริงๆ แต่ฉันไม่แน่ใจว่าจะทำให้ฉันไปถึงที่นั่นได้ 100% ดูเหมือนว่า coroutines จะยอมให้คุณใช้วิธีเรียก แต่ฉันต้องการวิธีที่จะให้ผลจากสคริปต์ Lua ตลอดจนสแต็คไปยังรหัส C # โดยไม่ต้องเขียนในขณะที่ (walk_to ()! = เสร็จสิ้น) {yield}
ojrac

@ojrac: ฉันไม่รู้เกี่ยวกับ Lua แต่ถ้าคุณใช้วิธี C # ของ Nick Gravelyn คุณสามารถคืนผู้รับมอบสิทธิ์ (หรือวัตถุที่มีหนึ่ง) ที่ถือเงื่อนไขสำหรับผู้จัดการสคริปต์ของคุณเพื่อตรวจสอบ (โค้ดของ Nick เพิ่งส่งกลับเวลา ซึ่งเป็นเงื่อนไขโดยปริยาย) คุณสามารถให้ฟังก์ชันแฝงตัวเองส่งคืนผู้รับมอบสิทธิ์เพื่อให้คุณสามารถเขียน: yield return walk_to();ในสคริปต์ของคุณ
Andrew Russell

ผลตอบแทนใน C # นั้นยอดเยี่ยม แต่ฉันได้ปรับวิธีการแก้ปัญหาของฉันให้ง่ายขึ้นและยากต่อการเขียนสคริปต์ ฉันจะมีเวลาอธิบายการโทรกลับได้ง่ายกว่าผลตอบแทนดังนั้นฉันจะยอมรับคำตอบอื่น ฉันจะเพิ่ม 2 ถ้าทำได้
ojrac

1
โดยปกติคุณไม่จำเป็นต้องอธิบายการเรียกผลตอบแทน - คุณสามารถสรุปได้ในฟังก์ชั่น "walk_to"
Kylotan

3

ไม่ไปเธรดเป็นสมาร์ท

เอ็นจิ้นเกมส่วนใหญ่ทำงานเป็นชุดของด่านแบบแยกส่วนพร้อมเนื้อหาในหน่วยความจำในการขับขี่แต่ละด่าน สำหรับ 'walk to example' คุณมักจะมีเวที AI ที่ตัวละครที่คุณเดินอยู่ในสถานะที่พวกเขาไม่ควรมองหาศัตรูที่จะฆ่าเวทีแอนิเมชั่นที่พวกเขาควรจะเรียกใช้แอนิเมชั่น X, เวทีฟิสิกส์ (หรือ ระยะการจำลองสถานการณ์) เมื่อมีการอัพเดทตำแหน่งจริง ฯลฯ

ในตัวอย่างของคุณข้างต้น 'อลิซ' เป็นนักแสดงที่ประกอบไปด้วยชิ้นส่วนที่อาศัยอยู่ในหลายช่วงเวลาดังนั้นการปิดกั้นนักแสดง walk_to call (หรือ coroutine ที่คุณโทรหา next () ในครั้งเดียวต่อเฟรม) อาจไม่มีบริบทที่เหมาะสม เพื่อทำการตัดสินใจมากมาย

ฟังก์ชัน 'start_walk_to' อาจทำสิ่งต่อไปนี้แทน:

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

จากนั้นลูปหลักของคุณจะรัน ai tick, ติ๊กฟิสิกส์, ติ๊กแอนิเมชั่นและเห็บ cutscene และ cutscene จะอัพเดตสถานะสำหรับแต่ละระบบย่อยในเอ็นจิ้นของคุณ ระบบ cutscene ควรติดตามสิ่งที่ cutscenes แต่ละตัวทำอยู่และระบบ coroutine ซึ่งเป็นระบบขับเคลื่อนสำหรับสิ่งที่เป็นเชิงเส้นและกำหนดค่าเช่น custscene อาจเข้าท่า

เหตุผลสำหรับโมดูลย่อยนี้ก็คือมันทำให้สิ่งที่ดีและเรียบง่ายและสำหรับบางระบบ (เช่นฟิสิกส์และ AI) คุณจำเป็นต้องรู้สถานะของทุกสิ่งในเวลาเดียวกันเพื่อแก้ไขสิ่งต่าง ๆ อย่างถูกต้องและทำให้เกมอยู่ในสถานะที่สอดคล้องกัน .

หวังว่านี่จะช่วยได้!


ฉันชอบสิ่งที่คุณกำลังจะทำ แต่ฉันรู้สึกถึงคุณค่าของตัวละครอย่างแท้จริง walk_to ([นักแสดงคนอื่นตำแหน่งที่ตายตัวหรือแม้กระทั่งฟังก์ชั่นที่คืนตำแหน่ง)] เป้าหมายของฉันคือการจัดหาเครื่องมือที่เข้าใจง่ายและจัดการกับความซับซ้อนทั้งหมดออกไปจากส่วนการสร้างเนื้อหาของเกม คุณยังช่วยให้ฉันตระหนักว่าสิ่งที่ฉันต้องการจริงๆคือวิธีที่จะปฏิบัติต่อสคริปต์แต่ละบทในฐานะกลไกแห่งรัฐ จำกัด
ojrac

ดีใจที่ฉันสามารถช่วย! ฉันรู้สึกว่าคำตอบของฉันเป็นเรื่องเล็กน้อย :) เห็นด้วยอย่างแน่นอนกับคุณค่าของฟังก์ชั่นนักแสดง walk_to สำหรับการบรรลุเป้าหมายของคุณฉันรอคอยที่จะรับฟังการปฏิบัติของคุณ
Aaron Brady

ดูเหมือนว่าฉันจะไปด้วยการผสมผสานการโทรกลับและฟังก์ชั่นที่ถูกล่ามโซ่ ดูคำตอบที่ยอมรับ;)
ojrac
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.