มีวิธีใดบ้างในการแยกลอจิกเกมออกจากอนิเมชั่นและลูปการวาด?


9

ฉันเพิ่งสร้างเกมแฟลชโดยใช้ MovieClips และแยกภาพเคลื่อนไหวออกจากตรรกะเกมของฉัน ตอนนี้ฉันพยายามทำเกมสำหรับ Android แต่ทฤษฎีการเขียนโปรแกรมเกมเกี่ยวกับการแยกสิ่งเหล่านี้ยังทำให้ฉันสับสน ฉันมาจากพื้นหลังของการพัฒนาเว็บแอปพลิเคชันที่ไม่ใช่เกมดังนั้นฉันจึงมีประสบการณ์ใน MVC มากกว่าเช่นรูปแบบและติดอยู่ในความคิดนั้นเมื่อฉันเข้าใกล้การเขียนโปรแกรมเกม

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

ฉันจะแยกความคิดนี้ออกและเริ่มคิดเกี่ยวกับรูปแบบที่เหมาะสมกับเกมได้อย่างไร

คำตอบ:


6

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

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

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

ในรหัส (ใช้ไวยากรณ์ C ++ ที่นี่):

class Sprite //Model
{
    private:
       Rectangle subrect;
       Vector2f position;
       //etc.

    public:
       Rectangle GetSubrect() 
       {
           return subrect;
       }
       //etc.
};

class AnimatedSprite : public Sprite, public Updatable //arbitrary interface for classes that need to change their state on a regular basis
{
    AnimationController animation_controller;
    //etc.
    public:
        void Update()
        {
            animation_controller.Update(); //Good OOP design ;) It will take control of changing animations in time etc. for you
            this.SetSubrect(animation_controller.GetCurrentAnimation().GetRect());
        }
        //etc.
};

นั่นคือข้อมูล โหมดแสดงภาพของคุณจะใช้ข้อมูลนั้นและวาด เนื่องจากทั้ง Sprite ธรรมดาและการ์ตูนเคลื่อนไหวถูกวาดในลักษณะเดียวกันคุณสามารถใช้ polymorphy ได้ที่นี่!

class Renderer
{
    //etc.
    public:
       void Draw(const Sprite &spr)
       {
           graphics_api_pointer->Draw(spr.GetAllTheDataThatINeed());
       }
};

TMV:

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

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

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

วิธีที่คุณต้องการอัปเดตโมเดลเมื่อผู้เล่นย้ายขึ้นอยู่กับสิ่งที่คุณต้องการจะทำ

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

ทั้งสองวิธีถ้าคุณต้องการทำให้มันง่ายขึ้นในตัวคุณเองทุกรุ่นของคุณจะมีวิธีการ Update () หากพวกเขาต้องการตรรกะบางอย่างเพื่อปรับปรุงตัวเอง (แทนที่จะเป็นแค่การเปลี่ยนค่าของตัวแปร) - คุณไม่ได้มอบตัวควบคุม ในรูปแบบ MVC ด้วยวิธีนี้คุณเพียงแค่ย้ายรหัสจาก "หนึ่งขั้นตอนข้างบน" (ตัวควบคุม) ลงไปที่รูปแบบและตัวควบคุมทั้งหมดจะเรียกวิธีการอัพเดตนี้ () ของรูปแบบ (ตัวควบคุมในกรณีของเราจะเป็น RPGMap) คุณยังสามารถสลับรหัส logics ได้อย่างง่ายดาย - คุณสามารถเปลี่ยนรหัสของคลาสได้โดยตรงหรือหากคุณต้องการพฤติกรรมที่แตกต่างไปจากเดิมอย่างสิ้นเชิงคุณสามารถได้มาจากคลาสรุ่นของคุณและแทนที่เมธอด Update () เท่านั้น

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


คุณจะเก็บข้อมูลแอนิเมชั่นในคลาสที่มีลอจิกเกมคลาสที่มีลูปเกมหรือแยกจากทั้งสองหรือไม่ ยิ่งไปกว่านั้นมันขึ้นอยู่กับวงวนหรือคลาสที่มีลูปเพื่อที่จะเข้าใจวิธีการแปลข้อมูลอนิเมชั่นไปสู่การวาดภาพบนหน้าจอจริงไหม? มันมักจะไม่ง่ายเหมือนการรับ rect ที่แทนส่วนของ sprite ชีทและใช้มันเพื่อ clip bitmap draw จาก sprite ชีท
TMV

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

ฉันแก้ไขในคำตอบสำหรับคำถามของคุณเนื่องจากความคิดเห็นไม่อนุญาตให้มีอักขระเพียงพอ
TravisG

ถ้าฉันเข้าใจทุกอย่างถูกต้อง:
TMV

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

2

ฉันสามารถพัฒนาสิ่งนี้ได้หากคุณต้องการ แต่ฉันมีโหมดแสดงภาพกลางที่บอกให้วาดในลูป ค่อนข้างมากกว่า

handle input

for every entity:
    update entity

for every entity:
    draw entity

ฉันมีระบบมากกว่านี้

handle input (well, update the state. Mine is event driven so this is null)

for every entity:
    update entity //still got game logic here

renderer.draw();

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

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

เฟรมแอนิเมชั่นอิสระไม่จำเป็นต้องทำมากเกินไปกับลูปการวาด


0

ฉันได้เรียนรู้กระบวนทัศน์ของตัวเองเมื่อเร็ว ๆ นี้ดังนั้นหากคำตอบนี้ไม่สมบูรณ์ฉันมั่นใจว่าจะมีคนเพิ่มเข้าไป

วิธีการที่ดูเหมือนว่าเหมาะสมที่สุดสำหรับการออกแบบเกมคือการแยกตรรกะออกจากหน้าจอ

ในกรณีส่วนใหญ่ที่คุณต้องการจะใช้วิธีการหลายเธรดถ้าคุณไม่คุ้นเคยกับหัวข้อที่เป็นคำถามทั้งหมดเองของไพรเมอร์วิกินี่ของของ โดยพื้นฐานแล้วคุณต้องการให้ตรรกะเกมของคุณดำเนินการในหนึ่งเธรดล็อคตัวแปรที่จำเป็นในการเข้าถึงเพื่อให้แน่ใจว่ามีความสมบูรณ์ของข้อมูล หากวงลอจิกของคุณเร็วอย่างไม่น่าเชื่อ (พงษ์เคลื่อนไหว 3 มิติที่ยิ่งใหญ่อย่างยิ่งใหญ่) คุณสามารถลองแก้ไขความถี่ของการวนรอบที่ดำเนินการโดยการนอนด้ายสำหรับช่วงเวลาเล็ก ๆ (120 hz ได้รับการแนะนำในฟอรัมนี้ ในขณะเดียวกันเธรดอื่นกำลังวาดหน้าจอใหม่ (แนะนำให้ 60 เฮิร์ตซ์ในหัวข้ออื่น) พร้อมกับตัวแปรที่ได้รับการอัพเดตอีกครั้งเพื่อขอให้ล็อคตัวแปรก่อนที่จะเข้าถึง

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

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

หวังว่าจะช่วย :)

[แก้ไข] ในระบบที่ไม่รองรับการทำงานแบบมัลติเธรดภาพเคลื่อนไหวยังสามารถเข้าไปในลูปการวาดได้ แต่คุณต้องการตั้งค่าสถานะเพื่อส่งสัญญาณไปยังลอจิกที่สิ่งต่าง ๆ เกิดขึ้นและไม่ประมวลผลต่อไป ระดับปัจจุบัน / แผนที่ / ฯลฯ ...


1
ฉันไม่เห็นด้วยที่นี่ ในกรณีส่วนใหญ่คุณไม่ต้องการใช้หลายเธรดโดยเฉพาะถ้าเป็นเกมเล็ก ๆ
The Communist Duck

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