Столкновение мяча в 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