Печать на консоль обновляет изображения? - Джава

Для проекта колледжа мне поручили сделать приключенческую игру на Java. Среди прочего, я использовал потоки и библиотеку свинга, но теперь столкнулся с проблемой.

// The following is in the constructor of a subclass of JPanel
gameViewThread = new Thread(() -> {
    double previous = System.currentTimeMillis();
        double lag = 0.0;
        double current;
        double elapsed;
        while(true){
            current = System.currentTimeMillis();
            elapsed = current - previous;
            previous = current;
            lag += elapsed;
            System.out.println("Checking up on stuff...");
            while(OverworldMap.initialised && lag >= 17) {
                generateBackground();
                lag -= 17;
            }
        }
});

В строке 11 приведенного выше вставленного кода у меня есть оператор печати, который я хотел бы удалить. (Я не хочу заливать консоль ненужной информацией). Однако, когда я удаляю это утверждение, визуальные эффекты не обновляются. Я проверял это снова и снова и убедился, что OverworldMap.initialized возвращает true. При запуске кода в режиме отладки я убедился, что работает generateBackground(). Так что мне кажется, что визуальные эффекты просто не обновляются, пока вы не «разбудите» Систему.

ПРИМЕЧАНИЕ. generateBackground() создает и сохраняет изображение в переменной BufferedImage, а repaint() обеспечивает его отрисовку в правильном месте. (repaint() вызывается в основном потоке 30 раз в секунду) Эти два метода работают. Они работали до того, как я попытался перенести генерацию фона (вспомните анимацию тайлов из Pokemon Fire Red o/e) в другой поток. (Я не хотел, чтобы логика и анимация мешали друг другу. (У меня была низкая частота кадров))


person Alexz    schedule 22.03.2017    source источник
comment
Это похоже на отсутствие связи между потоками. Как вы передаете результаты generateBackground() в repaint()? Есть ли изменчивая переменная синхронизированного раздела?   -  person    schedule 22.03.2017
comment
generateBackround() сохраняет изображение в статическое поле основного потока. Я не знаю, как создать посредника, если вы это имеете в виду. Мне сказали, что нельзя. Мне сказали, что когда одно из значений изменяется в основном потоке, все остальные потоки вызываются/обновляются.   -  person Alexz    schedule 22.03.2017
comment
Сделайте статическую переменную изменчивой и не используйте повторно сгенерированные растровые изображения.   -  person    schedule 22.03.2017
comment
На само фоновое изображение ссылается только один раз компонент рисования, который получает его субизображение. Кроме того, он также перезаписывается новым фоновым изображением. Я сделал его изменчивым, но это само по себе не решило проблему. :(   -  person Alexz    schedule 22.03.2017


Ответы (1)


Потому что вы пытаетесь обновить пользовательский интерфейс из основного потока. Это не сработает таким образом. Попробуйте использовать в своей программе метод SwingUtilities.invokeLater().

Следующий фрагмент кода может быть полезен:

public void init() {
    gameViewThread = new Thread(() -> {
        double previous = System.currentTimeMillis();
        double lag = 0.0;
        double current;
        double elapsed;
        while (true) {
            current = System.currentTimeMillis();
            elapsed = current - previous;
            previous = current;
            lag += elapsed;
            //System.out.println("Checking up on stuff...");
            while (OverworldMap.initialised && lag >= 17) {
                updateUI();
                //generateBackground();
                lag -= 17;
            }
        }
    });
}


public void updateUI() {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            // All swing UI update code here
            generateBackground();                    
        }
    });
}

PS: Также помните, НЕ используйте метод run() для своего потока. Вы должны вызвать метод start(), чтобы запустить его. Во-первых, в вашем конструкторе вызовите метод init(), который я предоставил, и где-то в вашем коде (может быть и в вашем конструкторе) используйте gameViewThread.start(), чтобы запустить ваш поток.

person Socal Coder    schedule 22.03.2017
comment
Вы имеете в виду что-то вроде этого? gameViewThread = new Thread(() -> { SwingUtilities.invokeLater(() -> { ... }} - person Alexz; 22.03.2017
comment
Вы можете попробовать добавить его в свой цикл while. - person Socal Coder; 22.03.2017
comment
Как это? SwingUtilities.invokeLater(this::generateBackground); - person Alexz; 22.03.2017
comment
Да, это тоже должно работать. Будет лучше, если вы создадите метод, например updateUI(), и обновите его соответствующим образом, чтобы весь код, связанный с пользовательским интерфейсом, мог быть обновлен в этой области. - person Socal Coder; 22.03.2017
comment
Спасибо за помощь. Однако теперь я получаю исключение NullPointerException. Похоже, generateBackground запускается, даже если OverworldMap.initialised имеет значение false. - person Alexz; 22.03.2017
comment
Этого не должно быть из-за вашего оператора while: while (OverworldMap.initialized && lag ›= 17) - person Socal Coder; 22.03.2017
comment
Давайте продолжим это обсуждение в чате. - person Socal Coder; 22.03.2017