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

131 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
spydon 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
Airamentis 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);
}