Flutter Flame 教程3 -- Components组件

4,247 阅读7分钟

Components组件


这个类表示了屏幕上的单一对象,可以是浮动的方块或者旋转的精灵。
基本抽象类有期望被实现的update和render方法。
中间的实现类PositionComponent 为Component类 添加了x,y,width,height和angle属性,也包括一些有用的方法比如distance和angleBetween。
最常用的实现是SpriteComponent,可以用于创建Sprite(精灵):

    import 'package:flame/components/component.dart';
    Sprite sprite = Sprite('player.png');
    
    const size = 128.0;
    var player = SpriteComponent.fromSprite(size,size,sprite);
    
    // 屏幕坐标
    player.x = ... //默认是0 
    player.y = ... // 默认是0
    player.angle = ... //默认是0
    
    player.render(canvas); //只有当图片被加载,并且x,y,width,height参数不为空时,它才会被绘制

在这个事件中,如果你想简单的改变正在绘制的组件方向,你也可以在render(Canvas canvas)方法中使用renderFlipX和renderFlipY来旋转任何会知道canvas的组件。对于所有的PositionComponent对象都有用,尤其适用于SpriteComponent和AnimationComponent。比如设置component.renderFlipX = true就可以翻转横向绘制。

每个Componeng都有一些你可以选择使用的方法,它们被用于BaseGame类。如果你不使用BaseGame,你可以在自己的游戏循环中使用这些方法。

每次屏幕重置大小,resize方法都会被调用。刚开始是通过add方法使得component被加载的。当屏幕重置大小的时候,你需要应用这些变化到组件的x,y,width和height,或者其他变化。你可以将这些变量开始应用到add方法,因为直到所有都设置好的时候,精灵才会被绘制。

destroy方法可以被实现成返回true,提醒BaseGame你的对象被标记为销毁,而且在当前更新循环之后被移除。它将不再被绘制或者更新。

isHUD方法可以被实现为返回true(默认为false),使得BaseGame忽略这个element的camera。

onMount方法可以重写以便为这个component执行初始化代码。当这个方法被调用的时候,BaseGame确保所有可以改变这个component行为的mixins都已经被处理过。

onDestroy方法可以被重写,以便这个component在从游戏中移除之前执行代码。

还有一些其他实现:

  • AnimationComponent接受一个Animation对象,并且绘制一个周期的动画精灵(更多关于Animations的详情请点击这里
  • SvgComponent接受一个Svg对象,并在游戏中绘制这个Svg
  • ParallaxComponent可以绘制许多帧的的一个并行背景
  • Box2DComponent,有一个内置的物理引擎(使用Dart版的Box2D)

Animation Component 动画组件


这个组件使用Animation的一个实例,去展现一个可以运行一个周期动画的精灵的组件。
下面的例子创建了简单的三帧动画:

    List<Sprite> sprites = [0,1,2].map((i) => new Sprite('player_${i}.png')).toList();
    this.player = AnimationComponent(64.0,64.0,new Animation.spriteList(sprites,stepTime:0.01));

如果你有一张精灵表,你可以使用sequenced构造方法,等同于提供一个Animation类(在这一章中查看更多详情)

    this.player = AnimationComponent.sequenced(64.0,64.0,'player.png',2);

如果你不使用BaseGame,就算这个组件是静态的,也不要忘了去更新,因为这个动画对象需要滴答滴答才能移动帧。

SvgComponent


这个组件使用Svg类的实例,这个实例有一个绘制到游戏中的svg图像。

    Svg svg = Svg('android.svg');
    SvgComponent android = SvgComponent.fromSvg(100,100,svg);
    android.x = 100;
    android.y = 100;

FlareAnimation Component


这个组件包装了FlareAnimation的一个实例,它接受一个Flare动画文件的文件名称,文件中包含你想要使用的动画。组件还可以接受绘制动画的长度和高度。

    final fileName = "assets/Bob_Minion.flr";
    final animation = "Wave";
    final width = 306;
    final height = 228;
    
    FlareComponent flareAnimation = FlareComponent(fileName,aniamtion,width,height);
    flareAnimation.x = 50;
    flareAnimation.y = 240;
    add(flareAnimation);

你可以使用updateAnimation去改变当前播放的动画。

作为实例,请查看这个源文件.

Composed component 组合组件

一个帮助你将一个组件和另一个组件包裹在一起的mixin。通过层次结构对课件组件进行分组很有用。当实现的时候,使得每一项在它的components集合属性中都能在同一种情况下更新和渲染。

例子展现了两个组件的可见性是如何被一个包装类处理的:

    class GameOverPanel extends PositionComponent with Resizable, ComposedComponent{
        bool visible = false;
        
        GameOverText gameOverText;
        GameOverButton gameOverButton;
        
        GameOverPanel(Image spriteImage):super(){
            gameOverText = GameOverText(spriteImage);
            gameOverButton = GameOverButton(spriteImage);
            
            components..add(gameOverText)..add(gameOverButton);
        }
        
        @override
        void render(Canvas canvas){
            if(visible){
                super.render(canvas);
            }
        }
    }

Parallax Component 并行组件


这个组件可以通过绘制彼此之上的多个透明图像,来渲染漂亮的背景,每个都错位一点。

这个基本原理是,当你观察地平线并移动时,更近的物体似乎比更远的物体移动的更快。

这个组件模拟了这种效果,创建了一种更有深度感的真实背景。

像这样创建它:

    final images = [
        ParallaxImage("mountains.jpg"),
        ParallaxImage("forest.jpg"),
        ParallaxImage("city.jpg"),
    ];
    this.bg = ParallaxComponent(images);

这样创建了一个静态背景,如果你想要移动的话,你需要设置命名可选参数baseSpeed和layerDelta。比如你想要在X轴上移动背景图,并使图像离得更远,你可以像这样做:

    this.bg = ParallaxComponent(images, baseSpeed: Offset(50,0),layerDelta: Offset(20,0));

你可以在任何时候设置baseSpeed和layerDelta。比如当你的任务跳跃或者你的游戏加速的时候.

    this.bg.baseSpeed = Offset(100,0);
    this.bg.layerDelta = Offset(40,0);

默认图像左下角对齐,在X轴上不断重复,并且按比例缩放以便图像可以覆盖屏幕高度。如果你想要修改这个属性,比如你并不是在做一个边缘滚动的游戏,你可以为每一个ParallaxImage设置repeat、alignment和fill参数。

高级例子:

    final images = [
        ParallaxImage("stars.jpg", repeat: ImageRepeat.repeat, alignment: Alignment.center, fill:LayerFill.width),
        ParallaxImage("planets.jpg", repeat: ImageRepeat.repeatY, alignment: Alignment.bottomLeft, fill: LayerFill.none),
        ParallaxImage("dust.jpg", repeat: ImageRepeat.repeatX, alignment: Alignment.topRight, fill: LayerFill.height),
    ];
    this.bg = ParallaxComponent(images, baseSpeed: Offset(50,0),layerDelta: Offset(20,0));
  • 在这个例子中,stars图像会在两个轴中重复绘制,屏幕中间对齐,所以会缩放以适应屏幕宽度
  • planets图片会在Y轴中重复,与屏幕左下角对齐,不缩放
  • dust图片在X轴重复,右上角对齐,缩放以填充屏幕高度

一旦你设置了这些你需要的参数,就可以当做其他component去绘制ParallaxComponent了。

就像AnimationComponent一样,尽管你的并行是静态的,你必须调用这个component的update方法,它才能运行动画。同时,也不要忘了将你的图片添加到pubspec.yaml目录中作为资源文件,否则它们将无法被找到。

一个实现的例子,可以在示例目录中找到。

Box2D Component


Flame与Flutter实现的Box2D有一个基本的集成。

一个box2d世界的所有概念被映射到Box2DComponent组件;每一个Body都应该是BodyComponent,并且直接被添加到Box2DComponent,而不是game list。

所以在你game list中,你有一个HUD和其他的非物理相关的组件,也可以有足够多的Box2DComponents如果你喜欢的话(我猜通常是一个),然后添加你的物理实体到你的Components实例。当这个Component更新的时候,它会使用box2d物理引擎去正确的更新每一个子组件。

你可以查看一个更完整的例子,这个例子是@feroult制作的WIP游戏(请注意:它使用0.6.x版本的flame,但是相关Box2D api并没有改变)。

Tiled Component


现在我们有一个Tiled 组件的非常基本的实现。这个API使用Tiled库来解析地图文件和绘制可见图层。

如何使用这个API可以在这里找到.

Nine Tile Box Component 九宫格组件

九宫格组件使用格子精灵绘制的长方形。

这个格子精灵是3x3的格子,有9块,表示4个角、4个边以及中间一块。

角被绘制到同一大小,边在边的方向上拉伸,中间的块两边都拉伸。

通过使用这个,你可以获得在任何大小上都扩展很好的box/rectangle.这对于制作面板、对话框、边框非常有用。

你可以在例子 nine_tile_box中查看更多如何使用的细节。