понедельник, 20 сентября 2010 г.

Спрайтовая анимация

Вот и настало время следующего рассказа о возможностях платформы JavaFX. Я думал, о чем бы мне рассказать вам и решил, что попробую вкратце рассказать про спрайтовую анимацию. Так как я сам начал изучать JavaFX не так давно, будем изучать вместе :)
Итак, что такое спрайтовая анимация? Она представляет собой набор кадров, которые чередуются через определенный промежуток времени и создают иллюзию движения.
Описывать создание проекта, который будет отображать анимацию спрайтами буду на примере:
Вот такой вот бандит на верблюде, найденный на просторах интернета :)

Что мы имеем в арсенале JavaFX для работы с графическими изображениями? Сейчас мы это узнаем.

Начнем с вывода изображения на экран. Для работы с изображениями в JavaFX есть класс ImageView, который используется для отображения изображения, загруженного с помощью класса Image.
Пусть имя файла с нашим изображением будет bandit.png.
Чтобы загрузить его и подготовить к выводу на экран напишем следующий код:

var img2: ImageView = ImageView {
    image: Image {
        url: "{__DIR__}res.png"
    }
}

Рассмотрим подробнее этот код.
В строке 1 мы определяем переменную img2 типа ImageView и присваем ей экземпляр созданного объекта.
В строке 2 мы экземпляру переменной image класса ImageView присваиваем экземпляр объекта Image, который используется для загрузки изображения по указанному URL.

Если мы теперь добавим в сцену переменную img2, то это изображение отобразится в окне нашего приложения.
Код для создания сцены:
Stage {
    title: "Application title"
    scene: Scene {
        width: 900
        height: 150
        content: img2
    }
}
Результатом нашей работы мы должны увидеть вот таке окно:

Но наша цель еще не достигнута. Мы хотим чтобы этот бандит стал анимированным, чтобы в разный промежуток времени отображался один из кадров всего изображения.
Что нам для этого нужно знать?
  • размеры одного кадра (ширина и высота)
  • количество кадров из которых состоит полное изображение
Для данного изображения размер одного кадра равен 75х89 пикселей и количество кадров равно 12..
Продолжим дорабатывать наш код... Определим 3 переменные: ширину, высоту спрайта и количество спрайтов.
def w = 75;
def h = 89;
def n = 12;

У класса ImageView есть свойство viewport типа Rectangle2D. Оно задает область изображения, которая будет отображаться. Если это свойство не задано, то изображение будет отображено полностью. А ведь это именно то, чего мы хотим добиться :) Для решения нашей задачи я предлагаю создать массив значений типа Rectangle2D с разными значениями, которые будут ограничивать каждый кадр из исходного изображения и присвоить начальное значение для отображения нулевому элементу, созданного массива.

def viewports = for (i in [0..&ltn]) = 
           {Rectangle2D{minX:i*w,minY:0,height:h,width:w}}>
img2.viewport = viewports[0];

При запуске приложения на данном этапе мы увидим лишь первый кадр из нашего изображения.
Теперь нам необходимо организовать Timeline, в котором на каждом KeyFrame будет устанавливаться новое отображение из массива и запустить его:

var timeline: Timeline = Timeline {
    repeatCount: Timeline.INDEFINITE
    keyFrames: [
        for (i in [0..&ltn]) {
            KeyFrame {
                time: i*0.1s
                action: function() {
                    img2.viewport = viewports[i];
                }
            }
        }
    ]
}
timeline.play();

Этот код в цикле создает 12 кей-фреймов, каждому из них задается конкретное время с шагом в 0.1 сек и устанавливается действие по окончанию времени, которе устанавливает для вывода на экран очередной спрайт.

Вот и все!!! Поздравляю всех с первой спрайтовой анимацией на JavaFX.

Ну и под конец статьи я думаю что стоит сделать что-то еще :) Например заставить двигаться этого бандита вдоль прямой с права налево. Для этого необходимо задать путь по которому будет двигаться изображение:
def path = Path {
    elements: [
        MoveTo {x:800 y:100}
        LineTo {x:100 y:100}
    ]
}
В JavaFX есть мого разных встроенных преобразований в числе которых присутствует PathTransition. Именно его мы будем использовать для перемещения объекта вдоль пути. В нашем случае путь - это линия, поэтому перемещение можно было сделать и обычным изменением координаты X, однако в случае более сложных путей использование PathTransition значительно облегчает жизнь программисту.

var pathtr = PathTransition {
    duration: 20s
    node: img2
    path: AnimationPath.createFromPath(path)
    interpolator: Interpolator.LINEAR
}
pathtr.play();

Здесь мы создаем объект PathTransition с длительностью прохода по пути 20 сек, привязываем это преобразование в объекту img2, создаем аинмационный путь из нашего пути, устанавливаем линейную интерполяцию и в конце всего этого запускаем Transition и любуемся нашим результатом.

В конце приведу полный текст нашего эксперимента с анимацией:

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.animation.Timeline;
import javafx.animation.KeyFrame;
import javafx.scene.image.ImageView;
import javafx.scene.image.Image;
import javafx.geometry.Rectangle2D;
import javafx.scene.shape.Path;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.LineTo;
import javafx.animation.transition.PathTransition;
import javafx.animation.transition.AnimationPath;
import javafx.animation.Interpolator;


var img2: ImageView = ImageView {
    x: 0
    y: 0
    image: Image {
        url: "{__DIR__}res.png"
    }
}

def w = 75;
def h = 89;
def n = 12;
def viewports = for (col in [0..&ltn]) {Rectangle2D{minX:col*w, minY:0, height: h, width: w}}
img2.viewport = viewports[0];

def path = Path {
    elements: [
        MoveTo {x:800 y:100}
        LineTo {x:100 y:100}
    ]
}


var timeline: Timeline = Timeline {
    repeatCount: Timeline.INDEFINITE
    keyFrames: [
        for (i in [0..&ltn]) {
            KeyFrame {
                time: i*0.1s
                action: function() {
                    img2.viewport = viewports[i];
                }

            }
        }
    ]
}
timeline.play();

var pathtr = PathTransition {
    duration: 20s
    node: img2
    path: AnimationPath.createFromPath(path)
    interpolator: Interpolator.LINEAR
}
pathtr.play();

Stage {
    title: "Application title"
    scene: Scene {
        width: 900
        height: 150
        content: img2
    }
}

Удачи и новых открытий!!!

Комментариев нет:

Отправить комментарий