Flutter Flame: Player Movement Stop after no key is pressed not working

99 Views Asked by At

I've been trying to create player movement in my Flutter game. I want the player to stop moving if no movement key is pressed but the code below doesn't work:

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, // Number of columns in the sprite sheet
      rows: 4, // Number of rows in the sprite sheet
    );
    _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);

    if (isLeftKeyPressed && isRightKeyPressed) {
      playerDirection = PlayerDirection.none;
    } else if (isUpKeyPressed && isDownKeyPressed) {
      playerDirection = PlayerDirection.none;
    } else if (isLeftKeyPressed) {
      playerDirection = PlayerDirection.left;
      print('left');
    } else if (isRightKeyPressed) {
      playerDirection = PlayerDirection.right;
      print('right');
    } else if (isUpKeyPressed) {
      playerDirection = PlayerDirection.back;
      print('back');
    } else if (isDownKeyPressed) {
      playerDirection = PlayerDirection.front;
      print('front');
    } else {
      playerDirection = PlayerDirection.none;
    }
    return super.onKeyEvent(event, keysPressed);
  }

  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;
  }

  // Load all animations
  void _loadAllAnimations() {

    // Create sprite animations using the sprite sheet
    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
      ),
    };
  }
}

Also, player movement keys are only available when I remove the else statement at the end. I'm sorry if this is a dumb question, I'm still new to Flutter and Flame.

I tried moving the PlayerDirection.none to higher parts of the code and also tried to add a boolean if keyup then PlayerDirection.none and else the movement keys are activated.

2

There are 2 best solutions below

0
On

You can simplify that logic a bit and get it correctly working:

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

  double _speed = 50;
  final Vector2 _direction = Vector2.zero();

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

  @override
  FutureOr<void> onLoad() {
    ...
  }

  @override
  void update(double dt) {
    super.update(dt);
    final displacement = _direction.normalized() * _speed * dt;
    position.add(displacement);
  }

  @override
  bool onKeyEvent(RawKeyEvent event, Set<LogicalKeyboardKey> keysPressed) {
    final isKeyDown = event is RawKeyDownEvent;

    // Avoiding repeat event as we are interested only in
    // key up and key down event.
    if (!event.repeat) {
      if (event.logicalKey == LogicalKeyboardKey.keyA) {
        _direction.x += isKeyDown ? -1 : 1;
      } else if (event.logicalKey == LogicalKeyboardKey.keyD) {
        _direction.x += isKeyDown ? 1 : -1;
      } else if (event.logicalKey == LogicalKeyboardKey.keyW) {
        _direction.y += isKeyDown ? -1 : 1;
      } else if (event.logicalKey == LogicalKeyboardKey.keyS) {
        _direction.y += isKeyDown ? 1 : -1;
      }
    }

    return super.onKeyEvent(event, keysPressed);
  }

  // Load all animations
  ...
}

The code is extracted from this example: https://examples.flame-engine.org/#/Input_Keyboard

0
On

Quick update to Spydon's Answer code snippet this since Flame 1.16.0 was released

!event.repeat looks to have been removed, you just need to access it through the KeyRepeatEvent subclass

  @override
  bool onKeyEvent(KeyEvent event, Set<LogicalKeyboardKey> keysPressed) {
    horizontalMovement = 0;
// Add this to access the repeat bool
    final isKeyRepeat = event is KeyRepeatEvent;
    final isKeyDown = event is KeyDownEvent;

// Avoiding repeat event as we are interested only in
// key up and key down event.
if (!isKeyRepeat) {
  if (event.logicalKey == LogicalKeyboardKey.keyA) {
    _direction.x += isKeyDown ? -1 : 1;
  } else if (event.logicalKey == LogicalKeyboardKey.keyD) {
    _direction.x += isKeyDown ? 1 : -1;
  } else if (event.logicalKey == LogicalKeyboardKey.keyW) {
    _direction.y += isKeyDown ? -1 : 1;
  } else if (event.logicalKey == LogicalKeyboardKey.keyS) {
    _direction.y += isKeyDown ? 1 : -1;
  }
}
return super.onKeyEvent(event, keysPressed);
}