เทคนิคการจัดการอินพุตในเกมขนาดใหญ่


16

มีเทคนิคมาตรฐานในการจัดการอินพุตในเกมขนาดใหญ่หรือไม่ ขณะนี้ในโครงการของฉันการจัดการอินพุตทั้งหมดเสร็จในลูปเกมดังนี้:

while(SDL_PollEvent(&event)){
            switch(event.type){
                case SDL_QUIT:
                    exit = 1;
                    break;
                case SDL_KEYDOWN:
                    switch(event.key.keysym.sym){
                        case SDLK_c:
                            //do stuff
                            break;
                    }
                    break;
                case SDL_MOUSEBUTTONDOWN:
                    switch(event.button.button){
                        case SDL_BUTTON_MIDDLE:
                                //do stuff
                                break;
                            }
                    }
                    break;
            }

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


ด้วยผู้จัดการเหตุการณ์คุณสามารถยิงเหตุการณ์ในอินพุทและปล่อยให้ส่วนอื่น ๆ ทั้งหมดของเกมของคุณลงทะเบียนได้
danijar

@danijar คุณหมายถึงอะไรโดยผู้จัดการกิจกรรมเป็นไปได้หรือไม่ถ้าคุณสามารถให้รหัสเทียมของโครงกระดูกเพื่อแสดงว่าคุณกำลังพูดถึงเรื่องอะไร
w4etwetewtwet


1
ฉันเขียนคำตอบเพื่ออธิบายอย่างละเอียดเกี่ยวกับผู้จัดการกิจกรรมซึ่งเป็นวิธีการจัดการป้อนข้อมูลสำหรับฉัน
danijar

คำตอบ:


12

ตั้งแต่ถูกถามโดยผู้เริ่มต้นเธรดฉันได้อธิบายรายละเอียดเกี่ยวกับผู้จัดการเหตุการณ์ ฉันคิดว่านี่เป็นวิธีที่ดีในการจัดการอินพุตในเกม

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

การเรียกกลับอาจเป็นstd::functionวัตถุที่สามารถเก็บลูกแกะได้ กุญแจอาจเป็นสตริง เนื่องจากผู้จัดการเป็นสากลส่วนประกอบของแอปพลิเคชันของคุณสามารถลงทะเบียนเพื่อรับกุญแจจากส่วนประกอบอื่น

// in character controller
// at initialization time
Events->Register("Jump", [=]{
    // perform the movement
});

// in input controller
// inside the game loop
// note that I took the code structure from the question
case SDL_KEYDOWN:
    switch(event.key.keysym.sym) {
    case SDLK_c:
        Events->Fire("Jump");
        break;
    }
    break;

คุณสามารถขยายผู้จัดการเหตุการณ์นี้เพื่ออนุญาตให้ส่งค่าเป็นอาร์กิวเมนต์เพิ่มเติม เทมเพลต C ++ นั้นยอดเยี่ยมสำหรับสิ่งนี้ คุณสามารถใช้ระบบดังกล่าวเพื่อบอกว่า"WindowResize"เหตุการณ์ผ่านขนาดหน้าต่างใหม่เพื่อให้องค์ประกอบการฟังไม่จำเป็นต้องดึงข้อมูลเอง สิ่งนี้สามารถลดการพึ่งพาโค้ดได้เล็กน้อย

Events->Register<int>("LevelUp", [=](int NewLevel){ ... });

ฉันใช้งานตัวจัดการเหตุการณ์สำหรับเกมของฉันแล้ว หากคุณสนใจฉันจะโพสต์ลิงค์ไปยังรหัสที่นี่

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


4
คำตอบที่ดีขอบคุณ แม้ว่าฉันจะชอบที่จะเห็นรหัส แต่ฉันไม่ต้องการที่จะคัดลอกมันดังนั้นฉันจะไม่ขอให้คุณโพสต์มันจนกว่าฉันจะนำไปใช้เองฉันจะเรียนรู้เพิ่มเติม
w4etwetewtwet

ฉันแค่คิดถึงบางสิ่งฉันสามารถส่งผ่านฟังก์ชั่นสมาชิกใด ๆ แบบนี้ได้หรือไม่ฟังก์ชัน register จะต้องใช้ AClass :: func จำกัด เฉพาะฟังก์ชันสมาชิกคลาสเดียว
w4etwetewtwet

นั่นคือสิ่งที่ยอดเยี่ยมเกี่ยวกับการแสดงออกแลมบ์ดาใน C ++ คุณสามารถระบุประโยคการจับภาพ[=]และการอ้างอิงไปยังตัวแปรท้องถิ่นทั้งหมดที่เข้าถึงได้จากแลมบ์ดาจะถูกคัดลอกไป ดังนั้นคุณไม่ต้องผ่านตัวชี้หรือสิ่งนี้ แต่ทราบว่าคุณไม่สามารถเก็บ lambdas ข้อจับภาพเก่า C คำแนะนำการทำงาน อย่างไรก็ตาม C ++ std::functionทำงานได้ดี
danijar

ฟังก์ชัน std :: ทำงานช้ามาก
TheStatehz

22

แบ่งเป็นหลายเลเยอร์

ที่เลเยอร์ต่ำสุดที่คุณมีเหตุการณ์การป้อนข้อมูลดิบจากระบบปฏิบัติการ อินพุตคีย์บอร์ด SDL, อินพุตของเมาส์, อินพุตของจอยสติ๊ก ฯลฯ คุณอาจมีหลายแพลตฟอร์ม (SDL เป็นตัวตั้งร่วมน้อยที่สุดที่ไม่มีแบบฟอร์มป้อนข้อมูลหลายรูปแบบเช่นคุณอาจสนใจในภายหลัง)

คุณสามารถสรุปสิ่งเหล่านี้ด้วยประเภทเหตุการณ์ที่กำหนดเองในระดับต่ำมากเช่น "ปุ่มแป้นพิมพ์ลง" หรือสิ่งที่คล้ายกัน เมื่อเลเยอร์แพลตฟอร์มของคุณ (วนเกม SDL) ได้รับอินพุตมันควรสร้างเหตุการณ์ระดับต่ำเหล่านี้แล้วส่งต่อไปยังตัวจัดการอินพุต มันสามารถทำสิ่งเหล่านี้ได้ด้วยวิธีการโทรแบบง่ายฟังก์ชั่นการโทรกลับระบบเหตุการณ์ที่ซับซ้อนไม่ว่าคุณจะชอบอะไรดีที่สุด

ขณะนี้ระบบอินพุตมีหน้าที่แปลอินพุตระดับต่ำเป็นเหตุการณ์โลจิคัลระดับสูง ตรรกะของเกมไม่ได้สนใจเลยว่ามีการกด SPACE มันสนใจว่า JUMP ถูกกด งานของผู้จัดการอินพุตคือการรวบรวมเหตุการณ์อินพุตระดับต่ำเหล่านี้และสร้างเหตุการณ์อินพุตระดับสูง มันเป็นความรับผิดชอบของการรู้ว่าสเปซบาร์และปุ่ม 'A' gamepad แมปไปยังคำสั่งทางตรรกะ Jump มันเกี่ยวกับ gamepad vs mouse look control และอื่น ๆ มันส่งเหตุการณ์โลจิคัลระดับสูงที่เป็นนามธรรมให้ได้มากที่สุดจากตัวควบคุมระดับต่ำ (มีข้อ จำกัด บางอย่างที่นี่ แต่คุณสามารถสรุปสิ่งต่าง ๆ ได้อย่างสมบูรณ์ในกรณีทั่วไป)

ตัวควบคุมอักขระของคุณจะได้รับเหตุการณ์เหล่านี้และประมวลผลเหตุการณ์อินพุตระดับสูงเหล่านี้เพื่อตอบสนองจริง เลเยอร์แพลตฟอร์มส่งกิจกรรม "Key down spacebar" ระบบป้อนข้อมูลที่ได้รับนั้นดูที่ตาราง / ตรรกะการแมปแล้วส่งเหตุการณ์ "กดข้าม" ตรรกะของเกม / ตัวควบคุมตัวละครได้รับเหตุการณ์นั้นตรวจสอบว่าผู้เล่นได้รับอนุญาตให้กระโดดแล้วปล่อยเหตุการณ์ "ผู้เล่นกระโดด" (หรือเพียงแค่ทำให้เกิดการกระโดดเกิดขึ้นโดยตรง) ซึ่งส่วนที่เหลือของเกมตรรกะใช้ทำอะไร .

อะไรก็ตามที่ขึ้นอยู่กับตรรกะของเกมจะเข้าสู่ตัวควบคุมผู้เล่น ระบบปฏิบัติการใดที่ขึ้นอยู่กับเลเยอร์แพลตฟอร์ม ส่วนที่เหลือทั้งหมดจะเข้าสู่ชั้นการจัดการอินพุต

นี่คือศิลปะ ASCII ที่ไม่ชำนาญที่จะอธิบายสิ่งนี้:

-----------------------------------------------------------------------
Platform Abstraction | Collect and forward OS input events
-----------------------------------------------------------------------
                          | |
                          | |
                         \   /
                          \_/
-----------------------------------------------------------------------
    Input Manager    | Translate OS input events into logical events
-----------------------------------------------------------------------
                          | |
                          | |
                         \   /
                          \_/
-----------------------------------------------------------------------
Character Controller | React to logical events and affect game play
-----------------------------------------------------------------------
                          | |
                          | |
                         \   /
                          \_/
-----------------------------------------------------------------------
      Game Logic     | React to player actions and provides feedback
-----------------------------------------------------------------------

เท่ศิลปะ ASCII แต่ไม่จำเป็นเลยฉันขอโทษ ฉันแนะนำให้ใช้รายการหมายเลขแทน อย่างไรก็ตามคำตอบที่ดี!
danijar

1
@danijar: เอ๊ะฉันกำลังทดลองไม่เคยลองวาดคำตอบก่อนหน้านี้ งานมากกว่าที่ควรจะเป็น แต่ก็มีงานน้อยกว่าการทำงานกับโปรแกรมระบายสี :)
Sean Middleditch

ดีเข้าใจได้ :-)
danijar

8
โดยส่วนตัวแล้วฉันชอบศิลปะ ASCII มากกว่ารายการตัวเลขที่น่าเบื่อ
Jesse Emond

@JesseEmond เฮ้นี่นี่เพื่อศิลปะเหรอ?
danijar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.