Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (498)
Games in Android Showcase (115)
games submitted by our members
Games in WIP (562)
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  
  Learning Class : Applet  (Read 4338 times)
0 Members and 1 Guest are viewing this topic.
Offline Gudradain
« Posted 2010-12-10 07:51:20 »

So here is the first entry in the learning class!!

Subject : What is your core applet code? The essential part that you have in every applet when you make a game.

Description

The essential parts of my applet are the following elements :

- start() method creating the canvas, the bufferstrategy and the input listeners as well as starting the gameloop.
- a gameloop.
- a couple of input listeners (MouseListener, KeyListener, etc.).
- render(Graphics2D g) method to draw on the canvas.
- update(int deltaTime) method to update the game state.

Code

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  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
import java.applet.Applet;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.image.BufferStrategy;

public class BasicApplet extends Applet implements Runnable{
   
   private final int width = 800;
   private final int height = 600;

   Canvas canvas;
   BufferStrategy bufferStrategy;
   Thread gameloopThread;
   
   @Override
   public void init(){
      canvas = new Canvas();
      canvas.setBounds(0, 0, width, height);
      add(canvas);
      canvas.setIgnoreRepaint(true);

      canvas.createBufferStrategy(2);
      bufferStrategy = canvas.getBufferStrategy();

      canvas.addMouseListener(new MouseControl());
      canvas.addMouseMotionListener(new MouseControl());
      canvas.requestFocus();
   }
   
   @Override
   public void start(){
      gameloopThread = new Thread(this);
      gameloopThread.start();
   }
   
   @Override
   public void stop(){
      setRunning(false);
      try {
         gameloopThread.join();
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }
   
   private class MouseControl extends MouseAdapter{
     
   }
   
   private long desiredFPS = 60;
        private long desiredDeltaLoop = (1000*1000*1000)/desiredFPS;
   
   private boolean running = true;
   
   private synchronized void setRunning(boolean running){
      this.running = running;
   }
   
   private synchronized boolean isRunning(){
      return running;
   }
   
   public void run(){
     
      setRunning(true);
     
      long beginLoopTime;
      long endLoopTime;
      long currentUpdateTime = System.nanoTime();
      long lastUpdateTime;
      long deltaLoop;
     
      while(!isActive()){
         Thread.yield();
      }
      while(isRunning()){
         beginLoopTime = System.nanoTime();
         
         render();
         
         lastUpdateTime = currentUpdateTime;
         currentUpdateTime = System.nanoTime();
         update((int) ((currentUpdateTime - lastUpdateTime)/(1000*1000)));
         
         endLoopTime = System.nanoTime();
         deltaLoop = endLoopTime - beginLoopTime;
           
           if(deltaLoop > desiredDeltaLoop){
               //Do nothing. We are already late.
          }else{
               try{
                   Thread.sleep((desiredDeltaLoop - deltaLoop)/(1000*1000));
               }catch(InterruptedException e){
                   //Do nothing
              }
           }
      }
   }

   private void render() {
      try{
         Graphics2D g = (Graphics2D) bufferStrategy.getDrawGraphics();
         g.clearRect(0, 0, width, height);
         render(g);
         bufferStrategy.show();
         g.dispose();
      }catch(Exception e){
         e.printStackTrace();
      }
   }
   
   /**
    * Overwrite this method in subclass
    */

   protected void update(int deltaTime){

   }
   
   /**
    * Overwrite this method in subclass
    */

   protected void render(Graphics2D g){

   }

}


Notes

[Warning] There might be some tearing with the rendering.
Offline cylab

JGO Ninja


Medals: 49



« Reply #1 - Posted 2010-12-10 09:28:52 »

Thanks for sharing! But please update the code to better support the Applet Lifecycle and implement all methods for best robustness.

For example the start-Method() might be called multiple times, so you should not simple spawn a new threads there (you might get multiple instances running in parallel). Better check, if the thread was already spawned and only wake it up. Also you should include means to suspend the rendering on stop() and do clean up in destroy().

Alternatively (e.g. to circumvent browsers that don't pass correct notifications to the applet) you could simple kill and clean up in stop() and destroy() and restart the applet in start(). But you also need to check if the Thread is already there and kill it before spawning a new one.

Mathias - I Know What [you] Did Last Summer!
Offline Gudradain
« Reply #2 - Posted 2010-12-10 20:10:57 »

Thanks for sharing! But please update the code to better support the Applet Lifecycle and implement all methods for best robustness.

For example the start-Method() might be called multiple times, so you should not simple spawn a new threads there (you might get multiple instances running in parallel). Better check, if the thread was already spawned and only wake it up. Also you should include means to suspend the rendering on stop() and do clean up in destroy().

Alternatively (e.g. to circumvent browsers that don't pass correct notifications to the applet) you could simple kill and clean up in stop() and destroy() and restart the applet in start(). But you also need to check if the Thread is already there and kill it before spawning a new one.

Hello, thank you for the feedback. I had already read the applet the applet section in the past and some of my design decision were based on it. But it's always good to refresh your memory. In the "methods" page, it said the following about the start method :

Quote
"The start method starts the execution of the applet. It is good practice to return quickly from the start method. If you need to perform computationally intensive operations it might be better to start a new thread for this purpose."

That's why I'm starting a new thread in the start method. Although, I agree with you that I need some means to kill a thread. I did some testing and I found out that effectively my thread continue to run after the stop method of the applet is called. But, the thread is then killed by the code :

1  
2  
3  
if(!isActive()){
        return;
}


I could possibly replace this code by overriding the stop method with the following code, which would nearly do the same.

1  
2  
3  
4  
@Override
public void stop(){
   running = false;
}


But the following code could produce some synchronization problem if the 2 threads access the running variable at the same time. So a safer version would look like it.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
@Override
public void stop(){
   setRunning(false);
}
 private synchronized void setRunning(boolean running){
   this.running = running;
}
private synchronized boolean isRunning(){
   return running;
}


In all case, the thread will stop to run (and in the process destroy itself because it is no longer in use). So starting a new one shouldn't be a problem. But which version is better? Or any other suggestion.

Another thing, that I could/should? change is putting the initialization code in the init() method instead of the start method. I don't think it does any difference but it might be good practice.

Btw, there is one thing that I don't understand. How can an applet call the start method more than once? I was sure that the methods were always called in order (init(), start(), stop() and destroy())
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline cylab

JGO Ninja


Medals: 49



« Reply #3 - Posted 2010-12-10 20:46:25 »

Stop is called to suspend an applet (for example when minimizing the Browser or switching to another tab) and Start is called again to wake it up

Mathias - I Know What [you] Did Last Summer!
Offline Gudradain
« Reply #4 - Posted 2010-12-10 22:12:40 »

Stop is called to suspend an applet (for example when minimizing the Browser or switching to another tab) and Start is called again to wake it up

Sorry, but I'm unable to reproduce this behavior. I tried it both in chrome and internet explorer. You can test it real quick with the applet on this page.

Applet life cycle

The only things that call the stop method are "Leaving and Returning to the Applet's Page", "Reloading the Applet" and "Quitting the Browser" as it is explain on the Applet life cycle page.

I heard it many times that stop was call when you change tab but it never did it for me. Was it like that in the past?
Offline DzzD
« Reply #5 - Posted 2010-12-11 06:07:09 »

Applet life cycle have been completly broken for a while (it always have been), not sure it is really correct now

as far as I know it look something like :
init => called once before all other method
start => may be called more than one time, every time applet have to resume from a previous stop and when applet start first time
stop=> may be called more than one time, every time applet have to pause itself and once before applet destroy (browser reduced, but also sometime when going to another web page, and then start is called when returning )
destroy => once when applet is being destoryed

only sure is that init  always happen and also start (at least once)

in my experience, the Official Applet life cycle is not reliable, may be it is now with the new plugin ?

@Guardian:
 you can simulate pause/resume using Applet viewer but other software may display Applet, that's why stop/start should be considered as pause/resume

Offline cylab

JGO Ninja


Medals: 49



« Reply #6 - Posted 2010-12-11 08:32:38 »

Thanks dzzd. I should have made this clearer before. Bottom line is, you may not know, so make it as robust as possible even for unexpected behavior. There have been loads of problems with applets, especially with multiple invocations and multiple threads.

Mathias - I Know What [you] Did Last Summer!
Offline Gudradain
« Reply #7 - Posted 2010-12-11 18:01:17 »

Ok we all agree that nobody really know how work an applet and that we should use "best practice" when we make an applet.

But I would need some help to see what an applet using best practice looks like. What's your applet?
Offline Gudradain
« Reply #8 - Posted 2010-12-13 05:18:25 »

Alright, I made the change in my applet. Now the stop method make sure that the gameloop thread finish before exiting. So there is no way that there can be more than 2 threads running simultaneously.

I update the code in the first post.

Btw, thx for the help.
Offline Conard

Junior Newbie





« Reply #9 - Posted 2011-02-06 15:35:22 »

Nice code, thanks for sharing.

Small question about the delta timing. If I understand correctly, you calculate how much time the render method took and pass that to the update method in milliseconds. What should I do with it in my update method? I tried using it to determine how much to update, but isn't the Thread.sleep already enforcing a steady frame rate?

I'm quite new to Java btw, so thanks for helping me out  Wink
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Gudradain
« Reply #10 - Posted 2011-02-06 18:52:17 »

Nice code, thanks for sharing.

Small question about the delta timing. If I understand correctly, you calculate how much time the render method took and pass that to the update method in milliseconds. What should I do with it in my update method? I tried using it to determine how much to update, but isn't the Thread.sleep already enforcing a steady frame rate?

I'm quite new to Java btw, so thanks for helping me out  Wink


No the Thread.sleep is not constant and is not reliable. It's better to use the deltatime between 2 updates.
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #11 - Posted 2011-02-07 14:38:32 »

Nice code, thanks for sharing.

Small question about the delta timing. If I understand correctly, you calculate how much time the render method took and pass that to the update method in milliseconds. What should I do with it in my update method? I tried using it to determine how much to update, but isn't the Thread.sleep already enforcing a steady frame rate?

I'm quite new to Java btw, so thanks for helping me out  Wink


You can use it in your updates. Just subtract it from the time *now*, and if that number is over 2 seconds - update!
If not, just add it to an int, which you add to the calculation above. You now have a 2-second-interval update  Cheesy

Offline Conard

Junior Newbie





« Reply #12 - Posted 2011-02-07 15:42:50 »

Ah, thanks guys. Had some trouble understanding the delta timing, but got it now. Game's running smooth and consistent Cool
Offline ra4king

JGO Kernel


Medals: 346
Projects: 3
Exp: 5 years


I'm the King!


« Reply #13 - Posted 2011-02-08 22:47:09 »

It is easy to test how to lifecycle works these days using println and looking at the console.
In my tests with Google Chrome, init() is treated like start()! Every time I refresh or close and reopen the tab init() is called then start().
stop() is called as soon as i refresh or close the tab and destroy() is called a while later when the JVM decides to shutdown.

Hope this helped Smiley

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.

BurntPizza (25 views)
2014-09-21 02:42:18

BurntPizza (16 views)
2014-09-21 01:30:30

moogie (19 views)
2014-09-21 00:26:15

UprightPath (26 views)
2014-09-20 20:14:06

BurntPizza (29 views)
2014-09-19 03:14:18

Dwinin (44 views)
2014-09-12 09:08:26

Norakomi (74 views)
2014-09-10 13:57:51

TehJavaDev (100 views)
2014-09-10 06:39:09

Tekkerue (50 views)
2014-09-09 02:24:56

mitcheeb (71 views)
2014-09-08 06:06:29
List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
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!