มีกรณีที่ globals / singletons มีประโยชน์ในการพัฒนาเกมหรือไม่? [ปิด]


11

ฉันรู้ว่าการมีตัวแปรส่วนกลางหรือคลาสเดี่ยวสร้างกรณีที่ยากต่อการทดสอบ / จัดการและฉันถูกจับในการใช้รูปแบบเหล่านั้นในรหัส แต่บ่อยครั้งที่คุณต้องจัดส่ง

มีกรณีที่ตัวแปรระดับโลกหรือซิงเกิลตันมีประโยชน์จริง ๆ ในการพัฒนาเกมหรือไม่?

คำตอบ:


30

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


ใกล้กับมนต์ของฉัน
Ólafur Waage

15

รายการที่ไม่ครบถ้วน แต่แน่นอนไปที่:

จุดด้อย

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

ทั้งโปรหรือต่อต้าน

  • เห็นพ้องด้วย ในสภาพแวดล้อมที่เกิดขึ้นพร้อมกันซิงเกิลตันอาจเป็นความเจ็บปวด (คุณต้องพิจารณาปัญหาการแข่งขันด้านข้อมูล / การกลับมาใหม่) หรือการให้พร (ง่ายต่อการรวมศูนย์และเหตุผลเกี่ยวกับการล็อคและการจัดการทรัพยากร) การใช้สิ่งต่าง ๆ อย่างชาญฉลาดเช่นที่จัดเก็บในตัวเครื่องสามารถลดปัญหาที่อาจเกิดขึ้นได้บ้าง แต่โดยทั่วไปไม่ใช่ปัญหาง่าย
  • Codegen (ขึ้นอยู่กับคอมไพเลอร์สถาปัตยกรรมและอื่น ๆ ): สำหรับการใช้งานแบบซิงเกิลที่ไร้เดียงสาต่อการใช้งานครั้งแรกคุณอาจประเมินสาขาที่มีเงื่อนไขเพิ่มเติมสำหรับการเข้าถึงแต่ละครั้ง (ซึ่งสามารถเพิ่มขึ้น.) Globals หลายฟังก์ชั่นที่ใช้ในการอาจบวมสระว่ายน้ำที่แท้จริง วิธีการ "โครงสร้างของกลมทั้งหมด" อาจประหยัดพื้นที่ในสระตัวอักษร: รายการเดียวไปยังฐานของโครงสร้างและจากนั้นชดเชยการเข้ารหัสในคำแนะนำการโหลด ในที่สุดหากคุณหลีกเลี่ยง globals และ singletons ที่ค่าใช้จ่ายทั้งหมดโดยทั่วไปคุณต้องใช้หน่วยความจำเพิ่มเติมอย่างน้อย (การลงทะเบียน stack หรือ heap) เพื่อส่งผ่านพอยน์เตอร์การอ้างอิงหรือแม้แต่การคัดลอก

ข้อดี

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

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

หากคุณรักษา API ของคุณให้สะอาดอยู่เสมอมันอาจไม่ยากที่จะปรับโครงสร้างออกจาก (หรือเป็น!) รูปแบบซิงเกิลเมื่อคุณต้องการ ...


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

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

4

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


2

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

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


2

ปัญหาหลักที่เกิดขึ้นกับ globals และการใช้งานซิงเกิลตันที่ไม่ดีคือข้อผิดพลาดในการก่อสร้างและการสร้างโครงสร้างที่ไม่ชัดเจน

ดังนั้นหากคุณทำงานกับ primitives ที่ไม่มีปัญหาเหล่านี้หรือทราบปัญหาของตัวชี้อย่างมาก จากนั้นสามารถใช้งานได้อย่างปลอดภัย

ลูกโลกมีสถานที่เช่นเดียวกับ gotos และไม่ควรออกจากมือ แต่ควรใช้ด้วยความระมัดระวัง

มีคำอธิบายที่ดีในคู่มือสไตล์ Google C ++


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

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

1

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

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


1

ฉันมักจะแนะนำให้ใช้คอนเทนเนอร์ DI / IoC บางประเภทกับการจัดการอายุการใช้งานที่กำหนดเองแทน singletons (แม้ว่าคุณจะใช้ตัวจัดการอายุการใช้งาน "อินสแตนซ์เดียว") อย่างน้อยก็ง่ายที่จะสลับการใช้งานเพื่ออำนวยความสะดวกในการทดสอบ


สำหรับบรรดาของคุณที่ไม่ทราบ DI คือ "การพึ่งพาการฉีด" และ IoC คือ "การผกผันของการควบคุม" Link: martinfowler.com/articles/inject.html (ฉันเคยอ่านเกี่ยวกับสิ่งเหล่านี้มาก่อน แต่ไม่เคยเห็นคำย่อดังนั้นฉันจึงต้องค้นหาหน่อย) +1 เพื่อพูดถึงการเปลี่ยนแปลงที่ยอดเยี่ยมนี้
อ่าน

0

หากคุณต้องการคุณสมบัติการประหยัดหน่วยความจำของซิงเกิลตันคุณอาจลองใช้รูปแบบการออกแบบฟลายเวท?

http://en.wikipedia.org/wiki/Flyweight_pattern

เท่าที่มีปัญหาหลายเธรดที่กล่าวถึงข้างต้นมันควรจะค่อนข้างง่ายด้วยความสุขุมในการใช้กลไกการล็อคสำหรับทรัพยากรที่อาจถูกแชร์ระหว่างเธรด http://en.wikipedia.org/wiki/Read/write_lock_pattern


0

Singletons เป็นวิธีที่ยอดเยี่ยมในการจัดเก็บสถานะที่ใช้ร่วมกันในแบบตัวอย่างต้น

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

ตัวอย่างเช่นใน iOS คุณใช้ singletons เพื่อรับ [UIApplication sharedApplication] ใน cocos2d คุณสามารถใช้มันเพื่อรับการอ้างอิงถึงวัตถุบางอย่างเช่น [CCNotifications sharedManager] และโดยส่วนตัวแล้วฉันมักจะเริ่มต้นด้วย [Game sharedGame] singleton ที่ฉันสามารถทำได้ สถานะร้านค้าที่แชร์ระหว่างองค์ประกอบที่แตกต่างกันมากมาย


0

ว้าวนี่น่าสนใจสำหรับฉันเพราะฉันไม่เคยมีปัญหากับรูปแบบซิงเกิล โปรเจ็กต์ปัจจุบันของฉันคือเอ็นจิ้นเกม C ++ สำหรับ Nintendo DS และฉันใช้เครื่องมือการเข้าถึงฮาร์ดแวร์มากมาย (เช่น VRAM Banks, Wifi, เอ็นจิ้นกราฟิกสองตัว) เป็นอินสแตนซ์เดี่ยวเพราะพวกมันตั้งใจจะห่อหุ้ม C ทั่วโลก ฟังก์ชั่นในห้องสมุดต้นแบบ


คำถามของฉันจะเป็นเช่นนั้นทำไมต้องใช้ซิงเกิลตันทั้งหมด ฉันเห็นว่าคนชอบห่อ API ด้วยซิงเกิลตันหรือคลาสที่มีเพียงฟังก์ชั่นสแตติก แต่ทำไมต้องรำคาญกับคลาสเลย? ดูเหมือนว่าฉันจะยิ่งง่ายกว่าที่จะเพียงแค่เรียกฟังก์ชั่น (ห่อตามที่คุณต้องการ) แต่จากนั้นภายในพวกเขาเข้าถึงสถานะ "ทั่วโลก" เช่นบันทึก :: GetInstance () -> LogError (... ) อาจเป็น LogError (... ) ซึ่งเข้าถึงสถานะภายในใด ๆ ภายในโดยไม่ต้องใช้รหัสไคลเอนต์เพื่อทราบเกี่ยวกับมัน
dash-tom-bang

0

เฉพาะเมื่อคุณมีรายการที่มีตัวควบคุมเพียงตัวเดียว แต่ถูกจัดการโดยโมดูลหลายตัว

เช่นอินเทอร์เฟซเมาส์ หรืออินเตอร์เฟซจอยสติ๊ก หรือเครื่องเล่นเพลง หรือเครื่องเล่นเสียง หรือหน้าจอ หรือผู้จัดการไฟล์บันทึก


-1

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

Singletons เป็น IMO ระดับโลกที่ดีกว่าและเป็นเครื่องมือที่เหมาะสม

ใช้เท่าที่จำเป็น!


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

เร็วกว่า getters และ setters และส่งผ่านการอ้างอิงไปรอบ ๆ แต่ไม่มาก มันช่วยลดขนาดลงดังนั้นมันจะช่วยในบางระบบ ฉันอาจพูดเกินจริงเมื่อฉันพูดว่า 'มาก' แต่ฉันไม่เชื่อใครพูดว่าคุณไม่ควรใช้อะไร คุณเพียงแค่ต้องใช้สามัญสำนึกของคุณ ท้ายที่สุดแล้วซิงเกิลตันไม่ได้เป็นอะไรมากไปกว่าสมาชิกของคลาสแบบสแตติก
jacmoe

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