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

Flutter Flame v1.2.0 角色移动旋转、屏幕越界Bug求解

问题背景

使用基于Flame v1.2.0的Flutter Flame游戏框架开发项目,所有依赖采用最新版本,运行时出现两个阻塞开发的异常:

  • 操控的幽灵角色移动时会高速旋转
  • 角色可从屏幕底部、右侧穿出地图边界

原问题实现代码如下:

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  Flame.device.fullScreen();
  Flame.device.setLandscape();
  runApp(GameWidget(game: MyGame()));
}

class MyGame extends FlameGame with HasDraggables {
  SpriteComponent background = SpriteComponent();
  late SpriteAnimationComponent ghost;
  late final JoystickComponent joystick;
  bool ghostFlipped = false;

  @override
  Future<void> onLoad() async {
    await super.onLoad();
    
    var ghostImage = await images.load('ghost.png');
    // 加载背景
    add(background
      ..sprite = await loadSprite('newyork.jpg')
      ..size = size);
    // 初始化角色控制摇杆
    final buttonPaint = BasicPalette.red.withAlpha(150).paint();
    final backgroundPaint = BasicPalette.black.withAlpha(100).paint();
    joystick = JoystickComponent(
        knob: CircleComponent(radius: 30, paint: buttonPaint),
        background: CircleComponent(radius: 100, paint: backgroundPaint),
        margin: const EdgeInsets.only(left: 40, bottom: 40));
    add(joystick);
    // 加载幽灵角色
    var ghostAnimation = SpriteAnimation.fromFrameData(
          ghostImage,
          SpriteAnimationData.sequenced(
              amount: 4, stepTime: 0.17, textureSize: Vector2(32, 32)));
    ghost = SpriteAnimationComponent()
        ..animation = ghostAnimation
        ..size = Vector2(120,120)
        ..position = Vector2(500, 250);
    add(ghost);
  }

  @override
  void update(double dt) {
    super.update(dt);
    bool moveUp = joystick.relativeDelta[1] < 0;
    bool moveDown = joystick.relativeDelta[1] > 0;
    bool moveLeft = joystick.relativeDelta[0] < 0;
    bool moveRight = joystick.relativeDelta[0] > 0;
    double ghostVectorX = (joystick.relativeDelta * 300 * dt) [0];
    double ghostVectorY = (joystick.relativeDelta * 300 * dt) [1];
    // X方向移动逻辑
    if((moveLeft && ghost.x > 0) || (moveRight && ghost.x < size[0])) {
      ghost.position.add(Vector2(ghostVectorX,0));
    }
    // Y方向移动逻辑
    if((moveUp && ghost.y > 0) || (moveDown && ghost.y < size[1])){
      ghost.position.add(Vector2(0, ghostVectorY));
    }
    // 朝向翻转逻辑
    if(joystick.relativeDelta[0] < 0 && ghostFlipped) {
      ghostFlipped = true;
      ghost.flipHorizontallyAroundCenter();
    }
    if(joystick.relativeDelta[0] > 0 && !ghostFlipped) {
      ghostFlipped = false;
      ghost.flipHorizontallyAroundCenter();
    }
  }
}
问题原因与修复方案

两个异常均为逻辑判断错误导致,对应修复点如下:

1. 角色高速旋转问题

问题原因:水平朝向翻转的判断条件和状态赋值完全写反。初始状态ghostFlipped = false代表角色默认朝右,原逻辑中只要摇杆向右推、角色处于未翻转状态,每帧都会触发一次翻转,且翻转后状态值没有正确更新,持续触发翻转就会出现高速旋转的视觉效果。
修复方式:调整翻转判断逻辑:

  • 摇杆向左推、角色当前朝右(!ghostFlipped)时,执行水平翻转,将ghostFlipped设为true
  • 摇杆向右推、角色当前朝左(ghostFlipped)时,执行水平翻转,将ghostFlipped设为false

2. 角色从右侧/底部穿出边界问题

问题原因:Flame中组件的position默认对应组件左上角锚点的坐标,原边界判断仅校验了组件左上角的坐标:

  • 右边界判断用ghost.x < size[0],没有扣除组件自身宽度,当组件左上角x坐标大于屏幕宽度 - 组件宽度时,组件右边缘已经超出屏幕
  • 下边界判断用ghost.y < size[1],没有扣除组件自身高度,同理会导致组件底部穿出屏幕
    修复方式:调整边界阈值,右边界判断改为ghost.x < size[0] - ghost.width,下边界判断改为ghost.y < size[1] - ghost.height。

修复后完整update方法代码

@override
void update(double dt) {
  super.update(dt);
  // 统一计算移动速度向量,避免重复计算
  final moveSpeed = 300.0;
  final delta = joystick.relativeDelta * moveSpeed * dt;

  // X方向移动与边界校验
  if (delta.x < 0 && ghost.x > 0) {
    // 左移
    ghost.position.x += delta.x;
  } else if (delta.x > 0 && ghost.x < size[0] - ghost.width) {
    // 右移,扣除组件宽度避免穿出右边界
    ghost.position.x += delta.x;
  }

  // Y方向移动与边界校验
  if (delta.y < 0 && ghost.y > 0) {
    // 上移
    ghost.position.y += delta.y;
  } else if (delta.y > 0 && ghost.y < size[1] - ghost.height) {
    // 下移,扣除组件高度避免穿出下边界
    ghost.position.y += delta.y;
  }

  // 朝向翻转逻辑
  if (delta.x < 0 && !ghostFlipped) {
    // 左移且当前朝右,执行翻转
    ghostFlipped = true;
    ghost.flipHorizontallyAroundCenter();
  } else if (delta.x > 0 && ghostFlipped) {
    // 右移且当前朝左,执行翻转
    ghostFlipped = false;
    ghost.flipHorizontallyAroundCenter();
  }
}

替换原有的update方法后即可解决两个异常。

额外优化:原代码中重复计算了两次移动向量,优化后只计算一次delta即可,减少不必要的性能开销。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 01:42:29