3 Replies - 463 Views - Last Post: 20 July 2012 - 03:14 PM Rate Topic: -----

#1 N1G3L2  Icon User is offline

  • New D.I.C Head

Reputation: 4
  • View blog
  • Posts: 28
  • Joined: 25-October 11

Java Game Dev: Problem with Animation

Posted 19 July 2012 - 05:04 AM

HI! I have been using Brackeen's tile game (http://www.brackeen.com/javagamebook/) as an engine for my sidescroller. His game uses an Animation class, which continuously loops through images whether or not a certain key is being pressed. So, whether the character is standing still, walking, jumping, etc. he is always looping through the same images and it doesn't look too great. In the past I have always used if statements/counters to decide which image is being displayed, but I find having this Animation class to be more organized and a little less clutter, which is nice. I've messed with the code and images enough to make a nice little scrolling platformer (and a topdown as well), but this Animation class is killing me. I have fussed with it, trying to add a method that doesn't switch frames, but I keep getting errors or just nothing at all. I don't think the problem lies within the Animation class itself, but maybe in one of the classes it is called in (probably the ResourceManager class). It's actually probably something very simple, but this is my first time encountering a class used for Animation, and I would love to understand it further. I probably should take another look at arrays and stuff. Anyways, if anyone is willing to help I would appreciate it to the max. I will paste the code here, but I'm going to do my best to only paste what I think is important because there is a lot of code here. I have a functioning game right now, but what I'm pasting here won't compile as I'm leaving out a ton of stuff. If somebody is willing to help, but needs more code to look at I can zip it right up and send it over. Thanks if you are even reading this I appreciate it people. =)

ANIMATION CLASS:
/**
 * Animation class
 */
public class Animation {

    private ArrayList frames;
    private int currFrameIndex;
    private long animTime;
    private long totalDuration;

    /**
     * Empty Animation
     */
    public Animation() {
        this(new ArrayList(), 0);
    }

    /**
     * Animation //how long to display each frame
     */
    public Animation(ArrayList frames, long totalDuration) {
        this.frames = frames;
        this.totalDuration = totalDuration;
        start();
    }

    /**
     * A duplicate of this animation
     */
    public Object clone() {
        return new Animation(frames, totalDuration);
    }

    /**
     * add an image to the animation for the specified duration
     */
    public synchronized void addFrame(Image image,
        long duration)
    {
        totalDuration += duration;
        frames.add(new AnimFrame(image, totalDuration));
    }

    /**
     * start animation over
     */
    public synchronized void start() {
        animTime = 0;
        currFrameIndex = 0;
    }
    
    /**
     * start animation over
     */
    public synchronized void stop() {
        animTime = 1;
        currFrameIndex = 1;
    }

    /**
     * update animations current image
     */
    public synchronized void update(long elapsedTime) {
        if (frames.size() > 1) {
            animTime += elapsedTime;

            if (animTime >= totalDuration) {
                animTime = animTime % totalDuration;
                currFrameIndex = 0;
            }

            while (animTime > getFrame(currFrameIndex).endTime) {
                currFrameIndex++;
            }
        }
    }

    /**
     * get the animations current image
     */
    public synchronized Image getImage() {
        if (frames.size() == 0) {
            return null;
        }
        else {
            return getFrame(currFrameIndex).image;
        }
    }
    
    private AnimFrame getFrame(int i) {
        return (AnimFrame)frames.get(i);
    }

    private class AnimFrame {

        Image image;
        long endTime;

        public AnimFrame(Image image, long endTime) {
            this.image = image;
            this.endTime = endTime;
        }
    }
}



PLAYER CLASS (at least whats important to the animation):
/**
    the user controlled player
*/
public class Player extends Creature{

    public Player(Animation left, Animation right,
        Animation deadLeft, Animation deadRight)
    {
    	super(left, right, deadLeft, deadRight);
    }
}



CREATURE CLASS:
public abstract class Creature extends Sprite {

    private static final int DIE_TIME = 1000;

    public static final int STATE_NORMAL = 0;
    public static final int STATE_DYING = 1;
    public static final int STATE_DEAD = 2;
    public static final int STATE_STILL = 3;

    private Animation left;
    private Animation right;
    private Animation deadLeft;
    private Animation deadRight;
    private int state;
    private long stateTime;

    public Creature(Animation left, Animation right,
        Animation deadLeft, Animation deadRight)
    {
        super(right);
        this.left = left;
        this.right = right;
        this.deadLeft = deadLeft;
        this.deadRight = deadRight;
        state = STATE_NORMAL;
    }

    public Object clone() {
        // use reflection to create the correct subclass
        Constructor constructor = getClass().getConstructors()[0];
        try {
            return constructor.newInstance(new Object[] {
                (Animation)left.clone(),
                (Animation)right.clone(),
                (Animation)deadLeft.clone(),
                (Animation)deadRight.clone()
            });
        }
        catch (Exception ex) {
            // should never happen
            ex.printStackTrace();
            return null;
        }
    }

    public void wakeUp() {
        if (getState() == STATE_NORMAL && getVelocityX() == 0) {
            setVelocityX(-getMaxSpeed());
        }
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        if (this.state != state) {
            this.state = state;
            stateTime = 0;
            if (state == STATE_DYING) {
                setVelocityX(0);
                setVelocityY(0);
            }
        }
    }

    public boolean isAlive() {
        return (state == STATE_NORMAL);
    }

    public boolean isStill() {
    	return (state == STATE_STILL);
    }

    public boolean isFlying() {
        return false;
    }
    
    public boolean isJumping() {
        return false;
    }

    public void collideHorizontal() {
        setVelocityX(-getVelocityX());
    }

    public void collideVertical() {
        setVelocityY(0);
    }

    public void update(long elapsedTime) {
        // select the correct Animation
        Animation newAnim = anim;
        if(state == STATE_STILL) {
        	newAnim = null;
        }
        if (getVelocityX() < 0) {
            newAnim = left;
        }
        else if (getVelocityX() > 0) {
            newAnim = right;
        }
        if (state == STATE_DYING && newAnim == left) {
            newAnim = deadLeft;
        }
        else if (state == STATE_DYING && newAnim == right) {
            newAnim = deadRight;
        }

        // update the Animation
        if (anim != newAnim) {
            anim = newAnim;
            anim.start();
        }
        else {
            anim.update(elapsedTime);
        }

        // update to "dead" state
        stateTime += elapsedTime;
        if (state == STATE_DYING && stateTime >= DIE_TIME) {
            setState(STATE_DEAD);
        }
    }

}




SPRITE CLASS:


public class Sprite {

    protected Animation anim;
    
    public Sprite() {
        
    }

    public Sprite(Animation anim) {
        this.anim = anim;
    }

    public void update(long elapsedTime) {
        x += dx * elapsedTime;
        y += dy * elapsedTime;
        anim.update(elapsedTime);
    }

    public int getWidth() {
        return anim.getImage().getWidth(null);
    }

    public int getHeight() {
        return anim.getImage().getHeight(null);
    }

    public Image getImage() {
        return anim.getImage();
    }

    public Object clone() {
        return new Sprite(anim);
    }
}




RESOURCE MANAGER
(where it is called):
public class ResourceManager {

    private Sprite playerSprite;
    //powerups
    private Sprite lifeSprite;
    private Sprite coinSprite;
    private Sprite goalSprite;
    //enemies
    private Sprite groundEnemySprite;
    private Sprite flyEnemySprite;
    private Sprite spikesEnemySprite;
    private Sprite spikesEnemyTopSprite;
    //special tiles
    private Sprite jumpBoxSprite;
    private Sprite waterTopSprite;
    private Sprite waterBottomSprite;
    //things/other
    private Sprite treeSprite;
    private Sprite bushSprite;
    private Sprite bgWallSprite;
    private Sprite windowSprite;
    
    private Sprite titleSprite;

    public ResourceManager(GraphicsConfiguration gc) {
        this.gc = gc;
        loadSpecialTiles();
        loadTileImages();
        loadCreatureSprites();
        loadPowerUpSprites();
        loadThingsSprites();
    }

    //loads image from images folder
    public Image loadImage(String name) {
        String filename = "images/" + name;
        return new ImageIcon(filename).getImage();
    }
    
    //mirror image
    public Image getMirrorImage(Image image) {
        return getScaledImage(image, -1, 1);
    }
    
    //flip image
    public Image getFlippedImage(Image image) {
        return getScaledImage(image, 1, -1);
    }

    private Image getScaledImage(Image image, float x, float y) {
        // scale image
        AffineTransform transform = new AffineTransform();
        transform.scale(x, y);
        transform.translate(
            (x-1) * image.getWidth(null) / 2,
            (y-1) * image.getHeight(null) / 2);

        // transparency
        Image newImage = gc.createCompatibleImage(
            image.getWidth(null),
            image.getHeight(null),
            Transparency.BITMASK);

        // draw image
        Graphics2D g = (Graphics2D)newImage.getGraphics();
        g.drawImage(image, transform, null);
        g.dispose();

        return newImage;
    }
    
    
    /**
     * load map. reads text file and translates chars to images/sprites
     */
    private TileMap loadMap(String filename)
        throws IOException
    {
        ArrayList lines = new ArrayList();
        int width = 0;
        int height = 0;

        //read every line in the text file
        BufferedReader reader = new BufferedReader(
            new FileReader(filename));
        while (true) {
            String line = reader.readLine();
            //no more lines to read
            if (line == null) {
                reader.close();
                break;
            }

            //add lines except for comments (lines starting with #)
            if (!line.startsWith("#")) {
                lines.add(line);
                width = Math.max(width, line.length());
            }
        }

        //parse lines to create tile map
        height = lines.size();
        TileMap newMap = new TileMap(width, height);
        for (int y=0; y<height; y++) {
            String line = (String)lines.get(y);
            for (int x=0; x<line.length(); x++) {
                char ch = line.charAt(x);

                //set tile sprites (A-Z)
                int tile = ch - 'A';
                if (tile >= 0 && tile < tiles.size()) {
                    newMap.setTile(x, y, (Image)tiles.get(tile));
                }

                // set sprites to characters
                else if (ch == '^') {
                    addSprite(newMap, treeSprite, x, y);
                }
                else if (ch == '#') {
                    addSprite(newMap, bushSprite, x, y);
                }
                else if (ch == 'o') {
                    addSprite(newMap, coinSprite, x, y);
                }
                else if (ch == '!') {
                    addSprite(newMap, lifeSprite, x, y);
                }
                else if (ch == '*') {
                    addSprite(newMap, goalSprite, x, y);
                }
                else if (ch == '+') {
                    addSprite(newMap, jumpBoxSprite, x, y);
                }
                else if (ch == 'h') {
                    addSprite(newMap, bgWallSprite, x, y);
                }
                else if (ch == '0') {
                    addSprite(newMap, windowSprite, x, y);
                }
                else if (ch == 'w') {
                    addSprite(newMap, waterTopSprite, x, y);
                }
                else if (ch == 'x') {
                    addSprite(newMap, waterBottomSprite, x, y);
                }
                else if (ch == '"') {
                    addSprite(newMap, titleSprite, x, y);
                }
                else if (ch == '1') {
                    addSprite(newMap, groundEnemySprite, x, y);
                }
                else if (ch == '2') {
                    addSprite(newMap, flyEnemySprite, x, y);
                }
                else if (ch == '3') {
                    addSprite(newMap, spikesEnemySprite, x, y);
                }
                else if (ch == '4') {
                    addSprite(newMap, spikesEnemyTopSprite, x, y);
                }
            }
        }

        //set player location based on current map
        Sprite player = (Sprite)playerSprite.clone();       
        if(currentMap == 1){
        	//start at end of map
        	player.setLocation(1056, 350);	
        }
        else if(currentMap == 2){
        	player.setLocation(125, 250);
        }
        else if(currentMap == 3){
        	player.setLocation(900, 150);	
        }
        else if(currentMap == 4){
        	player.setLocation(200, 200);	
        }
        newMap.setPlayer(player);
        return newMap;
    }

    private void addSprite(TileMap map,
        Sprite hostSprite, int tileX, int tileY)
    {
        if (hostSprite != null) {
            Sprite sprite = (Sprite)hostSprite.clone();

            //center the sprite
            sprite.setX(
                TileMapRenderer.tilesToPixels(tileX) +
                (TileMapRenderer.tilesToPixels(1) -
                sprite.getWidth()) / 2);

            //on top of tiles
            sprite.setY(
                TileMapRenderer.tilesToPixels(tileY + 1) -
                sprite.getHeight());

            //add sprite
            map.addSprite(sprite);
        }
    }

    // -----------------------------------------------------------
    // code for loading sprites 
    // -----------------------------------------------------------

    public void loadCreatureSprites() {

        Image[][] images = new Image[4][];

        //load left-facing images
        images[0] = new Image[] {
            loadImage("player1.png"),
            loadImage("player2.png"),
            loadImage("fly1.png"),
            loadImage("fly2.png"),
            loadImage("fly3.png"),
            loadImage("enemy1.png"),
            loadImage("enemy2.png"),
            loadImage("enemy3.png"),
            loadImage("spikes.png"),
            loadImage("spikes2.png"),
        };

        images[1] = new Image[images[0].length];
        images[2] = new Image[images[0].length];
        images[3] = new Image[images[0].length];
        for (int i=0; i<images[0].length; i++) {
            //right-facing images
            images[1][i] = getMirrorImage(images[0][i]);
            //left-facing dead images
            images[2][i] = getFlippedImage(images[0][i]);
            //right-facing dead images
            images[3][i] = getFlippedImage(images[1][i]);
        }

        //creature animations
        Animation[] playerAnim = new Animation[4];
        Animation[] flyEnemyAnim = new Animation[4];
        Animation[] groundEnemyAnim = new Animation[4];
        Animation[] spikesEnemyAnim = new Animation[4];    //pointless but needed for now
        Animation[] spikesEnemyTopAnim = new Animation[4]; //"                          "                           "

        for (int i=0; i<4; i++) {
        	
            playerAnim[i] = createPlayerAnim(
            		images[i][0], 
            		images[i][1]);
            
            flyEnemyAnim[i] = createFlyEnemyAnim(
            		images[i][2], 
            		images[i][3], 
            		images[i][4]);
            
            groundEnemyAnim[i] = createGroundEnemyAnim(
            		images[i][5], 
            		images[i][6],
            		images[i][7]);
            
            spikesEnemyAnim[i] = createNullAnim(
            		images[i][8]);
            
            spikesEnemyTopAnim[i] = createNullAnim(
            		images[i][9]);
            
        }

        playerSprite = new Player(
        		playerAnim[0], 
        		playerAnim[1],
        		playerAnim[2], 
        		playerAnim[3]);
        
        flyEnemySprite = new FlyEnemy(
        		flyEnemyAnim[0], 
        		flyEnemyAnim[1],
        		flyEnemyAnim[2],
        		flyEnemyAnim[3]);
        
        groundEnemySprite = new GroundEnemy(
        		groundEnemyAnim[0], 
        		groundEnemyAnim[1],
        		groundEnemyAnim[2], 
        		groundEnemyAnim[3]);
        
        spikesEnemySprite = new SpikesEnemy(
        		spikesEnemyAnim[0], 
        		spikesEnemyAnim[1],
        		spikesEnemyAnim[2], 
        		spikesEnemyAnim[3]);
        
        spikesEnemyTopSprite = new SpikesEnemyTop(
        		spikesEnemyTopAnim[0], 
        		spikesEnemyTopAnim[1],
        		spikesEnemyTopAnim[2], 
        		spikesEnemyTopAnim[3]);
        
    }

    private Animation createPlayerAnim(Image player1,
        Image player2) {
        Animation anim = new Animation();
        //anim.addFrame(image, duration);
        anim.addFrame(player1, 150);
        anim.addFrame(player2, 150);
        return anim;
    }

    private Animation createFlyEnemyAnim(Image img1, Image img2,
        Image img3) {
        Animation anim = new Animation();
        anim.addFrame(img1, 150);
        anim.addFrame(img2, 150);
        anim.addFrame(img1, 150);
        anim.addFrame(img3, 150);
        return anim;
    }

    private Animation createGroundEnemyAnim(Image img1, Image img2,
        Image img3) {
        Animation anim = new Animation();
        anim.addFrame(img1, 150);
        anim.addFrame(img2, 150);
        anim.addFrame(img1, 150);
        anim.addFrame(img3, 150);
        return anim;
    }
    
    private Animation createNullAnim(Image img1) {
        Animation anim = new Animation();
        anim.addFrame(img1, 500);
        return anim;
    }
    

    private void loadPowerUpSprites() {
        //create sign/goal sprite
        Animation anim = new Animation();
        anim.addFrame(loadImage("sign1.png"), 150);
        anim.addFrame(loadImage("sign2.png"), 150);
        anim.addFrame(loadImage("sign3.png"), 150);
        anim.addFrame(loadImage("sign2.png"), 150);
        goalSprite = new PowerUp.Goal(anim);

        //create coin sprite
        anim = new Animation();
        anim.addFrame(loadImage("coin1.png"), 100);
        anim.addFrame(loadImage("coin2.png"), 100);
        anim.addFrame(loadImage("coin3.png"), 100);
        anim.addFrame(loadImage("coin4.png"), 100);
        coinSprite = new PowerUp.Coin(anim);

        //create 1up sprite
        anim = new Animation();
        anim.addFrame(loadImage("life1.png"), 300);
        anim.addFrame(loadImage("life2.png"), 150);
        anim.addFrame(loadImage("life3.png"), 150);
        anim.addFrame(loadImage("life2.png"), 150);
        lifeSprite = new PowerUp.Life(anim);
    }
    
    private void loadThingsSprites() {
        //create tree sprite
        Animation anim = new Animation();
        anim.addFrame(loadImage("tree2.png"), 150);
        treeSprite = new Things.ThingsAnim(anim);
        //create bush sprite
        anim = new Animation();
        anim.addFrame(loadImage("bush.png"), 150);
        bushSprite = new Things.ThingsAnim(anim);
        
        //title animation for main menu
        anim = new Animation();
        anim.addFrame(loadImage("title1.png"), 300);
        anim.addFrame(loadImage("title2.png"), 100);
        anim.addFrame(loadImage("title3.png"), 100);
        anim.addFrame(loadImage("title4.png"), 100);
        anim.addFrame(loadImage("title3.png"), 100);
        anim.addFrame(loadImage("title2.png"), 100);
        titleSprite = new Things.ThingsAnim(anim);
        
        anim = new Animation();
        anim.addFrame(loadImage("bgWall.png"), 150);
        bgWallSprite = new Things.ThingsAnim(anim);
        
        anim = new Animation();
        anim.addFrame(loadImage("window.png"), 150);
        windowSprite = new Things.ThingsAnim(anim);
    }
    
    private void loadSpecialTiles() {
        //create jumpBox sprite
        Animation anim = new Animation();
        anim.addFrame(loadImage("jumpBox1.png"), 150);
        anim.addFrame(loadImage("jumpBox2.png"), 150);
        jumpBoxSprite = new SpecialTiles.JumpBox(anim);
        
        //create water sprites
        anim = new Animation();
        anim.addFrame(loadImage("waterTop.png"), 200);
        anim.addFrame(loadImage("waterTop2.png"), 200);
        waterTopSprite = new SpecialTiles.Water(anim);
        
        anim = new Animation();
        anim.addFrame(loadImage("waterBottom.png"), 150);
        waterBottomSprite = new SpecialTiles.Water(anim);
        
    }
}



Is This A Good Question/Topic? 0
  • +

Replies To: Java Game Dev: Problem with Animation

#2 N1G3L2  Icon User is offline

  • New D.I.C Head

Reputation: 4
  • View blog
  • Posts: 28
  • Joined: 25-October 11

Re: Java Game Dev: Problem with Animation

Posted 19 July 2012 - 05:17 AM

forgot the GameManager class. this one is actually probably where I can switch things up

/**
 * GameManager class....Manages the game....
 */
public class GameManager extends GameCore {

    public static void main(String[] args) {
        new GameManager().run();
    }
    
    //GameActions
    private GameAction moveLeft;
    private GameAction moveRight;
    private GameAction speedUp;
    private GameAction jump;
    private GameAction exit;
    private GameAction yes;
    private GameAction no;

    /**
     * Check if key has been pressed and act
     */
    private void checkInput(long elapsedTime) {

        Player player = (Player)map.getPlayer();
        if (player.isAlive()) {
            float velocityX = 0;
            if (moveLeft.isPressed()) {
                velocityX-=player.getMaxSpeed();
            }
            if (moveRight.isPressed()) {
                velocityX+=player.getMaxSpeed();
            }
            if (speedUp.isPressed()) {
            	Player.speed = .50f;
            }
            if (speedUp.isReleased()) {
            	Player.speed =.35f;
            }
            if (jump.isPressed() && Player.onGround == true) {
                player.jump(false);
                Sound.jump.play();
                
            }
            if (exit.isPressed()) {
                menu = true;
            }
            
            player.setVelocityX(velocityX);
        }
    }

    /**
     * updates animation and position of all sprites
     */
    public void update(long elapsedTime) {
    	Creature player = (Creature)map.getPlayer();


        //if player dies, start map over
        if (player.getState() == Creature.STATE_DEAD) {
            map = resourceManager.reloadMap();
            return;
        }

        //check input
        checkInput(elapsedTime);

        //update player
        updateCreature(player, elapsedTime);
        player.update(elapsedTime);

        //update other sprites
        Iterator i = map.getSprites();
        while (i.hasNext()) {
            Sprite sprite = (Sprite)i.next();
            if (sprite instanceof Creature) {
            	Creature creature= (Creature)sprite;
                if (creature.getState() == Creature.STATE_DEAD) {
                    i.remove();
                }
                else {
                    updateCreature(creature, elapsedTime);
                }
            }
            sprite.update(elapsedTime);
        }
    }

    /**
     * updates creature and checks for collision
     */
    private void updateCreature(Creature creature,
        long elapsedTime)
    {
        //apply gravity to non flying creatures
        if (!creature.isFlying()) {
            creature.setVelocityY(creature.getVelocityY() +
                gravity * elapsedTime);
        }
        //change x
        float dx = creature.getVelocityX();
        float oldX = creature.getX();
        float newX = oldX + dx * elapsedTime;
        Point tile =
            getTileCollision(creature, newX, creature.getY());
        if (tile == null) {
        	creature.setX(newX);
        }
        else {
            //line up with the tile boundary
            if (dx > 0) {
            	creature.setX(
                    TileMapRenderer.tilesToPixels(tile.x) -
                    creature.getWidth());
            }
            else if (dx < 0) {
            	creature.setX(
                    TileMapRenderer.tilesToPixels(tile.x + 1));
            }
            creature.collideHorizontal();
        }
        if (creature instanceof Player) {
            checkPlayerCollision((Player)creature, false, false, false);
        }

        //change y
        float dy = creature.getVelocityY();
        float oldY = creature.getY();
        float newY = oldY + dy * elapsedTime;
        tile = getTileCollision(creature, creature.getX(), newY);
        if (tile == null) {
        	creature.setY(newY);
        }
        else {
            //line up with the tile boundary
            if (dy > 0) {
            	creature.setY(
                    TileMapRenderer.tilesToPixels(tile.y) -
                    creature.getHeight());
            }
            else if (dy < 0) {
            	creature.setY(
                    TileMapRenderer.tilesToPixels(tile.y + 1));
            }
            creature.collideVertical();
        }
        if (creatureinstanceof Player) {
            boolean isAbove = (oldY < creature.getY());
            boolean isRight = (oldX <= creature.getX());
            boolean isLeft = (oldX >= creature.getX());
        }

    }
  
}


Was This Post Helpful? 0
  • +
  • -

#3 Hezekiah  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 207
  • View blog
  • Posts: 550
  • Joined: 12-July 09

Re: Java Game Dev: Problem with Animation

Posted 20 July 2012 - 02:02 AM

The solution depends on what you want to happen. If for example you want the animation to pause if the creature is standing still or jumping, you can change the anim.update(elapsedTime) line in the update() method to:
if(getVelocityX() != 0 && !isJumping()) {
    anim.update(elapsedTime);
}

This post has been edited by Hezekiah: 20 July 2012 - 02:03 AM

Was This Post Helpful? 1
  • +
  • -

#4 N1G3L2  Icon User is offline

  • New D.I.C Head

Reputation: 4
  • View blog
  • Posts: 28
  • Joined: 25-October 11

Re: Java Game Dev: Problem with Animation

Posted 20 July 2012 - 03:14 PM

wow its so obvious!!! thank you so much that really helped =)
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1