This site requires JavaScript, please enable it in your browser!
Greenfoot back
KCee
KCee wrote ...

2022/6/3

Making a casting animation over my idle animation

KCee KCee

2022/6/3

#
Im trying to make my character cast a fishing rod and play a casting animation when I press the "f" key but the animation isn't playing and I don't know why. Heres my code for the animations: private int speed = 5; GreenfootImage idleRight = new GreenfootImage; GreenfootImage idleLeft = new GreenfootImage; GreenfootImage cast = new GreenfootImage; String facing = "right"; SimpleTimer animationTimer = new SimpleTimer(); public Fisherman() { for (int i = 0; i < idleRight.length; i++) { idleRight = new GreenfootImage("images/idleAni/idle" + i + ".png"); idleRight.scale(100,80); } for (int i = 0; i < idleLeft.length; i++) { idleLeft = new GreenfootImage("images/idleAni/idle" + i + ".png"); idleLeft.mirrorHorizontally(); idleLeft.scale(100,80); } animationTimer.mark(); setImage(idleRight); } int imageIndex = 0; public void idle() { if(animationTimer.millisElapsed() < 500) { return; } animationTimer.mark(); if(facing.equals("right")) { setImage(idleRight); imageIndex = (imageIndex + 1) % idleRight.length; } else { setImage(idleLeft); imageIndex = (imageIndex + 1) % idleLeft.length; } } public void cast() { if (Greenfoot.isKeyDown("f")) { cast = new GreenfootImage("images/castAni/cast0.png"); cast.scale(100,80); cast = new GreenfootImage("images/castAni/cast1.png"); cast.scale(100,80); cast = new GreenfootImage("images/castAni/cast2.png"); cast.scale(100,80); cast = new GreenfootImage("images/castAni/cast3.png"); cast.scale(100,80); cast = new GreenfootImage("images/castAni/cast4.png"); cast.scale(100,80); } } public void fish() { cast(); }
Spock47 Spock47

2022/6/3

#
There are some points notable here: 1. There are some syntactical errors so that this code will not compile, e.g. missing parentheses at the end of
GreenfootImage idleRight = new GreenfootImage;
2. None of the defined methods seem to be called anywhere (especially method fish), so they won't be executed at all. 3. You create a lot of images without using them at all, e.g. in
            cast = new GreenfootImage("images/castAni/cast0.png");
            cast.scale(100,80);
cast is never used, so it will not show! You would need to have at least a setImage call for the image actually being used. 4. Greenfoot will only update what it shows at the end of each internal frame, i.e. that is at the end of the constructor and at the end of method act (for each frame). Especially that means that changes that happen within one of your methods, but are "overridden" before the end of the method won't show at all to the user. E.g.
            cast = new GreenfootImage("images/castAni/cast0.png");
            setImage(cast);
            cast = new GreenfootImage("images/castAni/cast1.png");
            setImage(cast);
would look the same as
            cast = new GreenfootImage("images/castAni/cast1.png");
            setImage(cast);
since the first image is already overwritten before the graphics are updated. So only the last image in each method (and internal frame) will be relevant. -> Therefore, if you want to have an animation, you have to control the current state over several frames, e.g.
private int currentFrame = 0;

@Override
public void act() {
    currentFrame = (currentFrame + 1) % 50;
    if (frame % 10 == 0) {
        final int imageIndex = frame / 10;
        setImage(new GreenfootImage("images/castAni/cast" + imageIndex + ".png"));
    }
}
will show an animation, where it changes to the next image every 10 frames (1/6 of a second). 5. In case Greenfoot doesn't find the image: I think the path base for the file name given to the GreenfootImage constructor is "...<scenarioName>/images", therefore
new GreenfootImage("images/castAni/cast1.png")
will look for an image at filepath "...<scenarioName>/images/images/castAni/cast1.png" (two folders called images in the path).
KCee KCee

2022/6/3

#
i fixed up the stuff you mentioned but now my fisherman character is rapidly switching between the first casting animation and the idle animation. did I forget to change something?
public class Fisherman extends Actor
{
    /**
     * Act - do whatever the Fisherman wants to do. This method is called whenever
     * the 'Act' or 'Run' button gets pressed in the environment.
     */
    private int currentFrame = 0;
    private int speed = 5;
    GreenfootImage[] idleRight = new GreenfootImage[2];
    GreenfootImage[] idleLeft = new GreenfootImage[2];
    GreenfootImage[] cast = new GreenfootImage[5];
    String facing = "right";
    SimpleTimer animationTimer = new SimpleTimer();
    
    public Fisherman()
    {
        for (int i = 0; i < idleRight.length; i++)
        {
            idleRight[i] = new GreenfootImage("images/idleAni/idle" + i + ".png");
            idleRight[i].scale(100,80);
        }
        
        for (int i = 0; i < idleLeft.length; i++)
        {
            idleLeft[i] = new GreenfootImage("images/idleAni/idle" + i + ".png");
            idleLeft[i].mirrorHorizontally();
            idleLeft[i].scale(100,80);
        }
        
        animationTimer.mark();
        setImage(idleRight[1]);
    }
    
    int imageIndex = 0;
    public void idle()
    {
        if(animationTimer.millisElapsed() < 500)
        {
            return;
        }
        animationTimer.mark();
        
        if(facing.equals("right"))
        {
            setImage(idleRight[imageIndex]);
            imageIndex = (imageIndex + 1) % idleRight.length;
        }
        
        else
        {
            setImage(idleLeft[imageIndex]);
            imageIndex = (imageIndex + 1) % idleLeft.length;
        }
    }
    
    public void cast()
    {
        if (Greenfoot.isKeyDown("f"))
        {
            cast[1] = new GreenfootImage("images/castAni/cast0.png");
            setImage(cast[1]);
            cast[2] = new GreenfootImage("images/castAni/cast1.png");
            setImage(cast[2]);
            cast[3] = new GreenfootImage("images/castAni/cast2.png");
            setImage(cast[3]);
            cast[4] = new GreenfootImage("images/castAni/cast3.png");
            setImage(cast[4]);
            cast[5] = new GreenfootImage("images/castAni/cast4.png");
            setImage(cast[5]);
        }
    }
    
    public void fish()
    {
       cast(); 
    }
    
    public void move()
    {
        int dx = 0;
        if (Greenfoot.isKeyDown("d")) 
        {
            dx++;
            facing = "right";

        }
        
        if (Greenfoot.isKeyDown("a")) 
        {
            dx--;
            facing = "left";
        }
        
        setLocation(getX()+dx, getY());
        if (isTouching(Boundary.class))
        {
            setLocation(getX()-dx, getY());
        }
        
        idle();
    }
    
    public void act()
    {
        int frame = 0;
        currentFrame = (currentFrame + 1) % 50;
        if (frame % 10 == 0) 
        {
            final int imageIndex = frame / 10;
            setImage(new GreenfootImage("images/castAni/cast" + imageIndex + ".png"));
            frame++;
        }
        move();
    }
}
Spock47 Spock47

2022/6/3

#
Well, I think point 4 is the most important part: The act method is called about 60 times a second. In the example code I gave, the image gets changed every 10 frames, i.e. 10/60 = 1/6 of a second. It uses the cast images. Still, it was just an example code. Additionally, you call the move method, which itself calls the idle method. This happens every frame (so 60 times a second). In this method the image Index is increased and the image changed. This happens 60 times a second. Since there are two different idle images defined for each side, it will oscillate between the two idle images (at a rate of 60 changes per second) - and additionally change to a cast image 6 times a second from the example code. This means, it will currently interchange images from two image series (cast images, idle right images) interconnected which creates the mess up. So, you now have to decide which image (series) should be shown at which time. There should be only ONE image series active at each time. Then just use the "currentFrame" method to decide the image index from the current image series. Also, you should clean up/remove unneccessary and unused stuff: animationTimer, methods fish and cast and imageIndex (as soon as you use currentFrame for the correct image index). This makes it easier to keep pace of what happens and to see how to do things.
You need to login to post a reply.