0
\$\begingroup\$

I am using C++ and SFML to make a top-down Zombie shooter. For now I have a player I can move around who can shoot but I am trying to have a basic AI for a Zombie who chases the player based on player position.

For some reason the zombie is moving in a straight direction rather than chasing player. I believe that the problem has to do with incorrect player position being used to calculate the direction of the zombie. But I can't seem to figure out how to fix the problem. Any help will be appreciated. Thanks!

Here is my code so far:

Player.cpp

//GetPosition() is getting player position
//I even tried getting output of player's x and y position in this class and 
//its correctly showing player's position
sf::Vector2f Player::GetPosition()
{
  xPos = playerSprite.getPosition().x;
  yPos = playerSprite.getPosition().y;

  sf::Vector2f position = sf::Vector2f(xPos, yPos);

  //Correctly outputs position
  std::cout << "X: " << position.x << " Y: " << position.y << std::endl;

  return position;
}


void Player::movePlayer(sf::RenderWindow& window)
{
playerPosition = GetPosition();
//std::cout << "X: " << GetPosition().x << " Y: " << GetPosition().y << 
std::endl;
mousePosWindow = sf::Vector2f(sf::Mouse::getPosition(window));
if (mousePosWindow.x < 0.f || mousePosWindow.x > window.getSize().x || 
mousePosWindow.y < 0.f || mousePosWindow.y > window.getSize().y)
{
    "";
}

aimDirection = mousePosWindow - playerPosition;
aimDirNorm = aimDirection / sqrt(pow(aimDirection.x, 2) + 
pow(aimDirection.y, 2));

//Player
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left) || 
sf::Keyboard::isKeyPressed(sf::Keyboard::A))
{
    playerSprite.move(-5.f, 0.f);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right) || 
sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
    playerSprite.move(5.f, 0.f);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up) || 
sf::Keyboard::isKeyPressed(sf::Keyboard::W))
{
    playerSprite.move(0.f, -5.f);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down) || 
sf::Keyboard::isKeyPressed(sf::Keyboard::S))
{
    playerSprite.move(0.f, 5.f);
}

//Rotate player relative to mouse position
const float PI = 3.14159265;

float dx = playerPosition.x - mousePosWindow.x;
float dy = playerPosition.y - mousePosWindow.y;

rotation = (atan2(dy, dx)) * 180 / PI;

playerSprite.setRotation(rotation + 180);
}

Zombie.h

#pragma once
#include <SFML/Graphics.hpp>
#include "Player.h"
class Zombie
{
public:
  Zombie();
  Player player;

  sf::Texture zombieTexture;
  sf::Sprite zombieSprite;

  sf::Vector2f zombiePosition;
  sf::Vector2f playerPosition;
  sf::Vector2f direction;
  sf::Vector2f normalizedDir;

  int xPos;
  int yPos;
  float speed;
  void Move();

};

Zombie.cpp

void Zombie::Move()
{

// Make movement
xPos = zombieSprite.getPosition().x;
yPos = zombieSprite.getPosition().y;

zombiePosition = sf::Vector2f(xPos, yPos);

playerPosition = player.GetPosition();

//Incorrectly outputs player position
//This outputs 0 constantly. But why?
std::cout << "X: " << player.playerPosition.x << " Y: " << 
player.playerPosition.y << std::endl;


direction = playerPosition - zombiePosition;
normalizedDir = direction / sqrt(pow(direction.x, 2) + pow(direction.y, 2));

speed = 2;

//Rotate the Zombie relative to player position
const float PI = 3.14159265;

float dx = zombiePosition.x - playerPosition.x;
float dy = zombiePosition.y - playerPosition.y;

float rotation = (atan2(dy, dx)) * 180 / PI;

zombieSprite.setRotation(rotation + 45);

sf::Vector2f currentSpeed = normalizedDir * speed;

zombieSprite.move(currentSpeed);
}

main.cpp

#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include "Player.h"
#include "Bullet.h"
#include "Zombie.h"
#include <vector>
#include <iostream>

int main()
{
sf::RenderWindow window(sf::VideoMode(1280, 720), "Arcade Zombies : 
Multiplayer");
window.setFramerateLimit(60);
window.setMouseCursorVisible(false); // Hide cursor
sf::View fixed = window.getView(); // Create a fixed view

//Background
sf::Texture backgroundTexture;
if (!backgroundTexture.loadFromFile("resources/background.png"))
{
    std::cout << "Load failed" << std::endl;

    system("pause");
}

sf::Sprite backgroundSprite;
backgroundSprite.setTexture(backgroundTexture);
backgroundSprite.setOrigin(sf::Vector2f(backgroundSprite.getTexture()- 
>getSize().x, backgroundSprite.getTexture()->getSize().y));
backgroundSprite.setPosition(window.getSize().x, window.getSize().y);
backgroundSprite.scale(sf::Vector2f(1.1f, 0.7f)); // absolute scale factor

//Cursor
sf::Texture cursorTexture;
if (!cursorTexture.loadFromFile("resources/gun-pointer.png"))
{
    std::cout << "Load failed" << std::endl;

    system("pause");
}
sf::Sprite cursorSprite(cursorTexture);
cursorSprite.setOrigin(sf::Vector2f(cursorSprite.getTexture()->getSize().x * 
0.5, cursorSprite.getTexture()->getSize().y * 0.5));

//Player
Player player;
player.playerSprite.setPosition(window.getSize().x / 2.f, window.getSize().y 
- 50);

//Zombie
Zombie zombie;
zombie.zombieSprite.setPosition(window.getSize().x / 2.f, window.getSize().y 
/ 2.f);
zombie.zombieSprite.scale(sf::Vector2f(0.3f, 0.3f));

//Bullet
Bullet bullet1;
std::vector<Bullet> bullets;
int shootTimer = 0;

//Sound
sf::SoundBuffer gunBuffer, footstepBuffer;
if (!gunBuffer.loadFromFile("resources/gun_shot_sound.wav"))
{
    std::cout << "Load failed" << std::endl;
}

sf::Sound gunSound;
gunSound.setBuffer(gunBuffer);

while (window.isOpen())
{
    sf::Event event;
    while (window.pollEvent(event))
    {
        if (event.type == sf::Event::Closed)
            window.close();
    }

    //Update
    player.movePlayer(window);

    //Zombie
    zombie.Move();

    //Shoot
    if (shootTimer < 20)
        shootTimer++;

    if (sf::Mouse::isButtonPressed(sf::Mouse::Left) && shootTimer >= 20)
    {
        gunSound.play();
        bullet1.bulletSprite.setOrigin(
        sf::Vector2f(bullet1.bulletSprite.getTexture()->getSize().x * 0.5, 
        bullet1.bulletSprite.getTexture()->getSize().y * 0.5));
        bullet1.bulletSprite.setPosition(player.playerPosition);
        bullet1.currentVelocity = player.aimDirNorm * bullet1.maxSpeed;
        bullet1.bulletSprite.setRotation(player.rotation + 90);
        bullets.push_back(Bullet(bullet1));
        shootTimer = 0;

    }

    for (size_t i = 0; i < bullets.size(); i++)
    {
        bullets[i].bulletSprite.move(bullets[i].currentVelocity);

        if (bullets[i].bulletSprite.getPosition().x < 0 || 
            bullets[i].bulletSprite.getPosition().x > window.getSize().x ||
            bullets[i].bulletSprite.getPosition().y < 0 || 
            bullets[i].bulletSprite.getPosition().y > window.getSize().y)
        {
            bullets.erase(bullets.begin() + i);
        }
    }

    // Set mouse position        
    cursorSprite.setPosition(static_cast<sf::Vector2f> 
    (sf::Mouse::getPosition(window)));

    //Draw
    window.clear();
    window.setView(fixed);
    window.draw(backgroundSprite);
    window.draw(player.playerSprite);
    window.draw(zombie.zombieSprite);
    window.draw(cursorSprite);
    for (size_t i = 0; i < bullets.size(); i++)
    {
        window.draw(bullets[i].bulletSprite);
    }

    window.display();

}

return 0;
}
\$\endgroup\$
1
  • \$\begingroup\$ Btw you forgot parentheses on your divide by length when normalizing. \$\endgroup\$ – Andrew Wilson Oct 23 '18 at 17:30
2
\$\begingroup\$

You create your Player in main.cpp, inside main(). You also set up the player's initial position here, and every time through your main loop you call movePlayer on this player to move it around the world. This is all mostly fine.

Your zombie, however, which you declare in main() just after the player... the zombie has it's own copy of the player, because you declared Player player; in the Zombie class. The zombie uses the zombie's player, which is always at zero because it is a different player than the one in main().

You probably need your zombie to have a pointer to the player it wants to follow, so you should change your Zombie class to look more like:

class Zombie {
  public:
    Zombie();
    Player* player; // Note: now a pointer!
...

And then, in main(), make sure you give that zombie the address of the player to follow:

Zombie zombie;
zombie.player = &player;
...

Note that because you've changed the type of the player inside Zombie to a pointer, you'll need to change how you access it in Zombie: you have to use player-> instead of player. to access methods and fields of the Player object.

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.