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

Flame游戏玩家无按键时停止移动失效问题求助

问题分析与解决方案

你的代码存在两个核心问题,导致移动逻辑异常:

1. 键盘事件处理返回值错误

Flame 的 KeyboardHandler 要求 onKeyEvent 返回 true 来标记事件已被处理,否则 Flame 会忽略你的状态修改逻辑。原代码返回父类默认的 false,这是移除末尾else语句后按键才响应的根本原因。

2. 方向状态初始化逻辑不严谨

原逻辑未先默认重置方向,多键冲突或按键抬起时可能出现状态残留。


修改后的完整代码

import 'dart:async';
import 'package:farm_game/farm_lands.dart';
import 'package:flame/components.dart';
import 'package:flame/sprite.dart';
import 'package:flutter/src/services/raw_keyboard.dart';

enum PlayerState {
  idle,
  backIdle,
  leftIdle,
  rightIdle,
  walkFront,
  walkBack,
  walkLeft,
  walkRight
}

enum PlayerDirection {
  front,
  back,
  left,
  right,
  none
}

class Player extends SpriteAnimationGroupComponent
    with HasGameRef<FarmLands>, KeyboardHandler {
  late final SpriteSheet spriteSheet;
  final double stepTime = 0.3;

  PlayerDirection playerDirection = PlayerDirection.none;
  double moveSpeed = 50;
  Vector2 velocity = Vector2.zero();

  Player({position}): super(position: position);

  @override
  FutureOr<void> onLoad() {
    spriteSheet = SpriteSheet.fromColumnsAndRows(
      image: game.images.fromCache('characters/Character_Spritesheet.png'),
      columns: 4,
      rows: 4,
    );
    _loadAllAnimations();
    return super.onLoad();
  }

  @override
  void update(double dt) {
    _updatePlayerMovement(dt);
    super.update(dt);
  }

  @override
  bool onKeyEvent(RawKeyEvent event, Set<LogicalKeyboardKey> keysPressed) {
    final isLeftKeyPressed = keysPressed.contains(LogicalKeyboardKey.keyA) ||
        keysPressed.contains(LogicalKeyboardKey.arrowLeft);
    final isRightKeyPressed = keysPressed.contains(LogicalKeyboardKey.keyD) ||
        keysPressed.contains(LogicalKeyboardKey.arrowRight);
    final isUpKeyPressed = keysPressed.contains(LogicalKeyboardKey.keyW) ||
        keysPressed.contains(LogicalKeyboardKey.arrowUp);
    final isDownKeyPressed = keysPressed.contains(LogicalKeyboardKey.keyS) ||
        keysPressed.contains(LogicalKeyboardKey.arrowDown);

    // 先默认重置为无方向,避免状态残留
    playerDirection = PlayerDirection.none;

    // 处理相反方向同时按下的情况
    if (isLeftKeyPressed && isRightKeyPressed) {
      playerDirection = PlayerDirection.none;
    } else if (isUpKeyPressed && isDownKeyPressed) {
      playerDirection = PlayerDirection.none;
    } 
    // 处理单个方向按键
    else if (isLeftKeyPressed) {
      playerDirection = PlayerDirection.left;
    } else if (isRightKeyPressed) {
      playerDirection = PlayerDirection.right;
    } else if (isUpKeyPressed) {
      playerDirection = PlayerDirection.back;
    } else if (isDownKeyPressed) {
      playerDirection = PlayerDirection.front;
    }

    // 必须返回true,告知Flame事件已被处理
    return true;
  }

  void _updatePlayerMovement(double dt){
    double dirX = 0.0;
    double dirY = 0.0;
    switch(playerDirection){
      case PlayerDirection.front:
        current = PlayerState.walkFront;
        dirY += moveSpeed;
        break;
      case PlayerDirection.back:
        current = PlayerState.walkBack;
        dirY -= moveSpeed;
        break;
      case PlayerDirection.left:
        current = PlayerState.walkLeft;
        dirX -= moveSpeed;
        break;
      case PlayerDirection.right:
        current = PlayerState.walkRight;
        dirX += moveSpeed;
        break;
      case PlayerDirection.none:
        current = PlayerState.idle;
        break;
      default:
    }
    velocity = Vector2(dirX, dirY);
    position += velocity * dt;
  }

  void _loadAllAnimations() {
    animations = {
      PlayerState.idle: spriteSheet.createAnimation(
          row: 0,
          stepTime: stepTime,
          from: 0,
          to: 2,
      ),
      PlayerState.backIdle: spriteSheet.createAnimation(
          row: 1,
          stepTime: stepTime,
          from: 0,
          to: 2
      ),
      PlayerState.leftIdle: spriteSheet.createAnimation(
          row: 2,
          stepTime: stepTime,
          from: 0,
          to: 2
      ),
      PlayerState.rightIdle: spriteSheet.createAnimation(
          row: 3,
          stepTime: stepTime,
          from: 0,
          to: 2
      ),
      PlayerState.walkFront: spriteSheet.createAnimation(
          row: 0,
          stepTime: stepTime,
          from: 2,
          to: 4
      ),
      PlayerState.walkBack: spriteSheet.createAnimation(
          row: 1,
          stepTime: stepTime,
          from: 2,
          to: 4
      ),
      PlayerState.walkLeft: spriteSheet.createAnimation(
          row: 2,
          stepTime: stepTime,
          from: 2,
          to: 4
      ),
      PlayerState.walkRight: spriteSheet.createAnimation(
          row: 3,
          stepTime: stepTime,
          from: 2,
          to: 4
      ),
    };
  }
}

额外优化:支持斜向移动

如果需要同时按下两个垂直方向键实现斜向移动,可以放弃PlayerDirection枚举,直接通过向量计算速度:

// 移除PlayerDirection枚举,修改onKeyEvent和_updatePlayerMovement
@override
bool onKeyEvent(RawKeyEvent event, Set<LogicalKeyboardKey> keysPressed) {
  final isLeft = keysPressed.contains(LogicalKeyboardKey.keyA) || keysPressed.contains(LogicalKeyboardKey.arrowLeft);
  final isRight = keysPressed.contains(LogicalKeyboardKey.keyD) || keysPressed.contains(LogicalKeyboardKey.arrowRight);
  final isUp = keysPressed.contains(LogicalKeyboardKey.keyW) || keysPressed.contains(LogicalKeyboardKey.arrowUp);
  final isDown = keysPressed.contains(LogicalKeyboardKey.keyS) || keysPressed.contains(LogicalKeyboardKey.arrowDown);

  velocity.x = 0;
  velocity.y = 0;

  if (isLeft && !isRight) velocity.x -= moveSpeed;
  if (isRight && !isLeft) velocity.x += moveSpeed;
  if (isUp && !isDown) velocity.y -= moveSpeed;
  if (isDown && !isUp) velocity.y += moveSpeed;

  // 斜向移动时保持速度大小一致(可选)
  if (velocity.x != 0 && velocity.y != 0) {
    velocity.normalize().scale(moveSpeed);
  }

  // 更新动画状态
  if (velocity.x > 0) {
    current = PlayerState.walkRight;
  } else if (velocity.x < 0) {
    current = PlayerState.walkLeft;
  } else if (velocity.y > 0) {
    current = PlayerState.walkFront;
  } else if (velocity.y < 0) {
    current = PlayerState.walkBack;
  } else {
    current = PlayerState.idle;
  }

  return true;
}

void _updatePlayerMovement(double dt){
  position += velocity * dt;
}

内容的提问来源于stack exchange,提问作者Brenz Gwynne Hababag

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 00:23:12