Graphics and Threads

ok i have this game where you pick the angle and power and then shoot. as is i draw things in the run statement. however i think im going about this all wrong. for example when the ball hits the ground i would like to draw an explosion. is there a good way to get different animations to pop up when things happen? perhaps make them their own threads? for example when space bar is pressed stop current thread and start thread that draws the flight of the ball? then when it hits ground stop that thread and start one that draws exposion?

thanks for any help…

here is the code i have …

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;

public class Test extends Applet implements Runnable, KeyListener {
    int x = 0;
    int y = 500;
    int z = 0;
    int q = 44;
    int d=0, p =0;
    
    double xfloat = 0;
    double yfloat = 0;
    double vx = 0,vy = 0;
    double angle = 0;
    double power = 0;
    final double gravity = 0.3;
    double pi = 2*Math.PI;
    
    Thread th = new Thread(this);
    BufferedImage offscreen = new BufferedImage(1000,600,BufferedImage.TYPE_INT_RGB);
    Graphics2D bufferGraphics = offscreen.createGraphics();
    boolean keys[];
    boolean paused;
    Image sprite[] = new Image[30];
    shot shoot = new shot();

    
    public void init() {
        setBackground(Color.white);
        addKeyListener(this);
        keys = new boolean[65536];
        th.start();
        Characters create = new Characters();
        for (int i = 0; i < 30; i++)
        {
            sprite[i] = create.createBSprite(q);
            q--;
        }
        
        paused = false;
    }

    public void start() {
        paused = false;
    }
    
    public void stop() {
        paused = true;
    }

    public void keyTyped(KeyEvent e) {
    }

    public void keyReleased(KeyEvent e) {
        keys[e.getKeyCode()] = false;
    }

    public void keyPressed(KeyEvent e) {
        keys[e.getKeyCode()] = true;
    }

    //override paint and update to stop any system rendering
  
    public void paint(Graphics g) {}
    public void update(Graphics g) {}

    public void run() {
        while (th != null) {
            //game loop
            if(!paused) {
                Graphics2D g = (Graphics2D)getGraphics();
                //game logic
                
                xfloat += vx;
                yfloat += vy;
                vy += 0.3;
                
                
                if(keys[KeyEvent.VK_RIGHT] && x < 374) {
                    x++;
                }
                if(keys[KeyEvent.VK_LEFT] && x > -3) {
                    x--;
                }
                
                if(keys[KeyEvent.VK_UP] && z < 15)  {
                    z++;
                    angle += 5;
                }
                
                if(keys[KeyEvent.VK_DOWN] && z > 0) {
                    z--;
                    angle -= 5;
                }
               
                
                if(keys[KeyEvent.VK_SPACE] ) {
                    d = 10;
                    p = 10;
                    xfloat = x+63;
                    yfloat = y+20;
                    power += 2;
                    vx = (power / 10.0) * (float)Math.cos((pi * (double)angle) / 360.0);
                    vy = -((power / 10.0) * (float)Math.sin((pi * (double)angle) / 360.0));
                    
                } 
                
                
                if (xfloat > 450 && xfloat < 550 && yfloat > 280 && yfloat < 290)
                     vy = -((power / 15.0) * (float)Math.sin((pi * (double)angle) / 360.0));
                
                if(yfloat > 600 || xfloat > 1000)
                    power = 0;
                    
                if(yfloat > 564)
                {
                   d = 0;
                   p = 0;
                }
                    
                
                
                //rendering
                bufferGraphics.setColor(Color.WHITE);
                bufferGraphics.fillRect(0, 0, getWidth(), getHeight());
                bufferGraphics.setColor(Color.red);
                bufferGraphics.fillRect(0,300,30,300);
                bufferGraphics.fillRect(450,300,100,300);
                bufferGraphics.setColor(Color.green);
                bufferGraphics.fillRect(0,565,1000,35);
                bufferGraphics.setColor(Color.magenta);
                bufferGraphics.fillOval((int)xfloat,(int)yfloat,d,p);
                bufferGraphics.setColor(Color.blue);
                bufferGraphics.fillRect(0,0,(int)power,10);
                bufferGraphics.drawImage(sprite[z], x, y,this); 
                
                g.drawImage(offscreen,0,0,this);
                g.dispose();
                
                
                

                
                try {
                    Thread.sleep(20);
                }
                catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
            else {
                try {
                    Thread.yield();
                    Thread.sleep(100);
                }
                catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
        }
        
    }
    
    

}

class Characters
{
 BufferedImage createBSprite(int x)
        {
            BufferedImage sprite = new BufferedImage(100,100,BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2D = sprite.createGraphics();
            g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
            g2D.fillRect(0,0,100,100);
            g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
            g2D.setColor(Color.black);
            //head
            g2D.fillOval(42,20,17,16);
            //neck
            g2D.fillRect(48,35,7,4);
            //body
            g2D.fillArc(42,36,17,16,0,180);
            g2D.fillRect(43,44,16,10);
            //left leg and foot
            g2D.fillRect(44,54,3,8);
            g2D.fillRect(44,62,6,3);
            //right leg and foot
            g2D.fillRect(55,54,3,8);
            g2D.fillRect(55,62,6,3);
            //right arm
            //g2D.fillRect(58,44,10,3);
            //g2D.fillOval(64,41,12,8);
            g2D.drawLine(58,44,68,x);
            g2D.drawLine(58,45,68,x+1);
            g2D.drawLine(58,46,68,x+2);
            //left arm
            g2D.fillRect(36,44,8,3);
            g2D.fillOval(32,43,5,5);
            return sprite;
        
        }
        
       
    }



I’m pretty sure that spawning new threads for animation purposes is bad idea. The least you’ll have - dozens of synchronization issues and deadlock headaches.

To simplify development a bit create separate class that will hold data that is needed for animation (i.e. list of frames, current frame, time till next frame). Then update your animation based on time passed since last update.

[quote]is there a good way to get different animations to pop up when things happen?
[/quote]
Try implementing Observer pattern. Create a list of listeners, then notify them of certain action happened.

http://www.java-gaming.org/index.php/topic,19276.0.html

Don’t ask the same question (or at least near the same question) twice.

And no, threads are a very very bad idea. They have a very high overhead on creation and you will need a lot of animations. Just have good encapsulation and individual timers that are dependent upon the main timer (i.e. pass the delta time to each animation so it knows how much to update).

Threads are a good idea! The problem is that people don’t know how to use them. I’m currently developing a game for advanced software engineering (postgrad) and I have to use Agent Oriented Software Engineering, to put it simply everything is in a thread.

Use executors.
Use atomic variables/data structures.
Create your own lock-free algorithms (if you have to, but this can be very difficult).

The easiest way of doing things is what the other suggest, but if you have the time and want to learn what every software developer will need to know in the next decade then go with a proper threaded model.

I wasn’t saying that multi threading itself is bad; of course it is an incredibly important idea to know and technique to use.

I was saying that using a different thread for every single animation you have is a horrible misuse of threads and definitely not the best way to do an animation.

I second that - this would be one of the worst possible uses of threads, since the frame updates in each animation all have to happen in lock-step. You’d end up spending far more time figuring out how to make that happen right than it would take to just set up an extremely simple timed animation handler.

If you’re getting to a point where you have thousands of these things and you really need to spread them out over processors, that’s when you would consider multi-threading, but it doesn’t sound like that’s what’s going on.

A very simple example of how to do this the way people are suggesting would be to have an Animation interface, something like:


public interface Animation{
  public void update(float deltaT);
  public void draw(Graphics g);
  public boolean isFinished(); //return true if the animation is done and should be removed
}

which you could implement as needed for each type of animation. Then in your main loop handling class you just hold onto a list of animations. Here’s some sample code (untested) that should give you an idea how to handle things:


private List<Animation> animations;

//this next bit would be in your main loop's update method
for (Animation animation:animations) {
  animation.update(deltaT);
}

//clean up the finished animations...there are probably better
//and faster ways to do this, but this is the first way that pops
//to mind that doesn't require me to actually test the code first. :)
Animation killMe = null;
do {
  killMe = null;

  //find an animation that is finished...
  for (Animation animation:animations) {
    if (animation.isFinished()) {
      killMe = animation;
      break;
    }
  }

  //...and remove it
  if (killMe != null) animations.remove(killMe);
} while (killMe != null); //repeat until there are no more finished animations in the list


//this would go in the draw method
for (Animation animation:animations) {
  animation.draw(g);
}

There are probably ways to better design this (you can probably turn it into several hundred lines of code by properly GoF pattern-izing it, YAY!), but IMO unless you’re trying to create a framework, just go with something simple that works.

ok im really new to this so thank you for the code.

[quote]Then in your main loop handling class you just hold onto a list of animations.
[/quote]
sorry if this is elementary but can we break it down. so each animation is what? and array of images?
and how do you declare each separate animation? so far i have just drawn everything in the run method.
sounds like each separate animation is its own class?

An animation could be an array of images if you wanted, or it could be some sort of movie file or GIF or sprite sheet or something like that.

At your level of knowledge, just have an animation represented by multiple images, then make your animation an array of them. Have you dealt with creating classes and objects before? If not, I suggest reading up on that before trying to continue.


public class Animation
{
     private Image[] frames;
     private float length;
     private float timeStarted;

     public Animation(String[] refs, float lengthInMilliseconds)
     {
          try
          {
               for (int i = 0; i < refs.length; i++)
                    frames[i] = ImageIO.readImage(new File(refs[i]));
          }
          catch (Exception e)
          {
               System.err.println("There was a problem loading one of the references.");
               e.printStackTrace();
          }

          length = lengthInMilliseconds;
     }

     public void draw(Graphics g, int x, int y)
     {
          int frameNumber = Math.min(frames.length-1, ((System.currentTimeMillis() - timeStarted) / length) * (frames.length-1));

          g.drawImage(frames[frameNumber],x,y,null);
     }

     public void start()
     {
          timeStarted = System.currentTimeMillis();
     }

     public bool isDone()
     {
          return System.currentTimeMillis() - timeStarted >= length;
     }
}

The above is a very simple way you might implement an animation class, rather than an interface as ewjordan showed above. Really I think how you want to do it is based on your needs - if an animation is more of an abstract thing and does not necessarily entail an array of images, go the interface route. If you know that every single animation is going to specifically need one implementation, go the class route. Also you should be able to see more of the guts of it above. I wouldn’t use System.currentTimeMillis(), by the way… instead your main loop should be periodically passing that information to your animation so that it is called only once per timestep.

Oh, and to declare animations:


//Declare your animations:
ArrayList<Animation> animations = new ArrayList<Animation>();
animations.add(new Animation(new String[]{"Images/Monkey0.jpg","Images/Monkey1.jpg","Images/Monkey2.jpg"},100));
animations.add(new Animation(new String[]{"Images/Dog0.jpg","Images/Dog1.jpg"},50));
// etc.

//Play them:
foreach (Animation anim in animations)
{
     if (anim.isDone())
          anim.start();
     anim.draw(g,xPos,yPos);
}

By the way, you basically restart the animation if you find that it was done from the last time you drew it. But there’s a bagillion other ways you could do that sort of thing, and many better (more complicated) ones. Also the above will permanently loop all the animations. To make them stop or whatever you could add new functionality, or you can start to get more complicated and make every drawn object contain its own list of animations, and then each animation has a certain priority, so standing would have a low priority whereas jumping would have a higher priority, meaning that jumping will play if it is asked to (i.e. you press a jump button) instead of standing, which is always played unless something else is (because it’s the default).

ok for some reason i keep getting ImageIO variable cannot be found on this line of code…

 frames[i] = ImageIO.readImage(new File(refs[i]));

im sure this is a noob mistake but i can’t figure it out.

It’s ImageIO.read(…), not ImageIO.readImage(…), I suspect that was just a typo in the code posted earlier. Make sure you’ve added “import javax.imageio.ImageIO;” to your imports, too, otherwise it still won’t work.

BTW, this seems like a good opportunity to suggest that you get to know and use Eclipse or Netbeans if you’re not doing so already (though I’ll only personally vouch for Eclipse), because in a good IDE a list of static member functions will pop up as soon as you type “ImageIO.”, and stuff like that makes it a lot easier to explore various APIs. I barely refer to actual Java documentation anymore because any properly Javadoc-ed library is almost trivial to poke around in and use once you know the base package name.

ok so now that i have declared everything i dont understand how i would call just say one animation and make it play through every frame once?

You do it just like I said before, sort of in the way detailed below. Basically, you want to use repaint().


public class MyPanel extends javax.swing.JPanel
{
     //Declare your list of animations so they can be accessed later.
     private java.util.ArrayList<Animation> animations;

     //In your constructor, create your animations.
     public MyPanel()
     {
          animations = new java.util.ArrayList<Animation>();
          animations.add(new Animation(new String[]{"Images/Monkey0.jpg","Images/Monkey1.jpg","Images/Monkey2.jpg"},100));
          animations.add(new Animation(new String[]{"Images/Dog0.jpg","Images/Dog1.jpg"},50));
          // etc.
     }

     //In your main loop, tell your panel to repaint itself.
     public void MainLoopl()
     {
          repaint();
          try {Thread.sleep(50);} catch(Exception e) {}
     }

     //In paint component (called every repaint), tell your animations to draw.
     public void paintComponent(java.awt.Graphics g)
     {
          //Make sure to do this or things will look screwed up.
          super.paintComponent(g);

          //Draw the animations and restart them if they're done.
          foreach (Animation anim in animations)
          {
               if (anim.isDone())
                    anim.start();
               anim.draw(g,xPos,yPos);
          }
     }
}

And yeah, the ImageIO thing from before was a typo. That code, along with the code above, comes from the top of my head and is specifically written here in the forum. As such I’m not bothering to compile it or anything, but it should give you an idea of what you’re doing.

ok i don’t think i entirely understand. so can i keep my original code that i posted in the first post? as it stands i draw everything in the run method which works fine but at certain points i would like to call an animation but then have it disappear completely once it has run through all of its frames. so do i have to modify the existing code or not?

No. In run() call repaint() and then put all your drawing paintComponent. This is always how you should handle drawing with Swing.

ok everything is working pretty good but i am having some problems reading my images in. first it takes forever to start applet…im guessing this is just java being slow loading images. second when the applet does start and i have loaded images if i try to draw them the applet either starts as a blank white screen or if i try to call an image with an if statement then the thread stops and the applet doesnt do anything.

what could cause this? any issues i should know about ImageIO?

Mmm, ImageIO isn’t so bad. It can definitely be improved, but it’s okay. What size / type are your images?

ummm they are gif images and they are 70x70 pixels… i can get them to load using getImage(getCodeBase,"…"); and they appear and stuff but i cannot use that outside the main class. ImageIO seems to load the images just fine but when they are displayed either something goes wrong or the images are blank and the size of the entire applet.

Oh, are you not storing them? You should only load them once, then store them in a map or list for subsequent access. If you make that collection global or you pass it around via parameters, then you can use it in other classes.

i would like to store them with the ImageIO.read method you wrote in the code above. however like i said i am having a problem getting them to display. should the animations class be nested in the applet class? as it stands its outside the applet class. if i call ImageIO from inside the applet class everything works fine. however if i use parameters to pass the file name to another class(nested or outside) as your example shows i cannot display the images.