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

Author Topic: Trying to create a streamer / viewer SpaceInvaders, bug on game time help!  (Read 944 times)

0 Members and 1 Guest are viewing this topic.

firepro20

  • Newbie
  • *
  • Posts: 22
    • View Profile
I am trying to create the possibility to play the game and for the same solution/app to be able to connect to a player to watch them play.

I will be using TCP to establish connection and UDP to send updates on the following -

1. Player Position
2. Enemy Position (based on synchronous game time since pattern is predictable)
3. Bullet Position

At the moment I have the following code, if server you have full control and can play the game, if viewer/client so far you can only see, still need to implement netcode.

Quote
int main()
{
   sf::Texture            mPlayerTexture;
   sf::Sprite            mPlayer;
   sf::Texture            mBackgroundTexture;
   sf::Sprite            mBackground;
   
   Bullet bullet(0, bulletSpeed);

   sf::RenderWindow window(sf::VideoMode(game->getWindowWidth(), game->getWindowHeight()), "SFML Application");
   //static const sf::Time   TimePerFrame;
   window.setVerticalSyncEnabled(true);
   //window.setFramerateLimit(60); // Added this

   if (!mPlayerTexture.loadFromFile("Media/Textures/PixelSpaceships/blue_01.png"))
   {
      // Handle loading error
   }
   if (!mBackgroundTexture.loadFromFile("Media/Textures/Space.jpg")) {
      // Handle loading error
   }
   
   // Set Texture and Position for Background
   mBackground.setTexture(mBackgroundTexture);
   mBackground.setOrigin(game->getWindowWidth() / 2.f, game->getWindowHeight() / 2.f);
   mBackground.setPosition(game->getWindowWidth() / 2.f, game->getWindowHeight() / 2.f);

   // Set Texture and Position for Player
   mPlayer.setTexture(mPlayerTexture);
   mPlayer.setOrigin(24.f, 24.f);
   mPlayer.setPosition(game->getWindowWidth() / 2.f, game->getWindowHeight() / 2.f);

   //enemy.enemyInstantiator(2, 100.f, 50.f);
   //enemyInstantiator(10, 100.f, 150.f);

   // Statistics Initialisation
   sf::Font            mFont;
   sf::Text            mStatisticsText;
   mFont.loadFromFile("Media/Sansation.ttf");
   mStatisticsText.setFont(mFont);
   mStatisticsText.setPosition(5.f, 5.f);
   mStatisticsText.setCharacterSize(10);

   // Enemy Creation // Separate method in main
      
   std::vector<Enemy> *enemyList = new std::vector<Enemy>();
   float xPosition = 100.f;
   float yPosition = 50.f;
   float yOffset = 50.f;
   float xOffset = 60.f;
   
   // New Original Instantiator
      
   for (int i = 0; i < NUMBER_OF_ALIENS; i++) {
      Enemy alien(i, enemySpeed);
      alien.getSprite().setOrigin(24.f, 24.f);
      alien.setLocation(xPosition + (i * xOffset), yPosition);
      alien.getSprite().setRotation(180.f);
      enemyList->push_back(alien);

      //std::cout << "I have " << enemyList->size() << " spaceships" << std::endl;
   }
   //std::cout << "I have " << enemyList->size() << " spaceships";
   
   // Enemy creation with rows
   /*
   int tracker = 0;

   for (int i = 0; i < NUMBER_OF_ALIENS; i++) {
      Enemy alien(i, enemySpeed);
      alien.getSprite().setOrigin(24.f, 24.f);
      alien.getSprite().setPosition(xPosition + (i * xOffset), yPosition);
      alien.getSprite().setRotation(180.f);
      if (i >= NUMBER_OF_ALIENS / 2) {
         // set position for remainder, problem is that every loop is overiding position and drawing last batch of 5
         for (int j = 0; j < NUMBER_OF_ALIENS / 2; j++) {
            
            alien.getSprite().setOrigin(24.f, 24.f);
            alien.getSprite().setPosition(xPosition + (j * xOffset), yPosition + yOffset);
            alien.getSprite().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
            enemyList->push_back(alien);
            tracker++;
            if (tracker >= NUMBER_OF_ALIENS / 2) {
               break;
            }
         }
      }
      //std::cout << "Enemy inside 1st for loop position " << tempEnemy.getPosition().x << " " << tempEnemy.getPosition().y << std::endl;
      enemyList->push_back(alien);
   }
   */

   /*
   //create a an array of enemys
   Enemy alienArray = new Enemy[];
   for (int i = 0; i < NUMBER_OF_ALIENS; i++)
   {
      Enemy alien(i, enemySpeed);

      alien.setLocation(i * 50.f + 50.f, alien.getSprite().getGlobalBounds().height / 2);
      alienArray;
   }
   */

   //game->run();

   // Measure elapsed time
   sf::Clock clock;
   sf::Time timeSinceLastUpdate = sf::Time::Zero;
   // Measure time to reach end of screen
   sf::Clock gameTime;
   sf::Time timeToHitEdge;
   char userChoice = NULL;

   // something here or within the window is open needs to happen to select whether to view or play
   // menu needs to happen over here or within

   while (window.isOpen())
   {
      
      if (userChoice == NULL){
         do {

            std::cout << "User Choice - " << userChoice << std::endl;
            std::cout << "Server or client? (s/c)" << std::endl;
            std::cin >> userChoice;
            
         } while (userChoice != 's' && userChoice != 'c');
         //system("pause");
      }
      if (userChoice == 's' || userChoice == 'c') { // start of main if  // else start the whole program
                  
         // Puts the time counter back to zero and also returns the time elapsed since the clock was started
         sf::Time deltaTime = clock.restart();
         timeSinceLastUpdate += deltaTime;
         while (timeSinceLastUpdate > TimePerFrame) // fixed time steps // userchoice != NULL otherwise it will start immeadiately
         {
            timeSinceLastUpdate -= TimePerFrame; // solves problem with variable delta time, each frame is unique

            processEvents(window);
            //update(TimePerFrame);

            // Start of if server
            if (userChoice == 's'){

               // Player 1 movement
               sf::Vector2f movement(0.f, 0.f);
               if (mIsMovingUp)
                  movement.y -= playerSpeed;
               if (mIsMovingDown)
                  movement.y += playerSpeed;
               if (mIsMovingLeft)
                  movement.x -= playerSpeed;
               if (mIsMovingRight)
                  movement.x += playerSpeed;
               // shooting logic
               if (mIsShooting) {
                  if (!bullet.isAlive()) // && !gameOver
                  {
                     bullet.spawn(true);
                     bullet.setLocation(mPlayer.getPosition().x - 13.f, mPlayer.getPosition().y - 24.f); // mPlayer.getPosition().x + 24 (offset) if origin is not set to center
                  }
               }
         
               // ** These two parts need to be put in an if statement, if server, direct movement, if client, move needs to happen with data received
               // from server (Server - main player streamer; Client - viewer)

               // Possibly this will be the data that needs to be sent to server
               mPlayer.move(movement * TimePerFrame.asSeconds()); // Time.deltaTime frame independent movement

               // for each player eventually
               screenBound(mPlayer);
               // screenbound enemies or else check that they do not go off bounds and change direction

               // **

            } // end of if server
            else {
               // Handle control through incoming network data
               // vector x = datacoming in
               // Hypothetically mPlayer.move(data coming in);

               // bullet spawn
               // setlocation from data packet
            }

            // Enemy Behaviour // Ideally move to separate method

            sf::Vector2f enemyMovement(1.f, 0.f);
            float yOffset = 30.f;
            float xOffset = 60.f;
            // Be careful with iterators as when deleting it will automatically jump to the next item in vector
            for (std::vector<Enemy>::iterator i = enemyList->begin(); i != enemyList->end(); ++i) // 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;
               i->getSprite().move(enemyMovement * direction); // Issue detected direction only changing sign if greater or less than screen bounds once
               if (i->getSprite().getPosition().x + i->getSprite().getLocalBounds().width / 2 > game->getWindowWidth() ||
                  i->getSprite().getPosition().x - i->getSprite().getLocalBounds().width / 2 < game->getWindowWidth() - game->getWindowWidth()) { // this will be 0
                  direction = -(direction);
                  // Time stamp over here for network calculations
                  //std::cout << "Direction inside if statement " << direction << std::endl;
                  // another for loop move all down by yOffset
                  for (std::vector<Enemy>::iterator i = enemyList->begin(); i != enemyList->end(); ++i) {
                     i->getSprite().setPosition(i->getSprite().getPosition().x, i->getSprite().getPosition().y + yOffset);
                     //enemy.move(0.f, enemy.getPosition().y + yOffset);
                  }
                  //enemy.setPosition(enemy.getPosition().x, enemy.getPosition().y + yOffset); // y axis is inverted in SFML
                  //return 0; // move to separate method
               }

               // This takes care of everything on both sides
               if ((i->getSprite().getPosition().y > game->getWindowHeight() - mPlayer.getLocalBounds().height)) {
                  //removeEnemy(enemy);
                  // ALIVE is false, then draw based on whether alien is alive or not
                  std::cout << "Everyone should be dead game over!" << std::endl;
                  return 0; // change state to paused
               }
            }
         }

         updateStatistics(deltaTime, mStatisticsText);
            
         // ** Start of if server
         if (userChoice == 's'){
            for (std::vector<Enemy>::iterator i = enemyList->begin(); i != enemyList->end(); ++i) {
         
               // Test player and alien collision
               if (CollisionManager::collidesWith(mPlayer, *i) && i->isAlive()) { // *i it points to the enemy, and will give me an enemy
                  std::cout << "You're dead!" << std::endl;
                  mPlayerIsAlive = false;
                  return 0; //-> I'm dead gameOver = true;
               }

               // Test collision between bullets and aliens
               if (CollisionManager::collidesWith(bullet, *i) && i->isAlive())
               {
                  i->kill();
                  //enemyList->erase(i); // issue here, we are trying to do this in order to get rid of rendering problems .. shit just move it out of way
                  //i->setLocation(700.f, 10.f);
                  bullet.kill();
                  bullet.setLocation(700.f, 0.f); // temporary solution for dealing with not actually killing but stop drawing bullet
                  // without above line, the bullet will not be drawn but still exist on screen
               }
         
               /*
               v.erase(std::remove_if(v.begin(), v.end(),
                  [](int i) { return i < 10; }), v.end());
               */
            }
         }
         // ** end of if server

         int deadEnemies = 0;
         for (std::vector<Enemy>::iterator i = enemyList->begin(); i != enemyList->end(); ++i) {
         
            if (!(i->isAlive())) {
               deadEnemies++;
            }
            if (deadEnemies >= NUMBER_OF_ALIENS) {
               std::cout << "You win!" << std::endl;
               return 0; // Set state win
            }
         
         }
         std::cout << "Total Dead Enemies - " << deadEnemies << std::endl;

         //for (Enemy& enemy : enemyList) {
         //   if (enemy.isAlive()) {
         //      enemy.draw(window);
         //   }
         //}

         //enemy.render(alienArray, mWindow);
         /*
         for (int i = 0; i < mEnemies.size(); i++)
         {
            sf::Sprite temp = mEnemies.at(i);
            mWindow.draw(temp);
         }
         */

         // Render All
         // Remember draw order is important
         window.clear();
         window.draw(mBackground);
         window.draw(mPlayer);
         window.draw(mStatisticsText);


         for (std::vector<Enemy>::iterator i = enemyList->begin(); i != enemyList->end(); ++i) {
            if (i->isAlive()) {
               i->draw(window);
               std::cout << "Enemy " << i->getID() << " position - " << i->getLocation().x << " " << i->getLocation().y << std::endl;
            }
         }

         if (bullet.isAlive()) // && !gameOver // for game state and win / lose screen representation
         {
            //draw bullet
            bullet.draw(window);
            //move bullet
            bullet.getSprite().move(0.f, -20);
         }

         //test collision with bullet and boundary
         if (bullet.getSprite().getPosition().y < 0)
            bullet.kill();

         window.display();
      } // end of main second if
   }

   return 0;
}

The issue I have is that if user takes x seconds to select an option, the game in the background would have already started and enemy movement would have been running in the background.

This creates an issue were once the player/viewer goes to the main game, the enemies are shown in the current position in time instead of the start position, i.e. when the game starts enemies should be at the top.

I know that the issue is related to timeSinceLastUpdate > TimePerFrame, as probably this will be far greater when actually starting the game.

In the screenshot I left the game running for 10 seconds before selecting an option. Enemies should be at the very top on start, however at the moment this is not the case.

firepro20

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: Trying to create a streamer / viewer SpaceInvaders, bug on game time help!
« Reply #1 on: November 24, 2019, 04:16:31 pm »
Solved the issue by continously resetting the clock until a user chooses an option -

Quote
if (userChoice == NULL){
         do {

            std::cout << "User Choice - " << userChoice << std::endl;
            std::cout << "Server or client? (s/c)" << std::endl;
            std::cin >> userChoice;
            
         } while (userChoice != 's' && userChoice != 'c');
         // The game clock will keep on restarting until the user makes a decision
         clock.restart(); // This solved issue to start movements and updates at same time once user chooses an option
      }