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

2021/12/9

Collisions not working properly

1
2
theVeritas_ theVeritas_

2021/12/9

#
Hi, I'm making bomberman game, and I'm having problem with collisions. They work fine, but when I try to go from left to right or right to left, then near the edge, I can walk through them, otherwise they work, as can be seen through the gif if it works with BBcode. So, I'm using getOneObjectAtOffset method for collisions in my Player class, act method. Player is 32x32 and so are other assets. It doesn't even recognize it as wall when I go through it at that part, tested it with println. This is the code for it:
Actor rightCollide = getOneObjectAtOffset(16, 0, Collisions.class);
        if (rightCollide != null) {
            if (Greenfoot.isKeyDown(keyRight)){
                moveX = 0; // set direction
                if (direction != direction.RIGHT){ // if I wasn't already moving this direction...
                    frame = 1; // start again at frame 1
                    direction = direction.RIGHT; // set direction to the newly specified direction
                }
            }
        } else {
            if (Greenfoot.isKeyDown(keyRight)){
                moveX = 1; // set direction
                if (direction != direction.RIGHT){ // if I wasn't already moving this direction...
                    frame = 1; // start again at frame 1
                    direction = direction.RIGHT; // set direction to the newly specified direction
                }
            }
        }
danpost danpost

2021/12/9

#
As you are probably aware, your code is bulky, as well as troublesome. Using getOneObjectAtOffset(0, 0, Collisions.class), no detection will be made until the colliding object reaches the center of the player. Even if using 16 for one of the values, it will not work if the intersection occurs outside the center of movement (i.e. when moving right, the colliding actor partially intersects, but less than half way into above or below the center axis of the player. That, obviously, is not what you want. If you use isTouching(Collision.class), detection will occur if any part of the actors' images are intersecting. Note that the bulkiness can be reduced by doing the following actions to build your move coding: (1) determine direction of movement by what keys are down; (2) move, unrestricted, in that direction; (3) check for is touching collisions actor, and if so, reverse move taken in (2). Another way, which might be more appropriate in your case, would be to only move right or left if y-value is 16 more than an odd multiple of 32; and only move up or down if x-value is 16 more than an odd multiple of 32. That should keep the player always in the center of a path. You can then, if the value is right, check for a colliding object prior to moving using plus or minus 32 instead of 16. This only needs checked when the coordinate-value along movement is 16 more than an odd multiple of 32. My Bomberman player movement code would involve the following (which does not account for animating the image; with 32x32 grid):
int dir; // angle of vector of movement divided by 90

public void act()
{
    // detect keys down, cancelling opposing keys
    int dx = 0, dy = 0;
    if (Greenfoot.isKeyDown("left")) dx--;
    if (Greenfoot.isKeyDown("right")) dx++;
    if (Greenfoot.isKeyDown("up")) dy--;
    if (Greenfoot.isKeyDown("down")) dy++;
    // do nothing if no keys down (or only opposing keys down)
    if (dx == 0 && dy == 0) return;
    // if two directions detected, set turn, if possible, else set last
    if (dx*dy != 0)
    {
        // cancel one of the directions
        if (dir%2 == 0) // horizontal
        {
            if (canMoveVertically()) dx = 0; else dy = 0;
        }
        else // vertical
        {
            if (canMoveHorizontally()) dy = 0; else dx = 0;
        }
    }
    // save direction (0 = right, 1 = down, 2 = left, 3 = up)
    dir = (1-dx)*dx*dx + (2-dy)*dy*dy;
    // move
    setRotation(90*dir);
    move(2);
    setRotation(0);
    // reverse move if collision occurs
    if (isTouching(Block.class))
    {
        setRotation(90*dir);
        move(-2);
        setRotation(0);
    }
}

// return true if x value is appropriate for moving vertically, else return false
private boolean canMoveVertically()
{
    return (getX()-48)%64 == 0;
}

// return true if y value is appropriate for moving horizontally, else return false
private boolean canMoveHorizontally()
{
    return (getY()-48)%64 == 0;
}
Oh, and I added the blocks into the world with the following code in the world constructor:
for (int y=0; y<getHeight()/32; y++) for (int x=0; x<getWidth()/32; x++)
{
    boolean add = x == 0 || x == (getWidth()-32)/32; // left and right sides
    add = add || y == 0 || y == (getHeight()-32)/32; // top and bottom
    add = add || ((x%2)+(y%2) == 0); // all places where x and y are both even
    if (add) addObject(new Block(), x*32+16, y*32+16);
}
The animation set changes can be made before the else on lines 19 and 23 (add brackets to group with the changing direction). I like this code because the player automatically turns when two keys are down, allowing "diagonal" movement.
theVeritas_ theVeritas_

2021/12/11

#
Yeah, it was bulky, fixed it. I tried using isTouching() before, but It won't work, as the player would have to be exactly in between walls, that's why I tried getOneObjectAtOffset, also the same with
// return true if x value is appropriate for moving vertically, else return false
private boolean canMoveVertically()
{
    return (getX()-48)%64 == 0;
}
 
// return true if y value is appropriate for moving horizontally, else return false
private boolean canMoveHorizontally()
{
    return (getY()-48)%64 == 0;
}
as I can only move if the player is centered and that's impossible most of the time when moving right/left between walls. I also used similiar code for adding blocks to world.
danpost danpost

2021/12/11

#
If play in movement between walls is allowed, then, simply:
int dx = 0, dy = 0;
if (Greenfoot.isKeyDown("left")) dx--;
if (Greenfoot.isKeyDown("right")) dx++;
if (Greenfoot.isKeyDown("up")) dy--;
if (Greenfoot.isKeyDown("down")) dy++;
setLocation(getX()+dx, getY()+dy);
if (isTouching(Collisions.class)) setLocation(getX()-dx, getY()-dy);
theVeritas_ theVeritas_

2021/12/12

#
So, I reworked the movement based on yours, now it works, but still needs to be precise to move. This is my movement code now:
        int dx = 0, dy = 0;
        int dir;
        //  Movement
        if (Greenfoot.isKeyDown(keyRight)){
            if (canMoveHorizontally())
                dx+=2;
        }
        if (Greenfoot.isKeyDown(keyLeft)){
            if (canMoveHorizontally())
                dx-=2;
        }
        if (Greenfoot.isKeyDown(keyUp)){
            if (canMoveVertically())
                dy-=2;
        } 
        if (Greenfoot.isKeyDown(keyDown)){
            if (canMoveVertically())
                dy+=2;
        }
        
        dir = (1-dx)*dx*dx + (2-dy)*dy*dy;
        // if two directions detected, set turn, if possible, else set last
        if (dx*dy != 0) {
            // cancel one of the directions
            if (dir%2 == 0) { // horizontal
                if (canMoveVertically()) dx = 0; else dy = 0;
            } else { // vertical
                if (canMoveHorizontally()) dy = 0; else dx = 0;
            }
        }
        
        setLocation(this.getX()+dx, getY()+dy);
        if (!canMove())
            setLocation(getX()-dx, getY()-dy);
I also have canMoveHorizontally() & canMoveVertically() and canMove() is just returning !isTouching(Collisions.class). The if (dx*dy !=) statement helps with it, when I'm moving horizontally and want to go vertically it will go to next free space as you can see here: But when I try to move horizontally from vertically then It won't work, it won't change direction. Why?
danpost danpost

2021/12/13

#
Try separating the horizontal movement from the vertical movement:
setLocation(getX())+dx, getY());
if (!canMove()) setLocation(getX()-dx, getY());
setLocation(getX(), getY()+dy);
if (!canMove()) setLocation(getX(), getY()-dy);
theVeritas_ theVeritas_

2021/12/13

#
Nope, still the same :/
danpost danpost

2021/12/13

#
Please provide width and height of wall image and player image, plus code placing them into world.
theVeritas_ theVeritas_

2021/12/13

#
Every image is 32x32, world is 672, 480, 1 cell. The way I place them, is by using X & Y arrays containing cell coordinates, in my case
private int wallsX[] = {2, 4, 6, 8, 10, 12, 14, 16, 18};
private int wallsY[] = {2, 4, 6, 8, 10, 12};
And then I use this function createWalls(wallsX, wallsY); in world constructor to make them:
public void createWalls(int x[], int y[]) {
        for (int o = 0; o < wallsX.length; o++ ) {
            for (int i = 0; i < wallsY.length; i++) {
                this.addObject(new Collisions(), x[o]*32+16, y[i]*32+16);
            }
        }
    }
Player is added almost the same way, but just takes x & y int coordinates as arguments in createPlayer(32*cellX+16, 32*cellY+16) function, in my case createPlayer(32*1+16, 32*11+16);
public void createPlayer(int x, int y) {
        //
        Player player = new Player("up", "left", "down", "right");
        this.addObject(player, x, y);
    }
danpost danpost

2021/12/13

#
Remove the if statements in your movement code above on lines 5, 9, 13 and 17. They will all be taken care of by line 33.
theVeritas_ theVeritas_

2021/12/13

#
Still not working, I think the fault is in dir variable, as it's always an even number, so it only exectues
if (dir%2 == 0) { // horizontal
    if (canMoveVertically()) dx = 0; else dy = 0;
danpost danpost

2021/12/13

#
theVeritas_ wrote...
Still not working, I think the fault is in dir variable, as it's always an even number, so it only exectues
Probably. I did not think about that. I had coded it to add or subtract 1 not 2. Use +2*dx, -2*dx, +2*dy and -2*dy in the setLocation lines and remove the *2 in the isKeyDown blocks.
theVeritas_ theVeritas_

2021/12/13

#
Uh, now dir is always odd number ._.
danpost danpost

2021/12/14

#
theVeritas_ wrote...
Uh, now dir is always odd number ._.
Please show current class codes.
theVeritas_ theVeritas_

2021/12/14

#
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)

public class Player extends Actor {
    private final String keyUp;
    private final String keyDown;
    private final String keyLeft;
    private final String keyRight;
    
    public Player(String aKeyUp, String aKeyLeft, String aKeyDown, String aKeyRight) {
        keyUp = aKeyUp;
        keyLeft = aKeyLeft;
        keyDown = aKeyDown;
        keyRight = aKeyRight;
    }
    
    public void act() {
        int dx = 0, dy = 0;
        int dir;
        //  Movement
        if (Greenfoot.isKeyDown(keyRight))
            dx++;
        
        if (Greenfoot.isKeyDown(keyLeft))
            dx--;
        
        if (Greenfoot.isKeyDown(keyUp))
            dy--;
         
        if (Greenfoot.isKeyDown(keyDown))
            dy++;
        
        
        dir = (1-dx)*dx*dx + (2-dy)*dy*dy;
        // if two directions detected, set turn, if possible, else set last
        if (dx*dy != 0) {
            // cancel one of the directions
            if (dir%2 == 0) { // horizontal
                if (canMoveVertically()) dx = 0; else dy = 0;
            } else { // vertical
                if (canMoveHorizontally()) dy = 0; else dx = 0;
            }
        }
        
        setLocation(getX()+2*dx, getY() );
        if (!canMove()) 
            setLocation(getX()-2*dx, getY() );
        setLocation(getX(), getY()+2*dy);
        if (!canMove()) 
            setLocation(getX(), getY()-2*dy);
    }  

    private boolean canMove() {
        return !isTouching(Collisions.class);
    }
    
    private boolean canMoveVertically() {
        return (this.getX()-48)%64 == 0;
        //return true;
    }
    
    private boolean canMoveHorizontally() {
        return (this.getY()-48)%64 == 0;
    }
}
There are more replies on the next page.
1
2