2015-12-25 9 views
1

Полный источник находится на Github по адресу this address.Почему мое нарисованное изображение мерцает на JPanel?

У меня есть JPanel, в котором я рисую фон и спрайт игрока. Логика и перекраска находятся в отдельных временных масштабах. Вот соответствующий код:

static long logicSleep = (long) (1e9/120); 
static long drawSleep = (long) (1e9/60); 
static long startTime; 
static long logicTime, drawTime; 

public static void main(String[] args) { 
    startTime = System.nanoTime(); 
    while (keys[KeyEvent.VK_ESCAPE] == false) { 
     if (System.nanoTime() > logicTime + startTime) { 
      player.logic(); 
      logicTime += logicSleep; 
     } 
     if (System.nanoTime() > drawTime + startTime) { 
      sheet.repaint(); 
      drawTime += drawSleep; 
     } 
    } 
    System.exit(0); 
} 

Таким образом, логика работает каждые 1/120 секунды, а рисунок проходит каждые 1/60 секунды. Это работает по назначению. Однако изображение мерцает так часто. Это не разрывает экран, так как у меня есть Java 8, и мой монитор обновляется на частоте 60 Гц (не говоря уже о том, как по умолчанию включена двойная буферизация на jpanels). Мерцание кажется резким изменением положения, хотя фактическое положение символа на экране влияет только на один кадр.

Вот мой заказ JPanel класс:

static class Sheet extends JPanel { 
    @Override 
    public void paintComponent(Graphics g1) { 
     super.paintComponent(g1); 
     Graphics2D g = (Graphics2D) g1; 
     g.translate((int) -(player.xPos + player.sprites[0].getWidth()/2 - getWidth()/2), 
       (int) -(player.yPos + player.sprites[0].getHeight()/2 - getHeight()/2)); 
     g.drawImage(map, 0, 0, null); 
     g.drawImage(player.sprites[player.spriteNum], (int) player.xPos, (int) player.yPos, null); 
     g.drawImage(foreground, 0, 0, null); 
    } 
} 

мне удалось это исправить, изменив логику сна для запуска каждые 1/180 секунды, но мне интересно, почему это происходит с логикой работает в определенные временные рамки. Это также происходит с обновлением логики каждые 1/240 секунд.

TL; DR: Мой спрайт мерцает из-за моей логики и я не знаю почему.

+1

использовать метод 'setDoubleBuffered (истинный),' на ваших JPanel – WalterM

+1

@WalterM: не нужно делать, и на самом деле это не поможет, так как JPanels уже двойной буферизации по умолчанию, ** но * * Вы рисуете краску, а не paintComponent? Вы должны рисовать последний (paintComponent), а не первый (краска). Вы не показываете нам свой графический код, поэтому здесь трудно помочь.Лучше всего для вас создать и показать [mcve]. –

+0

@HovercraftFullOfEels Отредактировано для наглядности. – GingerDeadshot

ответ

3

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

Кроме того, вы выбрали время обновления, которое имеет общий знаменатель (то есть 60 и 180, 60 и 120 и так далее). Это означает, что существуют циклы, в которых оба условия должны запускаться. Если ваш логический расчет длинный, это означает, что перекраска будет задерживаться на столько времени. Это может быть еще одной причиной мерцания.

Правильный способ делать периодические задания - использовать объект Timer. Поскольку у вас есть два разных цикла синхронизации, и вам нужно, чтобы они были независимыми, вы должны использовать два отдельных экземпляра Timer.

Вы как правило, имеют два варианта в Timer класса:

  • javax.swing.Timer - запускает запланированной операции в случае диспетчерского Thread. Это полезно для операций, таких как repaint() и допустимо для коротких логических операций, и поскольку вся работа, выполняемая в одном потоке, не потребует летучих или других средств обеспечения видимости памяти.
  • java.util.Timer (альтернативно, java.util.concurrent.ScheduledThreadPoolExecutor) - подходит для планирования не GUI, более сложных операций, которые вы не хотели бы запускать на EDT. Если вы используете это, вы должны позаботиться о обновлении компонента GUI с использованием методов SwingUtility и использовать volatile или другие способы внесения изменений в данные, видимые для EDT, поскольку они будут работать в другом потоке.
+0

Я изменил программу, чтобы использовать java.util.Timer для логики и javax.swing.Timer для перерисовки. Теперь мерцание еще хуже. Это было прекрасно, когда оба были качелями. – GingerDeadshot

Смежные вопросы