Напишите виджет, который отображает «изображения спрайтов» во Flutter.

внешний интерфейс Flutter
Напишите виджет, который отображает «изображения спрайтов» во Flutter.

предисловие

Раньше я использовал игровой движок Flare во Flutter, чтобы сделать игру «подожди 100 секунд, если ты мужчина».статья здесь

После использования движка Flare он полностью пропалFlutterСтиль кодирования, зависящий от приложения. Хотя это больше подходит для разработчиков с опытом разработки игр, таких как я, это не способствует нашему обучению.FlutterРамка. Так что я также сказал в конце той статьи, что мне нужно время, чтобы переписать эту игру с виджетами.

Первая задача - иметь опорный "спрайт"Widget, Поскольку это обучение, его не могут разработать другие, и его нужно сделать самому.

Что такое «спрайт-изображение»

image.png

Карта спрайтов на английском языкеspritesheet(лист спрайтов), который предназначен для размещения нескольких изображений на изображении, и их нужно загрузить в память только один раз. Во время презентации отображается только площадь одного изображения. Как правило, множественная графика используется для размещения нескольких ключевых кадров непрерывной анимации. Помимо того, что это распространено в игровых движках, для уменьшения веб-запросов это также распространено во внешнем интерфейсе.

Принцип демонтажа

Загружайте большое изображение, но каждый раз показывайте только определенную область изображения

image.png

Например, спрайтовое изображение этого самолета имеет размер 330x82 (пикселей) и 5 ​​картинок расположены по горизонтали, поэтому размер одной картинки равен330/5 = 66. Площадь, которую мы показываем каждый раз,x=66*画面序号,y=0,width=66,height=82.

Горизонтальное или вертикальное расположение может быть установлено

Спрайты могут располагаться горизонтально или вертикально, максимальный размер текстур некоторых игровых движков 4096x4096, поэтому в некоторых случаях приходится переключаться между строками, но принципиальная разница невелика, поэтому я не буду здесь подробно ее обсуждать.

Вы можете установить временной интервал воспроизведения и автоматически переключать несколько непрерывных областей

2021-04-08 09_45_16.gif

Большую часть времени нам нужно использовать спрайты для отображения анимации, например, спрайты этого самолета. Первое и второе изображения используются для демонстрации анимации состояния полета самолета и должны воспроизводиться в цикле.

2021-04-08 09_48_43.gif

3-й, 4-й и 5-й кадры используются для демонстрации анимации взрыва самолета, и их нужно воспроизвести только один раз.

Подумайте, какие виджеты следует использовать для создания

Давайте посмотрим, какие виджеты нам нужны, с помощью демонстрации анимации.

2021-04-08 10_00_37.gif

  • Виджет (контейнер), который может управлять областью отображения
  • Требуется виджет (Stack+Positioned), который может указывать координаты

Принцип тоже ясен, и я знаю какой виджет использовать, поэтому следующий код очень прост

Идеи в код

@override
Widget build(BuildContext context) {
return Container(
    width: 66,
    height: 82,
    child: Stack(
      children: [
        Positioned(
          left: 66*currentIndex,
          top: 0,
          child: widget.image
        )
      ],
    ),
);
}

Добавить таймер, который меняется в соответствии с установленным интервалом времениcurrentIndex, то кажется, что изображение движется.

Timer.periodic(widget.duration, (timer) { 
    setState(() {
      if(currentIndex>=4){
        currentIndex=0;
      }
      else currentIndex++;
    });
  }
});

Далее мы инкапсулируем его в оригинальный собственныйWidget, ниже приведен весь код этого виджета

import 'dart:async';

import 'package:flutter/widgets.dart';

class AnimatedSpriteImage extends StatefulWidget {

  final Image image;
  final Size spriteSize;
  final int startIndex;
  final int endIndex;
  final int playTimes;
  final Duration duration;
  final Axis axis;

  AnimatedSpriteImage({
    Key? key,
    required this.image,
    required this.spriteSize,
    required this.duration,
    this.axis = Axis.horizontal,
    this.startIndex = 0,
    this.endIndex = 0,
    this.playTimes = 0,//0 = loop
  }) : super(key: key);

  @override
  _AnimatedSpriteImageState createState() => _AnimatedSpriteImageState();
}

class _AnimatedSpriteImageState extends State<AnimatedSpriteImage> {

  int currentIndex = 0;
  int currentTimes = 0;

  @override
  void initState() {

    currentIndex = widget.startIndex;

    Timer.periodic(widget.duration, (timer) { 
      if(currentTimes<=widget.playTimes){
        setState(() {
          if(currentIndex>=widget.endIndex){
            if(widget.playTimes!=0)currentTimes++;
            if(currentTimes<widget.playTimes||widget.playTimes==0)currentIndex=widget.startIndex;
            else currentIndex = widget.endIndex;
          }
          else currentIndex++;
        });
      }
    });

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        width: widget.spriteSize.width,
        height: widget.spriteSize.height,
        
        child: Stack(
          children: [
            Positioned(
              left: widget.axis==Axis.horizontal?-widget.spriteSize.width*currentIndex:0,
              top: widget.axis==Axis.vertical?-widget.spriteSize.height*currentIndex:0,
              child: widget.image
            )
          ],
        ),
    );
  }
}

Хорошо упакован и очень удобен в использовании.

//播放飞机飞行状态动画
AnimatedSpriteImage(
  duration: Duration(milliseconds: 200),//动画的间隔
  image: Image.asset("assets/images/player.png"),//精灵图
  spriteSize: Size(66, 82),//单画面尺寸
  startIndex: 0,//动画起始画面序号
  endIndex: 1,//动画结束画面序号
  playTimes: 0,//播放次数,0为循环播放
)

//播放飞机爆炸动画
AnimatedSpriteImage(
  duration: Duration(milliseconds: 200),//动画的间隔
  image: Image.asset("assets/images/player.png"),//精灵图
  spriteSize: Size(66, 82),//单画面尺寸
  startIndex: 2,//动画起始画面序号
  endIndex: 4,//动画结束画面序号
  playTimes: 1,//播放次数,0为循环播放
)

Следуйте за Дашуаем

Любовь о старом интерфейсной программе развития разработки, только три платформа для обмена контентом