นักแสดงในเกมควรมีความรับผิดชอบในการวาดภาพตนเองหรือไม่?


41

ฉันยังใหม่ต่อการพัฒนาเกม แต่ไม่ได้เขียนโปรแกรม

ฉัน (อีกครั้ง) เล่นกับเกมประเภท Pong โดยใช้canvasองค์ประกอบของ JavaScript

ฉันสร้างPaddleวัตถุที่มีคุณสมบัติดังต่อไปนี้ ...

  • width
  • height
  • x
  • y
  • colour

ฉันยังมีPongวัตถุที่มีคุณสมบัติเช่น ...

  • width
  • height
  • backgroundColour
  • draw().

draw()วิธีการในปัจจุบันคือการรีเซ็ตcanvasและที่เป็นที่คำถามขึ้นมา

ควรPaddleวัตถุมีdraw()วิธีการรับผิดชอบสำหรับการวาดภาพหรือควรdraw()ของPongวัตถุต้องรับผิดชอบสำหรับการวาดภาพของนักแสดง (ฉันคิดว่าเป็นคำที่ถูกต้องโปรดแก้ไขฉันหากฉันไม่ถูกต้อง)

ฉันคิดว่ามันจะเป็น advantagous สำหรับPaddleการวาดตัวเองที่ผมยกตัวอย่างวัตถุสองและPlayer Enemyถ้ามันไม่ได้อยู่ในPongของdraw()ฉันจะต้องเขียนรหัสที่คล้ายกันสองครั้ง

การปฏิบัติที่ดีที่สุดที่นี่คืออะไร?

ขอบคุณ


คำถามที่คล้ายกัน: gamedev.stackexchange.com/questions/13492/…
MichaelHouse

คำตอบ:


49

การมีนักแสดงวาดตัวเองไม่ใช่การออกแบบที่ดีด้วยเหตุผลหลักสองประการ:

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

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

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

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

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


14
+1 สำหรับ "renderer รหัสภาพวาด" แต่ -1 หลักการรับผิดชอบเดียวซึ่งมีโปรแกรมเมอร์ทำอันตรายมากกว่าดี ก็มีความคิดที่ถูก(แยกของความกังวล)แต่วิธีที่มันระบุ( "ชั้นทุกคนควรมีเพียงคนเดียวที่รับผิดชอบและ / หรือเพียงหนึ่งเหตุผลที่จะเปลี่ยนแปลง")เป็นเพียงที่ไม่ถูกต้อง
BlueRaja - Danny Pflughoeft

2
-1 ต่อ 1) ตามที่ BlueRaja ชี้ให้เห็นหลักการนี้เป็นอุดมการณ์ แต่ไม่ใช่ในทางปฏิบัติ -1 สำหรับ 2) เนื่องจากไม่ทำให้ส่วนขยายยากขึ้นอย่างมีนัยสำคัญ หากคุณต้องเปลี่ยนเอนจิ้นการแสดงผลทั้งหมดของคุณคุณจะต้องมีรหัสมากขึ้นในการเปลี่ยนแปลงมากกว่าสิ่งที่คุณมีในรหัสนักแสดงของคุณ ฉันจะชอบactor.draw(renderer,x,y)มากกว่าrenderer.draw(actor.getAttribs(),x,y)นี้
corsiKa

1
คำตอบที่ดี! ดี "คุณจะไม่ละเมิดหลักการความรับผิดชอบเดียว" ไม่ได้อยู่ในบัญญัติ
สิบประการ

@ glowcoder นั่นไม่ใช่ประเด็นคุณสามารถรักษา actor.draw () ซึ่งถูกกำหนดเป็น {renderer.draw (mesh, attribs, transform); } ใน OpenGL ฉันเก็บรักษาเรนเดอร์ของฉันไว้ไม่struct RenderDetail { glVAO* vao; int shader; int texture; Matrix4f* transform; }มากก็น้อย ด้วยวิธีการที่ renderer ไม่สนใจว่าข้อมูลหมายถึงอะไรมันแค่ประมวลผล จากข้อมูลนี้ฉันสามารถตัดสินใจได้ว่าจะเรียงลำดับการเรียกแบบดึงเพื่อลดการเรียก GPU โดยรวมหรือไม่
decaviatedcaviar

16

รูปแบบของผู้เข้าชมจะมีประโยชน์ที่นี่

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

interface Renderer {
    void drawPaddle(Player owner, Rectangle position);
    // Note: the Renderer chooses the Color based on which player the paddle belongs to

    // Also drawBackground, drawBall etc.
}

interface Actor {
    void draw(Renderer renderer);
}

class Paddle implements Actor {
    void draw(Renderer renderer) {
        renderer.drawPaddle(this.owner, this.getBounds());
    }
}

วิธีนี้ยังง่ายต่อการย้ายไปยังไลบรารีกราฟิกหรือเฟรมเวิร์กอื่น (ครั้งหนึ่งฉันย้ายเกมจาก Swing / Java2D ไปเป็น LWJGL และดีใจมากที่ฉันใช้รูปแบบนี้แทนที่จะผ่านGraphics2Dไป)

มีข้อดีอีกอย่างคือ: โหมดแสดงภาพสามารถทดสอบแยกต่างหากจากรหัสนักแสดงใด ๆ


2

ในตัวอย่างของคุณคุณต้องการให้วัตถุ Pong ใช้ draw () และแสดงผลเอง

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

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

จากนั้นคุณจะมีคลาส PongRenderer () ที่มีการอ้างอิงถึงวัตถุ Pong และจากนั้นจะรับผิดชอบการสร้างคลาส Pong () ซึ่งอาจเกี่ยวข้องกับการเรนเดอร์ Paddles หรือมีคลาส PaddleRenderer เพื่อดูแลมัน

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


-1

หากคุณต้องทำในรูปPongวาดของ () คุณจะไม่จำเป็นต้องทำซ้ำรหัส - เพียงเรียกใช้Paddleฟังก์ชัน 'ดึง () สำหรับไม้พายแต่ละอันของคุณ ดังนั้นในการPongวาดของคุณ() คุณมี

humanPaddle.draw();
compPaddle.draw();

-1

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

class X
{
  public void Draw( )
  {
     // Do something 
  } 
}

class Y
{
  public void Draw( )
   { // Do Something 
   } 
}

class Game
{
  X objX;
  Y objY;

  @Override
  public void OnDraw( )
  {
      objX.Draw( );
      objY.Draw( ); 
  } 
}

นี่ไม่ใช่รหัส แต่เป็นเพียงการแสดงวิธีการวาดวัตถุที่ควรทำ


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

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