Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.java.gui > #1257 > unrolled thread

Requesting tips, comments

Started by"Oliver Wong" <oliver.wong@THRWHITE.remove-dii-this>
First post2011-04-27 15:32 +0000
Last post2011-04-27 15:32 +0000
Articles 9 — 3 participants

Back to article view | Back to comp.lang.java.gui


Contents

  Requesting tips, comments "Oliver Wong" <oliver.wong@THRWHITE.remove-dii-this> - 2011-04-27 15:32 +0000
    Re: Requesting tips, comm "Knute Johnson" <knute.johnson@THRWHITE.remove-dii-this> - 2011-04-27 15:32 +0000
      Re: Requesting tips, comm "Daniel Pitts" <daniel.pitts@THRWHITE.remove-dii-this> - 2011-04-27 15:32 +0000
        Re: Requesting tips, comm "Oliver Wong" <oliver.wong@THRWHITE.remove-dii-this> - 2011-04-27 15:32 +0000
          Re: Requesting tips, comm "Oliver Wong" <oliver.wong@THRWHITE.remove-dii-this> - 2011-04-27 15:32 +0000
            Re: Requesting tips, comm "Knute Johnson" <knute.johnson@THRWHITE.remove-dii-this> - 2011-04-27 15:32 +0000
              Re: Requesting tips, comm "Oliver Wong" <oliver.wong@THRWHITE.remove-dii-this> - 2011-04-27 15:32 +0000
                Re: Requesting tips, comm "Knute Johnson" <knute.johnson@THRWHITE.remove-dii-this> - 2011-04-27 15:32 +0000
                  Re: Requesting tips, comm "Oliver Wong" <oliver.wong@THRWHITE.remove-dii-this> - 2011-04-27 15:32 +0000

#1257 — Requesting tips, comments

From"Oliver Wong" <oliver.wong@THRWHITE.remove-dii-this>
Date2011-04-27 15:32 +0000
SubjectRequesting tips, comments
Message-ID<06eMh.52174$Zb.969574@weber.videotron.net>
  To: comp.lang.java.gui
    I recently saw a thread about the Swing EDT in the CLJP, and it made 
me wonder whether my general game architecture was thread safe or not. 
EDT, and threading in general, are one of my weaker points in Java. Is 
this general design okay? Are there things which I've put into the EDT 
which I shouldn't have? Are there things which are outside of the EDT that 
should be in it?

<SSCCE>
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.geom.AffineTransform;
import java.lang.reflect.InvocationTargetException;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Test {

 private static final int DEFAULT_RENDERING_WIDTH = 640;
 private static final int DEFAULT_RENDERING_HEIGHT = 480;
 private static JFrame mainWindow;
 /*
  * the mainPanel is where most of the drawing happens. I draw onto a 
panel
  * instead of directly onto the JFrame to avoid messing around with
  * getInsets().
  */
 private static JPanel mainPanel;
 /*
  * Returns true if the game is in the process of shutting down and 
quitting.
  * If it's in the middle of a game-loop iteration, it'll finish that
  * iteration (potentially drawing 1 more frame of animation, and then 
quit.
  */
 private static boolean timeToQuit = false;
 /*
  * True if the player pressed the "action" key since the last iteration
  * through the game loop, false otherwise. In a real game, I'd probably 
have
  * many of these booleans (e.g. an "up" key, a "down" key, etc., and move 
it
  * into a seperate class for organization purposes.
  */
 private static boolean pressedActionKey = false;

 public static void main(String[] args) throws InterruptedException,
   InvocationTargetException {
  /*
   * This first invokeAndWait initializes all the GUI components, and
   * registers the appropriate listeners to hook into the main game
   * engine.
   */
  EventQueue.invokeAndWait(new Runnable() {
   @Override
   public void run() {
    mainWindow = new JFrame("My game");
    mainWindow.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
    mainWindow.addWindowListener(new WindowListener() {
     @Override
     public void windowActivated(WindowEvent e) {
      // Does nothing.
     }

     @Override
     public void windowClosed(WindowEvent e) {
      // Does nothing
     }

     @Override
     public void windowClosing(WindowEvent e) {
      timeToQuit = true;
      EventQueue.invokeLater(new Runnable() {
       @Override
       public void run() {
        synchronized (mainWindow) {
         mainWindow.setVisible(false);
         mainWindow.dispose();
        }
       }
      });
     }

     @Override
     public void windowDeactivated(WindowEvent e) {
      // Does nothing.
     }

     @Override
     public void windowDeiconified(WindowEvent e) {
      // Does nothing.
     }

     @Override
     public void windowIconified(WindowEvent e) {
      // Does nothing.
     }

     @Override
     public void windowOpened(WindowEvent e) {
      // Does nothing.
     }
    });
    mainWindow.addKeyListener(new KeyListener() {
     @Override
     public void keyPressed(KeyEvent e) {
      if (e.getKeyCode() == KeyEvent.VK_SPACE) {
       pressedActionKey = true;
      }
     }

     @Override
     public void keyReleased(KeyEvent e) {
      // does nothing.
     }

     @Override
     public void keyTyped(KeyEvent e) {
      // does nothing.
     }

    });
    mainPanel = new JPanel();
    mainPanel.setPreferredSize(new Dimension(
      DEFAULT_RENDERING_WIDTH, DEFAULT_RENDERING_HEIGHT));
    mainWindow.add(mainPanel);
    mainWindow.pack();
    mainWindow.setVisible(true);

   }
  });
  /*
   * This while loop is the main game loop. It basically iterates through
   * 3 stages forever: getting the player input, reacting to it, and
   * drawing the results on screen.
   */
  while (!timeToQuit) {
   getPlayerInput();
   processGameLogic();
   EventQueue.invokeAndWait(new Runnable() {
    @Override
    public void run() {
     synchronized (mainWindow) {
      Graphics2D g = (Graphics2D) mainPanel.getGraphics();
      g.setTransform(AffineTransform.getScaleInstance(
        (double) mainPanel.getWidth()
          / (double) DEFAULT_RENDERING_WIDTH,
        (double) mainPanel.getHeight()
          / (double) DEFAULT_RENDERING_HEIGHT));
      updateScreen(g);
     }
    }
   });
   Thread.sleep(1);
  }
 }

 public static void processGameLogic() {
  /*
   * Check if pacman touched a ghost, stuff like that. Doesn't touch any
   * Swing components.
   */
 }

 public static void getPlayerInput() {
  /*
   * Doesn't touch any Swing components.
   */
  if (pressedActionKey) {
   /* make mario jump */
   System.out.println("Mario jumps.");
   pressedActionKey = false;
  }
 }

 public static void updateScreen(Graphics2D g) {
  assert SwingUtilities.isEventDispatchThread() : "Don't call updateScreen 
from outside the EDT.";
  g.setColor(Color.BLACK);
  g.fillRect(0, 0, 640, 480);
  /*
   * TODO: Draw all sorts of amazing eye-candy.
   */
 }
}
</SSCCE>

    - Oliver

---
 * Synchronet * The Whitehouse BBS --- whitehouse.hulds.com --- check it out free usenet!
--- Synchronet 3.15a-Win32 NewsLink 1.92
Time Warp of the Future BBS - telnet://time.synchro.net:24

[toc] | [next] | [standalone]


#1266 — Re: Requesting tips, comm

From"Knute Johnson" <knute.johnson@THRWHITE.remove-dii-this>
Date2011-04-27 15:32 +0000
SubjectRe: Requesting tips, comm
Message-ID<RLkMh.96279$_w.85830@newsfe13.lga>
In reply to#1257
  To: comp.lang.java.gui
Oliver Wong wrote:
>     I recently saw a thread about the Swing EDT in the CLJP, and it made 
> me wonder whether my general game architecture was thread safe or not. 
> EDT, and threading in general, are one of my weaker points in Java. Is 
> this general design okay? Are there things which I've put into the EDT 
> which I shouldn't have? Are there things which are outside of the EDT that 
> should be in it?
> 
>      @Override
>      public void windowClosing(WindowEvent e) {
>       timeToQuit = true;
>       EventQueue.invokeLater(new Runnable() {
>        @Override
>        public void run() {
>         synchronized (mainWindow) {
>          mainWindow.setVisible(false);
>          mainWindow.dispose();
>         }
>        }
>       });
>      }

Oliver:

Window events are already processed on the EDT you don't need to 
specifically call them with EventQueue.invokeLater().

I'm not sure why you have the calls to set then main window visible and 
dispose it synchronized.  They are already being called on the EDT and 
if you don't make any Swing method calls except on the EDT they will 
already be synchronized by default.

Having played a lot with one form of animation, I would use a completely 
different tack.  Use a Window or JWindow and do active rendering.  This 
avoids the EDT altogether for any drawing.  You still may have to 
synchronize some parts of your code but the more you can avoid that the 
better.  Synchronizing can have a rather significant performance hit.

-- 

Knute Johnson
email s/nospam/knute/

---
 * Synchronet * The Whitehouse BBS --- whitehouse.hulds.com --- check it out free usenet!
--- Synchronet 3.15a-Win32 NewsLink 1.92
Time Warp of the Future BBS - telnet://time.synchro.net:24

[toc] | [prev] | [next] | [standalone]


#1267 — Re: Requesting tips, comm

From"Daniel Pitts" <daniel.pitts@THRWHITE.remove-dii-this>
Date2011-04-27 15:32 +0000
SubjectRe: Requesting tips, comm
Message-ID<1174530560.096511.297000@y66g2000hsf.googlegroups.com>
In reply to#1266
  To: comp.lang.java.gui
On Mar 21, 6:14 pm, Knute Johnson <nos...@rabbitbrush.frazmtn.com>
wrote:
> Oliver Wong wrote:
> >     I recently saw a thread about the Swing EDT in the CLJP, and it made
> > me wonder whether my general game architecture was thread safe or not.
> > EDT, and threading in general, are one of my weaker points in Java. Is
> > this general design okay? Are there things which I've put into the EDT
> > which I shouldn't have? Are there things which are outside of the EDT that
> > should be in it?
> Having played a lot with one form of animation, I would use a completely
> different tack.  Use a Window or JWindow and do active rendering.  This
> avoids the EDT altogether for any drawing.  You still may have to
> synchronize some parts of your code but the more you can avoid that the
> better.  Synchronizing can have a rather significant performance hit.
>
> --
>
> Knute Johnson
> email s/nospam/knute/

And incorrectly avoid Synchronization can have an extreme correctness
"hit".

Generally, synchronization isn't the "bad thing" that people have
stigmatized it to be.  Used incorrectly, it can lead to problems,
yes... But those generally come from a lack of understanding.  If you
tell people "Avoid, Avoid, Avoid", they'll possible never learn when/
where they MUST use it.

I suggest Java Concurrency in Practice <http://jcip.net/> for anyone
who wants to know the correct way to deal with multithreaded
applications.  I already understood SOME of it, but that book
clarified a lot of concepts, and solidified my understanding of multi-
threaded programming.

On Mar 21, 9:41 am, "Oliver Wong" <o...@castortech.com> wrote:
> <SSCCE>
[snip]
>   while (!timeToQuit) {
>    getPlayerInput();
>    processGameLogic();
>    EventQueue.invokeAndWait(new Runnable() {
>     @Override
>     public void run() {
>      synchronized (mainWindow) {
>       Graphics2D g = (Graphics2D) mainPanel.getGraphics();
>       g.setTransform(AffineTransform.getScaleInstance(
>         (double) mainPanel.getWidth()
>           / (double) DEFAULT_RENDERING_WIDTH,
>         (double) mainPanel.getHeight()
>           / (double) DEFAULT_RENDERING_HEIGHT));
>       updateScreen(g);
>      }
>     }
>    });
>    Thread.sleep(1);
>   }
>  }
[snip]

> </SSCCE>
>
>     - Oliver


Oliver:
I personally think its a "Bad Thing" to have a busy wait -- a while
loop with a Thread.sleep().  Swing is design around an Event model,
you can probably refactor your code to avoid running on the main
thread, and use a javax.swing.Timer instead.

That way you don't need to have a "getPlayerInput()",
"processGameLogic()", and the strange call to invokeAndWait().  It can
all be event driven.

BTW, you should avoid "synchronize" in the EDT all together.  If you
have a thread that synchronizes on an object, and then calls
"invokeAndWait" with a runnable that syncs on the same object, you
have a deadlock.  And this is only the first order example.

Hope this helps,
Daniel.

P.S. Again, I suggest Java Concurrency in Practice <http://jcip.net/>

---
 * Synchronet * The Whitehouse BBS --- whitehouse.hulds.com --- check it out free usenet!
--- Synchronet 3.15a-Win32 NewsLink 1.92
Time Warp of the Future BBS - telnet://time.synchro.net:24

[toc] | [prev] | [next] | [standalone]


#1272 — Re: Requesting tips, comm

From"Oliver Wong" <oliver.wong@THRWHITE.remove-dii-this>
Date2011-04-27 15:32 +0000
SubjectRe: Requesting tips, comm
Message-ID<soxMh.490$NM.6958@wagner.videotron.net>
In reply to#1267
  To: comp.lang.java.gui
"Daniel Pitts" <googlegroupie@coloraura.com> wrote in message 
news:1174530560.096511.297000@y66g2000hsf.googlegroups.com...
> On Mar 21, 6:14 pm, Knute Johnson <nos...@rabbitbrush.frazmtn.com>
> wrote:
>> Having played a lot with one form of animation, I would use a 
>> completely
>> different tack.  Use a Window or JWindow and do active rendering.  This
>> avoids the EDT altogether for any drawing.  You still may have to
>> synchronize some parts of your code but the more you can avoid that the
>> better.  Synchronizing can have a rather significant performance hit.

    I thought I *was* using active rendering. I thought active rendering 
was simply having your own rendering loop instead of waiting for AWT to 
sent paint events (which is how my code is structured). As a quick check, 
I skimmed through 
http://java.sun.com/docs/books/tutorial/extra/fullscreen/rendering.html 
and now I'm a bit confused. The page mentions setIgnoreRepaint, which I've 
forgotten to do. Okay, fine. But the page also implies (but does not 
explicitly state) that active rendering is only possible in full screen 
mode.

    So:

(1) What's the difference between what I'm doing, and active rendering? 
Are you thinking mainly of BufferStrategies? (I'll address that later on 
in this post)
(2) I assume active rendering possible in windowed-mode. Is this 
assumption correct?
(3) And finally, why do you recommend JWindow over JFrame (or do you)?

[...]
>
> I suggest Java Concurrency in Practice <http://jcip.net/> for anyone
> who wants to know the correct way to deal with multithreaded
> applications.  I already understood SOME of it, but that book
> clarified a lot of concepts, and solidified my understanding of multi-
> threaded programming.

    Thanks, I'll take a look at that book.

>
> On Mar 21, 9:41 am, "Oliver Wong" <o...@castortech.com> wrote:
>> <SSCCE>
> [snip]
>>   while (!timeToQuit) {
>>    getPlayerInput();
>>    processGameLogic();
>>    EventQueue.invokeAndWait(new Runnable() {
>>     @Override
>>     public void run() {
>>      synchronized (mainWindow) {
>>       Graphics2D g = (Graphics2D) mainPanel.getGraphics();
>>       g.setTransform(AffineTransform.getScaleInstance(
>>         (double) mainPanel.getWidth()
>>           / (double) DEFAULT_RENDERING_WIDTH,
>>         (double) mainPanel.getHeight()
>>           / (double) DEFAULT_RENDERING_HEIGHT));
>>       updateScreen(g);
>>      }
>>     }
>>    });
>>    Thread.sleep(1);
>>   }
>>  }
> [snip]
>
>> </SSCCE>
>>
>>     - Oliver
>
>
> Oliver:
> I personally think its a "Bad Thing" to have a busy wait -- a while
> loop with a Thread.sleep().  Swing is design around an Event model,
> you can probably refactor your code to avoid running on the main
> thread, and use a javax.swing.Timer instead.

    Perhaps for a "normal" application, but I'm pretty sure the common 
wisdom for games is to have your own rendering loop. Sun themselves 
recommends a rendering loop in their active rendering tutorial: 
http://java.sun.com/docs/books/tutorial/extra/fullscreen/rendering.html

<quote>
public void myRenderingLoop() {
  while (!done) {
    Graphics myGraphics = getPaintGraphics();
    // Draw as appropriate using myGraphics
    myGraphics.dispose();
  }
}
</quote>


"Knute Johnson" <nospam@rabbitbrush.frazmtn.com> wrote in message 
news:RLkMh.96279$_w.85830@newsfe13.lga...
> I'm not sure why you have the calls to set then main window visible and
> dispose it synchronized.  They are already being called on the EDT and
> if you don't make any Swing method calls except on the EDT they will
> already be synchronized by default.

[...]

"Daniel Pitts" <googlegroupie@coloraura.com> wrote in message 
news:1174530560.096511.297000@y66g2000hsf.googlegroups.com...
> BTW, you should avoid "synchronize" in the EDT all together.  If you
> have a thread that synchronizes on an object, and then calls
> "invokeAndWait" with a runnable that syncs on the same object, you
> have a deadlock.  And this is only the first order example.

    I was getting some NPEs from mainPanel.getGraphics() returning null, 
and I had figured it was due to the window getting disposed while the 
rendering loop was still occurring or something along those lines. But 
after having read a bit more about the event model (e.g. that it is 
implemented as a queue and is single-threaded) I see now that my 
synchronizations didn't really make sense.

    Anyway, here's my new code, taken into account your advices. The two 
main changes are:

(1) Used BufferStrategy to do hardware accelerated page flipping/double 
buffering. I normally do this in my games, but I thought it wasn't really 
relevant to the threading questions, so I left it out for this example. 
Now that I've put it in, I can't really use the JPanel (since you can't 
get a strategy on the JPanel), so this also means there's a bit of extra 
code to fiddle with insets on the Window.

(2) Replaced the synchronize() keywords with an if statement checking that 
mainWindow is not null.

<SSCCE>
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferStrategy;
import java.lang.reflect.InvocationTargetException;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class Test {

 private static final int DEFAULT_RENDERING_WIDTH = 640;
 private static final int DEFAULT_RENDERING_HEIGHT = 480;
 private static JFrame mainWindow;
 /**
  * Returns true if the game is in the process of shutting down and 
quitting.
  * If it's in the middle of a game-loop iteration, it'll finish that
  * iteration (potentially drawing 1 more frame of animation, and then 
quit.
  */
 private static boolean timeToQuit = false;
 /**
  * True if the player pressed the "action" key since the last iteration
  * through the game loop, false otherwise. In a real game, I'd probably 
have
  * many of these booleans (e.g. an "up" key, a "down" key, etc., and move 
it
  * into a seperate class for organization purposes.
  */
 private static boolean pressedActionKey = false;

 public static void main(String[] args) throws InterruptedException,
   InvocationTargetException {
  /*
   * This first invokeAndWait initializes all the GUI components, and
   * registers the appropriate listeners to hook into the main game
   * engine.
   */
  EventQueue.invokeAndWait(new Runnable() {
   @Override
   public void run() {
    mainWindow = new JFrame("My game");
    mainWindow.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
    mainWindow.setIgnoreRepaint(true);
    mainWindow.addWindowListener(new WindowListener() {
     @Override
     public void windowActivated(WindowEvent e) {
      // Does nothing.
     }

     @Override
     public void windowClosed(WindowEvent e) {
      // Does nothing
     }

     @Override
     public void windowClosing(WindowEvent e) {
      timeToQuit = true;
      EventQueue.invokeLater(new Runnable() {
       @Override
       public void run() {
        mainWindow.dispose();
        mainWindow = null;
       }
      });
     }

     @Override
     public void windowDeactivated(WindowEvent e) {
      // Does nothing.
     }

     @Override
     public void windowDeiconified(WindowEvent e) {
      // Does nothing.
     }

     @Override
     public void windowIconified(WindowEvent e) {
      // Does nothing.
     }

     @Override
     public void windowOpened(WindowEvent e) {
      // Does nothing.
     }
    });
    mainWindow.addKeyListener(new KeyListener() {
     @Override
     public void keyPressed(KeyEvent e) {
      if (e.getKeyCode() == KeyEvent.VK_SPACE) {
       pressedActionKey = true;
      }
     }

     @Override
     public void keyReleased(KeyEvent e) {
      // does nothing.
     }

     @Override
     public void keyTyped(KeyEvent e) {
      // does nothing.
     }

    });
    mainWindow.setPreferredSize(new Dimension(
      DEFAULT_RENDERING_WIDTH, DEFAULT_RENDERING_HEIGHT));
    mainWindow.pack();
    mainWindow.setVisible(true);
    mainWindow.createBufferStrategy(2);
   }
  });
  /*
   * This while loop is the main game loop. It basically iterates through
   * 3 stages forever: getting the player input, reacting to it, and
   * drawing the results on screen.
   */
  while (!timeToQuit) {
   getPlayerInput();
   processGameLogic();
   EventQueue.invokeAndWait(new Runnable() {
    @Override
    public void run() {
     if (mainWindow != null) {
      BufferStrategy strategy = mainWindow
        .getBufferStrategy();
      Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
      Insets insets = mainWindow.getInsets();
      g.translate(insets.left, insets.top);
      g.setTransform(AffineTransform
          .getScaleInstance(
            (double) (mainWindow.getWidth() - insets.right)
              / (double) DEFAULT_RENDERING_WIDTH,
            (double) (mainWindow
              .getHeight() - insets.bottom)
              / (double) DEFAULT_RENDERING_HEIGHT));
      updateScreen(g);
      strategy.show();
     }
    }
   });
   Thread.sleep(1);
  }
 }

 public static void processGameLogic() {
  /*
   * Check if pacman touched a ghost, stuff like that. Doesn't touch any
   * Swing components.
   */
 }

 public static void getPlayerInput() {
  /*
   * Doesn't touch any Swing components.
   */
  if (pressedActionKey) {
   /* make mario jump */
   System.out.println("Mario jumps.");
   pressedActionKey = false;
  }
 }

 public static void updateScreen(Graphics2D g) {
  assert SwingUtilities.isEventDispatchThread() : "Don't call updateScreen 
from outside the EDT.";
  g.setColor(Color.BLACK);
  g.fillRect(0, 0, 640, 480);
  /*
   * TODO: Draw all sorts of amazing eye-candy.
   */
 }
}
</SSCCE>

    - Oliver

---
 * Synchronet * The Whitehouse BBS --- whitehouse.hulds.com --- check it out free usenet!
--- Synchronet 3.15a-Win32 NewsLink 1.92
Time Warp of the Future BBS - telnet://time.synchro.net:24

[toc] | [prev] | [next] | [standalone]


#1274 — Re: Requesting tips, comm

From"Oliver Wong" <oliver.wong@THRWHITE.remove-dii-this>
Date2011-04-27 15:32 +0000
SubjectRe: Requesting tips, comm
Message-ID<NEyMh.822$NM.15504@wagner.videotron.net>
In reply to#1272
  To: comp.lang.java.gui
"Oliver Wong" <owong@castortech.com> wrote in message 
news:soxMh.490$NM.6958@wagner.videotron.net...
[most of the code snipped]
>
>  /*
>   * This while loop is the main game loop. It basically iterates through
>   * 3 stages forever: getting the player input, reacting to it, and
>   * drawing the results on screen.
>   */
>  while (!timeToQuit) {
>   getPlayerInput();
>   processGameLogic();
>   EventQueue.invokeAndWait(new Runnable() {
>    @Override
>    public void run() {
>     if (mainWindow != null) {
>      BufferStrategy strategy = mainWindow
>        .getBufferStrategy();
>      Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
>      Insets insets = mainWindow.getInsets();
>      g.translate(insets.left, insets.top);
>      g.setTransform(AffineTransform
>          .getScaleInstance(
>            (double) (mainWindow.getWidth() - insets.right)
>              / (double) DEFAULT_RENDERING_WIDTH,
>            (double) (mainWindow
>              .getHeight() - insets.bottom)
>              / (double) DEFAULT_RENDERING_HEIGHT));
>      updateScreen(g);

Oops, I should probably call g.dispose() here.

>      strategy.show();
>     }
>    }
>   });
>   Thread.sleep(1);
>  }

    - Oliver

---
 * Synchronet * The Whitehouse BBS --- whitehouse.hulds.com --- check it out free usenet!
--- Synchronet 3.15a-Win32 NewsLink 1.92
Time Warp of the Future BBS - telnet://time.synchro.net:24

[toc] | [prev] | [next] | [standalone]


#1276 — Re: Requesting tips, comm

From"Knute Johnson" <knute.johnson@THRWHITE.remove-dii-this>
Date2011-04-27 15:32 +0000
SubjectRe: Requesting tips, comm
Message-ID<44AMh.104676$BK1.87802@newsfe13.lga>
In reply to#1274
  To: comp.lang.java.gui
Oliver Wong wrote:
> "Oliver Wong" <owong@castortech.com> wrote in message 
> news:soxMh.490$NM.6958@wagner.videotron.net...
> [most of the code snipped]
>>  /*
>>   * This while loop is the main game loop. It basically iterates through
>>   * 3 stages forever: getting the player input, reacting to it, and
>>   * drawing the results on screen.
>>   */
>>  while (!timeToQuit) {
>>   getPlayerInput();
>>   processGameLogic();
>>   EventQueue.invokeAndWait(new Runnable() {
>>    @Override
>>    public void run() {
>>     if (mainWindow != null) {
>>      BufferStrategy strategy = mainWindow
>>        .getBufferStrategy();
>>      Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
>>      Insets insets = mainWindow.getInsets();
>>      g.translate(insets.left, insets.top);
>>      g.setTransform(AffineTransform
>>          .getScaleInstance(
>>            (double) (mainWindow.getWidth() - insets.right)
>>              / (double) DEFAULT_RENDERING_WIDTH,
>>            (double) (mainWindow
>>              .getHeight() - insets.bottom)
>>              / (double) DEFAULT_RENDERING_HEIGHT));
>>      updateScreen(g);
> 
> Oops, I should probably call g.dispose() here.
> 
>>      strategy.show();
>>     }
>>    }
>>   });
>>   Thread.sleep(1);
>>  }
> 
>     - Oliver 
> 
> 

Oliver:

For active rendering I would take your rendering out of the EDT 
altogether.  The problem that you are going to run into is that 
eventually too much will be happening on the EDT and you won't be able 
to do any user input processing or that it will delay your rendering. 
You only need to create the BufferStrategy once not every time in the 
rendering loop.  I like to use java.util.Timer to drive the loop but you 
can use Thread.sleep().  As Daniel said I would do my user input event 
driven.  You can use a Frame or JFrame but you have to deal with the 
insets and title bar your self.  If you use an undecorated frame you 
might as well use a window.

Below is how I would do it.  See if you like any of it.

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;

public class test4 extends JWindow {
     java.util.Timer timer;
     BufferStrategy bs;
     int x,y,deltaX=3,deltaY=3;

     public test4() {
         // you want to do all drawing
         setIgnoreRepaint(true);

         addWindowListener(new WindowAdapter() {
             public void windowOpened(WindowEvent we) {
                 // don't create the buffer strategy
                 // until window is visible
                 test4.this.createBufferStrategy(2);
                 bs = getBufferStrategy();
                 // schedule animation
                 timer.schedule(new AnimationTask(),0,30);
             }
         });

         timer = new java.util.Timer();

         setSize(400,300);
         setVisible(true);
     }

     class AnimationTask extends TimerTask {
         public void run() {
             x += deltaX;
             y += deltaY;
             if (x > getWidth() - 30 || x < 0)
                 deltaX = -deltaX;
             if (y > getHeight() - 30 || y < 0)
                 deltaY = -deltaY;
             render();
         }
     }

     void render() {
         do {
             do {
                 // get new draw graphics every time
                 Graphics g = bs.getDrawGraphics();
                 // draw
                 g.setColor(Color.WHITE);
                 g.fillRect(0,0,getWidth(),getHeight());
                 g.setColor(Color.BLUE);
                 g.fillOval(x,y,30,30);
                 // dispose of the draw graphics
                 g.dispose();
             } while (bs.contentsRestored()) ;
             // blit/flip buffers
             bs.show();
         } while (bs.contentsLost()) ;
     }

     public static void main(String[] args) {
         Runnable r = new Runnable() {
             public void run() {
                 test4 t4 = new test4();
                 t4.addMouseListener(new MouseAdapter() {
                     public void mousePressed(MouseEvent me) {
                         System.exit(0);
                     }
                 });
             }
         };
         EventQueue.invokeLater(r);
     }
}

-- 

Knute Johnson
email s/nospam/knute/

---
 * Synchronet * The Whitehouse BBS --- whitehouse.hulds.com --- check it out free usenet!
--- Synchronet 3.15a-Win32 NewsLink 1.92
Time Warp of the Future BBS - telnet://time.synchro.net:24

[toc] | [prev] | [next] | [standalone]


#1279 — Re: Requesting tips, comm

From"Oliver Wong" <oliver.wong@THRWHITE.remove-dii-this>
Date2011-04-27 15:32 +0000
SubjectRe: Requesting tips, comm
Message-ID<SUDMh.3449$NM.43499@wagner.videotron.net>
In reply to#1276
  To: comp.lang.java.gui
"Knute Johnson" <nospam@rabbitbrush.frazmtn.com> wrote in message 
news:44AMh.104676$BK1.87802@newsfe13.lga...
>
> For active rendering I would take your rendering out of the EDT 
> altogether.  The problem that you are going to run into is that 
> eventually too much will be happening on the EDT and you won't be able 
> to do any user input processing or that it will delay your rendering.

    This key piece of information is pretty central to my 
(mis?)understanding. I thought it's written that whenever you touch Swing 
components, you should do so from within the EDT. If that's the case, 
shouldn't you perform painting of JFrames and other widgets within the 
EDT?

> You only need to create the BufferStrategy once not every time in the 
> rendering loop.

    Right: in the code I posted, I actually create the BufferStrategy 
once, but call getBufferStrategy() repeatedly from within the loop (as 
opposed to, in your code, calling getBufferStrategy once and storing its 
return value somewhere). From peering into the source code, it looks like 
getBufferStrategy() is just a plain getter anyway.

>  I like to use java.util.Timer to drive the loop but you can use 
> Thread.sleep().

    The Timer class seems to have its own internal "task queue". From the 
javadocs:
http://java.sun.com/javase/6/docs/api/java/util/Timer.html
<quote>
Timer tasks should complete quickly. If a timer task takes excessive time 
to complete, it "hogs" the timer's task execution thread. This can, in 
turn, delay the execution of subsequent tasks, which may "bunch up" and 
execute in rapid succession when (and if) the offending task finally 
completes.
</quote>

    I haven't spent too much time analyzing the impact of this (presumably 
there will only be one if I use Timer.schedule() at multiple locations in 
my code), but it sounds like I can avoid worrying about it entirely with a 
tight loop and Thread.sleep(1).

    I realize that the javadocs themselves claim that your design is 
appropriate for animations (It says "Fixed-delay execution is appropriate 
for recurring activities that require 'smoothness.' In other words, it is 
appropriate for activities where it is more important to keep the 
frequency accurate in the short run than in the long run. This includes 
most animation tasks"), but this fixed-delay behaviour sounds like it's 
incompatible with the way *I* want rendering to work in my engine.

    It's not shown in my code, but in a "real" engine, I usually calculate 
the amount of time that has passed between each frame of animation (I've 
got this library which tries to use native C code on platforms it 
recognizes, and falls back on System.currentTimeMillis() on those it 
doesn't), and then I use that elapsed time to calculate whether to skip 
ahead a couple of frames (on computers that are a bit slow for this game) 
or to interpolate between two frames of animations (on computers that are 
a bit fast for this game).

    For example, I might have a scripting language which says "It should 
take the character 2 seconds to walk from point A to point B", and the 
engine will smoothly animate a sprite moving from point A to point B at 
the highest frame rate that the computer can handle.

    Long story short, the "Fixed-delay execution" system seems like it'll 
work counter to my goal there.


>  As Daniel said I would do my user input event driven.

    Really? I find a polling-based input system much more natural to 
program for, in the context of making a game. For example, if the player 
presses the "Right" cursor key, I want Sonic the Hedgehog to start running 
towards the right. And if in the next frame, he's still holding down the 
"right" cursor I want Sonic to keep running (otherwise, he should start to 
brake, and slow down). And the longer the button is held down, the more 
Sonic accelerates (up to a certain terminal velocity).

    With a polling system, the logic would look something like:

{
  if (rightButtonIsDown()) {
    if (sonic.xVel < MAX_XVEL) {
      sonic.xVel += 1.0;
    }
  } else if (leftButtonIsDown()) {
    if (sonic.xVel > -MAX_XVEL) {
      sonic.xVel -= 1.0;
    }
  } else {
    sonic.xVel /= 2; /*Quickly approaches zero.*/
  }
}

    The only way I could think of implementing this with Swing's event 
model would be to set a flag when a keypress occurs, and then unset a flag 
when the key is released... which is pretty much emulating a polling 
system ontop of Swing's event system... which is pretty much what the code 
I posted does.

>  You can use a Frame or JFrame but you have to deal with the insets and 
> title bar your self.  If you use an undecorated frame you might as well 
> use a window.

    Right, the insets are a bit of a pain, and I sometimes wish JFrame had 
a setSizeWithoutInsets() method in addition to its standard setSize() 
method. But I *do* want a decorate frame (at least when the game is not 
running in full screen mode), so that the player can move the window 
around and resize it. Of course, if the player switches the game to 
fullscreen mode, then at that point, the window should be undecorated.

>
> Below is how I would do it.  See if you like any of it.
[informative code snipped]

    Thanks. You introduced me to the WindowAdapter class, and it looks 
like I'll have to re-read the javadocs for BufferStrategy (since it's not 
clear to me what you're trying to do with your calls to contentsLost() and 
contentsRestored()) It looks like it has something to do with losing image 
data due to the use of VolatileImage, but I'm wondering whether it's 
necessary for my game: In most of my games, so much is going on screen at 
any one point that I typically assume that the whole image is always lost 
and redraw the whole frame from scratch. The game is implemented as a 
state machine (not illustrated in the simplified version of the code I've 
been posting), and any state which needs access to previous frame contents 
(e.g. for a motion-blurring effect) needs to implement storing previous 
image data itself.

    Here's yet another iteration of my design, taking into account your 
advice of pulling the active-drawing code out of the EDT. I've had to 
reintroduce the synchronize() statements, because otherwise it's possible 
that the user might close the window (and thus invalidate the graphics 
object) while I'm still using the graphics object to render the next 
frame. I've added a Thread.sleep(1000) statement to increase the 
likelihood of this race condition occurring for illustrative purposes.

    I've also used your WindowAdapter (and KeyAdapter) trick.

<SSCCE>
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferStrategy;
import java.lang.reflect.InvocationTargetException;

import javax.swing.JFrame;

public class Test {

 private static final int DEFAULT_RENDERING_WIDTH = 640;
 private static final int DEFAULT_RENDERING_HEIGHT = 480;
 private static JFrame mainWindow;
 /**
  * Returns true if the game is in the process of shutting down and 
quitting.
  * If it's in the middle of a game-loop iteration, it'll finish that
  * iteration (potentially drawing 1 more frame of animation, and then 
quit.
  */
 private static boolean timeToQuit = false;
 /**
  * True if the player pressed the "action" key since the last iteration
  * through the game loop, false otherwise. In a real game, I'd probably 
have
  * many of these booleans (e.g. an "up" key, a "down" key, etc., and move 
it
  * into a seperate class for organization purposes.
  */
 private static boolean pressedActionKey = false;

 public static void main(String[] args) throws InterruptedException,
   InvocationTargetException {
  /*
   * This first invokeAndWait initializes all the GUI components, and
   * registers the appropriate listeners to hook into the main game
   * engine.
   */
  EventQueue.invokeAndWait(new Runnable() {
   @Override
   public void run() {
    mainWindow = new JFrame("My game");
    mainWindow.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
    mainWindow.setIgnoreRepaint(true);
    mainWindow.addWindowListener(new WindowAdapter() {
     @Override
     public void windowClosing(WindowEvent e) {
      timeToQuit = true;
      EventQueue.invokeLater(new Runnable() {
       @Override
       public void run() {
        synchronized (mainWindow) {
         mainWindow.dispose();
         mainWindow = null;
        }
       }
      });
     }
    });
    mainWindow.addKeyListener(new KeyAdapter() {
     @Override
     public void keyPressed(KeyEvent e) {
      if (e.getKeyCode() == KeyEvent.VK_SPACE) {
       pressedActionKey = true;
      }
     }
    });
    mainWindow.setPreferredSize(new Dimension(
      DEFAULT_RENDERING_WIDTH, DEFAULT_RENDERING_HEIGHT));
    mainWindow.pack();
    mainWindow.setVisible(true);
    mainWindow.createBufferStrategy(2);
   }
  });
  /*
   * This while loop is the main game loop. It basically iterates through
   * 3 stages forever: getting the player input, reacting to it, and
   * drawing the results on screen.
   */
  while (!timeToQuit) {
   getPlayerInput();
   processGameLogic();
   synchronized (mainWindow) {
    if (mainWindow != null) {
     Thread.sleep(1000);
     BufferStrategy strategy = mainWindow.getBufferStrategy();
     Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
     Insets insets = mainWindow.getInsets();
     g.translate(insets.left, insets.top);
     g.setTransform(AffineTransform.getScaleInstance(
       (double) (mainWindow.getWidth() - insets.right)
         / (double) DEFAULT_RENDERING_WIDTH,
       (double) (mainWindow.getHeight() - insets.bottom)
         / (double) DEFAULT_RENDERING_HEIGHT));
     updateScreen(g);
     g.dispose();
     strategy.show();
    }
   }
   Thread.sleep(1);
  }
 }

 public static void processGameLogic() {
  /*
   * Check if pacman touched a ghost, stuff like that. Doesn't touch any
   * Swing components.
   */
 }

 public static void getPlayerInput() {
  /*
   * Doesn't touch any Swing components.
   */
  if (pressedActionKey) {
   /* make mario jump */
   System.out.println("Mario jumps.");
   pressedActionKey = false;
  }
 }

 public static void updateScreen(Graphics2D g) {
  g.setColor(Color.BLACK);
  g.fillRect(0, 0, 640, 480);
  /*
   * TODO: Draw all sorts of amazing eye-candy.
   */
 }
}
</SSCCE>

    - Oliver

---
 * Synchronet * The Whitehouse BBS --- whitehouse.hulds.com --- check it out free usenet!
--- Synchronet 3.15a-Win32 NewsLink 1.92
Time Warp of the Future BBS - telnet://time.synchro.net:24

[toc] | [prev] | [next] | [standalone]


#1280 — Re: Requesting tips, comm

From"Knute Johnson" <knute.johnson@THRWHITE.remove-dii-this>
Date2011-04-27 15:32 +0000
SubjectRe: Requesting tips, comm
Message-ID<OrGMh.112450$BK1.4200@newsfe13.lga>
In reply to#1279
  To: comp.lang.java.gui
Oliver Wong wrote:
> "Knute Johnson" <nospam@rabbitbrush.frazmtn.com> wrote in message 
> news:44AMh.104676$BK1.87802@newsfe13.lga...
>> For active rendering I would take your rendering out of the EDT 
>> altogether.  The problem that you are going to run into is that 
>> eventually too much will be happening on the EDT and you won't be able 
>> to do any user input processing or that it will delay your rendering.
> 
>     This key piece of information is pretty central to my 
> (mis?)understanding. I thought it's written that whenever you touch Swing 
> components, you should do so from within the EDT. If that's the case, 
> shouldn't you perform painting of JFrames and other widgets within the 
> EDT?

Unless you are trying to do high performance active rendering.  Then you 
  do your rendering in another thread.  The normal Swing component 
drawing is disabled and you won't interfere with it at all.

>> You only need to create the BufferStrategy once not every time in the 
>> rendering loop.
> 
>     Right: in the code I posted, I actually create the BufferStrategy 
> once, but call getBufferStrategy() repeatedly from within the loop (as 
> opposed to, in your code, calling getBufferStrategy once and storing its 
> return value somewhere). From peering into the source code, it looks like 
> getBufferStrategy() is just a plain getter anyway.

Yes you are correct.  In your case it is just one less method call in 
the render loop to do it the way I did.

>>  I like to use java.util.Timer to drive the loop but you can use 
>> Thread.sleep().
> 
>     The Timer class seems to have its own internal "task queue". From the 
> javadocs:
> http://java.sun.com/javase/6/docs/api/java/util/Timer.html
> <quote>
> Timer tasks should complete quickly. If a timer task takes excessive time 
> to complete, it "hogs" the timer's task execution thread. This can, in 
> turn, delay the execution of subsequent tasks, which may "bunch up" and 
> execute in rapid succession when (and if) the offending task finally 
> completes.
> </quote>

Each Timer object creates a thread.  If you only schedule one task it 
won't run into problems with queue delays.  I did test this with 
something else I was working on and it is better to create a new Timer 
for each task.

>     I haven't spent too much time analyzing the impact of this (presumably 
> there will only be one if I use Timer.schedule() at multiple locations in 
> my code), but it sounds like I can avoid worrying about it entirely with a 
> tight loop and Thread.sleep(1).

>     I realize that the javadocs themselves claim that your design is 
> appropriate for animations (It says "Fixed-delay execution is appropriate 
> for recurring activities that require 'smoothness.' In other words, it is 
> appropriate for activities where it is more important to keep the 
> frequency accurate in the short run than in the long run. This includes 
> most animation tasks"), but this fixed-delay behaviour sounds like it's 
> incompatible with the way *I* want rendering to work in my engine.

I know but I can tell you after a lot of experimentation with adjusting 
timers and fixed rate Timers that it looks better with fixed delay.

>     It's not shown in my code, but in a "real" engine, I usually calculate 
> the amount of time that has passed between each frame of animation (I've 
> got this library which tries to use native C code on platforms it 
> recognizes, and falls back on System.currentTimeMillis() on those it 
> doesn't), and then I use that elapsed time to calculate whether to skip 
> ahead a couple of frames (on computers that are a bit slow for this game) 
> or to interpolate between two frames of animations (on computers that are 
> a bit fast for this game).

I don't know how real game developers handle timing.  I do know that 
with a fair amount of testing I have gone back to Timer.schedule().  It 
does not produce that accurate a result on Windows XP computers that I 
have tested it on.  It is just better than the alternatives I have tried.

I don't have any real experience with XP timing but I would be very 
curious to know if it is possible to make a JNI method that could sleep 
very accurately.  I would like to find something that could be accurate 
to 250 microseconds +- to try in my timing loop.  The accuracy of 
Timer.schedule() is no where near that close.  If you ran the test code 
I posted you can see the jitter caused by the timing errors.

>     For example, I might have a scripting language which says "It should 
> take the character 2 seconds to walk from point A to point B", and the 
> engine will smoothly animate a sprite moving from point A to point B at 
> the highest frame rate that the computer can handle.

I think what you will find with that method is that your frame rate is 
constantly changing as thread timing and system load changes.  But I 
will be interested in your results.

>     Long story short, the "Fixed-delay execution" system seems like it'll 
> work counter to my goal there.

I'm not trying to talk you out of it just trying to give you what little 
I know and have tried.

>>  As Daniel said I would do my user input event driven.
> 
>     Really? I find a polling-based input system much more natural to 
> program for, in the context of making a game. For example, if the player 
> presses the "Right" cursor key, I want Sonic the Hedgehog to start running 
> towards the right. And if in the next frame, he's still holding down the 
> "right" cursor I want Sonic to keep running (otherwise, he should start to 
> brake, and slow down). And the longer the button is held down, the more 
> Sonic accelerates (up to a certain terminal velocity).
> 
>     With a polling system, the logic would look something like:
> 
> {
>   if (rightButtonIsDown()) {
>     if (sonic.xVel < MAX_XVEL) {
>       sonic.xVel += 1.0;
>     }
>   } else if (leftButtonIsDown()) {
>     if (sonic.xVel > -MAX_XVEL) {
>       sonic.xVel -= 1.0;
>     }
>   } else {
>     sonic.xVel /= 2; /*Quickly approaches zero.*/
>   }
> }
> 
>     The only way I could think of implementing this with Swing's event 
> model would be to set a flag when a keypress occurs, and then unset a flag 
> when the key is released... which is pretty much emulating a polling 
> system ontop of Swing's event system... which is pretty much what the code 
> I posted does.

I used the old event model for the Pong game I wrote.  That works well 
and for that sort of input will probably be better.  I was thinking 
JTextFields and Buttons before.  Pong game and source is at 
www.knutejohnson.com.  It does not use active rendering.

>>  You can use a Frame or JFrame but you have to deal with the insets and 
>> title bar your self.  If you use an undecorated frame you might as well 
>> use a window.
> 
>     Right, the insets are a bit of a pain, and I sometimes wish JFrame had 
> a setSizeWithoutInsets() method in addition to its standard setSize() 
> method. But I *do* want a decorate frame (at least when the game is not 
> running in full screen mode), so that the player can move the window 
> around and resize it. Of course, if the player switches the game to 
> fullscreen mode, then at that point, the window should be undecorated.

I haven't used full screen exclusive mode much because most of what I 
have been working on uses multiple monitors and still requires user 
input.  That won't work with FSEM.  I don't know if it is possible to 
remove the decoration on a window once it has been created or not.

>> Below is how I would do it.  See if you like any of it.
> [informative code snipped]
> 
>     Thanks. You introduced me to the WindowAdapter class, and it looks 
> like I'll have to re-read the javadocs for BufferStrategy (since it's not 
> clear to me what you're trying to do with your calls to contentsLost() and 
> contentsRestored()) It looks like it has something to do with losing image 
> data due to the use of VolatileImage, but I'm wondering whether it's 
> necessary for my game: In most of my games, so much is going on screen at 
> any one point that I typically assume that the whole image is always lost 
> and redraw the whole frame from scratch. The game is implemented as a 
> state machine (not illustrated in the simplified version of the code I've 
> been posting), and any state which needs access to previous frame contents 
> (e.g. for a motion-blurring effect) needs to implement storing previous 
> image data itself.

Even a blit buffer strategy can use a VolatileImage as the back buffer. 
  If you read the docs for VolatileImage you will see what I'm up to 
there.  That code is directly out of the docs.

>     Here's yet another iteration of my design, taking into account your 
> advice of pulling the active-drawing code out of the EDT. I've had to 
> reintroduce the synchronize() statements, because otherwise it's possible 
> that the user might close the window (and thus invalidate the graphics 
> object) while I'm still using the graphics object to render the next 
> frame. I've added a Thread.sleep(1000) statement to increase the 
> likelihood of this race condition occurring for illustrative purposes.
> 
>     I've also used your WindowAdapter (and KeyAdapter) trick.
> 
> <SSCCE>
> import java.awt.Color;
> import java.awt.Dimension;
> import java.awt.EventQueue;
> import java.awt.Graphics2D;
> import java.awt.Insets;
> import java.awt.event.KeyAdapter;
> import java.awt.event.KeyEvent;
> import java.awt.event.WindowAdapter;
> import java.awt.event.WindowEvent;
> import java.awt.geom.AffineTransform;
> import java.awt.image.BufferStrategy;
> import java.lang.reflect.InvocationTargetException;
> 
> import javax.swing.JFrame;
> 
> public class Test {
> 
>  private static final int DEFAULT_RENDERING_WIDTH = 640;
>  private static final int DEFAULT_RENDERING_HEIGHT = 480;
>  private static JFrame mainWindow;
>  /**
>   * Returns true if the game is in the process of shutting down and 
> quitting.
>   * If it's in the middle of a game-loop iteration, it'll finish that
>   * iteration (potentially drawing 1 more frame of animation, and then 
> quit.
>   */
>  private static boolean timeToQuit = false;
>  /**
>   * True if the player pressed the "action" key since the last iteration
>   * through the game loop, false otherwise. In a real game, I'd probably 
> have
>   * many of these booleans (e.g. an "up" key, a "down" key, etc., and move 
> it
>   * into a seperate class for organization purposes.
>   */
>  private static boolean pressedActionKey = false;
> 
>  public static void main(String[] args) throws InterruptedException,
>    InvocationTargetException {
>   /*
>    * This first invokeAndWait initializes all the GUI components, and
>    * registers the appropriate listeners to hook into the main game
>    * engine.
>    */
>   EventQueue.invokeAndWait(new Runnable() {
>    @Override
>    public void run() {
>     mainWindow = new JFrame("My game");
>     mainWindow.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
>     mainWindow.setIgnoreRepaint(true);
>     mainWindow.addWindowListener(new WindowAdapter() {
>      @Override
>      public void windowClosing(WindowEvent e) {
>       timeToQuit = true;

You are already in the EDT here, invokeLater is not needed.

>       EventQueue.invokeLater(new Runnable() {
>        @Override
>        public void run() {
>         synchronized (mainWindow) {
>          mainWindow.dispose();
>          mainWindow = null;
>         }
>        }
>       });
>      }
>     });
>     mainWindow.addKeyListener(new KeyAdapter() {
>      @Override
>      public void keyPressed(KeyEvent e) {
>       if (e.getKeyCode() == KeyEvent.VK_SPACE) {
>        pressedActionKey = true;
>       }
>      }
>     });
>     mainWindow.setPreferredSize(new Dimension(
>       DEFAULT_RENDERING_WIDTH, DEFAULT_RENDERING_HEIGHT));
>     mainWindow.pack();
>     mainWindow.setVisible(true);
>     mainWindow.createBufferStrategy(2);
>    }
>   });
>   /*
>    * This while loop is the main game loop. It basically iterates through
>    * 3 stages forever: getting the player input, reacting to it, and
>    * drawing the results on screen.
>    */
>   while (!timeToQuit) {
>    getPlayerInput();
>    processGameLogic();
>    synchronized (mainWindow) {
>     if (mainWindow != null) {
>      Thread.sleep(1000);
>      BufferStrategy strategy = mainWindow.getBufferStrategy();
>      Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
>      Insets insets = mainWindow.getInsets();
>      g.translate(insets.left, insets.top);
>      g.setTransform(AffineTransform.getScaleInstance(
>        (double) (mainWindow.getWidth() - insets.right)
>          / (double) DEFAULT_RENDERING_WIDTH,
>        (double) (mainWindow.getHeight() - insets.bottom)
>          / (double) DEFAULT_RENDERING_HEIGHT));
>      updateScreen(g);
>      g.dispose();
>      strategy.show();
>     }
>    }
>    Thread.sleep(1);
>   }
>  }
> 
>  public static void processGameLogic() {
>   /*
>    * Check if pacman touched a ghost, stuff like that. Doesn't touch any
>    * Swing components.
>    */
>  }
> 
>  public static void getPlayerInput() {
>   /*
>    * Doesn't touch any Swing components.
>    */
>   if (pressedActionKey) {
>    /* make mario jump */
>    System.out.println("Mario jumps.");
>    pressedActionKey = false;
>   }
>  }
> 
>  public static void updateScreen(Graphics2D g) {
>   g.setColor(Color.BLACK);
>   g.fillRect(0, 0, 640, 480);
>   /*
>    * TODO: Draw all sorts of amazing eye-candy.
>    */
>  }
> }
> </SSCCE>
> 
>     - Oliver 

Try it and see how it works.  Post something that has some action!

If you are interested in writing some C timing code for me, let me know.

-- 

Knute Johnson
email s/nospam/knute/

---
 * Synchronet * The Whitehouse BBS --- whitehouse.hulds.com --- check it out free usenet!
--- Synchronet 3.15a-Win32 NewsLink 1.92
Time Warp of the Future BBS - telnet://time.synchro.net:24

[toc] | [prev] | [next] | [standalone]


#1283 — Re: Requesting tips, comm

From"Oliver Wong" <oliver.wong@THRWHITE.remove-dii-this>
Date2011-04-27 15:32 +0000
SubjectRe: Requesting tips, comm
Message-ID<JASMh.10159$px1.174594@weber.videotron.net>
In reply to#1280
  To: comp.lang.java.gui
"Knute Johnson" <nospam@rabbitbrush.frazmtn.com> wrote in message 
news:OrGMh.112450$BK1.4200@newsfe13.lga...
> Oliver Wong wrote:
>> "Knute Johnson" <nospam@rabbitbrush.frazmtn.com> wrote in message 
>> news:44AMh.104676$BK1.87802@newsfe13.lga...
>>> For active rendering I would take your rendering out of the EDT 
>>> altogether.  The problem that you are going to run into is that 
>>> eventually too much will be happening on the EDT and you won't be able 
>>> to do any user input processing or that it will delay your rendering.
>>
>>     This key piece of information is pretty central to my 
>> (mis?)understanding. I thought it's written that whenever you touch 
>> Swing components, you should do so from within the EDT. If that's the 
>> case, shouldn't you perform painting of JFrames and other widgets 
>> within the EDT?
>
> Unless you are trying to do high performance active rendering.  Then you 
> do your rendering in another thread.  The normal Swing component drawing 
> is disabled and you won't interfere with it at all.

    Okay, thanks.

[...]
>
> I don't have any real experience with XP timing but I would be very 
> curious to know if it is possible to make a JNI method that could sleep 
> very accurately.  I would like to find something that could be accurate 
> to 250 microseconds +- to try in my timing loop.  The accuracy of 
> Timer.schedule() is no where near that close.  If you ran the test code 
> I posted you can see the jitter caused by the timing errors.

    The library I use returns number of millisecond since the epoch, so 
you can't get sub-millisecond precision with it. Probably there are 
libraries out there that *do* provide sub-millisecond precision (and 
hopefully a corresponding accuracy), but I don't know of any.

>>     For example, I might have a scripting language which says "It 
>> should take the character 2 seconds to walk from point A to point B", 
>> and the engine will smoothly animate a sprite moving from point A to 
>> point B at the highest frame rate that the computer can handle.
>
> I think what you will find with that method is that your frame rate is 
> constantly changing as thread timing and system load changes.  But I 
> will be interested in your results.

    Yes, that's the case. The experience is "optimized" for the high end 
machines. With a fixed-delay animation, you have some frame rate cap (e.g. 
30fps), which all high end machines will be able to consistently produce, 
and there's a certain cut-off point after which slower machines will start 
to play the animation "too slowly", but will render every single frame. So 
for example, things like syncing the animation to music or sound track may 
become problematic.

    With my variable delay system, you have a frame rate bottom (e.g. 
24fps). If a machine is unable to consistently meet that 24fps rate, then 
every frame of the "minimal" 10 fps animation will play, but at a slower 
than intended speed (and thus have syncing issues once again). However, 
any machine which can easily produce the 10fps animation with cycles to 
spare will instead display an animation with the highest fps it can 
muster, correctly timed (e.g. perhaps at 60fps).

[...]
> I haven't used full screen exclusive mode much because most of what I 
> have been working on uses multiple monitors and still requires user 
> input.  That won't work with FSEM.  I don't know if it is possible to 
> remove the decoration on a window once it has been created or not.

    I just tested it, and you cannot remove the decoration once you've 
added it. I haven't used FSEM much either, because it's easier to debug 
your game if you can still access the Eclipse workbench. Looks like I'll 
have to rethink that part of the engine.

[...]
>>     mainWindow.addWindowListener(new WindowAdapter() {
>>      @Override
>>      public void windowClosing(WindowEvent e) {
>>       timeToQuit = true;
>
> You are already in the EDT here, invokeLater is not needed.
>
>>       EventQueue.invokeLater(new Runnable() {
>>        @Override
>>        public void run() {
>>         synchronized (mainWindow) {
>>          mainWindow.dispose();
>>          mainWindow = null;
>>         }
>>        }
>>       });
>>      }
>>     });

Thanks, I forgot about that.

[...]
>
> Try it and see how it works.  Post something that has some action!

    Okay, here's a version which animates a blue ball sort of like the 
code you posted:

<SSCCE>
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import java.lang.reflect.InvocationTargetException;

import javax.swing.JFrame;

public class Test {

 private static final int DEFAULT_RENDERING_WIDTH = 640;
 private static final int DEFAULT_RENDERING_HEIGHT = 480;
 private static JFrame mainWindow;
 /**
  * Returns true if the game is in the process of shutting down and 
quitting.
  * If it's in the middle of a game-loop iteration, it'll finish that
  * iteration (potentially drawing 1 more frame of animation, and then 
quit.
  */
 private static boolean timeToQuit = false;
 /**
  * True if the player pressed the "action" key since the last iteration
  * through the game loop, false otherwise. In a real game, I'd probably 
have
  * many of these booleans (e.g. an "up" key, a "down" key, etc., and move 
it
  * into a seperate class for organization purposes.
  */
 private static boolean pressedActionKey = false;
 static double x;
 static double y;
 static double deltaX = 3;
 static double deltaY = 3;

 public static void main(String[] args) throws InterruptedException,
   InvocationTargetException {
  /*
   * This first invokeAndWait initializes all the GUI components, and
   * registers the appropriate listeners to hook into the main game
   * engine.
   */
  EventQueue.invokeAndWait(new Runnable() {
   @Override
   public void run() {
    mainWindow = new JFrame("My game");
    mainWindow.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
    mainWindow.setIgnoreRepaint(true);
    mainWindow.addWindowListener(new WindowAdapter() {
     @Override
     public void windowClosing(WindowEvent e) {
      timeToQuit = true;
      synchronized (mainWindow) {
       mainWindow.dispose();
       mainWindow = null;
      }
     }
    });
    mainWindow.addKeyListener(new KeyAdapter() {
     @Override
     public void keyPressed(KeyEvent e) {
      if (e.getKeyCode() == KeyEvent.VK_SPACE) {
       pressedActionKey = true;
      }
     }
    });
    mainWindow.setPreferredSize(new Dimension(
      DEFAULT_RENDERING_WIDTH, DEFAULT_RENDERING_HEIGHT));
    mainWindow.pack();
    mainWindow.setVisible(true);
    mainWindow.createBufferStrategy(2);
   }
  });
  /*
   * This while loop is the main game loop. It basically iterates through
   * 3 stages forever: getting the player input, reacting to it, and
   * drawing the results on screen.
   */
  long lastIteration = System.currentTimeMillis();
  while (!timeToQuit) {
   getPlayerInput();
   {
    long now = System.currentTimeMillis();
    long elapsed = now - lastIteration;
    if (elapsed > 1000 / 24) {
     /* Don't do frame skipping when you drop below 24fps. */
     elapsed = 1000 / 24;
    }
    processGameLogic(elapsed);
    lastIteration = now;
   }
   synchronized (mainWindow) {
    if (mainWindow != null) {
     BufferStrategy strategy = mainWindow.getBufferStrategy();
     Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
     Insets insets = mainWindow.getInsets();
     g.translate(insets.left - 1, insets.top - 1);
     g
       .translate((double) (mainWindow.getWidth()
         - insets.right - insets.left)
         / (double) DEFAULT_RENDERING_WIDTH,
         (double) (mainWindow.getHeight()
           - insets.bottom - insets.top)
           / (double) DEFAULT_RENDERING_HEIGHT);
     updateScreen(g);
     g.dispose();
     strategy.show();
    }
   }
   Thread.sleep(1);
  }
 }

 public static void processGameLogic(long timeElapsed) {
  x += deltaX * timeElapsed / 10.0;
  y += deltaY * timeElapsed / 10.0;
  if (x > DEFAULT_RENDERING_WIDTH - 30) {
   deltaX = -Math.abs(deltaX);
  }
  if (x < 0) {
   deltaX = Math.abs(deltaX);
  }
  if (y > DEFAULT_RENDERING_HEIGHT - 30) {
   deltaY = -Math.abs(deltaY);
  }
  if (y < 0) {
   deltaY = Math.abs(deltaY);
  }
 }

 public static void getPlayerInput() {
  /*
   * Doesn't touch any Swing components.
   */
  if (pressedActionKey) {
   /* make mario jump */
   System.out.println("Mario jumps.");
   pressedActionKey = false;
  }
 }

 public static void updateScreen(Graphics2D g) {
  g.setColor(Color.BLACK);
  g.fillRect(0, 0, 640, 480);
  g.setColor(Color.BLUE);
  g.fillOval((int) x, (int) y, 30, 30);
 }
}
</SSCCE>

    Unfortunately, to keep it as a simple SSCCE, I'm not able to include 
the JNI library I'm using, so the timer resolution is pretty bad, which 
means the animation isn't as smooth as it could be. I also realized I had 
made a bug in the way I implemented insets and Graphics2D translation, 
which is fixed in this latest iteration, though the blue ball mysteriously 
seems to over-estimate the position of the bottom border, which I can't 
really explain...

>
> If you are interested in writing some C timing code for me, let me know.

    I'm not. I'm not a C programmer. The library I use I downloaded off of 
the internet: 
http://www.javaworld.com/javaqa/2003-01/01-qa-0110-timing_p.html

    - Oliver

---
 * Synchronet * The Whitehouse BBS --- whitehouse.hulds.com --- check it out free usenet!
--- Synchronet 3.15a-Win32 NewsLink 1.92
Time Warp of the Future BBS - telnet://time.synchro.net:24

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.java.gui


csiph-web