You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter Flame游戏重启后角色仍处于'up'状态的问题解决

问题:Flame游戏重启后角色状态未重置为middle

我使用Flutter和Flame开发一款游戏,游戏角色拥有middle和up两种状态:用户触发跳跃时角色切换至up状态,延迟后自动恢复为middle状态。

当前遇到的问题是:当角色处于up状态时发生碰撞导致游戏结束,重启游戏后角色仍停留在up状态,而非重置为middle状态。我已尝试在resetPosition()和reset()方法中重置角色位置与状态为middle,但问题仍未解决。

代码片段

import 'dart:async';
import 'package:flame/collisions.dart';
import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame_audio/flame_audio.dart';
import 'package:flutter/cupertino.dart';
import 'package:rungame/game/assets.dart';
import 'package:rungame/game/configuration.dart';
import 'package:rungame/game/skateboardera_girl.dart';
import 'package:rungame/game/skater_girl_movement.dart';

class SkaterGirl extends SpriteGroupComponent<SkaterGirlMovement>
    with HasGameRef<skateboarderGirl>, CollisionCallbacks {
  SkaterGirl();
  int score = 0;
  Timer? _delayTimer; // 新增:保存延迟任务引用

  @override
  Future<void> onLoad() async {
    final skaterGirlCharacter = await gameRef.loadSprite(Assets.skatergirl);
    final skaterGirlCharacterUp = await gameRef.loadSprite(Assets.skatergirlUp);
    FlameAudio.audioCache
        .loadAll([Assets.flying, Assets.collision, Assets.point]);

    size = Vector2(120, 120);
    double bottomPadding = 190;
    position = Vector2(120, gameRef.size.y - size.y - bottomPadding);

    current = SkaterGirlMovement.middle;
    sprites = {
      SkaterGirlMovement.middle: skaterGirlCharacter,
      SkaterGirlMovement.up: skaterGirlCharacterUp,
    };

    add(RectangleHitbox());
  }

  void fly() {
    if (current != SkaterGirlMovement.up && !gameRef.isHit) {
      try {
        FlameAudio.play(Assets.flying);
        debugPrint('çaldı canım');
      } catch (e) {
        debugPrint('Error playing audio: $e');
      }
      add(
        MoveByEffect(
          Vector2(0, Config.gravity),
          EffectController(duration: 0.5, curve: Curves.decelerate),
          onComplete: () {
            if (current != SkaterGirlMovement.up && !gameRef.isHit) {
              current = SkaterGirlMovement.up;
              debugPrint('if up tan sonra ki ');
              // 替换Future.delayed为Timer,方便取消
              _delayTimer?.cancel();
              _delayTimer = Timer(Duration(milliseconds: 1500), () {
                if (!gameRef.isHit) {
                  debugPrint("if game is hit -->  ${game.isHit}");
                  resetPosition();
                  current = SkaterGirlMovement.middle;
                  markNeedsPaint();
                  gameRef.skatergirl.score++;
                  try {
                    FlameAudio.play(Assets.point);
                    debugPrint('çaldı point');
                  } catch (e) {
                    debugPrint('Error playing audio: $e');
                  }
                  debugPrint("Score fly file: ${gameRef.skatergirl.score}");
                } else {
                  resetPosition();
                  return;
                }
              });
            }
          },
        ),
      );
    } else {
      debugPrint('Collision detected while flying, exiting fly function');
      current = SkaterGirlMovement.middle;
      markNeedsPaint();
      return;
    }
  }

  void resetPosition() {
    debugPrint('reset position ');
    size = Vector2(120, 120);
    double bottomPadding = 190;
    position = Vector2(
      120,
      gameRef.size.y - size.y - bottomPadding,
    );
    current = SkaterGirlMovement.middle;
    markNeedsPaint();
  }

  @override
  void onCollisionStart(
    Set<Vector2> intersectionPoints,
    PositionComponent other,
  ) {
    super.onCollisionStart(intersectionPoints, other);
    debugPrint('Collision detected ');

    gameOver();
  }

  void gameOver() {
    try {
      FlameAudio.play(Assets.collision);
      debugPrint('çaldı game over');
    } catch (e) {
      debugPrint('Error playing audio: $e');
    }

    // 清理所有未完成的异步任务和Effect
    removeAllEffects();
    _delayTimer?.cancel();

    gameRef.overlays.add('gameOver');
    gameRef.pauseEngine();
    game.isHit = true;
    resetPosition();

    debugPrint("game is hit -->  ${game.isHit}");
  }

  void reset() {
    gameRef.flowerGroup.resetFlowers();
    current = SkaterGirlMovement.middle;
    markNeedsPaint();
    score = 0;
    // 重置碰撞状态,避免后续逻辑异常
    game.isHit = false;
    resetPosition();
  }
}

解决方案

1. 清理未完成的异步任务与Effect

碰撞触发游戏结束时,角色的MoveByEffect或延迟任务可能仍在后台运行,会覆盖你重置的状态:

  • 新增Timer? _delayTimer变量保存跳跃后的延迟任务引用,方便取消
  • 在gameOver方法中调用removeAllEffects()移除所有未完成的动画效果,同时调用_delayTimer?.cancel()取消延迟任务

2. 强制刷新UI状态

SpriteGroupComponent的状态更新可能不会自动触发UI刷新,在所有重置current状态的地方添加markNeedsPaint(),强制组件重新绘制。

3. 完善重置逻辑

在reset方法中补充重置game.isHit状态,避免后续逻辑因碰撞状态未清除而异常,同时再次调用resetPosition()确保位置和状态正确。

4. 确保重启时正确调用重置方法

检查游戏重启按钮的回调逻辑,确保明确调用SkaterGirl的reset()方法,同时重置游戏引擎状态:

// 示例重启按钮回调
void onRestartButtonPressed() {
  gameRef.skatergirl.reset();
  gameRef.resumeEngine();
  gameRef.overlays.remove('gameOver');
}

内容的提问来源于stack exchange,提问作者senanurr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 12:44:56