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));
}
Related
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());
I have tried to make it look like falling snow but snow is coming from everywhere and I have to make falling snow from library graphics.h. I don't know how to make it move but I already add x and y. For int speed I don't know to use it I see from other code which is from movement of the ball. This code is for my assignment, can you guys help me?
#include <graphics.h>
class Snow
{
private:
int x,y;
int color;
int speed;
public:
Snow(int _x, int _y, int _color, int _speed)
{
x=_x;
y=_y;
color=_color;
speed=_speed;
}
int getColor()
{
return color;
}
int getSpeed()
{
return speed;
}
int getX()
{
return x;
}
int getY()
{
return y;
}
void setX(int value)
{
//x=rand();
x=value;
}
void setY (int value)
{
//y=rand();
y=value;
}
void setColor(int value)
{
color=value;
}
void setSpeed(int value)
{
speed=value;
}
void draw()
{
setcolor(color);
setfillstyle(SOLID_FILL,color);
fillellipse(x,y,2,2);
}
void undraw()
{
setcolor(BLACK);
fillellipse(x,y,2,2);
}
void move()
{
undraw();
x=rand() % getmaxwidth();
y=rand() % getmaxheight();
draw();
}
};
int main()
{
int screenWidth = getmaxwidth();
int screenHeight = getmaxheight();
initwindow(screenWidth, screenHeight, "Snowy day");
Snow p1(0,0,WHITE,10);
while (!kbhit())
{
delay(100);
p1.move();
}
return 0;
}
You should use speed in move function. For example:
void move() {
undraw();
x=rand() % getmaxwidth();
// fall
y += speed;
draw();
}
I have a sample of code for some framework that I am using. For ease of reading I have separated blocks of code from different files.
Tile.h
class Tile
{
public:
Tile(int row, int column);
~Tile();
void Draw();
void Update(double delta);
void SetValue(int value);
int GetValue();
private:
//Member variables
Label* m_ValueLabel;
int m_Value = 0;
int m_Row;
int m_Column;
float duration = 0.0f;
float xScale = 0;
float yScale = 0;
};
Tile.cpp
void Tile::SetValue(int value)
{
//Set the Tile's value
m_Value = value;
//if (m_Value != EMPTY_TILE)
{
//Update the Tile's Label
m_ValueLabel->SetText(to_string(m_Value));
}
/*else
{
m_ValueLabel->SetText("");
}*/
}
Game.h
class Game
{
public:
Game(); //Constructor
~Game(); //Destructor
void Update(double delta);
void Draw();
void HandleLeftMouseClick(float mouseX, float mouseY);
void HandleRightMouseClick(float mouseX, float mouseY);
void HandleKeyPress(Keyboard::Key key);
bool isBoardFull();
int GetEmptyTileIndex();
void SpawnTile();
void RandomizeSeed();
int RandomRange(int min, int max);
private:
//Member variables should go here
Tile* m_Tiles[NUM_TILES];
bool isTileFull[16] = {};
int TileValueArray[5] = { 2,2,2,4,4 };
};
Game.cpp
void Game::SpawnTile()
{
RandomizeSeed();
int meaningfulVariableName = GetEmptyTileIndex();
if (meaningfulVariableName != -1)
{
int RandomNumber = RandomRange(0,4);
int TileValue = TileValueArray[RandomNumber];
}
else if (meaningfulVariableName == -1)
{
//Does nothing
}
}
What I need to do with this is make it so that the TileValue created in Game.cpp can be passed to the SetValue function in Tile.cpp because the value created is required for m_Value in SetValue.
The majority of code present can probably be ignored, and anything commented out should not effect how I need the code to function at the moment. There is nothing wrong with the framework outside of these files, as I have used it several times before.
I know there are easier ways to do this, but this is how I am required to do it. I would really appreciate if someone could help me with this because anything that I have found online has not helped. If you think that you may need more code for clarification on something, please do not hesitate to ask. Also, in case it helps I am using this to create a 2048 clone.
You have a function Game::GetEmptyTileIndex which will return an index. You can use that index to set Game::m_Tiles by changing the Game::SpawnTile function like so:
// Game.cpp
void Game::SpawnTile()
{
RandomizeSeed();
// Returns a tile index.
int meaningfulVariableName = GetEmptyTileIndex();
if (meaningfulVariableName != -1)
{
int RandomNumber = RandomRange(0,4);
int TileValue = TileValueArray[RandomNumber];
// Set the value for the tile at the required index.
m_Tiles[meaningfulVariableName]->SetValue(TileValue);
}
else if (meaningfulVariableName == -1)
{
//Does nothing
}
}
Hi im doing little project tomy school and keep getting weird for me error.
While calling one of methods in my object this pointer is set to 0xcdcdcdcd. i googled it and found some info about erasing memory or destroing objects before calling, but i make sure no destructors are called before.
World.h
class Organism;
class Human;
class World
{
private:
vector <Organism*> organisms;
vector <Organism*> organismsToAdd;
vector <string> logs;
int turn_;
void initializeWorld();
void drawInterface();
void drawInfo();
void drawOrganisms();
void nextTurn();
bool isPositionTaken(int x, int y);
Organism* getOrganism(int x, int y);
void queueOrganismToAdd(Organism* newOrganism);
void addQueuedOrganisms();
void generateStartOrganisms();
bool isPlayerAlive();
public:
void executeMove(Organism* moving, int toX, int toY); //here's the problem
bool isPositionValid(int x, int y);
World(int x, int y);
struct
{
int x_, y_;
} worldSize;
void startGame();
~World();
};
executeMove
void World::executeMove(Organism* moving, int toX, int toY)
{
cout << moving->getSign();
getch();
if (!isPositionTaken(toX, toY)) // <- here it brake
{
moving->setPosition(toX, toY);
}
else if (moving->getSign() == getOrganism(toX, toY)->getSign())
{
//multiply
//make log
}
else {
if (!moving->specialCollision((getOrganism(toX, toY)))) return;
if (!getOrganism(toX, toY)->specialCollision(moving)) return;
if (moving->getPower() >= getOrganism(toX, toY)->getPower())
{
//log
//delete losser
}
else
{
//log
//delete losser
}
moving->setPosition(toX, toY);
}
}
isPositioinTaken
bool World::isPositionTaken(int x, int y)
{
for (int i = 0; i < this->organisms.size(); ++i) // here this is set to 0xcdcdcdcd
{
if (organisms[i]->getPositionX() == x && organisms[i]->getPositionY() == y) return true;
}
return false;
}
Method isPositionTaken is worlking well in other parts of project so im totally lost if finding whats wrong, i aprreciate any help
Since the organisms member has a default constructor, the only way to see this behavior at the line you indicated is if the call to executeMove() was using a pointer which was uninitialized.
Something like:
World *ptr; // not initialized on stack
...
ptr->executeMove();
Or this method was called from another method with the same problem.
Help me understand how to use the superclass variable in the subclass and being able to make changes to it, I am making a clone Space Invaders 2D game using SDL library
First off I have a Rectangle class that is inherited from SDL_Rect looking like this i'll leave the unimportant parts out
//Rectangle.h
namespace galaxy{
struct Rectangle : public SDL_Rect{
Rectangle();
Rectangle(int xx, int yy, int hh, int ww);
Rectangel centeredRect(int width, int height) const
bool overlaps(const Rectangle& other) const;
};
}
I'll leave the .cpp out because it's fairly easy to see what part the rectangel is playing in this and I don't want to bore you guys out,
Then I have a Sprite class that is the superclass of the figures in the game,
namespace galaxy{
class Sprite{
public:
virtual void draw() = 0;
virtual ~Sprite();
virtual void keyLeft(SDLKey k);
virtual void keyRight(SDLKey k);
......more buttons
protected:
Sprite(int x, int y, int h, int w);
private:
Rectangle rect;
Sprite (const Sprite&);
const Sprite& operator=(const Sprite&);
};
}
In the .cpp file I have the following code
namespace galaxy{
Sprite::Sprite{int x, int y, int h , int w) : rect (x, y, h, w){}
Sprite::~Sprite(){}
const Rectangel& Sprite::getRect() const{
return rect;
}
void Sprite::keyLeft(SDLKey k){}
void Sprite::keyRight(SDLKey k){}
void Sprite::keyDown(SDLKey k){}
...more buttons
}
Then to where the problem is, I have another class Ship, where I want to overload the keyLeft from the superclass, having the rectangle rect following with coordinates, and I need to change the x, and y in the subclass, but when doing so with the construction I have below the r.x++; is behaving as inside a function and the changes to rectangle x is being cleared when exiting the function, when trying to reach rect in Ship class I get error not reachable, when getting rect through r = getRect(); the change to r is only inside the function but the ship does not move on screen.
//Ship.h
namespace galaxy {
class Ship : public Sprite{
public:
void keyLeft(SDLKey k);
void keyRight(SDLKey k);
void keyDown(SDLKey k);
void keyUp(SDLKey k);
void space(SDLKey k);
Ship(int x, int y, int hits);
private:
SDL_Surface* ship;
int hits;
};
}
//Ship.cpp
using namespace std;
namespace galaxy{
Rectangel r;
Ship::Ship(int x, int y, int hits) : Sprite(x, y, NULL, NULL), hits(hits){
ship = IMG_Load("Ship.png");
}
//Here is where my problem is.....
void Ship::keyLeft(SDLKey k){
std::cout << "Im here!\n";
std::cout << r.getX(); // outputs 250
r.x--;
std::cout << r.getX(); // outputs 251
}
void Ship::keyRight(SDLKey k){
std::cout << "Im here!\n";
}
void Ship::keyDown(SDLKey k){
std::cout << "Im here!\n";
}
void Ship::keyUp(SDLKey k){
std::cout << "Im here!\n";
}
void Ship::space(SDLKey k){
std::cout << "Im here!\n";
}
void Ship::draw(){
r = getRect();
SDL_BlitSurface(ship, NULL, sys.screen, &r);
}
}
right now Im doing this like:
#ifndef SHIP_H
#define SHIP_H
#include "Sprite.h"
#include <string>
namespace galaxy {
class Ship : public Sprite{
public:
/*static Ship* getInstance(int x, int y, int hits);*/
void draw();
//virtual void perform() = 0;
/*int getHits() const;*/
int getX() const;
int getY() const;
const Rectangel& getRect() const;
void keyLeft(SDLKey k);
void keyRight(SDLKey k);
void keyDown(SDLKey k);
void keyUp(SDLKey k);
void space(SDLKey k);
//~Ship();
//protected:
Ship(int x, int y, int hits);
private:
SDL_Surface* ship;
int hits;
Rectangel rect;
};
}
#endif
#include "Ship.h"
#include "Globals.h"
#include "Sprite.h"
#include <SDL_image.h>
#include <iostream>
using namespace std;
namespace galaxy{
Rectangel r;
/*Ship* Ship::getInstance(int x, int y, int hits){
return new Ship(x, y, hits);
}*/
Ship::Ship(int x, int y, int hits) : Sprite(x, y, NULL, NULL), hits(hits){
ship = IMG_Load("Ship.png");
}
const Rectangel& Ship::getRect() const{
return rect;
}
void Ship::keyLeft(SDLKey k){
std::cout << "Im here!\n";
rect.x--;
}
void Ship::keyRight(SDLKey k){
std::cout << "Im here!\n";
}
void Ship::keyDown(SDLKey k){
std::cout << "Im here!\n";
}
void Ship::keyUp(SDLKey k){
std::cout << "Im here!\n";
}
void Ship::space(SDLKey k){
std::cout << "Im here!\n";
}
/* int Ship::getHits() const{
return hits;
}*/
int Ship::getX() const{
return r.getX();
}
int Ship::getY() const{
return r.getY();
}
void Ship::draw(){
r = getRect();
SDL_BlitSurface(ship, NULL, sys.screen, &r);
}
}
But this is just a workaround so that I'm not stuck forever.
If you do something like this:
Rectangel r;
...
r = getRect();
Then you'll just get the original rectangle copied to your r variable, when you change it, the original Rectangel wont change.
Maybe for quick fix you could implement setRect function to your Ship class to be actually able to write back modified data.
void Ship::setRect(const Rectangel& rr) { rect = rr; }
And if you want to reach the rect from the Sprite base class directly then you need to give the rect protected visibility like so:
class Sprite
{
[...]
protected:
Rectangle rect;
[...]
};