В этой статье я объясню, как писать собственные анимации в JavaFX и использовать этот подход для создания класса для спрайтовых анимаций. (Это также будет хорошей практикой для одной из моих сессий на конференции 33-й степени . Я планирую написать игру на JavaFX всего за один час. Это будет весело!)
Лошадь в движении |
Есть много очень хороших статей, описывающих предопределенные переходы (TranslateTransition, RotateTransition и т. Д.) И временные шкалы. В большинстве случаев этих подходов достаточно, но в некоторых случаях требуется больше гибкости. И тогда в игру вступает класс Transition, который можно расширить для определения пользовательской анимации.
Чтобы написать свой собственный класс анимации путем расширения Transition, необходимо выполнить два шага:
- Укажите продолжительность одного цикла
- Реализуйте метод interpolate ()
Продолжительность одного цикла
Вы можете установить продолжительность цикла, вызвав защищенный метод setCycleDuration (). В большинстве случаев продолжительность либо фиксирована (если анимация используется только один раз), либо настраивается пользователем. Почти все предопределенные переходы во время выполнения JavaFX попадают во вторую категорию. Они выставляют продолжительность своего цикла через свойство duration, вы, вероятно, захотите сделать это и в своем классе. В редких случаях продолжительность цикла зависит от других значений. Например, продолжительность SequentialTransition и ParallelTransition зависят от продолжительности их дочерних элементов.
Вы можете изменять продолжительность цикла так часто, как хотите, но учтите, что это не влияет на текущую анимацию. Только после остановки и повторного запуска анимации учитывается новая продолжительность цикла.
Метод интерполяции ()
Метод interpolate () является абстрактным и должен быть переопределен. Определяет реальное поведение анимации. Метод interpolate () вызывается средой выполнения в каждом кадре во время воспроизведения анимации. Передается значение frac, двойное от 0,0 до 1,0 (оба включительно), которое указывает текущую позицию. Значение 0.0 обозначает начало анимации, значение 1.0 — конец. Любое значение между ними определяет относительную позицию. Обратите внимание, что возможный Интерполятор уже учитывается при расчете значения ГРП.
Класс SpriteAnimation
Чтобы продемонстрировать, как определяется пользовательский переход, мы рассмотрим класс, который позволяет нам делать анимацию Sprite. Он берет изображение с несколькими кадрами и со временем перемещает область просмотра от одного кадра к другому. Мы проверим этот класс на знаменитой «Лошади в движении» от Eadweard Muybridge . Хватит говорить, вот код:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
package sandboxfx; import javafx.animation.Interpolator; import javafx.animation.Transition; import javafx.geometry.Rectangle2D; import javafx.scene.image.ImageView; import javafx.util.Duration; public class SpriteAnimation extends Transition { private final ImageView imageView; private final int count; private final int columns; private final int offsetX; private final int offsetY; private final int width; private final int height; private int lastIndex; public SpriteAnimation( ImageView imageView, Duration duration, int count, int columns, int offsetX, int offsetY, int width, int height) { this .imageView = imageView; this .count = count; this .columns = columns; this .offsetX = offsetX; this .offsetY = offsetY; this .width = width; this .height = height; setCycleDuration(duration); setInterpolator(Interpolator.LINEAR); } protected void interpolate( double k) { final int index = Math.min(( int ) Math.floor(k * count), count - 1 ); if (index != lastIndex) { final int x = (index % columns) * width + offsetX; final int y = (index / columns) * height + offsetY; imageView.setViewport( new Rectangle2D(x, y, width, height)); lastIndex = index; } } } |
Для простоты этот пример класса просто принимает все параметры в конструкторе и не позволяет изменять их позже. В большинстве случаев этого достаточно.
Класс ожидает ImageView, длительность одного цикла (то есть, сколько времени потребуется, чтобы пройти все кадры), количество кадров, количество столбцов (сколько кадров находится в одной строке изображения), смещение первого кадра, а также ширина и высота всех кадров. Длительность полного цикла передается в суперкласс путем вызова setCycleDuration (), все остальные значения сохраняются. В качестве последнего шага в конструкторе, интерполятор устанавливается на линейный. По умолчанию для всех переходов установлен замедляющий интерполятор, потому что именно это обычно дает наилучшие результаты. Но в нашем случае мы хотим, чтобы все кадры проходили с одинаковой скоростью, а интерполятор замедления выглядел бы странно.
Метод interpolate () принимает переданное значение и вычисляет, какой кадр необходимо отобразить в данный момент. Если он изменился с момента последнего вызова interpolate (), вычисляется позиция нового кадра и соответственно устанавливается видовой экран ImageView. Вот и все.
Лошадь в движении
Чтобы продемонстрировать класс SpriteAnimation, мы будем анимировать The Horse in Motion. Код для этого прост, большая часть работы уже выполнена. Он создает ImageView с окном просмотра, установленным на первый кадр, и создает экземпляр класса SpriteAnimation. Параметры являются лишь оценочными, вы можете немного их настроить.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
package sandboxfx; import javafx.animation.Animation; import javafx.application.Application; import javafx.geometry.Rectangle2D; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.stage.Stage; import javafx.util.Duration; public class SandboxFX extends Application { private static final Image IMAGE = new Image( "http://upload.wikimedia.org/wikipedia/commons/7/73/The_Horse_in_Motion.jpg" ); private static final int COLUMNS = 4 ; private static final int COUNT = 10 ; private static final int OFFSET_X = 18 ; private static final int OFFSET_Y = 25 ; private static final int WIDTH = 374 ; private static final int HEIGHT = 243 ; public static void main(String[] args) { launch(args); } public void start(Stage primaryStage) { primaryStage.setTitle( "The Horse in Motion" ); final ImageView imageView = new ImageView(IMAGE); imageView.setViewport( new Rectangle2D(OFFSET_X, OFFSET_Y, WIDTH, HEIGHT)); final Animation animation = new SpriteAnimation( imageView, Duration.millis( 1000 ), COUNT, COLUMNS, OFFSET_X, OFFSET_Y, WIDTH, HEIGHT ); animation.setCycleCount(Animation.INDEFINITE); animation.play(); primaryStage.setScene( new Scene( new Group(imageView))); primaryStage.show(); } } |
Вывод
Определение собственной анимации путем расширения класса Transition является простым и понятным. Это чрезвычайно мощный подход, потому что анимация, созданная таким образом, обладает всеми функциональными возможностями, которые имеют обычные анимации. Например, вы можете играть в нее медленнее и быстрее, изменяя скорость, и вы можете играть в нее даже задом наперед. Вы можете запустить его в цикле и использовать его в ParallelTransition и SequentialTransition для создания еще более сложных анимаций.
Ссылка: Создание анимации спрайта с помощью JavaFX от нашего партнера по JCG Майкла Хайнрихса в блоге Майка .