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
相关产品推荐
相关产品推荐

