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

Author Topic: Multiple bullets with isKeyPressed  (Read 4662 times)

0 Members and 1 Guest are viewing this topic.

coltranenaima

  • Newbie
  • *
  • Posts: 11
    • View Profile
Multiple bullets with isKeyPressed
« on: February 13, 2017, 12:41:56 am »
The bolded section is the area of concern. I am having a hard time wrapping my head around making a great bullet system that is easy to work with and manipulate when needed.

bullet_sprites is a vector of sprites, a new bullet sprite is pushed back into the vector when spacebar is pressed. I originally didn't have "&& bullet_sprites.size() == 0" in the first if statement, but it would create a stream of bullets and cause performance issues (see attached pic).

The bullet is erased when it is off screen and the issue I am having now is for one space bar keypress, two bullets are fired. When i say two, I mean one is fired and moved off screen and then a consecutive bullet is fired and moves off screen with only one keypress.

Thanks for any help, i can provide more code as well (like the header files being used). I watched youtube tutorials and even made a stripped down basic project that is having the same exact issue.

   while (main_window.isOpen())
   {      
      float delta = delta_clock.restart().asSeconds();
      if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space))
      {
         fired_bullet = true;
      }
      //clear...draw...
      main_window.clear();
      move_the_background(main_background, main_background2, delta, main_window);
      start_level.level_one(asteroid_sprites, main_window, delta, space_ship);
      start_level.level_two(asteroid_sprites, main_window, delta, space_ship);
      main_window.draw(space_ship);

      //keypress
      key_check.w_key(space_ship.getPosition(), space_ship);
      key_check.s_key(space_ship.getPosition(), space_ship);
      key_check.a_key(space_ship.getPosition(), space_ship);
      key_check.d_key(space_ship.getPosition(), space_ship);
      key_check.esc_key(main_window);

      if (fired_bullet == true && bullet_sprites.size() == 0)
      {
         sf::Sprite new_bullet = create_bullet.create_sprite("bullet.png", true);
         new_bullet.setPosition(space_ship.getPosition().x + 145.f, space_ship.getPosition().y + 0.f);
         bullet_sprites.push_back(new_bullet);
         fired_bullet = false;
      }

      for (int j = 0; j < bullet_sprites.size(); j++)
      {
         main_window.draw(bullet_sprites[j]);
         create_bullet.move_sprite(1500.f, 0.f, delta, bullet_sprites[j], false, 0.f, false, 0.f, 0.f, "pos");
         if (bullet_sprites[j].getPosition().x > 1940)
         {
            bullet_sprites.erase (bullet_sprites.end() - 1);
         }   
      }


      //display...
      main_window.display();
      
      //check to see if main window is closed
      close_event_checker(main_window);
   }

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Multiple bullets with isKeyPressed
« Reply #1 on: February 13, 2017, 02:02:36 am »
What is the behaviour that you would like to happen?
Fire bullets at regular intervals? Fire only one bullet per keypress? Something else?

It sounds like you might want one bullet per keypress.

Possibly the simplest/best way to trigger something once and only once when a key is pressed is to use events. A keypress event is received whenever the key is pressed; you simply trigger a new bullet when you get the event and that will be that. Note that you may want to turn off key repeating to avoid multiple events.

Another option would be to use flags. Basically, you would be emulating an event system using the realtime states. Track the previous value/state of the key (pressed or not) and compare it to the current state. If the current state is different from the previous state then also test to see if the new state is pressed and trigger the bullet if it is so. Then, make "previous state" the same as the current state.



If you would like to fire at regular intervals, you can use a timer and realtime key states. If the timer is below the minimum interval/delay, don't fire. If the timer is above that delay, reset the timer and trigger the bullet.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

coltranenaima

  • Newbie
  • *
  • Posts: 11
    • View Profile
Re: Multiple bullets with isKeyPressed
« Reply #2 on: February 13, 2017, 02:11:05 am »
What is the behaviour that you would like to happen?
Fire bullets at regular intervals? Fire only one bullet per keypress? Something else?

It sounds like you might want one bullet per keypress.

Possibly the simplest/best way to trigger something once and only once when a key is pressed is to use events. A keypress event is received whenever the key is pressed; you simply trigger a new bullet when you get the event and that will be that. Note that you may want to turn off key repeating to avoid multiple events.


Essentially one key press = one bullet is the goal. I would eventually like a real rate of fire where it's not reliant on the previous bullet being off screen to spawn the next bullet. It would be great if the user can also hold down space bar and a steady stream of bullets pours out, which can be sped up with power ups etc...

I will be giving your first suggestion a shot, I will try implementing this in my stripped down project and report back. If no luck, I will try your other suggestion.

Thanks!

coltranenaima

  • Newbie
  • *
  • Posts: 11
    • View Profile
Re: Multiple bullets with isKeyPressed
« Reply #3 on: February 13, 2017, 02:25:47 am »
I'm an idiot...I am using the Key pressed events lol. I have space bar in main and a header file for all other events. If this is not a standard way of handling key press events, sorry!

Main.cpp

      if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space))
      {
         fired_bullet = true;
      }

Key_logic.h

#pragma once
#include <SFML/Bullets.h>

#ifndef KEY_LOGIC_H
#define KEY_LOGIC_H

class key_logic {
private:
      int j = 0;
      int bullet_number = 0;
      bool space_pressed = false;
public:

   void w_key(sf::Vector2f position, sf::Sprite &sprite_name)
   {
      if ((sf::Keyboard::isKeyPressed(sf::Keyboard::W) || sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) && position.y > 0)
      {
         sprite_name.setPosition(position.x + 0, position.y - .45);
      }
   }

   void s_key(sf::Vector2f position, sf::Sprite &sprite_name)
   {
      if ((sf::Keyboard::isKeyPressed(sf::Keyboard::S) || sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) && position.y < 1080)
      {
         sprite_name.setPosition(position.x + 0, position.y + .45);
      }
   }

   void a_key(sf::Vector2f position, sf::Sprite &sprite_name)
   {
      if ((sf::Keyboard::isKeyPressed(sf::Keyboard::A) || sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) && position.x > 0)
      {
         sprite_name.setPosition(position.x - .45, position.y + 0);
      }
   }

   void d_key(sf::Vector2f position, sf::Sprite &sprite_name)
   {
      if ((sf::Keyboard::isKeyPressed(sf::Keyboard::D) || sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) && position.x < 1920)
      {
         sprite_name.setPosition(position.x + .45, position.y + 0);
      }
   }

   void esc_key(sf::RenderWindow &window)
   {
      if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
      {
         window.close();
      }
   }
};

#endif

coltranenaima

  • Newbie
  • *
  • Posts: 11
    • View Profile
Re: Multiple bullets with isKeyPressed
« Reply #4 on: February 13, 2017, 05:51:06 am »
Well in-case anyone else has the same issue, the easiest fix is from
If you would like to fire at regular intervals, you can use a timer and realtime key states. If the timer is below the minimum interval/delay, don't fire. If the timer is above that delay, reset the timer and trigger the bullet.

I removed the vector erase for now until I find a better way to clean up excess bullets, but using a cool-down timer achieves the goal of one key press = one bullet.

Now I have two settings to tweak with power ups, either the speed of the bullet or the cool down for rate of fire.

Thanks Hapax!

See below (changes are bolded):

#include <SFML/Graphics.hpp>
#include <SFML/Poll_Event.h>
#include <SFML/Sprite_Creator.h>
#include <SFML/Key_logic.h>
#include <SFML/ast_randomizer.h>
#include <SFML/Levels.h>
#include <SFML/Move_the_background.h>
#include <SFML/Bullets.h>

int main()
{
   //make all the things
   sf::RenderWindow   main_window(sf::VideoMode(1920, 1080), "Game", sf::Uint32());
   main_window.setKeyRepeatEnabled(true);
   sprite_creator      create_space_ship,
                  create_asteroid,
                  create_background,
                  create_bullet;
   levels            start_level;
   key_logic         key_check;
   sf::Sprite         space_ship = create_space_ship.create_sprite("spaceship.png", true),
                   new_bullet = create_bullet.create_sprite("bullet.png", true),
                  main_background = create_background.create_sprite("main_game_background.png", false),
                  main_background2 = create_background.create_sprite("main_game_background.png", false);
                  std::vector<sf::Sprite> asteroid_sprites(100, sf::Sprite(create_asteroid.create_sprite("Asteroid.png", true)));
                  std::vector<sf::Sprite> bullet_sprites;
   main_background2.setPosition(sf::Vector2f(3820.f, 0.f));
   space_ship.setPosition(sf::Vector2f(300, 100));
   space_ship.setScale(sf::Vector2f(.8f, .8f));

   //clock
   sf::Clock delta_clock, bullet_clock;
   sf::Time bullet_cooldown;

   while (main_window.isOpen())
   {      
      float delta = delta_clock.restart().asSeconds();
      bullet_cooldown = bullet_clock.getElapsedTime();

      //clear...draw...
      main_window.clear();
      move_the_background(main_background, main_background2, delta, main_window);
      start_level.level_one(asteroid_sprites, main_window, delta, space_ship);
      start_level.level_two(asteroid_sprites, main_window, delta, space_ship);
      main_window.draw(space_ship);

      //keypress
      key_check.w_key(space_ship.getPosition(), space_ship);
      key_check.s_key(space_ship.getPosition(), space_ship);
      key_check.a_key(space_ship.getPosition(), space_ship);
      key_check.d_key(space_ship.getPosition(), space_ship);
      key_check.esc_key(main_window);

      if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space) && bullet_cooldown.asSeconds() >= .5f)
      {
         bullet_clock.restart();

         sf::Sprite new_bullet = create_bullet.create_sprite("bullet.png", true);
         new_bullet.setPosition(space_ship.getPosition().x + 145.f, space_ship.getPosition().y + 0.f);
         bullet_sprites.push_back(new_bullet);
      }

      for (int j = 0; j < bullet_sprites.size(); j++)
      {
         main_window.draw(bullet_sprites[j]);
         create_bullet.move_sprite(1500.f, 0.f, delta, bullet_sprites[j], false, 0.f, false, 0.f, 0.f, "pos");
      }
      //display...
      main_window.display();
      
      //check to see if main window is closed
      close_event_checker(main_window);
   }
   return 0;
}
« Last Edit: February 13, 2017, 05:53:13 am by coltranenaima »

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Multiple bullets with isKeyPressed
« Reply #5 on: February 13, 2017, 04:13:15 pm »
You're welcome and I'm glad you got it working :)

Please post code inside [code=cpp] [/code] tags.

I'm an idiot...I am using the Key pressed events lol. I have space bar in main and a header file for all other events. If this is not a standard way of handling key press events, sorry!
I would like to just clarify that you are not actually using KeyPressed events, which are sent to the window when they happen; you check events using the pollEvent (usually) method.
You are using the real-time states of the keys. That is, you check to see - in real-time - the current state (pressed or not pressed) of a specific key at the time of checking.

Most common way to separate the two (for key presses) is to use events for single presses (i.e. do something when the key is pressed and not again while it is held - unless key repeat is still active, e.g. jump/shoot) and to use real-time states for held presses (e.g. move/fire continuously...)
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

coltranenaima

  • Newbie
  • *
  • Posts: 11
    • View Profile
Re: Multiple bullets with isKeyPressed
« Reply #6 on: February 14, 2017, 05:04:42 am »
I would like to just clarify that you are not actually using KeyPressed events, which are sent to the window when they happen; you check events using the pollEvent (usually) method.
You are using the real-time states of the keys. That is, you check to see - in real-time - the current state (pressed or not pressed) of a specific key at the time of checking.

Most common way to separate the two (for key presses) is to use events for single presses (i.e. do something when the key is pressed and not again while it is held - unless key repeat is still active, e.g. jump/shoot) and to use real-time states for held presses (e.g. move/fire continuously...)

I should totally have known what the difference is since I have the below header file for checking if the close event occurs. Sorry this is all new to me!

Slowly learning...I went from one massive int main (over 500 lines of code) to now using header files with functions and classes so that int main is 85 lines of code roughly. Now I struggle with when to use multiple .cpp files...(i get they are intended to define what is in the shared header file), but ah that goes out of the scope of this thread.

I appreciate your help Hapax!

I found the code drop down button as well for the tags lol.

#pragma once

#ifndef POLL_EVENT_H
#define POLL_EVENT_H

void close_event_checker(sf::RenderWindow &thatwindow)
{
        sf::Event close_event;
        thatwindow.pollEvent(close_event);
        if (close_event.type == sf::Event::Closed)
        {
                thatwindow.close();
        }
}

#endif

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Multiple bullets with isKeyPressed
« Reply #7 on: February 15, 2017, 10:50:31 am »
Again, you're welcome :)

Note that events may not be in any particular order (there may be multiple events waiting) and can only be "polled" once each. After that, they're gone.
With all that in mind, your "close checker" is problematic:

It only polls once; it should (preferably) be polling continuously until there are no more events to poll
e.g.
while (window.pollEvent(event))
{
    // check event
}
This checks all of the events waiting - one at a time - and deals with them there and then.

Any other event would be ignored and discarded.
If you receive an event saying, for example, that the window has resized or a mouse button has been pressed, your close checker function would discard that event.
Again, the code above that polls all of the events is the main solution to this.

The "check event" part in the code above could pass the event to functions that deal with different types.
e.g.
while (window.pollEvent(event))
{
    check_close_event(window, event);
    check_mouse_events(event);
    check_keyboard_events(event);
}
Then the checker would be a little smaller and simpler:
void check_close_event(sf::RenderWindow &thatwindow, sf::Event event)
{
    if (event.type == sf::Event::Closed)
    {
        thatwindow.close();
    }
}



As for headers and source files...
Generally, headers would contain only declarations and source files would contain definitions (the actual code).
Source (e.g. .cpp) files are compiled separately and are self-contained.
Header (e.g. .hpp or just .h) files are generally "included" - technically just copy and pasted where they are included.
This may not be the best description/explanation. Google knows more than I do ;)
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

 

anything