как разделить вид и модель в игре java? - PullRequest
0 голосов
/ 09 февраля 2020

Я делаю игру в java, используя шаблон проектирования MVC (со свингом), и я не знаю, как отделить класс Controller от класса представления.

до сих пор у меня нет модель, которая содержит все данные, контроллер, который отвечает за все логи c, и теперь я думаю о том, как отделить представление.

У меня есть GameView, который расширяет Jpanel и имеет paintComonent:

@Override
public void paintComponent(Graphics g){
    super.paintComponent(g);
    Graphics2D g2D = (Graphics2D) g;
   draw(g2D);

}


public void draw(Graphics2D g2D){
    drawComponent(background, g2D);
    drawComponent(arenaController.getArena().getPlane(), g2D);
    drawComponent(arenaController.getArena().getPlayer().getBoat(), g2D);

    ArrayList<PlaneDrop> planeDrops = arenaController.getArena().getPlaneDrops();
    for(PlaneDrop planeDrop : planeDrops){
        drawComponent(planeDrop, g2D);
    }
    g2D.drawString("Life: " + arenaController.getArena().getPlayer().getLife(), 10, 30);
    g2D.drawString("Score: " + arenaController.getArena().getPlayer().getScore(), GAME_WIDTH - 50, 30);
}

, но с другой стороны, у меня есть GameEngine, который отвечает за настройку и запускает

@Override
public void run() {
    arenaController.init();
    long waitTime = 0;

    Graphics g = this.getGraphics();
   gameViewer.paintComponent(g);

    while(arenaController.isRunning()){
        long startTime = System.currentTimeMillis();

        gameViewer.paintComponent(g);
        update(); // update game
        gameViewer.repaint();
        long endTime = System.currentTimeMillis() - startTime;
        waitTime = (MILLISECOND / FPS) - endTime / MILLISECOND;

        try{
            Thread.sleep(waitTime);
        } catch (Exception e){}

    }
}

метод run () в движке вызывает метод paintComponent () представления (который для меня звучит как контроллер -> вызывает средство просмотра), но я считаю, что это не лучший способ сделать это и не рекомендуется вызывать paintComponent () напрямую.

Поэтому я хочу четко разделить контроллер и представление, но у меня возникли проблемы с поиском подходящего способа сделать это.

Ответы [ 2 ]

0 голосов
/ 10 февраля 2020

Решение: Хороший способ разделить данные и логи c в игре - это использовать Entity Component System . Эшли из LibGDX - это то, что я использую. Эти системы помогают сохранять даже самые крупные игры относительно чистыми и понятными. Однако требуется некоторая начальная работа, поэтому для небольших игр вам может быть лучше смешать логи c и данные в таких классах, как Player или Enemy, поместить все экземпляры ваших игровых объектов в List и есть холст, который рисует их все в порядке их Z-Index . Краткое объяснение системы компонентов сущности: В Entity Component System вы Entities как ваши игровые объекты. Эти Entites похожи на пустые объекты, но с возможностью иметь неограниченное количество компонентов (по одному на тип компонента). Эти Компоненты дают Сущности возможность быть чем-то (например, расширением Класса, но не ограничиваясь этим). Таким образом, если у вашей сущности есть TextureComponent, она отображается, но без присвоения ей TransformComponent она еще не может быть нарисована в указанном c месте. Если вы теперь также дадите ему BodyComponent, он может иметь физическое тело в вашем мире и может быть перемещен. TransformComponent будет обновляться при каждом перемещении тела. Вся логика c выполняется Systems. Обрабатывать все объекты с указанными c типами компонентов. Самостоятельно нарисованная визуализация (извиняюсь за внешность, я не художник): ECS Visualization

0 голосов
/ 09 февраля 2020

Хорошо, значит, вы столкнулись с рядом проблем.

Первый

Swing уже MVC

Второй

Форма "MVC", которая преподается и обсуждается сегодня, на самом деле не соответствует тому, как предполагалось использовать первоначальное намерение MVC. Считалось, что это скорее руководство или рекомендация, а не просто правило или парадигма.

Проблема в том, что в итоге вы получаете «чистых», которые настаивают на абсолютном разделении всех моделей, контроллеров и представлений в некоторых случаев, в ущерб API или решению (это когда вы сталкиваетесь с проблемами "контроллера массивного представления").

Почему? Рассмотрим кнопку. Это представление, оно имеет модель и может выступать в качестве контроллера.

Вам не нужен отдельный «контроллер» для управления кнопкой. Он может регистрировать действия клавиатуры и мыши, необходимые для мониторинга с базовой системой. Он может координировать изменения между моделью и самим собой (представлением) и становится автономной единицей работы.

Серьезно, не могли бы вы себе представить необходимость создания отдельного контроллера каждый раз, когда вы создаете кнопку или любой другой пользовательский интерфейс компонент ... ??

Когда вы добавляете кнопку в другой контейнер (представление), кнопка действует как ее собственный вид / контроллер. В этом случае родительский контейнер может иметь более крупную модель и несколько подвидов, которыми он «управляет» или координирует.

Контейнер также может управляться через внешний контроллер, который действует для координации изменений между контейнером. и большая модель, позволяющая отделить ее, но в этом случае я бы спроектировал ее так, чтобы логика реализации c была отделена, чтобы внешний контроллер не заботился о том, «как» все сделано Просто существовал договор, позволяющий передавать информацию между этими слоями.

Итак, какой смысл?

Не зацикливайтесь на попытках произвести " clean "MVC, особенно в Swing, вы будете разрывать слух.

Также помните, что вы можете поговорить с контроллером с другим контроллером, это позволяет ему распространять информацию там, где не работают субконтроллеры не нужна или не нужна вся информация, которая может быть доступна родительскому контроллеру. Это также позволяет ему управлять совершенно разными контроллерами, обеспечивая большую развязку элементов и хороший подход «включай и работай».

Итак, как это вам поможет?

Это должно предоставить вам некоторые места, с которых можно спрыгнуть.

Если вы не хотите, чтобы GameEngine говорил напрямую с представлением, вы можете установить sh контроллер "рендеринга". Кто будет нести ответственность за координацию изменений, внесенных игровым движком, в модель с базовым видом (ie, расписание прохода рендеринга).

Это позволит GameEngine "управлять" одним или несколькими «рендеринг» контроллеров и позволяет отделить фактический рендеринг от GameEngine

. Вы можете сделать это, потому что контроллер может отвечать за другие контроллеры - это просто координатор между моделью и следующий слой (обычно вид)

В этом примере вы можете изменить систему рендеринга, просто изменив реализацию контроллера «рендеринга» (и если вы используете interface s, это становится намного проще) без влияния на GameEngie, что, в конце концов, является преимуществом следования этому типу конструкции - разделению ответственности посредством разъединения.

Передача мыслей ...

  • Никогда не звоните paintComponent напрямую. Есть причина, по которой это protected
  • Не используйте getGraphics. Это не так, как обычная живопись в Swing. getGraphics в лучшем случае является просто снимком последнего прохода краски, выполненного Swing. Помните, что в Swing вы не управляете процессом рисования, об этом позаботились сами, вместо этого вы запланировали проход краски (ie repaint) и позволили системе позаботиться о сбросе
  • Если вы хотите / хотите контролировать процесс рисования, вам нужно использовать java.awt.Canvas и BufferStrategy.
...