I am currently stuck trying to create a grid for my controllable snake to move around. Currently. I am using a resolution of 1024x768 and would like the snake to move between a 16x16 grid (64x48 resolution)
So far the snake just moves pixel by pixel at a set speed.
I'll paste the .cpp and .hpp files below which i think are relevant to where i need to implement the code. If anyone could provide any suggestions/code that would be great!
snake.cpp
#include "snake.hpp"
#include <cstdlib>
void Snake::move()
{
switch(direction_){
case Direction::North:
position_.y += 1;
break;
case Direction::East:
position_.x += 1;
break;
case Direction::South:
position_.y -= 1;
break;
case Direction::West:
position_.x -= 1;
}
if (position_.x < 0) position_.x = 63; else if (position_.x > 63) position_.x = 0;
if (position_.y < 0) position_.y = 47; else if (position_.y > 47) position_.y = 0;
}
void Snake::render(prg::Canvas& canvas) const
{
canvas.drawCircle(getPosition().x * 16, getPosition().y * 16,16,prg::Colour::WHITE);
}
void Snake::changeDirection(Direction new_direction)
{
direction_ = new_direction;
}
snake.hpp
#if !defined SNAKE_HPP
#define SNAKE_HPP
#include <prg_interactive.hpp>
enum class Direction {
North = 1, East, South, West
};
struct Position final {
int x{0}, y{0};
Position(int ix, int iy) : x{ix}, y{iy} {}
};
class Snake {
public:
virtual ~Snake() {}
virtual void move();
void render(prg::Canvas& canvas) const;
void changeDirection(Direction new_direction);
const Position& getPosition() const {return position_;}
void setPosition(const Position& position){ position_ = position;}
private:
Direction direction_ {Direction::North};
Position position_ {0,0};
};
class PlayerSnake : public Snake,
public prg::IKeyEvent {
public:
PlayerSnake();
virtual ~PlayerSnake();
bool onKey(const prg::IKeyEvent::KeyEvent& key_event) override;
};
#endif // SNAKE_HPP
play_state.cpp
#include "play_state.hpp"
#include "ai_snake.hpp"
#include "player_snake.hpp"
#include <iostream>
const size_t MaxShapes {5};
const unsigned int MaxScale {5};
bool PlayState::onCreate()
{
snakes_.push_back(new AISnake);
snakes_.back()->setPosition(Position(100,100));
snakes_.push_back(new PlayerSnake);
snakes_.back()->setPosition(Position(50,50));
double x, y;
for(unsigned shape = 0;shape < MaxShapes;shape++)
{
x = (double)(rand() % prg::application.getScreenWidth());
y = (double)(rand() % prg::application.getScreenHeight());
shapes_.push_back(Square({x, y}));
}
return true;
}
bool PlayState::onDestroy()
{
return true;
}
void PlayState::onEntry()
{
prg::application.addKeyListener(*this);
game_timer_.start();
}
void PlayState::onExit()
{
prg::application.removeKeyListener(*this);
game_timer_.stop();
}
void PlayState::onUpdate()
{
}
void PlayState::onRender(prg::Canvas& canvas)
{
const std::string text = "";
canvas.blitFast(
background_,
canvas.getWidth() / 2 - background_.getWidth() / 2,
canvas.getHeight() / 2 - background_.getHeight() / 2
);
prg::uint text_dims[2];
prg::Font::MASSIVE.computePrintDimensions(text_dims, text);
prg::Font::MASSIVE.print(
canvas,
prg::application.getScreenWidth() / 2 - text_dims[0] / 2,
prg::application.getScreenHeight() / 2 - text_dims[1] / 2,
prg::Colour::RED,
text);
for(const auto snake : snakes_) {
snake->render(canvas);
}
for(Shape shapes : shapes_) {
shapes.render(canvas);
}
}
bool PlayState::onKey(const prg::IKeyEvent::KeyEvent& key_event)
{
if(key_event.key_state == KeyEvent::KB_DOWN) {
switch(key_event.key) {
case KeyEvent::KB_ESC_KEY:
prg::application.exit();
break;
}
}
return true;
}
void PlayState::onTimer(prg::Timer& timer)
{
for(auto snake : snakes_) {
snake->move();
}
}
play_state.hpp
#if !defined PLAY_STATE_HPP
#define PLAY_STATE_HPP
#include <prg_interactive.hpp>
#include "snake.hpp"
#include "square.hpp"
#include <list>
//Example of forward declaration of Snake class
class Snake;
class PlayState final : public prg::IAppState,
public prg::IKeyEvent,
public prg::ITimerEvent {
public:
PlayState() = default;
bool onCreate() override;
bool onDestroy() override;
void onEntry() override;
void onExit() override;
void onUpdate() override;
void onRender(prg::Canvas& canvas) override;
bool onKey(const prg::IKeyEvent::KeyEvent& key_event) override;
void onTimer(prg::Timer& timer) override;
private:
//Snake* snakes_[2] {nullptr,nullptr};
std::list<Snake*> snakes_;
prg::Timer game_timer_ {0, 1000 / 30, *this};
const prg::Image background_ {prg::ImageFile("resources/images/border.bmp").load()};
std::vector<Shape> shapes_;
};
#endif // PLAY_STATE_HPP
player_snake.cpp
#include "player_snake.hpp"
//PlayerSnake Implementation
//
PlayerSnake::PlayerSnake()
{
prg::application.addKeyListener(*this);
}
PlayerSnake::~PlayerSnake()
{
prg::application.removeKeyListener(*this);
}
bool PlayerSnake::onKey(const prg::IKeyEvent::KeyEvent& key_event)
{
if(key_event.key_state == KeyEvent::KB_DOWN) {
switch(key_event.key) {
case KeyEvent::KB_LEFT_KEY:
changeDirection(Direction::West);
break;
case KeyEvent::KB_RIGHT_KEY:
changeDirection(Direction::East);
break;
case KeyEvent::KB_UP_KEY:
changeDirection(Direction::North);
break;
case KeyEvent::KB_DOWN_KEY:
changeDirection(Direction::South);
break;
}
}
return true;
}
player_snake.hpp
#if !defined PLAYER_SNAKE_HPP
#define PLAYER_SNAKE_HPP
#include "snake.hpp"
#include <prg_interactive.hpp>
#endif //PLAYER_SNAKE_HPP
You snake is initially set out of bounds:
snakes_.push_back(new AISnake);
snakes_.back()->setPosition(Position(100,100));
snakes_.push_back(new PlayerSnake);
snakes_.back()->setPosition(Position(50,50));
Because in the move function, you limit the snake to (64, 48) as the maximum location. The x position 100 is past the limit. The y positions are past the limit.
You may want to set them at different locations based on the MAXIMUM_WIDTH and MAXIMUM_HEIGHT constants (see my comments):
Snake AISnake;
AISnake.setPosition(MAXIMUM_WIDTH / 4, MAXIMUM_HEIGHT / 4);
snakes_.push_back(AISnake);
Snake PlayerSnake;
PlayerSnake.setPosition(MAXIMUM_WIDTH * 3 / 4,
MAXIMUM_HEIGHT * 3 / 4);
snakes_.push_back(PlayerSnake);
Also note that in the above code fragment, there is no dynamic memory allocation. The variables (snakes) are defined locally and copied into the snakes_ container. This means that there is no need to worry about memory leaks or memory management (like when to delete the memory occupied by the snake).
More Out Of Bounds
Some places, you use the screen dimensions for the boundaries, others, you use a hard coded value:
x = (double)(rand() % prg::application.getScreenWidth());
y = (double)(rand() % prg::application.getScreenHeight());
You need to decide whether the game board occupies the entire screen dimensions or is a fixed size. If you keep the above statements, you should test the positions to verify they are within the game borders.
If this is a GUI application, you need to decide on a fixed size game board or a board set to the size of the window or expanding to the entire screen. For a fixed size board, you should consider a "viewport" design. This means that the window is a view or port showing a small portion of the board (the board is bigger than the window).
Separation of Screen Vs. Board
You should separate the concepts between a logical board and the physical screen. This concept lets you adapt the board to the screen without affecting any other modules.
For example, the Board draws onto the screen. A Board cell may have different pixel dimensions depending on the screen resolution. However, a part of the snake will always occupy at least one Board cell. So the snake movement doesn't depend on the screen resolution, only the Board dimensions.
By placing the objects, Board and Screen, in separate files, you can change the screen dimensions without having to recompile any of the snake or main files.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I'm programming a Breakout game in C++. I'm having a HUGE problem that's preventing me from giving the game multi-ball functionality. I think it has something to do with the destructor. Have a look:
for loop for the balls (Driver.cpp):
for (Ball& b : balls) { // Loops over all balls
(...)
// Collision for when you miss
if (b.getYPos() > HEIGHT) { // If the ball falls below the defined height of the screen
balls.erase(balls.begin() + b.getID()); // Wipe the ball out of memory to make room (Troublesome line)
Ball::addToID(-1); // Shift the ball ID to assign to the next ball back one
(...)
}
And I get this error:
Debug Error!
Program: Breakout.exe
abort() has been called
(Press Retry to debug the application)
Do you know why this mysterious crash is happening? Or more importantly, a fix for it?
Here's a replicable piece of code to help:
Driver.cpp:
#include <vector>
#include <allegro5\allegro.h>
#include "Ball.h"
using namespace std;
vector<Ball> balls(0); // Pay attention to this line
const POS WIDTH = 1600, HEIGHT = 900;
int main {
while (running) {
if (al_key_down(&key, ALLEGRO_KEY_SPACE)) { // Spawn the ball
balls.push_back(Ball(WIDTH / 2, 500, 10, 10)); // Spawn the ball
balls[Ball::getIDtoAssign()].setYSpeed(5);
}
for (Ball& b : balls) { // Pay attention to this loop
b.draw(); // This line is what's crashing.
b.move();
(...)
// Collision for when you miss
balls.erase(
remove_if(balls.begin(), balls.end(),
[=](Ball& b) {
// Collision for when you miss
return b.getYPos() > HEIGHT; // If the ball falls below the defined height of the screen, wipe the ball out of memory to make room
}
),
balls.end()
);
}
}
}
}
return 0;
}
Ball.h:
#pragma once
#include <allegro5\allegro_primitives.h>
using namespace std;
class Ball {
public:
Ball();
Ball(float x, float y, float w, float h);
~Ball();
void draw();
void move();
float getYPos();
void setYSpeed(float set);
private:
float xPos; // Horizontal position
float yPos; // Vertical position (upside down)
float width; // Sprite width
float height; // Sprite height
float xSpeed; // Horizontal speed
float ySpeed; // Vertical speed (inverted)
}
Ball.cpp:
#include "Ball.h"
short Ball::ballIDtoAssign = 0;
Ball::Ball() {
this->xPos = 0;
this->yPos = 0;
this->width = 0;
this->height = 0;
this->xSpeed = 0;
this->ySpeed = 0;
}
Ball::Ball(float x, float y, float w, float h) {
this->xPos = x;
this->yPos = y;
this->width = w;
this->height = h;
this->xSpeed = 0;
this->ySpeed = 0;
}
Ball::~Ball() {
// Destructor
}
void Ball::draw() {
al_draw_filled_rectangle(xPos, yPos, xPos + width, yPos + height, al_map_rgb(0xFF, 0xFF, 0xFF));
}
void Ball::move() {
xPos += xSpeed;
yPos += ySpeed;
}
float Ball::getYPos() {
return yPos;
}
void Ball::setYSpeed(float set) {
ySpeed = set;
}
You cannot modify a container while you are iterating through it with a range-for loop. You don't have access to the iterator that the loop uses internally, and erase() will invalidate that iterator.
You can use the container's iterators manually, paying attention to the new iterator that erase() returns, eg:
for(auto iter = balls.begin(); iter != balls.end(); ) { // Loops over all balls
Ball& b = *iter:
...
// Collision for when you miss
if (b.getYPos() > HEIGHT) { // If the ball falls below the defined height of the screen
...
iter = balls.erase(iter); // Wipe the ball out of memory to make room
}
else {
++iter;
}
}
Alternatively, use the erase-remove idiom via std::remove_if() instead:
balls.erase(
std::remove_if(balls.begin(), balls.end(),
[=](Ball &b){
// Collision for when you miss
return b.getYPos() > HEIGHT; // If the ball falls below the defined height of the screen, wipe the ball out of memory to make room
}
),
balls.end()
);
UPDATE: now that you have posted more of your code, it is clear to see that you are trying to use ID numbers as indexes into the vector, but you are not implementing those IDs correctly, and they are completely unnecessary and should be eliminated.
The Ball::ballID member is never being assigned any value, so in this statement:
balls.erase(balls.begin() + b.getID()); // The troublesome line
Trying to erase() the result of balls.begin() + b.getID() causes undefined behavior since the iterator has an indeterminate value, thus you can end up trying to erase the wrong Ball object, or even an invalid Ball object (which is likely the root cause of your runtime crash).
Also, in this section of code:
balls.push_back(Ball(WIDTH / 2, 500, 10, 10)); // Spawn the ball
balls[Ball::getIDtoAssign()].setYSpeed(5);
Ball::addToID(1);
Since you want to access the Ball object you just pushed, that code can be simplified to this:
balls.back().setYSpeed(5);
And I already gave you code further above to show you how to remove balls from the vector without using IDs.
So, there is need for an ID system at all.
With that said, try something more like this:
Driver.cpp:
#include <vector>
...
#include "Ball.h"
using namespace std;
vector<Ball> balls;
const POS WIDTH = 1600, HEIGHT = 900;
int main {
...
while (running) {
...
if (input.type == ALLEGRO_EVENT_TIMER) { // Runs at 60FPS
...
if (al_key_down(&key, ALLEGRO_KEY_SPACE)) { // Spawn the ball
balls.push_back(Ball(WIDTH / 2, 500, 10, 10)); // Spawn the ball
balls.back().setYSpeed(5);
}
for (auto iter = balls.begin(); iter != balls.end(); ) {
Ball &b = *iter;
...
if (b.getYPos() > HEIGHT) { // Collision for when you miss
iter = balls.erase(iter);
}
else {
++iter;
}
}
/* alternatively:
for (Ball& b : balls) {
b.draw();
b.move();
}
balls.erase(
std::remove_if(balls.begin(), balls.end(),
[=](Ball &b){
// Collision for when you miss
return b.getYPos() > HEIGHT; // If the ball falls below the defined height of the screen, wipe the ball out of memory to make room
}
),
balls.end()
);
*/
}
}
return 0;
}
Ball.h:
#pragma once
...
class Ball {
public:
...
// NO ID METHODS!
private:
...
// NO ID MEMBERS!
}
Ball.cpp:
#include "Ball.h"
...
// NO ID MEMBER/METHODS!
OK, so I managed to figure out why the program crashes. It was because I had the erase-remove inside the for loop which can cause all sorts of problems.
I'm trying to make a simple game, basically there is a map that you can put walls on, after that the enemy will spawn and walk to the finish tile.
Here's the call stack
Game constructor (setscene etc, create buildwall object that handles the wall placement) -> BuildWall (inputs are handled by this object, after user pressed Enter, call the game.generatePath) -> game.generatePath(here's where some of the member variables are not accessible)
Here is the code:
Game.h
#ifndef GAME
#define GAME
#include "Wall.h"
#include "Tile.h"
#include "Enemy.h"
#include "Path.h"
#include <QGraphicsView>
#include "Tower.h"
class BuildWall;
class Game: public QGraphicsView{
Q_OBJECT
public:
Game();
QGraphicsScene * scene;
Wall * build_wall;
// map and tile details
int map_width_in_tiles; // number of tiles in x axis
int map_height_in_tiles;
int map_tile_size; // in pixels
// tiles are indexed in int for easy sorting in QMap. We must use indexOfPoint(x,y) to get an index of a tile
QMap<int,Tile*> tiles;
// spawning entities
void spawnBlueSlime(Tile &spawn, Tile &dest, Path &path);
int getTileSize();
QMap<int,Tile*> getTiles();
void generatePath();
// tile indexing
int indexOfPoint(int x, int y);
// convert tile coordinate to scene coordinate
int x_scene(int x);
int y_scene(int y);
Tower *tower;
void mouseMoveEvent(QMouseEvent *event);
private:
// game initializations
void createMapTiles(QString filename);
void createScene();
void setMapTile(int map_width_in_tiles, int map_height_in_tiles, int map_tile_size_);
// debugging
void drawTilesOverlay(QString filename);
void drawTilesPoint();
//
void printAllTiles();
// mouse input
// spawn dest
Tile *spawn1;
Tile *dest1;
Tile *spawn2;
Tile *dest2;
BuildWall *buildwall;
};
#endif // GAME
Game.cpp
Game::Game()
{
setMouseTracking(true);
grabMouse();
setMapTile(20,10,64); // but start from 0
createScene();
createMapTiles(":/floor/assets/floor/dirt.png");
//drawTilesOverlay(":/util/assets/util/sTrackBorder_0.png");
//drawTilesPoint();
//printAllTiles();
int index = indexOfPoint(19,4);
Tower *tower = new Tower(*this,this->tiles.value(index)->x(),this->tiles.value(index)->y());
BuildWall *buildwall = new BuildWall(*this);
}
void Game::generatePath() {
delete buildwall;
Tile *spawn1 = tiles.value(indexOfPoint(1,5));
Tile *dest1 = tiles.value(indexOfPoint(19,5));
Path *path = new Path(*this,*spawn1,*dest1);
spawnBlueSlime(*spawn1,*dest1, *path);
}
//
// others methods
BuildWall.h
class Game;
class BuildWall: public QGraphicsPixmapItem{
public:
BuildWall(Game &game_);
Game &game;
private:
void keyPressEvent(QKeyEvent *ev);
};
BuildWall.cpp
BuildWall::BuildWall(Game &game_) : game(game_)
{
setPixmap(QPixmap(":/wall/assets/wall/brick_red.png"));
setScale(0.5);
game.scene->addItem(this);
this->setFlag(QGraphicsItem::ItemIsFocusable, true);
this->setFocus();
}
void BuildWall::keyPressEvent(QKeyEvent *ev)
{
int grid = game.map_tile_size;
switch (ev->key())
{
//other keys
case Qt::Key_Return:
this->setFlag(QGraphicsItem::ItemIsFocusable, false);
this->setFocus();
game.generatePath();
break;
}
}
Here you can see I can access some of the member variables such as tiles which are generated in Game constructor
You are shadowing member variables.
In the constructor it should only say:
buildwall = new BuildWall(*this);
This is the case for all member variables (spawn1, dest1, spawn2, dest2, buildwall) and in all functions where you want to call these variables.
I have currently started working with SFML after learning the basics of C++. I have learnt about Arrays, References and everything that comes before it but have struggled to grasp the concept of using classes.
In SFML I have created a simple sprite movement program but, I would like to move this information into a class (lets say it will be called "Player"). I have messed around a lot but I can not get it to work.
I have tried creating a function in a class that would check for player input, but I can not access my sprite that I created in main. I would like to move everything related to the player into a Player class but need some advice.
What is the correct way to do this? (Please don't say go back and learn about classes, this is where I want to learn about them!)
main.cpp
#include <SFML/Graphics.hpp>
#include <string>
#include <iostream>
int main()
{
//character position
enum Direction{ Down, Left, Right, Up };
sf::Vector2i source(1, Down);
//window
sf::RenderWindow window(sf::VideoMode(1200, 700), "Testing");
window.setKeyRepeatEnabled(false);
//player character
sf::Texture pTexture;
sf::Sprite pSprite;
if(!pTexture.loadFromFile("image/playerSprite.png"))
std::cout << "Texture Error" << std::endl;
pSprite.setTexture(pTexture);
pSprite.setScale(1.5f, 1.5f);
//game loop
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) //move up
{
source.y = Up;
pSprite.move(0, -0.2);
//animation
source.x++;
if(source.x * 32 >= pTexture.getSize().x)
{
source.x = 0;
}
}
else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) //move down
{
source.y = Down;
pSprite.move(0, 0.2);
//animation
source.x++;
if(source.x * 32 >= pTexture.getSize().x)
{
source.x = 0;
}
}
else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) //move right
{
source.y = Right;
pSprite.move(0.2, 0);
//animation
source.x++;
if(source.x * 32 >= pTexture.getSize().x)
{
source.x = 0;
}
}
else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) //move left
{
source.y = Left;
pSprite.move(-0.2, 0);
//animation
source.x++;
if(source.x * 32 >= pTexture.getSize().x)
{
source.x = 0;
}
}
pSprite.setTextureRect(sf::IntRect(source.x * 32, source.y * 32, 32, 32));
window.draw(pSprite);
window.display();
}
return 0;
}
Disclaimer: You shouldn't expect that kind of answer, you really should read more on OOP to get the point, this has nothing to do with SFML, this is just basic refactoring.
How to think with OOP
First thing first, before coding a feature, you should design the OOP structure that really suits the situation. See each class as part of a whole, that is your program. A class in fact is just an aggregation of data with useful methods that only affects the data inside the class (or the data provided via method parameters) in a meaningful way.
See the basics of C++ (more the OOP part for you) to understand how to get it to work in C++. The concepts are similar in other programming languages.
Working with your provided code
What you asked for was a Player class and it's a great idea to get the player code out of the program main logic. You need to ask yourself: "What my player code needs to work?"
The player class
Basically, your player is only a sprite and a position. So you encapsulate those data into your Player class as private members. That keeps other code from messing with the player data. To use the player data, you need to provide methods in the class that each affects only the Player.
Texture and Sprite
I have kept the Texture outside of the player on purpose. Textures are heavy objects, that's why the Sprite object only keeps a pointer to it. Sprites are lightweight and can be changed and copied easily. The managing of texture objects and other assets is another subject, though here's my own resource manager code.
Optional
I did not took the time to change your code much, but you could change the way you handle the movement to only make one "move" method that takes a Player::Direction has a parameter.
To help you a little more and to give you some more guidelines on the subject, I used "forward declaration" and moved your Direction enum inside the class. It's maybe not the best way to achieve what you want, but I've only change your own code to avoid getting you lost.
The Code
Anyway, here's my go at this.
Player.h
#ifndef PLAYER_H_
#define PLAYER_H_
#include <SFML/Graphics/Drawable.hpp>
#include <SFML/Graphics/Sprite.hpp>
// Forward Declaration
namespace sf {
class Texture;
}
// provide your namespace to avoid collision/ambiguities
namespace test {
/*
*
*/
class Player: public sf::Drawable {
public:
enum Direction {
Down, Left, Right, Up
};
Player(const sf::Texture& playerTexture);
virtual ~Player();
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const;
void moveUp();
void moveDown();
void moveLeft();
void moveRight();
private:
sf::Sprite mSprite;
sf::Vector2i mSource;
};
} /* end namespace test */
#endif /* PLAYER_H_ */
Player.cpp
#include "Player.h"
// you need this because of forward declaration
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/Rect.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
namespace test {
Player::Player(const sf::Texture& imagePath) :
mSprite(imagePath),
mSource(1, Player::Down) {
// do not need that line anymore, thanks to initialiser list
//pSprite.setTexture(pTexture);
mSprite.setScale(1.5f, 1.5f);
}
Player::~Player() {
// TODO Auto-generated destructor stub
}
void Player::draw(sf::RenderTarget& target, sf::RenderStates states) const {
target.draw(mSprite, states);
}
void Player::moveUp() {
mSource.y = Up;
mSprite.move(0, -0.2);
//animation
mSource.x++;
if (mSource.x * 32 >= (int) mSprite.getTexture()->getSize().x) {
mSource.x = 0;
}
mSprite.setTextureRect(sf::IntRect(mSource.x * 32, mSource.y * 32, 32, 32));
}
void Player::moveDown() {
mSource.y = Down;
mSprite.move(0, 0.2);
//animation
mSource.x++;
if (mSource.x * 32 >= (int) mSprite.getTexture()->getSize().x) {
mSource.x = 0;
}
}
void Player::moveLeft() {
mSource.y = Left;
mSprite.move(-0.2, 0);
//animation
mSource.x++;
if (mSource.x * 32 >= (int) mSprite.getTexture()->getSize().x) {
mSource.x = 0;
}
}
void Player::moveRight() {
mSource.y = Right;
mSprite.move(0.2, 0);
//animation
mSource.x++;
if (mSource.x * 32 >= (int) mSprite.getTexture()->getSize().x) {
mSource.x = 0;
}
}
} /* end namespace test */
main.cpp
#include <SFML/Graphics.hpp>
//#include <string> // not used for now
#include <iostream>
// don't forget to include your own header
#include "Player.h"
int main() {
// just to save typing the "std::"
using std::cout;
using std::endl;
using std::cerr;
//window
sf::RenderWindow window(sf::VideoMode(1200, 700), "Testing");
window.setKeyRepeatEnabled(false);
//player texture
sf::Texture pTexture;
if (!pTexture.loadFromFile("image/playerSprite.png")) {
cerr << "Texture Error" << endl;
}
test::Player thePlayer(pTexture);
//game loop
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
}
window.clear();
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) //move up
{
thePlayer.moveUp();
} else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) //move down
{
thePlayer.moveDown();
} else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) //move right
{
thePlayer.moveRight();
} else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) //move left
{
thePlayer.moveLeft();
}
window.draw(thePlayer);
window.display();
}
return 0;
}
Other good practices
Accessors, or Getters/Setters, are member functions that gives one the access to a class private member.
In your code, you could do something like that:
class Player {
public:
Player(const sf::Texture& playerTexture);
virtual ~Player();
// to give access to a const reference of the sprite
// One could call it like: sf::Sprite mySprite = myPlayerObject.getSprite();
// notice also that the method itself is const, which assure you that
// myPlayerObject won't change by calling getSprite()
const sf::Sprite& getSprite() const{
return mSprite;
}
// setSprite is not a const method, so it will change the data
// inside myPlayerObject
void setSprite(const sf::Sprite& newSprite){
mSprite = newSprite;
}
private:
sf::Sprite mSprite;
sf::Vector2i mSource;
};
So I have a 2 games running side by side.
proj2/main.cpp (main that gets run that then can call either game)
proj2/spades/display.cpp
proj2/spades/gameplay.cpp
proj2/spades/otherFiles.cpp
proj2/hearts/display.cpp (this is the same display class as in the spades game with the same functions just sightly altered.)
proj2/hearts/hearts.cpp
proj2/hearts/otherFiles.cpp
I'm getting the following linker error,
./gamePlay.o: In function `__static_initialization_and_destruction_0(int, int)':
gamePlay.cpp:(.text+0x396): undefined reference to `spades::display::display()'
./gamePlay.o: In function `__tcf_3':
gamePlay.cpp:(.text+0x480): undefined reference to `spades::display::~display()'
in my gamePlay.cpp file I define the following and I think this is where my error is,
display monitor;
if I add the extern identifier to the line such as
extern display monitor;
the error will disappear only to produce the following issue,
./gamePlay.o: In function `spades::gamePlay::storeBid(std::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >&)':
gamePlay.cpp:(.text+0x14ee): undefined reference to `spades::monitor'
gamePlay.cpp:(.text+0x14f3): undefined reference to `spades::display::drawBox(int, int, int, int, int)'
I think this error is this coming from these following lines, but I have no idea how to fix this error. Feeling quite lost at the moment. (I don't understand why I would need the extern as an identifier for monitor either). Also all the includes should be alright.
int key = monitor.captureInput();
monitor.drawBox(2, 5, 3, 2, 0);
Below is my display.h file
/*
* Description: Defines constants and class prototypes and variables
*/
// These are include files they are used for accessing functions and
// variables in external libraries.
#include <ncursesw/ncurses.h>
#include <iostream>
#include <cstdio>
// This is the namespace std, without this you would need to add "std::"
// infront of every variable and function from the std library.
//using namespace std;
// These Mouse Mask Definitions are for some of the common mouse buttons
// that are used in this project.
#define LEFT_UP 1
#define LEFT_DOWN 2
#define LEFT_CLICK 4
#define LEFT_DOUBLECLICK 24
#define RIGHT_UP 4096
#define RIGHT_DOWN 8192
#define RIGHT_CLICK 16384
#define RIGHT_DOUBLECLICK 32768
#define MIDDLE_ROLLUP 1048576
#define MIDDLE_ROLLDOWN 256
namespace spades {
// Some extended character definitions for showing the special symbols
// in the extended UTF-8 character map.
const char joker[] = {0xE2, 0x98, 0xBA, 0};
const char clubs[] = {0xE2, 0x99, 0xA3, 0};
const char diamonds[] = {0xE2, 0x99, 0xA6, 0};
const char hearts[] = {0xE2, 0x99, 0xA5, 0};
const char spades[] = {0xE2, 0x99, 0xA0, 0};
/*
* This is the display class definitions
*/
class display {
public:
/* "constructor"
* This function is called whenever a new object of class display is first
* created. Put initialization functions in here.
*/
display(void){};
/* "destructor"
* This function is called just before a object is deleted. Put clean up
* functions in here.
*/
~display(){}; // destructor
// captures user input
int captureInput(void);
// stores new screensize on update
void handleResize(int sig);
/*
* Drawing commands
*/
// display a card on the screen
void displayCard(int x, int y, int suit, int number, int printAtt);
// erase in the shape of a box on the screen
void eraseBox(int x, int y, int sizeX, int sizeY);
// draw a box on the screen
void drawBox(int x, int y, int sizeX, int sizeY, int printAtt);
// display banner text at the bottom of the screen
void bannerBottom(std::string bannerText);
void bannerAboveBottom(std::string bannerText);
// display banner text at the top of the screen
void bannerTop(std::string bannerText);
// get information about the display
int getCols(void) {return cols;}
int getLines(void) {return lines;}
int getMouseEventX(void) {return mouseEventX;}
int getMouseEventY(void) {return mouseEventY;}
int getMouseEventButton(void) {return mouseEventButton;}
// Updates the screen after you finish drawing
void updateScreen(void) {refresh();}
// sets an offset for when cards clip the bottom of the screen
void setBottomOffset(int offset) {lineBoundaryOffset=offset;}
private:
// These are private functions and variables used inside of display.
// You should not try to access these from outside the display class.
void printFace(int suit, int number, int line, int printAtt);
void printSuit(int suit);
void printNumber(int number);
int cols;
int lines;
int mouseEventX;
int mouseEventY;
int mouseEventButton;
int lineBoundaryOffset;
};
}
Below is my display.cpp file
/*
* Description: Defines the functionality of the user interface.
*
* NOTES:
* * Requires the terminal (Putty) to be set to UTF-8.
* * Does not function when running a screen session.
*/
#include <iostream>
#include <sys/ioctl.h>
#include <cstdio>
#include <unistd.h>
#include <locale.h>
#include <ncursesw/ncurses.h>
#include "display.h"
namespace spades {
//using namespace std;
/* Function: This is the constructor.
* Description: It is called whenever an object of class display is initialized
*/
display::display(void) {
/* Initilization of Terminal */
// required to get card suits displaying, combined with UTF-8 set in terminal
setlocale(LC_ALL,"en_US.utf-8");
// initialize ncurses screen
initscr();
// allow for color settings
start_color();
// clear the screen
clear();
// turn off the keyboard echo (reqiured while drawing)
noecho();
// Change to character mode (so individual characters are being read at a
// time rather than waiting for a carriage return).
cbreak();
// Allows for function keys to be used (also nessacary for getting the mouse
// movement working).
keypad(stdscr, TRUE);
// set which mouse events are captured
mousemask(ALL_MOUSE_EVENTS, NULL);
// Setting the timeout for the capture input values are in 1/10ths of a second.
halfdelay(5);
// setup the screen size settings.
cols = 80;
lines = 24;
// this updates the locally stored col and line variables
handleResize(0);
// set a no card draw offset of 1 so the bottom banner is not overwritten
lineBoundaryOffset = 1;
// Settings for card colors (these can be set outside of the display class)
init_pair(1, COLOR_CYAN, COLOR_BLACK); // for card outline
init_pair(2, COLOR_BLUE, COLOR_BLACK); // for spades and clubs
init_pair(3, COLOR_RED, COLOR_BLACK); // for hearts and diamonds
init_pair(4, COLOR_GREEN, COLOR_BLACK); // for turned over card
init_pair(5, COLOR_GREEN, COLOR_BLACK); // for box drawing
init_pair(6, COLOR_GREEN, COLOR_BLACK); // for banner display
}
/* Function: This is the destructor.
* Description: This is called just before an object is deleted.
*/
display::~display() {
// this is turns off all the special settings and returns the terminal to normal
endwin();
// insert deletion of dynamically created objects here too
}
/*
* Function: This captures all the userinput.
* Description: It captures mouse and keyboard events.
* Returns "Positive Number"
* - for user keypress
* - this is a character code typed
* Returns "0" - for no user input
* - this is when nothing is typed for half a second
* - allows for other timed operations to occur
* Returns "-1" - for mouse event
* - details of the mouse event must be fetched from this class
* - use getMouseEventX, getMouseEventY and getMouseEventButton
*/
int display::captureInput(void) {
// obtain one mouse event or keypress
int ch=getch();
// this is a switch statement for the result of getch
switch (ch) {
case KEY_MOUSE: // this occurs when an mouse event occurs
{
MEVENT mevent; // this is a variable declared of type MEVENT
getmouse(&mevent); // this gets the mouse event from ncurses (library)
mouseEventX = mevent.x; // get the column location of the event
mouseEventY = mevent.y; // get the row location of the event
mouseEventButton = mevent.bstate; // get the button state of the mouse
return -1; // -1 is for a mouse event
}
break;
case ERR: // this occurs when there is a timeout
{
return 0; // 0 is when nothing occurs
}
break;
default: // this occurs when a key is pressed
return ch; // a character is when the user types something
}
return 0; // this is never called, but is used to stop compiler complaints
}
/*
* Function: Updates all the information in the display class on window resize
* Description: This function just updates information, it requires the call
* from a static singal handler. Signal handlers service interrupts and require
* a static function to be called, because they are not part of the main
* program control flow. The signal handler should be declared in the main
* class, because your game should redraw the display on a resize.
*/
void display::handleResize(int sig) {
#ifdef TIOCGSIZE // operating system dependant differences
struct ttysize ts;
ioctl(STDIN_FILENO, TIOCGSIZE, &ts); // get the information of the terminal
cols = ts.ts_cols;
lines = ts.ts_lines;
#elif defined(TIOCGWINSZ)
struct winsize ts;
ioctl(STDIN_FILENO, TIOCGWINSZ, &ts); // get the information of the terminal
cols = ts.ws_col;
lines = ts.ws_row;
#endif /* TIOCGSIZE */
resizeterm(lines, cols); // sets the ncurses window size correctly
}
/*
* Function: Displays various cards on the game screen
* Description: This function displays various playing cards on the screen.
* The first two arguments are the x and y coordinates of the top left corner
* of the card.
* The suit values are: 1=spades, 2=hearts, 3=clubs, 4=diamonds
* The numbers are: 1=Ace, 2-10=2-10, 11=Jack, 12=Queen, 13=King, 14=Joker
* Any suit and number that do not match the valid numberrs generates a face down
* card.
* The printAtt allows for one or more any of the following display settings:
* A_NORMAL Normal display (no highlight)
* A_STANDOUT Best highlighting mode of the terminal.
* A_UNDERLINE Underlining
* A_REVERSE Reverse video
* A_BLINK Blinking
* A_DIM Half bright
* A_BOLD Extra bright or bold
* A_PROTECT Protected mode
* A_INVIS Invisible or blank mode
* A_ALTCHARSET Alternate character set
* A_CHARTEXT Bit-mask to extract a character
* COLOR_PAIR(n) Color-pair number n
*/
void display::displayCard(int x, int y, int suit, int number, int printAtt) {
// Ncurses drawing settings
attron(COLOR_PAIR(1) | printAtt);
// prevent draw if it off the screen
if (x>=0 && y>=0 && x<cols-6 && y<lines-lineBoundaryOffset) {
// print the top lines of the card
mvprintw(y,x,"\u250c\u2500\u2500\u2500\u2500\u2510");
// the next 4 if statements prevent draw if it is over the bottom of the screen
if (y<lines-1-lineBoundaryOffset) {
move(y+1,x); // move command
printFace(suit,number,0, printAtt); // call function to print card face
}
if (y<lines-2-lineBoundaryOffset) {
move(y+2,x); // move command
printFace(suit,number,1, printAtt); // call function to print card face
}
if (y<lines-3-lineBoundaryOffset) {
move(y+3,x); // move command
printFace(suit,number,2, printAtt); // call function to print card face
}
if (y<lines-4-lineBoundaryOffset) {
// prints the bottom lines of the card
mvprintw(y+4,x,"\u2514\u2500\u2500\u2500\u2500\u2518");
}
}
// Ncurses turn off the drawing settings
attroff(COLOR_PAIR(1) | printAtt);
}
/*
* Function: Print a single line of what is written on the card.
* Description: This copies suit, number and printAtt from the calling function.
* Also includes what line of the card face is being drawn.
*/
void display::printFace(int suit, int number, int line, int printAtt) {
// draw left edge of the card
printw("\u2502");
if (suit==2 || suit==4) { // Red for Hearts and Diamonds
attron(COLOR_PAIR(3) | printAtt);
} else { // Black for Spades and Clover
attron(COLOR_PAIR(2) | printAtt);
}
// this the display of the joker
if (number==14) {
if (line==0)
printw("J%s ", joker);
if (line==1)
printw("oker");
if (line==2)
printw(" J%s", joker);
// this is the display for the cards with suits and numbers
} else if (suit>=1 && suit <=4 && number>=1 && number<=13) {
if (line==0) {
printSuit(suit); // function to draw suit
printNumber(number); // function to draw number
if (number!=10)
printw(" ");
printw(" ");
} else if (line==2) {
if (number!=10)
printw(" ");
printw(" ");
printNumber(number); // function to draw number
printSuit(suit); // function to draw suit
} else {
printw(" ");
}
// this is for a face down card
} else {
// the face down cards have a special color
attron(COLOR_PAIR(4) | printAtt);
if (line==0)
printw("%s %s", spades, hearts);
if (line==1)
printw("Play");
if (line==2)
printw("%s %s", diamonds, clubs);
attroff(COLOR_PAIR(1) | printAtt);
}
// turn on the card edge color settings
attron(COLOR_PAIR(1) | printAtt);
// print the right edge of the card
printw("\u2502");
}
/*
* Function: Print the suit of the card
* Description: This is just a look up table.
*/
void display::printSuit(int suit) {
switch (suit) {
case 1:
printw("%s",spades);
break;
case 2:
printw("%s",hearts);
break;
case 3:
printw("%s",clubs);
break;
case 4:
printw("%s",diamonds);
break;
default:
printw(" ");
break;
}
}
/*
* Function: Prints the number on the card
* Description: This is just a look up table.
*/
void display::printNumber(int number) {
switch (number) {
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
printw("%i",number);
break;
case 1:
printw("%c",'A');
break;
case 11:
printw("%c",'J');
break;
case 12:
printw("%c",'Q');
break;
case 13:
printw("%c",'K');
break;
default:
printw(" ");
break;
}
}
/*
* Function: Erases a rectangle on the screen
* Description: x,y is for the top left corner, sizeX and sizeY set
* how big the square is.
*/
void display::eraseBox(int x, int y, int sizeX, int sizeY) {
std::string strDraw;
int yCount;
int maxSizeX;
// this limits the column size of the draw when it is over the edge
// of the drawing area
if (sizeX+x > cols)
maxSizeX=cols-x;
else
maxSizeX=sizeX;
// for the number of rows that need to be drawn
for (yCount=0; yCount<sizeY;yCount++) {
// if the box goes over the edge of the drawable screen
// stop drawing by breaking the loop
if (yCount+y > lines-lineBoundaryOffset || y < 0)
break;
// reset the line to be drawn
strDraw = "";
// check that x is not off the screen
if (x<=cols && x >= 0) {
// make a string needed for box width
strDraw.append(maxSizeX,' ');
// print the line of the box
mvprintw(y+yCount,x,"%s",strDraw.c_str());
}
}
}
/*
* Function: Draws a box on the screen
* Description: x,y is for the top left corner, sizeX and sizeY set
* how big the square is. printAtt allows for changes in the
* display settings.
*/
void display::drawBox(int x, int y, int sizeX, int sizeY, int printAtt) {
std::string strDraw;
int ii;
int yCount;
// set the box setting colors on
attron(COLOR_PAIR(5) | printAtt);
// for the box height being drawn loop
for (yCount=0; yCount<sizeY;yCount++) {
// break loop if the drawing is offscreen
if (yCount+y > lines-lineBoundaryOffset || y < 0)
break;
// if x is on the screen
if (x<=cols) {
strDraw = "";
// for the box width loop
for (ii=0;ii<sizeX;ii++){
// stop drawing if the x is offscreen
if (ii+x > cols || x < 0)
break;
// first line
if (yCount==0) {
if (ii==0) {
strDraw.append("\u250c"); // left
} else if (ii==sizeX-1) {
strDraw.append("\u2510"); // right
} else {
strDraw.append("\u2500"); // middle
}
// last line
} else if (yCount==sizeY-1) {
if (ii==0) {
strDraw.append("\u2514"); // left
} else if (ii==sizeX-1) {
strDraw.append("\u2518"); // right
} else {
strDraw.append("\u2500"); // middle
}
// other lines
} else {
if (ii==0) {
strDraw.append("\u2502"); // left
} else if (ii==sizeX-1) {
strDraw.append("\u2502"); // right
} else {
strDraw.append(" "); // middle
}
}
}
// print the line that was created
mvprintw(y+yCount,x,"%s",strDraw.c_str());
}
}
// turn off the attribute colors
attroff(COLOR_PAIR(5) | printAtt);
}
/*
* Function: Draws a banner of text at the bottom right of the screen
* Description: Inverts the color and draws the banner at the bottom
* of the screen. Does not handle carriage returns on the string.
*/
void display::bannerBottom(std::string bannerText) {
// change to the banner draw settings
attron(COLOR_PAIR(6) | A_REVERSE | A_BOLD);
// checks if the banner string size is smaller than the width of the screen
if((unsigned)cols > bannerText.size()) {
// moves the cursor to the bottom of the screen
move(lines-1,0);
// fill in extra space to the banner text is right adjusted
hline(' ',cols - bannerText.size());
// prints out the banner text
mvprintw(lines-1,cols-bannerText.size(),"%s", bannerText.c_str());
// if banner string size is larger than width of screen
} else {
// clip the banner text so it doesn't wrap over to the next line
mvprintw(lines-1,0,"%s", (bannerText.substr(0,cols)).c_str());
}
// turn off the draw colors
attroff(COLOR_PAIR(6) | A_REVERSE | A_BOLD);
}
void display::bannerAboveBottom(std::string bannerText) {
// change to the banner draw settings
attron(COLOR_PAIR(1) | A_REVERSE | A_BOLD);
// checks if the banner string size is smaller than the width of the screen
if((unsigned)cols > bannerText.size()) {
// moves the cursor to the bottom of the screen
move(lines-2,0);
// fill in extra space to the banner text is right adjusted
hline(' ',cols - bannerText.size());
// prints out the banner text
mvprintw(lines-2,cols-bannerText.size(),"%s", bannerText.c_str());
// if banner string size is larger than width of screen
} else {
// clip the banner text so it doesn't wrap over to the next line
mvprintw(lines-2,0,"%s", (bannerText.substr(0,cols)).c_str());
}
// turn off the draw colors
attroff(COLOR_PAIR(1) | A_REVERSE | A_BOLD);
}
/*
* Function: Draws a banner of text at the top left of the screen
* Description: Inverts the color and draws the banner at the top
* of the screen. Does not handle carriage returns on the string.
*/
void display::bannerTop(std::string bannerText) {
// change to the banner draw settings
attron(COLOR_PAIR(6) | A_REVERSE | A_BOLD);
// checks if the banner string size is smaller than the width of the screen
if((unsigned)cols > bannerText.size()) {
// moves the cursor to the bottom of the screen
move(0,0);
// prints out the banner text
printw("%s", bannerText.c_str());
// fill in extra space after the banner text
hline(' ',cols - bannerText.size());
// if banner string size is larger than width of screen
} else {
// clip the banner text so it doesn't wrap over to the next line
mvprintw(0,0,"%s", (bannerText.substr(0,cols)).c_str());
}
// turn off the draw colors
attroff(COLOR_PAIR(6) | A_REVERSE | A_BOLD);
}
}
Below is my gameplay.h file
#include "player.h"
#include "display.h"
#ifndef gamePlay_H
#define gamePlay_H
namespace spades {
class gamePlay{
bool spadesBroken;
public:
vector <spades::card> getDeck();
vector <spades::player>getplayers();
bool getSpadesBroken() {return spadesBroken;}
void setSpadesBroken(bool b){spadesBroken = b;}
int compareCenter(int leadplayer);
void createDeck();
void deal(vector <spades::card> &deck, vector <spades::player> &players);
void handSort();
bool containSuit(spades::card lead, spades::player players);
bool onlySpade(spades::player play);
int handCheck(int xevent, int yevent, vector <spades::player> players, int trickStart);
void displayHand();
void displayAdd();
void humanPlay(int trickStart);
void CPUplay(int trickStart, int CPU);
void score(spades::player &play, spades::player &play2);
void storeBid(stringstream &msg);
void runGame();
};
}
#endif
Below is my gameplay.cpp file
#include <iostream>
#include <sys/ioctl.h>
#include <cstdio>
#include <unistd.h>
#include <locale.h>
#include <ncursesw/ncurses.h>
#include <fstream>
#include "gamePlay.h"
#include <cstdlib>
#include <sstream>
#include <ctime>
namespace spades {
vector <spades::player> players(4);
vector <spades::card> deck(52);
display monitor;
card center[4];
vector <spades::card> gamePlay::getDeck(){return deck;}
vector <spades::player> gamePlay::getplayers(){return players;}
//sorts the cards in the players hand into diamonds, clubs, hearts, spades
void gamePlay::handSort(){
int spades[13];
int hearts[13];
int clubs[13];
int diamonds[13];
int index;
int i;
for(i=0; i<13; i++){ //determines the card number and places them into corresponding indexes
index = (players.at(0).hand.at(i).getCardNum()+11)%13; //cause the cards to be placed based on their number with 2 being index 0 and 1(ace) being index 12
switch(players.at(0).hand.at(i).getSuit())
{
case 1: spades[index] = 1;
break;
case 2: hearts[index] = 1;
break;
case 3: clubs[index] = 1;
break;
case 4: diamonds[index] = 1;
break;
default: mvprintw(3,2,"!!!!!!!we have a problem!!!!!!!!!!");
break;
}
}
i = 0;
while(i<13){ //being placing the ordered cards back into the players hand
int j = 0;
while(j < 13){
if(diamonds[j] == 1){ //if a card has been placed in this index for the diamonds only array
if(j+2 == 14) //if the card is an ace
players.at(0).hand.at(i).setCardNum(1);
else
players.at(0).hand.at(i).setCardNum(j+2); //add 2 to each index to get the card number
players.at(0).hand.at(i).setSuit(4);
i++;
}
j++;
}
j = 0;
while(j < 13){
if(clubs[j] == 1){
if(j+2 == 14)
players.at(0).hand.at(i).setCardNum(1);
else
players.at(0).hand.at(i).setCardNum(j+2);
players.at(0).hand.at(i).setSuit(3);
i++;
}
j++;
}
j = 0;
while(j < 13){
if(hearts[j] == 1){
if(j+2 == 14)
players.at(0).hand.at(i).setCardNum(1);
else
players.at(0).hand.at(i).setCardNum(j+2);
players.at(0).hand.at(i).setSuit(2);
i++;
}
j++;
}
j = 0;
while(j < 13){
if(spades[j] == 1){
if(j+2 == 14)
players.at(0).hand.at(i).setCardNum(1);
else
players.at(0).hand.at(i).setCardNum(j+2);
players.at(0).hand.at(i).setSuit(1);
i++;
}
j++;
}
}
}
//compares the center pile of 4 played cards to determine who wins the trick
int gamePlay::compareCenter(int leadplayer){
int highest = center[leadplayer].getCardNum();
if(center[leadplayer].getCardNum() == 1)
highest = 14;
int suit = center[leadplayer].getSuit();
int player = leadplayer;
for(int i = leadplayer+1; i < leadplayer+4; i++)
{
if(center[i%4].getSuit() == 1)
setSpadesBroken(true);
if((suit != 1) && (center[i%4].getSuit() == 1))
{
player = i%4;
suit = 1;
highest = center[i%4].getCardNum();
}
if(suit == center[i%4].getSuit()){
if(center[i%4].getCardNum() == 1){
player = i % 4;
highest = 14;
}
if(highest < center[i%4].getCardNum())
{
player = i%4;
highest = center[i%4].getCardNum();
}
}
}
players.at(player).setTricksTaken(players.at(player).getTricksTaken()+1); //increments the trick count of the winning player
return player; //return the player who won to determine who goes first next turn
}
//Create the deck of 52 cards by setting the suit and number of each card to a nonzero integer
void gamePlay::createDeck() {
for(int j = 0; j < 52; j++)
{
deck.at(j).setCardNum((j%13)+1);
deck.at(j).setSuit((j/13)+1);
}
random_shuffle(deck.begin(), deck.end());
}
//deal out 13 cards to each player by setting the
void gamePlay::deal(vector <spades::card> &newdeck, vector <spades::player> &newplayers){
for(int i = 0; i<52; i++){
newplayers.at(i/13).addCard(newdeck.at(i));
newdeck.at(i).setSuit(0);
newdeck.at(i).setCardNum(0);
}
}
//determines if the player still has a card of the same suit in their hand as the leading card played
bool gamePlay::containSuit(spades::card lead, spades::player players){
bool suit = false;
for(int i = 0; i < players.getHand().size(); i++){
if(lead.getSuit() == players.getHand().at(i).get
You have to write default constructor and destructor like this:
struct display
{
display() { }
~display() { }
// ^^^^
};
and not like this:
struct display
{
display();
~display();
};
However, if you do have the latter form in your header and want to keep it that way, you need to add definitions to the source file:
display::display() { }
display::~display() { }
The linker is complaining that it can't find object spades::monitor and function spades::display::drawBox(int, int, int, int). Are these defined anywhere? Please note that when defining a member variable/member function in a .cpp file (or anywhere outside of its class definition), you have to qualify it with the class name. Example:
In .h
namespace spades {
class display {
static display monitor;
void drawBox(int, int, int, int);
};
}
In .cpp
namespace spades {
display display::monitor;
void display::drawBox(int, int, int, int) {
//...
}
}
It's hard to say more without seeing the full code (at least the definitions of the things the linker is complaining about).
So I was working on my code, which is designed in a modular way. Now, one of my classes; called Splash has to create a object of another class which is called Emitter. Normally you would just create the object and be done with it, but that doesn't work here, as the Emitter class has a custom constructor. But when I try to create an object, it doesn't work.
As an example;
Emitter has a constructor like so: Emitter::Emitter(int x, int y, int amount); and needs to be created so it can be accessed in the Splash class.
I tried to do this, but it didn't work:
class Splash{
private:
Emitter ps(100, 200, 400, "firstimage.png", "secondimage.png"); // Try to create object, doesn't work.
public:
// Other splash class functions.
}
I also tried this, which didn't work either:
class Splash{
private:
Emitter ps; // Try to create object, doesn't work.
public:
Splash() : ps(100, 200, 400, "firstimage.png", "secondimage.png")
{};
}
Edit: I know the second way is supposed to work, however it doesn't. If I remove the Emitter Section, the code works. but when I do it the second way, no window opens, no application is executed.
So how can I create my Emitter object for use in Splash?
Edit:
Here is my code for the emitter class and header:
Header
// Particle engine for the project
#ifndef _PARTICLE_H_
#define _PARTICLE_H_
#include <vector>
#include <string>
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include "image.h"
extern SDL_Surface* gameScreen;
class Particle{
private: // Particle settings
int x, y;
int lifetime;
private: // Particle surface that shall be applied
SDL_Surface* particleScreen;
public: // Constructor and destructor
Particle(int xA, int yA, string particleSprite);
~Particle(){};
public: // Various functions
void show();
bool isDead();
};
class Emitter{
private: // Emitter settings
int x, y;
int xVel, yVel;
private: // The particles for a dot
vector<Particle> particles;
SDL_Surface* emitterScreen;
string particleImg;
public: // Constructor and destructor
Emitter(int amount, int x, int y, string particleImage, string emitterImage);
~Emitter();
public: // Helper functions
void move();
void show();
void showParticles();
};
#endif
and here is the emitter functions:
#include "particle.h"
// The particle class stuff
Particle::Particle(int xA, int yA, string particleSprite){
// Draw the particle in a random location about the emitter within 25 pixels
x = xA - 5 + (rand() % 25);
y = yA - 5 + (rand() % 25);
lifetime = rand() % 6;
particleScreen = Image::loadImage(particleSprite);
}
void Particle::show(){
// Apply surface and age particle
Image::applySurface(x, y, particleScreen, gameScreen);
++lifetime;
}
bool Particle::isDead(){
if(lifetime > 11)
return true;
return false;
}
// The emitter class stuff
Emitter::Emitter(int amount, int x, int y, string particleImage, string emitterImage){
// Seed the time for random emitter
srand(SDL_GetTicks());
// Set up the variables and create the particles
x = y = xVel = yVel = 0;
particles.resize(amount, Particle(x, y, particleImage));
emitterScreen = Image::loadImage(emitterImage);
particleImg = particleImage;
}
Emitter::~Emitter(){
particles.clear();
}
void Emitter::move(){
}
void Emitter::show(){
// Show the dot image.
Image::applySurface(x, y, emitterScreen, gameScreen);
}
void Emitter::showParticles(){
// Go through all the particles
for(vector<Particle>::size_type i = 0; i != particles.size(); i++){
if(particles[i].isDead() == true){
particles.erase(particles.begin() + i);
particles.insert(particles.begin() + i, Particle(x, y, particleImg));
}
}
// And show all the particles
for(vector<Particle>::size_type i = 0; i != particles.size(); i++){
particles[i].show();
}
}
Also here is the Splash Class and the Splash Header.
The second option should work, and I would start looking at compilation errors to see why it doesn't. In fact, please post any compilation errors you have related to this code.
In the meantime, you can do something like this:
class Splash{
private:
Emitter* ps;
public:
Splash() { ps = new Emitter(100,200,400); }
Splash(const Splash& copy_from_me) { //you are now responsible for this }
Splash & operator= (const Splash & other) { //you are now responsible for this}
~Splash() { delete ps; }
};
Well, I managed to fix it, in a hackish way though. What I did was create a default constructor, and move my normal Constructor code into a new function. Then I created the object and called the the new init function to set everything up.