RafaelBarbosatec / bonfire

(RPG maker) Create RPG-style or similar games more simply with Flame.
https://bonfire-engine.github.io
MIT License
1.24k stars 189 forks source link

[Bug]: after moveToPosition(position), ally is spinning #449

Closed MikaReesu closed 1 year ago

MikaReesu commented 1 year ago

What happened?

Hello I have a simple ally that is a guard with AutomaticRandomMovement.

'seeAndMoveToEnemy( closeEnemy: (p0) => meleeAttack(), radiusVision: GlobalConstants.TILE_SIZE * 8, runOnlyVisibleInScreen: true, margin: GlobalConstants.TILE_SIZE / 3, visionAngle: 180, notObserved: () { if (position.distanceTo(initialPosition) > tolerance) { moveToPosition(initialPosition); } else { stopMove(); } });'

I added a code to see and move to the enemy and I added a not observed with a stopMove or a move to initial position. So the position in which the guard was loaded first. moveToPosition(initialPosition);

What is happening is that, the guard detect the enemy, goes to it. Once the enemy is killed, he goes back to his initial position but that's where the bug starts.

The guard starts to run on the spot, so he is not moving from his position and the animation left and right are played non stop and very fast, he could almost fly away.

I tried so many things, to put him back in idle, to stop the move, but it doesn't matter and there is still the same issue at the end. Thank you very much.

Do you have any idea? @Rafael Almeida

Steps to reproduce?

Use moveToPosition(initialPosition), and the ally is gonna start spinning left and right.

What did you expect to happen?

I expected the Ally to reach his position and stay idle, before he could see another enemy to chase.

Bonfire version

3.0.0

Relevant log output

No response

RafaelBarbosatec commented 1 year ago

Hi @MikaReesu ! Using the version 3.0.2 I got it like this:

import 'package:bonfire/bonfire.dart';
import 'package:example/shared/util/person_sprite_sheet.dart';

enum EnemyState {
  guarding,
  backing,
}

class MeleeEnemy extends SimpleEnemy {
  late Vector2 initialPOsition;
  EnemyState state = EnemyState.guarding;
  double distanceTolerance = 0;
  MeleeEnemy({required Vector2 position})
      : super(
          position: position,
          animation: PersonSpritesheet(path: 'orc2.png').simpleAnimation(),
          size: Vector2.all(24),
          speed: 25,
          initDirection: Direction.down,
        ) {
    initialPOsition = center.clone();
    distanceTolerance = width;
  }

  @override
  void update(double dt) {
    switch (state) {
      case EnemyState.guarding:
        if (position.distanceTo(initialPOsition) > distanceTolerance) {
          state = EnemyState.backing;
        } else {
          seeAndMoveToPlayer(closePlayer: (p) {
            if (checkInterval('attack', 600, dt)) {
              _playAttackAnimation();
            }
          });
        }
        break;
      case EnemyState.backing:
        if (center.distanceTo(initialPOsition) < 2) {
          state = EnemyState.guarding;
          lastDirection = Direction.down;
          stopMove();
        } else {
          moveToPosition(initialPOsition);
        }
        break;
    }
    super.update(dt);
  }

  @override
  Future<void> onLoad() {
    /// Adds rectangle collision
    add(RectangleHitbox(size: size / 2, position: size / 4));
    return super.onLoad();
  }

  void _playAttackAnimation() {
    switch (lastDirection) {
      case Direction.left:
        animation?.playOnceOther(PersonAttackEnum.meeleLeft);
        break;
      case Direction.right:
        animation?.playOnceOther(PersonAttackEnum.meeleRight);
        break;
      case Direction.up:
        animation?.playOnceOther(PersonAttackEnum.meeleUp);
        break;
      case Direction.down:
        animation?.playOnceOther(PersonAttackEnum.meeleDown);
        break;
      case Direction.upLeft:
        animation?.playOnceOther(PersonAttackEnum.meeleUpLeft);
        break;
      case Direction.upRight:
        animation?.playOnceOther(PersonAttackEnum.meeleUpRight);
        break;
      case Direction.downLeft:
        animation?.playOnceOther(PersonAttackEnum.meeleDownLeft);
        break;
      case Direction.downRight:
        animation?.playOnceOther(PersonAttackEnum.meeleDownRight);
        break;
    }
  }
}
MikaReesu commented 1 year ago

Why is notObserved now a boolean? I am in the Ally, and I want it as a guard to attack enemies.

Could you please show me how to use it?

The idea was to have the ally here my guard, defending against enemies. Once enemy bitten, the guards would come back to their initial positions.

RafaelBarbosatec commented 1 year ago

If you return true it's stop movement of your component. If return false not. So, when you need do some custom movement when not observe it's necessary return false to not have strange behavior.

RafaelBarbosatec commented 1 year ago

Humm. I understood. You can recycle this implementation. When I got a time I will try do a example about this type of guardian.

MikaReesu commented 1 year ago

It is much better now, I tried to implement your code and it's working well. Only thing is to master a bit more the distanceTolerance, the concept is interesting, but, tricky at the same time.

I would like the guard to be able to go a bit further, it's just that, I want to avoid the back and forth, back and forth movement, that I see at the moment.

When I increase distanceTolerance and if (center.distanceTo(initialPosition) < 2) {, what is triggered is the stopMove(), and instead of going back to their initial position they stay static and waiting for the next enemy to appear.

I don't know if you see what I mean, but normally if put let's distanceTolerance = width * 2 and if (center.distanceTo(initialPosition) < 4) {, it's gonna be visible directly.

Hope I was clear, and thank you very much for your help :)

MikaReesu commented 1 year ago

I also noticed that you were using a playOnceOther, what is the difference between the playOnceOther and the playOnce ?

RafaelBarbosatec commented 1 year ago

The playOnceOther is used to play once time the registered animation in the SimpleDirectionAnimation in him there is a param named others to register other animation beyond the animation of directions.

MikaReesu commented 1 year ago

So is it something that allows you to play one animation after another? Not sure that I understand the difference honestly.

RafaelBarbosatec commented 1 year ago

So both do the something but playOnce expected a SpriteAnimation and playOnceOther expected only key to play a animation registered previously in param others of SimpleDirectionAnimation like this:

var animation = SimpleDirectionAnimation(
  idleRight: SpriteAnimation(),
 runRight: SpriteAnimation(),
 others:{
  'attackLeft': SpriteAnimation(),
 }
);

animation.playOnceOther('attackLeft'');
MikaReesu commented 1 year ago

I see, thank you for your help!