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

2022/6/6

casting fishing rod animation only plays 1 frame every time i click the key

1
2
KCee KCee

2022/6/6

#
I want to make an animation for casting a fishing rod, but when I click the key to cast ("f"), it only plays 1 frame of the animation. I have to press the key several times for the full animation to play out. Heres my code:
public class Fisherman extends Actor
{
    private int currentFrame = 0;
    private int speed = 5;
    GreenfootImage[] walkRight = new GreenfootImage[5];
    GreenfootImage[] walkLeft = new GreenfootImage[5];
    GreenfootImage[] castRight = new GreenfootImage[5];
    GreenfootImage[] castLeft = new GreenfootImage[5];
    String facing = "right";
    SimpleTimer animationTimer = new SimpleTimer();
    public Fisherman()
    {
        for (int i = 0; i < walkRight.length; i++)
        {
            walkRight[i] = new GreenfootImage("images/walkingAni/walking" + i + ".png");
            walkRight[i].scale(100,80);
        }
        
        for (int i = 0; i < walkLeft.length; i++)
        {
            walkLeft[i] = new GreenfootImage("images/walkingAni/walking" + i + ".png");
            walkLeft[i].mirrorHorizontally();
            walkLeft[i].scale(100,80);
        }
        
        
        animationTimer.mark();
        setImage(walkRight[0]);
    }
    
    int imageIndex = 0;
    public void walk()
    {
        if(animationTimer.millisElapsed() < 200)
        {
            return;
        }
        animationTimer.mark();
        
        if(facing.equals("right"))
        {
            setImage(walkRight[imageIndex]);
            imageIndex = (imageIndex + 1) % walkRight.length;
        }
        
        else
        {
            setImage(walkLeft[imageIndex]);
            imageIndex = (imageIndex + 1) % walkLeft.length;
        }
    }
    
    public void cast()
    {
        for (int i = 0; i < castRight.length; i++)
        {
            castRight[i] = new GreenfootImage("images/castAni/cast" + i + ".png");
            castRight[i].scale(100,80);
        }
        
        for (int i = 0; i < castLeft.length; i++)
        {
            castLeft[i] = new GreenfootImage("images/castAni/cast" + i + ".png");
            castLeft[i].mirrorHorizontally();
            castLeft[i].scale(100,80);
        }
        
        if(animationTimer.millisElapsed() < 200)
        {
            return;
        }
        animationTimer.mark();
        
        if(facing.equals("right"))
        {
            setImage(castRight[imageIndex]);
            imageIndex = (imageIndex + 1) % castRight.length;
        }
        
        else
        {
            setImage(castLeft[imageIndex]);
            imageIndex = (imageIndex + 1) % castLeft.length;
        }
    }
    
    public void castIt()
    {
        if (Greenfoot.isKeyDown("f"))
        {
            cast();
        }
    }
    
    public void move()
    {
        int dx = 0;
        if (Greenfoot.isKeyDown("d")) 
        {
            dx++;
            facing = "right";
            walk();
        }
        
        if (Greenfoot.isKeyDown("a")) 
        {
            dx--;
            facing = "left";
            walk();
        }
        
        setLocation(getX()+dx, getY());
        if (isTouching(Boundary.class))
        {
            setLocation(getX()-dx, getY());
        }
        
    }
    
    public void act()
    {
        castIt();
        move();
    }
}
danpost danpost

2022/6/6

#
KCee wrote...
I want to make an animation for casting a fishing rod, but when I click the key to cast ("f"), it only plays 1 frame of the animation. I have to press the key several times for the full animation to play out. << Code Omitted >>
You need more control over the animations. It is probably reverting back to walking once the 'f' key is released. In fact, that is exactly what your castIt method does (cast only if 'f' is down). For the casting animation to continue even after the 'f' key is released, it must be known that the animation is running to begin with. It would be best to load all the images of the animations up front -- in the constructor (just as you have with the walk animations). It would also be better to use an act counter field instead of a SimpleTimer object, to control the speed of the animation; that way the action performed stays in time with other actors in the world. This is not a necessity, but it also adds consistency to the game. Finally, you will need a field to indicate whether the casting animation is running or not. The field needs to also indication which casting animation is running; so, the best thing to do is have the field hold the currently running animation. With this field, you can probably remove the String facing field. The currentFrame and imageIndex fields can probably be removed as well because the act counter value can be used to determine the current frame and image. I also noticed that the private int speed field is not used anywhere; so, it can be removed as well. I wrote a simple test scenario to illustrate. My Man class resulted in the following:
import greenfoot.*;

public class Man extends Actor
{
    static final Color TRANS = new Color(0, 0, 0, 0);
    
    GreenfootImage[] walkR, walkL, castR, castL, currAnim;
    int animTimer;
    
    public Man()
    {
        // initialize and load image arrays
        walkR = new GreenfootImage[5];
        walkL = new GreenfootImage[5];
        castR = new GreenfootImage[5];
        castL = new GreenfootImage[5];
        for (int i=0; i<5; i++)
        {
            walkR[i] = new GreenfootImage("wR"+(i+1), 30, Color.BLACK, TRANS);
            walkL[i] = new GreenfootImage("wL"+(i+1), 30, Color.GRAY, TRANS);
            castR[i] = new GreenfootImage("cR"+(i+1), 30, Color.BLUE, TRANS);
            castL[i] = new GreenfootImage("cL"+(i+1), 30, Color.CYAN, TRANS);
        }
        // initialize image of actor
        setAnimation(walkR);
    }
    
    /** begin an animation */
    private void setAnimation(GreenfootImage[] anim)
    {
        currAnim = anim;
        animTimer = -1;
        setImage();
    }
    
    /** continue an animation */
    private void setImage()
    {
        animTimer = (animTimer+1)%(10*currAnim.length);
        if (animTimer%10 == 0) setImage(currAnim[animTimer/10]);
    }
    
    public void act()
    {
        if ( ! casting() ) move();
    }
    
    private boolean casting()
    {
        // currently casting
        if (currAnim == castL || currAnim == castR)
        {
            setImage();
            if (animTimer == 0) setAnimation(currAnim == castL ? walkL : walkR);
            else return true;
        }
        // initiating casting
        if (Greenfoot.isKeyDown("f"))
        {
            setAnimation(currAnim == walkL ? castL : castR);
            return true;
        }
        return false;
    }
    
    private void move()
    {
        // moving
        int dx = 0;
        if (Greenfoot.isKeyDown("a")) dx--;
        if (Greenfoot.isKeyDown("d")) dx++;
        if (dx == 0) return;
        setLocation(getX()+dx, getY());
        if (dx < 0 && currAnim != walkL) setAnimation(walkL);
        else if (dx > 0 && currAnim != walkR) setAnimation(walkR);
        else setImage();
    }
}
My for loop from line 17 to line 23 loads mock images into the arrays. You will need to replace that code to load your images there.
KCee KCee

2022/6/7

#
thank you! another question so i want to make my fisherman catch different types of fish. Each fish will have an assigned rarity and ill use "Greenfoot.getRandomNumber" to choose which fish gets caught. How would I implement something like this? i want the fisherman to catch a fish at the end of the casting animation and have the image of the fish caught displayed in the middle of the screen for a few seconds. you don't have to give me all the code if its really complicated I just wanna know how i would add something like this in my game
danpost danpost

2022/6/7

#
KCee wrote...
thank you! another question so i want to make my fisherman catch different types of fish. Each fish will have an assigned rarity and ill use "Greenfoot.getRandomNumber" to choose which fish gets caught. How would I implement something like this? i want the fisherman to catch a fish at the end of the casting animation and have the image of the fish caught displayed in the middle of the screen for a few seconds. you don't have to give me all the code if its really complicated I just wanna know how i would add something like this in my game
This is one way to approach it. You could list the different types of fish using their images (in an image array). Then, you could have a second array that has a one-to-one correspondence with the image list, giving the number of each type fish in the water. Now, you can easily get the total number of fish in the water and, by use of another array, choose exactly one at random. This third array would need to be created when needed and could be filled with index values from the image array, one for each fish in the water. Here is an example of code used in world class:
// fields (arrays to be loaded in the constructor of the world)
private final int typeCount = 3;
private GrenfootImage[] fishImages = new GreenfootImage[typeCount];
private int[] fishCount = new int[typeCount];

/** to choose one fish */
// get total number of fish in water
int totalFish = 0;
for (int i=0; i<typeCount; i++) totalFish += fishCount[i];

// build array of all fish (as type using index of image array)
int[] fishes = new int[totalFish];
int n = 0;
for (int t=0; t<typeCount; t++)
{
    for (int i=0; i<fishCount[t]; i++) fishes[n+i] = t;
    n += fishCount[t];
}

// choose a caught fish
int caughtFish = Greenfoot.getRandomNumber(totalFish);
int type = fishes[caughtFish]; // type of fish caught
fishCount[type]--; // one less of this type in water
GreenfootImage image = fishImages[type]; // image of the type caught
KCee KCee

2022/6/8

#
i was thinking of something like an unlimited pool of fish to catch, and just each type of fish would have a percentage chance of being caught everytime I reeled in my fishing rod. So maybe a common fish would have a 60% chance while a rare would have a 20%, and an epic maybe 15% and a legendary maybe 5%? would i just remove all the ints for the amount of fishes and types of fishes? and instead i would make it so if the randomizer picked a number from 1-60 it would catch a common and a number from 61-80 would be a rare and so on?
Spock47 Spock47

2022/6/8

#
Yes, very good, that's a second way how to do it. Having said that, in danpost's solution you can make the pool unlimited, too, by just not removing the caught fish from the pool: Line 23 (fishCount--;) removes the caught fish. If you just delete this line, the original percentages will be used for each time a fish is caught. Just for reference, here is some example code for the second way (but with 62%, 27%, 11% for the three fish types):
import java.util.function.Supplier;
import java.util.HashMap;
import java.util.Map;

public class Fish extends Actor
{
    // Define fish types with probabilities.
    private static final Map<Supplier<Fish>, Integer> fishTypes = new HashMap<>() {{
        put(() -> { return new StarFish(); }, 27);
        put(() -> { return new BubbleFish(); }, 62);
        put(() -> { return new Unagi(); }, 11);
    }};
    
    public static Fish getRandom() {
        final int weightSum = fishTypes.values().stream().mapToInt(d->d).sum();
        final int choice = Greenfoot.getRandomNumber(weightSum);
        int upperLimitForCurrentOption = 0;
        for (final Map.Entry<Supplier<Fish>, Integer> fishType : fishTypes.entrySet()) {
            upperLimitForCurrentOption += fishType.getValue();
            if (choice <= upperLimitForCurrentOption) {
                return fishType.getKey().get();
            }
        }
        return new Fish();
    }
}
Each call of "Fish.getRandom()" gives you a fish (62% probability for a bubble fish and so on).
danpost danpost

2022/6/8

#
In my example code above, just load percentages into the fishCount array and remove line 23. If you use percentages, you do not need to add up the values for totalFish -- you can just use a literal '100'. That is, remove lines 7 thru 10 and replace any remaining occurrences of totalFish with 100.
KCee KCee

2022/6/9

#
danpost wrote...
In my example code above, just load percentages into the fishCount array and remove line 23. If you use percentages, you do not need to add up the values for totalFish -- you can just use a literal '100'.
sorry I'm a little confused about loading percentages into the fishCount array. Something like this?:
private int[] fishCount = new int[10, 20, 70];
danpost danpost

2022/6/9

#
KCee wrote...
I'm a little confused about loading percentages into the fishCount array. Something like this?: << Code Omitted >>
Close -- more like this:
private int[] fishCount = new int[] { 10, 20, 70 };
KCee KCee

2022/6/9

#
so to actually have the images for the types of fish would I put
fishImages[typeCount] = new GreenfootImage("Images/fishTypes/fish" + typeCount + ".png");
in the constructor of the world? also since i want to use the getFish method whenever i reel in, would i make another method like reeledFish in MyWorld that would play the getFish method every time the key to reel in a fish is pressed
danpost danpost

2022/6/9

#
KCee wrote...
so to actually have the images for the types of fish would I put
fishImages[typeCount] = new GreenfootImage("Images/fishTypes/fish" + typeCount + ".png");
in the constructor of the world?
You cannot assign anything to an array using an index equal to the number of elements in the array (IndexOutOfBoundsException will occur). You will need to use a for loop to assign the images.
since i want to use the getFish method whenever i reel in, would i make another method like reeledFish in MyWorld that would play the getFish method every time the key to reel in a fish is pressed
No. You can place that part in the reeling method -- when animTimer resets back to zero.
KCee KCee

2022/6/9

#
since i want to use the getFish method whenever i reel in, would i make another method like reeledFish in MyWorld that would play the getFish method every time the key to reel in a fish is pressed
No. You can place that part in the reeling method -- when animTimer resets back to zero. When I call MyWorld.getFish it says that non static method getFish cannot be referenced from a static context so i tried changing the type count and fish count to static types as well but the problem persists
//code in the world
private static int typeCount = 1;
GreenfootImage[] fishImages = new GreenfootImage[typeCount];
private static int[] fishCount = new int[] {10, 20, 70};
public int score = 0;
public Label scoreLabel = new Label(0,80);

// code in fisherman class
    boolean reeling()
    {
        if (currAnim == reelLeft || currAnim == reelRight)
        {
            setImage();
            if(animTimer == 0)
            {
                setAnimation(currAnim == reelLeft ? walkLeft : walkRight);
                MyWorld.getFish();
            }
            else
            {
                return true;
            }
        }
danpost danpost

2022/6/10

#
KCee wrote...
When I call MyWorld.getFish it says that non static method getFish cannot be referenced from a static context so i tried changing the type count and fish count to static types as well but the problem persists << Code Omitted >>
Nothing here should be static.
//code in the world
private int typeCount = 3;
GreenfootImage[] fishImages = new GreenfootImage[typeCount];
private int[] fishCount = new int[] {10, 20, 70};
public int score = 0;
public Label scoreLabel = new Label(0,80);

// code in fisherman class
    boolean reeling()
    {
        if (currAnim == reelLeft || currAnim == reelRight)
        {
            setImage();
            if(animTimer == 0)
            {
                setAnimation(currAnim == reelLeft ? walkLeft : walkRight);
                ((MyWorld)getWorld()).getFish();
            }
            else
            {
                return true;
            }
        }
See lines 17 to see how to call the method in your MyWorld class. The getWorld method returns a World object. But, the method is in your MyWorld class. So, you need to tell the compiler that the method is in your MyWorld class.
KCee KCee

2022/6/10

#
When i call the getFish method should I assign the fish images im using in the world or in the fisherman class? because right now im assigning the images in a for loop in the world but the images arent showing up when I reel in the fishing rod
public class MyWorld extends World
{

    /**
     * Constructor for objects of class MyWorld.
     * 
     */
    private final int typeCount = 1;
    GreenfootImage[] fishImages;
    private int[] fishCount = new int[] {10, 20, 70};
    public int score = 0;
    public Label scoreLabel = new Label(0,80);
    public MyWorld()
    {    
        super(600, 400, 1, false); 
        fishImages = new GreenfootImage[typeCount];
        Fisherman man = new Fisherman();
        addObject(man, 200, 275);
        Boat boat = new Boat();
        addObject(boat, 200, 300);
        //invisible boundary for boat
        Boundary boundary1 = new Boundary();
        addObject(boundary1, 62, 300);
        Boundary boundary2 = new Boundary();
        addObject(boundary2, 325, 300);
        //score
        addObject(scoreLabel, 50, 50);
        
        for (int i = 0; i < 1; i++)
        {
            fishImages[i] = new GreenfootImage("Images/fishTypes/fish" + i + ".png");
        }
    }

    //choosing a fish
    public void getFish()
    {
        int[] fishes = new int[100];
        int n = 0;
        /*making an array for each type of fish using the percentages in the
        fishCount array so that when it chooses a random number, depending on 
        which fish's array the number is in,it will choose that type of fish*/
        for (int t=0; t<typeCount; t++)
        {
            for (int i=0; i<fishCount[t]; i++) 
            fishes[n+i] = t;
            n += fishCount[t];
        }
         
        // choose a caught fish
        int caughtFish = Greenfoot.getRandomNumber(100);
        int type = fishes[caughtFish]; // type of fish caught
        GreenfootImage image = fishImages[type];
        
    }
}
danpost danpost

2022/6/10

#
KCee wrote...
When i call the getFish method should I assign the fish images im using in the world or in the fisherman class? because right now im assigning the images in a for loop in the world but the images arent showing up when I reel in the fishing rod
From line 10, I presume you have 3 different types of fish each with its own image. So, 3 is your typeCount value -- not 1. Change line 8 to reflect that. Also, your for loop is only loading one image. Change line 29 to the following:
for (int i=0; i<typeCount; i++)
Finally, line 53 loads the image of the chosen fish into the variable image. You need further code (after line 53) to do something with that image so it is displayed. You may also want to add some extra code to track the fish caught, add to the score, etc.
There are more replies on the next page.
1
2