【问题标题】:How to detect collisions between objects in LibGDX如何检测 LibGDX 中对象之间的碰撞
【发布时间】:2020-04-28 23:49:47
【问题描述】:

这是我关于堆栈溢出的第一篇文章,所以如果我违反了有关发帖等的任何规则,我提前道歉。我一直在开发一款小行星-esque 射击游戏,我可以不知道如何在岩石和激光之间进行碰撞检测。
源代码可以在here找到。我不得不对 LevelScreen 的更新方法进行一些更改,因为原始代码依赖于使用 BlueJ IDE。我在这个post 中找到了一个修复程序,并让宇宙飞船和岩石之间的碰撞起作用。

LevelScreen 类

    package com.mygdx.game;

import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;

import java.util.ArrayList;

public class LevelScreen extends BaseScreen {

    private Spaceship spaceship;
    private boolean gameOver;
    private boolean rocksNeedRemoved;
    private Rock toRemove;

    private ArrayList<Rock> rocks;
    private ArrayList<Laser> lasers;

    public void initialize() {

        gameOver = false;
        toRemove = null;
        rocksNeedRemoved = false;

        BaseActor space = new BaseActor(0, 0, mainStage);
        space.loadTexture("space.png");
        space.setSize(800, 600);
        BaseActor.setWorldBounds(space);

        spaceship = new Spaceship(400, 300, mainStage);

        rocks = new ArrayList<Rock>();
        lasers = new ArrayList<Laser>();

        rocks.add(new Rock(600, 500, mainStage));
        rocks.add(new Rock(600, 300, mainStage));
        rocks.add(new Rock(600, 100, mainStage));
        rocks.add(new Rock(400, 100, mainStage));
        rocks.add(new Rock(200, 100, mainStage));
        rocks.add(new Rock(200, 300, mainStage));
        rocks.add(new Rock(200, 500, mainStage));
        rocks.add(new Rock(400, 500, mainStage));

        lasers.add(new Laser(400, 500, mainStage));


    }

    public void update(float dt) {


        //Code from book(Throws class not found error)
        /*
        for (BaseActor rockActor : BaseActor.getList(mainStage, 
            "Rock")) {

            if (rockActor.overlaps(spaceship)) {

                if (spaceship.shieldPower <= 0) {

                    Explosion boom = new Explosion(0, 0, 
                        mainStage);
                    boom.centerAtActor(spaceship);
                    spaceship.remove();
                    spaceship.setPosition(-1000, -1000);

                    BaseActor messageLose = new BaseActor(0, 0, 
                        uiStage);
                    messageLose.loadTexture("message- 
                        lose.png");
                    messageLose.centerAtPosition(400, 300);
                    messageLose.setOpacity(0);
                    messageLose.addAction(Actions.fadeIn(1));
                    gameOver = true;
                }
                else {

                    spaceship.shieldPower -= 34;
                    Explosion boom = new Explosion(0, 0, 
                        mainStage);
                    boom.centerAtActor(rockActor);
                    rockActor.remove();
                }
            }

            for (BaseActor laserActor : 
                BaseActor.getList(mainStage, "Laser")) {

                if (laserActor.overlaps(rockActor)) {

                }
                Explosion boom = new Explosion(0, 0, 
                    mainStage);
                boom.centerAtActor(rockActor);
                laserActor.remove();
                rockActor.remove();
            }
        }

        if (!gameOver && BaseActor.count(mainStage, "Rock") == 
            0) {

            BaseActor messageWin = new BaseActor(0, 0, 
                uiStage);
            messageWin.loadTexture("message-win.png");
            messageWin.centerAtPosition(400, 300);
            messageWin.setOpacity(0);
            messageWin.addAction(Actions.fadeIn(1));
            gameOver = true;
        }

    }

         */





        // loop I used to get collision working between rocks 
            and spaceship

        for (Rock each : rocks)
            if (spaceship.overlaps(each) && !each.crashed && 
                spaceship.shieldPower <= 0) {
                Explosion boom = new Explosion(0, 0, 
                    mainStage);
                Explosion boom2 = new Explosion(0, 0, 
                    mainStage);
                boom.centerAtActor(spaceship);
                boom2.centerAtActor(each);

                spaceship.remove();
                spaceship.setPosition(-1000, -1000);
                each.crashed = true;
                each.clearActions();
                each.addAction(Actions.fadeOut(1));

    each.addAction(Actions.after(Actions.removeActor()));
                rocksNeedRemoved = true;
                toRemove = each;

            } else if (spaceship.overlaps(each) && 
                  !each.crashed) {
                Explosion boom = new Explosion(0, 0, 
                    mainStage);
                boom.centerAtActor(each);
                spaceship.shieldPower -= 34;
                each.crashed = true;
                each.clearActions();
                each.addAction(Actions.fadeOut(1));

    each.addAction(Actions.after(Actions.removeActor()));
                rocksNeedRemoved = true;
                toRemove = each;
            }

        //check for collision between rocks and lasers (Not 
          working correctly)

        for (int i = rocks.size() - 1; i >= 0; i--) {
            Rock rock = rocks.get(i);
            for (int j = lasers.size() - 1; j >= 0; j--) {
                Laser laser = lasers.get(j);

    if(rock.getBounds().overlaps(laser.getBounds())) {
                    Explosion boom = new Explosion(0, 0, 
                        mainStage);
                    boom.centerAtActor(rock);
                    rock.crashed = true;
                    rock.clearActions();
                    rock.addAction(Actions.fadeOut(1));

    rock.addAction(Actions.after(Actions.removeActor()));
                    rocksNeedRemoved = true;
                    toRemove = rock;
                }
            }
        }

    }







    //override default InputProcessor method
    public boolean keyDown(int keycode) {

        if (keycode == Keys.X)
            spaceship.warp();

        if (keycode == Keys.SPACE)
            spaceship.shoot();

        return false;
    }
}

宇宙飞船类。

    package com.mygdx.game;

import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.math.MathUtils;

public class Spaceship extends BaseActor {

    private Thrusters thrusters;
    private Shield shield;
    public int shieldPower;

    public Spaceship(float x, float y, Stage s) {

        super(x, y, s);

        loadTexture("spaceship.png");
        setBoundaryPolygon(8);

        setAcceleration(100);
        setMaxSpeed(100);
        setDeceleration(10);

        thrusters = new Thrusters(0, 0, s);
        addActor(thrusters);
        thrusters.setPosition(-thrusters.getWidth(), 
            getHeight() / 2 - thrusters.getHeight() / 2);

        shield = new Shield(0, 0, s);
        addActor(shield);
        shield.centerAtPosition(getWidth() / 2, getHeight() / 
            2);
        shieldPower = 100;

    }

    public void act(float dt) {

        super.act(dt);

        float degreesPerSecond = 160; //rotation speed
        if (Gdx.input.isKeyPressed(Keys.LEFT))
            rotateBy(degreesPerSecond * dt);
        if (Gdx.input.isKeyPressed(Keys.RIGHT))
            rotateBy(-degreesPerSecond * dt);

        if (Gdx.input.isKeyPressed(Keys.UP)) {
            accelerateAtAngle(getRotation());
            thrusters.setVisible(true);
        }
        else {
            thrusters.setVisible(false);
        }

        shield.setOpacity(shieldPower / 100f);
        if (shieldPower <= 0)
            shield.setVisible(false);

        applyPhysics(dt);

        wrapAroundWorld();
    }

    public void warp() {

        if(getStage() == null)
            return;

        Warp warp1 = new Warp(0, 0, this.getStage());
        warp1.centerAtActor(this);
        setPosition(MathUtils.random(800), 
            MathUtils.random(600));
        Warp warp2 = new Warp(0, 0, this.getStage());
        warp2.centerAtActor(this);
    }

    public void shoot() {

        if (getStage() == null)
            return;

        Laser laser = new Laser(0, 0, this.getStage());
        laser.centerAtActor(this);
        laser.setRotation(this.getRotation());
        laser.setMotionAngle(this.getRotation());


    }
}

激光类。

    package com.mygdx.game;

import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;

public class Laser extends BaseActor {

    Rectangle bounds;

    public Laser(float x, float y, Stage s) {

        super(x, y, s);
        bounds = new Rectangle((int)getX(), (int)getY(), 
             (int)getWidth(), (int)getHeight());






        loadTexture("laser.png");

        addAction(Actions.delay(1));
        addAction(Actions.after(Actions.fadeOut(0.5f)));
        addAction(Actions.after(Actions.removeActor()));

        setSpeed(400);
        setMaxSpeed(400);
        setDeceleration(0);
    }

    public void act(float dt) {

        super.act(dt);
        applyPhysics(dt);
        wrapAroundWorld();
    }

    public Rectangle getBounds() {
        return bounds;
    }

    private void setXY(float pX,float pY) {
        setPosition(pX, pY);
        bounds.setX((int)pX);
        bounds.setY((int)pY);
    }

}

摇滚类

    package com.mygdx.game;

import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.math.MathUtils;
import com.mygdx.game.BaseActor;

public class Rock extends BaseActor {


    public boolean crashed;
    Rectangle bounds;

    public Rock(float x, float y, Stage s) {

        super(x, y, s);

        bounds = new Rectangle((int)getX(), (int)getY(), 
            (int)getWidth(), (int)getHeight());



        loadTexture("rock.png");

        float random = MathUtils.random(30);

        addAction(Actions.forever(Actions.rotateBy(30 + random, 
            1)));

        setSpeed(50 + random);
        setMaxSpeed(50 + random);
        setDeceleration(0);
        setMotionAngle(MathUtils.random(360));

        crashed = false;
    }

    public void act(float dt) {


        super.act(dt);
        applyPhysics(dt);
        wrapAroundWorld();
    }



    public boolean isCrashed() {
        return crashed;
    }

    public void crashed() {

        crashed = true;
        clearActions();
        addAction(Actions.fadeOut(1));
        addAction(Actions.after(Actions.removeActor()));

    }
    public Rectangle getBounds() {
        return bounds;
    }

    private void setXY(float pX,float pY) {
        setPosition(pX, pY);
        bounds.setX((int)pX);
        bounds.setY((int)pY);
    }
}

BaseActor 类中的边界和重叠方法

public void setBoundaryPolygon(int numSides) {

        float w = getWidth();
        float h = getHeight();
        float[] vertices = new float[2 * numSides];

        for(int i = 0; i < numSides; i ++) {

            float angle = i * 6.28f / numSides;
            //x coordinate
            vertices[2 * i] = w / 2 * MathUtils.cos(angle) + w / 2;
            //y coordinate
            vertices[2 * i + 1] = h / 2 * MathUtils.sin(angle) + h / 2;

        }

        boundaryPolygon = new Polygon(vertices);
    }

    public Polygon getBoundaryPolygon() {

        boundaryPolygon.setPosition(getX(), getY());
        boundaryPolygon.setOrigin(getOriginX(), getOriginY());
        boundaryPolygon.setRotation(getRotation());
        boundaryPolygon.setScale(getScaleX(), getScaleY());
        return boundaryPolygon;
    }

    public boolean overlaps(BaseActor other) {

        Polygon poly1 = this.getBoundaryPolygon();
        Polygon poly2 = other.getBoundaryPolygon();

        //initial text to improve performance
        if(!poly1.getBoundingRectangle().overlaps(poly2.getBoundingRectangle()))
            return false;

        return Intersector.overlapConvexPolygons(poly1, poly2);
    }

所以我想真正的问题是如何检查岩石 ArrayList 和玩家发射的激光之间的碰撞?在这一点上,我只想完成游戏,即使它没有使用最佳实践。我尝试使用here 描述的方法,没有收到任何错误,但激光和岩石之间也没有碰撞。即使我手动将激光添加到激光 ArrayList。我发现的最后一个post 让我相信我需要像 getAllLasers() 这样的东西,但我不能 100% 确定如何去做。只学习 Box2D 或 Quadtree 会更容易吗?

我意识到这是一个复杂的问题,并提前感谢您抽出宝贵时间阅读它。我很乐意提供您需要的更多信息。

【问题讨论】:

  • 这段代码很难读完,请阅读此“create a Minimal, Reproducible Example”,然后尝试再次询问您遇到的问题。
  • “无法正常工作”是什么意思?它是如何工作的,你期望它如何工作?还有什么理由在更新 bounds 时将 floats 转换为 intsrock.getBounds().overlaps(laser.getBounds()) 应该可以正常工作。如果 RockLaser 被缩放或旋转,你应该相应地更新你的边界
  • 我正在尝试做的就是移除一块被激光击中的石头。我摆脱了铸造的整数,它的工作原理是一样的。我想知道激光是否没有被添加到阵列列表中?即使我停止岩石的旋转和运动,它们与激光之间也不会发生碰撞。我将接受上述建议,并以更合适的方式重新询问更多相关信息。谢谢
  • 您的代码太长,无法阅读。我猜你的Rectangles 不是他们应该在的地方。只需在LaserRock 上覆盖drawDebug 方法并在那里绘制它们的bounds,参见示例ibb.co/jDTYtyv。然后启动游戏,看看Rectangles 是否与actor一起绘制。
  • 非常感谢您的帮助! bounds.set(getX(), getY(), getWidth(), getHeight()); 是我所缺少的。之后,我只需将激光添加到拍摄方法中的数组列表中,一切正常。

标签: java arraylist libgdx collision-detection game-development


【解决方案1】:

您已经为这两个实体设置了Rectangle 边界,这就是您所需要的。你可以使用Rectangle.overlaps()

for(Laser laser: lasers){
   for(Rock rock: rocks){
      if(laser.getBounds().overlaps(rock.getBounds())){
         //collided! 
      }
   }
}

确保您获得更新的矩形/边界。在 RockLaser getBounds() 方法中添加这一额外的行:

public Rectangle getBounds() {
   bounds.set(getX(),getY(),getWidth(),getHeight());
   return bounds;
}

如果您的演员缩放或旋转,您应该相应地更新bounds

【讨论】:

    【解决方案2】:

    我认为问题可能在于更新您的演员界限,我找不到您在哪里更新它。我编写了类似的游戏,并在每个更新步骤中更改了Bounds 的演员,并且在某些方面一切正常......

    public void update() {
    ...
            // changing bounds
            bounds = new Rectangle(position.x,position.y,
                    actorWidth,
                    actorHeight);
    }
    

    或者,如果您使用其他方法,请检查您的界限是否及时变化

    【讨论】:

    • 每帧创建一个矩形不是一个好主意,这将每秒在堆中创建 60 个新对象。而是使用bounds.set(...) 甚至更好。在getBounds() 中需要它之前更新它
    • 是的,主要想法是更新边界值,你这样做的方式可能不同
    猜你喜欢
    • 2017-08-07
    • 2017-10-30
    • 2013-02-21
    • 1970-01-01
    • 2013-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多