2015-02-08 2 views
0

Я создал Java-игру, в которой используется JFrame, содержащий JApplet, который, в свою очередь, содержит JPanel, на который нарисованы графики, но по какой-то причине левая часть графики иногда мигает, Подумайте, почему это так. Я полагаю, что это может быть из-за того, что я на самом деле не использовал EDT, что я понял только потом, так кто-нибудь сможет показать мне, как я правильно интегрирую использование EDT в программу?Правильно использовать Event Dispatch Thread в JApplet?

Мой главный класс выглядит следующим образом:

public class Main extends JFrame{ 

public static final int HEIGHT = 600; 
public static final int WIDTH = 800; 
public static RApplet app; 

public Main(){ 
    setTitle("RCrawl"); 
    Container c = getContentPane(); 
    c.setPreferredSize(new Dimension(WIDTH, HEIGHT)); 
    pack(); 
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    setLocationRelativeTo(null); 
    setResizable(false); 
    final Player p = new Player(50, 70, true); 
    final Room r = new Room(0xff222244); 
    r.addEntity(new EntityRock(200, 150)); 
    app = new RApplet(WIDTH, HEIGHT); 
    app.setRoom(r); 
    app.setPlayer(p); 
    app.setFps(25); 
    add(app); 
    setVisible(true); 
    new UpdateThread().start(); 
    System.out.println("thread run"); 
} 

public static void main(String[] args){ 
    Main m = new Main(); 
} 

class UpdateThread extends Thread{ 
    public void run(){ 
     while(true)update(); 
    } 

    public void update(){ 
     app.refresh(); 
    } 
} 
} 

Хотя класс RApplet выглядит следующим образом:

public class RApplet extends JApplet{ 
public int width, height, fps; 
public long curTime, delta; 
public RenderPanel panel; 
public Room curRoom; 
public Player player; 
public boolean isSingle; 
public InputHandler input; 

public RApplet(int x, int y){ 
    width = x; 
    height = y; 
    panel = new RenderPanel(width, height); 
    fps = 30; 
    isSingle = true; 
    input = new InputHandler(); 
    addKeyListener(input); 
    setFocusable(true); 
    add(panel); 
} 

public void setPlayer(Player p){ 
    player = p; 
    curRoom.addPlayer(p); 
} 

public void setFps(int f){ 
    fps = f; 
} 

public void initialize(){ 
    curTime = System.currentTimeMillis(); 
    delta = curTime; 
} 

public void refresh(){ 
    delta = (int)(System.currentTimeMillis() - curTime); 
    if(delta > 1000/fps){ 
     curTime = System.currentTimeMillis(); 
     render(); 
     tick(); 
    } 
} 

public void setRoom(Room r){ 
    curRoom = r; 
} 

public void render(){ 
    panel.renderStart(curRoom); 
    panel.renderRoom(curRoom); 
    panel.renderEnd(); 
    panel.repaint(); 
} 

public void tick(){ 
    curRoom.tick(this); 
} 
} 

Так что я хотел бы сделать для того, чтобы использовать EDT правильно? Я пробовал несколько вещей, используя разные комбинации invokeLater() и invokeAndWait(), но не смог заставить их работать. Если вы можете помочь, любая помощь будет оценена.

EDIT: Вот метод визуализации от RenderPanel:

public void paintComponent(Graphics g){ 
    super.paintComponent(g); 
    Graphics2D g2 = (Graphics2D) g; 
    BufferedImage drawer = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 
    Graphics g1 = drawer.getGraphics(); 
    g1.drawImage(canvas, 0, 0, this); 
    g2.drawImage(drawer, 0, 0, this); 
    g1.dispose(); 
    g2.dispose(); 
} 

не было бы двойной буферизации?

+0

'', который использует JFrame, содержащий JApplet «' - почему этот * очень странный и хрупкий дизайн? Разве это не JFrame, который имеет JPanel? –

+0

Я использую JFrame, чтобы проверить его, прежде чем перемещать апплет на веб-страницу. – user2649681

+1

1. Не выполняйте рисунок BufferedImage в методе paintComponent, то есть все, начиная с 'BufferedImage drawer = ....' до 'g1.drawImage (...)'. Это должно быть сделано в другом месте, возможно, в фоновом потоке. Кроме того, несмотря на то, что для объекта Graphics Graphics BufferImage отлично, g1, когда вы закончите с ним, никогда не удаляйте объект Graphics, предоставленный вам JVM, а именно объект g2. Это сломает дерево живописи Свинг. –

ответ

0

Вызов перерисовки вызовет событие на EDT. ,

Если вы испытываете мерцание, я бы посмотрел, как долго фактически выполняется метод рендеринга для выполнения (и, возможно, рендеринга за кадром BufferedImage, и ваш метод рисования только рисует это.), Переключайтесь между буферизуемым изображением, которое вы рисуете и буферизованное изображение, которое нарисовано на экране при завершении рендеринга. (Это известно как двойной буферизации.)

Вы можете использовать BufferStrategy (PS Я не 100% уверен, что будет работать для апплетов или если он требует полного окна.)

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

Ленивый способ проверки того, какой поток вы в распечатывает из

Thread.currentThread().getName() 

или

Thread.dumpStack(); 

в методах, где вы непосредственно доступ графического объекта.

+0

Я добавил метод 'paintComponent()', разве он уже не использует двойную буферизацию? – user2649681

1

Вопросы ...

  • Будь осторожны обновлениями состояния игры из-за пределы событий диспетчерского Thread, это может привести к грязной краске как часть состояния обновления в процессе рисования
  • Не уничтожайте контексты Graphics, которые вы не создали, избавление от системы Graphics контекст повлияет на то, как окрашиваются другие компоненты, помните, что вы не могли быть единственным, что было окрашено во время цикла рисования, а контекст - это общий ресурс
  • Из-за того, что это реализованный, repaint - это безопасный поток.Событие с краской отправляется в очередь событий, которая обрабатывается потоком Dispatching Event, поэтому, если вы не делаете что-то ужасно неправильно (печать была возможным исключением из правила), рисование всегда будет происходить в контексте диспетчеризации событий Резьба
  • В paintComponent нет никакого пункта рендеринга в BufferedImage, компоненты Swing уже имеют двойной буфер. Если вы хотите реализовать перетаскивание страницы, тогда вы должны рисовать более одного BufferedImage, но должны обновлять буфер вне экрана вне EDT.
  • Базовая структура кода не имеет смысла, вы даете много контроль в JApplet И JFrame, но ни должны иметь какой-либо, они не должны быть не более, чем контейнеры для RenderPanel

возможных решений ...

Start путем упрощения процесса рендеринга/обновления. Я бы предпочел использовать Swing Timer в качестве основного механизма для процесса обновления, это просто, он запускает обновления в EDT, что означает, что было бы безопасно вносить изменения в состояние игры, не беспокоясь о гоночных условиях и грязной живописи. Это позволяет вам напрямую использовать метод paintComponent.

Когда вы получаете основной ход процесса, вы могли бы изучить более авансовое обновление двигателей (как с помощью Thread) и покадрового technquies

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

MVC

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

Для получения более подробной информации см. Model-View-Controller.

+0

Выглядит так, как будто я начинаю с нуля, а? – user2649681

+0

По крайней мере, немного резки и склеивания;) – MadProgrammer

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