Облегченная модификация изображения в Java

В качестве моего проекта CS я создаю полноценную игру по уклонению от пуль в стиле Touhou, которая включает в себя рендеринг тысяч изображений пуль по их правильным координатам на JPanel. К счастью, JVM могла хранить десятки тысяч буферизованных изображений без заметного падения кадров, поэтому я не ожидал, что столкнусь с гигантским препятствием: вращением изображений.

Изначально я хотел добиться поворота BufferedImage вражеской пули; Я использовал методы ротации для другого вопроса о переполнении стека с небольшой выборкой, и они сработали отлично. Проблема возникла, когда я попытался повернуть тысячи спрайтов пуль в ArrayList объектов пуль. Создание десятков тысяч новых BufferedImage и Graphics2D полностью остановило работу JVM.

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

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

public Bullet(... , double deg, ... , BufferedImage shape /*actual bullet sprite*/, String tag, BufferedImage emp /*empty bufferedimage to act as a template to modify image then redraw*/ ) throws IOException
    {
        rotor = emp;
        img = shape;
        rotate(deg);
        setDeg(deg);
        this.deg = deg;
        ...
    }
public void rotate(double angle) { //tried AffineTransform and image Op and everything but all the same...
        Graphics2D g = rotor.createGraphics();
        g.setBackground(new Color(255, 255, 255, 0));
        g.clearRect(0,0, rotor.getWidth(), rotor.getHeight());
        g.rotate(Math.toRadians(angle), img.getWidth() / 2, img.getHeight() / 2);
        g.drawImage(img, null, img.getWidth() - rotor.getWidth(), img.getHeight() - rotor.getHeight());
        g.dispose();
        img = rotor;
}

Тем не менее, при таком большом количестве маркеров (не менее 10 000), этот метод не имеет никакого инновационного значения. Есть ли способ сделать вращение изображения как можно более легким, чтобы не добавлять соответствующего веса рендерингу (и, надеюсь, спасти проект от обреченной гибели)?

Без вращения ножи выглядят не так. Пожалуйста, помогите :с

У вас на экране [компьютера] одновременно отображается хотя бы десять тысяч пуль? Если нет, то, возможно, стоит рассмотреть возможность вращения только маркеров, отображаемых на экране. Не уверен, что это уместная аналогия, но JTable использует один TableCellRenderer для рисования всех ячеек [таблицы], но рисует только те, которые видны на экране.

Abra 09.06.2024 11:48

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

Abra 09.06.2024 11:49

К счастью, JVM может хранить десятки тысяч буферизованных изображений — зачем вам 10 КБ изображений? Все, что вам нужно, это одно изображение. Затем вы можете раскрасить изображение в разных местах. Поэтому вам нужен собственный класс, содержащий изображение, которое нужно нарисовать, и местоположение изображения. Или, если вы воспользуетесь предложением по созданию изображений, повернутых на 360 градусов, тогда ваш пользовательский объект будет просто содержать местоположение и степени поворота, а затем код рисования получит доступ к массиву изображения для рисования.

camickr 09.06.2024 16:46
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
4
3
92
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Предварительный этап расчета:

Предварительно вычислите повернутые версии каждого спрайта пули через фиксированные интервалы (например, каждый 1 градус). Сохраните эти предварительно повернутые изображения в массиве или на карте, где каждый индекс/ключ представляет угол поворота. Пример реализации:

// Precompute rotated images
BufferedImage[] preRotatedImages = new BufferedImage[360];
for (int i = 0; i < 360; i++) {
    preRotatedImages[i] = rotateImage(originalImage, i);
}

// Rotate function
private BufferedImage rotateImage(BufferedImage img, int angle) {
    int w = img.getWidth();
    int h = img.getHeight();
    int newW = (int) Math.ceil(Math.sqrt(w * w + h * h));
    BufferedImage rotated = new BufferedImage(newW, newW, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2d = rotated.createGraphics();
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    g2d.rotate(Math.toRadians(angle), newW / 2, newW / 2);
    g2d.drawImage(img, (newW - w) / 2, (newW - h) / 2, null);
    g2d.dispose();
    return rotated;
}

// During gameplay, fetch pre-rotated images
public void render(Graphics g, int angle, int x, int y) {
    g.drawImage(preRotatedImages[angle % 360], x, y, null);
}

Оптимизации:

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

Большое спасибо!!! Значит, больше не будет необходимости в полях и конструкторах буферизованных изображений в объекте маркера, верно?

Polypeptide 09.06.2024 21:08

Другие вопросы по теме