C++ Error (C2280) tring to access a deleted function [closed] - c++

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 3 years ago.
Improve this question
So, i was trying to make a 2d game with opengl and sfml,
so i created a button class in an input namespace,
i made a render() function in it, but when i call it (no matter wheter i use a pointer or i don't) even if I pass all the required arguments it's still giving
me an error, saying that I'm trying to access a deleted function
here's the Button header:
#pragma once
#include <SFML/Graphics.hpp>
#include "InputManager.h"
namespace input {
class Button {
private:
bool m_Selected, m_Clicked;
sf::Vector2f m_Position;
sf::Sprite m_Sprite;
sf::Texture m_Texture;
public:
Button(sf::Vector2f position, sf::Sprite sprite);
Button(sf::Vector2f position, sf::Texture texture);
Button(sf::Vector2f position);
Button();
~Button();
void update(engine::InputManager inputManager);
void render(sf::RenderWindow window);
inline bool isClicked() { return m_Clicked; }
inline bool isSelected() { return m_Selected; }
inline sf::Vector2f getPosition() { return m_Position; }
void setPosition(sf::Vector2f position) { m_Position = position; }
};
}
here is Button.cpp:
#include "Button.h"
namespace input {
Button::Button(sf::Vector2f position, sf::Sprite texture) : m_Position(position), m_Sprite(texture) {
m_Sprite.setPosition(m_Position);
}
Button::Button(sf::Vector2f position, sf::Texture texture) : m_Position(position), m_Texture(texture) {
m_Sprite.setTexture(m_Texture);
m_Sprite.setPosition(m_Position);
}
Button::Button(sf::Vector2f position) : m_Position(position) {
m_Sprite.setPosition(m_Position);
}
void Button::update(engine::InputManager inputManager) {}
void Button::render(sf::RenderWindow window) {
window.draw(m_Sprite);
}
Button::~Button() {
delete &m_Position;
delete &m_Texture;
delete &m_Clicked;
delete &m_Selected;
}
}
Here is the main.cpp code:
#include <iostream>
#include <vector>
#include "InputManager.h"
#include "Button.h"
#define WIDTH 800
#define HEIGHT 600
#define TITLE "C++ Platformer"
int main() {
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), TITLE, sf::Style::Close | sf::Style::Titlebar);
sf::Texture ButtonTexture;
ButtonTexture.loadFromFile("Texture.png");
sf::Sprite sprite;
sprite.setTexture(ButtonTexture);
input::Button* button = new input::Button(sf::Vector2f(100.f, 100.f), sprite);
int fps = 0;
int ticks = 0;
sf::Clock clock;
sf::Clock FpsClock;
sf::Time time;
sf::Time Fpstime;
long delta = 0;
long target = 1000000 / 60;
engine::InputManager IManager;
sf::Event ev;
while (window.isOpen()) {
while (window.pollEvent(ev)) {
if (ev.type == sf::Event::Closed) window.close();
if (ev.key.code == sf::Keyboard::Escape) window.close();
if (ev.type == sf::Event::KeyPressed) IManager.onKeyDown(ev.key.code);
}
time = clock.getElapsedTime();
if (target > time.asMicroseconds()) {
clock.restart();
// UPDATE
button->update(IManager);
delta = time.asMicroseconds() % 60;
target = 1000000 / 60 + delta;
ticks++;
} Fpstime = FpsClock.getElapsedTime();
if (Fpstime.asSeconds() >= 1) {
std::cout << "FPS: " << fps << " Ticks: " << ticks << std::endl;
FpsClock.restart();
fps = 0;
}
fps++;
// RENDER
button->render(window);
}
return EXIT_SUCCESS;
}
I've searched on microsoft docs and on other stackoverflow questions, but i couldn't find any case like mine, hope someone can help, thanks

The problem is that sf::RenderWindow is non-copyable. This is a good thing, because it means that you can't accidentally get confused about having multiple windows, and it's clear when you create a new window and where it is owned. However, in a function signature like
void Button::render(sf::RenderWindow window);
you are accepting an sf::RenderWindow by value. This means that whenever you call Button::render and pass a window, that window is copied before it is received by the function. See the problem?
You need to accept the render window by reference, to make sure you don't try to create a copy:
void Button::render(sf::RenderWindow& window);
Aside: as pointed out by NathanOliver, you try to delete all of your instance members in your Button destructor, even though they are not pointers, and you didn't specifically allocate them with new. Only delete what you new, and avoid new/delete altogether if you can.
If you're unsure about what it means to pass by reference, or what new and delete are for, I would suggest you pick up a copy of a good C++ book.

First of all. what the...
Button::~Button() {
delete &m_Position;
delete &m_Texture;
delete &m_Clicked;
delete &m_Selected;
}
You shouldn't do that.
But back to your error. It is quite descriptive. You are trying to use a function that was most likely deleted explicitly. Like so:
struct Test {
Test(const Test & other) = delete;
};
Obviously I cannot copy Test now because I deleted the copy constructor.
Which is what sf::RenderWindow does by inheriting from sf::NonCopyable class.
And since your render() method takes it by copy and not reference. It obviously tries to do something it was forbidden.
You should change from this:
void Button::render(sf::RenderWindow window)
To this:
void Button::render(sf::RenderWindow & window)

Related

Sprites aren't animating

I've been trying to update enemy sprites in my game by creating a vector of pointers to enemy objects and then using an update function to animate the sprites belonging to the objects. Although the enemy sprites are displayed on the screen, they won't get updated so they look as though they're frozen.
Here's how I've written my code:
#include<iostream>
#include<SFML/Graphics.hpp>
#include<math.h>
#include<vector>
#include<cstdlib>
#include "Enemy.h"
sf::RenderWindow window(sf::VideoMode(1920, 1080), "Zombie game", sf::Style::Default);
std::vector<Enemy*>enemies;
int main()
{
window.setFramerateLimit(60);
sf::Clock clock;
Enemy *enemy = new Enemy();
enemy->init("Assets/graphics/zombieSpriteSheetWalk.png", 4, 1.0f, sf::Vector2f(200.0f, 200.0f), sf::Vector2i(100, 107));
enemies.push_back(enemy);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed || event.key.code == sf::Keyboard::Escape) window.close();
}
sf::Time dt = clock.restart();
for (int i = 0; i < enemies.size(); i++)
{
Enemy *enemy = enemies[i];
enemy->update(dt.asSeconds());
}
window.clear();
for (Enemy* enemy : enemies) window.draw(enemy->getSprite());
window.display();
}
return 0;
}
Enemy.h file:
#pragma once
#include<SFML/Graphics.hpp>
#include <iostream>
class Enemy
{
public:
Enemy();
~Enemy();
void init(std::string textureName, int frameCount, float animDuration, sf::Vector2f position, sf::Vector2i spriteSize);
void update(float dt);
sf::Sprite& getSprite();
void test();
private:
sf::Texture m_texture;
sf::Sprite m_sprite;
sf::Vector2f m_position;
int m_frameCount; //no. of frames in animation
float m_animDuration; //How long animation lasts (speed)
float m_elapsedTime; //keeps track of how long game has been running
sf::Vector2i m_spriteSize; //Size of each frame
};
Enemy.cpp file:
#include "Enemy.h"
Enemy::Enemy()
{
}
Enemy::~Enemy()
{
}
void Enemy::init(std::string textureName, int frameCount, float animDuration, sf::Vector2f position, sf::Vector2i spriteSize)
{
m_position = position;
m_frameCount = frameCount;
m_animDuration = animDuration;
m_spriteSize = spriteSize;
m_texture.loadFromFile(textureName.c_str());
m_sprite.setTexture(m_texture);
m_sprite.setTextureRect(sf::IntRect(0, 0, m_spriteSize.x, m_spriteSize.y));//sets which part of sprite sheet we want to display
m_sprite.setPosition(m_position);
m_sprite.setOrigin((m_texture.getSize().x / frameCount) - 25.0f, m_texture.getSize().y / 2);
}
void Enemy::update(float dt)
{
m_elapsedTime += dt;
int animFrame = static_cast<int>((m_elapsedTime / m_animDuration) * m_frameCount) % m_frameCount; //calculates current animation frame number. static_class converts float to int
m_sprite.setTextureRect(sf::IntRect(animFrame * m_spriteSize.x, 0, m_spriteSize.x, m_spriteSize.y)); //updates part of sprite sheet to be displayed
}
sf::Sprite& Enemy::getSprite()
{
return m_sprite;
}
You're always at time 0
From the SFML Clock header:
////////////////////////////////////////////////////////////
/// \brief Restart the clock
///
/// This function puts the time counter back to zero.
/// It also returns the time elapsed since the clock was started.
///
/// \return Time elapsed
///
////////////////////////////////////////////////////////////
Time restart();
Possible Fix
Use sf::Clock for dt instead of sf::Time and move it out of the event loop (you want it to accumulate as the program runs, not reset to 0 every time through.
Replace:
enemy->update(dt.asSeconds());
with
enemy->update(dt.getElapsedTime());
More Notes
I'm making some assumptions in this answer of types because your example was incomplete. One big omission you made was not including the class Enemy definition (especially the types of the member variables).
Based on the definition of Enemy::init, I would assume it would contain the following:
class Enemy
{
float m_elapsedTime;
float m_animDuration;
int m_frameCount;
...
However, those definitions result in the following compilation error in your version of Enemy::update:
enemy.cpp: In member function ‘void Enemy::update(float)’:
enemy.cpp:14:88: error: invalid operands of types ‘float’ and ‘int’ to binary ‘operator%’
int animFrame = static_cast<int>(((m_elapsedTime / m_animDuration) * m_frameCount) % m_frameCount);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~
floats
You don't want to run animations that are a function of game time if game time is a float. The reason why not is that floating point numbers have different precision in different value ranges, the closer the number is to 0, the more precision you have, and the farther you are from 0, the less precision you have. This means that as game time progresses, your animations will not run at a constant rate (they will appear to be choppy, inconsistent, not smooth after enough time has elapsed).
A better approach would be to have an wrapping animation counter that adds the delta times and then wraps back to zero (or perhaps wraps back to 0 + some remainder, perhaps using a function like fmod). This will make sure your animation frame rate is consistent for the entire duration of the game.

SFML not displaying sprite

I recently started working with sfml and I cannot solve this problem. I have two classes which should work and display my sprite but nothing shows on the screen. I have tried a few things but none of them have worked so far, that's why I've decided to ask here :/
Thanks for any of your help, tips will also be appreciated ;)
Main.cpp:
#include <SFML\Graphics.hpp>
#include "Player.hpp"
sf::RenderWindow frame;
sf::Texture player_texture;
Player player(player_texture, 100, 100);
bool quit;
bool handle_events() {
sf::Event event;
if (frame.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
return true;
}
}
return false;
}
void update() {
}
void render() {
frame.clear(sf::Color(127, 142, 123));
player.draw_player(frame);
frame.display();
}
void run() {
while (quit != true) {
quit = handle_events();
update();
render();
}
}
int main() {
player_texture.loadFromFile("player.png");
frame.create(sf::VideoMode(800, 600), "frame");
run();
return 0;
}
Player.cpp:
#include "Player.hpp"
Player::Player(sf::Texture & player_texture, int pos_x, int pos_y) {
player_sprite.setTexture(player_texture);
player_sprite.setPosition(pos_x, pos_y);
player_sprite.scale(4, 4);
}
void Player::draw_player(sf::RenderWindow & frame) {
frame.draw(player_sprite);
}
Player.hpp:
#ifndef Player_hpp
#define Player_hpp
#include <SFML\Graphics.hpp>
#include <iostream>
class Player
{
private:
sf::Sprite player_sprite;
public:
Player::Player(sf::Texture & player_texture, int pos_x, int pos_y);
void Player::draw_player(sf::RenderWindow & frame);
};
#endif
Your player sprite is initialized with an empty texture. Then you load another picture into the texture and the player sprite does not know this. All the calculations the sprite made on the texture (for example size) are now invalid.
Make sure you load the texture before you pass it to the sprite. And don't change it afterwards.
nvoigt is completely right.
You set the the texture of the sprite first (in the player constructor) and load it afterwards (in the main function):
...
player_sprite.setTexture(player_texture);
...
player_texture.loadFromFile("player.png");
...
You either have to reload the texture again with .setTexture inside the main function after the loading. Or you have to complete restructure you code.
By the way, this if (frame.pollEvent(event)) is not a good idea.
There could have been multiple events triggered in on frame, for example mouse movement and window close after that. With the if you would only handle the first event in this frame, which is the mouse movement, even if you were looking for the second event. So you should do it with a while (while (frame.pollEvent(event))) to make sure that all the events are being handled.
Are you sure the texture is loaded correctly? Check the return value of sf::Texture::loadFromFile to test.
More over, why your class Player does not extends from sf::Sprite? I think inheritance would be more appropriated that composition in this case.
Also, in your function handle_events, you can directly call the method RenderWindow::close when the user wants to close the window. Then, in your function run, call RenderWindow::isOpen to check if your app can continue. It's would be less dirty than this ugly not initialised quit variable.

sprite out of scope because it's local

I'm having trouble implementing this function.
//Engine.cpp
void Game::createPlayer(sf::Sprite &player)
{ ///Can't get this to work
sf::Texture player_texture;
if (!player_texture.loadFromFile("player.png"))
{
//Error Loading
}
player.setTexture(player_texture);
}
I want it to replace the "creates player in void Game::run but I realize that Player_texture is local to Createplayer and that it won't exist when the function returns.
void Game::run()
{
sf::RenderWindow window(sf::VideoMode(SCREEN_X, SCREEN_Y), "Shogun Master");
srand((unsigned int)time(NULL));
//Creates Player [Makes into function]
sf::Texture player_texture;
player_texture.loadFromFile("sprites/player.png");
sf::Sprite player(player_texture);
//Creates Enemy [Make into function]
sf::Texture enemy_texture;
enemy_texture.loadFromFile("sprites/enemy.png");
sf::Sprite enemy[MAX_ENEMIES];
for (int x = 0; x < MAX_ENEMIES; x++)
{
enemy[x].setTexture(enemy_texture);
enemy[x].setPosition(rand_int(100, SCREEN_X - 100), rand_int(100, SCREEN_Y - 100)); //Spawning Point
}
//Sets Positions
player.setPosition(500, 300);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
check_closeWindows(event, window); //Closes Game if Executed
player_movement(event); //Moves Character
attack(event); //Character's attacks
}
border(player); //Border so player does not go off screen
for (int x = 0; x < total_enemies; x++)
border(enemy[x]);
movementUpdate(player, enemy); //Player & Enemy Movement Updates
collision(player, enemy[0]);
window.clear();
window.draw(player); //Draws Player
for (int x = 0; x < total_enemies; x++)
window.draw(enemy[x]); //Draws Enemy
window.display();
}
}
so how would I implement this, so that my sprite doesn't return a white box because it went out of scope.
//Engine.h
void Game::createPlayer(sf::Sprite &player);
The documentation (http://www.sfml-dev.org/documentation/2.0/classsf_1_1Sprite.php#a3729c88d88ac38c19317c18e87242560) for the setTexture() method you're calling says:
The texture argument refers to a texture that must exist as long as the sprite uses it. Indeed, the sprite doesn't store its own copy of the texture, but rather keeps a pointer to the one that you passed to this function. If the source texture is destroyed and the sprite tries to use it, the behaviour is undefined.
One way to deal with this would be to make your own struct or class which contains both the Sprite and its texture:
struct SpriteWithTexture
{
sf::Texture texture;
sf::Sprite sprite;
SpriteWithTexture()
{
sprite.setTexture(texture);
}
SpriteWithTexture(const SpriteWithTexture& that)
: texture(that.texture)
{
sprite.setTexture(texture);
}
SpriteWithTexture& operator=(const SpriteWithTexture& that)
{
texture = that.texture;
sprite.setTexture(texture);
return *this;
}
};
Then you can return this from your function:
SpriteWithTexture Game::createPlayer()
{
SpriteWithTexture player;
if (!player.texture.loadFromFile("player.png"))
{
//Error Loading
}
return player;
}
Now the texture will always live as long as the sprite.
Note however that when you construct your "enemies" you use a single texture for all of them. To enable sharing one texture between many sprites, we can enhance the above:
struct SpriteWithTexture
{
std::shared_ptr<sf::Texture> texture;
sf::Sprite sprite;
SpriteWithTexture(const std::shared_ptr<sf::Texture>& texture_)
: texture(texture_)
{
sprite.setTexture(*texture);
}
};
Now you can use it this way:
std::shared_ptr<sf::Texture> player_texture(new sf::Texture);
player_texture->loadFromFile("sprites/player.png");
SpriteWithTexture player(player_texture);
std::shared_ptr<sf::Texture> enemy_texture(new sf::Texture);
enemy_texture->loadFromFile("sprites/enemy.png");
std::vector<SpriteWithTexture> enemies;
for (int x = 0; x < MAX_ENEMIES; x++)
{
enemies.emplace_back(enemy_texture); // construct enemy Sprite
enemies.back().sprite.setPosition(rand_int(100, SCREEN_X - 100), rand_int(100, SCREEN_Y - 100)); //Spawning Point
}
Now all the enemies in the vector share a single texture. Maybe this matters for efficiency.

use of deleted function ‘sf::RenderWindow& sf::RenderWindow::operator=(const sf::RenderWindow&)’

I'm pretty new to C++ so please go easy on me.
I'm trying to use sfml to create a RenderWindow. Then, on creation of a player, that player's associated "window" object gets set to the RenderWindow created previously. My purpose is to be able to run window methods, such as window.draw(), from the player object, i.e.:
player::drawSprite() {
window.draw(sprite);
}
However, I run into the error:
error: use of deleted function ‘sf::RenderWindow& sf::RenderWindow::operator=(const sf::RenderWindow&)’
window = win;
^
Further down in the error log, I also see:
error: initializing argument 1 of ‘Player::Player(sf::RenderWindow)’
Player(sf::RenderWindow win)
^
My code (with anything not pertinent to the question omitted) is as follows:
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <cstring>
#include <cstdlib>
#include <iostream>
class Player
{
private:
float x;
float y;
float speed;
sf::RenderWindow window;
public:
Player(sf::RenderWindow win)
{
x = 640;
y = 360;
speed = 5;
window = win;
}
};
int main()
{
//Window Initialization
sf::RenderWindow window(sf::VideoMode(800, 600), "My window");
//Player Creation
Player player(window);
}
I believe the problem may have something to do with const's, references, or the like, but I am not familiar enough with C++ to easily identify it. How can I remedy this issue?
You should use reference or pointer to window object as I don't think that you will want each player to have its own window.
Thus your Player should look like:
class Player
{
private:
float x;
float y;
float speed;
sf::RenderWindow& window; // reference
public:
Player(sf::RenderWindow& win) // accepts reference
: window(win) // stores reference
{
x = 640;
y = 360;
speed = 5;
// window = win;
}
};

SFML 2.0 Looping a sprite to display more than once

I have asked a similar question in the past but I still can't get my head around this. I am doing an invaders game based on SFML 2.0. So far I have one sprite sheet which runs through using my clock. This part works just fine:
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <iostream>
#include <string>
int spriteWalkSpeed = 10;
int DownspriteWalkSpeed = 5;
int up=-spriteWalkSpeed, down=DownspriteWalkSpeed, left=-spriteWalkSpeed, right=spriteWalkSpeed;
int xVelocity =0, yVelocity=0;
const int SC_WIDTH=800;
const int SC_HEIGHT= 600;
const float REFRESH_RATE =0.01f; //how often we draw the frame in seconds
const double delay=0.1;
const int SPRITEROWS=1; //number of ROWS OF SPRITES
const int SPRITECOLS=2;//number of COLS OF SPRITES
std::string gameOver = "Game Over";
int main()
{
// Create the main window
sf::RenderWindow App (sf::VideoMode(SC_WIDTH, SC_HEIGHT, 32), "Space Invaders!",sf::Style::Close );
// Create a clock for measuring time elapsed
sf::Clock Clock;
//background texture
sf::Texture backGround;
backGround.loadFromFile("images/background.jpg");
sf::Sprite back;
back.setTexture(backGround);
//load the invaders images
sf::Texture invaderTexture;
invaderTexture.loadFromFile("images/invaders.png");
sf::Sprite invadersSprite(invaderTexture);
std::vector<sf::Sprite> invaderSprites(10, sf::Sprite(invaderTexture));
int invadersWidth=invaderTexture.getSize().x;
int invadersHeight=invaderTexture.getSize().y;
int spaceWidth=invadersWidth/SPRITECOLS;
int spaceheight=invadersHeight/SPRITEROWS;
//Sprites
sf::IntRect area(0,0,spaceWidth,spaceheight);
invadersSprite.setTextureRect(area);
invadersSprite.setPosition(30, NULL);
App.setKeyRepeatEnabled(false);
//Collision detection
// Start game loop
while (App.isOpen())
{
// Process events
sf::Event Event;
while (App.pollEvent(Event))
{
// Close window : exit
if (Event.type == sf::Event::Closed)
App.close();
}
// Create an array of 10 sprites (cannot initialise them with textures here)
for (int i = 0; i < 10; i++)
{
invaderSprites[i].setPosition(30,0);
if(Clock.getElapsedTime().asSeconds()>REFRESH_RATE)
{
//carry out updating tasks
static float spriteTimer=0.0; //keep track of sprite time
spriteTimer+=Clock.getElapsedTime().asSeconds();
static int count=0; //keep track of where the sub rect is
if(spriteTimer>delay)
{
invaderSprites[i].setTextureRect(area);
++count;
invaderSprites[i].move(xVelocity, yVelocity);
if(count==SPRITECOLS) //WE HAVE MOVED OFF THE RIGHT OF THE IMAGE
{
area.left=0; //reset texture rect at left
count=0; //reset count
}
else
{
area.left+=spaceWidth; //move texture rect right
}
spriteTimer=0; //we have made one move in the sprite tile - start timing for the next move
}
Clock.restart();
}
App.draw(back);
App.draw(invaderSprites[i]);
// Finally, display the rendered frame on screen
App.display();
}
}
return EXIT_SUCCESS;
}
The issue I am having is that the sprite only shows once, not 10 times (as the for loop states)
std::vector<sf::Sprite> invaderSprites(10, sf::Sprite(invaderTexture));
// Loop over the elements of the vector of sprites
for (int i = 0; i < invaderSprites.size(); i++)
{
invaderSprites[i].setPosition(30, NULL);
}
// Create an array of 10 sprites (cannot initialise them with textures here)
sf::Sprite invaderSprites[10]; // Loop over each sprite, setting their textures
for (int i = 0; i < 10; i++)
{
invaderSprites[i].setTexture(invaderTexture);
}
I am pretty sure it has something to do with the app drawing invadersSprite whereas the loop is setup for invaderSprites. Even just a little insight into what is going wrong would be such a big help.
I am pretty sure it has something to do with the app drawing
invadersSprite whereas the loop is setup for invaderSprites.
Yes, that certainly has something to do with it. You need to call App.draw(...) for each sprite that you want to draw. You're not calling it for any of the sprites in your vector. For that, you would want a loop:
for (int i=0; i<invaderSprites.size(); ++i)
App.draw(invaderSprites[i]);
There are other problems though. For example, why are you declaring an array of sprites called invaderSprites, when you already have a vector of sprites with that same name? The latter hides the former once it is declared.
Another thing is that you are setting all the sprites to the same position, so even if if you do manage to draw them all, they will all be in the same spot, and as such they will not appear as separate objects.