I am using SFML to display a sprite on the screen. I first use a texture and call loadFromFile() to load the image. This works well with no errors occurring. It seems that the image path is correct and it loads the image, but it doesn't seem to store the data in the sprite. The sprite is displayed without error, but it shows a blank white image.
Is there a problem with my code in the following snippets? They should be fairly easy to read :).
My code:
Main.cpp
#include <SFML/Graphics.hpp>
#include "Menu.h"
using namespace sf;
int main()
{
VideoMode vm = VideoMode::getDesktopMode();
int width = vm.width, height = vm.height;
RenderWindow window(VideoMode(width, height), "Score Arena v1.0", Style::Fullscreen);
window.setPosition(Vector2i(0, 0));
Menu menu(width, height);
while (window.isOpen())
{
Event event;
while (window.pollEvent(event))
{
switch (event.type) {
case Event::Closed:
window.close();
break;
//when a key is pressed
case Event::KeyPressed:
if (Keyboard::isKeyPressed(Keyboard::Escape))
window.close();
break;
}
}
window.clear();
menu.draw(window);
window.display();
}
return 0;
}
Menu.h
#pragma once
#include <string>
#include <SFML/Graphics.hpp>
using namespace std;
using namespace sf;
class Menu {
int page = 0;
string title = "Score Arena";
string labels[4]{
"Single Player",
"Multi Player",
"Options",
"Exit"
};
Sprite background;
int width, height;
public:
Menu(int, int);
void draw(RenderWindow &window);
};
Menu.cpp
#include "Menu.h"
#include <SFML/Graphics.hpp>
#include <iostream>
Menu::Menu(int width, int height) {
this->width = width;
this->height = height;
Texture bg;
if (!bg.loadFromFile("menuBg.jpg"))
std::cout << "Error loading image!" << std::endl;
background.setTexture(bg);
}
void Menu::draw(RenderWindow &window) {
window.draw(background, RenderStates::Default);
}
An sf::Sprite object doesn't internally copy the passed sf::Texture object when you call the setTexture() member function. An sf::Sprite just refers to an sf::Texture; the former doesn't own the latter. The associated sf::Texture must exist when you render the sf::Sprite.
It is stated in the documentation for sf::Sprite:
It is important to note that the sf::Sprite instance doesn't copy the
texture that it uses, it only keeps a reference to it. Thus, a
sf::Texture must not be destroyed while it is used by a sf::Sprite
(i.e. never write a function that uses a local sf::Texture instance
for creating a sprite).
The problem is in your Menu's constructor:
Menu::Menu(int width, int height) {
this->width = width;
this->height = height;
Texture bg; // <-- it is local variable !!!
if (!bg.loadFromFile("menuBg.jpg"))
std::cout << "Error loading image!" << std::endl;
background.setTexture(bg); // <-- associating sprite to a local object!!!
// bg goes out of existence!!!
}
The sf::Texture object, bg, ceases to exist as soon the program control flow leaves the constructor because bg is a local variable inside the constructor. As a result, the sf::Sprite object, background – a Menu data member – outlives its associated sf::Texture object, bg – a local object.
Then, in Menu::draw() you use this sf::Sprite object, background, which internally refers to an sf::Texture object that no longer exists:
void Menu::draw(RenderWindow &window) {
window.draw(background, RenderStates::Default);
}
Possible Solution
Instead of the sf::Texture object being a local object in Menu's constructor, you could make it a data member of Menu:
class Menu {
// ...
Texture bg;
Sprite background;
// ...
};
This way, Menu owns bg (in contrast to Menu's constructor owning bg as was the case before), and therefore Menu controls bg's lifetime: bg is kept alive while the Menu object lives. This time, when you call Menu::draw(), the sf::Sprite object does refer to a living sf::Texture object.
Related
I am currently trying to make a simple pong game with SFML in C++. I consistently get the error in the main file.
#include <iostream>
#include "Ball.h"
Ball b;
int main()
{
// create the window
sf::RenderWindow window(sf::VideoMode(1200, 600), "My window");
// 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
sf::Event event;
while (window.pollEvent(event))
{
// "close requested" event: we close the window
if (event.type == sf::Event::Closed)
window.close();
}
// clear the window with black color
window.clear(sf::Color::Black);
// draw everything here...
window.draw(b);
// end the current frame
window.display();
}
return 0;
}
This is the ball.h file:
#pragma once
#include <iostream>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
class Ball
{
public:
double x, y;
int Radius;
void Render(int Rad, sf::Color BallColour) {
sf::CircleShape shape(Rad);
// set the shape color to green
shape.setFillColor(BallColour);
}
};
I am unsure as to why the error occurs. Any help would be appreciated.
You had not call function of render, you just wrote window.draw(b), and programm dont know what do you mean. In Ball.h you should write:
#pragma once
#include <iostream>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
class Ball
{
public:
double x, y;
int Radius;
void Render(int Rad, RenderWindow& window) {
sf::CircleShape shape(Rad);
// set the shape color to green
shape.setFillColor(sf::Color::Green);
shape.setPosition(600, 300);//SETTING POSITION OF THE BALL
window.draw(shape);//if you know what is reference on variable, you will understand what is it
}
};
And in your main.cpp you shhould call your function Render:
#include <iostream>
#include "Ball.h"
int main()
{
// create the window
sf::RenderWindow window(sf::VideoMode(1200, 600), "My window");
Ball b; //I advice you to create object of class in main function
// 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
sf::Event event;
while (window.pollEvent(event))
{
// "close requested" event: we close the window
if (event.type == sf::Event::Closed)
window.close();
}
// clear the window with black color
window.clear(sf::Color::Black);
b.Render(10, window);// give the function reference on window
// end the current frame
window.display();
}
return 0;
}
sf::RenderWindow::draw requires an implementation of sf::Drawable. You can either pass sf::CircleShape (it's a sf::Drawable) directly or implement sf::Drawable and delegate the call.
class Ball : public sf::Drawable {
public:
double x, y;
int Radius;
void draw(sf::RenderTarget& target, sf::RenderStates states) const override {
sf::CircleShape shape(Radius);
shape.setFillColor(BallColour);
shape.draw(target, states);
}
};
Am trying to load an image with SFML using Xcode, but everytime I run the program, the window (which has been created using the code) flashes and disappears...
Here's the code I am using:
#include <SFML/Graphics.hpp>
#include <iostream>
#define SCREEN_WIDTH 1024
#define SCREEN_HEIGHT 1024
int main()
{
sf::RenderWindow window(sf::VideoMode(SCREEN_WIDTH, SCREEN_HEIGHT), "Orsome Game");
while(window.isOpen())
{
sf::Event e;
while(window.pollEvent(e))
{
switch (e.type)
{
case sf::Event::Closed:
window.close();
break;
}
}
sf::Image image;
if(!image.loadFromFile("sprite.png")){
return -1;
}
window.clear(sf::Color(255,255,255));
window.display();
}
return EXIT_SUCCESS;
}
Have also put the image file where the c++ file is, but it still doesn't work!
I would put an sf::Style::Default in the RenderWindow arguments, straight after "Orsome Game" window name. Because you are using the pollEvent, that would allow you to exit the window by pressing the x button in the top right corner, like a default application layout.
Is the picture in the same directory as your solution file? Or your main.cpp?
It should be, of course.
And, maybe, try loading your image with Texture and Sprite, like this:
sf::Texture texture;
texture.loadFromFile("picturename.png");
sf::Sprite sprite;
sprite.setTexture(texture);
inside the while window is open loop.
To draw it:
// after window.clear() ...
//... your code
window.draw(sprite);
//... other things to draw
// and then, your window.display();
But, probably, your sf::Image should also work. Tell me how it goes
EDIT:
Try this code and see if the window opens.
Otherwise, that might be the problem of setting up the SFML on your computer and you will have to find some good tutorial on how to set it up properly! But let's see:
#include <SFML/Graphics.hpp>
#include <iostream>
using namespace sf;
int main()
{
RenderWindow window(VideoMode(640, 640), "Test", Style::Default);
while (window.isOpen()) {
Event _event;
while (window.pollEvent(_event)) {
switch (_event.type) {
case Event::Closed:
window.close();
break;
}
}
window.clear();
// draw
// ...
window.display();
}
return 0;
}
I am trying to create a Screen class for SFML, however for some reason, the application works when using the Xcode example but as soon as I put the window into it's own class, it does not work. Why is this and how would I fix it?
Here is my code (adapted form the example):
Edit:
After reading the comments, I have changed to the following code. This still does not show a screen and the program still quits.
#include <SFML/Audio.hpp>
#include <SFML/Graphics.hpp>
#include "ResourcePath.hpp"
class Screen{
public:
sf::RenderWindow window;
Screen(){
sf::RenderWindow window(sf::VideoMode(800, 600), "SFML window");
}
};
int main(int, char const**)
{
Screen* screen = new Screen();
// Set the Icon
sf::Image icon;
if (!icon.loadFromFile(resourcePath() + "icon.png")) {
return EXIT_FAILURE;
}
screen->window.setIcon(icon.getSize().x, icon.getSize().y, icon.getPixelsPtr());
// Load a sprite to display
sf::Texture texture;
if (!texture.loadFromFile(resourcePath() + "cute_image.jpg")) {
return EXIT_FAILURE;
}
sf::Sprite sprite(texture);
// Create a graphical text to display
sf::Font font;
if (!font.loadFromFile(resourcePath() + "sansation.ttf")) {
return EXIT_FAILURE;
}
sf::Text text("Hello SFML", font, 50);
text.setFillColor(sf::Color::Black);
// Load a music to play
sf::Music music;
if (!music.openFromFile(resourcePath() + "nice_music.ogg")) {
return EXIT_FAILURE;
}
// Play the music
music.play();
// Start the game loop
while (screen->window.isOpen())
{
// Process events
sf::Event event;
while (screen->window.pollEvent(event))
{
// Close window: exit
if (event.type == sf::Event::Closed) {
screen->window.close();
}
// Escape pressed: exit
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) {
screen->window.close();
}
}
// Clear screen
screen->window.clear();
// Draw the sprite
screen->window.draw(sprite);
// Draw the string
screen->window.draw(text);
// Update the window
screen->window.display();
}
return EXIT_SUCCESS;
}
You have a logic error in your constructor. The way you created it, you leave Screen::window uninitialized, and create another RenderWindow object which becomes inaccessible as soon as execution leaves constructor.
Instead, you should do
class Screen{
public:
sf::RenderWindow window;
Screen():
window(sf::VideoMode(800, 600), "SFML window") {}
};
Everything else should work as expected, unless it does not have any other bugs. If you're curious about the syntax I've used, you can visit this link.
Hard to say. Maybe your accessing a memory address that isn't part of your program? Try using unique pointers and make_unique to define some of your pointers.
For example:
std::unique_ptr<sf::RenderWindow> window;
window = std::make_unique<sf::RenderWindow>(sf::VideoMode(WIDTH, HEIGHT), "PushBlox",sf::Style::Default);
I did make a project using sfml before, so feel free to check out my code here --> https://github.com/FromAlaska/ComputerScience/tree/2017/Projects/Frontier
I'm trying to create some classes containing textures&sprites using SFML.
When I create object of class in SFML library in my class - i cannot use that object in main.
I wonder how to display sprite in main (e.g.):
class MainMenu
{
public:
int DrawMenu(){
sf::Texture texture;
if (!texture.loadFromFile("idle.png"))
return EXIT_FAILURE;
sf::Sprite spritemenu(texture);
return 0;
}
};
int main()
{
// Create the main window
sf::RenderWindow app(sf::VideoMode(800, 600), "SFML window");
// Load a sprite to display
sf::Texture texture;
if (!texture.loadFromFile("cb.bmp"))
return EXIT_FAILURE;
sf::Sprite sprite(texture);
MainMenu menu;
// Start the 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();
}
// Clear screen
app.clear();
// Draw the sprite
app.draw(sprite);
menu.DrawMenu();
app.draw(spritemenu);
// Update the window
app.display();
}
return EXIT_SUCCESS;
}
Your DrawMenu function name is lying. It doesn't draw anything. It loads the texture and sprite.
DrawMenu should simply draw the menu sprite:
void DrawMenu(sf::RenderWindow& window) { // window = where to draw the menu
window.draw(spritemenu);
}
Now where do you load spritemenu? It stays the same through the lifetime of MainMenu, so it should naturally be instantiated in the constructor of MainMenu:
class MainMenu
{
public:
MainMenu() {
if (!texture.loadFromFile("cb.bmp"))
abort(); // or throw exception
spritemenu.setTexture(texture); // "initialize" spritemenu with texture
}
…
private:
sf::Texture texture; // Store these as member data, so they will be
sf::Sprite spritemenu; // kept alive through the lifetime of MainMenu.
};
Now when you do MainMenu menu; the texture and sprite will be initialized once, and not every time you call the draw function.
And when you need to draw the menu, simply call menu.DrawMenu(app); instead of menu.DrawMenu(); app.draw(spritemenu);.
The best way to do this is probably to pass app as an argument to DrawMenu, something like this:
class MainMenu {
public:
int DrawMenu(sf::RenderWindow& app){
sf::Texture texture;
if(!texture.loadFromFile("idle.png"))
return EXIT_FAILURE;
sf::Sprite spritemenu(texture);
app.draw(spritemenu);
return 0;
}
};
Then call menu.DrawMenu(app) instead of menu.DrawMenu().
Note that it's meaningless to return EXIT_FAILURE from any function other than main(); I suggest throwing an exception instead.
Also, it's silly to reload the menu every time you draw it. I suggest moving the loading to the MainMenu constructor.
Recently, I decided to make an Animal Crossing clone in
C++ and SFML 2.1. But I'm having some issues. The Player won't show up when commanded to be rendered. The program will compile and run just fine but the player just won't show up.
Here's my code:
#include <iostream>
#include <SFML/Graphics.hpp>
using namespace std;
using namespace sf;
RenderWindow window(VideoMode(700, 500), "Animal Crossing: Old oak");
View view(FloatRect(1000, 1000, 300, 200));
class Villager{
public:
int x, y, w, h;
Sprite pl;
string loadDir;
Villager(int x, int y, int w, int h, Color c, string loadDir){
this->x = x;
this->y = y;
this->w = w;
this->h = h;
Image image;
image.loadFromFile(loadDir);
image.createMaskFromColor(Color::Magenta);
Texture tex;
tex.loadFromImage(image);
pl.setTexture(tex);
}
}
};
int main(){
Villager villager(1100, 1000, 100, 100, Color::Blue, "player.png");
view.zoom(5);
Image grasstexloader;
grasstexloader.loadFromFile("grass.png");
Texture grasstex;
grasstex.loadFromImage(grasstexloader);
Sprite grass;
grass.setTexture(grasstex);
while(window.isOpen()){
Event event;
while(window.pollEvent(event)){
if(event.type == Event::Closed)
window.close();
if(Keyboard::isKeyPressed(Keyboard::Up))
villager.moveUp();
if(Keyboard::isKeyPressed(Keyboard::Down))
villager.moveDown();
if(Keyboard::isKeyPressed(Keyboard::Left))
villager.moveLeft();
if(Keyboard::isKeyPressed(Keyboard::Right))
villager.moveRight();
if(Keyboard::isKeyPressed(Keyboard::Escape))
window.close();
}
window.setView(view);
window.draw(grass);
window.draw(villager.pl);
window.display();
window.clear();
}
}
I've been staring at this code for an hour now. But I just can't find an error!
Please help!
Edit: I solved the problem with the sprite not being visible, but the sprite is just white instead of the appropriate colors. It proboably has something to do with how I load the file. Please post any suggestions you have on how to fix this new problem!
Your sprite is rendered white because in your Villager constructor, you're giving a local Texture variable to setTexture, which then gets destructed at the end of the constructor scope.