1

我想设计一个简单的游戏,其中球击中盒子,用户必须尝试用光标将球向上移动。当球返回时,球运动结束,是屏幕底部的偏移量,如果球偏移量等于光标,我想重置动画,然后给它一个新的方向,但这永远不会发生。 请查看我打印的值。
游戏

当球落下时打印值。

532.0cursor.position.dy,其他人是positionBall.dy + renderBall.size.height
为什么只有当球向上移动时(我点击屏幕的那一刻),球的偏移量和光标的偏移量才相等,而不是相反?
---更新---
当我增加持续时间(例如,10秒),或者从颤振检查器中激活慢速动画按钮时,数字变得更接近,并且通过将它们调整为int,条件是制成。

I/flutter (21563): 532.0
I/flutter (21563): 532.45585

我真的很困惑,我不知道后台发生了什么。

  void initState() {
    super.initState();
    Offset init = initialBallPosition();
    final g = Provider.of<GameStatus>(context, listen: false);
    var key = ball.key;
    _animationController = AnimationController(duration: Duration(seconds: 1), vsync: this);
    _tweenOffset = Tween<Offset>(begin: init, end: init);
    _animationOffset = _tweenOffset.animate(
      CurvedAnimation(parent: _animationController, curve: Curves.linear),
    )..addListener(() {
        if (_animationController.isAnimating) {
          //if (_animationController.status == AnimationStatus.forward) {
          RenderBox renderBall = key.currentContext.findRenderObject();
          final positionBall = renderBall.localToGlobal(Offset.zero);
          print(cursor.position.dy);
          print(positionBall.dy + renderBall.size.height);
          if (positionBall.dy + renderBall.size.height == cursor.position.dy && g.ballDirection == 270) {
            print('bang');
            colideWithCursor();
          }
        }
        if (_animationController.status == AnimationStatus.completed) {
          if (bottomOfBall().dy == Screen.screenHeight / ball.width) {
            gameOver();
          } else
            collision();
        }
      });
    _animationController.isDismissed;
  }

  @override
  Widget build(BuildContext context) {
    final game = Provider.of<GameStatus>(context, listen: false);

    return Selector<GameStatus, bool>(
        selector: (ctx, game) => game.firstShoot,
        builder: (context, startGame, child) {
          if (startGame) {
            game.ballDirection = 90;
            routing(game.ballDirection);
          }
          return UnconstrainedBox(child: (SlideTransition(position: _animationOffset, child: ball.createBall())));
        });
  }
4

2 回答 2

0

由于您(在评论中)询问了一个使用 的示例onTick,所以这是我为一个在屏幕上随机弹跳的球编写的示例应用程序。您可以点击以随机化它的方向和速度。现在它有点伤害你的眼睛,因为它在每一帧都将球重新绘制到一个新的位置。

您可能希望在每次改变方向(例如替换PositionedAnimatedPositioned)之间平滑地为球设置动画,以消除眼睛疲劳。这个重构超出了我的时间。

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:vector_math/vector_math.dart' hide Colors;

Random _rng = Random();

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
  get randomizedDirection =>
      _randomDirectionWithVelocity((150 + _rng.nextInt(600)).toDouble());

  Ticker _ticker;
  Vector2 _initialDirection;
  Duration prevT = Duration.zero;

  BallModel _ballModel;

  @override
  void dispose() {
    super.dispose();
    _ticker.dispose();
  }

  void _init(Size size) {
    _ballModel = BallModel(
      Vector2(size.width / 2, size.height / 2),
      randomizedDirection,
      16.0,
    );
    _ticker = createTicker((t) {
      // This sets state and forces a rebuild on every frame. A good optimization would be
      // to only build when the ball changes direction and use AnimatedPositioned to fluidly
      // draw the ball between changes in direction.
      setState(() {
        _ballModel.updateBall(t - prevT, size);
      });
      prevT = t;
    });
    _ticker.start();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: GestureDetector(
        child: Scaffold(
          body: LayoutBuilder(
            builder: (context, constraints) {
              // Initialize everything here because we need to access the constraints.
              if (_ticker == null) _init(constraints.biggest);
              return Stack(children: [
                Ball(_ballModel),
              ]);
            },
          ),
        ),
        onTap: () => setState(() => _ballModel.v = randomizedDirection),
      ),
    );
  }
}

class BallModel {
  // The current x,y position of the ball.
  Vector2 p;

  // The direction, including speed in pixels per second, of the ball
  Vector2 v;

  // The radius of the ball.
  double r;

  BallModel(this.p, this.v, this.r);

  void updateBall(Duration elapsed, Size size) {
    // Move the ball by v, scaled by what fraction of a second has passed
    // since the last frame.
    p = p + v * (elapsed.inMilliseconds / 1000);
    // If the ball overflows on a given dimension, correct the overflow and update v.
    var newX = _correctOverflow(p.x, r, 0, size.width);
    var newY = _correctOverflow(p.y, r, 0, size.height);
    if (newX != p.x) v.x = -v.x;
    if (newY != p.y) v.y = -v.y;
    p = Vector2(newX, newY);
  }
}

class Ball extends StatelessWidget {
  final BallModel b;

  Ball(this.b);

  @override
  Widget build(BuildContext context) {
    return Positioned(
        left: b.p.x - b.r,
        bottom: b.p.y - b.r,
        child: DecoratedBox(
            decoration:
                BoxDecoration(shape: BoxShape.circle, color: Colors.black)),
        width: 2 * b.r,
        height: 2 * b.r);
  }
}

double _correctOverflow(s, r, lowerBound, upperBound) {
  var underflow = s - r - lowerBound;
  // Reflect s across lowerBound.
  if (underflow < 0) return s - 2 * underflow;
  var overflow = s + r - upperBound;
  // Reflect s across upper bound.
  if (overflow > 0) return s - 2 * overflow;
  // No over or underflow, return s.
  return s;
}

Vector2 _randomDirectionWithVelocity(double velocity) {
  return Vector2(_rng.nextDouble() - .5, _rng.nextDouble() - 0.5).normalized() *
      velocity;
}

从头开始编写游戏和物理逻辑会很快变得非常复杂。我鼓励你使用像 Unity 这样的游戏引擎,这样你就不必自己构建所有东西。还有一个基于 Flutter 的游戏引擎,你可以尝试一下: https ://github.com/flame-engine/flame 。

于 2020-12-12T20:14:01.763 回答
0

这两个数字永远不会完全匹配,因为每帧都会检查动画值并且帧之间会发生重叠。

您可能想要添加一个容差(例如,如果它们在一定数量内,则考虑匹配的值)或创建一些插值逻辑,您可以在其中检查球是否即将与当前帧之间的光标碰撞和下一个。例如替换:

positionBall.dy + renderBall.size.height == cursor.position.dy && g.ballDirection == 270

和:

positionBall.dy + renderBall.size.height + <current_speed_per_frame_of_ball> <= cursor.position.dy && g.ballDirection == 270

这里重要的是动画实际上并不流畅。动画不会从 0.0 连续通过每个可能的值到 1.0。动画的值仅在渲染帧时计算,因此您实际获得的值可能类似于:0.0、0.14、0.30、0.44、0.58....0.86、0.99、1.0。确切的值将取决于动画的持续时间和 Flutter 框架渲染每一帧的确切时间。

于 2020-12-07T19:26:38.903 回答