Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (526)
Games in Android Showcase (127)
games submitted by our members
Games in WIP (593)
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  
  How to program updating animation?  (Read 2016 times)
0 Members and 1 Guest are viewing this topic.
Offline SCCY

Junior Newbie





« Posted 2010-12-31 07:55:16 »

Hello all,

I am a newbie in Java game development though I have been doing general java programming for quite some time now. This post is more of a question about software engineering for game development.

One of the things that I discover I need to do a lot during game development is animation. As I was making a few mini games, a question appears and got my attention - How should I program for the "updating" animation?

I was quite used to Model-View-Controller and making ordinary Swing applications. Traditionally with Swing application, when something happens in the "Model", I want the "View" to update straight away. Similarly, when I do something with the "Controller", I want "View" to response straight away.

However it's not quite the same in game development when animation involves. Sometimes, when something happens in model, the view does not update straight away, instead, it has to go through some animation before the view catches up with the model's latest status. I just don't know what is the best way to program the updates with animations... Some advice would be great here...

For example, I have a Hero object and a Monster object. The Hero object has a attack method that will shred away Monster's health. If Monster's health reaches zero, it dies. A traditional and simplest way would be:

Class Hero {
  ...
  void attack (Monster m) {
    m.health--;
    if (m.health <= 0) {
      m.die();
    }
  }
}

As you can see, everything happens within a fraction of a second... and there's no time to wait for the animation to finish.

My thoughts on inserting animation is to have multiple threads - one for the model and one for the animation... The animation thread is looped with timed sleep intervals like many game developers would do. The model thread get suspended when certain animations are playing:

Class Hero {
  ...
  void attack (Monster m) {
    playAttackAnimation(); // falls into an infinite while loop and wait for the animation to finish
    m.health--;
    if (m.health <= 0) {
      m.playDyingAnimation (); // falls into an infinite while loop and wait for the animation to finish
      m.die();
    }
  }
  void playAttackAnimation() {
    status = HERO_ATTACKING;
    while (status == HERO_ATTACKING) { // the infinite while loop and wait for the animation thread to update status to something else
    }
  }
}

For the animation thread:

while (...) {
  ...
  switch (hero.status) {
    case Hero.HERO_ATTACKING:
      renderHeroAttack(); // how this renders depends on the value of heroAttackFrame which acts like a frame counter for the animation
      heroAttackFrame++;
      if (heroAttackFrame >= heroAttackEndFrame) {
        hero.status = HERO_STANDING; // or any other status after ATTACKING
        heroAttackFrame = 0; // reset the attack frame counter back to zero
      }
  }
}

Personally I don't think using infinite while loop is a wise way of doing animation. However on the other hand, I don't think it's a very good idea either to finish everything in the model and then pause and wait for the animation to play and catch up the latest status of the model, i.e. finish running m.health--; and m.die(); before start calling playAttackAnimation(); and m.playDyingAnimation (); . (Of course, playAttackAnimation(); and m.playDyingAnimation (); are not just empty while loops now. They handle the animations here, so there is only one thread to handle both model and animation. ) Understand what I mean here? The main point of this post basically is about how and when I should animate the updates triggered by the model or the controller...

Surely I don't want to do this:

Class Hero {
  ...
  void attack (Monster m) {
    renderAttackFrame1();
    Thread.sleep(...);
    renderAttackFrame2();
    Thread.sleep(...);
    renderAttackFrame3();
    Thread.sleep(...);
    renderAttackFrame4();
    Thread.sleep(...);
    ...
    m.health--;

    renderAttackFrameX();
    Thread.sleep(...);
    renderAttackFrameY();
    Thread.sleep(...);
    renderAttackFrameZ();
    Thread.sleep(...);
    ...

    if (m.health <= 0) {
      m.die();
      m.renderDyingFrame1 ();
      Thread.sleep(...);
      m.renderDyingFrame2 ();
      Thread.sleep(...);
      m.renderDyingFrame3 ();
      Thread.sleep(...);
      ...
    }
  }
}


Any better suggestions from anyone?
Offline zoto

Senior Devvie


Medals: 4



« Reply #1 - Posted 2010-12-31 08:40:17 »

Swing apps use whats called passive rendering, basically the program doesn't bother to redraw unless there is a reason to. Passive rendering can work for some type of games but active rendering is much more common.
Active rendering redraws at set time intervals, 60 frames per second is a very common speed. If you use active rendering doing animations becomes very easy.
I would recommend checking out the basic game loop in the tutorials section and the book Killer game Programming in Java to get started.

As a side note if you post code on the forum put it in the code brackets.

Offline Nate

« JGO Bitwise Duke »


Medals: 158
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #2 - Posted 2010-12-31 14:26:12 »

Creates states for objects in your model, track the time each object has been in its current state, change states for whatever events you like (object took damage, object has been in state X for Y milliseconds, etc), have your view display the appropriate animation for an object's state, using the state time to determine what frame to display.

Don't use multiple threads. Use int counters and increment or decrement them by the time elapsed since last frame.

Say your monster attacks, 300ms later, the target gets hit:

1  
2  
3  
4  
5  
6  
7  
monster.setState(attacking)
game.addTimer(new Timer(300) {
   void execute () {
       target.hp -= monster.attackDamage;
       target.setState(target.hp <= 0 ? dead : hit);
   }
});

The view goes to display the monster. It chooses the attack animation, and plays the attack frames based on the state time. The game decrements the timer's 300ms counter until it <= 0, then calls execute. The view displays the target using the dead or hit animation, appropriately. You have a clear separation between model and view.

Note that is not java.util.Timer, it is some class you write. You also write the game code to manage timers.

That was a somewhat bad example, as you don't generally want to allocate in your game loop. But worry about that when it actually becomes a problem.

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline fireside

Senior Newbie





« Reply #3 - Posted 2010-12-31 15:49:56 »

You have to get used to working from the main game loop, especially with multiple entities doing animations.  You use states, as already posted, but those states are called from the main loop and only deliver the next frame of animation each loop, so you are updating all entities at once.  I keep them in a collection and then cycle through and update.  The update can use a main timer or just update each game loop, depending.  Threading just causes a lot of unnecessary complications in my  experience.  This way, you update all entities and then render and you know there isn't a large chunk of code getting executed each loop.  The game loop is the one and only loop needed.  That's an exaggeration, but I try to use it as much as possible.
Offline loom_weaver

JGO Coder


Medals: 17



« Reply #4 - Posted 2011-01-06 22:08:09 »

Hi there, I'm tackling the same issue and here's what I came up with.  In my game animations block the game state and I use passive rendering which leverages the EDT.

1. As other posters said, don't use multiple threads.

2. I have a swing timer that fires off about 5-20 times per second.  I also have a GameCanvas extends JPanel and overrides paint().

For each tick: {
    if (no animations to play) {
      advance the game state
    } else {
      process animations i.e. calculate what to paint and where
    }
    repaint();  // if necessary
}

GameCanvas.paint(Graphics2D g) {
    paint all mobiles
    foreach animation that is playing: animation.paint(g);
}

Advancing the game state might trigger an animation to play by adding it to the queue.  Note that animations and game state is not synced.  For example if the player attacks and kills a monster that happens instantaneously in the game state.  I then fire off an animation which will show the monster hitting the ground 500ms later.

I don't think it's possible to have them perfectly synced.  How do you know to play the death animation unless something is already dead?  Chicken and egg scenario!

I'm sure there are better ways but this works well in my case as my game is turn based and I use Swing controls for part of the UI.
Offline ReBirth
« Reply #5 - Posted 2011-01-29 01:06:11 »

For each tick: {
    if (no animations to play) {
      advance the game state
    } else {
      process animations i.e. calculate what to paint and where
    }
    repaint();  // if necessary
}

GameCanvas.paint(Graphics2D g) {
    paint all mobiles
    foreach animation that is playing: animation.paint(g);
}

I'm sure there are better ways but this works well in my case as my game is turn based and I use Swing controls for part of the UI.

what is that animation class? I mean, what inside it or extending what?
also, I think on active rendering "foreach animation that is playing" isnt necessary. In my approach
1  
2  
3  
4  
protected void update(){
    if (player_death) showDeathScreen(); else
        updateAsUsual();
}

update() is called again and again.

Offline philfrei
« Reply #6 - Posted 2011-01-29 03:57:03 »

One basic approach is where each object to be animated is given state variables, an update function and a draw function. This allows loose coupling all around. Let the panel or stage call a draw function for every object, passing the graphics object as a parameter, of course. Whether you decide to run this via a game loop or a Timer, I think there is some latitude. Whether the display is active or passive, there is also some latitude.

In the object's update() function: you check its state variables and manage incrementers or decrementers to tell you how far along you are in a particular animation sequence, and make state updates, such as putting the correct index of an array of images from the animation sequence into a predefined variable. Then, the draw function, when it is called, can just grab the index and display whatever graphic it finds.

Suppose the death rattle has 5 images to display in a second. The update() function starts a "deathrattleCounter" decrementer at 30 (if 30 updates per sec) and sets the current index for the image to 0. When the update is next called, and finds out the drCounter is now active so it decrements and tests its value. At drCounter==24, maybe set the current image index to 1, at drCounter==18, set the index to 2, etc.

"It's after the end of the world! Don't you know that yet?"
Offline loom_weaver

JGO Coder


Medals: 17



« Reply #7 - Posted 2011-02-09 04:50:33 »

what is that animation class? I mean, what inside it or extending what?

The animation class is an abstract class.  My main render routine (GameCanvas.paint()) will call the Animation if it's playing to set the camera position and for a list of glyphs to paint in addition to all the of the usual things it draws.

Here it is in all its glory.  I've changed it slightly because I didn't want Animation to have a dependency on Graphics2D.  Please excuse the scala code but the general intent should be clear.

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  
abstract class Animation {

    val start_ms = System.currentTimeMillis

    // end of primary constructor
    // --------------------------

    /** Return fi, fj, and fh.  Usually the center of the terrain tile. */
    def getFocalPoint: (Double, Double, Double) = {
        return (GameState.player.loc.i,
                GameState.player.loc.j,
                Terrain.getElevation(GameState.player.loc.i, GameState.player.loc.j))
    }

    /** return list of glyphs to be painted */
    def getGlyphs: List[(Glyph, Double, Double, Double)] = { return List() }

    def isComplete: Boolean = { return false }

    /**
     * Called every animTick.  Recalculate all necessary values.
     * Return true if a repaint is required, false otherwise
     */

    def advance: Boolean = { return false }
}

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.

toopeicgaming1999 (72 views)
2014-11-26 15:22:04

toopeicgaming1999 (62 views)
2014-11-26 15:20:36

toopeicgaming1999 (15 views)
2014-11-26 15:20:08

SHC (29 views)
2014-11-25 12:00:59

SHC (27 views)
2014-11-25 11:53:45

Norakomi (32 views)
2014-11-25 11:26:43

Gibbo3771 (27 views)
2014-11-24 19:59:16

trollwarrior1 (40 views)
2014-11-22 12:13:56

xFryIx (78 views)
2014-11-13 12:34:49

digdugdiggy (56 views)
2014-11-12 21:11:50
Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

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
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!