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

2018/4/17

Transparency & Collisions

Hoohoo2 Hoohoo2

2018/4/17

#
Making collisions that ignore transparency. its fun. Anyway, so I've been working on an arcanists game (that old game from jagex/funorb) it is something of a magical land-based version of gravitee-wars for any who know that one, they are known as "artillery games" The big problem is that while I have finally managed to get a working version of it, it is incredibly slow (meaning at max speed it runs at about the same as .5 speed) here's the code currently (feel free to use this if all you want is accurate collisions, just know that it's slow) It is a modified version of the classic smooth-mover class which is able to handle collisions based on opaque pixels colliding rather than the rectangular GreenfootImages as a whole intersecting.
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
import java.util.List;
import java.util.ArrayList;
/**
 * Write a description of class Mover here.
 * 
 * @author (your name) 
 * @version (a version number or a date)
 */
public class Mover extends Actor
{
private Vector movement;
    private double exactX;
    private double exactY;
    
    
    /**
     * get objects visually overlapping this object
     * @param cls - The type of object to return (null returns all objects)
     * @returns Objects
     */
    public <E extends Mover> List<E> getCollidingObjects(Class<E> cls){
        List<E> objects = getIntersectingObjects(cls);
        List<E> fObjects = new ArrayList(); //finalObjects list. this will contain any objects properly intersecting with this object.
        if(objects.size()>0){
            int x = getImage().getWidth();
            int y = getImage().getHeight();
            int d = (int) Math.ceil(Math.sqrt(Math.pow(x,2)+Math.pow(y,2)));
            double scaleFactor = x>y ? (double) d / (double) y : (double) d / (double) x; // this coupled with the previous 3 lines set up the math for scaling our image to prevent cropping
            GreenfootImage img = new GreenfootImage((int) Math.ceil(x*scaleFactor), (int) Math.ceil(y*scaleFactor)); //this is the objects image so we can scale it to a large enough size that rotation won't crop it.
            img.drawImage(getImage(), (img.getWidth()-getImage().getWidth())/2, (img.getHeight()-getImage().getHeight())/2); //puts our image into the center of the enlarged image for rotating
            img.rotate(getRotation()); //rotating the image to match direction
            boolean[][] bitmask = new boolean[img.getWidth()][img.getHeight()];//Not sure on the terminology, but "bitmask" appears to be how to refer to a 1/0 version of an image which is what this is, 1 (true) being opaque (or partially transparent), 0 (false) being fully transparent
            for(int col = 0; col < bitmask.length; col++){ //Cycles through every x value of image
                for(int row = 0; row < bitmask[0].length; row++){ //Cycles through every y value of image
                    bitmask[col][row] = (img.getColorAt(col, row).getAlpha()>0); //alpha 0 is transparent, this sets the bitmask pixel to the proper transparency t/f state
                }
            }
            for(E a : objects){ //Cycles through every "intersecting" object so we can check if it really is intersecting
                if(isCollidedWith(a, bitmask)){
                    fObjects.add(a);
                }
            }
        }
        return fObjects;
    }
    
    /**
     * For checking lots of objects, you provide the bitmask for this object so it is only calculated once. returns if this and "atr" are visually colliding or not
     * @param atr - the actor to check if it is colliding with
     * @param bitmask - the bitmask of this object
     * @returns true - is collided with "atr", false - isn't collided with "atr"
     */
    public boolean isCollidedWith(Actor atr, boolean[][] bitmask){
        int x2 = atr.getImage().getWidth();
        int y2 = atr.getImage().getHeight();
        int d2 = (int) Math.ceil(Math.sqrt(Math.pow(x2,2)+Math.pow(y2,2)));
        double scaleFactor2 = x2>y2 ? (double) d2 / (double) y2 : (double) d2 / (double) x2; // this coupled with the previous 3 lines set up the math for scaling our image to prevent cropping
        GreenfootImage img2 = new GreenfootImage((int) Math.ceil(x2*scaleFactor2), (int) Math.ceil(y2*scaleFactor2)); //this is the objects image so we can scale it to a large enough size that rotation won't crop it.
        img2.drawImage(atr.getImage(), (img2.getWidth()-atr.getImage().getWidth())/2, (img2.getHeight()-atr.getImage().getHeight())/2); //puts our image into the center of the enlarged image for rotating
        img2.rotate(atr.getRotation()); //rotating the image to match direction
        boolean[][] bitmask2 = new boolean[img2.getWidth()][img2.getHeight()];//new bitmask for the image of colliding object, 1 (true) being opaque (or partially transparent), 0 (false) being fully transparent
        for(int col = 0; col < bitmask.length && col < img2.getWidth(); col++){ //Cycles through every x value of image
            for(int row = 0; row < bitmask[0].length && row < img2.getHeight(); row++){ //Cycles through every y value of image
                bitmask2[col][row] = img2.getColorAt(col, row).getAlpha()>0; //alpha 0 is transparent, this sets the bitmask pixel to the proper transparency t/f state
            }
        }
        
        int xOffset = 0; //the col of THIS to start with if - it changes start of atr
        int yOffset = 0; //the row of THIS to start with, if - it changes start of atr
        xOffset += 0-(getX()-atr.getX()); //position offset
        yOffset += 0-(getY()-atr.getY()); //position offset
        xOffset += (int) Math.ceil((double) ((bitmask.length-img2.getWidth())/2.0)); //image offset - goes right 1 pixel for each pixel/2 rounded up
        yOffset += (int) Math.ceil((double) ((bitmask[0].length-img2.getHeight())/2.0)); //image offset - goes right 1 pixel for each pixel/2 rounded up
        int cLength = (xOffset>0 ? (bitmask.length-xOffset > bitmask2.length ? bitmask2.length : bitmask.length-xOffset) : (bitmask2.length+xOffset > bitmask.length ? bitmask.length : bitmask2.length+xOffset)); //amount of cols to process
        int rLength = (yOffset>0 ? (bitmask[0].length-yOffset > bitmask2[0].length ? bitmask2[0].length : bitmask[0].length-yOffset) : (bitmask2[0].length+yOffset > bitmask[0].length ? bitmask[0].length : bitmask2[0].length+yOffset)); //amount of rows to process
        int startX = xOffset>0 ? xOffset : 0;
        int startX2 = xOffset>0 ? 0 : 0-xOffset;
        for(int xChange = 0; xChange < cLength; xChange++){
            int startY = yOffset>0 ? yOffset : 0;
            int startY2 = yOffset>0 ? 0 : 0-yOffset;
            for(int yChange = 0; yChange < rLength; yChange++){
                if(bitmask[xChange+startX][yChange+startY] && bitmask2[xChange+startX2][yChange+startY2]){
                    return true;
                }
            }
        }
        
        return false;
    }
    
    /**
     * For checking a single object, calculates bitmask for this for you. returns if this and "atr" are visually colliding or not
     * @param atr - the actor to check if it is colliding with
     * @returns true - is collided with "atr", false - isn't collided with "atr"
     */
    public boolean isCollidedWith(Actor atr){
        int x = getImage().getWidth();
        int y = getImage().getHeight();
        int d = (int) Math.ceil(Math.sqrt(Math.pow(x,2)+Math.pow(y,2)));
        double scaleFactor = x>y ? (double) d / (double) y : (double) d / (double) x; // this coupled with the previous 3 lines set up the math for scaling our image to prevent cropping
        GreenfootImage img = new GreenfootImage((int) Math.ceil(x*scaleFactor), (int) Math.ceil(y*scaleFactor)); //this is the objects image so we can scale it to a large enough size that rotation won't crop it.
        img.drawImage(getImage(), (img.getWidth()-getImage().getWidth())/2, (img.getHeight()-getImage().getHeight())/2); //puts our image into the center of the enlarged image for rotating
        img.rotate(getRotation()); //rotating the image to match direction
        boolean[][] bitmask = new boolean[img.getWidth()][img.getHeight()];//Not sure on the terminology, but "bitmask" appears to be how to refer to a 1/0 version of an image which is what this is, 1 (true) being opaque (or partially transparent), 0 (false) being fully transparent
        for(int col = 0; col < bitmask.length; col++){ //Cycles through every x value of image
            for(int row = 0; row < bitmask[0].length; row++){ //Cycles through every y value of image
                bitmask[col][row] = img.getColorAt(col, row).getAlpha()!=0; //alpha 0 is transparent, this sets the bitmask pixel to the proper transparency t/f state
            }
        }
        int x2 = atr.getImage().getWidth();
        int y2 = atr.getImage().getHeight();
        int d2 = (int) Math.ceil(Math.sqrt(Math.pow(x2,2)+Math.pow(y2,2)));
        double scaleFactor2 = x2>y2 ? (double) d2 / (double) y2 : (double) d2 / (double) x2; // this coupled with the previous 3 lines set up the math for scaling our image to prevent cropping
        GreenfootImage img2 = new GreenfootImage((int) Math.ceil(x2*scaleFactor2), (int) Math.ceil(y2*scaleFactor2)); //this is the objects image so we can scale it to a large enough size that rotation won't crop it.
        img2.drawImage(atr.getImage(), (img2.getWidth()-atr.getImage().getWidth())/2, (img2.getHeight()-atr.getImage().getHeight())/2); //puts our image into the center of the enlarged image for rotating
        img2.rotate(atr.getRotation()); //rotating the image to match direction
        boolean[][] bitmask2 = new boolean[img2.getWidth()][img2.getHeight()];//new bitmask for the image of colliding object, 1 (true) being opaque (or partially transparent), 0 (false) being fully transparent
        for(int col = 0; col < bitmask.length; col++){ //Cycles through every x value of image
            for(int row = 0; row < bitmask[0].length; row++){ //Cycles through every y value of image
                bitmask2[col][row] = img2.getColorAt(col, row).getAlpha()!=0; //alpha 0 is transparent, this sets the bitmask pixel to the proper transparency t/f state
            }
        }
        
        //check stuff
        
        return false;
    }
    
    public Mover()
    {
        this(new Vector());
    }
    
    /**
     * Create new thing initialised with given speed.
     */
    public Mover(Vector movement)
    {
        this.movement = movement;
    }
    
    /**
     * Move in the current movement direction. Wrap around to the opposite edge of the
     * screen if moving out of the world.
     */
    public void move() 
    {
        exactX = exactX + movement.getX();
        exactY = exactY + movement.getY();
        if(exactX >= getWorld().getWidth()) {
            exactX = 0;
        }
        if(exactX < 0) {
            exactX = getWorld().getWidth() - 1;
        }
        if(exactY >= getWorld().getHeight()) {
            exactY = 0;
        }
        if(exactY < 0) {
            exactY = getWorld().getHeight() - 1;
        }
        super.setLocation((int) exactX, (int) exactY);
    }
    
    /**
     * Set the location from exact coordinates.
     */
    public void setLocation(double x, double y) 
    {
        exactX = x;
        exactY = y;
        super.setLocation((int) x, (int) y);
    }
    
    /**
     * Set the location from int coordinates.
     */
    public void setLocation(int x, int y) 
    {
        exactX = x;
        exactY = y;
        super.setLocation(x, y);
    }

    /**
     * Return the exact x-coordinate (as a double).
     */
    public double getExactX() 
    {
        return exactX;
    }

    /**
     * Return the exact y-coordinate (as a double).
     */
    public double getExactY() 
    {
        return exactY;
    }

    /**
     * Increase the speed with the given vector.
     */
    public void addForce(Vector force) 
    {
        movement.add(force);
    }
    
    /**
     * Accelerate the speed of this mover by the given factor. (Factors < 1 will
     * decelerate.)
     */
    public void accelerate(double factor)
    {
        movement.scale(factor);
        if (movement.getLength() < 0.15) {
            movement.setNeutral();
        }
    }
    
    /**
     * Return the speed of this actor.
     */
    public double getSpeed()
    {
        return movement.getLength();
    }
    
    /**
     * Stop movement of this actor.
     */
    public void stop()
    {
        movement.setNeutral();
    }
    
    /**
     * Return the current speed.
     */
    public Vector getMovement() 
    {
        return movement;
    }
}
Hoohoo2 Hoohoo2

2018/4/20

#
So... is this not set up properly or something? going through the discussion board almost everything else has been replied to, did I set it up wrong, or is this just not something people are interested in?
danpost danpost

2018/4/20

#
Hoohoo2 wrote...
So... is this not set up properly or something? going through the discussion board almost everything else has been replied to, did I set it up wrong, or is this just not something people are interested in?
There does not appear to be a question here. It seems like you posted the given code for others who may wish to use it. Why would you think a reply would be forthcoming?
Yehuda Yehuda

2018/4/20

#
See Pixel-perfect collision detection. When I went to the scenario to get the URL for the link I noticed that it does seem slow. Maybe in HTML5 it's slower than regular.
Hoohoo2 Hoohoo2

2018/4/30

#
Er... I suppose it was mostly giving it for use, but also I was asking if anyone knew a more efficient method considering how much it lagged out greenfoot.
Hoohoo2 Hoohoo2

2018/4/30

#
Yehuda wrote...
See Pixel-perfect collision detection. When I went to the scenario to get the URL for the link I noticed that it does seem slow. Maybe in HTML5 it's slower than regular.
It appears that that is what I am using, I didn't look at the code at all, but it seems about as slow as what mine was running at.
Yehuda Yehuda

2018/4/30

#
The scenario I suggested is only slow in HTML5 (on the site) not in regular Greenfoot. Yours is slow in the IDE (according to what you say) unlike Busch's (your code is also more complicated).
You need to login to post a reply.