SFML Drawing a Moving Sprite in Another Class - c++

I am fairly new to SFML and am trying to make a scrolling plane game that shoots out a bullet whenever I press the spacebar. I've gotten all of the movement working, but I'm trying to figure out how to put the bullets into another class file so that I can have a whole set of them later on (I.E. create.bullet(), create.missle(), etc.). Here is the code that I have now.
void create::bullet(int xPos, int yPos)
{
sf::Texture bulletTexture;
if(!bulletTexture.loadFromFile("bullet.png"))
cout << "There was an error loading the bullet texture file." << endl;
sf::Sprite bulletSprite;
bulletSprite.setTexture(bulletTexture);
bulletSprite.setPosition(xPos, yPos);
window.draw(bulletSprite);
}
I have the sf::RenderWindow instance called window in the main class, but I apparently can't reference it directly from another class. I haven't been able to implement velocity yet either, but I should be able to do that. Another thing that I need help with however, is getting it so that there is no limit on the number of bullets that can be fired. It seems like if I just have this function run whenever spacebar is pressed, it will just reset the sprite to the new position and get rid of the old one. Thanks for the help!

First of all, loading textures from files is slow. You should do it once when the program or level starts, and store the texture somewhere.
Instead of a Create class, make a Bullet class. Then, you can have a vector of bullets (or pointers/smart pointers to them). Each time you want a new bullet, you just push_back() it to the vector. If a bullet needs to be destroyed you erase() it. Then, for the game itself, you need to call Move() for every bullet and then Draw() for every bullet in the vector. Once you have that done, you can add the collision detection and so on.
You also have a choice - each bullet can have its own sf::sprite and modify it, or you can have one sf::sprite for each game sprite and reposition it for every bullet.
Personally, I'm using the second approach. My Bullet class looks like this:
Bullet::Bullet(std::string ntype, double nx, double ny, double nvx, double nvy):
type(ntype), x(nx), y(ny), vx(nvx), vy(nvy)
{
angle=atan2(vy, vx)*(180/M_PI);
}
void Bullet::Move()
{
x+=vx;
y+=vy;
};
void Bullet::Draw()
{
DrawSprite(type, x, y, angle+90);
};
In the separate .cpp file, I have a string ordered map of sf::sprites. My draw function looks like this:
void DrawSprite(std::string type, float x, float y, float angle)
{
sf::Sprite temp=sprites[type];
temp.setRotation(angle);
temp.setPosition(x, y);
window.draw(temp);
}

Related

Why that code don't draw sprites C++ SFML

I wanted to draw sprites on screen having only information about it in my struct and next draw text on it
Information are good
X and Y scale=1
Path is good and point to graphic
position and rotation =0;
int free have a good number
But Text works
so i don't know why Sprite don't
I tried commenting code with drawing and making text don't work
struct ObjectInfo
{
float Xpoz,Ypoz;
std::string TexPath;
float Xscale,Yscale;
float Rotation;
};
ObjectInfo OI[1000];
int free;
void Draw()
{
for(int i=0;i<free;i++)
{
sf::Texture t;
t.loadFromFile(OI[i].TexPath);
sf::Sprite s;
s.setTexture(t);
s.setPosition(OI[i].Xpoz,OI[i].Ypoz);
s.setScale(OI[i].Xpoz,OI[i].Ypoz);
s.setRotation(OI[i].Rotation);
okno.draw(s);
sf::Text text;
text.setFont(font);
text.setCharacterSize(48);
text.setColor(sf::Color::Black);
text.setPosition(s.getPosition());
text.setString(IntToString(i));
okno.draw(text);
}
}
I expected that sprites and Text display
but only text display
When you call s.setTexture(t) sprite s remembers texture t by pointer/reference. So, when your code exit from for loop sf::Texture t is destroyed (C++ destroy scope variables when exiting some scope) and pointer/reference in sprite class points to deleted memory locations, which cause SFML to have error while drawing your sprites. The solution to this problem is the global array for textures which you use. I also recommend adding the global array for sf::Sprites, because it makes your code safer. Here is how it may be implemented:
//In the global scope
sf::Texture textures[1000];
sf::Sprite sprites[1000];
//void draw() new for loop
for(int i=0;i<free;i++)
{
textures[i].loadFromFile(OI[i].texPath);
sprites[i].setTexture(s);
//Set other parameters
okno.draw(s);
sf::Text text;
text.setFont(font);
text.setCharacterSize(48);
text.setColor(sf::Color::Black);
text.setPosition(s.getPosition());
text.setString(IntToString(i));
okno.draw(text);
}
By the way, there are some more improvements to your code. Texture loading operation is a heavy operation, so if paths to textures are immutable, I will recommend you adding some method for loading them at once. Also, creating text class may be a heavy operation, so it is good to add global array of sf::Text classes you use

Access violation when writing to vector at runtime

I'm pretty new to C++ and just getting started with pointers and such. I'm making a simple game with SFML. I've made a vector "drawList" to store all the shapes in the game that will be rendered on screen. If I add the shapes to the vector before the main loop starts it works fine, however when I add objects at runtime I get an Access violation error on the following code:
for (size_t i = 0; i < drawList.size(); i++){
window.draw(*drawList[i]);
}
I think that the *drawlist[i] is an invalid pointer, however attempts to fix the issue so far have been unsuccessfull. The drawList vector and the method to add objects to it are setup like this in a header:
extern std::vector<const sf::Drawable*> drawList;
struct Globals {
void AddDrawable(const sf::Drawable &drawable);
};
and like this in the associated cpp:
std::vector<const sf::Drawable*> drawList;
void Globals::AddDrawable(const sf::Drawable & drawable){
drawList.push_back(&drawable);
}
I then try to add a shape to the vector in the constructor of a newly instatiated projectile class by calling the "AddDrawable" method and giving it the following method as parameter:
sf::RectangleShape& GetShape() { return shape; };
I feel like there is something really obvious going wrong with the references and pointers of things but so far I've not been able to figure it out. If anyone could suggest me ways to adress this issue it would be greatly appriciated. Thanks in advance!
EDIT:
I'll try to give some more context on Cris Luengo's suggestion, I have a main.cpp containing the main while loop. Before that while loop is initiated I instantiate a Player and a Projectile class. Inside the while loop I check if there is anything to update and draw in a forloop like so:
int main() {
Projectile bullet = Projectile(WINDOW_WIDTH /2.f, WINDOW_HEIGHT /2.f, 0.05f, 0.05f);
Player player = Player(WINDOW_WIDTH / 2.f, WINDOW_HEIGHT / 1.2f);
while (true) {
window.clear(Color::Black);
if (Keyboard::isKeyPressed(Keyboard::Key::Escape)) break;
for (size_t i = 0; i < updateList.size(); i++){
updateList[i]();
window.draw(*drawList.at(i));
}
window.display();
The updateList and drawList are both vectors that get filled from the constructors of the Player and the Projectile classes like so (This is identical for both the player and the projectile constructor respectively):
Projectile::Projectile(float mX, float mY, float velX, float velY){
velocity = sf::Vector2f(velX, velY);
GetShape().setPosition(mX, mY);
//other shape stuff
Globals globals;
updateIndex = globals.AddToUpdateList([this]() {Update(); });
globals.AddDrawable(GetShape());
}
As shown earlier, the GetShape() method is setup in the Player and Projectile headers like like this:
sf::RectangleShape& GetShape() { return shape; };
As it stands right now, this code works when I instantiate the objects to be added to the drawList before the while loop starts. If I read new data to it at runtime, when it is also being read, the program throws an access violation error in the forloop when the drawList is being accessed.
I use the same code when adding things to the drawList at runtime. I simply instantiate a new Projectile on a button press. The idea is that the constructor of the instantiated class adds the needed data to the lists (The updateList works fine)
Thank you in advance

Variable not passing trough (maybe end of scope)

So I am trying to assign a variable trough a function. The code in the function would normally exist in the constructor but because I dont want to have to write it in the constructor for all the classes that inherite the same parent I made a function.
But the function doesnt seem to work properly or assign the vallue properly.
This is the code in the constructor:
sf::Texture holdTexture;
sf::Sprite holdSprite;
Wolf::Wolf(float speed, Player& p) :
Monster(speed, p)
{
holdTexture.loadFromFile("Assets\\Wolf.png");
sprite.setTexture(holdTexture);
sprite.setOrigin(24,22);
}
The sprite value is declared in a parent-parent-parent called Entity.h
class Entity {
public:
Entity();
sf::Sprite sprite;
public:
sf::Vector2f getEntityCoords();
protected:
void loadSprite(const std::string &filename);
};
Which has the function:
void Entity::loadSprite(const std::string &filename) {
sf::Texture holdTexture;
holdTexture.loadFromFile(filename);
sprite.setTexture(holdTexture);
}
So now my constructor looks like:
Wolf::Wolf(float speed, Player& p) :
Monster(speed, p)
{
loadSprite("Assets\\Wolf.png");
}
Wolf is derived from Monster. Monster is derived from Entity. And the call to loadSprite works.
But now the sprite just turns into a white box. As the title says, it may be an end of scope issue but I am not experienced enough with c++ to know for sure. Looking around hasnt gotten me an answer too. I hope this is not a stupid question. If you need to see more code please ask, it would be great if someone could help :)
As long as the sprite lives the sprite needs an existing texture to be present. As soon as the method ends the sf::Texture holdtexturewould be cleared from the memory and because of that it would display an white square.
I fixed it by adding an sf::Texture to the Entity class and assigning that texture to the sprite.

Making an object move independently in SDL(like a gun shot)

I have been following lazyfoos tutorials on SDL, and I have heavily modified his code to make sort of a ship game, that moves around. I'm trying to make the ship shoot, but i have absolutely no idea how to go about doing this. I have the ship and it's movements and the actual application of the image in a class, and I as wondering if anyone had any techniques or certain ways that are efficient in making the ship shoot, making the shot move independently and then disappearing when it goes off screen. I know that I am giving a vague explanation sort of, but I don't want to be given all of the answers, just a little sample code and a point in the right direction.
Create a class to hold a projectile, with all the information you need in it, such as this:
struct Projectile
{
Vector2 position;
Vector2 velocity;
shared_ptr<Image> graphic;
Time time_until_my_destruction;
bool dead;
void update(Time time_delta) {
if(!dead) {
position += velocity * time_delta;
time_until_my_destruction -= time_delta;
if(time_until_my_destruction < 0.0) dead = true;
}
}
void draw(DrawDest & dest) const {
graphic->draw(dest, position);
}
bool checkCollision(const GameObject & object) const {
return object.area().contains(position);
}
};
This class is not complete obviously, you'll probably want to make adjustments to access levels, and write some constructors and other things, but it should give you the basic idea.
Make a container of those. When the ship fires, put one into the container. Each frame, call update, draw, check if the projectile is dead and check for collisions against the game objects. If a collision occurs, apply damage or whatever. If the object is dead, remove it from the container.
I can only absolutely recommend Aaron's Game Programming Tutorials, it uses C++ and SDL.

Share variables between classes C++?

Yet another question about classes as Im new to OOP. I am creating a game just for fun. Its a top down shooter, space shooter.
I have a few different classes:
Bullet (a list of bullet coordinates),
Player (player sprite, position etc),
Enemy (enemy sprite, position etc),
Collision (taking coordinates Ax, Ay, and Bx, By to see if they have collided)
How can I send the coordinates from Bullet, Enemy to the Collision class to see if they have collided?
Collision col
col.collision(ax, ay, bx, by) //how can I get the Player and Bullet pos?
Probably what you want to do is to have a common class "SceneObject" that has a position. Then Player, Enemy and Bullet all inherit from that class.
Your Collision then does not need knowledge about Players, Enemies etc. but only SceneObjects which have a position. You can write a Getter method in your base class that returns the position.
I would create a base class for Bullet, Enemy (and maybe Player). Let's call it Object and the Object would own the Coordinates and would have a function determining if a Collision happened.
In code it would look like to following:
class Object
{
private:
Coord position;
public:
bool Collide(const Object& otherObject) const;
};
You typically have to go through every enemy and check the coordinates of that enemy against your player coordinates.
I'm sure you stored those enemies somewhere, preferably in a container like std::vector. Now let me just assume you did exactly that:
// somewhere in a 'Game' class
std::vector<Enemy> myEnemies;
Player myPlayer;
Collision col;
for(int i=0; i < myEnemies.size(); ++i){
col.collision(myPlayer.getX(), myPlayer.getY(), myEnemies[i].getX(), myEnemies[i].getY())
}
And that's it. :) You do the same for bullets and similar objects. Save them all in a container, iterate over that container, and check the positions of the objects.
Now, for another tip, if a class only contains methods and no variables, you may aswell make that free functions. Just because you use OOP in some parts of your project doesn't require you to use it everywhere. Make that collision a free function and be done with it. :)