Пятница, 2023-09-29, 0:18 AM

Поиск
Меню
Категории раздела
Поддержи проект!
Рекомендуем


Расширение простой игры

В этом уроке мы будет расширять простую игру с названием «Drop», сделанную в предыдущем базовом уроке. Мы добавим экран меню и пару возможностей делающих игру более полнофункциональной.

Давайте начнем с знакомства с более продвинутыми классами в игре.

Интерфейс Screen

Экраны имеют основополагающее значение для любой игры с несколькими компонентами. Экраны содержат много методов, которые вы использовали в ApplicationListener объектах, и включают в себя пару новых методов show и hide, которые вызываются при получении и потери фокуса соответственно.

Класс Game

Класс Game является абстрактным и предоставляет для использования реализацию ApplicationListener, наряду с вспомогательными методами для обработки визуализации экрана.

Вместе объекты Screen и Game используются для создания простой и мощной структуры для игр.

Мы начнем с создания Game объекта, которой будет входной точкой нашей игры.

Давайте посмотрим и пройдемся по коду:

package com.badlogic.drop;

import com.badlogic.gdx.Game;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;


public class Drop extends Game {

SpriteBatch batch;
BitmapFont font;

public void create() {
batch = new SpriteBatch();
// LibGDX по умолчанию использует Arial шрифт.
font = new BitmapFont();
this.setScreen(new MainMenuScreen(this));
}

public void render() {
super.render(); // важно!
}

public void dispose() {
batch.dispose();
font.dispose();
}

}

Приложение начинается с создания экземпляра SpriteBatch и BitmapFont. Создание множества экземпляров, которые можно использовать совместно является плохой практикой. Объект SpriteBatch используется для отображения объектов на экране, таких как текстуры; BitmapFont используется отображения текста на экране. Мы коснемся этого подробнее в Screen классах.

Затем мы устанавливаем Screen игры как MainMenuScreen, единственный параметр экземпляр Drop класса.

Общей ошибкой является забывчивость вызова super.render(). Без этого вызова экран, который вы установили в Create() методе не будет отображен!

Наконец напоминание об освобождении объектов.

Главное меню

Рассмотрим MainMenuScreen класс.

package com.badlogic.drop;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;

public class MainMenuScreen implements Screen {

final Drop game;

OrthographicCamera camera;

public MainMenuScreen(final Drop gam) {
game = gam;

camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);

}


       // остальное опущено для краткости...

}

В этом фрагменте кода, мы делаем конструктор для MainMenuScreen класса, который реализует интерфейс Screen. Интерфейс Screen не предоставляет какой-либо create() метод, поэтому вместо этого используется конструктор. Единственным необходимым параметром для конструктора для игры является экземпляр Drop, так что если необходимо, то можно вызывать его методы и поля.

Последний метод в MainMenuScreen классе: render(float)

public class MainMenuScreen implements Screen {

       //public MainMenuScreen(final Drop gam)....      

@Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

camera.update();
game.batch.setProjectionMatrix(camera.combined);

game.batch.begin();
game.font.draw(game.batch, "Welcome to Drop!!! ", 100, 150);
game.font.draw(game.batch, "Tap anywhere to begin!", 100, 100);
game.batch.end();

if (Gdx.input.isTouched()) {
   game.setScreen(new GameScreen(game));
   dispose();
}
}

       // остальное опущено для краткости...

}

Этот код довольно прост, за исключением того, что нам нужно вызывать SpriteBatch и BitmapFont экземпляры из game поля, вместо создания собственных. game.font.draw(SpriteBatch, String, float,float) это то, как отобразить текст на экране. LibGDX поставляется с предварительно установленным шрифтом, Arial, так что можно использовать конструктор по умолчанию и все равно получить шрифт.

Замет проверяется было ли прикосновение к экрану, если это так, то мы устанавливаем GameScreen экземпляр и освобождаем ресурсы MainMenuScreen экземпляра. Остальные методы, которые необходимы для реализации в MainMenuScreen остаются пустыми.

Экран игры

Теперь, когда главное меню закончено, время сделать игру. Мы будем использовать большую часть кода из предыдущего урока простой игры чтобы избежать избыточности и предположения того, что это новая игра, так как это все та же Drop игра.

package com.badlogic.drop;

import java.util.Iterator;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.Screen;
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.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.TimeUtils;

public class GameScreen implements Screen {
final Drop game;

Texture dropImage;
Texture bucketImage;
Sound dropSound;
Music rainMusic;
OrthographicCamera camera;
Rectangle bucket;
Array raindrops;
long lastDropTime;
int dropsGathered;

public GameScreen(final Drop gam) {
this.game = gam;

// загрузка изображений для капли и ведра, 64x64 пикселей каждый
dropImage = new Texture(Gdx.files.internal("droplet.png"));
bucketImage = new Texture(Gdx.files.internal("bucket.png"));

// загрузка звукового эффекта падающей капли и фоновой "музыки" дождя
dropSound = Gdx.audio.newSound(Gdx.files.internal("drop.wav"));
rainMusic = Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"));
rainMusic.setLooping(true);

// create the camera and the SpriteBatch
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);

// создается Rectangle для представления ведра
bucket = new Rectangle();
                // центрируем ведро по горизонтали
bucket.x = 800 / 2 - 64 / 2;
                // размещаем на 20 пикселей выше нижней границы экрана.
bucket.y = 20;

bucket.width = 64;
bucket.height = 64;

// создает массив капель и возрождает первую
raindrops = new Array();
spawnRaindrop();

}

private void spawnRaindrop() {
Rectangle raindrop = new Rectangle();
raindrop.x = MathUtils.random(0, 800 - 64);
raindrop.y = 480;
raindrop.width = 64;
raindrop.height = 64;
raindrops.add(raindrop);
lastDropTime = TimeUtils.nanoTime();
}

@Override
public void render(float delta) {
// очищаем экран темно-синим цветом.
// Аргументы для glClearColor красный, зеленый
// синий и альфа компонент в диапазоне [0,1]
// цвета используемого для очистки экрана.
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

// сообщает камере что нужно обновить матрицы.
camera.update();

// сообщаем SpriteBatch о системе координат
// визуализации указанных для камеры.
game.batch.setProjectionMatrix(camera.combined);

// начитаем новую серию, рисуем ведро и
// все капли
game.batch.begin();
game.font.draw(game.batch, "Drops Collected: " + dropsGathered, 0, 480);
game.batch.draw(bucketImage, bucket.x, bucket.y);
for (Rectangle raindrop : raindrops) {
   game.batch.draw(dropImage, raindrop.x, raindrop.y);
}
game.batch.end();

// обработка пользовательского ввода
if (Gdx.input.isTouched()) {
   Vector3 touchPos = new Vector3();
   touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
   camera.unproject(touchPos);
   bucket.x = touchPos.x - 64 / 2;
}
if (Gdx.input.isKeyPressed(Keys.LEFT))
   bucket.x -= 200 * Gdx.graphics.getDeltaTime();
if (Gdx.input.isKeyPressed(Keys.RIGHT))
   bucket.x += 200 * Gdx.graphics.getDeltaTime();

// убедитесь что ведро остается в пределах экрана
if (bucket.x < 0)
   bucket.x = 0;
if (bucket.x > 800 - 64)
   bucket.x = 800 - 64;

// проверка, нужно ли создавать новую каплю
if (TimeUtils.nanoTime() - lastDropTime > 1000000000)
   spawnRaindrop();

// движение капли, удаляем все капли выходящие за границы экрана
// или тем, то попали в ведро. Воспроизведение звукового эффекта
// при попадании.
Iterator iter = raindrops.iterator();
while (iter.hasNext()) {
   Rectangle raindrop = iter.next();
   raindrop.y -= 200 * Gdx.graphics.getDeltaTime();
   if (raindrop.y + 64 < 0)
    iter.remove();
   if (raindrop.overlaps(bucket)) {
    dropsGathered++;
    dropSound.play();
    iter.remove();
   }
}
}

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

@Override
public void show() {
// воспроизведение фоновой музыки
// когда отображается экрана
rainMusic.play();
}

@Override
public void hide() {
}

@Override
public void pause() {
}

@Override
public void resume() {
}

@Override
public void dispose() {
dropImage.dispose();
bucketImage.dispose();
dropSound.dispose();
rainMusic.dispose();
}

}

Это код почти на 95% из оригинальной реализации, только теперь мы используем конструктор вместо Create() метода ApplicatonListener и передаем объект Drop как и в MainMenuScreen классе. Мы также воспроизводим музыку, как только экран установлен в GameScreen.

Мы также добавили строку в верхнем левом углу игры, которая отслеживает количество собранных каплей дождя.

Теперь у вас есть полностью законченная игра. Вот и все, что нужно знать о Screen интерфейсе и абстрактном Game классе, и все это создает многогранную игру с несколькими состояниями.

Пожалуйста посетите Github для получения полного исходного кода.

Будущее

Теперь, когда у вас есть понимание о нескольких экранах, настало время ими воспользоваться. Изучите Scene2d, Scene2D.ui и Skin чтобы сделать более красивым ваше главное меню.

Если вы также уже прочили следующие шаги из предыдущего урока Drop, вы должны быть готовы, чтобы сделать вашу собственную игру. Лучшей практикой будет сделать это, так что идите и делайте собственную игру.