Чи повинні актори в грі нести відповідальність за малювання себе?


41

Я дуже новачок у розробці ігор, але не в програмуванні.

Я (знову) граю з грою типу Pong, використовуючи canvasелемент JavaScript .

Я створив Paddleоб'єкт, який має такі властивості ...

  • width
  • height
  • x
  • y
  • colour

У мене також є Pongоб'єкт, який має такі властивості, як ...

  • width
  • height
  • backgroundColour
  • draw().

В draw()даний час метод скидає canvasі саме тут виникло питання.

У разі , якщо Paddleоб'єкт є draw()метод відповідає за його креслення, або якщо draw()на Pongоб'єкті буде відповідати за розробку своїх акторів (я припускаю , що це правильний термін, будь ласка , поправте мене , якщо я неправильно).

Я подумав, що Paddleмалюнок буде вигідним малювати себе, оскільки я створюю два об'єкти, Playerі Enemy. Якщо б це було не в Pongdraw(), я повинен був би написати подібний код двічі.

Яка тут найкраща практика?

Дякую.


Відповіді:


49

Наявність акторів малювати себе не є гарним дизайном з двох основних причин:

1) він порушує принцип єдиної відповідальності , тому що ці актори, мабуть, мали іншу роботу, перш ніж ви внесли в них код візуалізації.

2) утруднює розширення; якщо кожен тип актора реалізує власний малюнок, і вам потрібно змінити спосіб малювання в цілому , можливо, вам доведеться змінити багато коду. Уникнення надмірного використання спадщини може певною мірою полегшити це, але не повністю.

Краще для вашого рендерінга обробляти креслення. Зрештою, саме це означає бути рендером. Метод малювання рендерінга повинен мати об'єкт "опис візуалізації", який містить все необхідне для рендерування речі. Посилання на (ймовірно, загальні) дані геометрії, конкретні екземпляри перетворень або властивості матеріалу, такі як колір та ін. Потім це малює, і не байдуже, яким повинен бути цей опис візуалізації.

Потім ваші актори можуть дотримуватися опису візуалізації, який вони створюють самі. Оскільки суб'єкти типово логічні типи обробки, вони можуть підштовхувати зміни стану до опису візуалізації за необхідності - наприклад, коли актор отримує шкоду, він може встановити колір опису візуалізації червоним, щоб вказати на це.

Тоді ви можете просто повторити кожного видимого актора, включити їх описи візуалізації в рендерінг, і дозволити це зробити своє (в основному, ви могли б ще більше узагальнити це).


14
+1 - "код малювання рендерера", але -1 за принцип єдиної відповідальності, який приніс програмістам більше шкоди, ніж користі . У нього правильна ідея (відокремлення питань) , але спосіб її висловлення ("кожен клас повинен мати лише одну відповідальність та / або лише одну причину зміни") просто неправильний .
BlueRaja - Danny Pflughoeft

2
-1 для 1), як вказував BlueRaja, цей принцип ідеалістичний, але не практичний. -1 для 2), оскільки це не робить розширення значно складніше. Якщо вам доведеться змінити весь механізм візуалізації, вам доведеться змінити набагато більше коду, ніж те, що мало у коді актора. Я б більше віддавав перевагу, actor.draw(renderer,x,y)ніж renderer.draw(actor.getAttribs(),x,y).
corsiKa

1
Гарна відповідь! Ну "Ви не будете порушувати принцип єдиної відповідальності" - це не в моєму декалозі, але все-таки хороший принцип, який потрібно враховувати
FxIII,

@glowcoder Це не суть, ви можете підтримувати act.draw (), який визначається як {renderer.draw (mesh, attribs, transform); }. У OpenGL я підтримую struct RenderDetail { glVAO* vao; int shader; int texture; Matrix4f* transform; }більш-менш свого рендерінга. Таким чином рендерінгу не важливо, що представляють дані, він просто обробляє їх. Виходячи з цієї інформації, я також можу вирішити, чи сортувати порядок розіграшу дзвінків, щоб зменшити загальні дзвінки GPU.
deceleratedcaviar

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'draw (), вам не потрібно буде дублювати код - просто зателефонуйте на функцію Paddle' draw () для кожного з ваших весла. Тож у вашому 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
Це не "Як слід робити малювання", а єдиний спосіб зробити це. Як було сказано в попередніх відповідях, цей метод має деякі недоліки та мало переваг, тоді як інші структури мають значні переваги та гнучкість.
Тройсеф

Я вдячний, але оскільки наводився простий приклад різних способів малювання акторів на сцені, я згадував про це.
користувач43609
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.