Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (475)
Games in Android Showcase (106)
games submitted by our members
Games in WIP (530)
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  
  best 2d engine recommendation for simple animation (frames with layers collage)  (Read 4176 times)
0 Members and 1 Guest are viewing this topic.
Offline manuelbarzi

Senior Newbie





« Posted 2008-12-17 00:08:37 »

hi,

i am learning about 2d animation and i want to know which 2d engine is currently more suitable for what i need: a simple animation consisting of 160 frames (images).

a subset of those 160 frames are variable, about 40, they have to be constructed with combination of layers depending on configurable conditions (which can change dynamically but not with much frequency).

so, my questions is, for performing the following tasks in as high-speed as possible:

- loading layers (images)
- collaging them to create 160 frames (images) - processing layers, merging them as fast as possible to create each frame
- writing frames on screen on an adjustable frames-per-second basis

what's the best 2d engine recommendation to achieve this simple animation?

thank you very much!
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11


Game Engineer


« Reply #1 - Posted 2008-12-17 01:29:59 »

You don't need an engine to do that, it's pretty simple. And if your images are not gigantic (which they shouldn't be if there are 160 of them) ImageIO will work just fine to load them.

You can use my Animation and AnimationSet class for the animations if you want. It's pretty basic but it sounds like it will work for you. It functions like a hash map, so you just specify the name of the animation you want and it will play. Similarly you give each animation an integer priority so it will automatically prioritize animations as you would want. So like someone's standing pose would be your default animation with a priority of 0, and then walk would have a priority of 1 and could therefore replace it, and kick would be 2, etc.

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  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
//A Vector2 is simply two floats.
//The Model.TARGET_DELTA has to do with my adaptive framerate. So basically if the delta was at
//0.4 and the target delta was 0.2, it would update the animations twice as quickly.
//The ImageManager just loads and holds images/sprites.

/**
 * An Animaton contains an array of Textures that can be used for an Animation.
 * @author Eli Delventhal
 */

public class Animation
{
   /** The sprites that will be drawn. */
   private String[] sprites;
   
   /** The offsets at which to draw each sprite, automatically determined. */
   private Vector2[] offsets;
   
   /** The length of each frame in ticks. */
   private int frameLength;
   
   /** Whether or not this Animation loops. */
   private boolean loops;
   
   /** The priority of this Animation. */
   private int priority;
   
   /** How many ticks have gone by this frame. */
   private float ticksThisFrame;
   
   /** The frame number we're currently in. */
   private int frameNumber;
   
   /** Whether or not this Animation is finished. */
   private boolean isFinished;
   
   public Animation(String sprite, int prior)
   {
      this(new String[]{sprite},Integer.MAX_VALUE,true,prior);
   }
   
   /**
    * Creates an Animation from an array of string references.
    * @param images   An array of Strings to use as keys for the ImageManager.
    * @param delay      The delay in ticks to have between frame changes.
    * @param loop      Whether or not this Animation loops.
    * @param prior      The priority of this Animation.
    */

   public Animation(String[] images, int delay, boolean loop, int prior)
   {
      sprites = images;
      frameLength = delay;
      ticksThisFrame = 0;
      frameNumber = 0;
      isFinished = false;
      loops = loop;
      priority = prior;
      offsets = new Vector2[sprites.length];
   }
   
   /**
    * Draws the current frame of this Animation at the specified position.
    * Also uses the specified scale.
    * In addition, advances the animation if appropriate.
    */

   public void draw(float x, float y, Vector2 scale, float rotation, boolean flipped)
   {
      Sprite sprite = ImageManager.getSprite(sprites[frameNumber]);
      Vector2 offset = offsets[frameNumber];
     
      int width = (int) (sprite.getWidth()*scale.x);
      int height = (int) (sprite.getHeight()*scale.y);
     
      //Draw the current frame at the specified position and size.
     if (flipped && offset != null)
         sprite.draw((int)((x-offset.x)*scale.x),(int)((y-offset.y)*scale.y),width,height,rotation,flipped);
      else
         sprite.draw((int)(x*scale.x),(int)((y-offset.y)*scale.y),width,height,rotation,flipped);
   }
   
   /**
    * Draws the current frame of this Animation at the specified position.
    * In addition, advances the animation if appropriate.
    */

   public void draw(float x, float y, float width, float height, float rotation, boolean flipped)
   {
      Sprite sprite = ImageManager.getSprite(sprites[frameNumber]);
      Vector2 offset = offsets[frameNumber];
     
      //Draw the current frame at the specified position and size.
     if (flipped && offset != null)
         sprite.draw((int)(x-offset.x),(int)(y-offset.y),(int)width,(int)height,rotation,flipped);
      else
         sprite.draw((int)x,(int)(y-offset.y),(int)width,(int)height,rotation,flipped);
   }
   
   public void advanceAnimation(float delta)
   {
      //Advance the frame if appropriate.
     ticksThisFrame += delta / Model.TARGET_DELTA;
      if (ticksThisFrame >= frameLength)
      {
         frameNumber++;
         ticksThisFrame = 0;
      }
      if (frameNumber >= sprites.length)
      {
         if (loops)
            frameNumber = 0;
         else
            frameNumber = sprites.length-1;
         isFinished = true;
      }
   }
   
   public void restart()
   {
      frameNumber = 0;
      isFinished = false;
      ticksThisFrame = 0;
   }
   
   /**
    * Whether or not this animation has reached and finished its last frame.
    * @return   True / false if it's finished.
    */

   public boolean isFinished()
   {
      return isFinished;
   }
   
   public int getPriority()
   {
      return priority;
   }
   
   public boolean loops()
   {
      return loops;
   }
   
   public int getFrameNumber()
   {
      return frameNumber;
   }
   
   public void preloadAll()
   {
      for (int i = 0; i < sprites.length; i++)
         ImageManager.preloadSprite(sprites[i]);
   }
   
   /**
    * Will calculate image offsets that should be used when it is flipped in order to make
    * the animation look fluid. This must be called by whoever has the AnimationSet after
    * preloadAll() (or the dependent images have been preloaded), and is therefore not
    * automatically called.
    */

   public void findOffsets()
   {
      //Find the smallest sprite (in both width and height).
     int minWidth = ImageManager.getSprite(sprites[0]).getWidth();
      int minHeight = ImageManager.getSprite(sprites[0]).getHeight();
      for (int i = 1; i < sprites.length; i++)
      {
         int wi = ImageManager.getSprite(sprites[i]).getWidth();
         int hi = ImageManager.getSprite(sprites[i]).getHeight();
         if (wi < minWidth)
            minWidth = wi;
         if (hi < minHeight)
            minHeight = hi;
      }
     
      //Now that we've got the smallest sprite in the animation,
     //adjust the offsets of the bigger sprites to match the difference.
     for (int i = 0; i < sprites.length; i++)
      {
         int wi = ImageManager.getSprite(sprites[i]).getWidth();
         int hi = ImageManager.getSprite(sprites[i]).getHeight();
         offsets[i] = new Vector2(wi-minWidth,hi-minHeight);
      }
   }
   
   public void manuallySetOffset(int position, Vector2 offset)
   {
      if (position >= 0 && position < offsets.length)
         offsets[position] = offset;
   }
   
   public float getTime()
   {
      return frameNumber * frameLength + ticksThisFrame;
   }
   
   public int getLength()
   {
      return frameLength * sprites.length;
   }
}


See my work:
OTC Software
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11


Game Engineer


« Reply #2 - Posted 2008-12-17 01:30:14 »

Cont'd.

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  
129  
import java.util.HashMap;
import java.util.Iterator;

//Entity is simply what I use to represent each thing that exists in the game. They all have an AnimationSet
//and owner logic is checked elsewhere.
/**
 * An AnimationSet contains a map of animations that can be referred to by string names.
 * @author Eli Delventhal
 *
 */

public class AnimationSet
{
   private Entity owner;
   private HashMap<String,Animation> animations;
   private String defaultAnimation;
   private Animation currentAnimation;
   private String currentAnimationName;
   
   public AnimationSet(Animation defaultAnim, String defaultAnimName)
   {
      animations = new HashMap<String,Animation>();
      animations.put(defaultAnimName, defaultAnim);
      defaultAnimation = defaultAnimName;
      currentAnimation = animations.get(defaultAnimation);
      currentAnimationName = defaultAnimation;
   }
   
   /**
    * Draws this animation at the specified coords and width and stuff.
    */

   public void draw(float x, float y, float width, float height, float rotation, boolean flipped)
   {
      currentAnimation.draw(x, y, width, height, rotation, flipped);
   }
   
   /**
    * Sets an animation to the new passed one. It will only set
    * if its priority works or the old one is done.
    * @param animation   The animation to set.
    */

   public boolean setAnimation(String animation)
   {
      boolean wasSet = false;
      Animation newAnimation = animations.get(animation);
      if (newAnimation != null
         && (newAnimation != currentAnimation || currentAnimation.isFinished())
         && (newAnimation.getPriority() >= currentAnimation.getPriority()
               || (currentAnimation.isFinished() && !currentAnimation.loops())))
      {
         if (owner != null)
            owner.notifyAnimationFinished(currentAnimationName,currentAnimation.isFinished());
         currentAnimation = newAnimation;
         currentAnimation.restart();
         currentAnimationName = animation;
         wasSet = true;
      }
     
      if (newAnimation == currentAnimation)
         wasSet = true;
     
      return wasSet;
   }
   
   /**
    * Sets the new animation no matter what.
    * @param animation   The new animation.
    */

   public void forciblySetAnimation(String animation)
   {
      Animation newAnimation = animations.get(animation);
      if (newAnimation != null)
      {
         currentAnimation = newAnimation;
         currentAnimationName = animation;
      }
   }
   
   public Animation getCurrentAnimation()
   {
      return currentAnimation;
   }
   
   public String getCurrentAnimationName()
   {
      return currentAnimationName;
   }
   
   public Animation getAnimation(String name)
   {
      return animations.get(name);
   }
   
   public void addAnimation(Animation anim, String name)
   {
      animations.put(name, anim);
   }
   
   public void preloadAll()
   {
      for (Iterator<Animation> i = animations.values().iterator(); i.hasNext();)
         i.next().preloadAll();
   }
   
   public void findOffsets()
   {
      for (Iterator<Animation> i = animations.values().iterator(); i.hasNext();)
         i.next().findOffsets();
   }
   
   public void setOwner(Entity e)
   {
      owner = e;
   }
   
   public float getAnimationTime()
   {
      return currentAnimation.getTime();
   }
   
   public int getAnimationLength()
   {
      return currentAnimation.getLength();
   }
   
   public void advanceCurrentAnimation(float delta)
   {
      currentAnimation.advanceAnimation(delta);
   }
}


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
//Example initialization:
AnimationSet set = new AnimationSet(new Animation(sheet+ANIM_WALK_0,0),"Stand");
      set.addAnimation(new Animation(sheet+ANIM_HIT_2, 500), "Death");
     
      set.addAnimation(new Animation(sheet+ANIM_HIT_2, 100), "Knocked");
      set.addAnimation(new Animation(sheet+ANIM_JUMP_0, 20), "Jump");
     
      String[] walkSprites = new String[]
      {
         sheet+ANIM_WALK_0,
         sheet+ANIM_WALK_1,
         sheet+ANIM_WALK_2
      };
      set.addAnimation(new Animation(walkSprites, 10, false, 1), "Walk");
     
      String[] punchSprites = new String[]
      {
         sheet+ANIM_PUNCH_0,
         sheet+ANIM_PUNCH_1,
         sheet+ANIM_PUNCH_2
      };
      set.addAnimation(new Animation(punchSprites, 6, false, 10), "Punch");
//etc.


1  
2  
3  
4  
5  
6  
//Example use, from Entity, who has a Vector2 position and a Vector2 size.
public void draw(Vector2 scale)
{
   animations.getCurrentAnimation().draw(position.x, position.y, scale, rotation, facingLeft);
   animations.advanceCurrentAnimation(Model.TARGET_DELTA);
}


Hope that helps, let me know if you have any questions.

See my work:
OTC Software
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline manuelbarzi

Senior Newbie





« Reply #3 - Posted 2008-12-17 09:54:18 »

hi, Demonpants,

thank you very much for your replies. i will try your proposal.

i have already done simpler animations before, using swing-timer and printing bufferedimage's on a jframe from an array, the index of which i was increasing on each timer cycle. this scenary worked fine, but to get a good fps i had to set timer cycle to 0 seconds. that is, at the most faster as possible. this is a basic animation scenary in java-swing that was good then, but now i have much more processing to do, the images are more numerous and some of them (subset) have to be re-collaged sometimes.

taking a quick look at your code now, i have a little question: should i use it in an escenary like described, with swing-timer and so on? i would appreciate to see what's your scenary proposal.

thank you!
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11


Game Engineer


« Reply #4 - Posted 2008-12-17 10:03:46 »

That was using LWJGL and Display.sync(), but you can really use any timer method.

The reason you pass a float delta and there's a target delta, however, is so the animation will catch up or slow down if the framrate gets abnormal. This also allows you to do fun stuff like add in slow motion and whatnot.

And if you're using the Swing timer, you would indeed probably need to have it at a very low level in order to get a nice looking animation. I wouldn't recommend using it... instead maybe use System.nanoTime() and Thread.yield, which is probably the best method if you're not using LWJGL.

See my work:
OTC Software
Offline manuelbarzi

Senior Newbie





« Reply #5 - Posted 2008-12-17 10:09:18 »

ok, i'm gonna learn lwjgl then, cause i never used it directly before, only indirectly by means of jme on other projects.

any suggestion about where to start with it; some good, clear and quick tutorial about lwjgl i can find here? i guess i would find that in lwjgl site, but if here there are good resources, that would be great.

thank you, once again, Demonpants.
Offline manuelbarzi

Senior Newbie





« Reply #6 - Posted 2008-12-17 10:12:57 »

btw, the answer to my question: "best 2d engine recommendation", from your side i guess it might be: "use lwjgl api" right?
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11


Game Engineer


« Reply #7 - Posted 2008-12-17 19:17:43 »

Check out Kevin Glass's Space Invaders tutorial.

http://www.cokeandcode.com/spaceinvaderstutorial

Also, LWJGL is indeed what I use for 2D games, although it's not necessary to do so. However it's a good thing to learn if you've got the time for it.

See my work:
OTC Software
Offline manuelbarzi

Senior Newbie





« Reply #8 - Posted 2008-12-18 12:42:40 »

hi, Demonpants,

yes, i was exactly checking that example yesterday! Smiley thanks for confirming it to me.

i downloaded the source code for that game sample, but i had a problem: could not resolve the dependencies with net.java.games.jogl.*.

i downloaded api from jogl, but that package net.java.games.jogl doesn't seem to exist anymore...

so i coulnt yet run the game from source.

any idea about this issue?

thank you, once again.
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11


Game Engineer


« Reply #9 - Posted 2008-12-18 18:40:07 »

Get the source in the last section of that tutorial, where it is designed for LWJGL. You noticed that there were a few sections, right, starting with Java2D and going on up to LWJGL?

In fact, just go to lwjgl.org and download their latest build, and in the source I believe you'll find the space invaders example all set up for you already.

See my work:
OTC Software
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline princec

JGO Kernel


Medals: 339
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #10 - Posted 2008-12-18 21:56:51 »

Actually if you want to make your life a lot easier get hold of Slick which is a very handy library of useful 2D stuff for doing games.

Cas Smiley

Offline manuelbarzi

Senior Newbie





« Reply #11 - Posted 2008-12-18 22:33:35 »

hi, cas,

thanks for your recommendation.

my goal is:

- 2d engine
- best performance
- best manageability
- best mantainability
- best extensibility
- most standard

i guess lwjgl may be a good candidate for this.

what's your opinion?

thank you
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11


Game Engineer


« Reply #12 - Posted 2008-12-19 00:54:09 »

Cas is right. Slick is literally just LWJGL with another level of detail added on. And it's specifically meant for 2D.

See my work:
OTC Software
Offline manuelbarzi

Senior Newbie





« Reply #13 - Posted 2008-12-19 15:04:41 »

ok, Demonpants, i will check it out Wink thanks
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.

ctomni231 (34 views)
2014-07-18 06:55:21

Zero Volt (30 views)
2014-07-17 23:47:54

danieldean (25 views)
2014-07-17 23:41:23

MustardPeter (27 views)
2014-07-16 23:30:00

Cero (42 views)
2014-07-16 00:42:17

Riven (44 views)
2014-07-14 18:02:53

OpenGLShaders (33 views)
2014-07-14 16:23:47

Riven (34 views)
2014-07-14 11:51:35

quew8 (30 views)
2014-07-13 13:57:52

SHC (66 views)
2014-07-12 17:50:04
HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54

HotSpot Options
by Roquen
2014-05-06 15:03:10

Escape Analysis
by Roquen
2014-04-29 22:16:43

Experimental Toys
by Roquen
2014-04-28 13:24:22
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!