ในความคิดเห็นฉันโพสต์คำตอบแบบเก่าของฉันแบบย่อคำตอบ:
การใช้DrawableGameComponentล็อคคุณในลายเซ็นวิธีการชุดเดียวและรูปแบบข้อมูลที่เก็บไว้เฉพาะ โดยทั่วไปแล้วสิ่งเหล่านี้จะเป็นตัวเลือกที่ผิดในตอนแรกและการล็อคอินจะขัดขวางวิวัฒนาการของรหัสในอนาคต
ที่นี่ฉันจะพยายามแสดงให้เห็นว่าทำไมเป็นกรณีนี้โดยการโพสต์รหัสบางส่วนจากโครงการปัจจุบันของฉัน
วัตถุรากรักษาสถานะของโลกของเกมมีUpdateวิธีการดังนี้:
void Update(UpdateContext updateContext, MultiInputState currentInput)
มีจุดเข้าใช้งานจำนวนมากUpdateขึ้นอยู่กับว่าเกมนั้นรันบนเครือข่ายหรือไม่ เป็นไปได้เช่นสำหรับสถานะเกมที่จะย้อนกลับไปยังจุดก่อนหน้านี้ในเวลาและจากนั้นจำลองอีกครั้ง
นอกจากนี้ยังมีวิธีการปรับปรุงเพิ่มเติมเช่น:
void PlayerJoin(int playerIndex, string playerName, UpdateContext updateContext)
ภายในสถานะเกมจะมีรายชื่อของนักแสดงที่จะได้รับการอัพเดต แต่นี่เป็นอะไรที่ง่ายมากกว่าUpdateโทรมีการผ่านเพิ่มเติมก่อนและหลังเพื่อจัดการกับฟิสิกส์ นอกจากนี้ยังมีสิ่งที่แปลกใหม่สำหรับการวางไข่ / ทำลายนักแสดงเช่นเดียวกับการเปลี่ยนระดับ
นอกเหนือจากเกมแล้วยังมีระบบ UI ที่ต้องจัดการแยกต่างหาก แต่บางหน้าจอใน UI จำเป็นต้องสามารถทำงานในบริบทเครือข่ายได้เช่นกัน
สำหรับการวาดภาพเนื่องจากลักษณะของเกมมีระบบการเรียงและกำหนดเองที่รับอินพุตจากวิธีการเหล่านี้:
virtual void RegisterToDraw(SortedDrawList sortedDrawList, Definitions definitions)
virtual void RegisterShadow(ShadowCasterList shadowCasterList, Definitions definitions)
จากนั้นเมื่อมันรู้ว่าจะวาดอะไรโทรออกโดยอัตโนมัติ:
virtual void Draw(DrawContext drawContext, int tag)
tagพารามิเตอร์เป็นสิ่งจำเป็นเพราะนักแสดงบางคนสามารถถือไปยังนักแสดงอื่น ๆ - และจึงไม่จำเป็นต้องสามารถที่จะวาดทั้งด้านบนและด้านล่างของนักแสดงที่จัดขึ้น ระบบการเรียงลำดับช่วยให้มั่นใจว่าสิ่งนี้เกิดขึ้นในลำดับที่ถูกต้อง
นอกจากนี้ยังมีระบบเมนูในการวาด และระบบลำดับชั้นสำหรับการวาด UI รอบ ๆ HUD รอบ ๆ เกม
ตอนนี้เปรียบเทียบความต้องการเหล่านั้นกับสิ่งที่DrawableGameComponentให้:
public class DrawableGameComponent
{
public virtual void Update(GameTime gameTime);
public virtual void Draw(GameTime gameTime);
public int UpdateOrder { get; set; }
public int DrawOrder { get; set; }
}
ไม่มีวิธีที่สมเหตุสมผลในการรับจากระบบที่ใช้DrawableGameComponentไปยังระบบที่ฉันอธิบายไว้ข้างต้น (และสิ่งเหล่านี้ไม่ใช่ข้อกำหนดที่ผิดปกติสำหรับเกมที่มีความซับซ้อนเพียงเล็กน้อย)
นอกจากนี้ยังเป็นสิ่งสำคัญที่จะต้องทราบว่าข้อกำหนดเหล่านั้นไม่ได้เกิดขึ้นตั้งแต่เริ่มโครงการ พวกเขาพัฒนาในช่วงหลายเดือนของการพัฒนา
สิ่งที่ผู้คนมักทำถ้าเริ่มDrawableGameComponentเส้นทางคือพวกเขาเริ่มสร้างโดยใช้แฮ็ก:
แทนที่จะเพิ่มวิธีการพวกเขาลงเอยที่น่าเกลียดในประเภทฐานของตัวเอง (ทำไมไม่ใช้เพียงแค่นั้นโดยตรง)
แทนที่จะเปลี่ยนรหัสสำหรับการเรียงลำดับและเรียกใช้Draw/ Updateพวกเขาทำสิ่งที่ช้ามากในการเปลี่ยนหมายเลขการสั่งซื้อแบบทันที
และที่เลวร้ายที่สุดของทั้งหมด: แทนที่จะเพิ่มพารามิเตอร์ในเมธอดพวกเขาจะเริ่มต้น "ผ่าน" "พารามิเตอร์" ในตำแหน่งหน่วยความจำที่ไม่ใช่สแต็ก (การใช้Servicesสิ่งนี้เป็นที่นิยม) สิ่งนี้ซับซ้อนสับสนผิดพลาดช้าเปราะบางไม่ค่อยปลอดภัยต่อเธรดและมีแนวโน้มที่จะเป็นปัญหาหากคุณเพิ่มเครือข่ายสร้างเครื่องมือภายนอกและอื่น ๆ
นอกจากนี้ยังทำให้การใช้รหัสซ้ำยากขึ้นเพราะตอนนี้การพึ่งพาของคุณถูกซ่อนอยู่ภายใน "ส่วนประกอบ" แทนที่จะเผยแพร่ที่ขอบเขต API
หรือพวกเขาเกือบจะเห็นว่ามันบ้าไปหมดแล้วและเริ่มทำ "ผู้จัดการ" DrawableGameComponentเพื่อแก้ไขปัญหาเหล่านี้ ยกเว้นพวกเขายังมีปัญหาเหล่านี้ที่ขอบเขต "ผู้จัดการ"
"ผู้จัดการ" เหล่านี้สามารถถูกแทนที่อย่างง่ายดายด้วยชุดของการเรียกใช้เมธอดตามลำดับที่ต้องการ สิ่งอื่นใดที่ซับซ้อนเกินไป
แน่นอนว่าเมื่อคุณเริ่มใช้แฮ็กเหล่านั้นมันจะต้องใช้งานจริงเพื่อยกเลิกแฮ็กเหล่านั้นเมื่อคุณรู้ว่าคุณต้องการมากกว่าสิ่งที่DrawableGameComponentมีให้ คุณกลายเป็น " ล็อคใน " และการล่อลวงมักจะทำต่อเนื่องเพื่อการแฮ็ก - ซึ่งในทางปฏิบัติจะทำให้ยุ่งเหยิงคุณและ จำกัด ประเภทของเกมที่คุณสามารถทำได้สำหรับคนที่ค่อนข้างง่าย
ผู้คนจำนวนมากดูเหมือนจะมาถึงจุดนี้และมีปฏิกิริยาต่ออวัยวะภายใน - อาจเป็นเพราะพวกเขาใช้DrawableGameComponentและไม่ต้องการที่จะยอมรับว่า "ผิด" ดังนั้นพวกเขาจึงยึดถือความจริงที่ว่าอย่างน้อยก็เป็นไปได้ที่จะทำให้มันเป็น "งาน"
แน่นอนมันสามารถทำให้ "ทำงาน"! เราเป็นโปรแกรมเมอร์ - การทำสิ่งต่าง ๆ ให้ทำงานภายใต้ข้อ จำกัด ที่ผิดปกตินั้นเป็นหน้าที่ของเรา แต่ข้อ จำกัด นี้ไม่จำเป็นมีประโยชน์หรือมีเหตุผล ...
สิ่งที่ทำให้งุนงงเป็นพิเศษคือมันไม่น่าประหลาดใจที่จะก้าวไปข้างหน้าในประเด็นทั้งหมดนี้ ที่นี่ฉันจะให้รหัสแก่คุณด้วย:
public class Actor
{
public virtual void Update(GameTime gameTime);
public virtual void Draw(GameTime gameTime);
}
List<Actor> actors = new List<Actor>();
foreach(var actor in actors)
actor.Update(gameTime);
foreach(var actor in actors)
actor.Draw(gameTime);
(มันง่ายที่จะเพิ่มในการจัดเรียงตามDrawOrderและUpdateOrder. วิธีหนึ่งที่ง่ายคือการรักษาสองรายการและใช้List<T>.Sort(). นอกจากนี้ยังเป็นเรื่องเล็กน้อยที่จะจัดการLoad/ UnloadContent.)
ไม่สำคัญว่ารหัสนั้นคืออะไร สิ่งที่สำคัญคือฉันสามารถปรับเปลี่ยนได้เล็กน้อย
เมื่อฉันรู้ว่าฉันต้องการระบบจับเวลาที่แตกต่างกันgameTimeหรือฉันต้องการพารามิเตอร์เพิ่มเติม (หรือUpdateContextวัตถุทั้งหมดที่มีประมาณ 15 สิ่ง) หรือฉันต้องการลำดับการปฏิบัติการที่แตกต่างกันโดยสิ้นเชิงสำหรับการวาดรูปหรือฉันต้องการวิธีพิเศษบางอย่าง สำหรับการอัปเดตต่าง ๆ ฉันสามารถดำเนินการต่อได้
และฉันสามารถทำได้ทันที ฉันไม่จำเป็นต้องกังวลเกี่ยวกับการเลือกDrawableGameComponentรหัสของฉัน หรือแย่กว่านั้นคือ: แฮ็คมันด้วยวิธีใดวิธีหนึ่งที่จะทำให้มันยากขึ้นในครั้งต่อไปที่ความต้องการดังกล่าวเกิดขึ้น
จริงๆมีเพียงหนึ่งในสถานการณ์ที่มันเป็นทางเลือกที่ดีที่จะใช้DrawableGameComponent: และนั่นคือถ้าคุณมีอักษรทำและเผยแพร่การลากและวางองค์ประกอบสไตล์ตัวกลางสำหรับ XNA ซึ่งเป็นวัตถุประสงค์ดั้งเดิมของมัน อันไหน - ฉันจะเพิ่ม - ไม่มีใครทำ!
(ในทำนองเดียวกันมันสมเหตุสมผลดีที่จะใช้Servicesเมื่อสร้างประเภทเนื้อหาที่กำหนดเองContentManagerเท่านั้น)