I'm trying to learn C++, and I'm making this little space-invader-like game to get better at it.
Currently everything works just fine but for some reason the performance of the game is kind of terrible considering how simple the graphics are (the game runs smoothly for a while and then stops for like half a second, constantly).
I'm pretty sure the lag is due to the amount of bullets I'm removing and creating from the vector containing them (I'm removing the bullets that go outside the screen because there's no point in updating them or rendering them). So how could I fix this?
I checked the game loop and the problem isn't there.
Code below
player.h:
#pragma once
#include <vector>
#include <iostream>
#include "entity.h"
#include "SFML/Graphics.hpp"
#include "vector.h"
class Player : public Entity {
private:
int f_lastshot = 0;
const double shoot_delay = 0.5;
const float bullet_speed = 1, speed = 0.75;
std::vector<Entity> bullets;
sf::Clock *c;
sf::Sprite bullet_sprite;
public:
Player(Vector &pos, int w, int h, sf::Sprite &spr);
~Player();
void init();
void update();
void render(Window &win);
void shoot();
};
player.cpp:
#include "player.h"
Player::Player(Vector &pos, int w, int h, sf::Sprite &spr) : Entity() {
this->pos = pos;
this->sprite = spr;
this->width = w;
this->height = h;
float scale_x = (float)w / (float)spr.getTexture()->getSize().x;
float scale_y = (float)h / (float)spr.getTexture()->getSize().y;
sprite.setScale(scale_x, scale_y);
}
void Player::update() {
if (sf::Mouse::isButtonPressed(sf::Mouse::Left) || sf::Keyboard::isKeyPressed(sf::Keyboard::S)) {
if ((double)(f_lastshot / 60) > shoot_delay) // shoot only if enough time has passed
shoot();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) dir.setX(-speed); // move left
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) dir.setX(speed); // move right
else dir.setX(0); // stop if no button is being pressed
f_lastshot++;
pos = pos + dir; // add the direction to the position
for (Entity &e : bullets) {
e.update(); // update each bullet
if (e.getPos().getY() < 0) // if the bullet is outside the screen delete it
bullets.erase(bullets.begin());
}
}
void Player::render(Window &win) {
sprite.setPosition(pos.getX(), pos.getY()); // update the position of the sprite
win.render(sprite);
for (Entity &bullet : bullets)
bullet.render(win);
}
void Player::shoot() {
f_lastshot = 0;
bullets.push_back(Entity::Entity(Vector::Vector(pos.getX() + width / 2 - 8, pos.getY()), 16, 32, bullet_sprite, Vector::Vector(0,-bullet_speed)));
}
void Player::init() {
if (c == nullptr) c = new sf::Clock();
else c->restart();
dir.setX(0); dir.setY(0);
sf::Texture *bullet_texture = new sf::Texture();
if (!bullet_texture->loadFromFile("bullet.png"))
std::cerr << "Could not load bullet image" << std::endl;
bullet_sprite.setTexture(*bullet_texture);
}
Player::~Player() {
delete bullet_sprite.getTexture();
delete c;
}
entity.h:
#pragma once
#include <SFML/Graphics.hpp>
#include <iostream>
#include "vector.h"
#include "window.h"
class Entity {
protected:
Vector pos;
sf::Sprite sprite;
int width, height;
Vector dir; // the direction
public:
Entity();
Entity(Vector &pos, int w, int h, sf::Sprite &spr);
Entity(Vector pos, int w, int h, sf::Sprite &spr);
Entity(Vector pos, int w, int h, sf::Sprite &spr, Vector dir);
void update();
void render(Window &win);
bool collides(Entity &other) const;
public:
Vector getPos() const;
Vector getDirection() const;
sf::Sprite getSprite() const;
int getWidth() const;
int getHeight() const;
void setPos(Vector &pos);
void setWidth(int width);
void setHeight(int height);
void setSize(int width, int height);
void setSprite(sf::Sprite &sprite);
void setDirection(Vector dir);
};
entity.cpp:
#include "entity.h"
Entity::Entity() {}
Entity::Entity(Vector &pos, int w, int h, sf::Sprite &spr)
: pos(pos), width(w), height(h) {
this->sprite = spr;
float scale_x = (float)w/(float)spr.getTexture()->getSize().x;
float scale_y = (float)h/(float)spr.getTexture()->getSize().y;
sprite.setPosition(pos.getX(), pos.getY());
sprite.setScale(scale_x, scale_y);
}
Entity::Entity(Vector pos, int w, int h, sf::Sprite &spr)
: pos(pos), width(w), height(h) {
this->sprite = spr;
float scale_x = (float)w / (float)spr.getTexture()->getSize().x;
float scale_y = (float)h / (float)spr.getTexture()->getSize().y;
sprite.setPosition(pos.getX(), pos.getY());
}
Entity::Entity(Vector pos, int w, int h, sf::Sprite &spr, Vector dir)
: pos(pos), width(w), height(h) {
this->sprite = spr;
this->dir = dir;
float scale_x = (float)w / (float)spr.getTexture()->getSize().x;
float scale_y = (float)h / (float)spr.getTexture()->getSize().y;
sprite.setPosition(pos.getX(), pos.getY());
}
bool Entity::collides(Entity &other) const {
bool x_coll = (pos.getX() + width> other.getPos().getX() && pos.getX()< other.getPos().getX())||(pos.getX() < other.getPos().getX() + other.getWidth() && pos.getX() > other.getPos().getX());
bool y_coll = (pos.getY() + height > other.getPos().getY() && pos.getY() < other.getPos().getY()||(pos.getY() < other.getPos().getY() + other.getHeight() && pos.getY() > other.getPos().getY()));
return (x_coll && y_coll);
}
void Entity::render(Window &win) {
sprite.setPosition(pos.getX(), pos.getY());
win.render(sprite);
}
void Entity::update() {
pos = pos + dir;
}
Vector Entity::getPos() const { return pos; }
int Entity::getWidth() const { return width; }
int Entity::getHeight() const { return height; }
sf::Sprite Entity::getSprite() const { return sprite; }
Vector Entity::getDirection() const { return dir; }
void Entity::setPos(Vector &pos) { this->pos = pos; }
void Entity::setWidth(int width) { this->width = width; }
void Entity::setHeight(int height) { this->height = height; }
void Entity::setSprite(sf::Sprite &sprite) { this->sprite = sprite; }
void Entity::setDirection(Vector dir) { this->dir = dir; }
void Entity::setSize(int width, int height) {
this->width = width;
this->height = height;
}
To identify bottlenecks and other performance issues you should use a profiler. Only by looking the code it's hard if not impossible to find the correct places that need optimisation. But one problem is indeed the way you remove bullets:
for (Entity &e : bullets) {
e.update(); // update each bullet
if (e.getPos().getY() < 0) // if the bullet is outside the screen delete it
bullets.erase(bullets.begin());
}
There is a logic error. You check if the current bullet e is outside the screen, but then delete the first one anyway. This can't be right.
It's extremely inefficient to delete the first element of a std::vector, because all the following elements will have to be moved one place left. This happens for every bullet you remove!
It may cause undefined behavior. The reference says that erase
Invalidates iterators and references at or after the point of the erase, including the end() iterator.
Changing the size of a vector while iterating over it is usually a bad idea.
To overcome those issues you could use the Erase-Remove-Idiom like this:
// Update as usual
for (Entity &e : bullets) {
e.update();
}
// Erase-Remove bullets that are out of screen
bullets.erase(std::remove_if(bullets.begin(), bullets.end(),
[](const Entity &e){ return e.getY() < 0;}), bullets.end());
Related
I think I should write implementation for my Circle class, but I'm not sure and I don't know, how to transfer Color as function parameter in main bcz compiler doesn't work with sf::Color::Red or just Red as function parameter in main function
#include <SFML/Graphics.hpp>
using namespace sf;
const int APPLICATION_WIDTH = 400;
const int APPLICATION_HEIGHT = 300;
class Figure : public sf::Shape{
protected:
double m_x0 = 0, m_y0 = 0;
float m_angle = 0;
double m_scale;
public:
virtual ~Figure() {}
void setCoordX(double x0) { m_x0 = x0; }
void setCoordY(double y0) { m_y0 = y0; }
void setAngle(float angle) { m_angle = angle; }
void setScale(double scale) { m_scale = scale; }
double getCoordX() { return m_x0; }
double getCoordY() { return m_y0; }
float getAngle() { return m_angle; }
double getScale() { return m_scale; }
virtual void drawFigure(sf::RenderWindow& w) = 0;
//virtual void moveFigure(sf::RenderWindow& w, const double vx, const double vy) = 0;
void hideFigure(sf::RenderWindow& w);
virtual void rotateFigure(sf::RenderWindow& w) = 0;
//virtual void scaleFigure(const double vx, const double vy) = 0;
};
void Figure::rotateFigure(sf::RenderWindow& w) {
sf::Shape::rotate(m_angle);
}
void Figure::hideFigure(sf::RenderWindow& w) {
sf::Shape::setFillColor(sf::Color::Transparent);
}
class Circle : public Figure {
private:
CircleShape m_obj;
//double m_x1 , m_y1;
double m_radius = 1;
Vector2f getPoint(std::size_t index) const override {
return m_obj.getPoint(index);
}
std::size_t getPointCount() const override {
return m_obj.getPointCount();
}
public:
//void setCoordX1(double x1) { m_x1 = x1; }
void setRad(double r) { m_radius = r; }
double getRad() { return m_obj.getRadius(); }
double getCenterX() { return m_obj.getRadius(); }
double getCenterY() { return m_obj.getRadius(); }
void drawFigure(sf::RenderWindow& w);
//void moveFigure(sf::RenderWindow& w, const double vx, const double vy);
//void hideFigure(sf::RenderWindow& w) override;
void rotateFigure(sf::RenderWindow& w) override;
};
void Circle::drawFigure(sf::RenderWindow& w) {
m_obj.setRadius(m_radius);
m_obj.setPosition(m_x0, m_y0);
w.draw(m_obj);
}
//void Circle::hideFigure(sf::RenderWindow &w) {
// m_obj.setFillColor(sf::Color::Transparent);
//}
void Circle::rotateFigure(sf::RenderWindow& w) {
//m_obj.setFillColor(sf::Color::Magenta); // if I'll paint it here, it works
m_obj.rotate(m_angle);
}
int main()
{
RenderWindow window(VideoMode(APPLICATION_WIDTH, APPLICATION_HEIGHT), "Lab 6 using SFML");
Circle a, b, c, d;
a.setFillColor(sf::Color::Red);
b.setFillColor(sf::Color::Green);
//c.setFillColor(sf::Color::Blue);
//d.setFillColor(sf::Color::Magenta);
a.setRad(32);
a.setCoordX(50); a.setCoordY(34);
b.setRad(16);
b.setCoordX(10); b.setCoordY(34);
b.setAngle(45);
b.rotateFigure(window);
//b.hideFigure(window);
while (window.isOpen())
{
Event event;
while (window.pollEvent(event))
{
if (event.type == Event::Closed)
window.close();
}
//sf::CircleShape circle;
//circle.setRadius(50);
//circle.setFillColor(sf::Color::Green);
//circle.setPosition((APPLICATION_WIDTH - (circle.getRadius()*2))/2.0,
// (APPLICATION_HEIGHT - (circle.getRadius()*2))/2.0);
//circle.move(30, 30);
//window.draw(circle);
a.drawFigure(window);
b.drawFigure(window);
window.display();
}
return 0;
}
sf::Color is a class that can be passed as a parameter. You can read how sf::Color works in the documentation: https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Color.php
Example:
#include <SFML/Graphics.hpp>
class MyCircle
{
public:
MyCircle()
{
mCircle.setRadius(50.f);
}
void setColor(sf::Color color)
{
mCircle.setFillColor(color);
}
void draw(sf::RenderWindow& window)
{
window.draw(mCircle);
}
private:
sf::CircleShape mCircle;
};
int main()
{
sf::RenderWindow window(sf::VideoMode(200, 200), "SFML works!");
MyCircle circle;
circle.setColor(sf::Color::Red);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
circle.draw(window);
window.display();
}
return 0;
}
I am having 12 errors looping from:
Unexpected token(s) preceding ';'
missing type specifier - int assumed, Note: C++ does not support default - int
syntax error: missing ';' before '*
at Line 11 of Player.h
Here is the player.h code:
#pragma once
#include "GameObjects.h"
using namespace sf;
class Player : GameObject2D
{
public:
// Classes
GameSprite* playerSprite = new GameSprite("Textures/player.png", Vector2f(32.f, 32.f), IntRect(0, 0, 64, 64));
PlayerAnimationPlayer* anim = new PlayerAnimationPlayer(this, "idle", 0.5f, 0, 2, "", true);
// Sprite and animation variables
IntRect spriteFrames[3] = {
IntRect(0, 0, 64, 64),
IntRect(64, 0, 64, 64),
IntRect(128, 0, 64, 64),
};
// Movement variables
sf::Vector2f direction;
sf::Vector2f velocity;
const float speed = 300.f;
// Player functions
void inputDirection();
// Game Object functions
void update(float deltaTime) override;
void inputEvent(Event ev) override;
void draw(RenderWindow& target) override;
void free() override;
// 2D Functions
void setPos(float x, float y) override;
Vector2f moveObj(Vector2f direction, float speed, float deltaTime);
};
class PlayerAnimationPlayer : AnimationPlayer
{
public:
Player* parent;
PlayerAnimationPlayer(Player* newParent, std::string startAnimation,
float newSpeed, int startIndex, int newMaxIndex, bool isOneShot, bool autoStart);
void animation(int index) override;
};
Here is the game object code (INCLUDING THE ANIMATION PLAYER):
#pragma once
#include <SFML\Graphics.hpp>
#include <map>
#include <iostream>
// BASE OBJECT OF ALL GAME OBJECTS
class GameObject
{
public:
virtual void update(float deltaTime) {}
virtual void inputEvent(sf::Event ev) {}
virtual void draw(sf::RenderWindow& target) {} // This draws all drawable objects
virtual void free() {} // This frees all pointer variables including the class itself
};
// GAMEOBJECT USED FOR 2D AND MOVEMENT
class GameObject2D : GameObject
{
public:
sf::Vector2f position;
sf::Vector2f getPos(); // Returns the position
virtual void setPos(float x, float y); // This moves all child classes including the class itself
virtual sf::Vector2f moveObj(sf::Vector2f direction, float speed, float deltaTime); // Moves all child classes by direction
};
// Class for sprites
class GameSprite
{
public:
sf::Sprite sprite;
sf::Texture texture;
GameSprite(std::string texturePath, sf::Vector2f offset, sf::IntRect visibleFrame);
void setSprite(std::string newTexturePath, sf::Vector2f offset = sf::Vector2f(0.f, 0.f), sf::IntRect visibleFrame = sf::IntRect(0, 0, -1, -1));
};
// THIS ANIMATES THE OBJECTS
class AnimationPlayer
{
public:
int maxIndex = 10;
sf::Clock clock;
std::string activeAnimation;
void updateTime(); // Updates the time
public:
bool isActive;
bool oneShot;
float speed = 1.f;
int index = 0;
AnimationPlayer(std::string startAnimation, float newSpeed, int startIndex, int newMaxIndex, bool isOneShot = false, bool autoStart = false);
void start(std::string animation, int startIndex = 0); // Starts the animation player
void stop(); // Stops the animation player
virtual void animation(int index) {}
};
Here is the cpp file for animation player
#include "GameObjects.h"
// CONSTRUCTOR AND MAIN LOGIC
AnimationPlayer::AnimationPlayer(std::string startAnimation, float newSpeed, int startIndex, int newMaxIndex, bool isOneShot, bool autoStart)
{
activeAnimation = startAnimation;
speed = newSpeed;
index = startIndex;
maxIndex = newMaxIndex;
oneShot = isOneShot;
isActive = autoStart;
std::cout << "Constructor called" << std::endl;
if (oneShot)
start(startAnimation, startIndex);
}
// Updates the time
void AnimationPlayer::updateTime()
{
if (clock.getElapsedTime().asSeconds() > speed)
{
if (!isActive)
return;
if (index > maxIndex)
{
if (oneShot)
isActive = false;
else
index = 0;
}
else
{
index += 1;
}
animation(index);
}
}
/// STARTS AND STOP THE ANIMATION PLAYER
// Starts the animation player
void AnimationPlayer::start(std::string animation, int startIndex)
{
isActive = true;
index = startIndex;
}
// Stops the animation player
void AnimationPlayer::stop()
{
isActive = false;
}
Here is the code for the player animation player cpp
#include "Player.h"
PlayerAnimationPlayer::PlayerAnimationPlayer(Player* newParent, std::string startAnimation, float newSpeed, int startIndex, int newMaxIndex, bool isOneShot, bool autoStart) :
AnimationPlayer(startAnimation, newSpeed, startIndex, newMaxIndex, isOneShot, autoStart)
{
parent = newParent;
}
void PlayerAnimationPlayer::animation(int index)
{
parent->playerSprite->sprite.setTextureRect(parent->spriteFrames[index]);
}
Ty!
I have a class named Particle and i want to create a vector of particles. The problem is that my code is not drawing the objects stored in the vector. I tried different implementations, the first one was:
std::vector<Particle> particles(10);
and then i filled the vectors:
for(size_t i = 0; i < 10; ++i) particles.emplace_back(new Particle(false));
for drawing i used:
for(const auto& x: particles) window.draw(x);
the problem is that this is drawing just one object. Then i tried to create a class of particles container with a vector of shared_ptr:
std::vector<std::shared_ptr<Particle>> particle_container;
And the problem is the same, it just draws one particle, i don't know if theres something wrong with my implementation of the class particle or something wrong in the waay i use the draw function of sfml. This is my Particle class:
#include "SFML/Graphics.hpp"
#include <random>
#include <cmath>
#include <limits>
#include <memory>
using namespace sf;
class Particle : public sf::Drawable{
sf::CircleShape particle;
Vector2f position; //current position of the particle
Vector2f velocity; //velocity of the particle
double radius; //radius of each particle
double mass; //mass of the particle
int count; //number of collisions of each particle
bool sick; //if the particle is infected
Color color; //color of the particle
public:
//constructors
Particle(Vector2f _position, Vector2f _velocity, double _radius, double _mass, bool _sick);
Particle(bool sick);
}:
the Particle.cpp:
#include "Particle.h"
template<class T>
double Rand(T first, T last) {
std::random_device dev;
std::default_random_engine eng(dev());
std::uniform_real_distribution<double> dis(first, last);
return dis(eng);
}
Particle::Particle(Vector2f _position, Vector2f _velocity, double _radius, double _mass, bool _sick) {
position = _position;
velocity = _velocity;
radius = _radius;
mass = _mass;
sick = _sick;
if(sick) color = sf::Color::Green;
else color = sf::Color::Red;
//giving values to the particle
particle.setPosition(position);
particle.setRadius(radius);
particle.setFillColor(color);
}
Particle::Particle(bool sick) {
position.x = Rand(0.0, 550.0);
position.y = Rand(0.0, 550.0);
velocity.x = Rand(-0.005, 0.005);
velocity.y = Rand(-0.005, 0.005);
radius = 2.0;
mass = 0.5;
if(sick) color = sf::Color::Green;
else color = sf::Color::Red;
particle.setPosition(position);
particle.setRadius(radius);
particle.setFillColor(color);
}
And this is the particles_container
class particles_container : public sf::Drawable {
std::vector<std::shared_ptr<Particle>> particle_container;
//std::vector<Particle> particle_container;
virtual void draw(sf::RenderTarget &renderTarget, sf::RenderStates renderStates) const;
public:
//default constructor
particles_container();
//constructor with a number of given particles
explicit particles_container(size_t size);
//default destructor
~particles_container() override;
//returns the container
std::vector<std::shared_ptr<Particle>> getContainer();
//another function to draw
void drawContainer(sf::RenderWindow &window);
};
the particles_container.cpp:
void particles_container::draw(RenderTarget &renderTarget, sf::RenderStates renderStates) const {
for (const auto & i : particle_container) {
renderTarget.draw(*i);
}
}
particles_container::particles_container(size_t size) {
if(size > 0){
for(size_t i = 0; i < size; ++i) particle_container.emplace_back(new Particle(false));
}
}
std::vector<std::shared_ptr<Particle>> particles_container::getContainer() {
return particle_container;
}
void particles_container::drawContainer(RenderWindow &window) {
for (auto & i : particle_container) {
window.draw(*i);
}
}
and finally this is my main.cpp:
#include "Particle.h"
#include "particles_container.h"
int main() {
sf::RenderWindow window(sf::VideoMode (600, 600), "PF");
std::vector<Particle> particles(10);
for(size_t i = 0; i < 10; ++i) particles.emplace_back(new Particle(false));
Particle p1(sf::Vector2f(45.0,45.0), sf::Vector2f(0.01,0.005), 2, 0.5, false);
particles_container parts(100);
Particle p3(true);
std::vector<std::shared_ptr<Particle> > my_vector(10);
for(size_t i = 0; i < 10; ++i) my_vector.emplace_back(new Particle(false));
while(window.isOpen()){
sf::Event event;
while(window.pollEvent(event)){
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
}
}
window.clear();
//parts.drawContainer(window);
//window.draw(parts);
for(const auto& x: particles) window.draw(x);
window.display();
}
}
this is the output:
output
Any help or advice would be greatly appreciated, thanks.
I am using:
SFML 2.5.1 and c++17
Your Rand function constructs a new random device every time, which has a high probability to always return the same number sequence. Move the construction of dev out of your Rand function.
I have some code that when compiled, runs an Asteroid Game. I want to make some changes. In place of sprites for the ship, I would like to use trianges. For a bullet, I'd like to use a small rectangle, and finally, a polygon for the asteroids. The code uses an Entity Master Class with a list. Can somebody please elaborate on how to make these changes?
#include <SFML/Graphics.hpp>
#include <time.h>
#include <list>
using namespace sf;
const int W = 1200;
const int H = 800;
float DEGTORAD = 0.017453f;
class Animation
{
public:
float Frame, speed;
Sprite sprite;
std::vector<IntRect> frames;
Animation(){}
Animation (Texture &t, int x, int y, int w, int h, int count, float Speed)
{
Frame = 0;
speed = Speed;
for (int i=0;i<count;i++)
frames.push_back( IntRect(x+i*w, y, w, h) );
sprite.setTexture(t);
sprite.setOrigin(w/2,h/2);
sprite.setTextureRect(frames[0]);
}
void update()
{
Frame += speed;
int n = frames.size();
if (Frame >= n) Frame -= n;
if (n>0) sprite.setTextureRect( frames[int(Frame)] );
}
bool isEnd()
{
return Frame+speed>=frames.size();
}
};
class Entity
{
public:
float x,y,dx,dy,R,angle;
bool life;
std::string name;
Animation anim;
Entity()
{
life=1;
}
void settings(Animation &a,int X,int Y,float Angle=0,int radius=1)
{
anim = a;
x=X; y=Y;
angle = Angle;
R = radius;
}
virtual void update(){};
void draw(RenderWindow &app)
{
anim.sprite.setPosition(x,y);
anim.sprite.setRotation(angle+90);
app.draw(anim.sprite);
CircleShape circle(R);
circle.setFillColor(Color(255,0,0,170));
circle.setPosition(x,y);
circle.setOrigin(R,R);
//app.draw(circle);
}
virtual ~Entity(){};
};
class asteroid: public Entity
{
public:
asteroid()
{
dx=rand()%8-4;
dy=rand()%8-4;
name="asteroid";
}
void update()
{
x+=dx;
y+=dy;
if (x>W) x=0; if (x<0) x=W;
if (y>H) y=0; if (y<0) y=H;
}
};
class bullet: public Entity
{
public:
bullet()
{
name="bullet";
}
void update()
{
dx=cos(angle*DEGTORAD)*6;
dy=sin(angle*DEGTORAD)*6;
// angle+=rand()%7-3; /*try this*/
x+=dx;
y+=dy;
if (x>W || x<0 || y>H || y<0) life=0;
}
};
class player: public Entity
{
public:
bool thrust;
player()
{
name="player";
}
void update()
{
if (thrust)
{ dx+=cos(angle*DEGTORAD)*0.2;
dy+=sin(angle*DEGTORAD)*0.2; }
else
{ dx*=0.99;
dy*=0.99; }
int maxSpeed=15;
float speed = sqrt(dx*dx+dy*dy);
if (speed>maxSpeed)
{ dx *= maxSpeed/speed;
dy *= maxSpeed/speed; }
x+=dx;
y+=dy;
if (x>W) x=0; if (x<0) x=W;
if (y>H) y=0; if (y<0) y=H;
}
};
bool isCollide(Entity *a,Entity *b)
{
return (b->x - a->x)*(b->x - a->x)+
(b->y - a->y)*(b->y - a->y)<
(a->R + b->R)*(a->R + b->R);
}
int main()
{
srand(time(0));
RenderWindow app(VideoMode(W, H), "Asteroids!");
app.setFramerateLimit(60);
Texture t1,t2,t3,t4,t5,t6,t7;
t1.loadFromFile("images/spaceship.png");
t2.loadFromFile("images/background.jpg");
t3.loadFromFile("images/explosions/type_C.png");
t4.loadFromFile("images/rock.png");
t5.loadFromFile("images/fire_blue.png");
t6.loadFromFile("images/rock_small.png");
t7.loadFromFile("images/explosions/type_B.png");
t1.setSmooth(true);
t2.setSmooth(true);
Sprite background(t2);
Animation sExplosion(t3, 0,0,256,256, 48, 0.5);
Animation sRock(t4, 0,0,64,64, 16, 0.2);
Animation sRock_small(t6, 0,0,64,64, 16, 0.2);
Animation sBullet(t5, 0,0,32,64, 16, 0.8);
Animation sPlayer(t1, 40,0,40,40, 1, 0);
Animation sPlayer_go(t1, 40,40,40,40, 1, 0);
Animation sExplosion_ship(t7, 0,0,192,192, 64, 0.5);
std::list<Entity*> entities;
for(int i=0;i<15;i++)
{
asteroid *a = new asteroid();
a->settings(sRock, rand()%W, rand()%H, rand()%360, 25);
entities.push_back(a);
}
player *p = new player();
p->settings(sPlayer,200,200,0,20);
entities.push_back(p);
/////main loop/////
while (app.isOpen())
{
Event event;
while (app.pollEvent(event))
{
if (event.type == Event::Closed)
app.close();
if (event.type == Event::KeyPressed)
if (event.key.code == Keyboard::Space)
{
bullet *b = new bullet();
b->settings(sBullet,p->x,p->y,p->angle,10);
entities.push_back(b);
}
}
if (Keyboard::isKeyPressed(Keyboard::Right)) p->angle+=3;
if (Keyboard::isKeyPressed(Keyboard::Left)) p->angle-=3;
if (Keyboard::isKeyPressed(Keyboard::Up)) p->thrust=true;
else p->thrust=false;
for(auto a:entities)
for(auto b:entities)
{
if (a->name=="asteroid" && b->name=="bullet")
if ( isCollide(a,b) )
{
a->life=false;
b->life=false;
Entity *e = new Entity();
e->settings(sExplosion,a->x,a->y);
e->name="explosion";
entities.push_back(e);
for(int i=0;i<2;i++)
{
if (a->R==15) continue;
Entity *e = new asteroid();
e->settings(sRock_small,a->x,a->y,rand()%360,15);
entities.push_back(e);
}
}
if (a->name=="player" && b->name=="asteroid")
if ( isCollide(a,b) )
{
b->life=false;
Entity *e = new Entity();
e->settings(sExplosion_ship,a->x,a->y);
e->name="explosion";
entities.push_back(e);
p->settings(sPlayer,W/2,H/2,0,20);
p->dx=0; p->dy=0;
}
}
if (p->thrust) p->anim = sPlayer_go;
else p->anim = sPlayer;
for(auto e:entities)
if (e->name=="explosion")
if (e->anim.isEnd()) e->life=0;
if (rand()%150==0)
{
asteroid *a = new asteroid();
a->settings(sRock, 0,rand()%H, rand()%360, 25);
entities.push_back(a);
}
for(auto i=entities.begin();i!=entities.end();)
{
Entity *e = *i;
e->update();
e->anim.update();
if (e->life==false) {i=entities.erase(i); delete e;}
else i++;
}
//////draw//////
app.draw(background);
for(auto i:entities) i->draw(app);
app.display();
}
return 0;
}
it is not really relevant question to be asked here,
but take a look at this part of code:
Texture t1,t2,t3,t4,t5,t6,t7;
t1.loadFromFile("images/spaceship.png");
t2.loadFromFile("images/background.jpg");
t3.loadFromFile("images/explosions/type_C.png");
t4.loadFromFile("images/rock.png");
t5.loadFromFile("images/fire_blue.png");
t6.loadFromFile("images/rock_small.png");
t7.loadFromFile("images/explosions/type_B.png");
Hey I am trying to make this space game. Now I have developed my ship, and am able to display it. However I would like to be able to use the class for more than one object. I can do this with a constructor but have no clue how to get a constructor working, what changes would I need to make to my code to make the object take an int value as a constructor and allow me to make multiple ships with the code by calling the object.
Here is my header file.
//
// Ship.hpp
// Zerg_Invasion
//
// Created by Flik Wolf on 11/9/15.
//
//
#ifndef Ship_h
#define Ship_h
#include <stdio.h>
#include "ofMain.h"
class Ship {
public:
// Constructor
Ship();
// Methods
void moveLeft();
void moveRight();
void load();
void draw();
void fire();
void keyPressed();
// Properties
int x;
int y;
ofColor color;
ofImage cat;
};
#endif
and here is my CPP file.
//
// Ship.cpp
// Zerg_Invasion
//
// Created by Flik Wolf on 11/9/15.
//
//
#include "Ship.h"
Ship::Ship() {
// Set the initial color
//color.set( ofRandom(255), ofRandom(255), ofRandom(255));
// Initial x position of the ball
x = 450;
// Initial y position of the ball
y = 200;
}
void Ship::moveLeft() {
x -= 10;
}
void Ship::moveRight() {
x += 10;
}
void Ship::load() {
cat.load("spaceShip.png");
}
void Ship::draw() {
cat.draw(x, y);
// ofCircle(x, y, 30);
}
void Ship::fire() {
ofSetColor(255, 255, 255);
ofCircle(x, 200, 2);
}
Also here is the .h and .cpp files for Openframeworks which I am using for graphics.
#pragma once
#include "ofMain.h"
#include "Ship.h"
class ofApp : public ofBaseApp {
public:
void setup();
void update();
void draw();
void keyPressed(int key);
void keyReleased(int key);
void mouseMoved(int x, int y);
void mouseDragged(int x, int y, int button);
void mousePressed(int x, int y, int button);
void mouseReleased(int x, int y, int button);
void mouseEntered(int x, int y);
void mouseExited(int x, int y);
void windowResized(int w, int h);
void dragEvent(ofDragInfo dragInfo);
void gotMessage(ofMessage msg);
Ship theShip;
};
#include "ofApp.h"
//--------------------------------------------------------------
void ofApp::setup() {
// Smooth edges
ofEnableSmoothing();
// Fixed framerate
ofSetFrameRate(120);
theShip.load();
// No need to define the initial position of the ball
// because the Ball constructor does it for you
}
//--------------------------------------------------------------
void ofApp::update() {
// theShip.move();
}
//--------------------------------------------------------------
void ofApp::draw() {
ofBackground(0);
std::vector <int> nums;
nums.push_back(0);
nums.push_back(1);
nums.push_back(3);
nums.push_back(4);
nums.push_back(5);
nums.push_back(6);
nums.push_back(7);
nums.push_back(8);
cout << nums[0] << endl;
cout << nums[1] << endl;
theShip.draw();
}
//--------------------------------------------------------------
void ofApp::keyPressed(int key) {
if (key == 'a')
{
theShip.moveLeft();
}
if (key == 'd')
{
theShip.moveRight();
}
}
//--------------------------------------------------------------
void ofApp::keyReleased(int key) {
}
//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y) {
}
//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button) {
}
//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button) {
theShip.fire();
}
//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button) {
}
//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y) {
}
//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y) {
}
//--------------------------------------------------------------
void ofApp::windowResized(int w, int h) {
}
//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg) {
}
//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo) {
}
As mentioned, the constructor in your case should only produce one ship, and this should be the case with all object constructors.
However, it's still easy enough to create and maintain multiple ships (as you've implemented them) if you use a container like std::vector.
Containing multiple ships:
To create a container for your ships, you can use a vector like so:
std::vector<Ship> Ships;
Adding new ships:
To add additional ships to it, you can use std::vector::push_back():
Ships.push_back(Ship()); //Adds a new ship to 'Ships'
Updating the ships:
There are a couple of ways to cycle through your ships:
for (auto& i : Ships)
i.Update(); //Some updating function for each ship
Or, if you need to keep track of the specific position of each ship inside the vector:
for (unsigned int i = 0; i < Ships.size(); ++i)
Ships[i].Update() //The same updating function
How about to get the x and y as constructor argument?
in the .h file:
struct Ship {
// Constructor
Ship(int _x = 450, int _y = 200);
// ...
in the cpp file:
Ship::Ship(int _x, int _y) : x{_x}, y{_y} {
// Set the initial color
//color.set( ofRandom(255), ofRandom(255), ofRandom(255));
}