Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Space Invaders - Enemy Movement Logic  (Read 5347 times)

0 Members and 1 Guest are viewing this topic.

firepro20

  • Newbie
  • *
  • Posts: 22
    • View Profile
Space Invaders - Enemy Movement Logic
« on: November 17, 2019, 04:22:06 pm »
I am new to SFML and also C++. I would like to create logic for space invaders enemy movement as follows

// pseudo
instantiate enemy
with position, scale, rotation
create nth row of enemies
if enemy reaches max x bound, move enemy down y by value y from current position
until hit player / dies / reaches end of screen

so far I have the following for creating two rows of enemies and rendering on screen -

Quote
void Game::enemyInstantiator() {
   int noOfEnemies = 10;
   sf::Sprite tempEnemy;
   for (int i = 0; i < noOfEnemies; i++) {
      tempEnemy.setTexture(mEnemyTexture);
      tempEnemy.setOrigin(24.f, 24.f);
      tempEnemy.setPosition(100.f + (i * 60.f), 100.f);
      tempEnemy.setRotation(180.f);
      if (i >= noOfEnemies / 2) {
         for (int j = 0; j < i; j++) {
            tempEnemy.setTexture(mEnemyTexture);
            tempEnemy.setOrigin(24.f, 24.f);
            tempEnemy.setPosition(100.f + (j * 60.f), 150.f);
            tempEnemy.setRotation(180.f);
         }
      }
      mEnemies.push_back(tempEnemy);
   }
}

// Render
void Game::render()
{
   // Remember draw order is important
   mWindow.clear();
   mWindow.draw(mBackground);
   mWindow.draw(mPlayer);
   mWindow.draw(mStatisticsText);
   
   for (int i = 0; i < mEnemies.size(); i++)
   {
      sf::Sprite temp = mEnemies.at(i);
      mWindow.draw(temp);
   }

   mWindow.display();
}

I am following code from SFML Game Development 2013

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10800
    • View Profile
    • development blog
    • Email
Re: Space Invaders - Enemy Movement Logic
« Reply #1 on: November 17, 2019, 08:04:08 pm »
Is there a question to this thread or are you simply showing your progress so far?
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Tigre Pablito

  • Full Member
  • ***
  • Posts: 225
    • View Profile
    • Email
Re: Space Invaders - Enemy Movement Logic
« Reply #2 on: November 18, 2019, 01:18:39 am »
Hi firepro20

Basically it depends on how you want your enemies to move. They should have X and Y variables to set where they are, Sense (1 if going right, -1 left), Health, Points by destruction, some object list for weapon, maybe SenseY (for up/down), etc. If I understood well the movement you described, it would be like

//inside your enemy update code
X += 3f * Sense; // goes from side to side through the screen
if (X >= SCREEN_WIDTH - 24 || X < 0 + 24)  // you could use XRadius instead of 24
    Sense = -Sense;  // has reached bound, then goes oposite
Y += 1f; // just goes down screen

Then you would have to check if player (space ship) shoots have touched the enemy, if it collides with player, or if it goes off side and then disappears, etc ...

You can also make curve movements, for which you would have to use, generally, sine and cosine functions, and sometimes also arc tangent

Continue with it, your graphics are very good
And please come back here if you need any more help
Pablo


firepro20

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: Space Invaders - Enemy Movement Logic
« Reply #3 on: November 18, 2019, 06:52:10 pm »
To answer eXpl0it3r's question, it is asking for opinions on how I should go about it. I might need to create an enemy class perhaps as well, for readability and code best practice.

I have managed to get  the effect I wanted using the following code for gam devs who might be reading this -

Quote
// Creates two rows of enemies: takes even noOfEnemies, start xPosition and yPosition
void Game::enemyInstantiator(int noOfEnemies, float xPosition, float yPosition) {
   //int noOfEnemies = 10;
   sf::Sprite tempEnemy;
   int tracker = 0;

   for (int i = 0; i < noOfEnemies; i++) {
      tempEnemy.setTexture(mEnemyTexture);
      tempEnemy.setOrigin(24.f, 24.f);
      tempEnemy.setPosition(xPosition + (i * 60.f), yPosition);
      tempEnemy.setRotation(180.f);
      if (i >= noOfEnemies / 2) {
         // set position for remainder, problem is that every loop is overiding position and drawing last batch of 5
         for (int j = 0; j < noOfEnemies/2; j++) {
            tempEnemy.setTexture(mEnemyTexture);
            tempEnemy.setOrigin(24.f, 24.f);
            tempEnemy.setPosition(xPosition + (j * 60.f), yPosition + 50.f);
            tempEnemy.setRotation(180.f);
            cout << "Enemy inside 2nd for loop position" << tempEnemy.getPosition().x << " " << tempEnemy.getPosition().y << endl;
            // we were not pushing each and every tempEnemy but only the last one when exiting the for loop, therefore we were only printing the last one
            mEnemies.push_back(tempEnemy);
            tracker++;
            if (tracker >= 5) {
               return;
            }
         }
      }
      cout << "Enemy inside 1st for loop position" << tempEnemy.getPosition().x << " " << tempEnemy.getPosition().y << endl;
      mEnemies.push_back(tempEnemy);
   }
   
}

I am currently going through Tigre Pablito's message as I'm writing this, I will answer soon. Thank you all for your replies!

firepro20

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: Space Invaders - Enemy Movement Logic
« Reply #4 on: November 19, 2019, 10:18:49 pm »
I have managed to migrate functionality to a class, and now I am trying to move each space ship on screen. I am successful in doing so however I have noticed an issue with current code - spaceships go down quite quickly, however I have an idea of how to arrange that. However, the major issue is that once the two rows of spaceships reach edge of screen, only the 5th spaceship in the second row goes down, the others are stuck.

Any ideas?

Code -

Quote
void Enemy::enemyInstantiator(int noOfEnemies, float xPosition, float yPosition) {
   sf::Sprite tempEnemy;
   int tracker = 0;
   float yOffset = 50.f;
   float xOffset = 60.f;

   for (int i = 0; i < noOfEnemies; i++) {
      tempEnemy.setTexture(mEnemyTexture);
      tempEnemy.setOrigin(24.f, 24.f);
      tempEnemy.setPosition(xPosition + (i * xOffset), yPosition);
      tempEnemy.setRotation(180.f);
      if (i >= noOfEnemies / 2) {
         // set position for remainder, problem is that every loop is overiding position and drawing last batch of 5
         for (int j = 0; j < noOfEnemies / 2; j++) {
            tempEnemy.setTexture(mEnemyTexture);
            tempEnemy.setOrigin(24.f, 24.f);
            tempEnemy.setPosition(xPosition + (j * xOffset), yPosition + yOffset);
            tempEnemy.setRotation(180.f);
            std::cout << "Enemy inside 2nd for loop position " << tempEnemy.getPosition().x << " " << tempEnemy.getPosition().y << std::endl;
            // we were not pushing each and every tempEnemy but only the last one when exiting the for loop, therefore we were only printing the last one
            mEnemies.push_back(tempEnemy);
            tracker++;
            if (tracker >= noOfEnemies/2) {
               return;
            }
         }
      }
      std::cout << "Enemy inside 1st for loop position " << tempEnemy.getPosition().x << " " << tempEnemy.getPosition().y << std::endl;
      mEnemies.push_back(tempEnemy);
   }
}

Quote
void Enemy::enemyBehaviour(std::vector<sf::Sprite>& enemyList) {
   float direction = 1.f;
   //sf::Sprite tempEnemy;
   sf::Vector2f enemyMovement(1.f, 0.f);
   float yOffset = 50.f;
   float xOffset = 60.f;
   for (sf::Sprite& enemy : enemyList) // CAUTION & solved everything again - lesson in C++, if we don't get address, we always create a copy and modify it, which is not what we want
   {

      if (enemy.getPosition().x + enemy.getLocalBounds().width / 2 > 640.f) { // can be improved by calling getWidth method
         direction = -direction;
         enemy.setPosition(enemy.getPosition().x, enemy.getPosition().y + yOffset); // y axis is inverted in SFML
      }
      if (enemy.getPosition().x - enemy.getLocalBounds().width / 2 < 0.f) {
         direction = -direction;
         enemy.setPosition(enemy.getPosition().x, enemy.getPosition().y + yOffset);
      }
      enemy.move(enemyMovement * direction);
      std::cout << enemy.getPosition().x << " " << enemy.getPosition().y << std::endl;
      /*
      std::cout << "Temp Enemy has " << enemy.getPosition().x << " " << enemy.getPosition().y << std::endl;
      enemy.move(10.f, 0.f);
      std::cout << "Temp Enemy has " << enemy.getPosition().x << " " << enemy.getPosition().y << std::endl;
      */
   }
}

Quote
void Enemy::render(sf::RenderWindow& window) {
   for (int i = 0; i < mEnemies.size(); i++)
   {
      sf::Sprite temp = mEnemies.at(i);
      window.draw(temp);
   }
}

Also how would I use radius to check bounds instead of 24?

Picture x and y coordinates in debug for four spaceships

firepro20

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: Space Invaders - Enemy Movement Logic
« Reply #5 on: November 20, 2019, 02:00:30 am »
Update EnemyBehaviour and found the issue, not sure why this is happening on screen bounds, added comments

Quote
void Enemy::enemyBehaviour(std::vector<sf::Sprite>& enemyList) {
   float direction = 1.f;
   //sf::Sprite tempEnemy;
   sf::Vector2f enemyMovement(1.f, 0.f);
   float yOffset = 50.f;
   float xOffset = 60.f;
   for (sf::Sprite& enemy : enemyList) // CAUTION & solved everything again - lesson in C++, if we don't get address, we always create a copy and modify it, which is not what we want
   {
      std::cout << "Direction before working move " << direction << std::endl;
      enemy.move(enemyMovement * direction); // Issue detected direction only changing sign if greater or less than screen bounds once
      if (enemy.getPosition().x + enemy.getLocalBounds().width / 2 >= 640.f ||
         enemy.getPosition().x - enemy.getLocalBounds().width / 2 <= 0.f) { // can be improved by calling getWidth method
         direction = -(direction);
         enemy.setPosition(enemy.getPosition().x, enemy.getPosition().y + yOffset);
         std::cout << "Direction inside if statement " << direction << std::endl;
         //enemy.setPosition(enemy.getPosition().x, enemy.getPosition().y + yOffset); // y axis is inverted in SFML
         return;
      }
      
      /*
      if (enemy.getPosition().x - enemy.getLocalBounds().width / 2 <= 0.f) {
         direction = -direction;
         //enemy.setPosition(enemy.getPosition().x, enemy.getPosition().y + yOffset);
      }
      */
      //std::cout << "Direction now has value of " << direction << std::endl;
      std::cout << enemy.getPosition().x << " " << enemy.getPosition().y << std::endl;
      /*
      std::cout << "Temp Enemy has " << enemy.getPosition().x << " " << enemy.getPosition().y << std::endl;
      enemy.move(10.f, 0.f);
      std::cout << "Temp Enemy has " << enemy.getPosition().x << " " << enemy.getPosition().y << std::endl;
      */
   }
   /*
   for (auto it = enemyList.begin(); it != enemyList.end();)
   {
      //enemyMovement.x += enemySpeed;
      tempEnemy = *it;
      cout << "Temp Enemy has for iterator " << tempEnemy.getPosition().x << " " << tempEnemy.getPosition().y << endl;

      /*
      if (tempEnemy.getPosition().x + tempEnemy.getLocalBounds().width/2 > windowWidth) {
         direction = -direction;
         tempEnemy.setPosition(tempEnemy.getPosition().x, tempEnemy.getPosition().y + yOffset); // y axis is inverted in SFML
      }
      if (tempEnemy.getPosition().x - tempEnemy.getLocalBounds().width / 2 < 0.f) {
         direction = -direction;
         tempEnemy.setPosition(tempEnemy.getPosition().x, tempEnemy.getPosition().y + yOffset);
      }
      tempEnemy.move(enemyMovement * direction);

   }
   */
   //enemyMovement.x += enemySpeed;
   // Pseudo
   /*
   if we are at end of move rotation, change direction (-/+) from current position
   and increment position by move.
   */
}

firepro20

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: Space Invaders - Enemy Movement Logic
« Reply #6 on: November 20, 2019, 02:04:47 am »
Figured out the problem, float direction was being instantiated every time enemyBehaviour was called.

This meant that on every update frame, it was being reset to 1, so switching between -1 and 1 once it reaches the edge of the screen border.

I will keep you posted on development of enemy behaviour.. I think next step would be to have a constructor to manage enemies as entitys and not just sprites!

Tigre Pablito

  • Full Member
  • ***
  • Posts: 225
    • View Profile
    • Email
Re: Space Invaders - Enemy Movement Logic
« Reply #7 on: November 21, 2019, 12:46:19 am »
Hi firepro20

I called, in my "Massacre Space Shooter", XRadius and YRadius to half the width and height, respectively, of any ship. So then you can check collision, reaching the bounds, and also if bullets have touched a ship. As I saw in your code, your enemy's XRadius would be 24.

Yes! I'm sure it would be a good practise to make a class for each different enemy, and then a "collection" class for groups of them. Also you can write an abstract class to put there all enemies' common attributes and methods and then make all enemies inherit from it.

firepro20

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: Space Invaders - Enemy Movement Logic
« Reply #8 on: November 21, 2019, 02:46:44 pm »
Hi Tigre,

Tried to do that today however I am facing this issue, it is more C++ related I believe -

Access Violation Exception thrown in enemy constructor

https://en.sfml-dev.org/forums/index.php?topic=26729.0

What can I provide to assist in identifying the issue, I'm new to C++ so I have no idea where to begin troubleshooting this

Tigre Pablito

  • Full Member
  • ***
  • Posts: 225
    • View Profile
    • Email
Re: Space Invaders - Enemy Movement Logic
« Reply #9 on: November 21, 2019, 05:57:16 pm »
Sorry, I'm a C# programmer, I know very little C++

Maybe if I saw the entire code (and if I had the C++ compiler) I might find out ... but surely any C++ programmer here will tell you what is wrong

But check the array, or list, or vector's size ... did you allocate it for containing NUMBER_OF_ALIENS?



firepro20

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: Space Invaders - Enemy Movement Logic
« Reply #10 on: November 21, 2019, 05:59:52 pm »
I have restructured the code, I will try and use vectors instead of arrays