libGDX. Урок 13. Обнаружение столкновений и гравитация

Обнаружение столкновений

В этом уроке мы разберемся с такой важной темой в разработке игр, как обнаружение столкновений объектов. В конце предыдущего урока мы сделали так, что мяч опускается сверху вниз и улетает в небытие. В этом уроке мы реализуем такие вещи, как проверка столкновения мяча с землей и проверка столкновения мяча с корзиной.

А перед этим добавим немного реалистичности падению нашего мяча.

 

Гравитация

Давайте более реалистично смоделируем процесс падения мяча. В нашем случае нужно добавить мячу не только скорость, но еще и ускорение. Ускорение будет определять скорость изменения скорость падения. В классе Ball определим переменную gravity:

public Vector2 velocity = new Vector2(); // вектор для обозначения скорости
public final Vector2 gravity = new Vector2(0,-0.12f);// вектор для обозначения ускорения падения мяча

Поскольку гравитация будет постоянно воздействовать на мяч, нужно реализовать постоянное изменение скорости его падения. Отредактируйте метод update() класса Ball:

velocity.add(gravity); // обновление значения переменной velocity путем добавления к нему значения переменной gravity
position.add(velocity); // обновляем положение мяча в зависимости от скорости

Помимо этого, для большей реалистичности отредактируем метод initialize() в классе GameManager и инициализируем переменную velocity изначальным значением скорости (0,0):

ball.position.set(0.0f, height-ball.ballSprite.getHeight());
ball.velocity.set(0, 0);
} // конец метода initialize()

Теперь запустив игру, вы увидите, что мяч опускается не с постоянной скоростью, а набирает её с течением времени:

игра android studio libgdx

Столкновение с землей

Обнаружение столкновения мяча с землей достаточно просто реализуется. Для этого нам всего лишь нужно проверить попал ли мяч в основание экрана нашего устройства. Для этих целей разработаем новый метод в классе Ball и назовем его checkCollisions():

}//конец метода update

public boolean checkCollisions(){

    // проверяем, упал ли мяч на землю?
    if(position.y <= 0.0){
        return true;
    }
    return false;
}

Способ, которым мы узнаем, что мяч столкнулся с землей – это обычная проверка по Y-координате. Если мяч достиг координаты по Y меньше 0(нуля), значит он коснулся земли. Вызовем этот метод в методе update() и для проверки его работоспособности будем выводить простое сообщение, информирующее о столкновении с землей:

public void update() {

    if(checkCollisions()){
        System.out.println("Collided with ground"); // для проверки, попозже удалим эту строчку.
    }
    velocity.add(gravity); // обновление значения переменной velocity путем добавления к нему значения переменной gravity

 

Столкновение с корзиной

Для обнаружения столкновения мяча с корзиной используем совершенно другой подход. Для того, чтобы сделать обнаружение такого столкновения несложным мы предположим, что наша корзина – это прямоугольник. В LibGDX есть служебные методы для обнаружения столкновений для объектов прямоугольной и круглой формы.

Давайте добавим в класс Ball объект класса Circle:

public final Vector2 gravity = new Vector2(0,-0.12f);// вектор для обозначения ускорения падения мяча
public Circle ballCircle; //круг для обнаружения столкновений мяча

Теперь, для того, чтобы корректно обнаруживать столкновение нашего мяча, нужно обозначить центр нашего мяча, а радиус обозначить как половина высоты нашего спрайта. Сделаем это в методе initialize() класса GameManager. В нашем случае конструктор Circle будет принимать в качестве первого аргумента центр спрайта, а в качестве второго аргумента – радиус:

ball.velocity.set(0, 0);

Vector2 center = new Vector2();
// обозначим центр круга в центре спрайта нашего мяча
center.x=ball.position.x + (ball.ballSprite.getWidth()/2);
center.y=ball.position.y + (ball.ballSprite.getHeight()/2);
ball.ballCircle = new Circle(center, (ball.ballSprite.getHeight()/2));

Но теперь нам нужно внести изменения в метод update() и обновлять позицию введённого нами только что объекта Circle в каждом кадре:

ballSprite.setPosition(position.x, position.y); // устанавливаем позицию спрайта
ballCircle.setPosition(position.x + (ballSprite.getWidth()/2), (position.y + ballSprite.getHeight()/2)); //установка позиции круга, который отвечает за обнаружение столкновений

Почти таким же методом реализуем нашу корзину как прямоугольник. В классе Basket добавьте следующую строку:

public Sprite basketSprite; //спрайт для отображение корзины
public Rectangle basketRectangle = new Rectangle();//прямоугольник для проверки столкновений с корзиной

В методе setPosition() установим позицию этого прямоугольника, как показано внизу:

public void setPosition(float x,float y){
    basketSprite.setPosition(x, y);
    basketRectangle.setPosition(x, y);
}

А в методе initialize() класса GameManager установим размер нашего прямоугольника:

basket.setPosition(0, 0);// установим позицию корзины в левом нижнем углу
//задаем размер прямоуголькика, ограничивающего нашу корзину
basket.basketRectangle.setSize(basket.basketSprite.getWidth(), basket.basketSprite.getHeight());

Теперь нужно разделить логику столкновения мяча на две функции: 1. Столкновение мяча с землей; 2. Столкновение мяча с корзиной. Сделаем это в классе Ball:

public boolean checkCollisionsWithGround(){

    // проверяем, упал ли мяч на землю?
    if(position.y <= 0.0){
        return true;
    }
    return false;
}

Это тот же самый метод, который мы создали ранее, только изменили ему имя и переместили сюда строчку с выводом информации о столкновении с землей из метода update().

Теперь создадим метод checkCollisionsWithBasket() для обнаружения столкновений с корзиной:

}//конец метода checkCollisionsWithGround()

public boolean checkCollisionsWithBasket(){

    // проверка, было ли столкновение между мячом и корзиной
    if(Intersector.overlaps(ballCircle, GameManager.basket.basketRectangle)){
        System.out.println("Collided with basket");
        return true;
    }
    return false;
}

LibGDX снабжен классом Intersector, который в нашем случае поможет обнаружить пересечение между кругом и прямоугольником. С помощью метода overlaps()  мы проверяем столкновение мяча и корзины. Для вызова этих методов объединим их в методе checkCollisions():

}//конец метода checkCollisionsWithBasket()

public void checkCollisions(){
    checkCollisionsWithGround();
    checkCollisionsWithBasket();
}

А метод checkCollisions() будем вызывать в методе update():

public void update() {

    if(checkCollisions()){
        System.out.println("Collided with ground"); // для проверки, попозже удалим эту строчку.
    }
    velocity.add(gravity); // обновление значения переменной velocity путем добавления к нему значения переменной gravity
    position.add(velocity); // обновляем положение мяча в зависимости от скорости
    ballSprite.setPosition(position.x, position.y); // устанавливаем позицию спрайта
    ballCircle.setPosition(position.x + (ballSprite.getWidth()/2), (position.y + ballSprite.getHeight()/2)); //установка позиции круга, который отвечает за обнаружение столкновений

    checkCollisions();

}//конец метода update

 
Не забудьте удалить уже ненужные нам строки, они выделены красным цветом. Теперь посмотрим, что у нас получилось:

игра libgdx на android studio

А вот, что происходит в логе Android Studio в момент пролета мяча при пересечении сначала корзины, потом корзины и земли, а потом только земли:

игра на libgdx android studio

Код для этого урока можно скачать внизу:

Скачать исходники