Shooting a sprite to a specific point in SFML

I have no idea how to do this.
Last edited on
bump? i don't know how to do this at all.
I'm not sure what you mean by "shooting a sprite" to a specific position.
Moving a Sprite to a given position x, y is easy:
spr.SetPosition(x,y); where spr is an instance of sf::Sprite
That sets the sprite to a position..

I want to shoot a sprite from a tower to a specific point. i.e

Get the tower's rotation (already did that), and make the bullet shoot out of where the tower is pointing at to the position I want.

Sorry if I worded that wrong!
Last edited on
You wish to have the bullet move smoothly from the tower to the target?

Calculate the velocity components for the bullet. Assuming the bullet speed = v then

distance = sqrt( (targetX-towerX)*(targetX-towerX) + (targetY-towerY)*(targetY-towerY) );

vX = v*(targetX-towerX)/distance;
vY = v*(targetY-towerY)/distance;

Increment the bullet position by vX and vY each frame
x += vX;
y += vY;

Set the new sprite position each frame.
spr.SetPosition(x,y);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
}
	for(int i = 0; i < Towers.size(); i++)
	{
		for(int x = 0; x < Towers[i].Bullets.size(); x++)
		{
			Towers[i].Bullets[x].BulletSpr.setRotation(Towers[i].Bullets[x].angle);
			
			Towers[i].Bullets[x].distance = sqrt((sf::Mouse::getPosition(Window).x - Towers[i].TowerSpr.getPosition().x) 
				* (sf::Mouse::getPosition(Window).x - Towers[i].TowerSpr.getPosition().x) + (sf::Mouse::getPosition(Window).y - Towers[i].TowerSpr.getPosition().y) 
				* (sf::Mouse::getPosition(Window).y - Towers[i].TowerSpr.getPosition().y));
			Towers[i].Bullets[x].vX = .5 * (sf::Mouse::getPosition(Window).x - Towers[i].TowerSpr.getPosition().x) / Towers[i].Bullets[x].distance;
			Towers[i].Bullets[x].vY = .5 * (sf::Mouse::getPosition(Window).y - Towers[i].TowerSpr.getPosition().y) / Towers[i].Bullets[x].distance;

			Towers[i].Bullets[x].x += Towers[i].Bullets[x].vX;
			Towers[i].Bullets[x].y += Towers[i].Bullets[x].vY;

			Towers[i].Bullets[x].BulletSpr.setPosition(Towers[i].Bullets[x].x, Towers[i].Bullets[x].y);

		}

	}


Hmm. It doesn't seem to work, but I'll keep trying on your code.
That was just a general method description I posted.
Your bullets actually have data members named x, y, vX, vY and distance? What an amazing coincidence!

Are you getting compiler errors?

Note that distance, vX and vY only need to be calculated at the frame when a bullet is fired, not in each frame while the bullet is moving.

The x += vX; y += vY; instructions apply while the bullet is in flight. You wouldn't want to recalculate vX and vY while the bullets are moving (unless they're homing shots).
I just actually made them because you had them, ha. Is there a way without having those? All of this is really confusing.

No, I'm not getting any errors. Just doesn't work.
Just doesn't work.


You should try to be more specific.

fun2code's approach will work. So if it's not working you're just implementing it wrong.

Conceptually it's very simple:

- when a bullet is fired, it's assigned an X and Y velocity.
- every update, you move the bullet according to its velocity.

The trick here is determining the velocity, which fun2code already showed you how to do:

 
velocity = (target - currentposition) / distance * bulletspeed;
So I ended up with this -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
if(sf::Mouse::isButtonPressed(sf::Mouse::Right))
	{
		for(int i = 0; i < Towers.size(); i++)
		{
			Bullet bullet;
			BulletTxtr -> loadFromFile("assets//BulletTxtr.png");
			bullet.BulletSpr.setTexture(*BulletTxtr);
			for(int x = 0; x < Towers.size(); x++)
			{
				bullet.angle = Towers[i].angle + 270;
			}
			bullet.BulletSpr.setPosition(Towers[i].TowerSpr.getPosition().x, Towers[i].TowerSpr.getPosition().y);
			bullet.BulletSpr.setOrigin(bullet.BulletSpr.getGlobalBounds().width / 2, (bullet.BulletSpr.getGlobalBounds().height / 2) + 6);

			bullet.speed = 2;
			bullet.x = bullet.BulletSpr.getPosition().x;
			bullet.y = bullet.BulletSpr.getPosition().y;

			bullet.distance = sqrt((sf::Mouse::getPosition(Window).x - bullet.x) * (sf::Mouse::getPosition(Window).x - bullet.x) + (sf::Mouse::getPosition(Window).y - 
				bullet.y) * (sf::Mouse::getPosition(Window).y - bullet.y));

			
			
			Towers[i].Bullets.push_back(bullet);

			

		}
	}
		for(int i = 0; i < Towers.size(); i++)
		{
			for(int x = 0; Towers[i].Bullets.size(); x++)
			{
				Towers[i].Bullets[x].vX = Towers[i].Bullets[x].speed * (sf::Mouse::getPosition(Window).x - Towers[i].Bullets[x].x) / Towers[i].Bullets[x].distance;
				Towers[i].Bullets[x].vX = Towers[i].Bullets[x].speed * (sf::Mouse::getPosition(Window).y - Towers[i].Bullets[x].y) / Towers[i].Bullets[x].distance;

				Towers[i].Bullets[x].BulletSpr.move(Towers[i].Bullets[x].vX, Towers[i].Bullets[x].vY);

			}
		}


I just realized what you had to do. Calculate the distance between the two points and then use that to move the sprite. For some reason I'm getting a vector subscript out of range error every time I click the right mouse.. Am I making this too complicated? Because it's getting kind of hard to read all of those code.
Am I making this too complicated?


Yes.

You need organizational help. You're trying to cram too much into one function and it's getting unwieldly.

line 6: You are reloading the bullet texture for every tower, every time the right mouse button is clicked. This is a huge waste. The texture should be loaded exactly once. Were you the one I talked to about resource managers? Use one.

line 34,35... you are calculating the distance every update. This is wasteful. Once you have a bullet velocity that velocity doesn't change (unless there are curving or homing bullets) so just record the velocity up front and use it for the entire lifetime of the bullet.


Lastly.... and this is the important bit.... if Bullet is a class, why isn't it doing the movement itself? The whole point of having a class is that they do their own work. This should be very simple from outside the bullet class:

1
2
3
4
5
6
7
8
9
10
11
12
13
if( /* time to fire a bullet */ )
{
   // add a new bullet to your list/vector/whatever of bullets
   your_container_of_bullets.push_back(  Bullet( starting position, targetposition )  );
}

void every_logic_update()
{
  for( auto& i : your_container_of_bullets )   // if c++11 is allowed.  Use a normal for loop otherwise
  {
    i.update();  // update does the bullet movement
  }
}


The bullet class then has 2 very simple functions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Bullet
{
private:
  sf::Vector2f  position;
  sf::Vector2f  velocity;
  sf::Sprite  sprite;

public:
  Bullet( const sf::Vector2f& startingposition, const sf::Vector2f& targetposition )
  {
    sprite.setTexture( your_texture_manager.get( "assets/BulletTxtr.png" ) ); // note: one forward slash.

    // do other sprite setup here, like setting the texture rect or whatever

    position = startingposition;
    sprite.setPosition( position );

    // calculate the velocity here per fun2code's approach
  }

  // called every logic update
  void update()
  {
    // move the bullet position, update the sprite to match
    position += velocity;
    sprite.setPosition( position );
  }
};


Note that you probably should have one container for all the bullets. I see no reason for the towers to own their own bullets. Just give the Tower class a way to spawn bullets into the main container.
Wow, ok. I'm just going to restart and follow you and fun2code's way.

I don't really understand what certain things are (like in your resource manager's "get"), but I can learn that by myself. Should I be making all of these in different files? (ResourceManager.h, Tower.h, Bullet.h)?

Also, how would it work without having each tower have its own bullet vector if every tower will shoot different bullets at different times in different positions?

And lastly, thanks a lot for all the help you've been giving me. I'm going to try all of this out now.
Wow


Heh. OOP seems daunting at first, but once it "clicks" and you start really understanding it, it really makes a lot of things simpler. ;)

I don't really understand what certain things are (like in your resource manager's "get")


The idea behind a get() function:

- It returns a reference to a texture.
- The resource manager has an internal list (or really, a map<> would make more sense) of textures.
- When you call get(), the resource manager would check its map to see if the desired texture is already loaded.
- If it is, it just returns that texture (no need to reload it)
- If it isn't, it will load it, put it in the map, then return it.

That way textures are only ever loaded once... and any number of sprites that need a texture can easily grab it.

Should I be making all of these in different files?


General rule of thumb: 1 class = 1 header + 1 cpp file.

That's a very, very basic guideline though and it my no means is a solid rule that you must follow. If you have a small class that just acts as support for a larger class, it might make sense to keep both classes in the same files.

Just use your judgement. Whatever you feel helps keep your code more organized.

Also, how would it work without having each tower have its own bullet vector if every tower will shoot different bullets at different times in different positions?


This would be where you'd use polymorphism.

Have an abstract 'Bullet' class with a few fundamental pure virtual functions... like update for logic updates... and draw to actually draw to the screen. Then from there you can derive a different class to represent each different kind of bullet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class Bullet
{
public:
    virtual ~Bullet() { }  // make a virtual destructor for abstract parent classes.  General rule
    virtual void update() = 0;  // pure virtual 'update' function
    virtual void draw( sf::RenderTarget& target ) = 0;  // pure virtual 'draw' function
};

//...

// your class to represent normal bullets
class NormalBullet : public Bullet
{
private:
    // <the sprite, position, velocity, and other data you need to represent this bullet>

public:
    virtual void update() override
    {
        // ... do 'normal bullet' behavior here
    }

    virtual void draw( sf::RenderTarget& target ) override
    {
        // ... do 'normal bullet' drawing here
    }
};

//...

class ExplodingBullet : public Bullet
{
private:
    // <the data needed for this kind of bullet>

public:
    virtual void update() override
    {
        // ... do 'exploding bullet' behavior here
    }

    virtual void draw( sf::RenderTarget& target ) override
    {
        // ... do 'exploding bullet' drawing here
    }
};


And so on. Just make a new class for each different type of bullet.

Since they're all derived from the Bullet parent class, you can put them all in the same array through the use of pointers:

1
2
3
4
5
6
7
8
std::vector< Bullet* >  bulletlist;

bulletlist.push_back( new NormalBullet( ... ) );
bulletlist.push_back( new ExplodingBullet( ... ) );

for(auto& i : bulletlist)
    i->update();  // this will call the appropriate version of 'update' depending
             // on what kind of bullet this is 



Of course the danger here is that now you're using new, so memory will leak unless you delete it. The best way to approach that is to use a smart pointer, such as unique_ptr to automatically delete the bullet when you're done with it:

1
2
3
4
5
6
7
8
9
typedef std::unique_ptr< Bullet >  BulletPtr;

std::vector< BulletPtr >  bulletlist;

bulletlist.push_back( 
    BulletPtr( 
        new NormalBullet( ... )
        )
    );


Now since you're new'ing into a smart pointer, you do not have to delete it because the smart pointer class will automatically delete it. No risk of memory leak.
So I've read over your message about 5 times now. Should I do what you're telling me to do with bullets with towers?

Like

1
2
3
4
5
6
7
8
9
10
11
class  Tower
{


};

class BasicTower : public Tower
{


};


Also, should I make the parent class (Tower) in the same file(s) as the children class (TowerBasic, etc). Or should they all have their own different files? Same with bullet.
Topic archived. No new replies allowed.