[C++/SDL2] Adding Bullets

I’m trying to add some bullets or something alike to my project. Whenever the player presses the “f”-key, a bullet should be shot. I don’t have a Entity-class yet, which would be quite useful I guess… I’ll do that later on. Can you see anything obviously wrong with my funtions?

There’s no cooldown and movement added, so at the moment there should be a lot of sprites drawn over each other in the top left corner, but somehow there aren’t.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void CPlayer::HandleInput(SDL_Event* Event)
{
    while (SDL_PollEvent(Event))
    {
        std::cout << "Polling...\n";
        switch (Event->type)
        {
            case SDL_KEYDOWN:
                switch (Event->key.keysym.sym)
                {
                    case SDLK_f:
                        Attack.New();
                        std::cout << "f pressed...\n";
                        break;
                }
        }
    }
}


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
 //CAttack.h
#ifndef _CATTACK_H_
    #define _CATTACK_H_


#include "CSprite.h"
#include <vector>


class CAttack
{
    public:
        CAttack();

    public:
        void Load(SDL_Renderer* pRenderer);
        void New();
        void Update();
        void Render(SDL_Renderer* pRenderer);
        void CleanUp();

        static std::vector <CAttack> AttackList;

    private:
        SDL_Texture* m_tFireball;

        int PosX;
        int PosY;
        int Width;
        int Height;

        int CurrentFrame;
        int MaxFrames;

        CSprite AttackSprite;
};


#endif // _CATTACK_H_ 


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
47
48
49
50
51
52
53
54
 // CAttack.cpp

#include "CAttack.h"

std::vector<CAttack> CAttack::AttackList;

CAttack::CAttack()
{
    PosX = 0;
    PosY = 0;
    Width = 16;
    Height = 16;

    CurrentFrame = 0;
    MaxFrames = 4;
}

void CAttack::Load(SDL_Renderer* pRenderer)
{
    m_tFireball = CAttack::AttackSprite.Load(pRenderer,"data/imgs/player/attacks/Fireball.png",255,0,255);
}

void CAttack::New()
{   CAttack newAttack;
    CAttack::AttackList.push_back(newAttack);
}


void CAttack::Update()
{
    for (int i = 0; i < CAttack::AttackList.size(); i++)
    {
        if (CAttack::AttackList[i].CurrentFrame >= CAttack::AttackList[i].MaxFrames)
        {
            CAttack::AttackList[i].CurrentFrame = 0;
        }

        CAttack::AttackList[i].CurrentFrame++;
        std::cout << i << ": ";
        std::cout << CAttack::AttackList[i].CurrentFrame << "\n";

        CAttack::AttackList[i].AttackSprite.SetSourceRect(Width * CurrentFrame,0,Width,Height);
    }
}


void CAttack::Render(SDL_Renderer* pRenderer)
{
    for (int i = 0; i < CAttack::AttackList.size(); i++)
    {
        CAttack::AttackList[i].AttackSprite.SetDestinationRect(PosX,PosY,Width,Height);
        CAttack::AttackList[i].AttackSprite.Render(pRenderer);
    }
}

You are adding a lot of CAttack objects that do not have a texture or sprite set, so they are invisible.
Just a tip:
Consider using an object pool and a resource manager. You're going to quickly get terrible performance if you load an image for each bullet like that, and iterating over dead bullets can bring the same depending on how many you have.
@Avilius:
What do you mean by object pool? Creating an Entity class und then let other classes inherit it?
My 'resource manager' would be my CSprite-class with the 4 methods used above (Load/SetSourceRect/SetDestinationRect/Render). I just realised I'm missing the cleanup ^^' I'll add that ;) do you have an example of how a resource manager should look? Whats its exact job?


@ne555:
First a general question: Is it beneficial to/for my program if I'd put all these 'for'-statements in the CPlayer class or is it ok to loop trough the vector in the class where it has been created?
@HalfNOoB
I have got something similar with a way to handle resources. I use a singleton class and use a map to map the textures to a string and call it to avoid having duplicates.
1
2
3
4
5
6
7
8
9
10
11
12
13
class TextureManager
{
public:
	static TextureManager* Instance();

private:
	static TextureManager* smInstance;
	TextureManager();
	~TextureManager();

	// Texture map
	std::map<std::string, SDL_Texture*> mTextureMap;
};


Just like that, but with functions to create and delete elements.
Alright :)

Seems a great way of doing things :D I didn't work with the std::map yet but it seems that it was created for the purpose of a texturemanager only ;P

I'll probably add this in the next step of my Project but for now, how would you alter my original code, to get it working?

ne555 hinted to me that I don't load the sprites for alot of elements. So do I really Need to load the sprite for every bullet that is created? I thought I'll just load it once and then render that Image multiple times at the same time...

Secondly, should I Loop trough my vector in the Player class?

Last edited on
ne555 hinted to me that I don't load the sprites for alot of elements. So do I really Need to load the sprite for every bullet that is created? I thought I'll just load it once and then render that Image multiple times at the same time...


You want to only load textures into memory a single time since grabbing a texture from your hard drive and loading it is a very slow operation. This is where a resource manager comes in, but before we go into that I feel I should explain the difference between a Sprite and a Texture and other assets.

A texture is a heavyweight object (Meaning it is a large object which will take time to load into memory). This is basically the image you will be using for your bullet (To keep things simple I won't go into spritesheets). You only want a single instance of this image loaded into memory when the image is needed.

Now a Sprite is a lightweight object that is meant to represent every single 2D entity in your game. The sprite will hold pointers to all the assets that make up that entity. It does not actually contain the texture itself and has no part in the actual loading of the texture or other assets because that would make it a heavyweight object with bad performance.

So lets say we want to create 10 bullets on the screen. Are we going to want to load up and store the bullet texture in memory 10 times? No we definitely do not want to do that, instead we only want to load the bullet texture into memory 1 time and then have all of those 10 bullet sprites on screen contain a pointer to that 1 bullet texture to use when it is drawn.

Hopefully that makes sense.

Now for the resource manager part. The sole purpose of a resource manager is to make sure a certain type of asset (Textures, Audio Clips, Shaders, ect) are only loaded into memory a single time and then to provide access (Usually by returning a pointer to use) to those assets whenever a Sprite or other entity requires it.

So for example here is a simple resource manager in SFML https://github.com/bjumbeck/AsteroidsClone/blob/master/Source/ResourceManagement/ResourceManager.hpp . Each instance of that class acts as a container to store a single type of asset (Though the class itself is generic and can handle all asset types supported by SFML). The load method basically assures that the asset hasn't been loaded into memory already and then loads and stores that asset away.

The get method then is able to retrieve any asset that has been loaded already. For example lets say your sprites constructor takes a single parameter which is a pointer to the texture which you want that sprite to use (Which is quite common). With this resource manager you would just do something simple like Sprite bullet(textureManager.get("bullet.png")); and that is it.

I didn't do anything fancy with this resource manager but you could make it so that the get() method first checks to see if the asset is loaded already and if not loads it and returns a pointer but that is up to you.

Here is some other things regards the resource manager that might help also.

I used enums as IDs for the different assets and also at the bottom you can see common typedefs for the resource managers - https://github.com/bjumbeck/AsteroidsClone/blob/master/Source/ResourceManagement/ResourceIDs.hpp

Here you can see how you can use the resource manager to preload assets into memory - https://github.com/bjumbeck/Space-Shooter/blob/master/Application.cpp#L26

Hopefully this helps a little bit explaining about why you don't want a bunch of the same texture loaded into memory and how resource manager look. Even though the source code is in SFML it shouldn't be to hard to transfer it over to SDL.
Last edited on
I want to recommend to kill the class hierarchy system. As the size of the project grows, it slowly becomes more and more unmaintainable and inflexible.

Try looking into entity-component systems. If you plan on this project being even medium sized then you'll probably end up doing this later on in your project's lifetime.

@Zereo
Yes you did! :)) I've already heard/read something like that, thats why I was trying to load the texture of the Bullet only once (The load function is only called once, while render and update are called every Frame)
I'm adding such a resource Manager to my Project at the Moment. But how does that help me Fixing my Problem with the bullets? :/

@Avilius
Thanks, ill take that into consideration
Topic archived. No new replies allowed.