Pacman в вопросах Java - PullRequest
       13

Pacman в вопросах Java

5 голосов
/ 15 января 2009

Для моего университетского задания я должен сделать сетевую версию pacman. Я подумал, что лучше всего подойти к этой проблеме, сначала сделав локальную копию pacman, а затем расширив эту функциональность для сетевой игры.

Я бы сказал, что я относительно новичок в разработке java GUI и использую такие функции в java.

Я начал переходить по указанным выше ссылкам в отношении разработки игр в Java и примера игры pacman.

Я решил представить лабиринт как массив int с разными значениями, означающими разные вещи. Однако когда метод рисования внутри основного игрового цикла запущен, я перерисовываю весь лабиринт этим методом.

    for (int i : theGame.getMaze())
    {
        if (i == 4)
        {
            g.setColor(mazeWallColour);
            g.fillRect(curX, curY, cellSize, cellSize);
            curX += 25;
        }
        else
        {
            curX += cellSize;
        }

        index++;


        // Move to new row
        if (index == 25)
        {
            index = 0;
            curX = 10;
            curY += cellSize;
        }
    }

Однако это дает мне скорость менее 1 кадра в секунду. Хотя я заметил, что в приведенном выше примере используется аналогичный способ перерисовки при каждом вызове метода рисования, и я верю, что это происходит на изображении, которое невозможно просмотреть (вроде двойной буферизации [Я использовал BufferStrategy, как и первую ссылку) объясняет]) Как лучше перерисовать лабиринт?

Любые указатели / советы с этим были бы полезны.

Спасибо за ваше время.

http://pastebin.com/m25052d5a - для основного игрового класса.

Редактировать: Я только что заметил что-то очень странное, что произошло после попытки увидеть, какой код так долго выполнялся.

В методе paintClear (Graphics g) я добавил

ocean = sprites.getSprite("oceano.gif");
g.setPaint(new TexturePaint(ocean, new Rectangle(0,t,ocean.getWidth(),ocean.getHeight())));
g.fillRect(10, 10,getWidth() - 20,getHeight() - 110);

, что заставило все это работать гладко - однако, когда я удалил эти строки, все замедлилось? Что могло вызвать это?

Обновленный код

Ответы [ 10 ]

4 голосов
/ 15 января 2009

Я все еще считаю себя новичком в Java, но недавно я разработал игру Frogger-esque с динамической картой и редактором, используя некоторые из упомянутых вами приемов, и я только рад предоставить некоторую помощь.

Как уже упоминалось, enum - это путь. Я настроил свою карту как двумерный массив и установил перечисление для каждого отдельного типа, написав метод внутри класса моей карты, чтобы взять одно изображение и разделить каждый квадрат на карте на каждое значение в моем перечислении.

Учебник, который помог мне с отображением, можно найти на Coke and Code . Весь исходный код есть, если вам нужна помощь с любым из них, хотя вы, кажется, хорошо понимаете, что делаете. Если вам все еще нужна помощь, я всегда могу вытащить некоторый исходный код.

4 голосов
/ 15 января 2009

Прежде всего, я бы порекомендовал вам использовать именованные константы, а не использовать случайные магические числа в вашем коде, и рассмотреть возможность использования перечислений для типов ваших ячеек. Хотя ваш код не будет работать быстрее, он, безусловно, облегчит его понимание. Кроме того, «i» обычно используется в качестве счетчика, а не для возвращаемого значения. Вы, вероятно, должны назвать это cellType или что-то подобное. Я также рекомендую вам использовать 2D-массив для вашей сценической карты, поскольку он упрощает некоторые вещи, как в материально-техническом, так и в концептуальном плане.

Тем не менее, вот несколько вещей, которые нужно попробовать:

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

Попробуйте позвонить drawRect() вместо fillRect() и посмотрите, будет ли это быстрее. Я не думаю, что это будет, но это стоит попробовать, даже если это выглядит уродливее. Точно так же вы можете попробовать создать Image и затем нарисовать его. Преимущество этого заключается в том, что вашему графическому объекту действительно легко сказать, что нужно преобразовать изображение. Кроме того, рассмотрите возможность полного устранения этой проблемы и убедитесь, что она значительно снижает производительность.

Кроме того, обычно вам не нужно запрашивать родительский объект для объекта Graphics и осуществлять рисование непосредственно на нем. Скорее, вы должны переопределить его метод paintComponent() и просто использовать предоставленную вам графику (возможно, вызывая вспомогательные методы, как и вы). Компоненты Swing по умолчанию имеют двойную буферизацию, поэтому вам не нужно реализовывать это самостоятельно; просто позвольте объекту свинга выполнить свою работу и сообщите, когда рисовать.

Кроме того, вы перекрашиваете весь экран, что является чем-то излишним. Если вы звоните repaint(Rectangle), Swing может перерисовать только те разделы вашей доски, которые явно помечены как грязные. Когда вы обновляете один из ваших спрайтов, вызывайте repaint (r) только в области старого и нового местоположения спрайта. Когда вы пройдете уровень и вам понадобится новая доска, вы можете вызвать repaint () (без параметров), чтобы перерисовать всю карту.

Вам также следует обратиться к учебному пособию Sun , чтобы получить советы по повышению эффективности в Swing.

3 голосов
/ 15 января 2009

Похоже, ваш звонок в Thread.sleep не делает то, что вы хотели, но я не думаю, что это источник вашей проблемы. У вас есть:

Thread.sleep(Math.max(0, startTime - System.currentTimeMillis()));

startTime всегда будет меньше, чем System.currentTimeMillis (), поэтому startTime - System.currentTimeMillis () всегда будет отрицательным, и, следовательно, ваш сон всегда будет длиться 0 миллисекунд. Это отличается от примера, который вы показали, потому что пример увеличивает startTime на 40 миллисекунд перед выполнением вычисления. Он рассчитывает, сколько времени нужно спать, чтобы увеличить время рисования до 40 миллисекунд.

В любом случае, вернемся к вашей проблеме. Я бы порекомендовал измерения, чтобы выяснить, где ваше время тратится. Нет смысла оптимизировать, пока вы не узнаете, что медленно. Вы уже знаете, как использовать System.currentTimeMillis (). Попробуйте использовать это, чтобы измерить, где все время идет. Все ли потрачено на рисование стен?


РЕДАКТИРОВАТЬ - я вижу, что это было помечено как принятое, поэтому я должен сделать вывод, что проблема исчезла, когда вы установили время сна? У меня нет большого опыта работы с графическим интерфейсом Java, но я могу предположить, что, возможно, ваш код истощал другие важные потоки. Установив для вашего потока максимальный приоритет и вызывая только sleep (0), вы в значительной степени гарантируете, что никакой другой поток в вашем процессе не сможет ничего сделать. Вот сообщение из блога Рэймонда Чена , объясняющее почему.

3 голосов
/ 15 января 2009

Код, который вы перечислили выше, не может быть источником проблемы 1fps ... У меня есть код, выполняющий гораздо больше, чем этот, который работает намного быстрее.

Можете ли вы сравнить этот код и убедиться, что это корень проблемы?

2 голосов
/ 15 января 2009

Чтобы вы не беспокоились о том, что это Java, я работал над Spectrum Analyzer (например, o-scope), где вся часть графического интерфейса пользователя (трассировка, меню, обработка кнопок и колес) была выполнена на Java. Когда я туда попал, он получал 1 кадр в секунду, когда я уходил, было 12-20. Это занимало много времени и работало на очень медленном процессоре.

Посмотрите только те части графического интерфейса, которые необходимо обновить. Зачастую вы можете перерисовать весь экран, но просто установите область отсечения для действительно обновленной части.

Будьте осторожны с внутренними петлями - они - Убийца Скорости.

Старайтесь избегать выделения и освобождения огромного количества объектов. Я не говорю, не используйте объекты, я говорю, не создавайте по одному для каждого пикселя:)

Удачи

2 голосов
/ 15 января 2009

Я не разработчик игр, но эта частота кадров кажется очень медленной.

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

Создайте BufferedImage, содержащий константные элементы (лабиринт ?, фон), а затем заново отрисуйте его сначала для каждого кадра. Поверх этого буферизованного изображения нарисуйте переменные элементы (PacMan, ghosts, dots и т. Д.).

Эта техника, наряду со многими другими советами по производительности Java2D, обсуждается в превосходной книге Ромена Гая Filthy Rich Clients .

1 голос
/ 15 января 2009

Если возможно, вы должны сохранить изображение лабиринта и нарисовать его за один вызов библиотеки. Вероятно, это не обязательно должно быть в полном разрешении - если вы хотите получить блочное 8-битное ощущение, я ожидаю, что графическая библиотека с радостью выполнит 8 ^)

Кроме того, как уже упоминали другие, вы можете сэкономить время, перерисовывая только те части экрана, которые требуют обновления. Это может быть раздражающим для реализации, но это может позволить вам значительно улучшить частоту кадров. Обязательно проведите несколько экспериментов, чтобы убедиться в этом, прежде чем предпринимать необходимые усилия!

1 голос
/ 15 января 2009

Java / Swing двойной буфер по умолчанию. Если вы используете Swing, вам не нужно создавать двойной буфер отдельно, как предлагают другие ответы.

Я согласен с Алленом, что указанный вами код не может быть причиной 1fps. Я написал крайне неэффективный код анимации Java / Swing, который работает намного быстрее, чем вы описываете. Сделайте еще несколько тестов, чтобы сузить причину медлительности.

1 голос
/ 15 января 2009

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

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

Извините за чрезвычайно общий совет

1 голос
/ 15 января 2009

Ничего себе, это довольно сложная проблема, чтобы дать кому-то только изучение Java.

Мой совет? Думайте с точки зрения объектов. Можете ли вы написать что-то БЕЗ пользовательского интерфейса, который имитирует поведение самой игры? Как только вы это заработаете, вы можете сосредоточиться на особых проблемах пользовательского интерфейса. Да, начните с локальной версии перед сетевым компонентом.

Я не геймер. Интересно, что бы предложил Java2D API, чтобы сделать вашу жизнь лучше?

Сколько у тебя времени, чтобы закончить?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...