Столкновение мяча в libgdx

В настоящее время я пытаюсь сделать клон с использованием Java и libgdx. В настоящее время я испытываю проблемы с тем, чтобы заставить мяч отскочить от блоков под соответствующим углом. Короче говоря, проблема, с которой я сталкиваюсь, состоит в том, что шар перемещается на 12 пикселей каждый кадр и не всегда совпадает с краем кирпича. Если у кого-нибудь есть какие-либо предложения по поводу лучшего способа перемещения мяча или другого способа проверки столкновения, это будет очень цениться!

Основной игровой класс

    package com.kyleparker.breakout;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.utils.Array;

public class BreakoutGameScreen implements ApplicationListener {
   Texture dropImage;
   Sound dropSound;
   Music rainMusic;
   SpriteBatch batch;
   OrthographicCamera camera;
   Rectangle bucket;
   Paddle paddle;
   //Brick bricks[];
   Array<Brick> bricks;
   Ball ball;

   @Override
   public void create() {
      // load the images for the droplet, 64x64 pixels
      dropImage = new Texture(Gdx.files.internal("droplet.png"));

      // load the drop sound effect and the rain background "music"
      dropSound = Gdx.audio.newSound(Gdx.files.internal("drop.wav"));
      rainMusic = Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"));

      // start the playback of the background music immediately
      rainMusic.setLooping(true);
      rainMusic.play();

      // create the camera and the SpriteBatch
      camera = new OrthographicCamera();
      camera.setToOrtho(false, 1280, 720);
      batch = new SpriteBatch();

      paddle = new Paddle(new Texture(Gdx.files.internal("bucket.png")));

      bricks = new Array<Brick>();
      populateBricks();

      ball = new Ball(new Texture(Gdx.files.internal("bucket.png")), paddle, bricks);
   }

   private void populateBricks() {
       bricks.add(new Brick(200,100));
       for (int i = 0; i < 5; i++) {
           for (int j = 0; j <= 7; j++) {
               bricks.add(new Brick (j * 144 + 76, i * 80 + 300)); //Offsets each new brick
           }
       }
   }

   @Override
   public void render() {
       // clear the screen with a dark blue color. The
       // arguments to glClearColor are the red, green
       // blue and alpha component in the range [0,1]
       // of the color to be used to clear the screen.
       Gdx.gl.glClearColor(0, 0, 0.2f, 1);
       Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

       // tell the camera to update its matrices.
       camera.update();

       // tell the SpriteBatch to render in the
       // coordinate system specified by the camera.
       batch.setProjectionMatrix(camera.combined);

       // begin a new batch and draw the bucket and
       // all drops
       batch.begin();

       paddle.render(batch, camera);

       ball.move();
       ball.render(batch, camera);

       for (int x = bricks.size - 1; x > 0; x--) {
           bricks.get(x).render(batch,camera);
       }

       batch.end();     
   }

   @Override
   public void dispose() {
      // dispose of all the native resources
      dropImage.dispose();
      dropSound.dispose();
      rainMusic.dispose();
      batch.dispose();
      paddle.dispose();
   }

   @Override
   public void resize(int width, int height) {
   }

   @Override
   public void pause() {
   }

   @Override
   public void resume() {
   }
}

Бальный класс

    package com.kyleparker.breakout;

import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.utils.Array;

public class Ball{
   Texture ballImage;
   Rectangle ball;
   private int xdir;
   private int ydir;
   Paddle paddle;
   Array<Brick> bricks;
   final int BALL_SPEED = 12;

   public Ball(Texture ballImage, Paddle paddle, Array<Brick> bricks) {
      // load the ball image
      this.ballImage = ballImage;
      xdir = 1;
      ydir = -1;

      // create a Rectangle for the balls collision
      ball = new Rectangle();
      ball.x = 1280 / 2 - 64 / 2; // center the ball
      ball.y = 100; // put the ball 200px away from the bottom of the screen
      ball.width = 64;
      ball.height = 64;

      this.paddle = paddle;
      this.bricks = bricks;
   }

   public void render(SpriteBatch batch, OrthographicCamera camera) {
       // draw the paddle onto the batch of the level
       batch.draw(ballImage, ball.x, ball.y);
   }

   public void move() {
       ball.x += xdir * BALL_SPEED;
       ball.y += ydir * BALL_SPEED;

       if (ball.x <= 0) {
           setXDir(1);
       }

       if (ball.x >= 1280 - 64) {
           setXDir(-1);
       }

       if (ball.y <= 0) {
           setYDir(1);
       }

       if (ball.y >= 720 - 64) {
           setYDir(-1);
       }

       if (ball.overlaps(paddle.getRect())) {        
          setYDir(1);
       }

       for (int i = 0; i < bricks.size; i++) {
           if (ball.overlaps(bricks.get(i).getRect())) {
               if ((ball.x == (bricks.get(i).getRect().x + 128))) 
               { 
                   setXDir(1);
                   bricks.get(i).setDestroyed(true);
                   System.out.println("Collision RIGHT");
               }
               if (((ball.x + 64) == bricks.get(i).getRect().x)) 
               { 
                   setXDir(-1);
                   bricks.get(i).setDestroyed(true);
                   System.out.println("Collision LEFT");
               }
               if ((ball.y == (bricks.get(i).getRect().y + 64))) 
               { 
                   setYDir(1);
                   bricks.get(i).setDestroyed(true);
                   System.out.println("Collision TOP");
               }
               if (((ball.y + 64) == bricks.get(i).getRect().y)) 
               {                  
                   setYDir(-1);
                   bricks.get(i).setDestroyed(true);
                   System.out.println("Collision BOTTOM");
               }
           }
       }// end of for
   }

   public void setXDir(int x) {
       xdir = x;
   }

   public void setYDir(int y) {
       ydir = y;
   }

   public int getYDir() {
       return ydir;
   }

   public int getXDir() {
       return xdir;
   }

   public Rectangle getRect() {
       // return the collision rectangle for checking overlaps
       return ball;
   }

   public void dispose() {
      // dispose of all the native resources
      ballImage.dispose();
   }
}// end of class

Кирпичный код на всякий случай

package com.kyleparker.breakout;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Rectangle;

public class Brick{
   Texture brickImage;
   Rectangle brick;
   boolean destroyed;

   public Brick(int x, int y) {
       brickImage = new Texture(Gdx.files.internal("brick.png"));   

       // create a Rectangle for the bricks collision
       brick = new Rectangle();
       brick.x = x;
       brick.y = y;
       brick.width = 128;
       brick.height = 64;

       destroyed = false;
   }

   public void render(SpriteBatch batch, OrthographicCamera camera) {
       // draw the brick onto the batch of the level
       batch.draw(brickImage, brick.x, brick.y);
   }

   public boolean isDestroyed() {
       // return the collision rectangle for checking overlaps
       return destroyed;
   }

   public void setDestroyed(boolean destroyed)
   {
     this.destroyed = destroyed;

     if (this.destroyed == true) {
         dispose();
         brick.x = -1000;
         brick.y = -1000;
     }
   }

   public Rectangle getRect() {
       return brick;
   }

   public void dispose() {
      // dispose of all the native resources
      brickImage.dispose();
   }
}

2 ответа

Не беспокойтесь о том, что мяч не всегда совпадает с объектом, для которого необходимо обработать столкновение - это на самом деле не актуально. Вы можете (и должны) справляться со своими коллизиями менее "точно". То есть путь мяча фиксирован, поэтому вы можете рассчитать его положение в любой будущей точке. Проверьте его положение, вычислите его положение в следующем кадре (что вы должны сделать, чтобы нарисовать его в любом случае) и добавьте некоторый код для обработки столкновения, которое должно произойти, вместо того, чтобы пытаться обнаружить и обработать столкновение, которое произошло. Вы можете замедлить шар, если вам действительно нужно чистое отражение, или вы можете ускорить вашу частоту кадров, или вы можете позволить мячу частично "поглощаться" объектом, прежде чем он отразит:

public class Ball {
. . .
    public void move() {
    . . .
        if (collisionObject.overlaps(new Rectangle(ball.x + xdir, ball.y + ydir, ball.width, ball.height))) {
            //a collision will have occurred in the next frame
            //handle the collision however you please
        }
    }
}

Я также отмечаю, что ваш BALL_SPEED поле неверно названо. В нынешнем коде шар всегда движется под углом 45° со скоростью около 17 пикселей на кадр (в этом направлении). Вы кодировали его x- и y-смещение как 12 пикселей, но если (когда?) Вы измените направление шара, вы обнаружите, что скорость сильно колеблется в зависимости от того, какие значения помещены для xdir а также ydir поля. Например, если бы вы (несколько) рандомизировали их, но оставляли остальную часть своего кода как есть, вы можете обнаружить, что xdir = 2 а также ydir = 4 в одном случае, и xdir = 6 а также ydir = 12 по другому. Обратите внимание, что они описывают одно и то же направление, но вторая версия будет двигаться в три раза быстрее.

Чтобы правильно управлять направлением и скоростью мяча, назначьте угол и рассчитайте xdir а также ydir значения через соответствующие тригонометрические функции (xdir = BALL_SPEED * Math.cos(ballAngle) а также ydir = BALL_SPEED * Math.sin(ballAngle)).

Я бы использовал box2d для всего этого. То, что вы не использовали box2d, вероятно, означает, что у вас нет опыта в этом, так что это будет небольшим препятствием, но я уверен, что вы сможете быстро обернуть его вокруг. Вот ссылка для этого: http://code.google.com/p/libgdx/wiki/PhysicsBox2D

Другие вопросы по тегам