首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从火焰0.29.0到火焰1.0.0和从box2d_flame:^0.4.6到flame_forge2d 0.11.0

从火焰0.29.0到火焰1.0.0和从box2d_flame:^0.4.6到flame_forge2d 0.11.0
EN

Stack Overflow用户
提问于 2022-05-16 15:27:42
回答 1查看 144关注 0票数 0

我迁移到火焰1.1.1,从火焰v0.29.4,我找不到一个好的路线图。

如何有效地替换Box2DComponent?比如组件和视图,我不知道在哪里以及如何替换它们。

用火焰游戏代替BaseGame正确吗?用Forge2DGame代替Forge2DGame是否正确?

在这里我的三节课。我知道这是个困难的问题,但我真的需要一些帮助。谢谢

代码语言:javascript
复制
import 'dart:math' as math;
import 'dart:ui' as ui;

import 'package:artista_app/features/tastes/model/presentation/tastes_vm.dart';
import 'package:box2d_flame/box2d.dart';
import 'package:flame/box2d/box2d_component.dart';
import 'package:flame/box2d/viewport.dart' as box2d_viewport;
import 'package:flame/components/mixins/tapable.dart';
import 'package:flame/game/base_game.dart';
import 'package:flame/gestures.dart';
import 'package:flame/text_config.dart';
import 'package:flutter/material.dart';

class BubblePicker extends BaseGame with TapDetector {
  PickerWorld _pickerWorld;

  @override
  ui.Color backgroundColor() {
    return Colors.transparent;
  }

  final void Function(TastesVM) onTastesChange;

  BubblePicker(TastesVM tastes, {this.onTastesChange}) : super() {
    _pickerWorld = PickerWorld(tastes);
    _pickerWorld.initializeWorld();
    onTastesChange?.call(TastesVM(
        tastes: (tastes.tastes.where((taste) => taste.checked).toList())));
  }

  @override
  void onTapUp(TapUpDetails details) {
    _pickerWorld.handleTap(details);
    onTastesChange?.call(TastesVM(tastes: _pickerWorld.checkedTastes));
    super.onTapUp(details);
  }

  @override
  bool debugMode() => true;
  @override
  void render(Canvas canvas) {
    super.render(canvas);
    _pickerWorld.render(canvas);
  }

  @override
  void resize(Size size) {
    super.resize(size);
    _pickerWorld.resize(size);
  }

  @override
  void update(double t) {
    super.update(t);
    _pickerWorld.update(t);
  }
}

class PickerWorld extends Box2DComponent {
  final TastesVM tastes;
  PickerWorld(this.tastes) : super(gravity: 0);

  @override
  void initializeWorld() {}

  @override
  void render(Canvas canvas) {
    super.render(canvas);
  }

  Offset screenOffsetToWorldOffset(Offset position) {
    return Offset(position.dx - (viewport.size.width / 2),
        position.dy - (viewport.size.height / 2));
  }

  List<TasteVM> get checkedTastes => [
        for (final component in components)
          if (component is Ball && component.checked) component.model
      ];

  void handleTap(TapUpDetails details) {
    for (final component in components) {
      if (component is Ball) {
        final worldOffset = screenOffsetToWorldOffset(details.localPosition);
        if (component.checkTapOverlap(worldOffset)) {
          component.onTapUp(details);
        }
      }
    }
  }

  @override
  void resize(Size size) {
    dimensions = Size(size.width, size.height);
    viewport = box2d_viewport.Viewport(size, 1);
    if (components.isEmpty) {
      var tastesList = tastes.tastes;
      tastesList.forEach((element) {
        var ballPosOffset = Vector2(
            math.Random().nextDouble() - 0.5, math.Random().nextDouble() - 0.5);
        var x = ballPosOffset.x * 150;
        var y = ballPosOffset.y * 150;
        add(Ball(Vector2.array([x, y]), this, element));
      });
    }
  }
}

class Ball extends BodyComponent with Tapable {
  static const transitionSeconds = 0.5;
  var transforming = false;
  var kNormalRadius;
  static const kExpandedRadius = 50.0;
  var currentRadius;
  var lastTapStamp = DateTime.utc(0);
  final TasteVM model;
  final TextConfig smallTextConfig = TextConfig(
    fontSize: 12.0,
    fontFamily: 'Arial',
    color: Colors.white,
    textAlign: TextAlign.center,
  );
  final TextConfig bigTextConfig = TextConfig(
    fontSize: 24.0,
    fontFamily: 'Arial',
    color: Colors.white,
    textAlign: TextAlign.center,
  );
  Size screenSize;
  ui.Image ballImage;

  bool get checked => model.checked;

  Ball(
    Vector2 position,
    Box2DComponent box2dComponent,
    this.model,
  ) : super(box2dComponent) {
    ballImage = model.tasteimageResource;
    final shape = CircleShape();

    kNormalRadius = model.initialRadius;
    currentRadius = (model.checked) ? kExpandedRadius : model.initialRadius;
    shape.radius = currentRadius;
    shape.p.x = 0.0;

    // checked = model.checked;

    final fixtureDef = FixtureDef();
    fixtureDef.shape = shape;

    fixtureDef.restitution = 0.1;
    fixtureDef.density = 1;
    fixtureDef.friction = 1;
    fixtureDef.userData = model;

    final bodyDef = BodyDef();
    bodyDef.linearVelocity = Vector2(0.0, 0.0);
    bodyDef.position = position;
    bodyDef.type = BodyType.DYNAMIC;
    bodyDef.userData = model;
    body = world.createBody(bodyDef)..createFixtureFromFixtureDef(fixtureDef);
  }

  @override
  void renderCircle(Canvas canvas, Offset center, double radius) async {
    final rectFromCircle = Rect.fromCircle(center: center, radius: radius);
    final ballDiameter = radius * 2;

    if (ballImage == null) {
      return;
    }

    final image = checked ? ballImage : null;

    final paint = Paint()..color = const Color.fromARGB(255, 101, 101, 101);
    final elapsed =
        DateTime.now().difference(lastTapStamp).inMicroseconds / 1000000;

    final transforming = elapsed < transitionSeconds;

    if (transforming) {
      _resizeBall(elapsed);
    }

    canvas.drawCircle(center, radius, paint);

    if (image != null) {
      //from: https://stackoverflow.com/questions/60468768/masking-two-images-in-flutter-using-a-custom-painter/60470034#60470034

      canvas.saveLayer(rectFromCircle, Paint());

      //draw the mask
      canvas.drawCircle(
        center,
        radius,
        Paint()..color = Colors.black,
      );

      //fit the image into the ball size
      final inputSize = Size(image.width.toDouble(), image.height.toDouble());
      final fittedSizes = applyBoxFit(
        BoxFit.cover,
        inputSize,
        Size(ballDiameter, ballDiameter),
      );
      final sourceSize = fittedSizes.source;
      final sourceRect =
          Alignment.center.inscribe(sourceSize, Offset.zero & inputSize);

      canvas.drawImageRect(
        image,
        sourceRect,
        rectFromCircle,
        Paint()..blendMode = BlendMode.srcIn,
      );
      canvas.restore();
    }

    final span = TextSpan(
        style: TextStyle(color: Colors.white, fontSize: 10),
        text: model.tasteDisplayName);

    final tp = TextPainter(
      text: span,
      textAlign: TextAlign.center,
      textDirection: TextDirection.ltr,
    );
    tp.layout(minWidth: ballDiameter, maxWidth: ballDiameter);
    tp.paint(canvas, Offset(center.dx - radius, center.dy - (tp.height / 2)));
  }

  @override
  void update(double t) {
    final center = Vector2.copy(box.world.center);
    final ball = body.position;

    center.sub(ball);
    var distance = center.distanceTo(ball);
    body.applyForceToCenter(center..scale(1000000 / (distance)));
  }

  @override
  ui.Rect toRect() {
    var rect = Rect.fromCircle(
      center: Offset(body.position.x, -body.position.y),
      radius: currentRadius,
    );
    return rect;
  }

  @override
  void onTapUp(TapUpDetails details) {
    lastTapStamp = DateTime.now();
    model.checked = !checked;
    if (checked) {
      currentRadius = kExpandedRadius;
    } else {
      currentRadius = kNormalRadius;
    }
  }

  void _resizeBall(elapsed) {
    var progress = elapsed / transitionSeconds;
    final fixture = body.getFixtureList();
    var sourceRadius = (checked) ? kNormalRadius : kExpandedRadius;
    var targetRadius = (checked) ? kExpandedRadius : kNormalRadius;

    var progressRad = ui.lerpDouble(0, math.pi / 2, progress);
    var nonLinearProgress = math.sin(progressRad);

    var actualRadius =
        ui.lerpDouble(sourceRadius, targetRadius, nonLinearProgress);
    fixture.getShape().radius = actualRadius;
  }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-05-16 20:37:01

这个问题对StackOverflow来说有点太宽泛了,但是我会尽我所能来回答它。

要在火焰中使用Forge2D (以前是box2d.dart),您必须添加flame_forge2d作为依赖项。在flame_forge2d中,您将得到一个应该使用的Forge2DGame,而不是FlameGame (而不是您正在使用的古老的BaseGame类)。

在此之后,为要添加到BodyComponent的每个主体扩展Forge2DGame s。

代码语言:javascript
复制
class Ball extends BodyComponent {
  final double radius;
  final Vector2 _position;

  Ball(this._position, {this.radius = 2});

  @override
  Body createBody() {
    final shape = CircleShape();
    shape.radius = radius;

    final fixtureDef = FixtureDef(
      shape,
      restitution: 0.8,
      density: 1.0,
      friction: 0.4,
    );

    final bodyDef = BodyDef(
      userData: this,
      angularDamping: 0.8,
      position: _position,
      type: BodyType.dynamic,
    );

    return world.createBody(bodyDef)..createFixture(fixtureDef);
  }
}

createBody()方法中,您必须创建Forge2D主体,在本例中创建一个圆。如果不希望它直接呈现圆,则可以设置renderBody = false。要在BodyComponent上呈现其他内容,可以重写render方法,或者将普通的Flame组件作为子组件添加到其中,例如SpriteComponentSpriteAnimationComponent

要添加一个子元素,只需在add方法(或在另一个合适的地方)中调用onLoad

代码语言:javascript
复制
class Ball extends BodyComponent {
  ...

  @override
  Future<void> onLoad() async {
    await super.onLoad();
    add(SpriteComponent(...));
  }

  ...
}

由于您使用的是Tappable混音,所以您还应该将HasTappables混音添加到Forge2D游戏类中。

您可以在这里找到一些示例:https://examples.flame-engine.org/#/flame_forge2d_Blob%20example (按右上角的< >以获取代码)。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72261720

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档