简体   繁体   中英

Collision detection in a java based 2D space shooter game

The context behind the this question is that I am part of a small group working to create a small game on Android. The game we have chosen to develop is a top-down 2D shooter where the player controls a spaceship that must defend itself from alien creatures that move towards the player from the top of the gamescreen, similar to space invader, but different in that they spawn in random patterns.

So far, we have been able to get the game running, have the player and enemies to spawn and move in the right direction, as well as the player's weapon which is labelled as "projectile" (although it currently just spawns on its own and not on the player command). The issue we have though is that we are trying to set up a collision event in the code between the projectile and the enemies so that when they meet, the enemy and bullet both disappear.

However, what we have currently written in the code does not seem to make this work, so I was wondering if perhaps anyone else could see where we are going wrong. I've listed the relevant classes of code below. We would greatly appreciate any help anyone can give.

Collision Detector class( Holds the behavoir for collision detection for all entities in the game).

package uk.ac.qub.eeecs.gage.util;

import uk.ac.qub.eeecs.gage.world.GameObject;

/**
 * Collision Detector Helper Library
 * 
 * @version 1.0
 */
public class CollisionDetector {

    /**
     * Type of collision
     */
    public enum CollisionType {
        None, Top, Bottom, Left, Right, destroy //for the platform game, not yours
    };

    /**
     * Determine if the two specified bounding boxes are in collision
     * 
     * @param one
     *            First bounding box
     * @param two
     *            Second bounding box
     * @return boolean true if the boxes overlap, false otherwise
     */
    //is relevant to your game
    public static boolean isCollision(BoundingBox one, BoundingBox two) {
        return (one.x - one.halfWidth < two.x + two.halfWidth
                && one.x + one.halfWidth > two.x - two.halfWidth
                && one.y - one.halfHeight < two.y + two.halfHeight && one.y
                + one.halfHeight > two.y - two.halfHeight); 

        //Do we add the code to set the enemies to disappear/destroy here or in the 
        //individual classes such as the player?
    }

    /**
     * Determine the type of collision between the two bounding boxes.
     * CollisionType.None is returned if there are no collisions.
     * 
     * @param one
     *            First bounding box
     * @param two
     *            Second bounding box
     * @return Collision type
     */

AISpaceship.java class (class used for emeny alien entities and their properties).

package uk.ac.qub.eeecs.game.spaceDemo;

import uk.ac.qub.eeecs.gage.ai.SteeringBehaviours;
import uk.ac.qub.eeecs.gage.engine.ElapsedTime;
import uk.ac.qub.eeecs.gage.util.CollisionDetector;
import uk.ac.qub.eeecs.gage.util.Vector2;
import uk.ac.qub.eeecs.gage.util.CollisionDetector.CollisionType;
import uk.ac.qub.eeecs.gage.world.GameObject;
import uk.ac.qub.eeecs.gage.world.Sprite;

/**
 * AI controlled spaceship
 * 
 * @version 1.0
 */
    public class AISpaceship extends Sprite {

// /////////////////////////////////////////////////////////////////////////
// Properties
// /////////////////////////////////////////////////////////////////////////

/**
 * AI control behaviour
 */

/**
 * Acceleration with which the spaceship can move along
 * the x-axis
 */
private float RUN_ACCELERATION = 150.0f;

public enum ShipBehaviour {
    Turret, Seeker
}

private boolean visible;

private ShipBehaviour mShipBehaviour;

/**
 * Distance at which the spaceship should avoid other game objects
 */
private float separateThresholdShip = 75.0f;
private float separateThresholdAsteroid = 125.0f;

/**
 * Accumulators used to build up the net steering outcome
 */
private Vector2 accAccumulator = new Vector2();
private Vector2 accComponent = new Vector2();



// /////////////////////////////////////////////////////////////////////////
// Constructors
// /////////////////////////////////////////////////////////////////////////

/**
 * Create a AI controlled spaceship
 * 
 * @param startX
 *            x location of the AI spaceship
 * @param startY
 *            y location of the AI spaceship
 * @param shipBehaviour
 *            Steering behaviour to be used by the AI ship
 * @param gameScreen
 *            Gamescreen to which AI belongs
 */

public AISpaceship(float startX, float startY, ShipBehaviour shipBehaviour,
        SteeringDemoGameScreen gameScreen) {
    super(startX, startY, 50.0f, 50.0f, null, gameScreen);

    mShipBehaviour = shipBehaviour;

    visible = true;

    switch (mShipBehaviour) {
    case Turret:
        maxAcceleration = 0.0f;
        maxVelocity = 0.0f;
        mBitmap = gameScreen.getGame().getAssetManager().getBitmap("Turret");
        break;
    case Seeker:
        maxAcceleration = -40.0f;
        maxVelocity = 50.0f;
        mBitmap = gameScreen.getGame().getAssetManager().getBitmap("enemy sprites 7");
        break;
    }
}

// /////////////////////////////////////////////////////////////////////////
// Methods
// /////////////////////////////////////////////////////////////////////////

/*
 * (non-Javadoc)
 * 
 * @see
 * uk.ac.qub.eeecs.gage.world.Sprite#update(uk.ac.qub.eeecs.gage.engine.
 * ElapsedTime)
 */
@Override
public void update(ElapsedTime elapsedTime) {

    switch (mShipBehaviour) {
    case Seeker:
        //Move down towards the player in a straight line
        acceleration.y = RUN_ACCELERATION;

        // Seek towards the player

        // Try to avoid a collision with the playership


        // Try to avoid a collision with the other spaceships           


        // Try to avoid a collision with the asteroids
        SteeringBehaviours.separate(this,
                ((SteeringDemoGameScreen) mGameScreen).getAsteroids(),
                separateThresholdAsteroid, 1.0f, accComponent);
        accAccumulator.add(accComponent);

        // If we are trying to avoid a collision then combine
        // it with the seek behaviour, placing more emphasis on
        // avoiding the collision.          
        if (!accAccumulator.isZero()) {
            acceleration.x = 0.3f * acceleration.x + 0.7f
                    * accAccumulator.x;
            acceleration.y = 0.3f * acceleration.y + 0.7f
                    * accAccumulator.y;
        }

        // Make sure we point in the direction of travel.

        break;
    }

    // Call the sprite's superclass to apply the determine accelerations
    super.update(elapsedTime);

    // Check that our new position has not collided by one of the
    // defined projectiles. If so, then removing any overlap and
    // ensure a valid velocity.
    checkForAndResolveCollisions(projectiles);      
}

/**
 * Check for and then resolve any collision between the AiShip and the
 * player projectiles.
 * 
 * @param projectiles
 *            Array of projectiles to test for collision against
 */
private void checkForAndResolveCollisions(GameObject[] projectiles) {

    CollisionType collisionType;

    // Consider each platform for a collision
    for (GameObject projectile : projectiles) {
        collisionType = 
                CollisionDetector.determineAndResolveCollision(this, Projectile);

        switch (collisionType) {
        case destroy:
            visible = false;
            break;
        }
    }

}

}

Projectile1 (The class used to determine all the game's projectiles and their behavoir).

package uk.ac.qub.eeecs.game.spaceDemo;

import uk.ac.qub.eeecs.gage.ai.SteeringBehaviours;
import uk.ac.qub.eeecs.gage.engine.ElapsedTime;
import uk.ac.qub.eeecs.gage.util.Vector2;
import uk.ac.qub.eeecs.gage.world.Sprite;

/**
 * AI controlled spaceship
 * 
 * @version 1.0
 */
public class Projectile1 extends Sprite {

    // /////////////////////////////////////////////////////////////////////////
    // Properties
    // /////////////////////////////////////////////////////////////////////////

    /**
     * AI control behaviour
     */

    /**
     * Acceleration with which the projectile can move along
     * the x-axis
     */
    private float RUN_ACCELERATION = 150.0f;


    public enum ShipBehaviour {
        bullet
    }

    private ShipBehaviour mShipBehaviour;

    /**
     * Distance at which the spaceship should avoid other game objects
     */


    /**
     * Accumulators used to build up the net steering outcome
     */
    private Vector2 accAccumulator = new Vector2();
    private Vector2 accComponent = new Vector2();

    // /////////////////////////////////////////////////////////////////////////
    // Constructors
    // /////////////////////////////////////////////////////////////////////////

    /**
     * Create a AI controlled spaceship
     * 
     * @param startX
     *            x location of the AI spaceship
     * @param startY
     *            y location of the AI spaceship
     * @param shipBehaviour
     *            Steering behaviour to be used by the AI ship
     * @param gameScreen
     *            Gamescreen to which AI belongs
     */
    public Projectile1(float startX, float startY, ShipBehaviour shipBehaviour,
            SteeringDemoGameScreen gameScreen) {
        super(startX, startY, 50.0f, 50.0f, null, gameScreen);

        mShipBehaviour = shipBehaviour;

        switch (mShipBehaviour) {
        case bullet:
            maxAcceleration = 20.0f;
            maxVelocity = 30.0f;
            mBitmap = gameScreen.getGame().getAssetManager().getBitmap("Spaceship2");
            break;

        }
    }

    // /////////////////////////////////////////////////////////////////////////
    // Methods
    // /////////////////////////////////////////////////////////////////////////

    /*
     * (non-Javadoc)
     * 
     * @see
     * uk.ac.qub.eeecs.gage.world.Sprite#update(uk.ac.qub.eeecs.gage.engine.
     * ElapsedTime)
     */
    @Override
    public void update(ElapsedTime elapsedTime) {

        switch (mShipBehaviour) {
        case bullet:
            // Seek towards the player

                acceleration.y = RUN_ACCELERATION;




            break;
        }

        // Call the sprite's superclass to apply the determine accelerations
        super.update(elapsedTime);
    }
}

SterringGameDemo (The main class for the level on which the game play occurs and where the player, enemies and projectiles behave and interact with each other (note-it is called "SteeringGameDemo" because this is a leftover from a template we used to help make the class and has not been renamed yet)).

package uk.ac.qub.eeecs.game.spaceDemo;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import uk.ac.qub.eeecs.gage.Game;
import uk.ac.qub.eeecs.gage.engine.AssetStore;
import uk.ac.qub.eeecs.gage.engine.ElapsedTime;
import uk.ac.qub.eeecs.gage.engine.graphics.IGraphics2D;
import uk.ac.qub.eeecs.gage.util.BoundingBox;
import uk.ac.qub.eeecs.gage.util.CollisionDetector;
import uk.ac.qub.eeecs.gage.world.GameObject;
import uk.ac.qub.eeecs.gage.world.GameScreen;
import uk.ac.qub.eeecs.gage.world.LayerViewport;
import uk.ac.qub.eeecs.gage.world.ScreenViewport;
import android.graphics.Color;

/**
 * Simple steering game world 
 * 
 * @version 1.0
 */
public class SteeringDemoGameScreen extends GameScreen {

    // /////////////////////////////////////////////////////////////////////////
    // Properties
    // /////////////////////////////////////////////////////////////////////////

    /**
     * Width and height of the level 
     */
    private final float LEVEL_WIDTH = 480.0f;
    private final float LEVEL_HEIGHT = 280.0f;

    /**
     * Define viewports for this layer and the associated screen projection
     */
    private ScreenViewport mScreenViewport;
    private LayerViewport mLayerViewport;

    /**
     * Define a background object, alongside a player controlled
     * space ship and separate lists of asteroids and AI controlled
     * space ships.
     */

    private GameObject mSpaceBackground;

    private PlayerSpaceship mPlayerSpaceship;


    private final int NUM_ASTEROIDS = 0;
    private List<Asteroid> mAsteroids;

    private final int NUM_SEEKERS = 2;
    private final int NUM_TURRETS = 1;
    private final int NUM_PROJECTILE = 1;
    private List<Projectile1> mProjectile1;
    private List<AISpaceship> mAISpaceships;

    // /////////////////////////////////////////////////////////////////////////
    // Constructors
    // /////////////////////////////////////////////////////////////////////////

    /**
     * Create a simple steering game world
     * 
     * @param game
     *            Game to which this screen belongs
     */
    public SteeringDemoGameScreen(Game game) {
        super("SteeringDemoGameScreen", game);

        // Create the screen viewport
        mScreenViewport = new ScreenViewport(0, 0, game.getScreenWidth(),
                game.getScreenHeight());

        // Create the layer viewport, taking into account the orientation
        // and aspect ratio of the screen.
        if (mScreenViewport.width > mScreenViewport.height)
            mLayerViewport = new LayerViewport(240.0f, 240.0f
                    * mScreenViewport.height / mScreenViewport.width, 240,
                    240.0f * mScreenViewport.height / mScreenViewport.width);
        else
            mLayerViewport = new LayerViewport(240.0f * mScreenViewport.height
                    / mScreenViewport.width, 240.0f, 240.0f
                    * mScreenViewport.height / mScreenViewport.width, 240);

        // Load in the assets used by the steering demo
        AssetStore assetManager = mGame.getAssetManager();
        assetManager.loadAndAddBitmap("Space BG 2", "img/Space BG 2.png");      
        assetManager.loadAndAddBitmap("Asteroid1", "img/Asteroid1.png");
        assetManager.loadAndAddBitmap("Asteroid2", "img/Asteroid2.png");
        assetManager.loadAndAddBitmap("enemy sprites 7", "img/enemy sprites 7.png");
        assetManager.loadAndAddBitmap("Spaceship2", "img/Spaceship2.png");
        assetManager.loadAndAddBitmap("Spaceship3", "img/Spaceship3.png");
        assetManager.loadAndAddBitmap("Spaceship3", "img/Spaceship3.png");
        assetManager.loadAndAddBitmap("Player sprite", "img/Player sprite.png");
        assetManager.loadAndAddBitmap("Turret", "img/Turret.png");

        // Create the space background
        mSpaceBackground = new GameObject(LEVEL_WIDTH / 2.0f,
                LEVEL_HEIGHT / 2.0f, LEVEL_WIDTH, LEVEL_HEIGHT, getGame()
                        .getAssetManager().getBitmap("Space BG 2"), this);

        // Create the player spaceship
        mPlayerSpaceship = new PlayerSpaceship(230, 10, this);



        // Create a number of randomly positioned asteroids
        Random random = new Random();
        mAsteroids = new ArrayList<Asteroid>(NUM_ASTEROIDS);
        for (int idx = 0; idx < NUM_ASTEROIDS; idx++)
            mAsteroids.add(new Asteroid(random.nextFloat() * LEVEL_WIDTH, random.nextFloat() * LEVEL_HEIGHT, this));


        // Create a number of randomly positioned AI controlled ships
        mAISpaceships = new ArrayList<AISpaceship>(NUM_SEEKERS + NUM_TURRETS);
        for (int idx = 0; idx < NUM_SEEKERS; idx++)
            mAISpaceships.add(new AISpaceship(random.nextFloat() * LEVEL_WIDTH,
                    180 + random.nextFloat() * LEVEL_HEIGHT,
                    AISpaceship.ShipBehaviour.Seeker, this));
        for (int idx = 0; idx < NUM_TURRETS; idx++)
            mAISpaceships.add(new AISpaceship(random.nextFloat() * LEVEL_WIDTH,
                    random.nextFloat() * LEVEL_HEIGHT,
                    AISpaceship.ShipBehaviour.Turret, this));

        //Use the above to help spawn the bullets
        mProjectile1 = new ArrayList<Projectile1>(NUM_PROJECTILE);
        for (int idx = 0; idx < NUM_PROJECTILE; idx++)
        mProjectile1.add(new Projectile1(mPlayerSpaceship.position.x, mPlayerSpaceship.position.y,
                Projectile1.ShipBehaviour.bullet, this));

    }

    // /////////////////////////////////////////////////////////////////////////
    // Support methods
    // /////////////////////////////////////////////////////////////////////////

    /**
     * Return the player spaceship 
     * 
     * @return Player spaceship
     */ 
    public PlayerSpaceship getPlayerSpaceship() {
        return mPlayerSpaceship;
    }

    public List<Projectile1> getProjectile1() {
        return mProjectile1;
    }

    /**
     * Return a list of the AI spaceships in the level
     * 
     * @return List of AI controlled spaceships
     */
    public List<AISpaceship> getAISpaceships() {
        return mAISpaceships;
    }

    /**
     * Return a list of asteroids in the the level
     * 
     * @return List of asteroids in the level
     */
    public List<Asteroid> getAsteroids() {
        return mAsteroids;
    }

    // /////////////////////////////////////////////////////////////////////////
    // Update and Draw methods
    // /////////////////////////////////////////////////////////////////////////

    /*
     * (non-Javadoc) fs
     * 
     * @see
     * uk.ac.qub.eeecs.gage.world.GameScreen#update(uk.ac.qub.eeecs.gage.engine
     * .ElapsedTime)
     */
    @Override
    public void update(ElapsedTime elapsedTime) {

        // Update the player spaceship
        mPlayerSpaceship.update(elapsedTime);

        // Ensure the player cannot leave the confines of the world
        BoundingBox playerBound = mPlayerSpaceship.getBound();
        if (playerBound.getLeft() < 0)
            mPlayerSpaceship.position.x -= playerBound.getLeft();
        else if (playerBound.getRight() > LEVEL_WIDTH)
            mPlayerSpaceship.position.x -= (playerBound.getRight() - LEVEL_WIDTH);

        if (playerBound.getBottom() < 0)
            mPlayerSpaceship.position.y -= playerBound.getBottom();
        else if (playerBound.getTop() > LEVEL_HEIGHT)
            mPlayerSpaceship.position.y -= (playerBound.getTop() - LEVEL_HEIGHT);

        // Ensure the enemyships cannot leave the confines of the world


        //Use the above for player and enemies bullets in this class


        //IMPORTANT - THE below code is VITAL for the collision detection and was a
                //added by the tutor. It calls the bounding box and collision detector for the
                //player and enemy ship. Add this to the above section where the bounding box
                // is already called, just below the update method.
                BoundingBox playersBound = mPlayerSpaceship.getBound();
                for (AISpaceship aiSpaceship : mAISpaceships) {
                    BoundingBox aiBound = aiSpaceship.getBound();
                    if(CollisionDetector.isCollision(playersBound, aiBound)) {


                    }
                }


        // Focus the layer viewport on the player


        // Ensure the viewport cannot leave the confines of the world
        if (mLayerViewport.getLeft() < 0)
            mLayerViewport.x -= mLayerViewport.getLeft();
        else if (mLayerViewport.getRight() > LEVEL_WIDTH)
            mLayerViewport.x -= (mLayerViewport.getRight() - LEVEL_WIDTH);

        if (mLayerViewport.getBottom() < 0)
            mLayerViewport.y -= mLayerViewport.getBottom();
        else if (mLayerViewport.getTop() > LEVEL_HEIGHT)
            mLayerViewport.y -= (mLayerViewport.getTop() - LEVEL_HEIGHT);

        // Update each of the AI controlled spaceships
        for (AISpaceship aiSpaceship : mAISpaceships)
            aiSpaceship.update(elapsedTime);

        // Update each of the asteroids
        for (Asteroid asteroid : mAsteroids)
            asteroid.update(elapsedTime);

        // Update each of the Player Projectile1
                for (Projectile1 projectile1 : mProjectile1)
                    projectile1.update(elapsedTime);


    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * uk.ac.qub.eeecs.gage.world.GameScreen#draw(uk.ac.qub.eeecs.gage.engine
     * .ElapsedTime, uk.ac.qub.eeecs.gage.engine.graphics.IGraphics2D)
     */
    @Override
    public void draw(ElapsedTime elapsedTime, IGraphics2D graphics2D) {

        // Create the screen to black and define a clip based on the viewport
        graphics2D.clear(Color.BLACK);
        graphics2D.clipRect(mScreenViewport.toRect());

        // Draw the background first of all
        mSpaceBackground.draw(elapsedTime, graphics2D, mLayerViewport,
                mScreenViewport);

        // Draw each of the asteroids
        for (Asteroid asteroid : mAsteroids)
            asteroid.draw(elapsedTime, graphics2D, mLayerViewport,
                    mScreenViewport);

        // Draw each of the AI controlled spaceships
                for (Projectile1 projectile1 : mProjectile1)
                    projectile1.draw(elapsedTime, graphics2D, mLayerViewport,
                            mScreenViewport);

        // Draw each of the AI controlled spaceships
        for (AISpaceship aiSpaceship : mAISpaceships)
            aiSpaceship.draw(elapsedTime, graphics2D, mLayerViewport,
                    mScreenViewport);

        // Draw the player
        mPlayerSpaceship.draw(elapsedTime, graphics2D, mLayerViewport,
                mScreenViewport);


    }
}

Again, I say thank you to anyone who can help.

I've done something similar before. Try to create a rectangle for every Bullet, Enemy or whatever you've got. The rectangles should have a size thats about the size of the graphics used. Of course the rectangles have to move with the graphics.

You can check if a collision occured simply with

if(rectangleA.intersects(rectangleB)){
    doAwesomeStuff();
}

I hope that helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM