После рассмотрения жизненного цикла игры сразу стоит рассмотреть архитектуру (каркас). Вообще Роллингс и Моррис (Rollings and Dave Morris) в своей книге «Game Architecture and Design» подробно описывают создание игр с точки зрения архитектуры. В своё время я правда не особо проникся этой книгой, но вам может понравится. Я же опишу архитектуру, которую стараюсь использовать сам.
Разбиение приложения на компоненты со слабым связыванием — это не просто какой-то идеологических ход, такой подход действительно очень упрощает разработку. В частности, я предлагаю использовать заезженный паттерн — MVC. Часть материала брал с занятного сайта http://obviam.net/. Там вообще очень много полезной информации для разработчиков игр.
MVC
Довольно-таки удобный образец архитектуры для разработки игр. Главным его преимуществом, как по мне, является то, что можно вносить изменения в какую-то часть игры, при этом не затрагивая остальные компоненты приложения.

Примерно как всё в играх происходит? Игрок производит какую-то манипуляцию:
- Игрок нажимает на экран (или на клавиатуру).
- В controller обрабатывается нажатие. Здесь же по сути вся логика реализована: проверка на препятствия, отслеживание состояний объектов, изменение их состояний и т.д.
- То есть, controller изменяет состояние объектов (model‘s).
- После чего объекты отрисовываются (view).
MVC очень удачно подходит. Если ещё не поняли, поясню кое-какие моменты. Объекты (Model) абсолютно ничего не знают про рендеринг. Многие пишут, что объекты не должны и состояние менять сами, а за них это должен делать контроллер. Я к этому вопросы подошёл практически. Возьмите, к примеру, вашего персонажа, которому надо как-то описать логику обхода препятствий. Большинство скажет, что в контроллере сие дело надо реализовывать. Но почему? Ведь, когда вы идёте по улице, то обходите препятствие после собственных расчётов, а не мир или контроллер просчитывает это дело. Так что, часть логики взаимодействия с миром я бы посоветовал именно в сами объекты добавлять.
Я имею ввиду именно живые объекты (если можно так сказать про виртуальных персонажей (: ). Логику неодушевлённых предметов можно и в контроллере делать. Перейдём к практической части. За основу берём проект из введения.
Игровые компоненты
В данной статье покажу как разбить на части приложение. Немного про объекты мира расскажу.
Создание мира и его объектов
Добавьте к проекту package suvitruf.libgdxtutorial.model. Здесь у нас будут объекты мира. Добавьте в этот пакет класс Brick
и зададим базовые свойства:
01.
package
suvitruf.libgdxtutorial.model;
02.
03.
//импорт нужных либ
04.
import
com.badlogic.gdx.math.Rectangle;
05.
import
com.badlogic.gdx.math.Vector2;
06.
07.
public
class
Brick {
08.
//размер объекта
09.
static
final
float
SIZE = 1f;
10.
//координаты
11.
Vector2 position =
new
Vector2();
12.
Rectangle bounds =
new
Rectangle();
13.
14.
public
Brick(Vector2 pos) {
15.
this
.position = pos;
16.
this
.bounds.width = SIZE;
17.
this
.bounds.height = SIZE;
18.
}
19.
20.
public
Rectangle getBounds() {
21.
return
bounds;
22.
}
23.
24.
public
Vector2 getPosition() {
25.
return
position;
26.
}
27.
}
У блока нет никакой логики, он представляет собой…ммм…просто кирпич. Он ни с чем не взаимодействует, но с ним могут взаимодействовать живые объекты. Мы используем тип Vector2
от libgdx. Это позволяет нам работать лишь с Евклидовыми векторами. Мы будем использовать векторы для позиционирования, вычисления скорости и для движения (ну да, кирпич не двигается…но наш персонаж будет).
Далее добавим класс (добавьте класс Player
к пакету suvitruf.libgdxtutorial.model), который будет являть собой нашего персонажа.
01.
package
suvitruf.libgdxtutorial.model;
02.
03.
import
com.badlogic.gdx.math.Rectangle;
04.
import
com.badlogic.gdx.math.Vector2;
05.
06.
public
class
Player {
07.
08.
//состояние
09.
public
enum
State {
10.
NONE, WALKING, DEAD
11.
}
12.
13.
14.
//скорость движения
15.
public
static
final
float
SPEED = 2f;
16.
//размер
17.
public
static
final
float
SIZE =
0
.7f;
18.
19.
//позиция в мире
20.
Vector2 position =
new
Vector2();
21.
//используется для вычисления движения
22.
Vector2 velocity =
new
Vector2();
23.
//прямоугольник, в который вписан игрок
24.
//будет использоваться в будущем для нахождения коллизий (столкновение со стенкой и т.д.
25.
Rectangle bounds =
new
Rectangle();
26.
//текущее состояние
27.
State state = State.NONE;
28.
29.
public
Player(Vector2 position) {
30.
this
.position = position;
31.
this
.bounds.height = SIZE;
32.
this
.bounds.width = SIZE;
33.
}
34.
35.
public
Rectangle getBounds() {
36.
return
bounds;
37.
}
38.
39.
public
Vector2 getVelocity() {
40.
return
velocity;
41.
}
42.
43.
public
Vector2 getPosition() {
44.
return
position;
45.
}
46.
47.
//обновления движения
48.
public
void
update(
float
delta) {
49.
position.add(velocity.tmp().mul(delta));
50.
}
51.
}
Теперь нам нужно создать мир, в котором будут все эти объекты. Добавляем в пакет suvitruf.libgdxtutorial.model класс World
. Мир условно делится на клетки. К примеру создадим мир 8×5.
01.
package
suvitruf.libgdxtutorial.model;
02.
03.
04.
import
com.badlogic.gdx.math.Vector2;
05.
import
com.badlogic.gdx.utils.Array;
06.
07.
public
class
World {
08.
//массив блоков
09.
Array<Brick> bricks =
new
Array<Brick>();
10.
//наш персонаж
11.
public
Player player;
12.
13.
//ширина мира
14.
public
int
width;
15.
//высота мира
16.
public
int
height;
17.
18.
//получить массив блоков
19.
public
Array<Brick> getBricks() {
20.
return
bricks;
21.
}
22.
//получить игрока
23.
public
Player getPlayer() {
24.
return
player;
25.
}
26.
27.
public
World() {
28.
width =
8
;
29.
height =
5
;
30.
createWorld();
31.
}
32.
33.
//создадим тестовый мир какой-нибудь
34.
public
void
createWorld() {
35.
player =
new
Player(
new
Vector2(
6
,
2
));
36.
bricks.add(
new
Brick(
new
Vector2(
0
,
0
)));
37.
bricks.add(
new
Brick(
new
Vector2(
1
,
0
)));
38.
bricks.add(
new
Brick(
new
Vector2(
2
,
0
)));
39.
bricks.add(
new
Brick(
new
Vector2(
3
,
0
)));
40.
bricks.add(
new
Brick(
new
Vector2(
4
,
0
)));
41.
bricks.add(
new
Brick(
new
Vector2(
5
,
0
)));
42.
bricks.add(
new
Brick(
new
Vector2(
6
,
0
)));
43.
bricks.add(
new
Brick(
new
Vector2(
7
,
0
)));
44.
45.
46.
}
47.
}
World
— модель мира. По сути он является контейнером для объектов, что логично (:
Контроллер
Создадим пакет suvitruf.libgdxtutorial.controller и добавим в него класс WorldController
. В этом классе как раз и будет реализована логика вся. По идеи в контроллере производятся изменения состояний объектов мира. И главное, в зависимости от действий юзера будут манипуляции с объектом Player
.
001.
package
suvitruf.libgdxtutorial.controller;
002.
003.
import
java.util.HashMap;
004.
import
java.util.Map;
005.
import
suvitruf.libgdxtutorial.model.*;
006.
007.
public
class
WorldController {
008.
009.
//направление движения
010.
enum
Keys {
011.
LEFT, RIGHT, UP, DOWN
012.
}
013.
//игрок
014.
public
Player player;
015.
016.
//куда движемся...игрок может двигаться одновременно по 2-м направлениям
017.
static
Map<Keys, Boolean> keys =
new
HashMap<WorldController.Keys, Boolean>();
018.
019.
//первоначально стоим
020.
static
{
021.
keys.put(Keys.LEFT,
false
);
022.
keys.put(Keys.RIGHT,
false
);
023.
keys.put(Keys.UP,
false
);
024.
keys.put(Keys.DOWN,
false
);
025.
};
026.
027.
public
WorldController(World world) {
028.
this
.player = world.getPlayer();
029.
}
030.
031.
//флаг устанавливаем, что движемся влево
032.
public
void
leftPressed() {
033.
keys.get(keys.put(Keys.LEFT,
true
));
034.
}
035.
036.
//флаг устанавливаем, что движемся вправо
037.
public
void
rightPressed() {
038.
keys.get(keys.put(Keys.RIGHT,
true
));
039.
}
040.
041.
//флаг устанавливаем, что движемся вверх
042.
public
void
upPressed() {
043.
keys.get(keys.put(Keys.UP,
true
));
044.
}
045.
046.
//флаг устанавливаем, что движемся вниз
047.
public
void
downPressed() {
048.
keys.get(keys.put(Keys.DOWN,
true
));
049.
}
050.
051.
//освобождаем флаги
052.
public
void
leftReleased() {
053.
keys.get(keys.put(Keys.LEFT,
false
));
054.
}
055.
public
void
rightReleased() {
056.
keys.get(keys.put(Keys.RIGHT,
false
));
057.
}
058.
public
void
upReleased() {
059.
keys.get(keys.put(Keys.UP,
false
));
060.
}
061.
public
void
downReleased() {
062.
keys.get(keys.put(Keys.DOWN,
false
));
063.
}
064.
065.
//главный метод класса...обновляем состояния объектов здесь
066.
public
void
update(
float
delta) {
067.
processInput();
068.
player.update(delta);
069.
}
070.
071.
public
void
resetWay(){
072.
rightReleased();
073.
leftReleased();
074.
downReleased();
075.
upReleased();
076.
}
077.
078.
//в зависимости от выбранного направления движения выставляем новое направление движения для персонажа
079.
private
void
processInput() {
080.
if
(keys.get(Keys.LEFT))
081.
player.getVelocity().x = -Player.SPEED;
082.
083.
if
(keys.get(Keys.RIGHT))
084.
player.getVelocity().x =Player.SPEED;
085.
086.
if
(keys.get(Keys.UP))
087.
player.getVelocity().y = Player.SPEED;
088.
089.
090.
if
(keys.get(Keys.DOWN))
091.
player.getVelocity().y = -Player.SPEED;
092.
093.
//если не выбрано направление, то стоим на месте
094.
if
((keys.get(Keys.LEFT) && keys.get(Keys.RIGHT)) || (!keys.get(Keys.LEFT) && (!keys.get(Keys.RIGHT))))
095.
player.getVelocity().x =
0
;
096.
if
((keys.get(Keys.UP) && keys.get(Keys.DOWN)) || (!keys.get(Keys.UP) && (!keys.get(Keys.DOWN))))
097.
player.getVelocity().y =
0
;
098.
099.
}
100.
}
Рендеринг
И так, про контроллер и объекты мира поговорили. Теперь нужно отрисовать объекты наши. Для этого создадим пакет suvitruf.libgdxtutorial.view, а в нём класс WorldRenderer
.
01.
package
suvitruf.libgdxtutorial.view;
02.
03.
import
suvitruf.libgdxtutorial.model.*;
04.
import
com.badlogic.gdx.graphics.Color;
05.
import
com.badlogic.gdx.graphics.OrthographicCamera;
06.
import
com.badlogic.gdx.graphics.glutils.ShapeRenderer;
07.
import
com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
08.
import
com.badlogic.gdx.math.Rectangle;
09.
10.
public
class
WorldRenderer {
11.
public
static
float
CAMERA_WIDTH = 8f;
12.
public
static
float
CAMERA_HEIGHT = 5f;
13.
14.
private
World world;
15.
public
OrthographicCamera cam;
16.
ShapeRenderer renderer =
new
ShapeRenderer();
17.
18.
19.
public
int
width;
20.
public
int
height;
21.
public
float
ppuX;
// пикселей на точку мира по X
22.
public
float
ppuY;
// пикселей на точку мира по Y
23.
24.
public
void
setSize (
int
w,
int
h) {
25.
this
.width = w;
26.
this
.height = h;
27.
ppuX = (
float
)width / CAMERA_WIDTH;
28.
ppuY = (
float
)height / CAMERA_HEIGHT;
29.
}
30.
//установка камеры
31.
public
void
SetCamera(
float
x,
float
y){
32.
this
.cam.position.set(x, y,
0
);
33.
this
.cam.update();
34.
}
35.
36.
public
WorldRenderer(World world) {
37.
38.
this
.world = world;
39.
this
.cam =
new
OrthographicCamera(CAMERA_WIDTH, CAMERA_HEIGHT);
40.
//устанавливаем камеру по центру
41.
SetCamera(CAMERA_WIDTH / 2f, CAMERA_HEIGHT / 2f);
42.
43.
}
44.
45.
//основной метод, здесь мы отрисовываем все объекты мира
46.
public
void
render() {
47.
drawBricks();
48.
drawPlayer() ;
49.
50.
}
51.
52.
//отрисовка кирпичей
53.
private
void
drawBricks() {
54.
renderer.setProjectionMatrix(cam.combined);
55.
//тип устанавливаем...а данном случае с заливкой
56.
renderer.begin(ShapeType.FilledRectangle);
57.
//прогоняем блоки
58.
for
(Brick brick : world.getBricks()) {
59.
Rectangle rect = brick.getBounds();
60.
float
x1 = brick.getPosition().x + rect.x;
61.
float
y1 = brick.getPosition().y + rect.y;
62.
renderer.setColor(
new
Color(
0
,
0
,
0
,
1
));
63.
//и рисуем блоки
64.
renderer.filledRect(x1, y1, rect.width, rect.height);
65.
}
66.
67.
renderer.end();
68.
}
69.
70.
//отрисовка персонажа по аналогии
71.
private
void
drawPlayer() {
72.
renderer.setProjectionMatrix(cam.combined);
73.
Player player = world.getPlayer();
74.
renderer.begin(ShapeType.Rectangle);
75.
76.
Rectangle rect = player.getBounds();
77.
float
x1 = player.getPosition().x + rect.x;
78.
float
y1 = player.getPosition().y + rect.y;
79.
renderer.setColor(
new
Color(
1
,
0
,
0
,
1
));
80.
renderer.rect(x1, y1, rect.width, rect.height);
81.
renderer.end();
82.
}
83.
84.
}
ppuX
и ppuY
очень важны…Ведь мир у нас 8на5, а экран телефона в пикслеях не соответствует этим размерам. Поэтому нужны эти переменные, которые при рендеринге будут корректировать координаты объектов для вывода на реальный экран телефона.
OrthographicCamera cam
— камера, которая используется для того, чтобы «посмотреть» на мир. В текущем примере мир очень маленький, и он влезает в камеру, но когда у нас будет большой уровень, и персонаж будет перемещается в нем, мы должны будем менять положение камеры. Собственно, там и расчёт координат изменится…В будущих статьях остановлюсь на этом.
GameScreen
Теперь осталось лишь связать все наши компоненты вместе. Для этого создадим пакет suvitruf.libgdxtutorial.screens, а в нём класс GameScreen
.
GameScreen
реализует интерфейс Screen, который очень походит на ApplicationListener, но у этого есть 2 важных ключевых отличия (два метода).
show()
– вызывается, когда становится активным.
hide()
– вызывается, когда активным становится другой экран.
001.
package
suvitruf.libgdxtutorial.screens;
002.
import
suvitruf.libgdxtutorial.model.*;
003.
import
suvitruf.libgdxtutorial.controller.*;
004.
import
suvitruf.libgdxtutorial.view.*;
005.
006.
import
com.badlogic.gdx.Gdx;
007.
import
com.badlogic.gdx.InputProcessor;
008.
import
com.badlogic.gdx.Screen;
009.
import
com.badlogic.gdx.Application.ApplicationType;
010.
011.
import
com.badlogic.gdx.graphics.GL10;
012.
013.
public
class
GameScreen
implements
Screen, InputProcessor {
014.
private
World world;
015.
private
WorldRenderer renderer;
016.
private
WorldController controller;
017.
018.
private
int
width, height;
019.
020.
@Override
021.
public
void
show() {
022.
023.
world =
new
World();
024.
renderer =
new
WorldRenderer(world);
025.
controller =
new
WorldController(world);
026.
Gdx.input.setInputProcessor(
this
);
027.
028.
}
029.
@Override
030.
public
boolean
touchDragged(
int
x,
int
y,
int
pointer) {
031.
ChangeNavigation(x,y);
032.
return
false
;
033.
}
034.
035.
036.
public
boolean
touchMoved(
int
x,
int
y) {
037.
return
false
;
038.
}
039.
040.
@Override
041.
public
boolean
mouseMoved(
int
x,
int
y) {
042.
return
false
;
043.
}
044.
045.
@Override
046.
public
boolean
keyTyped(
char
character) {
047.
return
false
;
048.
}
049.
050.
@Override
051.
public
void
resize(
int
width,
int
height) {
052.
renderer.setSize(width, height);
053.
this
.width = width;
054.
this
.height = height;
055.
}
056.
057.
@Override
058.
public
void
hide() {
059.
Gdx.input.setInputProcessor(
null
);
060.
}
061.
062.
@Override
063.
public
void
pause() {
064.
}
065.
066.
@Override
067.
public
void
resume() {
068.
}
069.
070.
@Override
071.
public
void
dispose() {
072.
Gdx.input.setInputProcessor(
null
);
073.
}
074.
075.
076.
@Override
077.
public
boolean
keyDown(
int
keycode) {
078.
079.
return
true
;
080.
}
081.
082.
@Override
083.
public
void
render(
float
delta) {
084.
085.
Gdx.gl.glClearColor(
1
,
1
,
1
,
1
);
086.
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
087.
088.
controller.update(delta);
089.
renderer.render();
090.
}
091.
@Override
092.
public
boolean
keyUp(
int
keycode) {
093.
094.
return
true
;
095.
}
096.
097.
private
void
ChangeNavigation(
int
x,
int
y){
098.
controller.resetWay();
099.
if
(height-y > controller.player.getPosition().y * renderer.ppuY)
100.
controller.upPressed();
101.
102.
if
(height-y < controller.player.getPosition().y * renderer.ppuY)
103.
controller.downPressed();
104.
105.
if
( x< controller.player.getPosition().x * renderer.ppuX)
106.
controller.leftPressed();
107.
108.
if
(x> (controller.player.getPosition().x +Player.SIZE)* renderer.ppuX)
109.
controller.rightPressed();
110.
111.
}
112.
113.
@Override
114.
public
boolean
touchDown(
int
x,
int
y,
int
pointer,
int
button) {
115.
116.
if
(!Gdx.app.getType().equals(ApplicationType.Android))
117.
return
false
;
118.
ChangeNavigation(x,y);
119.
return
true
;
120.
}
121.
122.
@Override
123.
public
boolean
touchUp(
int
x,
int
y,
int
pointer,
int
button) {
124.
if
(!Gdx.app.getType().equals(ApplicationType.Android))
125.
return
false
;
126.
controller.resetWay();
127.
128.
return
true
;
129.
}
130.
131.
@Override
132.
public
boolean
scrolled(
int
amount) {
133.
return
false
;
134.
}
135.
}
Этот класс отзывается на действия юзера. По названиям методов, думаю, понятно всё. В ChangeNavigation()
мы определяем и устанавливаем направление движения персонажа. В данном примере, если вы кликаете левее игрока, то двигаетесь влево, если вверх то вверх и т.д.
Если запустите игру, то увидите что-то такое. Персонаж (красный квадратик) будет двигаться в зависимости от того, куда вы кликните относительно него.

Собственно всё. Можете скачать исходники урока Libgdxtutorial-lesson2.rar.
Уведомление: libGDX: Часть 3. Обзор модулей | Suvitruf's Blog
Уведомление: libGDX: Часть 4. Спрайты, Текстуры, Регионы, Атлас | Suvitruf's Blog
И зачем такие костыли? если уж пользуетесь движком так пользуйтесь есть Stage, Actor,Group и не нужно заново выдумывать велосипед.
Это для того, чтобы люди поняли суть. Не хотел писать, ибо MVC не заюзать со scene2d.
Но, вот Архитектура игры на основе scene2d.
Уведомление: libGDX: Часть 6. Работа с Box2D | Suvitruf's Blog
Сделал все по инструкции, при запуске приложения у меня только черный экран. Что это может быть?
Сами делали или взяли готовые сорсы?
Просмотрел ваши исходники, в классе «MyGame» у вас появились несколько новых строк, которые не были указаны ранее. Спасибо вам за статью, очень интерестно =) Читаю дальше ;)
O-o-o. MVC-старая добрая ЯВА и C++ ! А где AWT -) ? Спасибо за рекомендацию-книгу- “Game Architecture and Design” !
Сделал все по инструкции, но при запуске вижу только черный экран. Делал вручную, так как описывалось в статье. исходники смотрел, но там достаточно существенные отличия, а хотелось бы сделать как в статье.
Вроде нечего такого не менял, но все заработало. Выложил на GitHub. Если кому-то надо, вот ссылка: https://github.com/liosha2007/my-gdx-game-sample
Что поменяли то ? )
Да в том то и дело, что свои изменения я отменил а оно все равно работает. Видимо а процессе написания когда я запускал еще не дописав до конца, какие-то файлы остались и из-за этого не работало. А при моих изменениях старые файлы просто перетерлись и с новыми все заработало.
Для тех у кого не работает, проверьте в классе MyGame что бы сам класс был public, т.е. имел вид public class MyGame extends Game.
Добрый день. У меня не запускается исходники не с этого примера не с 6го урока…
Среда Android Developer Tools
Build: v21.1.0-569685
This product includes Eclipse Platform, JDT, CDT, EMF, GEF and WTP,
all of which are Copyright (c) Eclipse contributors and others.
Лог:
[2013-02-15 17:45:20 — MainActivity] ——————————
[2013-02-15 17:45:20 — MainActivity] Android Launch!
[2013-02-15 17:45:20 — MainActivity] adb is running normally.
[2013-02-15 17:45:20 — MainActivity] Performing suvitruf.libgdxtutorial.MainActivity activity launch
[2013-02-15 17:45:20 — MainActivity] Automatic Target Mode: launching new emulator with compatible AVD ‘AVD_for_Nexus_S_by_Google’
[2013-02-15 17:45:20 — MainActivity] Launching a new emulator with Virtual Device ‘AVD_for_Nexus_S_by_Google’
[2013-02-15 17:45:32 — Emulator] Warning: No DNS servers found
[2013-02-15 17:45:32 — Emulator] emulator: WARNING: Requested RAM size of 343MB is too large for your environment, and is reduced to 257MB.
[2013-02-15 17:45:34 — MainActivity] New emulator found: emulator-5554
[2013-02-15 17:45:34 — MainActivity] Waiting for HOME (‘android.process.acore’) to be launched…
Запускается через эмулятор AVD_for_Nexus_S_by_Google, android 4.2
Вы сначала запускаете эмулятор, а потом пытаетесь проект запустить или вместе?
Не запускается, черный экран и больше ничего. Пробовал и на эмуляторе и на телефоне напрямую. Сделал все по уроку.Только была ошибка.
Тут
renderer.filledRect(x1, y1, rect.width, rect.height);
исправил на
renderer.rect(x1, y1, rect.width, rect.height);
и тут
renderer.begin(ShapeType.FilledRectangle);
просило поменять FilledRectangle на Filled поменял -ошибки исчезли.
Почему возникли эти ошибки? мб из-за того что исправил неправильно и не запускается?
Извиняюсь, плохо читал предыдущие комменты. Проблема была в том что
в класс MyGame нужно было добавить строки.Все же выложите лучше сюда и код MyGame.java.
И что насчет той ошибки с renderer.filledRect(x1, y1, rect.width, rect.height); ?
Внизу статьи ссылка на исходники -_-
Я понял…еще как-то коряво двигается игрок, у вас тоже?
Это просто как пример)
Хотите плавное движение, меняйте метод processInput() в зависимости от того, чего хотите добиться.
Извините… Я правильно понял что теперь в Vector2 не рекомендуют использовать tmp() и mul(float)? И строчка:
position.add(velocity.tmp().mul(delta));
теперь должна выглядеть так:
position.add(velocity.scl(delta));
?
Вместо
velocity.tmp().mul(delta)
сейчасvelocity.scl(delta)
вроде. Илиvelocity.cpy().scl(delta)
. Не разбирался в новом билде.спасибо за комментарий, а то никак не мог найти описание метода tmp() в документации к libgdx.
public void rightReleased() {
keys.get(keys.put(Keys.RIGHT, false));
}
Разве нельзя просто использовать <> ????
Или движок теги съел или я вас не понял)
Разве нельзя просто использовать keys.put(Keys.RIGHT, false) ?
Да, в данном случае можно)
Уведомление: Как я писал Bomberman'а на Android | Suvitruf's Blog
Почему не показано как запустить не под андроид? Причем вопрос был такой задан ранее, ответ: «Java мультиплатформенная». Но в итоге, совершенно непонятно как запустить этот код прямо на компьютере. Ломаю голову до сих пор, при чем был неприятно удивлен, когда дописал все по уроку, а в итоге ничего не работает.
На эмуляторе. Либо использовать эмуляторы, которые входят в SDK (тогда можно будет дебажить), либо упаковывать проект в .apk и запускать уже на независимом эмуляторе вроде BlueStack.
Меня как таковая разработка под мобильные устройства не интересовала, в том-то и дело. Я разобрался — там идет обвес под каждую из платформ, по сути надо создать Main класс, где проконфигурировать под десктоп (также как под андройд) и потом уже из этого класса создать инстанс LwjglApplication с экземпляром MyGame (и экземпляром настроек) качестве параметра. Только меня все это сбило столку — генератор проектов, который идет с LibGdx делает тоже самое, но выглядит это эпичнее.
Кстати, спасибо большое за уроки. От себя бы посоветовал делать код более чистый. В паре мест заметил такие штуки, как методы с заглавной буквы, ну и прочая мелочевка.
Рад, что полезны уроки.
Что есть, то есть)
Для людей страдающих грамматическим нацизмом это как серпом по яйцам, я думаю)) но в целом, порядок в голове — порядок в коде. Так что рефакторинг уроков может быть вполне хорошей затеей.
Сейчас реально нету времени =/
Парни, Чёрный экран(( И не понимаю из-за чего так, кому не сложно, посмотрите. Я уже энное количество раз просмотрел свои исходники и исходники из примера…толку ноль(
https://www.dropbox.com/s/n7y940q3efwf6uc/HappyArcanoid.zip
Решение проблем с «ЧЕРНЫМ ЭКРАНОМ»
В myGame добавить импорт:
import suvitruf.libgdxtutorial.screens.*;
Во- первых — спасибо автору за такие уроки с разъяснениями и выкладыванием сорцов.
У кого черный экран и чтобы не копаться в сорцах, надо просто в MyGame прописать следующее:
package suvitruf.libgdxtutorial;
import com.badlogic.gdx.Game;
import suvitruf.libgdxtutorial.screen.*;
public class MyGame extends Game {
public GameScreen game;
public void create() {
game = new GameScreen();
setScreen(game);
}
}
Не понял данную строку
по идее ширина и высота камеры должна быть равна точкам экрана, а тут на сколько я понял идет установка в размерах клеток игры
SetCamera(CAMERA_WIDTH / 2f, CAMERA_HEIGHT / 2f);
Мы просто работаем с относительными координатами. При отрисовке то всё в абсолютные трансформируем.
а в чем тогда смысл камере устанавливать такой диапазон? Или класс OrthographicCamera это понимает?
static final int WIDTH = 480;
static final int HEIGHT = 320;
cam = new OrthographicCamera(WIDTH, HEIGHT);
cam.position.set(WIDTH / 2, HEIGHT / 2, 0);
это из других примеров здесь все понятно
не пойму где трансформируется )
как мне кажется после установки камеры в координаты 8,5 ее центр будет именно в точке 8,5 абсолютных координат
У нас весь мир в относительных координатах:
width = 8;
height = 5;
И камера:
public static float CAMERA_WIDTH = 8f;
public static float CAMERA_HEIGHT = 5f;
Не пойму, в чём проблема то)
кажется понял
пробовал менять настройки камеры ничего не меняет
Пример запускаю в Eclipse, всё нормально, но одно но — плейер и кирпичики все в виде прямоугольников, вытянуты в высоту. Из за чего может быть нарушена пропорция? Сам код как и здесь.
Добрый день! Не могли бы вы пояснить строки, в методе drawBricks():
float x1 = brick.getPosition().x + rect.x;
float y1 = brick.getPosition().y + rect.y;
Для чего нужно + rect.x и + rect.y ???
Привет! Спасибо за урок, многое стало понятнее.
Появился вопрос: делал по твоему примеру, всё заработало, решил на основе этого примера начать делать что-то похожее, и в какой-то момент заметил что у меня игра каждую секунду сжирает по паре метров оперативки. Подскажи пожалуйста, как мне отследить где у меня ошибка и найти место где у меня «утечка памяти»
Спасибо!
try VisualVM
При попытке запуска выдает ошибку:
07-18 13:50:39.892: W/GL2JNIView(1832): creating OpenGL ES 2.0 context
07-18 13:50:39.936: W/dalvikvm(1832): Exception Ljava/lang/UnsatisfiedLinkError; thrown while initializing Lcom/badlogic/gdx/backends/android/AndroidGL20;
07-18 13:50:39.936: W/dalvikvm(1832): threadid=11: thread exiting with uncaught exception (group=0xa4d34b20)
07-18 13:50:39.940: E/AndroidRuntime(1832): FATAL EXCEPTION: GLThread 129
07-18 13:50:39.940: E/AndroidRuntime(1832): Process: suvitruf.libgdxtutorial, PID: 1832
07-18 13:50:39.940: E/AndroidRuntime(1832): java.lang.UnsatisfiedLinkError: Couldn’t load androidgl20 from loader dalvik.system.PathClassLoader[DexPathList[[zip file «/data/app/suvitruf.libgdxtutorial-1.apk»],nativeLibraryDirectories=[/data/app-lib/suvitruf.libgdxtutorial-1, /system/lib]]]: findLibrary returned null
07-18 13:50:39.940: E/AndroidRuntime(1832): at java.lang.Runtime.loadLibrary(Runtime.java:358)
07-18 13:50:39.940: E/AndroidRuntime(1832): at java.lang.System.loadLibrary(System.java:526)
07-18 13:50:39.940: E/AndroidRuntime(1832): at com.badlogic.gdx.backends.android.AndroidGL20.(AndroidGL20.java:27)
07-18 13:50:39.940: E/AndroidRuntime(1832): at com.badlogic.gdx.backends.android.AndroidGraphics.setupGL(AndroidGraphics.java:251)
07-18 13:50:39.940: E/AndroidRuntime(1832): at com.badlogic.gdx.backends.android.AndroidGraphics.onSurfaceCreated(AndroidGraphics.java:302)
07-18 13:50:39.940: E/AndroidRuntime(1832): at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1501)
07-18 13:50:39.940: E/AndroidRuntime(1832): at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1240)
На чём тестили?
Мягко говоря, исходники ужасны. А ведь это будет растащено новичками и надолго засядет у них в головах.
Ждём примеров того, как надо.
исходники рил написаны по mvc ,хотя и есть пару косяков… Так что норм
The method tmp() is undefined for the type Vector2
так ругается на строку 49 в классе Player
У вас новая версия LibGDX, видимо )
Так, а как обновлять-то тогда?
«Создадим пакет suvitruf.libgdxtutorial.controller и добавим в него классWorldController. В этом классе как раз и будет реализована логика вся.» — вся логика должна быть в моделе, а не в контроллере. В общем, вышеуказанный код — извращение над паттерном MVC.
Хех, ознакомился с уроком ,очень понравился патерн MVC, удобная штука (теперь хоть меньше быдлокодить буду),но проблема в том ,что данный урок достаточно сильно устарел и приходилось много гуглить чтобы пофиксить баги ,в этоги получилось как-то так http://prntscr.com/ahd9sq =) , а так все отлично ,спс, буду дальше осваивать движок)
Рад, что хоть кому-то это пригодилось)
не могу понять, почему мы не можем писать логику и отрисовку обьекта в model.
Я например пишу на OpenGl графику и через шейдерную программу движется визуальную часть обьекта.
for (Block brick : world.getBlocks()) {
* * *
}
Can only iterate over an array or an instance of java.lang.Iterable
Очень много лишнего для новичка)
Сложно о таких вещах кусочно рассказывать.
а зачем нам World Renderer? Не лучше ли Screen использовать как View ?И что если в игре несколько окон , как это спроектировать под MVC
В текущих версиях LibGDX куча вспомогательных классов есть нынче. Статье то более 4-х лет уже )
я хочу сделать игру с использованием mvc полностью
Обновите, пожалуйста, уроки. Position.add(velocity.tmp().mul(delta)) Idea ругается, что нет метода tmp() и mul() принимает только Matrix3
Поддержу, статьи не актуальны, больше вводят в ступор чем учат…
Спасибо за статью, очень жаль что уже не все актуально. Есть ли похожие уроки только с актуальной инфой?