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

2019/11/10

Reflection law calculate new direction (Tank Game)

1
2
3
SushiLlama SushiLlama

2019/11/12

#
Oh I'm so Sorry, I already has this idea but I gave up on it. Yes, for small angles this might be kind of inaccurate. Anyway, I could not figure out how to really code it. Thank you so much! Going to test it right when I am home. Is there maybe some kind of formula to calculate the real distance between image center and intersection coordinate? My shell is just a rectangle with a half circle on the front. By the way it's only like 10×4 pixels which makes it questionable if more precise calculation is needed, since Greenfoot is not about perfection but learning things. Thanks for your kind help! So glad to have this forum!
danpost danpost

2019/11/12

#
SushiLlama wrote...
Is there maybe some kind of formula to calculate the real distance between image center and intersection coordinate?
"intersection coordinate"? seems like a misnomer. There will be many points of intersection. If you are asking if there is a way to calculate the exact distances along the horizontal and vertical the wall was encroached, I am sure there is. << Convolution Omitted >> LOL There might be an easier way. Maybe using sin or cos on rotation of shell to adjust to the actual upright rectangular scope of the image. Then use basic intersecting techniques along horizontal and vertical.
SushiLlama SushiLlama

2019/11/13

#
Sadly the shell now just folds like paper when hitting a wall. It seems like the turn axis is the back of the shell :( By the way i removed the move in the beginning of the method because the shell already moved at that point
danpost danpost

2019/11/13

#
SushiLlama wrote...
i removed the move in the beginning of the method because the shell already moved at that point
I think you better show what you have for the method.
SushiLlama SushiLlama

2019/11/14

#
danpost wrote...
SushiLlama wrote...
i removed the move in the beginning of the method because the shell already moved at that point
I think you better show what you have for the method.

public boolean checkBounce() {
        if (getOneIntersectingObject(Wall.class) != null)
        {   //undo initial move
            move(-speed);
            int dx = (int) (Math.cos(Math.PI*getQR()/QVAL/180)*speed*QVAL);
            move(dx, 0*QVAL);
            Actor wall = getOneIntersectingObject(Wall.class);
            move(-dx, 0);
            if (wall != null) {
                /**
                 * its a left right one
                 */
                int shellToWall = getImage().getWidth()/2*QVAL;
                move(shellToWall);
                setQRotation(180*QVAL-getQR());
                move(shellToWall);
            }
            int dy = (int) (Math.sin(Math.PI*getQR()/QVAL/180)*speed*QVAL);
            move(dy, 90*QVAL);
            wall = getOneIntersectingObject(Wall.class);
            move(-dy, 90*QVAL);
            if (wall != null)
            {
                /**
                 * its a top bottom one
                 */
                int shellToWall = getImage().getWidth()/2*QVAL;
                move(shellToWall);
                setQRotation(-getQR());
                move(shellToWall);
            }
            bounceCounter++;
            return true;
        }
        return false;
    }

Sadly this does not work. I tried to: I 1. undo initial move II 2. get x amount of move vector 3. undo check x case move 4. check if wall is hit 5. my new vector = measure distance wall / shell 6. move my new vector 7. set rotation with matching formula: setQRotation(180*QVAL-getQR() 8. move my new vector III 9. get y amount of move vector 10. undo check y case move 11. check if wall is hit 12. my new vector = measure distance wall / shell 13. move my new vector 14. set rotation with matching formula: setQRotation(-getQR()) 15. move my new vector Why use the signum function? Thanks!
danpost danpost

2019/11/14

#
Your lines 13 and 27 do not get the correct distances. They only give half of shell's dimension in Q-units. That would be along the line of rotation of the shell, as well, which is not very helpful (it is not along the axial, horizontal or vertical, line of movement).
SushiLLama wrote...
Why use the signum function?
It gets the unit direction, which can be multiplied by any distance, positive or negative, to move with respect to the initial movement direction. I was thinking that a not quite exact bounce, but close and easier to code might be the following:
move(speed*QVAL);
if (isTouching(Wall.class))
{
    move(-speed*QVAL);
    int dx = (int) (Math.cos(Math.PI*getQR()/QVAL/180)*speed*QVAL);
    move(dx, 0*QVAL);
    if (isTouching(Wall.class))
    {
        move(-dx, 0*QVAL);
        setQRotation(180*QVAL-getQR());
    }
    int dy = (int) (Math.sin(Math.PI*getQR()/QVAL/180)*speed*QVAL);
    move(dy, 90*QVAL);
    if (isTouching(Wall.class))
    {
        move(-dy, 90*QVAL);
        setQRotation(-getQR());
    }
}
SushiLlama SushiLlama

2019/11/14

#
danpost wrote...
Your lines 13 and 27 do not get the correct distances. They only give half of shell's dimension in Q-units. That would be along the line of rotation of the shell, as well, which is not very helpful (it is not along the axial, horizontal or vertical, line of movement).
SushiLLama wrote...
Why use the signum function?
It gets the unit direction, which can be multiplied by any distance, positive or negative, to move with respect to the initial movement direction. I was thinking that a not quite exact bounce, but close and easier to code might be the following:
move(speed*QVAL);
if (isTouching(Wall.class))
{
    move(-speed*QVAL);
    int dx = (int) (Math.cos(Math.PI*getQR()/QVAL/180)*speed*QVAL);
    move(dx, 0*QVAL);
    if (isTouching(Wall.class))
    {
        move(-dx, 0*QVAL);
        setQRotation(180*QVAL-getQR());
    }
    int dy = (int) (Math.sin(Math.PI*getQR()/QVAL/180)*speed*QVAL);
    move(dy, 90*QVAL);
    if (isTouching(Wall.class))
    {
        move(-dy, 90*QVAL);
        setQRotation(-getQR());
    }
}
I dont know if i am doing something wrong, but it still won´t work. Why do we do a move before the if clause? Anyway i´ll give you all my shells moving methods. You already spent a lot of time for helping me, so i understand if it takes some longer time.
public class Shell extends Projectile
{
    private final int EXPLOSION_OFFSET = 10;
    /**
     * TODO: Fix Shell not firing for first five times!
     */
    public Shell(Tank tank) {
        this.shootingTank = tank;
        this.speed = 1;
    }
    public void act() {
        if (!checkBounce()) {
            if(!checkTankHit()) {
                if(!checkShellHit()) {
                    if(!checkMineHit()) {
                        executeShell();
                        smoke();
                        if(smokeTimer > 0) {
                            smokeTimer--;
                        }
                    }
                }
            }
        }
    }
    public void removeProjectile() {
        getWorld().removeObject(this);
    }
    public boolean checkShellHit() {
        if(getOneIntersectingObject(Shell.class) != null) {
            Shell hitShell = (Shell) getOneIntersectingObject(Shell.class);
            if(shootingTank instanceof PlayerTank) {
                PlayerTank playerTank = (PlayerTank) shootingTank;
                playerTank.reloadShell();
            }
            Explosion explosion = new Explosion();
            getWorld().addObject(explosion, getX(), getY());
            explosion.move(EXPLOSION_OFFSET*QVAL, getRotation());
            this.detonate();
            hitShell.detonate();
            return true;
        }
        return false;
    }
    public boolean checkMineHit() {
        if(getOneIntersectingObject(Mine.class) != null) {
            Mine hitMine = (Mine)getOneIntersectingObject(Mine.class);
            hitMine.setExplodeTimer(0);
            if(shootingTank instanceof PlayerTank) {
                PlayerTank playerTank = (PlayerTank) shootingTank;
                playerTank.reloadShell();   
                removeProjectile();
                return true;
            }
        }
        return false;
    }
    public void executeShell() {
        if(bounceCounter == 2) {
            if(shootingTank instanceof PlayerTank) {
                PlayerTank playerTank = (PlayerTank) shootingTank;
                playerTank.reloadShell();   
            } 
            removeProjectile();    
        } else {
            move(speed*QVAL);
        }
    }
    public void smoke() {
        if(this.getWorld() != null && smokeTimer == 0) {    
            Smoke smoke = new Smoke();
            smoke.setRotation(getRotation());
            getWorld().addObject(smoke, getX(), getY());
            smokeTimer = 1;
        }
    }

    public boolean checkBounce() {
        //move(speed*QVAL);
        if (getOneIntersectingObject(Wall.class) != null && bounceCounter < 2)
        {
            move(-speed*QVAL);
            int dx = (int) (Math.cos(Math.PI*getQR()/QVAL/180)*speed*QVAL);
            move(dx, 0*QVAL);
            if (getOneIntersectingObject(Wall.class) != null)
            {
                move(-dx, 0*QVAL);
                setQRotation(180*QVAL-getQR());
            }
            int dy = (int) (Math.sin(Math.PI*getQR()/QVAL/180)*speed*QVAL);
            move(dy, 90*QVAL);
            if (getOneIntersectingObject(Wall.class) != null)
            {
                move(-dy, 90*QVAL);
                setQRotation(-getQR());
            }
            bounceCounter++;
            return true;
        }
        return false;
    }


    public void detonate() {
        //Create explosion and add it to world
        Explosion explosion = new Explosion();
        getWorld().addObject(explosion, getX(), getY());
        //Adjust explosion position
        explosion.move(EXPLOSION_OFFSET*QVAL, getRotation());
        //reload shell if playerTank
        if (isPlayer(shootingTank)) {
            PlayerTank playerTank = (PlayerTank)shootingTank;
            playerTank.reloadShell();
        }
        //remove Shell
        removeProjectile();
    }

    public boolean checkTankHit() {
        //save touching tank as hitTank
        Tank hitTank = (Tank) getOneIntersectingObject(Tank.class);
        if(hitTank != null) {
            //if is a PlayerTank
            if(hitTank instanceof PlayerTank) {
                //condition for valid kill
                if(hitTank != shootingTank || bounceCounter != 0) {
                    Level level = (Level) getWorld();
                    level.getPlayerHealthGUI().setLife(level.getPlayerHealthGUI().getLife()-1);
                    detonate();
                    return true;
                }
            } else {
                //if is not PlayerTank
                //condition for valid kill
                if(hitTank != shootingTank || bounceCounter != 0) {
                    detonate();
                    hitTank.destroyTank();
                    return true;
                }
            }

        }
        return false;
    }

    public boolean isPlayer(Tank tank) {
        //check if given player is the player
        if(shootingTank instanceof PlayerTank) {
            PlayerTank playerTank = (PlayerTank)tank;
            return true;
        } else {
            return false;
        }
    }
}

danpost danpost

2019/11/14

#
You will not maintain a state of being off a wall if you check for a bounce before moving. The order of execution should be along these lines:
move;
smoke;
if (at edge) bounce;
if (hit wall)
{
  bounce;
  if (bounced twice) remove;
}
else if (hit enemy shell or tank)
{
    explode;
    remove;
}
The Explosion object should remove any objects within a set range of its location.
SushiLlama SushiLlama

2019/11/14

#
danpost wrote...
You will not maintain a state of being off a wall if you check for a bounce before moving. The order of execution should be along these lines:
move;
smoke;
if (at edge) bounce;
if (hit wall)
{
  bounce;
  if (bounced twice) remove;
}
else if (hit enemy shell or tank)
{
    explode;
    remove;
}
The Explosion object should remove any objects within a set range of its location.
Thank you so much! You are so nice! How much time you give other peoples problems. Just wow.!
SushiLlama SushiLlama

2019/11/14

#
Removing if bounceCounter == 1 (or should it be 2?) is now broken. On angles smaller than 90 the bullets sometimes glitch into the wall, or return towards my tank after bouncing. Sadly i dont know why. This confuses me :(

public class Shell extends Projectile
{
    private final int EXPLOSION_OFFSET = 10;

    public Shell(Tank tank) {
        this.shootingTank = tank;
        this.speed = 1;
    }
    public void act() {
        move(speed*QVAL);
        smoke();
        if(smokeTimer > 0) {
            smokeTimer--;
        }
        if(getOneIntersectingObject(Wall.class) != null) {
            checkBounce();
            checkBounceCounter();
        } else if(!checkTankHit()) {
            if(!checkShellHit()) {
                checkMineHit();
            }
        }
    }
    public void checkBounceCounter() {
    // bounceCounter == 1 or == 2???


        if(bounceCounter == 1) {
            if(shootingTank instanceof PlayerTank) {
                PlayerTank playerTank = (PlayerTank) shootingTank;
                playerTank.reloadShell();
            }
            removeProjectile();
        }
    }

    public void removeProjectile() {
        getWorld().removeObject(this);
    }
    public boolean checkShellHit() {
        if(getOneIntersectingObject(Shell.class) != null) {
            Shell hitShell = (Shell) getOneIntersectingObject(Shell.class);
            if(shootingTank instanceof PlayerTank) {
                PlayerTank playerTank = (PlayerTank) shootingTank;
                playerTank.reloadShell();
            }
            Explosion explosion = new Explosion();
            getWorld().addObject(explosion, getX(), getY());
            explosion.move(EXPLOSION_OFFSET*QVAL, getRotation());
            this.detonate();
            hitShell.detonate();
            return true;
        }
        return false;
    }
    public boolean checkMineHit() {
        if(getOneIntersectingObject(Mine.class) != null) {
            Mine hitMine = (Mine)getOneIntersectingObject(Mine.class);
            hitMine.setExplodeTimer(0);
            if(shootingTank instanceof PlayerTank) {
                PlayerTank playerTank = (PlayerTank) shootingTank;
                playerTank.reloadShell();   
                removeProjectile();
                return true;
            }
        }
        return false;
    }
    public void smoke() {
        if(this.getWorld() != null && smokeTimer == 0) {    
            Smoke smoke = new Smoke();
            smoke.setRotation(getRotation());
            getWorld().addObject(smoke, getX(), getY());
            smokeTimer = 1;
        }
    }

    public boolean checkBounce() {
        move(speed*QVAL);
        if (getOneIntersectingObject(Wall.class)!=null && bounceCounter <2)
        {
            move(-speed*QVAL);
            int dx = (int) (Math.cos(Math.PI*getQR()/QVAL/180)*speed*QVAL);
            move(dx, 0*QVAL);
            if (getOneIntersectingObject(Wall.class) != null)
            {
                move(-dx, 0*QVAL);
                setQRotation(180*QVAL-getQR());
            }
            int dy = (int) (Math.sin(Math.PI*getQR()/QVAL/180)*speed*QVAL);
            move(dy, 90*QVAL);
            if (getOneIntersectingObject(Wall.class) != null)
            {
                move(-dy, 90*QVAL);
                setQRotation(-getQR());
            }
            return true;
        }
        return false;
    }

    public void detonate() {
        //Create explosion and add it to world
        Explosion explosion = new Explosion();
        getWorld().addObject(explosion, getX(), getY());
        //Adjust explosion position
        explosion.move(EXPLOSION_OFFSET*QVAL, getRotation());
        //reload shell if playerTank
        if (isPlayer(shootingTank)) {
            PlayerTank playerTank = (PlayerTank)shootingTank;
            playerTank.reloadShell();
        }
        //remove Shell
        removeProjectile();
    }

    public boolean checkTankHit() {
        //save touching tank as hitTank
        Tank hitTank = (Tank) getOneIntersectingObject(Tank.class);
        if(hitTank != null) {
            //if is a PlayerTank
            if(hitTank instanceof PlayerTank) {
                //condition for valid kill
                if(hitTank != shootingTank || bounceCounter != 0) {
                    Level level = (Level) getWorld();
                    level.getPlayerHealthGUI().setLife(level.getPlayerHealthGUI().getLife()-1);
                    detonate();
                    return true;
                }
            } else {
                //if is not PlayerTank
                //condition for valid kill
                if(hitTank != shootingTank || bounceCounter != 0) {
                    detonate();
                    hitTank.destroyTank();
                    return true;
                }
            }

        }
        return false;
    }

    public boolean isPlayer(Tank tank) {
        //check if given player is the player
        if(shootingTank instanceof PlayerTank) {
            PlayerTank playerTank = (PlayerTank)tank;
            return true;
        } else {
            return false;
        }
    }
}

danpost danpost

2019/11/14

#
Remove line 79. You perform that op at line 10.
SushiLlama SushiLlama

2019/11/14

#
danpost wrote...
Remove line 79. You perform that op at line 10.
Still the same problem :(
danpost danpost

2019/11/15

#
Try the following, where I added lines 7 and 15:
public boolean checkBounce()
{
    if (getOneIntersectingObject(Wall.class)!=null && bounceCounter <2)
    {
        move(-speed*QVAL);
        int dx = (int) (Math.cos(Math.PI*getQR()/QVAL/180)*speed*QVAL);
        dx += dx > 0 ? 99 : -99;
        move(dx, 0*QVAL);
        if (getOneIntersectingObject(Wall.class) != null)
        {
            move(-dx, 0*QVAL);
            setQRotation(180*QVAL-getQR());
        }
        int dy = (int) (Math.sin(Math.PI*getQR()/QVAL/180)*speed*QVAL);
        dy += dy > 0 ? 99 : -99;
        move(dy, 90*QVAL);
        if (getOneIntersectingObject(Wall.class) != null)
        {
            move(-dy, 90*QVAL);
            setQRotation(-getQR());
        }
        return true;
    }
    return false;
}
SushiLlama SushiLlama

2019/11/15

#
danpost wrote...
Try the following, where I added lines 7 and 15:
public boolean checkBounce()
{
    if (getOneIntersectingObject(Wall.class)!=null && bounceCounter <2)
    {
        move(-speed*QVAL);
        int dx = (int) (Math.cos(Math.PI*getQR()/QVAL/180)*speed*QVAL);
        dx += dx > 0 ? 99 : -99;
        move(dx, 0*QVAL);
        if (getOneIntersectingObject(Wall.class) != null)
        {
            move(-dx, 0*QVAL);
            setQRotation(180*QVAL-getQR());
        }
        int dy = (int) (Math.sin(Math.PI*getQR()/QVAL/180)*speed*QVAL);
        dy += dy > 0 ? 99 : -99;
        move(dy, 90*QVAL);
        if (getOneIntersectingObject(Wall.class) != null)
        {
            move(-dy, 90*QVAL);
            setQRotation(-getQR());
        }
        return true;
    }
    return false;
}
There are two errors: One is that the shell bounces mirrored on the orthogonal of the wall (comes straight back the way it went before). The second problem is bullets getting stuck in the wall (by not moving far away from the wall or so?). I analyzed the behaviour and could not find a regular pattern. But what i found out is that for most shells, a near to orthogonal bounce almost always results in the shell coming-just-back (mirror) error. Larger angles make the bullet disappear (roughly 20-40 degrees in relation to wall orthogonal). Angles bigger than 45 degrees are close to always without any problems. So sorry to still bother you :( Hope you got an idea
danpost danpost

2019/11/15

#
See if a larger value than 99 (and -99) will do the trick. Try 100, 150, 199 (I would not think more than 199 would be needed. The initial 99 was for the fact that the QActor is not placed at its Q-location (cannot be placed between pixels on the screen). The additional amount I am suggesting now is for the change in rotation of the actor (the center of the image along an even sized dimension is a half pixel off from where the actor is actually placed on the screen). These values are on the two lines I just added (two values on each line).
There are more replies on the next page.
1
2
3