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

Author Topic: Spawning random enemies  (Read 2386 times)

0 Members and 1 Guest are viewing this topic.

Baro

  • Newbie
  • *
  • Posts: 6
    • View Profile
Spawning random enemies
« on: February 16, 2022, 02:55:26 pm »
Hello, im new using SFML. I'm doing a proyect for my carreer, the proyect consist in a 2D game, where the user plays as a spaceship, and you have to destroy or avoid asteroids.

The program compiles and execute normally, but it has a problem, and is that the asteroids does not draw in the screen, and I don`t now how to fix it.

For the asteroids I use 2 classes, Entidades(entity) and entidadManager(EntityManager).

///ENTIDAD
#ifndef ENTIDAD_H
#define ENTIDAD_H
#include <SFML/System/Vector2.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/Sprite.hpp>
using namespace std;
using namespace sf;
class entidad {
public:
        entidad();
        void actualizar();     
        void dibujar (RenderWindow &w);
        Vector2f verposi();
private:
        Vector2f m_pos;
        Sprite m_sprite;
};

#endif

#include "entidad.h"
#include <SFML/System/Vector2.hpp>
#include <SFML/System/Clock.hpp>
#include <vector>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <cstdlib>
using namespace std;

entidad::entidad() {
        Texture tex;
        Sprite sp;
        tex.loadFromFile("meteorito.png");
        sp.setTexture(tex);
        m_sprite = sp;
        m_sprite.setScale(0.70f,0.70f);
        m_pos.x = rand () % 800 + 30;;
        m_pos.y = 0;
        m_sprite.setPosition(m_pos.x,m_pos.y);
}
void entidad::actualizar(){
        m_sprite.move(0,-5);
}
void entidad::dibujar(RenderWindow &w){
        w.draw(m_sprite);
}

Vector2f entidad::verposi(){
        Vector2f charpos = m_sprite.getPosition();
        return charpos;
}
///ENTIDAD

///ENTIDADMANAGER

#ifndef ENTIDADMANAGER_H
#define ENTIDADMANAGER_H
#include <vector>
#include "entidad.h"
#include <SFML/Graphics/RenderWindow.hpp>
using namespace std;

class entidadmanager {
public:
        entidadmanager();
        void actualizar();
        void dibujar(RenderWindow &w);
private:
        vector<entidad> m_v;
};

#endif

#include "entidadmanager.h"
#include "entidad.h"
#include <vector>
#include <iterator>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/System/Clock.hpp>
using namespace std;


entidadmanager::entidadmanager() {
        vector<entidad> v;
        m_v = v;
}
void entidadmanager::actualizar(){
        Clock reloj;
        if(reloj.getElapsedTime().asMilliseconds() == 1000){
                entidad aux;
                m_v.push_back(aux);
                reloj.restart();
        }
        for(size_t i=0;i<m_v.size();i++) {
                m_v[i].actualizar();
                Vector2f charaux;
                charaux = m_v[i].verposi();
                if(charaux.y > 900){
                        auto it = next(m_v.begin(),i);
                        m_v.erase(it);
                }
        }
       
}
void entidadmanager::dibujar(RenderWindow &w){
        for(size_t i=0;i<m_v.size();i++) {
                entidad aux;
                aux = m_v[i];
                aux.dibujar(w);
        }
}
///ENTIDADMANAGER

///PARTIDA

Partida::Partida() {
        nave n1;
        entidadmanager met;
       
}
bool fuera(disparo &d){
        Vector2f p = d.verPosicion();
        if(p.x<0 or p.x>1600) return true;
        if(p.y<0 or p.y>900) return true;
        return false;

}
void Partida::actualizar(juego &j) {
        n1.actualizar();
        met.actualizar();
        if(n1.dispara()) vd.push_back(n1.dispGen());
        for(disparo &d : vd) d.actualizar();
        auto it2 = remove_if(vd.begin(),vd.end(),fuera);
        vd.erase(it2,vd.end());
}
void Partida::dibujar(RenderWindow &w, Sprite fondo) {
        w.clear(Color(220,220,180,255));
        w.draw(fondo);
        n1.dibujar(w);
        met.dibujar(w);
        for(disparo &d : vd) d.dibujar(w);
}
///PARTIDA

Sorry if  the post it`s a bit long (a bit too long haha) but it`s my first one.

Any help is appreciated  ;D
« Last Edit: February 16, 2022, 04:51:47 pm by eXpl0it3r »

kojack

  • Sr. Member
  • ****
  • Posts: 309
  • C++/C# game dev teacher.
    • View Profile
Re: Spawning random enemies
« Reply #1 on: February 16, 2022, 06:54:28 pm »
In this code:
entidad::entidad() {
        Texture tex;
        Sprite sp;
        tex.loadFromFile("meteorito.png");
        sp.setTexture(tex);
        m_sprite = sp;
        m_sprite.setScale(0.70f,0.70f);
        m_pos.x = rand () % 800 + 30;;
        m_pos.y = 0;
        m_sprite.setPosition(m_pos.x,m_pos.y);
}
you make a temporary texture called tex and set the sprite's texture to it.
But sprites need the texture to have the same lifetime, they don't copy the texture, they just keep a pointer to it. So when tex leaves scope at the end of the function, the sprite is now trying to use an invalid texture.

Putting the texture in the asteroid won't work though, because every time you copy the asteroid (like pushing it into m_v) it will break the connection again.
The easiest way to have a texture that doesn't move around in memory and lasts longer than the sprite is to make it as a pointer.
entidad::entidad() {
        Texture *tex;
        Sprite sp;
        tex->loadFromFile("meteorito.png");
        sp.setTexture(*tex);
        m_sprite = sp;
        m_sprite.setScale(0.70f,0.70f);
        m_pos.x = rand () % 800 + 30;;
        m_pos.y = 0;
        m_sprite.setPosition(m_pos.x,m_pos.y);
}
That should work.
Although it has a downside which your version also had: every asteroid loads its own copy of the texture. Many sprites can share a single texture, which is more efficient. It works, it just won't handle massive numbers of sprites as well.

Baro

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Spawning random enemies
« Reply #2 on: February 16, 2022, 07:38:58 pm »
Thank you so much! :D

How can I fix that downside that you said before?


Have a nice day.

kojack

  • Sr. Member
  • ****
  • Posts: 309
  • C++/C# game dev teacher.
    • View Profile
Re: Spawning random enemies
« Reply #3 on: February 17, 2022, 06:58:57 am »
A resource manager of some kind would let you do that.
I think there's one in one of the sfml books.
I wrote my own. :)

Basically you ask it for a texture (such as by filename) and it will either load the texture, or reuse one already loaded.

But we can do a simple cheat method:
entidad::entidad() {
        static Texture *tex = 0;
        if(tex==0)
        {
            tex = new Texture();
            tex->loadFromFile("meteorito.png");
        }
        Sprite sp;
        sp.setTexture(*tex);
        m_sprite = sp;
        m_sprite.setScale(0.70f,0.70f);
        m_pos.x = rand () % 800 + 30;;
        m_pos.y = 0;
        m_sprite.setPosition(m_pos.x,m_pos.y);
}
The way this works is the static tex starts at 0 (null pointer). Statics keep their value on every run of the function (so the =0 part only happens once, not every time). Then if checks if tex has been set. If not, it creates a texture, puts it in tex and loads the file.
This way tex will only load the texture if it is the first asteroid. If it's not the first, it will use the texture that was created by the first asteroid.

Hopefully that makes sense, I just finished running a 6 hour class a few mnutes ago and my head isn't feeling good. :)

This is pretty hacky though, a good resource manager that handles this for you would be better, but it's more complicated to write.

Baro

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Spawning random enemies
« Reply #4 on: February 17, 2022, 08:49:11 pm »
Now the program is working!!! Asteroids appears in screen now, but I have to find a way to spawn a lot of them, cuz now only one appears.

Thank you!!!

kojack

  • Sr. Member
  • ****
  • Posts: 309
  • C++/C# game dev teacher.
    • View Profile
Re: Spawning random enemies
« Reply #5 on: February 18, 2022, 05:52:02 am »
Ah, yes, I wasn't looking at that part.
There's two minor things to fix that should get it spawning.
In your entidadmanager::actualizar() function, you make a clock called reloj. But this is remade every time you call the actualizar function, so the elapsed time keeps resetting to 0 before it reaches the 1 second time.
Move the Clock reloj; line into the entidadmanager.h file, in the same spot as the m_v is declared.

Second, this bit is unreliable:
if(reloj.getElapsedTime().asMilliseconds() == 1000)
The problem is == means exact match. The milliseconds is an integer (so no floating point error), but if you are running at 60fps, the elapsed time will jump 16ms each update. So it can jump over 1000, it might never hit exactly on it.
Change the == to >=
Now if it hits 1000 or goes over 1000 (by any amount), it will trigger the spawn.

Baro

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Spawning random enemies
« Reply #6 on: February 21, 2022, 08:01:36 pm »
Hi, im back.

I tried that but the program does not work, just one asteorid appear. If you have time and want to help me with this last thing, I appreciate it!!

Have a good day.