การแยกภาพวาดและตรรกะในเกม


11

ฉันเป็นนักพัฒนาที่เพิ่งเริ่มยุ่งเกี่ยวกับการพัฒนาเกม ฉันเป็น. Net ผู้ชายดังนั้นฉันจึงยุ่งกับ XNA และตอนนี้ฉันกำลังเล่นกับ Cocos2d สำหรับ iPhone คำถามของฉันนั้นกว้างกว่าจริง ๆ

สมมติว่าฉันกำลังสร้างเกมโป่งง่ายๆ ฉันมีBallชั้นเรียนและPaddleชั้นเรียน มาจากการพัฒนาของโลกธุรกิจสัญชาตญาณแรกของฉันคือไม่มีรหัสรูปวาดหรือการป้อนข้อมูลในทั้งสองคลาส

//pseudo code
class Ball
{
   Vector2D position;
   Vector2D velocity;
   Color color;
   void Move(){}
}

ไม่มีสิ่งใดในคลาสบอลที่จัดการอินพุตหรือจัดการกับรูปวาด จากนั้นฉันจะมีคลาสอีกคลาสของฉันGameหรือของฉันScene.m(ใน Cocos2d) ซึ่งจะสร้างบอลขึ้นมาใหม่และในระหว่างวนรอบเกมมันจะจัดการกับบอลตามที่ต้องการ

ในบทเรียนหลายบทสำหรับทั้ง XNA และ Cocos2d ฉันเห็นรูปแบบเช่นนี้:

//pseudo code
class Ball : SomeUpdatableComponent
{
   Vector2D position;
   Vector2D velocity;
   Color color;
   void Update(){}
   void Draw(){}
   void HandleInput(){}
}

คำถามของฉันคือถูกไหม นี่เป็นรูปแบบที่ผู้คนใช้ในการพัฒนาเกมหรือไม่? มันขัดแย้งกับทุกสิ่งที่ฉันคุ้นเคยเพื่อให้ชั้นบอลทำทุกอย่าง นอกจากนี้ในตัวอย่างที่สองนี้ที่ฉันBallรู้วิธีที่จะย้ายไปรอบ ๆ ว่าฉันจะจัดการตรวจสอบการชนกับPaddle? จะBallต้องมีความรู้เกี่ยวกับPaddle? ในตัวอย่างแรกของฉันGameชั้นจะมีการอ้างอิงถึงทั้งBallและPaddleและจากนั้นจัดส่งทั้งสองไปยังCollisionDetectionผู้จัดการหรือบางสิ่งบางอย่าง แต่ฉันจะจัดการกับความซับซ้อนของส่วนประกอบต่าง ๆ ได้อย่างไรถ้าแต่ละองค์ประกอบทำทุกอย่างด้วยตัวเอง? (ฉันหวังว่าฉันจะทำให้รู้สึก ..... )


2
น่าเสียดายจริง ๆ แล้วมันเป็นวิธีที่ทำได้ในหลาย ๆ เกม ฉันไม่คิดว่ามันเป็นแนวปฏิบัติที่ดีที่สุด บทเรียนจำนวนมากที่คุณกำลังอ่านอาจมีจุดมุ่งหมายเพื่อผู้เริ่มต้นดังนั้นพวกเขาจะไม่เริ่มอธิบายโมเดล / มุมมอง / คอนโทรลเลอร์หรือรูปแบบแอกให้กับผู้อ่าน
tenpn

@tenpn ความคิดเห็นของคุณดูเหมือนจะแนะนำว่าคุณไม่คิดว่านี่เป็นวิธีปฏิบัติที่ดีฉันสงสัยว่าคุณจะอธิบายเพิ่มเติมได้ไหม? ฉันคิดเสมอว่ามันเป็นการจัดเรียงที่เหมาะสมที่สุดเนื่องจากวิธีการเหล่านั้นมีรหัสซึ่งมีความเฉพาะเจาะจงมากสำหรับแต่ละประเภท แต่ฉันมีประสบการณ์น้อยมากดังนั้นฉันจึงอยากฟังความคิดของคุณ
sebf

1
@sebf มันไม่ทำงานถ้าคุณชอบการออกแบบที่เน้นข้อมูลและมันจะไม่ทำงานถ้าคุณชอบการออกแบบที่เน้นวัตถุ ดูเจ้าชายโซลิดของลุงบ็อบ: butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
tenpn

@tenpn บทความและเอกสารที่เชื่อมโยงภายในนั้นน่าสนใจมากขอบคุณ!
sebf

คำตอบ:


10

คำถามของฉันคือถูกไหม นี่เป็นรูปแบบที่ผู้คนใช้ในการพัฒนาเกมหรือไม่?

ผู้พัฒนาเกมมักจะใช้งานอะไรก็ได้ มันไม่ได้เข้มงวด แต่เฮ้มันจัดส่ง

มันขัดแย้งกับทุกสิ่งที่ฉันคุ้นเคยเพื่อให้ชั้นบอลทำทุกอย่าง

ดังนั้นสำนวนทั่วไปที่มากขึ้นสำหรับสิ่งที่คุณมีคือ handleMessages / update / draw ในระบบที่ใช้ข้อความเป็นจำนวนมาก (ซึ่งมีทั้งข้อดีและข้อเสียตามที่คุณคาดไว้) เอนทิตีเกมจะคว้าข้อความอะไรก็ตามที่ข้อความนั้นสนใจทำตรรกะในข้อความเหล่านั้นแล้วดึงตัวเองออกมา

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

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

นอกจากนี้ในตัวอย่างที่สองนี้ที่ลูกของฉันรู้วิธีเคลื่อนที่ไปรอบ ๆ ฉันจะจัดการการตรวจจับการชนด้วย Paddle ได้อย่างไร บอลจะต้องมีความรู้เกี่ยวกับไม้พายหรือไม่?

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

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

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

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

ดูด้านบนสำหรับการสำรวจนี้ หากคุณอยู่ในภาษาที่รองรับอินเทอร์เฟซได้ง่าย (เช่น Java, C #) นี่เป็นเรื่องง่าย หากคุณอยู่ใน C ++ สิ่งนี้อาจเป็น ... น่าสนใจ ฉันชอบที่จะใช้การส่งข้อความเพื่อแก้ปัญหาเหล

อีกครั้งสิ่งที่ใช้งานได้และง่ายที่สุดสำหรับคุณ


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

5

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

คุณควรอ่านสิ่งนี้:

http://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/

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

การทำซ้ำของฉันเป็นดังนี้:

การวนซ้ำครั้งแรกมีวัตถุ "EntitySystem" ซึ่งเหมือนกับอดัมอธิบาย อย่างไรก็ตามองค์ประกอบของฉันยังคงมีวิธีการ - องค์ประกอบ 'ที่แสดงผลได้' ของฉันมีวิธีการทาสี () และองค์ประกอบตำแหน่งของฉันมีวิธีการเคลื่อนไหว () และอื่น ๆ เมื่อฉันเริ่มเนื้อความเอนทิตีฉันรู้ว่าฉันต้องเริ่มส่งข้อความระหว่าง ส่วนประกอบและสั่งการดำเนินการของการปรับปรุงส่วนประกอบ .... ทางยุ่งเกินไป

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

ต่อไปสำหรับการทำซ้ำ # 2 นี่คือสิ่งที่ฉันรวบรวมจากบล็อก:

EntityManager - ทำหน้าที่เป็นส่วนประกอบ "ฐานข้อมูล" ซึ่งสามารถสอบถามได้สำหรับเอนทิตีที่มีส่วนประกอบบางประเภท สิ่งนี้สามารถสำรองข้อมูลได้จากฐานข้อมูลในหน่วยความจำเพื่อการเข้าถึงที่รวดเร็ว ... ดูที่ส่วนเครื่อง 5 สำหรับข้อมูลเพิ่มเติม

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

เอนทิตี - เพียงแค่รหัสเช่นยาว เอนทิตีคือกลุ่มอินสแตนซ์ของกลุ่มส่วนประกอบเข้าด้วยกันเป็น 'เอนทิตี'

ส่วนประกอบ - ชุดของฟิลด์ .... ไม่มีพฤติกรรม! เมื่อคุณเริ่มเพิ่มพฤติกรรมมันจะเริ่มยุ่ง ... แม้ในเกม Space Invadors ง่ายๆ

แก้ไข : โดยวิธีการ 'dt' เป็นเวลาของเดลต้านับตั้งแต่การร้องขอลูปหลักล่าสุด

ดังนั้นห่วง Invadors หลักของฉันคือ:

Collection<Entity> entitiesWithGuns = manager.getEntitiesWith(Gun.class);
Collection<Entity> entitiesWithDamagable =
manager.getEntitiesWith(BulletDamagable.class);
Collection<Entity> entitiesWithInvadorDamagable = manager.getEntitiesWith(InvadorDamagable.class);

keyboardShipControllerSystem.update(entitiesWithGuns, dt);
touchInputSystem.update(entitiesWithGuns, dt);
Collection<Entity> entitiesWithInvadorMovement = manager.getEntitiesWith(InvadorMovement.class);
invadorMovementSystem.update(entitiesWithInvadorMovement);

Collection<Entity> entitiesWithVelocity = manager.getEntitiesWith(Velocity.class);
movementSystem.move(entitiesWithVelocity, dt);
gunSystem.update(entitiesWithGuns, System.currentTimeMillis());
Collection<Entity> entitiesWithPositionAndForm = manager.getEntitiesWith(Position.class, Form.class);
collisionSystem.checkCollisions(entitiesWithPositionAndForm);

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

ฉันชอบคุณ; ฉันเป็นนักพัฒนาที่มีประสบการณ์ แต่ไม่มีประสบการณ์ในการเขียนเกม ฉันใช้เวลาค้นคว้ารูปแบบของ dev และอันนี้จับตาฉัน มันไม่ใช่วิธีเดียวที่จะทำสิ่งต่าง ๆ แต่ฉันพบว่ามันใช้งานง่ายและแข็งแกร่ง ผมเชื่อว่ารูปแบบที่ถูกกล่าวถึงอย่างเป็นทางการในหนังสือเล่ม 6 ของชุด "อัญมณีเขียนโปรแกรมเกม" - http://www.amazon.com/Game-Programming-Gems/dp/1584500492 ฉันไม่ได้อ่านหนังสือด้วยตัวเอง แต่ฉันได้ยินมาว่าพวกเขาเป็นข้อมูลอ้างอิงจริงสำหรับการเขียนโปรแกรมเกม


1

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

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

แต่ถ้าคุณมาจากโลกธุรกิจคุณอาจรู้สึกสบายใจในการเขียนชั้นเรียนที่รู้วิธีการทำทุกอย่างเพื่อตัวเอง

ฉันขอแนะนำให้คุณเขียนสิ่งที่คุณรู้สึกว่าเป็นธรรมชาติที่สุดสำหรับคุณ


ฉันคิดว่าคุณเข้าใจคำถามของฉันผิด ฉันกำลังบอกว่าฉันไม่ชอบเรียนที่ทำทุกอย่างด้วยตัวเอง ฉันตกหลุมเหมือนบอลก็ควรจะเป็นตัวแทนตรรกะของลูก แต่มันควรจะรู้อะไรเกี่ยวกับห่วงเกม, การจัดการการป้อนข้อมูล ฯลฯ ...
BFree

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

1

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

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

คนส่วนใหญ่ใช้คลาสอินพุตที่ผลลัพธ์พร้อมใช้งานสำหรับวัตถุเกมทั้งหมดผ่านบริการหรือคลาสคงที่


0

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

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

นี่เป็นเพียงการเก็งกำไรของฉันตั้งแต่ฉันเป็นนักพัฒนาเกมตะกาย แต่เป็นนักพัฒนามืออาชีพ


0

ไม่ไม่ใช่ 'ถูกต้อง' หรือ 'ผิด' เป็นเพียงการทำให้เข้าใจง่าย

รหัสธุรกิจบางรหัสเหมือนกันทุกประการยกเว้นว่าคุณกำลังทำงานกับระบบที่บังคับให้แยกงานนำเสนอและตรรกะออก

นี่คือตัวอย่าง VB.NET ที่ "ตรรกะทางธุรกิจ" (เพิ่มตัวเลข) ถูกเขียนในตัวจัดการเหตุการณ์ GUI - http://www.homeandlearn.co.uk/net/nets1p12.html

ฉันจะจัดการกับความซับซ้อนของส่วนประกอบต่าง ๆ ได้อย่างไรหากแต่ละองค์ประกอบทำทุกอย่างด้วยตนเอง

ถ้าและเมื่อมันได้รับความซับซ้อนนั้นคุณสามารถแยกมันออกมา

class Ball
{
    GraphicalModel ballVisual;
    void Draw()
    {
        ballVisual.Draw();
    }
};

0

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

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