the only other code used in this from anywhere else is Player::getPosition(), which is just returning playerSprite.getPosition() and Game::update which passes Player::getPosition() to this function
sf::Vector2f enemyLocation{0.f, 0.f};
starting value of enemyLocation for debugging
///<summary>
/// gets the amount enemy should move
/// called from Game.update()
///</summary>
/// <param name="t_playerPos"> player window position </param>
/// <returns> enemy move amount per frame (normalised) </returns>
sf::Vector2f Enemy::attemptMove(sf::Vector2f t_playerPos)
{
sf::Vector2f movement = { 0.f, 0.f }; // movement towards player each frame
sf::Vector2f direction = t_playerPos - enemyLocation; // delta playerPos and enemyPos
float angle = atan2f(direction.y, direction.x); // angle to player (rad)
angle = angle * 180.f / M_PI; // convert angle to degrees
float hyp = 1.f; // length of line to player (used for normalisation of vector)
// check if enemy is horizontally in line with player
if (direction.x == 0.f) {
if (direction.y > 0.f) { movement.y = hyp; } // move straight down
else { movement = -hyp; } // move straight up
}
// check if enemy is vertically in line with player
else if (direction.y == 0.f) {
if (direction.x > 0.f) { movement.x = hyp; } // move right
else { movement.x = -hyp; } // move left
}
// if enemy is not in line with player
else {
// ratio of sides y:x = opp:adj
movement.y = sinf(angle);
movement.x = cosf(angle);
// normalising the vector
hyp = sqrtf(abs(movement.x * movement.x) + abs(movement.y * movement.y));
hyp = abs(1.f / hyp); // inverse of pythagoras theorem hypothenuse
movement.x = hyp * movement.x;
movement.y = hyp * movement.y; // sqrt(x^2 + y^2) should equal 1
move(movement);
}
return movement; // return to Game::update() (not currently assigned to anything there)
}
///<summary>
/// moves the enemy by the amount it should each frame
/// in final code will be called from Game.update()
/// for now only called from Enemy::attemptMove()
///</summary>
///<param name="t_movement"> amount to move each frame towards player </param>
void Enemy::move(sf::Vector2f t_movement)
{
enemyLocation += t_movement; // update enemy location
enemySprite.setPosition(enemyLocation); // set enemy position to updated location
}
my includes are
#include <SFML/Graphics.hpp>
#include <iostream>
#define _USE_MATH_DEFINES
#include <math.h>
I expect the enemy to move in a straight line to the player, which does happen if the player is stationary If the player moves diagonally away from the enemy, this also works, but if the player moves any other direction, the enemy spins in a circle (sometimes spiral) until the player stops moving or starts moving diagonally away from the enemy, or sometimes moves in a straight line in a completely wrong direction. I have tried:
- assigning attemptMove() to a variable in Game::update and passing that to Enemy::move()
- including an alpha and beta angle and calculating the ratios of the angle by Sine rule
- including breakpoints to verify the angle is correct (it always seems to be)
- removing the checks for "in line" with player and always running the calculation
- changing if (movement.x/y == 0.f) to if (abs(movement.x/y) < 0.01f) and < epsilon
- changing where the functions are called from and what args are passed to them
- a few other changes, none of which seemed to have any promise
- rewriting large sections of the code outside of the block shown, which naturally had no effect
- cleaning up my code, (this is actually even cleaner than the one in my actual project, though I did run it to make sure it definitely didn't work before posting it)
nothing seems to have any effect, and a lot of things make the problem worse or more severe, I really don't know what else there is to do
EDIT: Below is working code
void Enemy::attemptMove(sf::Vector2f t_playerPos)
{
sf::Vector2f direction = t_playerPos - enemyLocation; // playerPos-enemyPos (direction to player)
float hyp;
// normalising the vector
hyp = sqrtf(abs(direction.x * direction.x) + abs(direction.y * direction.y));
hyp = abs(1.f / hyp); // inverse of pythagoras theorem hypothenuse
direction.x = hyp * direction.x;
direction.y = hyp * direction.y; // sqrt(x^2 + y^2) should equal 1
move(direction);
}
void Enemy::move(sf::Vector2f t_movement)
{
enemyLocation += t_movement; // update enemy location
enemySprite.setPosition(enemyLocation); // set enemy position to updated location
}
Thank you to user @trojanfoe again, who said "You are overthinking the issue. To move
AtowardsBat a given speed you need to do nothing more thanauto dir = normalize(B - A); A += dir * (speed * deltaTime);"Below is my code modified to incorporate this much simpler solution (this code already has a deltaTime function elsewhere which fixes the intervals between frame rendering.)