Flutter 当动画状态为“转发”时,如何重置特定偏移中的动画?

Flutter 当动画状态为“转发”时,如何重置特定偏移中的动画?,flutter,animation,offset,animationcontroller,Flutter,Animation,Offset,Animationcontroller,我想设计一个简单的游戏,在这个游戏中,球打在盒子上,用户必须试着用光标把球带上来。 当球返回时,球移动的终点是屏幕底部的偏移量,如果球偏移量等于光标,我想重置动画,然后给它一个新的方向,但这永远不会发生 请查看我打印的值。 532.0是cursor.position.dy其他是positionBall.dy+renderBall.size.height 为什么只有当球向上移动时(我点击屏幕的那一刻),球偏移量和光标偏移量相等,但不返回? ---更新--- 当我增加持续时间(例如,10秒)或从

我想设计一个简单的游戏,在这个游戏中,球打在盒子上,用户必须试着用光标把球带上来。 当球返回时,球移动的终点是屏幕底部的偏移量,如果球偏移量等于光标,我想重置动画,然后给它一个新的方向,但这永远不会发生

请查看我打印的值。

532.0
cursor.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())));
        });
  }
void initState(){
super.initState();
偏移初始=初始球位置();
final g=Provider.of(上下文,listen:false);
var key=ball.key;
_animationController=animationController(持续时间:持续时间(秒数:1),vsync:this);
_tweenOffset=Tween(开始:init,结束:init);
_animationOffset=\u tweenOffset.animate(
曲线动画(父对象:_animationController,曲线:Curves.linear),
)…addListener(){
如果(_animationController.isAnimating){
//if(_animationController.status==AnimationStatus.forward){
RenderBox renderBall=key.currentContext.finderObject();
最终位置球=renderBall.localToGlobal(偏移量为0);
打印(cursor.position.dy);
打印(positionBall.dy+renderBall.size.height);
if(positionBall.dy+renderBall.size.height==cursor.position.dy&&g.ballDirection==270){
打印(‘砰’);
colideWithCursor();
}
}
如果(_animationController.status==AnimationStatus.completed){
if(球的底部().dy==Screen.screenHeight/ball.width){
gameOver();
}否则
碰撞();
}
});
_animationController.isdissed;
}
@凌驾
小部件构建(构建上下文){
最终游戏=Provider.of(上下文,listen:false);
返回选择器(
选择器:(ctx,游戏)=>game.firstshot,
生成器:(上下文、开始名、子){
if(startGame){
game.ballDirection=90;
路由(游戏。球方向);
}
返回UnconstrainedBox(子项:(SlideTransition(位置:_animationOffset,子项:ball.createBall()));
});
}

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

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

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

与:


positionBall.dy+renderBall.size.height+这两个数字永远不会完全匹配,因为每一帧都会检查动画值,并且帧之间会发生重叠

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

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

与:

positionBall.dy+renderBall.size.height+自您询问后(在评论中)举一个使用
onTick
的例子,下面是我为一个在屏幕上随机反弹的球编写的一个示例应用程序。你可以点击它来随机调整它的方向和速度。现在它有点伤你的眼睛,因为它在每一帧上都在一个新的位置重新绘制球

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

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;
}
import'dart:math';
进口“包装:颤振/材料.省道”;
导入“package:flatter/scheduler.dart”;
导入“package:vector_math/vector_math.dart”隐藏颜色;
随机的_rng=Random();
void main(){
runApp(MyApp());
}
类MyApp扩展了StatefulWidget{
@凌驾
_MyAppState createState()=>\u MyAppState();
}
类_MyAppState使用SingleTickerProviderStateMixin扩展状态{
get randomizedDirection=>
_随机方向与速度((150+_rng.nextInt(600)).toDouble());
股票代码;
向量2_初始方向;
持续时间prevT=持续时间0;
鲍尔模型(BallModel);;
@凌驾
无效处置(){
super.dispose();
_dispose();
}
void _init(大小){
_ballModel=ballModel(
矢量2(尺寸.宽度/2,尺寸.高度/2),
随机方向,
16.0,
);
_ticker=createTicker((t){
//这将设置状态并强制在每个帧上重建
//仅在球改变方向时构建,并使用AnimatedPositioned使其流动
//在方向变化之间划出球。
设置状态(){
_updateBall(t-prevT,size);
});
prevT=t;
});
_ticker.start();
}
@凌驾
小部件构建(构建上下文){
返回材料PP(
主页:手势检测器(
孩子:脚手架(
正文:L