Java-Gaming.org    
Featured games (91)
games approved by the League of Dukes
Games in Showcase (579)
games submitted by our members
Games in WIP (500)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Timing: Decoupling rendering and logic updates  (Read 1874 times)
0 Members and 1 Guest are viewing this topic.
Offline mudman

Junior Member




Here we go again...


« Posted 2004-09-28 13:53:36 »

I have played around with some code from http://legion.gibbering.net/golem/tutorial_loop.html and got a working demonstration for the concept in Java Web Start:
http://www.javaengines.dk/test/TestTimer.jnlp.php shows 2 balls, both being updated at 4 fps but rendering as fast as possible.
1 ball is interpolated between last and next positions, the other isn't.
When you click on the screen the balls are set back a few pixels - notice that the first ball moves back instead of jumping there too, because of the interpolation.
I'll probably port to GAGETimer if System.currentTimeMillis won't cut the cake.
Please send comments or questions, I'm pretty new to this kind of timing myself and made this just to test the concept before using it in my own game.
(I figure I have to decouple now, because I want logic framerate as low as 8-10 fps to "eat" some of the network latency while still maintaining a smooth "feel" of the game)


TestTimer.java:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class TestTimer extends JPanel implements Runnable
{
      private Point aLast = new Point(0, 20);
      private Point aNext = new Point(aLast);

      private Point b = new Point(0, 50);

      private float percentWithinTick = 0f;

      public TestTimer()
      {
            addMouseListener(new MouseAdapter()
            {
                  public void mouseClicked(MouseEvent e)
                  {
                        aNext.x -= 30;
                        b.x -= 30;
                  }
            });

      }

      public synchronized void paintComponent(Graphics g)
      {
            System.out.println("paint pwt=" + percentWithinTick);
            g.clearRect(0, 0, getWidth(), getHeight());
            int ax = aLast.x + (int) (percentWithinTick * (aNext.x - aLast.x));
            int ay = aLast.y + (int) (percentWithinTick * (aNext.y - aLast.y));
            g.fillOval(ax, ay, 10, 10);

            g.fillOval(b.x, b.y, 10, 10);
            notifyAll();//done painting
     }

      public void logicUpdate()
      {
            System.out.println("Logic");
            aLast.x = aNext.x;
            aLast.y = aNext.y;
            aNext.x += 5;
            b.x += 5;
      }


      public synchronized void run()
      {
            long time0, time1;
            int numLoops;
            int TICK_TIME = 250;
            int MAX_LOOPS = 2;
            time0 = System.currentTimeMillis();
            while (true)
            {
                  time1 = System.currentTimeMillis();
                  numLoops = 0;
                  while ((time1 - time0) > TICK_TIME && numLoops < MAX_LOOPS)
                  {
                        logicUpdate();
                        time0 += TICK_TIME;
                        numLoops++;
                  }
                  percentWithinTick = (time1 - time0) / (float) TICK_TIME;
                  if (percentWithinTick > 1)
                        percentWithinTick = 1;
                  repaint();
                  try
                  {
                        //System.out.println(System.currentTimeMillis());
                       //Deal with the asynchronous call to repaint()
                       //duration to wait for shouldn't matter, but if
                       //paintComponent for some reason isn't called
                       //TICK_TIME is probably the best answer
                       wait(TICK_TIME);
                        //System.out.println(" " + System.currentTimeMillis());
                 } catch (InterruptedException e)
                  {
                        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                 }
            }
      }



      public static void main(String[] args)
      {
            final TestTimer timer = new TestTimer();
            JFrame f = new JFrame();

            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.getContentPane().add(timer, BorderLayout.CENTER);
            f.setSize(800, 600);

            JPanel p = new JPanel();
            JButton b = new JButton("Start");
            p.add(b);
            f.getContentPane().add(p, BorderLayout.SOUTH);
            b.addActionListener(new ActionListener()
            {
                  public void actionPerformed(ActionEvent e)
                  {
                        Thread t = new Thread(timer);
                        t.start();

                  }
            });
            f.setVisible(true);


      }
}
Offline Catharsis

Junior Member




EGR Software rocks!


« Reply #1 - Posted 2004-09-28 16:24:48 »

I've got a nice clocking package built into Typhon (http://typhon.egrsoftware.com/) that will be released when Auriga3D hits the net in December.

The clocking mechanism I created allows you to decouple scheduling on a single thread. In effect it is like psuedo-multitasking.   If the clock is running at 60hz subdivisions of 30hz, 20hz, 15hz, 12hz, 10hz, and 5hz are available, etc.  The clock is callback oriented so any "clockable" can be switched to a different subdivision dynamically.

Here are some notes and commentary from past presentations on it...
---------------------------------
Why have a scheduler at all?

Clock classification:
"Subdivided do while thread yield"

-Cycle of 12 due to common factors
and equal prime number spread.

-Other subdivision patterns are possible.

A scheduler is necessary for three main reasons. It is required for active rendering for graphics display, to run reoccurring events in Typhon, & to execute code off of the AWT Event Queue that is used by the AWT and Swing graphics APIs.

A scheduler or some way to execute code from GUI events off of the AWT queue is absolutely necessary for high performance Java applications. Most APIs that perform this task work with threads including Foxtrot and SwingWorker.
However, most real time applications (games/simulations) are designed for a single thread of execution.

With Typhon I take a different approach by having one main thread of execution that is driven by a subdivided do while thread yield scheduler.

Now, the "do while thread yield" part sounds a little suspicious as isn't that a spin wait? Well.. Yes and no... If no other processes need to CPU then Typhon will consume all of the CPU resources, but if any other threads or applications require some CPU time Typhon applications will immediately yield.  

This way it is possible to find a balance between scheduling events in Typhon and giving enough CPU time to other applications without being greedy. In fact this system works great while running a respectably high load in other software while Typhon applications are running.

The following slide will present some pseudo-code for the subdivided scheduler I am currently using.  

Next slide:
--------------------------------------
Method clockLoop:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
do {    
   ticks += tickSleep;    
   while (System.nanoTime() < ticks) Thread.yield();
   switch(++subdivideTicks)    
   {
       case SUB1:
          <handle add and remove>
          <handle pending clockable move events>
          <handle Subset 1 clockables>
       case SUB2:
          <handle Subset 2 clockables>
       case SUB3:
          <handle Subset 3 clockables>
       case SUB4:
          <handle Subset 4 clockables>
       case SUB5:
          <handle Subset 5 clockables>
          <handle abstract clockables 1>
       case SUB6:
          <handle Subset 6 clockables>
       case SUB7:
          <handle Subset 7 clockables>
          <handle pending clockable move events>
          <handle one time clockables>
       case SUB8:
          <handle Subset 8 clockables>
       case SUB9:
          <handle Subset 9 clockables>
       case SUB10:
          <handle Subset 10 clockables>
       case SUB11:
          <handle Subset 11 clockables>
       case SUB12:
          <handle Subset 12 clockables>
          subdivideTicks = -1
} while (clockLoopRunning)

Founder & Principal Architect; EGR Software LLC
http://www.typhonrt.org/
http://www.egrsoftware.com/
Offline Absolution

Senior Newbie




Java games rock!


« Reply #2 - Posted 2004-11-08 20:48:49 »

I was never a fan of that type of decoupling where you move your objects based on the last frame.  I prefer to just specify a number of updates per second and in my main game loop I just do logic processing when I reach that update time.  I basically just keep a counter of how much time has passed and when it adds up to my specified update time I update the logic, reset the counter, and make sure to keep track of any overflowed time.  Of course that all requires a high performance, reliable timer, but it has the added benifit of making things like demos/networking much easier.  There is a gamasutra article on this type of timing I believe.  It's an old one.  For older java I just using thelorax's timer to achieve a similar effect (i.e. you set the delay you want, sync your main loop to the timer, and update when it notifies).
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

xsi3rr4x (31 views)
2014-04-15 18:08:23

BurntPizza (28 views)
2014-04-15 03:46:01

UprightPath (43 views)
2014-04-14 17:39:50

UprightPath (26 views)
2014-04-14 17:35:47

Porlus (43 views)
2014-04-14 15:48:38

tom_mai78101 (64 views)
2014-04-10 04:04:31

BurntPizza (124 views)
2014-04-08 23:06:04

tom_mai78101 (224 views)
2014-04-05 13:34:39

trollwarrior1 (190 views)
2014-04-04 12:06:45

CJLetsGame (198 views)
2014-04-01 02:16:10
List of Learning Resources
by SHC
2014-04-18 03:17:39

List of Learning Resources
by Longarmx
2014-04-08 03:14:44

Good Examples
by matheus23
2014-04-05 13:51:37

Good Examples
by Grunnt
2014-04-03 15:48:46

Good Examples
by Grunnt
2014-04-03 15:48:37

Good Examples
by matheus23
2014-04-01 18:40:51

Good Examples
by matheus23
2014-04-01 18:40:34

Anonymous/Local/Inner class gotchas
by Roquen
2014-03-11 15:22:30
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!