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

2019/9/30

Replace Greenfoot.delay() with a clock/timer to prevent freezes

1
2
SushiLlama SushiLlama

2019/9/30

#
I've got a method called moveBomb() called when a Player throws a bomb into the world. After many unlucky tries i wanted to ask for help converting this method in a non-delay one. The problem now is that i can't move or anything when i drop a bomb for quite a time. I saw videos solving this issue with a clock counting up with help of the act method. The movement was designed to look realistic - the bomb is quickly when thrown, then slows down until it "lands" (done by for loop with function with counting variable). Could anyone explain me how to implement the clock so that i can replace the Greenfoot.delay(10) in the for loops? Would be really awesome! public void moveBomb(Bomb playerBomb, Player summoner) { Player playerOne; Player playerTwo; GameWorld bombWorld = (GameWorld)getWorld(); int a = 0; int b = 0; int playerRotation; switch(summoner.getRotation()) { case 0: a = 1; b = 0; case 90: a = 0; b = 1; case 180: a = -1; b = 0; case 270: a = 0; b = -1; } for(int x=1;x<101;x++) { newBombX = bombX + a * 10/x; newBombY = bombY + b * 10/x; this.setLocation(newBombX, newBombY); bombX = newBombX; bombY = newBombY; Greenfoot.delay(10); } }
danpost danpost

2019/9/30

#
It is not a simple task to convert the given code as you want. Currently, your moveBomb method does ALL the moving in one execution of the code; however what you are wanting is code that will execute multiple times to perform the movement steps. What you want is: (1) player creates bomb and sets its rotation (or saves it in a field) (2) bomb moves itself without regard to player (using act method and timer field) Please attempt something more appropriate and provide complete Bomb class codes as well as code in Player class dealing with bomb creation.
SushiLlama SushiLlama

2019/9/30

#
Youre right! I would need to separate the two methods in some way... But i dont really get how this is done in order to make implementing of a clock easier. By the way, the BombDirection switch always ends up being 270-->up no matter what rotation the player has :( switch(bombDirection) { case 0: a = 1; b = 0; case 90: a = 0; b = 1; case 180: a = -1; b = 0; case 270: a = 0; b = -1; } Maybe we could find a way to solve this quickly? Anyway here is the Code of Player and Bomb BOMB: public class Bomb extends Actor { /** * BombTeam holds the Information about the Bombs Team */ public enum BombTeam { PLAYER_ONE, PLAYER_TWO } private BombTeam bombTeam; private int bombX; private int bombY; private int newBombX; private int newBombY; private boolean isBouncedYet = false; private int bombDirection; private GameWorld bombWorld = (GameWorld)getWorld(); /** * Constructor */ public Bomb(Player summoningPlayer, int x, int y) { bombX = x; bombY = y; if(summoningPlayer.getPlayerTeam() == PlayerTeam.ONE){ bombTeam = BombTeam.PLAYER_ONE; } else if(summoningPlayer.getPlayerTeam() == PlayerTeam.TWO){ bombTeam = BombTeam.PLAYER_TWO; } } /** * Helper methods */ public int getBombX() { return this.bombX; } public int getBombY() { return this.bombY; } public void act() { } /** * MoveBomb gets called when Player throws a Bomb (to animate the Bomb) */ public void moveBomb(Bomb playerBomb, Player summoner) { bombDirection = summoner.getLocalRotation(); Player playerOne; Player playerTwo; GameWorld bombWorld = (GameWorld)getWorld(); int a = 0; int b = 0; int playerRotation; switch(bombDirection) { case 0: a = 1; b = 0; case 90: a = 0; b = 1; case 180: a = -1; b = 0; case 270: a = 0; b = -1; } for(int x=1;x<101;x++) { newBombX = bombX + a * 20/x; newBombY = bombY + b * 20/x; playerBomb.setLocation(newBombX, newBombY); if(!isBouncedYet){ Actor wall = getOneIntersectingObject(Wall.class); if(wall != null) { /** * bounce and move the bomb into the other direction */ isBouncedYet = true; System.out.println("Bounce"); b = -b; a = -a; } } bombX = newBombX; bombY = newBombY; Greenfoot.delay(10); } isBouncedYet = false; // TODO: This sadly only allows the bomb to bounce once, but without the boolean variable it would just get true on getOneIntersectingObject until movement ends. Try to let the bomb multiple bounces !!! } } PLAYER: public class Player extends Actor { protected static final int UP = 270; protected static final int RIGHT = 0; protected static final int DOWN = 90; protected static final int LEFT = 180; boolean isTrueLeft; boolean isTrueRight; private boolean isBombThrown; private Bomb playerBomb; private PlayerTeam playerTeam; protected String upKey; protected String leftKey; protected String downKey; protected String rightKey; protected String bombKey; protected String activateBombKey; protected char lastMove; protected int localRotation; protected int newLocalRotation; public Player(String upKey, String leftKey, String downKey, String rightKey, String bombKey, String activateBombKey, PlayerTeam playerTeam) { this.upKey = upKey; this.leftKey = leftKey; this.downKey = downKey; this.rightKey = rightKey; this.bombKey = bombKey; this.activateBombKey = activateBombKey; this.playerTeam = playerTeam; } public void act() { performMovement(); if(Greenfoot.isKeyDown(bombKey)){ dropBomb(); } if(Greenfoot.isKeyDown(activateBombKey)){ activateBomb(playerBomb); } } public void setPlayerTeam(PlayerTeam playerTeam) { this.playerTeam = playerTeam; } public PlayerTeam getPlayerTeam() { return this.playerTeam; } protected void performMovement() { if (Greenfoot.isKeyDown(upKey)) { lastMove = 'u'; localRotation = UP; if (canMove()) { setLocation(getX(), getY()-1); } } if (Greenfoot.isKeyDown(leftKey)) { lastMove = 'l'; if (getRotation() == RIGHT) { GreenfootImage img = getImage(); img.mirrorVertically(); setImage(img); } localRotation = LEFT; isTrueLeft = true; setRotation(localRotation); if (canMove()) { move(1); } } if (Greenfoot.isKeyDown(downKey)) { lastMove = 'd'; localRotation = DOWN; if (canMove()) { setLocation(getX(), getY()+1); } } if (Greenfoot.isKeyDown(rightKey)) { lastMove = 'r'; if (getRotation() == LEFT) { GreenfootImage img = getImage(); img.mirrorVertically(); setImage(img); } localRotation = RIGHT; setRotation(localRotation); if (canMove()) { move(1); } } } public char getLastMove() { return this.lastMove; } public void dropBomb() { if(!isBombThrown){ playerBomb = new Bomb(this, getX(), getY()); isBombThrown = true; getWorld().addObject(playerBomb, this.getX(), this.getY()); playerBomb.moveBomb(playerBomb, this); } } public void activateBomb(Bomb bomb) { if(isBombThrown) { Star star = new Star(); getWorld().addObject(star, bomb.getBombX(), bomb.getBombY()); Greenfoot.delay(100); getWorld().removeObject(playerBomb); Greenfoot.delay(200); getWorld().removeObject(star); GameWorld gameWorld = (GameWorld) getWorld(); isBombThrown = false; } } public int getLocalRotation() { return this.localRotation; }
danpost danpost

2019/9/30

#
I would suggest that you totally rewrite the Bomb class from scratch. Try to minimize the number of field used in the class (I only see a need for maybe 3 -- direction, timer and bounce_count). Maybe 4 if not allowed to explode on the one who summoned it. The player can create one using:
getWorld().addObject(new Bomb(localRotation, this), getX(), getY());
and the Bomb class can start with:
private int direction;
private Player summoner;
private int timer;

public Bomb(int dir, Player player)
{
    direction = dir;
    summoner = player;
}

public void act()
{
    setRotation(direction);
    move(10);
    setRotation(0);
    // etc.
}
I simplified the act method to show how to have a bomb move in the proper direction without showing any rotation. The direction can be reversed when needed with the following line of code:
direction = (direction+180)%360;
When exploding, it should add a Star object and remove itself. The Star class should also have a timer field to time its own existence in the world.
SushiLlama SushiLlama

2019/9/30

#
Thank you very much! Ill try it out!
SushiLlama SushiLlama

2019/10/2

#
How would you get the opponent? I guess since im kind of a newbie my Code is really not that clean and optimal... Would you mind if I try to rewrite the whole class in a more smart & efficient way and you have a look at it? I hope i can add the timers without big trouble. Otherwise i would be very Glad to get help from you or others! Ill just post the code and status quo tomorrow! Ouh and what did you mean by dividing the movement for the use of timers? My Problem is unterstanding how separating the setting of rotation and actual movement gives us the ability to implement a clock.
danpost danpost

2019/10/3

#
SushiLlama wrote...
How would you get the opponent?
When using getIntersectingObjects(Player.class), There are only 4 possible lists that can arise, only two of which are relevant -- when the list size is 2 and when the list size is 1 AND that element in the list is not the summoner. So, an intersecting non-summoning player can be detected with:
java.util.List pSeen = getIntersectingObjects(Player.class);
if ((pSeen.size() == 1 && (pSeen.get(0) != summoner)) || pSeen.size() == 2)
what did you mean by dividing the movement for the use of timers? My Problem is unterstanding how separating the setting of rotation and actual movement gives us the ability to implement a clock.
In actuality, your for loop is replaced by act steps and since greenfoot regulates the "frame" rate (timing of act cycles), you do not need a timer here. Just move, bounce and, when opp player touched, explode (in act). You will need a speed variable, which can be adjusted each act step.
SushiLlama SushiLlama

2019/10/3

#
I understand how i should optimize my code/classes but the delay States a little bit unclear for me, im afraid. So i would just do what for the game-not-stopping delay? Remove Greenfoot.delay() in the for-Loop and voila? Then the delay is just the time that it takes my laptop to execute the code and theoretically this delay is also non-fluid/program-stopping Same goes for delay with a speed parameter in it. (Small value->fast, large value->slow) Did i get something wrong here?
danpost danpost

2019/10/3

#
SushiLlama wrote...
I understand how i should optimize my code/classes but the delay States a little bit unclear for me, im afraid. So i would just do what for the game-not-stopping delay?
Remove all delay to prevent apparent "stopping".
Remove Greenfoot.delay() in the for-Loop and voila?
Remove the for loop also and rename the method act().
Then the delay is just the time that it takes my laptop to execute the code and theoretically this delay is also non-fluid/program-stopping Same goes for delay with a speed parameter in it. (Small value->fast, large value->slow) Did i get something wrong here?
The speed variable I mentioned previously was not meant in any way to be associated with any delay. It was meant to be used in my line 14 above, in place of the literal constant, 10.
danpost danpost

2019/10/3

#
danpost wrote...
Just move, bounce and, when opp player touched, explode (in act).
public void act()
{
    // move code
    // bounce code
    // explode code
}
With a speed field:
private double speed = 10; // initial value may be different
The move code would just be:
speed = << some expression >>; // depends on how you want bomb to move (vary speed from one instance to next)
setRotation(direction);
move((int)speed); // this is where speed is used
setRotation(0);
SushiLlama SushiLlama

2019/10/3

#
danpost wrote...
danpost wrote...
Just move, bounce and, when opp player touched, explode (in act).
public void act()
{
    // move code
    // bounce code
    // explode code
}
Here i see a problem because the for loop makes the bomb move x times y steps before just ending. The bomb move is calculated by an equation to slow the bomb down after each move (looks nicer). But if i choose to just put all this without a for loop into the act method, my bomb would get a direction, get a speed, set the rotation, move a constant (but i wanted something like a for loop and then move (100/i). Anyway after the move ((int) speed) it would set its rotation back to 0. After one move cycle, the bomb would continue with bouncing. Thats absolutely fine. But instead of doing this like 100 times and then exploding, the bomb explodes rightaway after just one move. If speed would be 5 for example, i would have just moved 5 steps in a constant not linear (looks unrealistic) before it detonates, as far as i understand. Shouldnt the move and bounce code get some if statements? For example the bouncing and parallel checking if touching the opponent can detonate the bomb. So the bomb would just explode and then try to explode again in the //Explode code So i need to make sure i dont execute things twice or execute code on a bomb thats detonated etc in order to avoid NullPointerExceptions right? Sorry for all these questions and thank you for your time and kindness!
danpost danpost

2019/10/3

#
SushiLlama wrote...
Here i see a problem because the for loop makes the bomb move x times y steps before just ending. The bomb move is calculated by an equation to slow the bomb down after each move (looks nicer).
No worries -- not a hard fix. With field:
private double speed = 5:
The expression in the last line 1 above could be:
speed *= 0.98; // adjust as desired
This will slow the bomb down as it moves along.
Shouldnt the move and bounce code get some if statements?
The move code does not require any; however, both the bounce and player checking codes will.
For example the bouncing and parallel checking if touching the opponent can detonate the bomb. So the bomb would just explode and then try to explode again in the //Explode code
Those comments were just to show what all goes in your act method. They can be changed to call other methods to perform those specific functions or you can replace them with the code directly.
So i need to make sure i dont execute things twice or execute code on a bomb thats detonated etc in order to avoid NullPointerExceptions right?
If that happens, and of course, you would hope not, you can always post here for help if you cannot figure out how to fix it.
SushiLlama SushiLlama

2019/10/3

#
The player now is able to drop a bomb:
public void dropBombNew() {
        if(!isBombThrown){
            Greenfoot.playSound("BombaMomba.wav");
            playerBomb = new BombNew(this.localRotation, this);
            isBombThrown = true;
            getWorld().addObject(playerBomb, this.getX(), this.getY()); 
        }
    }
The Code for the bombClass then looks as the following:
import greenfoot.*;  
import java.util.*;
public class BombNew extends Actor
{
    private int direction;
    private Player summoner;
    private Player opponent;
    private int timer;
    private double speed = 100;
    public BombNew(int dir, Player player) {
        direction = dir;
        summoner = player;
    } 
    public void act() {
        //Movement
        speed *= 0.98;
        if(getWorld() != null) {
            setRotation(direction);
            move((int)speed);
            setRotation(0);
            Greenfoot.delay(10);
        }
        //Opponent Intersection
        java.util.List<Player> pSeen = getIntersectingObjects(Player.class);
        if((pSeen.size() == 1 && (pSeen.get(0) != summoner)) || pSeen.size() == 2) {
            //getOpponent
            for(Player player : pSeen) {
                if(player != summoner) {
                    this.opponent = player;
                }
            }
            activateBomb();
        } else {
            //Bouncing
            Wall wall = (Wall) getOneIntersectingObject(Wall.class);
            if(wall != null) {
                //switch direction 180 degrees
                direction = (direction+180)%360;
                speed *= 0.75;
            }
        }
        //Exploding
        if(getWorld() != null) {
            activateBomb();
        }
    }
    public void activateBomb() {
        Star star = new Star();
        getWorld().addObject(star, this.getX(), this.getY());
        if(isOpponentInRadius()) {
            //subtract one heart
            Greenfoot.playSound("LoseLife.mp3");
            HealthGUI opponentHealthGUI = opponent.getHealthGUI();
            opponentHealthGUI.setLife(opponentHealthGUI.getLife()-1);
            //respawn the opponent
            getWorld().removeObject(opponent);
            getWorld().addObject(opponent, Greenfoot.getRandomNumber(900+1),Greenfoot.getRandomNumber(600+1));
        }
        /**
        *   TODO: get some delay in to show the exploding bomb (bomb+star) before removing both
        */ 
        Greenfoot.playSound("BombExplode.wav");
        getWorld().removeObject(this);
        summoner.setIsBombThrown(false);
    }
    public boolean isOpponentInRadius() {
        java.util.List<Player> possibleOpponent = getObjectsInRange(60, Player.class);
        if((possibleOpponent.size() == 1 && (possibleOpponent.get(0) != summoner)) || possibleOpponent.size() == 2) {
            //getOpponent by cycling through 
            for(Player player : possibleOpponent) {
                if(player != summoner) {
                    this.opponent = player;
                }
            }
            return true;
        }
        return false;
    }
}
I managed to implement a star clock to remove itself after a time (600 frames) :
public class Star extends Actor {
    int clockTime = 600;
    public Star(){
        getImage().scale(40, 40);
    }
    public void act(){      
        if(clockTime > 0) {
           clockTime--;
        } else {
            getWorld().removeObject(this);
        }
    }
}
But i failed at implementing a delayTimer for the movement to animate the movement of the bomb instead of "teleporting" it, and also got stuck with waiting 600 frames together with the star, standing still, and then detonate -> remove star (done) remove bomb (at the moment happens so fast you dont see the bomb when playing) Except for these two problems it works like a charm and i never thought i could code so clean :D Thank you danpost!!! I would like to prevent the player respawning in a wall and bombs getting stuck in walls when dropped just before them. But for now i would be happy if we could figure out how to implement the two timers. Thanks so much!
danpost danpost

2019/10/3

#
Remove lines 17 and 22, as there is no chance the bomb is not in the world at that point in the act method (nothing prior in act could possibly remove it and act is only called when the actor is in the world). Remove line 21, also. You do not want to use the delay method in your code (not appropriate, in this instance). Replace lines 24 thru 32 with the following:
for (Object obj : getObjectsInRange(60, Player.class)) if (obj != summoner) opponent = (Player)obj;
if (opponent != null) activateBomb();
Remove lines 33 and 41; then move lines 34 thru 40 to before line 20 and insert before line 40 (what was 40) the following line to prevent bomb from sticking to wall:
move(speed);
Remove lines 42 thru 45. This causes immediate exploding of bomb (why you never saw them). Remove lines 50 and 58. Opponent was previously determined to be in range. Then, remove lines 66 thru 78, as the method is no longer needed. In the Star class, change the 600 in line 2 to, say, 60. Then replace lines 7 thru 11 with this:
if (--clockTime == 0) getWorld().removeObject(this);
SushiLlama SushiLlama

2019/10/3

#
danpost wrote...
Remove lines 33 and 41; then move lines 34 thru 40 to before line 20 and insert before line 40 (what was 40) the following line to prevent bomb from sticking to wall:
move(speed);
Sorry i dont get what you mean by that... i couldnt figure out what to do here and with only the other changes i got this error when dropping a bomb: java.lang.NullPointerException at BombNew.activateBomb(BombNew.java:39) at BombNew.act(BombNew.java:32) at greenfoot.core.Simulation.actActor(Simulation.java:604) at greenfoot.core.Simulation.runOneLoop(Simulation.java:562) at greenfoot.core.Simulation.runContent(Simulation.java:221) at greenfoot.core.Simulation.run(Simulation.java:211) My code in Bomb looks like this:
import greenfoot.*;  
import java.util.*;
public class BombNew extends Actor
{
    private int direction;
    private Player summoner;
    private Player opponent;
    private int timer;
    private double speed = 100;
    public BombNew(int dir, Player player) {
        direction = dir;
        summoner = player;
    } 
    public void act() {
        //Movement
        speed *= 0.98;
        setRotation(direction);
        move((int)speed);
        setRotation(0);
        //Opponent Intersection
        java.util.List<Player> pSeen = getIntersectingObjects(Player.class);
        for (Object obj : getObjectsInRange(60, Player.class)) if (obj != summoner) opponent = (Player)obj;
        if (opponent != null) activateBomb(); 
        //Bouncing
        Wall wall = (Wall) getOneIntersectingObject(Wall.class);
        if(wall != null) {
            //switch direction 180 degrees
            direction = (direction+180)%360;
            speed *= 0.75;
        }
        //Exploding
        activateBomb();
    }
    public void activateBomb() {
        Star star = new Star();
        getWorld().addObject(star, this.getX(), this.getY());
        //subtract one heart
        Greenfoot.playSound("LoseLife.mp3");
        HealthGUI opponentHealthGUI = opponent.getHealthGUI();
        opponentHealthGUI.setLife(opponentHealthGUI.getLife()-1);
        //respawn the opponent
        getWorld().removeObject(opponent);
        getWorld().addObject(opponent, Greenfoot.getRandomNumber(900+1),Greenfoot.getRandomNumber(600+1));        
        /**
        *   TODO: get some delay in to show the exploding bomb (bomb+star) before removing both
        */
        Greenfoot.playSound("BombExplode.wav");
        getWorld().removeObject(this);
        summoner.setIsBombThrown(false);
    }
}
Remove lines 42 thru 45. This causes immediate exploding of bomb (why you never saw them). Well i sadly still dont see them but maybe thats becuase i missed out on the one improvement :( Could you quickly fix this, please?:
danpost wrote...
Remove lines 33 and 41; then move lines 34 thru 40 to before line 20 and insert before line 40 (what was 40) the following line to prevent bomb from sticking to wall:
move(speed);
There are more replies on the next page.
1
2