Малюємо, кодим під libGDX і інші маленькі радості життя інді-розробника



Привіт, Хабр! В цьому топіку, я хотів би поділитися враженнями від ігрового движка libGDX, розповісти про будні звичайного інді-розробника і відкрити завісу таємниці над грою, яку я роблю останні кілька місяців у вільний від офісного рабства час. Сподіваюся, ці мої записки будуть корисні тим хто тільки починає щось робити на libGDX або тим, хто вибирає движок для «гри своєї мрії».

І вибачте за котів. Вони абсолютно ніякого відношення не мають до игрострою. Я тут паралельно вчуся (намагаюся вчитися) малювати і тепер ці мої тренувальні коти просто всюди! Вимагають щоб їх, нероб, кому-небудь показували.

Малювання
До речі, про малюванні. Мене звичайно, закидають тапками досвідчені CG-художники (і будуть абсолютно праві), але як же я тащуся від процесу! Я і не думав, що малювання — це так круто! У мене давним-давно, серед всякого мотлоху, валявся кимось подарований графічний планшет. Bamboo що-то там, я думаю всі купують такі ж «побалуватися». І ось, як-то раз, мені на очі попалася книжка Марка Кистлера «Ви зможете малювати через 30 днів». Ні, природно, навчиться малювати за 30 днів — це все-одно що вивчити С++ за той же час. Всі це розуміють. Але виявляється, малювання — це не якийсь там нематеріальний талант, не ельфи пасущие єдинорогів на луках незвіданих островів. Це просто набір правил, такий же простий, як синтаксис мови програмування. Плюс досвід, звичайно! Набивання руки. Але ж і гарний програміст розвивається аналогічним чином, вирішуючи завдання, вивчаючи патерни, технології і слідуючи всяким best practice.

Так ось, в цій книзі (точніше книжці, вона несерйозна зовсім) є всі базові правила, щоб почати малювати щось більше ніж палка-палка-огуречек. І коли власноруч намальовані закарлючки перестають у тебе викликати жах хоча б, це класно! Процес сам по собі починає приносити задоволення. Особисто для мене це було відкриттям.

Ще, користуючись нагодою, хочу порадити відмінний редактор — SketchBook Pro. У ньому малюють і профі, але він саме такий, який потрібен не професіоналові. Юзерфрендли, не переобтяжений функціоналом. Ліцензія коштує відносно недорого, на відміну від того ж Photoshop. Хоча тут і застосовується ця огидна схема «за підпискою», базові функції доступні безкоштовно.

Я використовую всього 3 інструменти: олівець, кулькова ручка, кисть. Тут є, не знаю як правильно називається, функція виправлення на ходу ваших кривуль — мега корисна річ! Беремо в одну руку перо, другу на пробіл (поворот і масштабування полотна) і веред! Творити творчість.

Чому не Unity?
Але я відволікся. Коли розмова заходить про вибір ігрового движка, перший і єдине питання, яке тепер виникає — це «а чому не Юнити, чувак?». А тому! Тому що я звик до Android Studio (писав корпоративне додаток останнім часом), знаю Java більш-менш, Android SDK. І використання такого багатофункціонального монстра як Unity для простих 2D ігор — це трохи «з гармати по горобцях», ви не знаходите? Незнайома IDE, інший підхід, знову ж таки. А розмір порожнього проекту 20 Мб? Так у мене на libGDX вся гра разом з графікою (якої тут не так вже й мало) важить менше! Я розумію, ми живемо в століття швидкого Інтернету, але все ж залежність між розміром додатки і кількістю установок — існує.

Якщо копати глибше, то суть в тому, що попередження «Розмір додатки занадто великий, ви впевнені, що хочете завантажити його не через Wi-Fi?» (дослівно я не пам'ятаю, але суть така) — відсікає якусь кількість імпульсивних установок.

Загалом, якщо ви раніше мали справу з Android SDK, libGDX — це відмінний вибір для плавного переходу на кроссплатформенную розробку. Спробую сформулювати всі плюси і мінуси, які я виніс для себе з першого знайомства з libGDX.

Плюси
  • Безкоштовний
  • Багатоплатформовий. Desktop-білд однаково безпроблемно збирається і запускається під Windows і Mac (на Linux не було можливості перевірити, але я впевнений — аналогічно). Для HTML зіткнувся з невеликим нюансом: всі пакети, які ви додаєте, повинні бути прописані в Core-у проекті gwt.xml інакше не буде билдится. З Android взагалі ніяких проблем, iOS у мене поки тільки в планах.
  • Низький поріг входження для Android-розробника. Та ж Studio, той же Gradle. У нас є Core-проект в якому ми описуємо всю ігрову логіку і Android-проект — це, грубо кажучи, MainActivity на якій лежить View, а в ньому виповнюється Core-проект. З core-проекту ми можемо легко викликати платформозависимую частина, яку пишемо в Android-проекті. І що дуже приємно, платформний код (реклама, аналітика, ігрові сервіси) ми реалізуємо прямо по туториалам від Google. Як у звичайному додатку. Тобто ніяких бібліотек, сторонніх розширень і так далі!
Мінуси
  • Я не знайшов актуального, тлумачного туториала. Інформації дуже багато, навіть тут, на Хабре, але зазвичай вона зводиться до того як вивести спрайт на екран. На практиці це не потрібно, навіщо? Розумніше використовувати абстракції більш високого рівня: Актор, Сцена (про це нижче). При знайомстві з новим движком, в квикстарт-гайде, мені хотілося б побачити наступне: коректне масштабування гри під різні екрани, фонове завантаження ресурсів (з цим все добре), управління екранами/сценами і об'єктами на них.

Продовжуючи розмову про мінуси, мене не покидає відчуття легкої недомовленості в libGDX. Тут є відмінні абстракції: Screen — ігровий екран, Stage — сцена або шар, Actor — будь-який об'єкт на цій сцені. Я у своїй грі вирішив зробити один Screen і безліч Stage на всі ігрові екрани: рівні, меню і т. д. Від Actor успадковуємо ігрові об'єкти, потім просто робимо stage.addActor(ball) і все. Далі Stage сам займається промальовкою, переміщеннями і т. п. цього об'єкта. Так ось Actor, сам по собі, нічого не робить. Не виводить спрайтів (а 99% гральних об'єктів — спрайт), не можна перевірити колізію між двома акторами, загалом, нічого!

Доводиться робити свій SimpleActor і від нього вже успадковувати ігрові об'єкти:
Код
public class SimpleActor extends Actor {

private final TextureRegion region;

public SimpleActor(TextureRegion region) {
this.region = region;
setSize(region.getRegionWidth(), region.getRegionHeight());
setBounds(0, 0, getWidth(), getHeight());
}

@Override
public void draw(Batch batch, float parentAlpha) {
batch.draw(region, getX(), getY(), getOriginX(), getOriginY(), getWidth(), getHeight(), getScaleX(), getScaleY(), getRotation());
}
}

Якщо вам потрібні анімовані ігрові об'єкти (ну а куди ж без них?) пишемо SimpleAnimatedActor:
Код
public class SimpleAnimatedActor extends Actor {

private Animation animation;
private TextureRegion currentFrame;
private float stateTime;
private boolean started;

public SimpleAnimatedActor(float frameDuration, Array<TextureRegion> frames) {
animation = new Animation(frameDuration, frames);
currentFrame = animation.getKeyFrame(stateTime);
setBorders(frames.get(0));
}

private void setBorders(TextureRegion sample) {
setSize(sample.getRegionWidth(), sample.getRegionHeight());
setBounds(0, 0, getWidth(), getHeight());
}

public void start() {
stateTime = 0;
started = true;
}

public boolean isFinished() {
return animation.isAnimationFinished(stateTime);
}

@Override
public void act(float delta) {
super.act(delta);

if (!started) {
return;
}

stateTime += delta;
currentFrame = animation.getKeyFrame(stateTime);
}

@Override
public void draw(Batch batch, float parentAlpha) {
batch.draw(currentFrame, getX(), getY(), getOriginX(), getOriginY(), getWidth(), getHeight(), getScaleX(), getScaleY(), getRotation());
}
}

Для реалізації примітивної перевірки колізій, можна додати Polygon до нашого SimpleActor:
Код
Polygon polygon = new Polygon(new float[]{0, 0, getWidth(), 0, getWidth(), getHeight(), 0, getHeight()});
polygon.setPosition(getX(), getY());
polygon.setOrigin(getOriginX(), getOriginY());
polygon.setScale(getScaleX(), getScaleY());
polygon.setRotation(getRotation());

І так далі. Особливих труднощів це не викликає. Але все-таки, я не можу зрозуміти, чому б не зробити щось подібне в движку? А так доведеться тягати свій дописаний код з проекту в проект.

Фрагментація
Як зробити так, щоб гра виглядала однаково кльово на всьому зоопарку Android-пристроїв? Для мене це одне з ключових питань. Тому розповім детальніше як я вирішував проблему масштабування картинки в libGDX.

Мені подобається вже класичний підхід, коли ми на планшетах (4:3) показуємо більше оточення, а на смартфонах (16:9) — менше. При цьому ніде нічого не розтягується, а ігрові об'єкти, можна сказати, не змінюють своїх розмірів. Всю графіку зберігаємо в одному дозволі, у мене це 960х1280 (4:3). В центрі екрану у нас є «ігрова сфера» 720х1280 (16:9) куди зобов'язані потрапляти всі об'єкти з якими взаємодіє гравець, решта — декорації і можуть бути обрізані залежно від конкретного пристрою. Найпростіше цей принцип проілюструвати картинкою:



LibGDX надає всі можливості для цього. Єдиний нюанс — деякі об'єкти може знадобитися прикріпити до краю екрана. Як кнопку Home на зображенні вище. Для цього нам треба знати фактичні лівий і правий краї екрану в ігрових координатах (з верхом і низом простіше — це завжди 1280 і 0 відповідно). Напишемо трохи коду:
Код
public class MyGame extends Game {

public static final float SCREEN_WIDTH = 960f;
public static final float SCREEN_HEIGHT = 1280f;
public static float VIEWPORT_LEFT;
public static float VIEWPORT_RIGHT;

private GameScreen mGameScreen;

@Override
public void create() {
mGameScreen = new GameScreen();
setScreen(mGameScreen);
}

@Override
public void resize(int width, int height) {
super.resize(width, height);

float aspectRatio = (float) width / height;
float viewportWidth = SCREEN_HEIGHT * aspectRatio;

VIEWPORT_LEFT = (SCREEN_WIDTH - viewportWidth) / 2;
VIEWPORT_RIGHT = VIEWPORT_LEFT + viewportWidth;
}

@Override
public void dispose() {
super.dispose();
mGameScreen.dispose();
}
}

Що тут відбувається? Це головний клас гри, успадкований від Game. Тут ми створюємо і встановлюємо Screen (екран, в моєму випадку — єдиний) і перевизначаємо resize(). Це подія для Android викликається при старті програми і тут ми можемо розрахувати лівий і правий краї картинки (VIEWPORT_LEFT і VIEWPORT_RIGHT).

У GameScreen нам треба встановити FillViewport, саме він відповідає за те щоб зображення обрізувалося якщо співвідношення сторін не відповідає цільовим. Вьюпорт — це наше вікно в світ гри. Вони бувають декількох типів, документація про це розповість навіть краще, ніж я, а я просто наведу код:
Код
public class GameScreen implements Screen {

private final OrthographicCamera mCamera;
private final Viewport mViewport;
private final AssetManager mAssetManager;
private Stage mActiveStage;

public GameScreen() {
mCamera = new OrthographicCamera();
mViewport = new FillViewport(MyGame.SCREEN_WIDTH, MyGame.SCREEN_HEIGHT, mCamera);
mAssetManager = new AssetManager();
mActiveStage = new MyStage(mViewport, mAssetManager);
}

@Override
public void render(float delta) {
mCamera.update();

Gdx.gl.glClearColor(65 / 255f, 65 / 255f, 65 / 255f, 1f); // просто колір фону
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

mActiveStage.act(delta);
mActiveStage.draw();
}

@Override
public void resize(int width, int height) {
mViewport.update(width, height, true);
}

@Override
public void dispose() {
mActiveStage.dispose();
mAssetManager.dispose();
}
}

Тут, загалом-то, все інтуїтивно повинно бути зрозуміло, не забувайте робити mViewport.update(width, height, true) resize().

Механічна Коробка
Тепер, коли я розповів коротко про движку, мені б хотілося сказати ще кілька слів про саму гру. Я десь вже рік не займався іграми, корпоративне болото майже засмоктало мене. Але це не заважало мені мріяти. Мріяти про квесті, головоломці настільки складною, наскільки це взагалі можливо. Без підказок за гроші і інших фритуплейных дурниць.

Так з'явилася ідея Механічної Коробки — пристрою, створеного з однією єдиною метою: надійно захищати те, що знаходиться всередині. Де кожен шар захисту — це окрема, самостійна головоломка з новою ігровий механікою.

Величезний плюс інді, ми можемо дозволити собі робити те, що нам заманеться. Мені плювати на Retention Rate та інші маркетингові штучки. Так геймдиз будь-якої комерційної студії застрелиться, побачивши відсоток гравців застрягли на першому рівні! А я можу просто робити таку гру, в яку мені б хотілося пограти самому.

Загалом, у серпні цього року я приступив до будівництва Коробки. Порисовав трохи, я зрозумів що мені не витягнути такі обсяги, та й мій арт не влаштовував мене за якістю. Він, м'яко кажучи, своєрідний. В результаті, на gamedev.ru був знайдений художник, який погодився вплутатися в цю авантюру. Дивно, виявилося, що ми з Володимиром живемо не тільки в одному місті, але і на сусідніх вулицях. Він-то і надав Механічній Коробці відповідний зовнішній вигляд. Я навіть зберіг на пам'ять «як було» і «стало». Різниця, я думаю, очевидна.



Я поки продовжую працювати над Механічною Коробкою, у мене є деякі думки щодо розвитку цієї ідеї. Якщо ви вирішите випробувати свої сили, гра вже опублікована на Google Play. Посилання не даю, щоб не привертати увагу НЛО зайвий раз. І хоча Коробку досить швидко розібрали на запчастини на одному популярному ресурсі, для окремо взятої людини, ця головоломка — досить серйозний виклик. Що мене особисто тішить. Я навіть можу передбачити де ви застрягнете. Це рівень з краном (як зрушити вправо цю чортову клешню?), або три різнокольорових квадратів (ця загадка мені здається легкою, але статистика вперта штука). Або, ласкаво просимо в «клуб втраченого нуля» — ну це вже еліта.


Спасибі за прочитання, задавайте питання, пишіть зауваження!
Робіть добро і кидайте його у воду.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.