libGDX. Урок 9. Random и обнаружение касаний.

Добавляем функцию random

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

public void randomizeWaitTime(){
    maxTimeUnderGround =(float) Math.random()*2f;
}

Функция random() класса Math в нашем случае сгенерирует число между 0 и 2 секундами.

Этот метод мы вызовем в методе initialize() класса GameManager в месте, где происходит инициализация наших зомби:

zombie.position.x=((sprite.getX() + (sprite.getX() + sprite.getWidth()))/2 - (zombie.zombieSprite.getWidth()/2));
zombie.position.y=(sprite.getY() + sprite.getHeight()/5f);
zombie.zombieSprite.setPosition(zombie.position.x, zombie.position.y);
zombie.randomizeWaitTime();

 

Обнаружение касаний

А сейчас мы реализуем обнаружение касаний пользователя. Наша логика проста. Как только пользователь коснулся зомби, мы тут же посылаем его под землю. Для этого нужно реализовать метод handleTouch() в классе Zombie:

public boolean handleTouch(float touchX,float touchY){
    if((touchX>=position.x) &&touchX<=(position.x+width) && (touchY>=position.y) &&touchY<=(position.y+currentHeight) ){

        state = State.UNDERGROUND; // меняем состояние на UNDERGROUND
        currentHeight=0.0f; // меняем значение переменной currentHeight на 0
        zombieSprite.setRegion(0, 0, (int)(width/scaleFactor), (int)(currentHeight/scaleFactor));
        zombieSprite.setSize(zombieSprite.getWidth(), currentHeight);
        //перезапускаем таймер переменной timeUnderGround
        timeUnderGround=0.0f;
        randomizeWaitTime();
        return true;
    }
    return false;
}

Этот метод в качестве параметров принимает два аргумента: touchX и touchY. Это входные координаты точки на экране, где было обнаружено касание пользователя. Также, как и в прошлой игре мы будем проверять касание пользователя по экрану. Отличием будет то, как будут опознаваться вертикальные границы. Ввиду того, что высота наших зомби меняется нам придется использовать переменную currentHeight для обнаружения касания.

Как только мы узнаем, что пользователь коснулся зомби, происходит следующее: 1.  Мы отправляем зомби обратно в нору сменой состояния на UNDERGROUND; 2.  Устанавливаем значение переменной currentHeight на ноль; 3. Перезапускаем таймер переменной timeUnderGround; 4. И методом randomizeWaitTime() устанавливаем время для переменной maxTimeUnderGround. Метод handleTouch() возвращает значение true в случае если было обнаружено касание по зомби и false в любом другом случае.

Для организованности нашего кода реализуем новый класс с именем InputManager для вызова метода, написанного выше и передачи входных координат касания. Этот класс добавим в пакете com.gdx.whackazombie.managers.

создание класса в android studio

новый класс

Введите следующий код в этот класс:

package com.gdx.whackazombie.managers;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector3;
import com.gdx.whackazombie.gameobjects.Zombie;

public class InputManager {

    static Vector3 temp = new Vector3();

    public static void handleInput(OrthographicCamera camera) {

        // Проверяем было ли касание по экрану
        if (Gdx.input.justTouched()) {
            // Получаем входные координаты касания и
            // устанавливаем эти координаты в временный вектор
            temp.set(Gdx.input.getX(), Gdx.input.getY(), 0);
            // получаем координаты касания относительно области просмотра нашей камеры
            camera.unproject(temp);
            float touchX = temp.x;
            float touchY = temp.y;
            // выполняем итерацию массива наших зомби и проверяем были ли выполнено касание по зомби?
            for (int i = 0; i < GameManager.zombies.size; i++) {
                Zombie zombie = GameManager.zombies.get(i);
                if (zombie.handleTouch(touchX, touchY)) {
                    break;
                }
            }
        }
    }
}

Здесь не нужно особых объяснений, так как это почти тот же способ, которым мы пользовались в прошлой игре. Осталось только вызвать метод handleInput() в методе render() класса WhackAZombie:

Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
InputManager.handleInput(camera);

 

Добавим эффект оглушения

Теперь добавим эффект оглушения зомби в тот момент, когда по нему было произведено касание пользователем. Для этого потребуется объявить еще несколько переменных в классе Zombie:

public float stunTime = 0.1f; // общее время оглушения, в котором будет находиться зомби
public float stunCounter = 0.0f; // время в которое зомби был оглушен

И добавим еще одно состояние, которое будет определять оглушен зомби или нет:

public enum State {GOINGUP,GOINGDOWN,UNDERGROUND,STUNNED}; // определение состояний зомби

Переменная stunTime определяет сколько по времени зомби будет находиться в оглушении (в состоянии STUNNED), а переменная stunCounter будет использоваться для отслеживания времени оглушения. Так как нам больше нет необходимости после касания по зомби сразу же отправлять его под земля нужно внести изменения в код метода handleTouch() (зеленым цветом выделены изменения, красным цветом выделено то, что нужно удалить):

public boolean handleTouch(float touchX,float touchY){
    if((touchX>=position.x) &&touchX<=(position.x+width) && (touchY>=position.y) &&touchY<=(position.y+currentHeight) ){

        state = State.STUNNED; // меняем состояние на UNDERGROUND
        currentHeight=0.0f; // меняем значение переменной currentHeight на 0
        zombieSprite.setRegion(0, 0, (int)(width/scaleFactor), (int)(currentHeight/scaleFactor));
        zombieSprite.setSize(zombieSprite.getWidth(), currentHeight);
        //перезапускаем таймер переменной timeUnderGround
        timeUnderGround=0.0f;
        randomizeWaitTime();
        return true;
    }
    return false;
}

Теперь по касанию пользователя на зомби мы всего лишь меняем текущее состояние зомби на STUNNED. Поэтому нам нужно внести изменения в метод update() этого же класса для обработки состояния STUNNED:

    case STUNNED:
        if(stunCounter>=stunTime){
            //отправляем зомби под землю
            state= State.UNDERGROUND;
            stunCounter=0.0f;
            currentHeight=0.0f;
            randomizeWaitTime();
        }
        else{
            stunCounter+=Gdx.graphics.getDeltaTime();
        }
        break;
} // конец switch

В состоянии STUNNED происходит следующее: если значение переменной stunCounter превышает максимальное время, которое мы установили в переменной stunTime – мы меняем состояние на UNDERGROUND. Затем обнуляем переменную stunCounter, меняем значение переменной currentHeight на 0 и вызываем метод randomizeWaitTime(). Если же значение переменной stunCounter не превышает время, установленно в переменной stunTime, мы просто накапливаем значение переменной stunCounter, используя метод Gdx.graphics.getDeltaTime(). На диаграмме изображен алгоритм действий:

проектирование игры

Еще одна важная вещь – нам нет необходимости оглушать зомби, который в данный момент находится в состоянии STUNNED. Для этого нужно внести маленькое изменение в коде метода handleInput класса InputManager:

    for (int i = 0; i < GameManager.zombies.size; i++) {
        Zombie zombie = GameManager.zombies.get(i);
        if (zombie.state!= Zombie.State.STUNNED && zombie.handleTouch(touchX, touchY)) {
        break;
    }
}

 

Добавляем знак оглушения

Теперь нужно наглядно показать пользователю момент, что зомби оглушен.

Добавим спрайт в классе Zombie:

public float stunCounter = 0.0f; // время в которое зомби был оглушен
public Sprite stunSprite; // sprite для отображения изображения оглушения

В классе GameManager мы добавим текстуру, которую будем использовать в спрайте:

private static float HOLE_RESIZE_FACTOR = 1100f;
static Texture stunTexture; //текстура для изображения оглушения

Загрузим текстуру изображения оглушения в методе initialize() класса GameManager:

backgroundSprite.setPosition(0,0f);
stunTexture = new Texture(Gdx.files.internal("stun.png"));
holeTexture = new Texture(Gdx.files.internal("hole.png"));

Затем нужно инициализировать переменную stunSprite в цикле, в котором происходит инициализация наших зомби:

// установить спрайт для зомби, используя текстуру
zombie.zombieSprite = new Sprite(zombieTexture);

zombie.stunSprite = new Sprite(stunTexture);

// установка размеров зомби
float scaleFactor = width/600f;
zombie.scaleFactor = scaleFactor;
zombie.width = zombie.zombieSprite.getWidth()*(scaleFactor);
zombie.height = zombie.zombieSprite.getHeight()*(scaleFactor);
zombie.zombieSprite.setSize(zombie.width, zombie.height);
// установка позиции зомби
zombie.position.x=((sprite.getX() + (sprite.getX() + sprite.getWidth()))/2 - (zombie.zombieSprite.getWidth()/2));
zombie.position.y=(sprite.getY() + sprite.getHeight()/5f);
zombie.zombieSprite.setPosition(zombie.position.x, zombie.position.y);

zombie.stunSprite.setSize(zombie.width/2f, zombie.height/2f);

zombie.randomizeWaitTime();

Когда пользователь касается зомби, мы хотим, чтобы изображение оглушения появилось рядом с головой зомби. Для этого в методе handleTouch() класса Zombie мы сделаем это разместив stunSprite таким образом, что его центр будет выравнен с правого-верхнего угла зомби:

stunSprite.setPosition(position.x+width-(stunSprite.getWidth()/2), position.y+currentHeight -(stunSprite.getHeight()/2));
state = State.STUNNED; // меняем состояние на UNDERGROUND

Мы отображаем изображение оглушения только если зомби оглушен в методе render() класса Zombie:

zombieSprite.draw(batch);
if(state==State.STUNNED){
    stunSprite.draw(batch);
}

Ну и наконец нам нужно уничтожить текстуру stunTexture в методе dispose() класса GameManager:

public static void dispose() {
    // утилизация текстуры крота, чтобы обеспечить отсутствие утечек памяти
    zombieTexture.dispose();
    backgroundTexture.dispose();
    holeTexture.dispose();
    stunTexture.dispose();
}

Вот, что должно у вас получиться:

создание игры на android studio с помощью libGDX

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

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