Параллаксный фон
от aNNiMON
При разработке игр, фон играет немаловажную роль. Благодаря ему можно усилить восприятие игрового мира, скрасить игровой процесс, передать игроку соответствующее настроение.
Одним из простых, но интересных способов улучшить задний фон и восприятие глубины сцены, является применение параллаксной прокрутки фона, то есть, чем дальше объект от камеры, тем медленнее он передвигается.
Вот как бы выглядела игра Mario с этим эффектом. (нажмите для просмотра анимации)
Приступим к реализации.
Я подготовил несколько фоновых слоёв: небо, тучи, облака, горы, равнины, железная дорога, трава и столб. Прежде чем выводить это всё на экран, давайте создадим класс Background. Он будет хранить изображение и координаты для вывода на экран.
Координаты хранятся в double, потому что иначе нельзя сделать плавное перемещение, например на 0.1 — когда за 10 тактов нужно переместить картинку на 1 пиксель.
Заполним массив объектов Background и выведем на экран:
Вот что получилось:
Теперь добавим перемещение. Нужно слегка изменить класс Background, чтобы он отрисовывал ещё одну картинку и нормализировал координаты, если они выйдут далеко за пределы допустимых.
Поскольку не каждый фон нужно двигать (например, небо), можно создать подкласс StaticBackground, в котором вызов метода move(dx, dy) не изменяет координаты:
Изменим инициализацию неба и добавим прокрутку:
Теперь всё выглядит так (анимация):
Добавим класс ParallaxBackground. При перемещении будем умножать координаты на некоторый коэффициент. Если число будет меньше единицы, то фон будет перемещаться медленнее, если больше единицы — быстрее.
Настроим каждый фон. За точку отсчёта возьмём железную дорогу — остальные объекты будут двигаться быстрее или медленнее относительно неё. Чем дальше объект от железной дороги, тем меньше должен быть коэффициент.
Самый дальний объект, не считая неба — тучи. Им мы присвоим значение 0.09, что будет означать перемещение на 1 пиксель примерно за 11 тактов.
Горы будут немного ближе — 0.2, то есть перемещение на 1 пиксель за 5 тактов.
Трава, напротив, должна перемещаться быстрее, чем железная дорога, так как она визуально ближе, поэтому присвоим ей коэффициент 2. Получим:
Для столба мы переопределили ширину, чтобы он не так часто появлялся.
И вот что вышло в итоге:
Скачать: PC | Java ME | Android
Исходник (JECP): ParallaxExpress_src.zip
Фон SVG: vector_background.zip
Одним из простых, но интересных способов улучшить задний фон и восприятие глубины сцены, является применение параллаксной прокрутки фона, то есть, чем дальше объект от камеры, тем медленнее он передвигается.
Вот как бы выглядела игра Mario с этим эффектом. (нажмите для просмотра анимации)
Приступим к реализации.
Я подготовил несколько фоновых слоёв: небо, тучи, облака, горы, равнины, железная дорога, трава и столб. Прежде чем выводить это всё на экран, давайте создадим класс Background. Он будет хранить изображение и координаты для вывода на экран.
- public class Background {
- protected final Image background;
- protected double x, y;
- public Background(Image background, int x, int y) {
- this.background = background;
- this.x = x;
- this.y = y;
- }
- public void move(double dx, double dy) {
- x += dx;
- y += dy;
- }
- public void draw(Graphics g) {
- g.drawImage(background, (int)x, (int)y);
- }
- }
Заполним массив объектов Background и выведем на экран:
- public void onStartApp(int width, int height) {
- backgrounds = new Background[BACKGROUNDS_COUNT];
- try {
- int i = 0;
- backgrounds[i++] = new Background("sky.png", 0, 0);
- backgrounds[i++] = new Background("clouds2.png", 0, 0);
- Image temp = Image.createImage("mountains.png");
- backgrounds[i++] = new Background(temp, 0, height - temp.getHeight());
- // ...
- } catch (IOException ioe) { }
- }
- public void onPaint(Graphics g) {
- for (int i = 0; i < BACKGROUNDS_COUNT; i++) {
- backgrounds[i].draw(g);
- }
- }
Вот что получилось:
Теперь добавим перемещение. Нужно слегка изменить класс Background, чтобы он отрисовывал ещё одну картинку и нормализировал координаты, если они выйдут далеко за пределы допустимых.
- public class Background {
- protected final Image background;
- protected double x, y;
- public Background(Image background, int x, int y) {
- this.background = background;
- this.x = x;
- this.y = y;
- }
- public int getWidth() {
- return background.getWidth();
- }
- public void move(double dx, double dy) {
- x += dx;
- y += dy;
- if (x <= -getWidth()) {
- x += getWidth();
- }
- }
- public void draw(Graphics g) {
- g.drawImage(background, (int)x, (int)y);
- if (x < Main.getWidth() - getWidth()) {
- g.drawImage(background, (int)x + getWidth() - 1, (int)y);
- }
- }
- }
Поскольку не каждый фон нужно двигать (например, небо), можно создать подкласс StaticBackground, в котором вызов метода move(dx, dy) не изменяет координаты:
- public class StaticBackground extends Background {
- public StaticBackground(Image background, int x, int y) {
- super(background, x, y);
- }
- public final void move(double dx, double dy) {
- // prevent call to superclass
- }
- }
Изменим инициализацию неба и добавим прокрутку:
- public void onStartApp(int width, int height) {
- backgrounds = new Background[BACKGROUNDS_COUNT];
- try {
- int i = 0;
- backgrounds[i++] = new StaticBackground("sky.png", 0, 0);
- // ...
- } catch (IOException ioe) { }
- }
- public void onUpdate() {
- for (int i = 0; i < BACKGROUNDS_COUNT; i++) {
- backgrounds[i].move(-3, 0);
- }
- }
Теперь всё выглядит так (анимация):
Добавим класс ParallaxBackground. При перемещении будем умножать координаты на некоторый коэффициент. Если число будет меньше единицы, то фон будет перемещаться медленнее, если больше единицы — быстрее.
- public class ParallaxBackground extends Background {
- protected final double scale;
- public ParallaxBackground(Image background, int x, int y, double scale) {
- super(background, x, y);
- this.scale = scale;
- }
- public void move(double dx, double dy) {
- super.move(dx * scale, dy * scale);
- }
- }
Настроим каждый фон. За точку отсчёта возьмём железную дорогу — остальные объекты будут двигаться быстрее или медленнее относительно неё. Чем дальше объект от железной дороги, тем меньше должен быть коэффициент.
Самый дальний объект, не считая неба — тучи. Им мы присвоим значение 0.09, что будет означать перемещение на 1 пиксель примерно за 11 тактов.
- backgrounds[i++] = new ParallaxBackground("clouds2.png", 0, 0, 0.09);
Горы будут немного ближе — 0.2, то есть перемещение на 1 пиксель за 5 тактов.
- backgrounds[i++] = new ParallaxBackground(temp, 0, height - temp.getHeight(), 0.2);
Трава, напротив, должна перемещаться быстрее, чем железная дорога, так как она визуально ближе, поэтому присвоим ей коэффициент 2. Получим:
- int i = 0;
- backgrounds[i++] = new StaticBackground("sky.png", 0, 0);
- backgrounds[i++] = new ParallaxBackground("clouds2.png", 0, 0, 0.09);
- Image temp = Image.createImage("mountains.png");
- backgrounds[i++] = new ParallaxBackground(temp, 0, height - temp.getHeight(), 0.2);
- temp = Image.createImage("ground.png");
- backgrounds[i++] = new ParallaxBackground(temp, 0, height - temp.getHeight(), 0.7);
- backgrounds[i++] = new ParallaxBackground("clouds1.png", 0, 0, 0.35);
- temp = Image.createImage("rails.png");
- backgrounds[i++] = new Background(temp, 0, height - 3 * temp.getHeight() / 2);
- temp = Image.createImage("grass.png");
- backgrounds[i++] = new ParallaxBackground(temp, 0, height - 2 * temp.getHeight() / 3, 2);
- temp = Image.createImage("post.png");
- backgrounds[i++] = new ParallaxBackground("post.png", 900, height - temp.getHeight(), 6) {
- public int getWidth() {
- return 10000;
- }
- };
И вот что вышло в итоге:
Скачать: PC | Java ME | Android
Исходник (JECP): ParallaxExpress_src.zip
Фон SVG: vector_background.zip