Dragging a sprite in sfml - c++

I'm doing an assignment at the moment and for it, I need to drag chess pieces on a board, the problem is that I have no idea how to make this work for more than one piece. I can compute the mouse position and I've been able to move just one piece but the code only works for that single piece. My question is how do I click on a piece, have it become the active piece until I release the mouse button. This won't work if I only check if it intersects with one piece because it isn't dynamic then. My code is a mess from me trying different things but here are some of the relevant parts.
Here is the setup for the sprites:
// load the texture and setup the sprite for the logo
void Game::setupSprite()
{
sf::IntRect pieceRect(m_wking.getPosition().x, m_wking.getPosition().y, 128, 128);
if (!m_boardTexture.loadFromFile("ASSETS\\IMAGES\\board.png"))
{
// simple error message if previous call fails
std::cout << "problem loading board" << std::endl;
}
m_boardSprite.setTexture(m_boardTexture);
m_boardSprite.setPosition(0.0f, 0.0f);
//pieces
if (!m_wkingTex.loadFromFile("ASSETS\\IMAGES\\PIECES\\wking.png"))
{
// simple error message if previous call fails
std::cout << "problem loading piece" << std::endl;
}
m_wking.setTexture(m_wkingTex);
m_wking.setPosition(0.0f, 0.0f);
sf::IntRect wkingRect(pieceRect);
}
If the mouse button is down
void Game::processMouseButtonDown(sf::Event t_event)
{
if (sf::Mouse::isButtonPressed(sf::Mouse::Button::Left)) {
std::cout << "Mouse button pressed\n";
sf::Vector2f mouse = m_window.mapPixelToCoords(sf::Mouse::getPosition(m_window));
if (isSpriteClicked() && !m_holdingPiece) {
m_holdingPiece = true;
}
}
}
Mouse button is up:
void Game::processMouseButtonUp(sf::Event t_event)
{
m_holdingPiece = false;
sf::IntRect pieceRect(m_wking.getPosition().x, m_wking.getPosition().y, 128, 128);
m_wking.setTextureRect(pieceRect);
}
If the sprite is clicked
bool Game::isSpriteClicked()
{
sf::Vector2f mouse = m_window.mapPixelToCoords(sf::Mouse::getPosition(m_window));
sf::FloatRect bounds(mouse.x, mouse.y, 1.0f, 1.0f);
if (bounds.intersects(wkingRect.I dont know what Im doing)) {
std::cout << "King has Been clicked!\n";
return true;
}
else {
return false;
}
}
And finally to move the sprite:
void Game::move()
{
if (m_holdingPiece) {
sf::Vector2f mouse = m_window.mapPixelToCoords(sf::Mouse::getPosition(m_window));
m_wking.setPosition(mouse);
}
}
Sorry if that seems like a lot of code, but I feel like it was all relevant to the problem.

Problem:
You have hardcoded all your variables so even if you create a list and iterate over it to run Game::move() it will move all the pieces instead of only one because the variable m_holdingPiece is the same for all pieces.
Solution:
You should use a more OOP aproach creating a class for the chess pieces while the Game class should probably just control when the game starts, when it ends and check whether moves are legal or not. Explaning all that would be too broad Stack Overflow and it's part of the work you must do, so I will just let an example of how a basic chessPiece class would be to make pieces dragabble.
Example:
Here is an example of how you can make a group of pieces draggable.
#include <SFML/Graphics.hpp>
#include <string>
class chessPiece: public sf::Drawable, public sf::Transformable
{
private:
sf::RenderWindow& window;
sf::Texture texture;
sf::Sprite sprite;
bool moving;
public:
chessPiece(sf::RenderWindow& windowRef, const std::string& fileName): window(windowRef)
{
if(!texture.loadFromFile(fileName))
exit(0);
this->sprite.setTexture(this->texture);
this->moving = false;
this->sprite.setScale(128/this->sprite.getGlobalBounds().width,128/this->sprite.getGlobalBounds().height);
}
chessPiece(chessPiece&& rval): window(rval.window)
{
this->texture = std::move(rval.texture);
this->sprite.setTexture(this->texture);
this->sprite.setScale(rval.sprite.getScale());
this->moving = std::move(rval.moving);
}
chessPiece(const chessPiece& lval): window(lval.window)
{
this->texture = lval.texture;
this->sprite.setTexture(this->texture);
this->sprite.setScale(lval.sprite.getScale());
this->moving = lval.moving;
}
void draw(sf::RenderTarget& target, sf::RenderStates states) const
{
states.transform *= this->getTransform();
target.draw(this->sprite,states);
}
void move(bool& movingAPiece)
{
sf::Vector2f mousePosition = window.mapPixelToCoords(sf::Mouse::getPosition(window));
if(this->moving)
{
this->setPosition(mousePosition.x - this->sprite.getGlobalBounds().width/2,mousePosition.y - this->sprite.getGlobalBounds().height/2);
if(!sf::Mouse::isButtonPressed(sf::Mouse::Button::Left))
{
this->moving = false;
movingAPiece = false;
}
}
else
{
if(sf::Mouse::isButtonPressed(sf::Mouse::Button::Left) && mousePosition.x > this->getPosition().x &&
mousePosition.y > this->getPosition().y && mousePosition.x < this->getPosition().x + this->sprite.getGlobalBounds().width &&
mousePosition.y < this->getPosition().y + this->sprite.getGlobalBounds().height && !movingAPiece)
{
this->moving = true;
movingAPiece = true;
}
}
}
};
int main()
{
sf::RenderWindow window(sf::VideoMode(1000,500),"Window");
std::vector<chessPiece> pieces = {chessPiece(window,"white-king.png"),chessPiece(window,"white-pawn.png")};
pieces[0].setPosition(400,400);
bool movingAPiece = false;
while(true)
{
sf::Event event;
if(window.pollEvent(event))
if(event.type == sf::Event::Closed)
window.close();
for(unsigned int i = 0; i < pieces.size(); i++)
pieces[i].move(movingAPiece);
window.clear();
for(unsigned int i = 0; i < pieces.size(); i++)
window.draw(pieces[i]);
window.display();
}
}

Related

Why does isClick become false when button is not being clicked anymore?

I am working on a button in SFML. std::cout << isClick prints 1 when my mouse is being clicked, but somehow 0 when I let go. I have not put that in my code.
button.cpp:
bool Button::checkClick(sf::RenderWindow& window) {
sf::Vector2i mousePos = sf::Mouse::getPosition(window);
if (mousePos.x > pos.x
&& mousePos.x < pos.x + size.x
&& mousePos.y > pos.y
&& mousePos.y < pos.y + size.y) {
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
button.setFillColor(button_color_click);
isClick = true;
} else if (isClick == true) {
isClick = false;
return true;
} else {
button.setFillColor(button_color_hover);
}
} else {
button.setFillColor(button_color);
}
std::cout << isClick;
return false;
}
button.h:
class Button {
public:
sf::Vector2f pos, size;
sf::RectangleShape button;
sf::Text text;
bool isClick = false;
sf::Color button_color, button_color_hover, button_color_click;
Button(sf::Vector2f pos_, sf::Vector2f size_, std::string text_, sf::Font& font, float font_size, sf::Color button_color_, sf::Color button_color_hover_, sf::Color button_color_click_, sf::Color text_color, float outline, sf::Color outline_color);
void draw(sf::RenderWindow& window);
bool checkClick(sf::RenderWindow& window);
};
How I call it in main.cpp:
if (newGameButton.checkClick(window)) {
resetGame(window, status, ball, velocity, paddle1, paddle2);
std::cout << "clicked\n";
}
Except it is not printing anything.
I want isClick to not return false once I let go.
EDIT: Every other line of code that involves button newGameButton and button class:
void drawGameEnded(sf::RenderWindow& window, gameStatus& status, sf::Text& winningText, Button newGameButton, sf::CircleShape& ball, sf::Vector2f& velocity, sf::RectangleShape& paddle1, sf::RectangleShape& paddle2) {
// change winning text
winningText.setString(status.winner ? "Player 1 Wins!" : "Player 2 Wins!");
// update button
if (newGameButton.checkClick(window)) {
resetGame(window, status, ball, velocity, paddle1, paddle2);
std::cout << "clicked\n";
}
// draw everything on screen
window.clear();
window.draw(winningText);
newGameButton.draw(window);
window.display();
}
Main loop:
sf::Clock deltaTime;
gameStatus status;
while (window.isOpen())
{
sf::Time dt = deltaTime.restart();
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
if (!status.gameEnded) {
drawGame(paddle1, paddle2, ball, velocity, dt, window, paddleSpeed, status);
continue;
}
drawGameEnded(window, status, winningText, newGameButton, ball, velocity, paddle1, paddle2);
}
Initialising newGameButton:
Button newGameButton(sf::Vector2f(400.f, 500.f), sf::Vector2f(400.f, 150.f), "New Game", droid_sans, 24.f, grey1, grey3, grey4, grey2, 10, grey2);
button.cpp (without Button::checkClick()):
#include <SFML/Graphics.hpp>
#include "button.h"
#include <iostream>
Button::Button(sf::Vector2f pos_, sf::Vector2f size_, std::string text_, sf::Font& font, float font_size, sf::Color button_color_, sf::Color button_color_hover_, sf::Color button_color_click_, sf::Color text_color, float outline, sf::Color outline_color) {
button.setPosition(pos_);
button.setSize(size_);
button.setFillColor(button_color_);
button.setOutlineThickness(outline);
button.setOutlineColor(outline_color);
text.setFont(font);
text.setCharacterSize(font_size);
text.setString(text_);
text.setFillColor(text_color);
text.setPosition(pos_.x+(size_.x-text.getGlobalBounds().width)/2, pos_.y+(size_.y-text.getGlobalBounds().height)/2);
pos = pos_;
size = size_;
button_color = button_color_;
button_color_click = button_color_click_;
button_color_hover = button_color_hover_;
}
void Button::draw(sf::RenderWindow& window) {
window.draw(button);
window.draw(text);
}
OK, the problem is that you are copying your button object, and modifying the copy. That's why the isClick variable seems to go back to false.
The copy happens here
void drawGameEnded(sf::RenderWindow& window, gameStatus& status, sf::Text& winningText,
Button newGameButton, sf::CircleShape& ball, sf::Vector2f& velocity,
sf::RectangleShape& paddle1, sf::RectangleShape& paddle2) {
It should be pass by reference to avoid the copy
void drawGameEnded(sf::RenderWindow& window, gameStatus& status, sf::Text& winningText,
Button& newGameButton, sf::CircleShape& ball, sf::Vector2f& velocity,
sf::RectangleShape& paddle1, sf::RectangleShape& paddle2) {
In below condition, you are not returning anything from the method:
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
button.setFillColor(button_color_click);
isClick = true;
}
As a result, execution continues till the last statement i.e. "return false".
Try adding return statement as below.
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
button.setFillColor(button_color_click);
isClick = true;
return true;
}

SFML Window not responding to inputs

I recently started working on SFML. I just created a window then later started to separating my main code. First i wrote a window class.
Nothing special, just draws my window and prints mouse coordinates.
#include "Window.hpp"
#include <iostream>
Window::Window()
{
SetWindow(800,600, "JUST SFML");
}
void Window::SetWindow(unsigned int width, unsigned int height, sf::String title)
{
m_sfmlWindow.create(sf::VideoMode(width, height), title);
}
void Window::StartDrawing()
{
m_sfmlWindow.clear();
}
void Window::EndDrawing()
{
m_sfmlWindow.display();
}
bool Window::isClosed()
{
return !m_sfmlWindow.isOpen();
}
void Window::EventControl()
{
sf::Event event;
while (m_sfmlWindow.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
m_sfmlWindow.close();
}
if (event.type == sf::Event::MouseMoved)
{
std::cout << event.mouseMove.x << " , " << event.mouseMove.y << std::endl;
}
}
}
void Window::Draw(sf::Drawable& shape)
{
m_sfmlWindow.draw(shape);
}
Everything worked perfectly.
Then i tought i need a GameManager class that i can just use in my main class.
#include "GameManager.hpp"
GameManager::GameManager()
{
m_shape.setRadius(30.0f);
m_shape.setFillColor(sf::Color::Magenta);
m_incVal = 1.0f;
m_posX = 10.0f;
m_frameRate = 1.0f / 60.0f;
}
GameManager::~GameManager()
{
}
void GameManager::InputControl()
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
m_incVal = 1.0f;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
m_incVal = -1.0f;
}
}
void GameManager::UpdateScene()
{
if (m_deltaTime.asSeconds() >= m_frameRate)
{
m_posX += m_incVal;
m_shape.setPosition(m_posX, 300);
m_deltaTime -= sf::seconds(m_frameRate);
}
}
void GameManager::DrawScene()
{
m_window.StartDrawing();
m_window.Draw(m_shape);
m_window.EndDrawing();
}
void GameManager::RestartClock()
{
m_deltaTime += m_clock.restart();
}
bool GameManager::isFinished()
{
return m_window.isClosed();
}
It was perfectly working when i use window object, later i've changed to gamemanager it start being not responding.. I can get keyboard inputs but not mouse move coordinates. Where am i make it wrong?
#include <SFML/Graphics.hpp>
#include "GameManager.hpp"
#include "Window.hpp"
int main()
{
GameManager gameManager;
while (!gameManager.isFinished())
{
gameManager.InputControl();
gameManager.UpdateScene();
gameManager.DrawScene();
gameManager.RestartClock();
}
/*
sf::CircleShape shape(100.0f);
shape.setFillColor(sf::Color::Magenta);
shape.setOutlineThickness(5.0f);
shape.setOutlineColor(sf::Color::White);
Window window;
while (!window.isClosed())
{
window.EventControl();
window.StartDrawing();
window.Draw(shape);
window.EndDrawing();
}
*/
return 0;
}
In order to receive events (such as the mouse moving), you need to call pollEvents on the window.
In your commented code, you were doing this through your Window::EventControl() method. Your new GameManager class isn't calling this, however, so you aren't receiving any events.

C++ Sfml, How can I create a collision box for my Sprite

I have a question to sfml.
I am relative new to C++ and sfml.
I am trying to create a Space Invaders type of game.
I currently have some problems with collision,
between the enemy's bullets and the rocket,
I'm talking about line 145. This line:
if (collide(rocket, enemy_bullets[i]))
{
window.close();
}
Can you create something like a collision box?,
because I don't want to collide with the whole rocket sprite,
I only want to collide with parts of it, e.g not the transparent parts.
#include <SFML/Graphics.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <chrono>
void print(std::string string)
{
std::cout << string << std::endl;
}
sf::CircleShape create_bullet(sf::Vector2f possition, sf::Int16 offset)
{
sf::CircleShape circel;
circel.setRadius(10);
circel.setPosition(possition.x + offset, possition.y);
return circel;
}
bool collide(sf::Sprite a, sf::CircleShape b)
{
return a.getGlobalBounds().intersects(b.getGlobalBounds());
}
int main()
{
int speed;
speed = 25;
sf::RenderWindow window(sf::VideoMode(1200, 800), "Space Invaders", sf::Style::Titlebar | sf::Style::Close);
sf::Texture rocket_texture;
if (!rocket_texture.loadFromFile("data/rocket.png"))
{
print("Problem with loding file data/rocket.png");
exit(-1);
}
sf::Texture enemy_texture;
if (!enemy_texture.loadFromFile("data/enemy.png"))
{
print("Problem with loding file data/enemy.png");
exit(-1);
}
sf::Sprite rocket;
sf::Sprite enemy;
std::chrono::milliseconds couldown = std::chrono::milliseconds(0);
std::chrono::milliseconds time;
std::chrono::milliseconds enemy_couldown = std::chrono::milliseconds(0);
bool enemy_fire = false;
float bulletspeed = 0.02;
// sf::CircleShape test = create_bullet();
int changex;
rocket.setTexture(rocket_texture);
rocket.setPosition(500, 650);
rocket.scale(0.5, 0.5);
std::vector<sf::Sprite> enemy_list;
std::vector<sf::CircleShape> player_bullets;
std::vector<sf::CircleShape> enemy_bullets;
enemy.setTexture(enemy_texture);
enemy.scale(0.2, 0.2);
for (int i =0; i<8; i++)
{
enemy.setPosition(i * 150, 400);
enemy_list.push_back(enemy);
}
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
changex = 0;
switch (event.type)
{
// window closed
case sf::Event::Closed:
window.close();
break;
// key pressed
case sf::Event::KeyPressed:
if (event.key.code == sf::Keyboard::A)
{
if (rocket.getPosition().x >= 0 )
{
changex = changex - speed;
}
}
else if (event.key.code == sf::Keyboard::D)
{
if (rocket.getPosition().x <= 1100)
{
changex = changex + speed;
}
}
else if (event.key.code == sf::Keyboard::Space)
{
time = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch());
if (couldown < time - std::chrono::milliseconds(100)){
couldown = time;
player_bullets.push_back(create_bullet(rocket.getPosition(), 47));
}
}
break;
default:
break;
}
rocket.move(changex, 0);
}
window.clear();
window.draw(rocket);
//swindow.draw(test);
for (int i=0; i<player_bullets.size();i++)
{
player_bullets[i].move(0,-bulletspeed);
window.draw(player_bullets[i]);
if (player_bullets[i].getPosition().y < 0)
{
player_bullets.erase(player_bullets.begin()+i);
}
}
for (int i = 0; i < enemy_bullets.size(); i++)
{
enemy_bullets[i].move(0, bulletspeed);
window.draw(enemy_bullets[i]);
if (enemy_bullets[i].getPosition().y > 800)
{
enemy_bullets.erase(enemy_bullets.begin() + i);
}
if (collide(rocket, enemy_bullets[i]))
{
window.close();
}
}
time = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch());
if (enemy_couldown < time - std::chrono::milliseconds(2000))
{
enemy_couldown = time;
enemy_fire = true;
}
// Draw all Enemys
for (int i = 0; i < enemy_list.size(); i++)
{
for (int j = 0; j < player_bullets.size(); j++)
{
if (collide(enemy_list[i], player_bullets[j]))
{
enemy_list.erase(enemy_list.begin() + i);
}
}
if (enemy_fire)
{
enemy_couldown = time;
// ADD: Move enemys
enemy_bullets.push_back(create_bullet(enemy_list[i].getPosition(), 13));
}
window.draw(enemy_list[i]);
}
enemy_fire = false;
window.display();
}
return 0;
}
If you have any idea how to do that,
I would like to hear it.
Thanks, in advance
You can make a class that derives from sf::Sprite that has a sf::FloatRect for a hitbox, you will need to make a function to set the hitbox.
class Sprite : public sf::Sprite {
sf::FloatRect hitbox;
}
You can move the hitbox to the sprites location with:
getTransform().transformRect(hitbox);
I have used this in the past for hitboxes with SFML.
Edit, Here is an full example program:
#include <SFML/Graphics.hpp>
/// custom sprite class with hitbox
class HitboxSprite : public sf::Sprite {
public:
/// sets the hitbox
void setHitbox(const sf::FloatRect& hitbox) {
m_hitbox = hitbox;
}
/// gets the hitbox (use this instead of getGlobalBounds())
sf::FloatRect getGlobalHitbox() const {
return getTransform().transformRect(m_hitbox);
}
private:
sf::FloatRect m_hitbox;
};
int main() {
sf::RenderWindow window(sf::VideoMode(256, 128), "Example");
// create two sprites, player and enemy
HitboxSprite player;
player.setPosition({ 64.f, 64.f });
HitboxSprite enemy;
enemy.setPosition({ 128.f, 64.f });
enemy.setColor(sf::Color::Red);
// create sprite texture and apply to sprites
sf::Texture square_texture;
square_texture.loadFromFile("32x32square.png");
player.setTexture(square_texture);
enemy.setTexture(square_texture);
// set custom hitboxes
// (this one starts (8, 8) pixels from the top left and has a size of (16, 16)
// (this means the hitbox will be 1/2 of the square in the middle)
player.setHitbox({ 8.f, 8.f, 16.f, 16.f });
enemy.setHitbox({ 8.f, 8.f, 16.f, 16.f });
sf::Clock clock;
while (window.isOpen()) {
// process events
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed)
window.close();
}
const float dt = clock.restart().asSeconds();
constexpr float player_speed = 128.f;
// move player with arrow keys
player.move({
player_speed * dt * (sf::Keyboard::isKeyPressed(sf::Keyboard::Right) - sf::Keyboard::isKeyPressed(sf::Keyboard::Left)),
player_speed * dt * (sf::Keyboard::isKeyPressed(sf::Keyboard::Down) - sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
});
// check for collision
const bool colliding = player.getGlobalHitbox().intersects(enemy.getGlobalHitbox());
// set background color based on collision
window.clear(colliding ? sf::Color::Green : sf::Color::Blue);
// draw sprites
window.draw(enemy);
window.draw(player);
// display
window.display();
}
return 0;
}
If you need any part explained let me know.
Here is the translucent png I made with the center part being the hitbox:

View class is not receiving updated Model class data

Model class
class spaceshipModel {
private:
Vector2f position;
float speed, acceleration, energy, fuel;
public:
//Contructor
spaceshipModel() : position(0, 0), speed(0), acceleration(0), energy(0), fuel(0) {}
//Destructor
~spaceshipModel() {}
//Sets
void setPosition(float _x, float _y) { position.x = _x; position.y = _y; }
void setSpeed(float _speed) { speed = _speed; }
void setAcceleration(float _acceleration) { acceleration = _acceleration; }
void setEnergy(float _energy) { energy = _energy; }
void setFuel(float _fuel) { fuel = _fuel; }
//Gets
Vector2f getPosition() { return position; }
float getSpeed() { return speed; }
float getAcceleration() { return acceleration; }
float getEnergy() { return energy; }
float getFuel() { return fuel; }
};
View class
class spaceshipView {
private:
Texture* image;
Sprite sprite;
spaceshipModel model;
public:
//Constructor
spaceshipView() : image(0) {}
//Destructor
~spaceshipView() {}
//Setting the image
void setImage(Texture* _image) { image = _image; }
//Drawing the image
void drawImage(RenderWindow* _window) {
sprite.setTexture(*image);
sprite.setPosition(model.getPosition());
sprite.setScale(Vector2f(0.2f, 0.2f));
_window->draw(sprite);
_window->display();
}
};
Main
A left a lot of the code out, but I then call this in main:
int main() {
//Call instance of the Spaceship model
spaceshipModel shipModel;
//Call instance of the Spaceship view
spaceshipView shipView;
//Create the texture of the spaceship from file
Texture spaceship;
spaceship.loadFromFile("spaceship.png");
//Create the window
RenderWindow window(VideoMode(800, 600), "Spaceship with MVC");
//Run the program as long as the window is open
while (window.isOpen()) {
//Check all the window's events that were triggered since the last iteration of the loop
Event event;
while (window.pollEvent(event)) {
//"Close requested" event: we close the window
switch (event.type) {
//Window closed by pressing the X
case Event::Closed:
window.close();
break;
//Checking for key pressed event
case Event::KeyPressed:
//Pressing esc to close the window
if (event.key.code == Keyboard::Escape) {
window.close();
}
break;
//We don't process other types of events
default:
break;
}
//Clear screen with white BG
window.clear(Color::White);
//TESTING THE SETTING OF THE POSITION
std::cout << shipModel.getPosition().x << ", " << shipModel.getPosition().y << std::endl;
shipModel.setPosition(100, 100);
std::cout << shipModel.getPosition().x << ", " << shipModel.getPosition().y << std::endl;
//Set and draw the image
shipView.setImage(&spaceship);
shipView.drawImage(&window);
}
}
return 0;
}
The spaceship draws perfectly, but is only set at (0, 0). Even when setting the position to (100, 100) like it shows above. The image stays at (0, 0). As I'm using the getPosition function from the Model class in the View class, I don't think the data is being updated correctly, even when the cout test does show a change.
What am I doing wrong? Can someone give me some pointers?
In the code snippet above, shipModel object from main() and shipView.model are two distinct objects. You can either let your shipView be aware of the model using a setter in spaceshipView, or call shipView.model's methods directly.

C++/SFML Graphing Application-Repeating Texture/Sprite

I was taking up the challenge of creating a program to use for graphing simple math equations using C++ and the SFML 2.3.2. graphics library, and I've got the graph paper texture rendering fine. However, the problem I've had is getting it to repeat itself as I scroll around.
I was using sf::View to get it to pan, and it works well, but as soon as it pans outside the pre-defined size of the sprite it's just a blank background.
I thought of trying to determine when I'm outside the bounds of the sprite, and then expanding the size of the sprite or moving it, but then drawings (such as a graph) won't persist if the sprite is changed/reset.
What I need to know is how to create a background of a texture, in this case graphing paper, that will tile/repeat in every direction, and allow drawings to be made on top of it that persist if they move off screen.
Code is below:
GraphPaper.h:
#pragma once
#include "stdafx.h"
class GraphPaper
{
public:
GraphPaper();
~GraphPaper();
bool isObjectLoaded();
sf::Sprite getSprite();
private:
void load(std::string filename);
bool isLoaded;
sf::Texture texture;
sf::Sprite sprite;
const std::string file = "images/Graph-Paper.png";
};
GraphPaper.cpp: This is where I create and load the needed sprite and texture.
I set the texture to repeat inside the "load(string filename)" method, but that only affects it within the bounds set by the sprite's rectangle.
#include "GraphPaper.h"
GraphPaper::GraphPaper() : isLoaded(false)
{
load(file);
assert(isObjectLoaded());
}
GraphPaper::~GraphPaper() {};
void GraphPaper::load(std::string filename)
{
if (texture.loadFromFile(filename) == false)
isLoaded = false;
else
{
texture.setRepeated(true);
sprite.setTexture(texture);
//Huge size to at least make it look like it's infinite,
//but a temporary solution.
sprite.setTextureRect(sf::IntRect(0,0,10000,10000));
isLoaded = true;
}
}
bool GraphPaper::isObjectLoaded()
{
return isLoaded;
}
sf::Sprite GraphPaper::getSprite()
{
return sprite;
}
MainWindow.h:
#pragma once
#include "GraphPaper.h"
#include "stdafx.h"
class MainWindow
{
public:
void close();
void start();
void moveCamera(sf::Event);
private:
bool leftMousePressed, rightMousePressed, isExiting;
int r, g, b, mouseX, mouseY;
GraphPaper paper;
const sf::Color white = sf::Color(255, 255, 255);
sf::RenderWindow mainWindow;
sf::View view;
};
MainWindow.cpp: This is what handles all the drawing, and, at the moment, all input processing. "start()" is simply called from the main method.
#include "MainWindow.h"
#include "GraphPaper.h"
#include "stdafx.h"
void MainWindow::start()
{
sf::RectangleShape rectangle = sf::RectangleShape(sf::Vector2f(120, 50));
rectangle.setFillColor(sf::Color(0,0,0));
mainWindow.create(sf::VideoMode(1024, 768, 32), "Test");
sf::View view(sf::FloatRect(0,0,1000,600));
mainWindow.setView(view);
leftMousePressed, rightMousePressed, isExiting = false;
sf::Event currentEvent;
mainWindow.clear(white);
mainWindow.draw(paper.getSprite());
mainWindow.display();
while (!isExiting)
{
while (mainWindow.pollEvent(currentEvent))
{
switch (currentEvent.type)
{
case sf::Event::MouseMoved:
{
if (rightMousePressed == true)
{
std::cout << "Mouse Panned\n";
}
if (leftMousePressed == true)
{
std::cout << "Mouse is Drawing\n";
}
break;
}
case sf::Event::MouseButtonPressed:
{
std::cout << "Mouse Pressed\n";
mouseX = currentEvent.mouseButton.x;
mouseY = currentEvent.mouseButton.y;
if (currentEvent.mouseButton.button == sf::Mouse::Left)
{
leftMousePressed = true;
}
else if (currentEvent.mouseButton.button == sf::Mouse::Right)
{
rightMousePressed = true;
}
break;
}
case sf::Event::MouseButtonReleased:
{
std::cout << "Mouse Released\n";
if (currentEvent.mouseButton.button == sf::Mouse::Left)
{
leftMousePressed = false;
}
else if(currentEvent.mouseButton.button == sf::Mouse::Right)
{
rightMousePressed = false;
}
break;
}
case sf::Event::KeyPressed:
{
if (currentEvent.key.code == sf::Keyboard::Escape)
{
close();
}
//No right movement yet, was testing.
else if (currentEvent.key.code == sf::Keyboard::Left)
{
moveCamera(currentEvent);
}
break;
}
case sf::Event::Closed:
{
close();
break;
}
}
}
}
}
void MainWindow::moveCamera(sf::Event key)
{
std::cout << "Inside moveCamera\n";
//No right movement yet, was testing.
//Movement is also hardcoded for testing as well.
view.move(100, 0);
mainWindow.setView(view);
mainWindow.clear(white);
mainWindow.draw(paper.getSprite());
mainWindow.display();
std::cout << "Leaving moveCamera\n";
}
void draw()
{
//mainWindow.draw();
}
void MainWindow::close()
{
std::cout << "Closing...\n";
mainWindow.close();
isExiting = true;
}