จะหลีกเลี่ยงการพึ่งพาแบบวงกลมระหว่างผู้เล่นและโลกได้อย่างไร


60

ฉันกำลังทำงานกับเกม 2D ที่คุณสามารถเลื่อนขึ้นลงซ้ายและขวา ฉันมีวัตถุตรรกะของเกมเป็นหลักสองประการ:

  • ผู้เล่น:มีตำแหน่งที่สัมพันธ์กับโลก
  • โลก:วาดแผนที่และผู้เล่น

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

ตอนนี้ฉันต้องการเพิ่มการตรวจจับการชนเพื่อให้ผู้เล่นไม่สามารถเคลื่อนที่ผ่านกำแพงได้

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

ตัวเลือกที่ดีที่สุดของฉันคืออะไร? หรือหลีกเลี่ยงการพึ่งพาแบบวงกลมไม่คุ้ม


4
ทำไมคุณคิดว่าการพึ่งพาแบบวนเป็นสิ่งที่ไม่ดี? stackoverflow.com/questions/1897537/…
Fuhrmanator

@Fuhrmanator ฉันไม่คิดว่าพวกเขาโดยทั่วไปจะเป็นสิ่งที่ไม่ดี แต่ฉันต้องทำให้สิ่งต่าง ๆ ซับซ้อนขึ้นในรหัสเพื่อแนะนำ
futlib

ฉันบ้าโพสต์เกี่ยวกับการสนทนาเล็กน้อยของเราไม่มีอะไรใหม่แม้ว่า: yannbane.com/2012/11/… ...
jcora

คำตอบ:


61

โลกไม่ควรวาดเอง Renderer ควรวาดโลก ผู้เล่นไม่ควรดึงตัวเอง Renderer ควรดึงดูดผู้เล่นที่สัมพันธ์กับโลก

ผู้เล่นควรถามโลกเกี่ยวกับการตรวจจับการชนกัน; หรือบางทีการชนควรได้รับการจัดการโดยชั้นเรียนที่แยกต่างหากซึ่งจะตรวจสอบการตรวจจับการชนไม่เพียง แต่กับโลกคงที่ แต่ยังรวมถึงนักแสดงอื่น ๆ ด้วย

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


25
@ snake5 - มีความแตกต่างระหว่าง "สามารถ" และ "ควร" สิ่งใดสามารถวาดอะไรก็ได้ - แต่เมื่อคุณต้องการเปลี่ยนรหัสที่เกี่ยวกับรูปวาดมันง่ายกว่ามากที่จะไปที่คลาส "Renderer" แทนที่จะค้นหา "Anything" ที่กำลังวาด "obsessing on compartmentalization" เป็นอีกคำสำหรับ "cohesion"
เนท

16
@ Mr.Beast ไม่เขาไม่ใช่ เขาสนับสนุนการออกแบบที่ดี การยัดทุกอย่างในความผิดพลาดอันหนึ่งของชั้นเรียนไม่สมเหตุสมผล
jcora

23
โอ้โหฉันไม่คิดว่ามันจะจุดประกายปฏิกิริยาดังกล่าว :) ฉันไม่มีอะไรจะเพิ่มเติมไปยังคำตอบ แต่ฉันสามารถอธิบายได้ว่าทำไมฉันถึงได้ให้ - เพราะฉันคิดว่ามันง่ายกว่า ไม่ 'เหมาะสม' หรือ 'ถูกต้อง' ฉันไม่ต้องการให้มันเป็นอย่างนั้น มันง่ายกว่าสำหรับฉันเพราะถ้าฉันพบว่าตัวเองเข้าเรียนคลาสที่มีความรับผิดชอบมากเกินไปการแบ่งจะเร็วกว่าการบังคับให้โค้ดที่มีอยู่อ่านได้ ฉันชอบโค้ดในกลุ่มที่ฉันสามารถเข้าใจได้และ refactor ในการตอบสนองต่อปัญหาเช่น @futlib กำลังประสบอยู่
Liosan

12
@ snake5 การพูดว่าการเพิ่มคลาสให้มากขึ้นจะเพิ่มโอเวอร์เฮดให้กับโปรแกรมเมอร์มักจะผิดอย่างสิ้นเชิงในประสบการณ์ของฉัน ในความเห็นของฉันคลาสบรรทัด 10x100 ที่มีชื่อที่ให้ข้อมูลและความรับผิดชอบที่กำหนดไว้นั้นง่ายต่อการอ่านและค่าใช้จ่ายที่น้อยกว่าสำหรับโปรแกรมเมอร์มากกว่าคลาสพระเจ้า 1,000 บรรทัด
Martin

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

35

นี่คือวิธีที่เอ็นจินการเรนเดอร์ทั่วไปจัดการกับสิ่งเหล่านี้:

มีความแตกต่างพื้นฐานระหว่างที่วัตถุอยู่ในอวกาศและวิธีการวาดวัตถุ

  1. การวาดวัตถุ

    โดยทั่วไปคุณมีคลาสRendererที่ทำสิ่งนี้ มันใช้เวลาเพียงวัตถุ (รุ่น) และวาดบนหน้าจอ มันสามารถมีวิธีต่างๆเช่น drawSprite (Sprite), drawLine (.. ), drawModel (Model) ทุกสิ่งที่คุณรู้สึกว่าต้องการ มันคือ Renderer ดังนั้นมันควรจะทำสิ่งเหล่านี้ทั้งหมด นอกจากนี้ยังใช้ API ใด ๆ ที่คุณมีอยู่ด้านล่างเพื่อให้คุณสามารถมีอินสแตนซ์ของ renderer ที่ใช้ OpenGL และอันที่ใช้ DirectX หากคุณต้องการพอร์ตเกมของคุณไปยังแพลตฟอร์มอื่นคุณเพียงแค่เขียน renderer ใหม่และใช้อันนั้น มันเป็น "ที่" ง่าย

  2. การเคลื่อนย้ายวัตถุ

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

  3. การจัดการวัตถุ

    SceneNodes มีการจัดการอย่างไร? ผ่านSceneManager คลาสนี้สร้างและติดตามทุก ๆ SceneNode ในฉากของคุณ คุณสามารถขอมันสำหรับ SceneNode เฉพาะ (โดยปกติจะระบุด้วยชื่อสตริงเช่น "Player" หรือ "Table") หรือรายการของโหนดทั้งหมด

  4. วาดรูปโลก

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

  5. ตรวจจับการชนกัน

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

ดังนั้นผู้เล่นคืออะไรและโลกคืออะไร?

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

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


+1 - ฉันพบว่าตัวเองกำลังสร้างระบบเกมของฉันแบบนี้และพบว่ามันมีความยืดหยุ่น
Cypher

+1, คำตอบที่ดี เป็นรูปธรรมมากขึ้นและตรงประเด็นกว่าของฉันเอง
jcora

+1 ฉันได้เรียนรู้มากมายจากคำตอบนี้และมันก็จบลงด้วยการดลใจ ขอบคุณ @rootlocus
joslinm

16

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

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

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

World::checkForCollisions()
{
  [...]
  foreach(entityA in entityList)
    foreach(entityB in entityList)
      if([... entityA and entityB have collided ...])
         entityA.onCollision(entityB);
}

Player::onCollision(other)
{
  [... react on the collision ...]
}

โลกของฟิสิกส์ที่คุณอธิบายไว้ในคำถามนั้นสามารถจัดการได้หากคุณเปิดเผยความเร็วของเอนทิตี:

World::calculatePhysics()
{ 
  foreach(entityA in entityList)
    foreach(entityB in entityList)
    {
      [... move entityA according to its velocity as far as possible ...]
      if([... entityA has collided with the world ...])
         entityA.onWorldCollision();
      [... calculate the movement of entityB in order to know if A has collided with B ...]
      if([... entityA and entityB have collided ...])
         entityA.onCollision(entityB);
    }
}

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


4
+1 การพึ่งพาหนังสือเวียนไม่ใช่ปัญหาที่นี่จริงๆ ในขั้นตอนนี้ไม่มีเหตุผลที่จะต้องกังวลเกี่ยวกับเรื่องนี้ หากเกมเติบโตและรหัสเติบโตขึ้นมันอาจจะเป็นความคิดที่ดีที่จะปรับโครงสร้างคลาส Player และ World เหล่านั้นในคลาสย่อยอย่างไรก็ตามมีระบบที่ใช้ส่วนประกอบที่เหมาะสมคลาสสำหรับการจัดการอินพุตอาจเป็น Rendered เป็นต้น แต่สำหรับ การเริ่มต้นไม่มีปัญหา
Laurent Couvidou

4
-1 นั่นไม่ใช่เหตุผลเดียวที่จะไม่แนะนำการพึ่งพาแบบวงกลม คุณจะทำให้ระบบของคุณขยายและเปลี่ยนแปลงได้ง่ายขึ้น
jcora

4
@Bane คุณไม่สามารถเขียนโค้ดอะไรได้เลยหากไม่มีกาวนั้น ความแตกต่างคือคุณเพิ่มทางอ้อมมากแค่ไหน หากคุณมีเกมคลาส -> โลก -> เอนทิตีหรือถ้าคุณมีเกมคลาส -> โลก, SoundManager, InputManager, PhysicsEngine, ComponentManager มันทำให้สิ่งต่าง ๆ สามารถอ่านได้น้อยลงเนื่องจากค่าใช้จ่าย (วากยสัมพันธ์) ทั้งหมดและความซับซ้อนโดยนัยนั้น และเมื่อถึงจุดหนึ่งคุณจะต้องมีองค์ประกอบในการโต้ตอบซึ่งกันและกัน และนั่นคือจุดที่ชั้นกาวหนึ่งชั้นทำสิ่งต่าง ๆ ได้ง่ายกว่าทุกสิ่งที่แบ่งระหว่างชั้นเรียนหลายชั้น
API-Beast

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

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

13

การออกแบบในปัจจุบันของคุณดูเหมือนว่าจะไปกับหลักการแรกของการออกแบบที่เป็นของแข็ง

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

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

จะเกิดอะไรขึ้นหากรหัสการแสดงผลของคุณเปลี่ยนไป / ต้องเปลี่ยน เหตุใดคุณจึงต้องอัปเดตทั้งสองคลาสที่จริงแล้วไม่มีอะไรเกี่ยวข้องกับการเรนเดอร์? ในฐานะที่เป็น Liosan Rendererได้กล่าวแล้วคุณควรจะมี


ตอนนี้เพื่อตอบคำถามจริงของคุณ ...

มีหลายวิธีในการทำเช่นนี้และนี่เป็นวิธีเดียวในการแยกชิ้นส่วน:

  1. โลกไม่รู้ว่าผู้เล่นคืออะไร
    • มันมีรายชื่อของObjectผู้เล่นที่ตั้งอยู่ แต่ไม่ได้ขึ้นอยู่กับระดับผู้เล่น (ใช้การสืบทอดเพื่อให้บรรลุสิ่งนี้)
  2. ผู้เล่นบางคนInputManagerได้รับการอัพเดต
  3. โลกนี้จัดการการตรวจจับการเคลื่อนไหวและการชนใช้การเปลี่ยนแปลงทางกายภาพที่เหมาะสมและส่งการอัปเดตไปยังวัตถุ
    • ตัวอย่างเช่นหากวัตถุ A และวัตถุ B ชนกันโลกจะแจ้งให้ทราบแล้วพวกเขาก็สามารถจัดการได้ด้วยตนเอง
    • โลกจะยังคงจัดการกับฟิสิกส์ (ถ้าการออกแบบของคุณเป็นเช่นนั้น)
    • จากนั้นวัตถุทั้งสองจะเห็นว่าการชนกันนั้นน่าสนใจหรือไม่ เช่นถ้าวัตถุ A เป็นผู้เล่นและวัตถุ B เป็นรูปเข็มผู้เล่นก็สามารถสร้างความเสียหายได้
    • แม้ว่าจะสามารถแก้ไขได้ด้วยวิธีอื่น
  4. Rendererดึงวัตถุทั้งหมด

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

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

อ้าคุณหมายถึงว่าโลกไม่มีการอ้างอิงกับผู้เล่นมันมีวัตถุมากมายที่ใช้อินเทอร์เฟซ ICollidable ร่วมกับผู้เล่นหากจำเป็น
Markus von Broady

2
+1 คำตอบที่ดี แต่: "โปรดอย่าสนใจทุกคนที่พูดว่าการออกแบบซอฟต์แวร์ที่ดีนั้นไม่สำคัญ" ร่วมกัน ไม่มีใครพูดว่า
Laurent Couvidou

2
แก้ไข! มันไม่ดูเหมือนชนิดของที่ไม่จำเป็นอยู่แล้ว ...
jcora

1

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

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


1

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

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

ดังนั้นคุณไม่สามารถหลีกเลี่ยง "ปัญหา" นี้ได้และฉันคิดว่าไม่จำเป็นต้องกังวลเกี่ยวกับสิ่งนั้น ออกแบบให้โง่ง่าย ๆ ตราบเท่าที่คุณทำได้


0

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

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

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

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

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


0

อย่างที่คนอื่น ๆ พูดฉันคิดว่าคุณWorldกำลังทำสิ่งหนึ่งมากเกินไป: ทั้งสองกำลังพยายามบรรจุเกมMap(ซึ่งควรเป็นเอนทิตีที่แตกต่างกัน) และเป็นRendererเวลาเดียวกัน

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

แล้วคุณยังจำเป็นต้องมีRendererวัตถุ คุณสามารถทำให้Rendererวัตถุนี้เป็นสิ่งที่ทั้งสองมี GameMapและPlayer(รวมถึงEnemies) และดึงพวกมันออกมา


-6

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

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


1
ฉันอยู่กับคุณ OOP เกินไปแล้ว บทเรียนและการศึกษากระโดดได้อย่างรวดเร็วไปยัง OO หลังจากการเรียนรู้สิ่งที่ควบคุมการไหลพื้นฐาน โดยทั่วไปโปรแกรม OO จะช้ากว่ารหัสขั้นตอนเนื่องจากมีระบบราชการระหว่างวัตถุของคุณคุณมีการเข้าถึงตัวชี้จำนวนมากซึ่งทำให้แคชหายไป เกมของคุณใช้งานได้ช้ามาก เกมที่แท้จริงรวดเร็วและมีคุณสมบัติสูงโดยใช้อาร์เรย์ระดับโลกแบบธรรมดาและปรับฟังก์ชั่นที่ปรับแต่งแล้วด้วยมือเพื่อทุกสิ่งเพื่อหลีกเลี่ยงการพลาดแคช ซึ่งอาจส่งผลให้ประสิทธิภาพเพิ่มขึ้นเป็นสิบเท่า
Calmarius
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.