Using window.draw() outside of main SFML - c++

I am using SFML and this is the first time I have really used a library but I have a decent knowledge in C++. How do I access my window functions outside of main? e.g.
void checkWin()
{
if (iFilled[0] == 1 && iFilled[1] == 1 && iFilled[2] == 1) {
RectangleShape line(Vector2f(150, 5));
line.setPosition(10, 450);
window.draw(line); //error window is inside of main()
}
}
int main()
{
RenderWindow window;
window.create(VideoMode(800, 600), "Red vs. Green Peppers", Style::Close);
//more code
return 0;
}

First, you need to check those tutorials from SFML's site: http://www.sfml-dev.org/tutorials/2.1/ .
If you have decent knowledge in C++ you could use pointers and reference parameters:
void checkWin( sf::RenderWindow &window) { ... }
Anyway, to show an image to the screen you need to do that in a while, because, how you wrote that, you will show that image only for 1 frame, or less, because you don't know if window is still open.

Related

Problem with std::vector and a custom class that includes sf:: Text, C++, SFML

I am creating a graphical application using the SFML library. I have a class, Square, that creates a square block with a letter inside. This class has members sf:: Font and sf::Text except for the square itself (sf::RectangleShape). The constructor of this class takes the size of the side of the square, its position, the path to the font that is located in the project folder, and the letter that will be written to sf:: Text. The class also contains the draw() method, which takes an object of the sf::RenderWindow type and draws the square itself and the text using it.
Here's the problem. When I create multiple instances of a square (and therefore a letter with it) using separate variables, everything works fine, but when I try to push_back the squares in std::vector, everything breaks, errors fly out in Debug mode, and in Release mode only small dots are drawn instead of letters and the program does not work correctly, the window may stop responding
(Just squares without letters work fine with std::vector and they are drawn normally)
I think that there are errors related to dynamic memory, pointers or iterators, but I myself can not figure out and understand what is happening, thank you in advance for your help.
The code for better understanding:
class Square
{
public:
RectangleShape m_square;
Font m_font;
Text m_text;
Square(int size, Vector2f pos, string fontPath, string letter)
{
m_square.setFillColor(Color(0, 0, 0, 50));
m_square.setSize(Vector2f(size, size));
m_square.setPosition(pos);
if (!m_font.loadFromFile(fontPath))
wcout << "Error loading the font!" << endl;
m_text.setFont(m_font);
m_text.setFillColor(Color(0, 0, 0, 220));
m_text.setCharacterSize(size * 0.8);
m_text.setString(letter);
m_text.setPosition(Vector2f((pos.x - m_text.getGlobalBounds().left) + (size - m_text.getGlobalBounds().width) / 2, pos.y));
}
void draw(RenderWindow& window)
{
window.draw(m_square);
window.draw(m_text);
}
};
main.cpp:
#include <iostream>
#include <SFML/Graphics.hpp>
#include <vector>
#include <string>
#include "Square.h"
using namespace sf;
using namespace std;
int main()
{
ContextSettings settings;
settings.antialiasingLevel = 10;
RenderWindow window(VideoMode(1200, 800), "My window", Style::Default, settings);
window.setVerticalSyncEnabled(true);
Square sq1(50, Vector2f(100, 100), "Roboto-Bold.ttf", "A");
Square sq2(50, Vector2f(200, 100), "Roboto-Bold.ttf", "B");
Square sq3(50, Vector2f(100, 200), "Roboto-Bold.ttf", "C");
Square sq4(50, Vector2f(200, 200), "Roboto-Bold.ttf", "D");
vector<Square> squares;
squares.push_back(Square(50, Vector2f(400, 100), "Roboto-Bold.ttf", "A"));
squares.push_back(Square(50, Vector2f(500, 100), "Roboto-Bold.ttf", "B"));
squares.push_back(Square(50, Vector2f(400, 200), "Roboto-Bold.ttf", "C"));
squares.push_back(Square(50, Vector2f(500, 200), "Roboto-Bold.ttf", "D"));
while (window.isOpen())
{
window.clear(Color::White);
Event event;
while (window.pollEvent(event))
{
if (event.type == Event::Closed)
{
window.close();
}
}
sq1.draw(window);
sq2.draw(window);
sq3.draw(window);
sq4.draw(window);
for (auto& s : squares)
{
s.draw(window);
}
window.display();
if (Keyboard::isKeyPressed(Keyboard::Escape))
{
window.close();
}
}
}
Here is a screenshot of the program
window
Any help would be appreciated.
As both a simple workaround and a small memory & performance improvement, consider storing your fonts in a std::map<std::string, sf::Font> instead.
This data structure allows you to map the font name to the object, and it has the nice property that objects in it don't move around as you add new ones.
// add this at top level
std::map<std::string, sf::Font> fonts;
// Change the Square constructor as such:
Square(int size, Vector2f pos, string fontPath, string letter) {
if (auto it = fonts.find(fontPath); it == fonts.cend()) {
// Font was not created before.
auto& font = fonts[fontPath]; // this access will create a Font object and automatically store it in the map
if (!font.loadFromFile(fontPath))
wcout << "Error loading the font!" << endl;
}
...
m_text.setFont(fonts[fontPath]);
...
}
and of course get rid of the m_font member.

Generating multiple amounts of the same sprite broken

I have just started working on a 2d game of mine. I'm currently experiencing a problem where I am trying to make some land with a grass block texture by spawning the same sprites multiple times with the same grass texture. However, instead of getting a row of the same texture, I am getting the row being stretched for some reason.
Let me show you:
Normal grass texture:
Current grass output:
This is the code:
#include <iostream>
#include <vector>
#include <random>
#include <SFML/Graphics.hpp>
using namespace std;
using namespace sf;
int main() {
RenderWindow window(VideoMode(1920, 1080), "Treko");
Color bg(0, 205, 255);
Font errorFont;
Texture grass;
Texture dirt;
if (!errorFont.loadFromFile("font/Arial/Arial.ttf")) {
RenderWindow error(VideoMode(600, 500), "Error!");
Text errorText;
errorText.setFont(errorFont);
errorText.setString("Unfortunaetly, we are unable to find Arial.ttf.\nPlease reinstall the application and report the error. Thank you.");
errorText.setCharacterSize(18);
errorText.setFillColor(Color::White);
while (error.isOpen()) {
Event errorEvent;
while (error.pollEvent(errorEvent)) {
if (errorEvent.type == Event::Closed) {
error.close();
}
}
error.clear();
error.draw(errorText);
error.display();
}
}
if (!dirt.loadFromFile("img/Dirt.png")) {
RenderWindow error(VideoMode(600, 500), "Error!");
Text errorText;
errorText.setFont(errorFont);
errorText.setString("Unfortunaetly, we are unable to find Dirt.png.\nPlease reinstall the application and report the error. Thank you.");
errorText.setCharacterSize(18);
while (error.isOpen()) {
Event errorEvent;
while (error.pollEvent(errorEvent)) {
if (errorEvent.type == Event::Closed) {
error.close();
}
}
error.clear();
error.draw(errorText);
error.display();
}
}
if (!grass.loadFromFile("img/Grass.png")) {
RenderWindow error(VideoMode(600, 500), "Error!");
Text errorText;
errorText.setFont(errorFont);
errorText.setString("Unfortunaetly, we are unable to find Grass.png.\nPlease reinstall the application and report the error. Thank you.");
errorText.setCharacterSize(18);
errorText.setFillColor(Color::White);
while (error.isOpen()) {
Event errorEvent;
while (error.pollEvent(errorEvent)) {
if (errorEvent.type == Event::Closed) {
error.close();
}
}
error.clear();
error.draw(errorText);
error.display();
}
}
Sprite grassBlock;
grassBlock.setTexture(grass);
Sprite dirtBlock;
dirtBlock.setTexture(dirt);
vector<Sprite> grassM;
/* This is a work in progress. I'm trying to add height to the land using random y axis generation. Any of you know how to do this it would be great to let me know!
std::uniform_int_distribution<int> randomColorRange(680, 720);
std::random_device rd;
std::mt19937 RandYPos(rd());
*/
//THE PROBLEM PART 1
for (int i = 0; i <= 1918; i++) {
grassBlock.setPosition(Vector2f(i + 1, 690));
grassBlock.setScale(Vector2f(0.5f, 0.5f));
grassM.push_back(Sprite(grassBlock));
}
while (window.isOpen()) {
Event event;
while (window.pollEvent(event)) {
if (event.type == Event::Closed) {
window.close();
}
}
window.clear(bg);
//THE PROBLEM PART 2
for (int i = 0; i < grassM.size(); i++) {
window.draw(grassM[i]);
}
window.display();
}
return 0;
}
If anyone knows how to do this, that would be great!
While I didn't try to run the code, to me it looks like everything is working. Your problem is, that you're just moving the sprites one pixel apart:
grassBlock.setPosition(Vector2f(i + 1, 690));
Since all show the same texture with the same origin, this will result in all the sprites essentially showing the first column of your texture (since the rest is overlapped by the next tile).
While I think you should rework your whole code structure (it feels really messy and I wouldn't recommend to create new SFML windows if you encounter errors, since that might fail as well), all you have to do is move your sprites apart by multiplying the index used for your coordinate by the desired tile width:
grassBlock.setPosition(Vector2f(i * tile_width, 690));

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.

SFML 2 drawing does not work when connected with OGRE

I am currently working on connecting OGRE and SFML.
SFML should be used for 2D drawing, network stuff and input.
OGRE is for the 3d Graphics.
Currently the whole thing is on Linux.
What works: Connecting OGRE and SFML. First I create a SFML Render Window, then I grab the handle of this window and give it to the OGRE Render WIndow while creating it. I can use the SFML Events now. Did not test the Network stuff, but I am sure this will work too.
What does not work: Drawing in the SFML window.
Case 1: SFML and OGRE are not connected. OGRE does not have the SFML window handle and has its own window. SFML still can't draw in its own window! The main loop executes a maximum of 3 times and then just stops. Nothing more happens. A few seconds later (about 20 or so) I get a Memory Access violation and the program ends.
Case 2: SFML and OGRE are connected. A similar thing happens: The main loop executes exectly 53 times, nothing gets drawn and then the program stops with the terminal message "aborted" (actually its "Abgebrochen", because it's in German)
The strange behaviour also happens, when I let SFML draw into a sf::RenderTexture instead of the sfml_window.
Here is my code:
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include <iostream>
#include <OGRE/Ogre.h>
#include <vector>
#include <stdio.h>
int main(int argc, char * argv[])
{
if(argc == 1)
return -1;
// start with "1" and you get 1 window, start with "0" and you get two
bool together = atoi(argv[1]);
// create the SFML window
sf::RenderWindow sfml_window(sf::VideoMode(800, 600), "test");
sf::WindowHandle sfml_system_handle = sfml_window.getSystemHandle();
sfml_window.setVerticalSyncEnabled(true);
std::cout<<sfml_system_handle<<std::endl;
// init ogre
Ogre::Root * ogre_root = new Ogre::Root("", "", "");
std::vector<Ogre::String> plugins;
plugins.push_back("/usr/lib/x86_64-linux-gnu/OGRE-1.8.0/RenderSystem_GL");
for(auto p : plugins)
{
ogre_root->loadPlugin(p);
}
const Ogre::RenderSystemList& render_systems = ogre_root->getAvailableRenderers();
if(render_systems.size() == 0)
{
std::cerr<<"no rendersystem found"<<std::endl;
return -1;
}
Ogre::RenderSystem * render_system = render_systems[0];
ogre_root->setRenderSystem(render_system);
ogre_root->initialise( false, "", "");
// create the ogre window
Ogre::RenderWindow * ogre_window= NULL;
{
Ogre::NameValuePairList parameters;
parameters["FSAA"] = "0";
parameters["vsync"] = "true";
// if started with 1, connect the windows
if(together) parameters["externalWindowHandle"] = std::to_string(sfml_system_handle);
ogre_window = ogre_root->createRenderWindow("ogre window", 800, 600, false, &parameters);
}
// ogre stuff
Ogre::SceneManager * scene = ogre_root->createSceneManager(Ogre::ST_GENERIC, "Scene");
Ogre::SceneNode * root_node = scene->getRootSceneNode();
Ogre::Camera * cam = scene->createCamera("Cam");
Ogre::SceneNode * cam_node = root_node->createChildSceneNode("cam_node");
cam_node->attachObject(cam);
Ogre::Viewport * vp = ogre_window->addViewport(cam);
vp->setAutoUpdated(false);
vp->setBackgroundColour(Ogre::ColourValue(0, 1, 1));
ogre_window->setAutoUpdated(false);
ogre_root->clearEventTimes();
//sfml image loading
sf::Texture ring;
std::cout<<"ring loading: "<<ring.loadFromFile("ring.png")<<std::endl;
sf::Sprite sprite;
sprite.setTexture(ring);
// main loop
int counter = 0;
while(!ogre_window->isClosed() && sfml_window.isOpen())
{
std::cout<<counter<<std::endl;
counter++;
std::cout<<"a"<<std::endl;
// sfml events
sf::Event event;
while(sfml_window.pollEvent(event))
{
if(event.type == sf::Event::Closed)
{
sfml_window.close();
}
}
std::cout<<"b"<<std::endl;
std::cout<<"c"<<std::endl;
ogre_root->renderOneFrame();
std::cout<<"d"<<std::endl;
// here the strange behaviour happens
// if this line (draw) isn't present, everything works
sfml_window.pushGLStates();
sfml_window.draw(sprite);
sfml_window.popGLStates();
vp->update();
std::cout<<"e"<<std::endl;
sfml_window.display();
// only needs to be done for separated windows
// sfml display updates otherwise, both use double buffering
if(!together) ogre_window->update(true);
}
return 0;
}
Help would be really appreciated.
EDIT: I added the pushGLStates(); and popGLStates(); commands, forgot those earlier!
Not an answer really, but too long for a comment:
ogre_window = ogre_root->createRenderWindow("ogre window", 800, 600, false, &parameters);
Are you sure that it's okay to pass the address of an object you destroy the very next line here with &parameters?

SFML 2.0 Targeting a player by a single turret

At the beginning - sorry for my english (i'm still learning).
I've created a turret which targets player. It works fine but when i'm moving around within the range of tower, turret no longer targets me. Just take a look at this code and run this in your compilator.
int detection (sf::Sprite statek,sf::RectangleShape linia,sf::Texture textstatku)
{
sf::FloatRect rect, rect2;
rect = linia.getGlobalBounds();
rect2 = statek.getGlobalBounds();
if(rect2.intersects(rect))
return 1;
else
return 2;
}
int _tmain(int argc, _TCHAR* argv[])
{
sf::Event evente;
sf::RenderWindow okno ( sf::VideoMode(500,500,32)," TURRET TEST ");
sf::Texture textturreta;
textturreta.loadFromFile ("C:\\Users\\Darono\\C++\\Projekty\\IN PROGGRES\\Single turret\\Debug\\turret.png");
sf::CircleShape turret (20.0,100);
turret.setTexture((sf::Texture *)&textturreta);
turret.setPosition (240,240);
sf::Texture Lufatext;
Lufatext.loadFromFile("C:\\Users\\Darono\\C++\\Projekty\\IN PROGGRES\\Single turret\\Debug\\Lufa.png");
sf::Sprite lufa;
lufa.setTexture(Lufatext);
sf::Texture gracztext;
gracztext.loadFromFile("C:\\Users\\Darono\\C++\\Projekty\\IN PROGGRES\\Single turret\\Debug\\gracz.png");
sf::Sprite gracz(gracztext);
int orginY=turret.getPosition().y+20;
int orginX=turret.getPosition().x+20;
lufa.setPosition(turret.getPosition().x+20,turret.getPosition().y+20);
lufa.setOrigin (2,-20);
sf::RectangleShape liniastrzalu(sf::Vector2f(1,200));
liniastrzalu.setOrigin(0,-20);
liniastrzalu.setPosition(turret.getPosition().x+20,turret.getPosition().y+20);
int a =0;
while (okno.isOpen())
{
if (gracz.getPosition().y >= turret.getPosition().y-240||gracz.getPosition().y <= turret.getPosition().y+280)
{
if (detection(gracz,liniastrzalu,textturreta)== 1)
{
std::cout <<"lol";
}
if (detection(gracz,liniastrzalu,textturreta)==2)
{
lufa.rotate(1);
liniastrzalu.rotate(1);
}
}
while (okno.pollEvent(evente))
{
//lufa obraca się razem z kołem
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
gracz.move(-2,0);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
gracz.move(2,0);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
{
gracz.move(0,2);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
gracz.move(0,-2);
}
}
okno.display();
okno.clear();
okno.draw(turret);
okno.draw(lufa);
okno.draw(gracz);
//okno.draw(liniastrzalu);
}
return 0;
}
You should follow a few best practices:
Your game loop consists of 3 main parts: handling user input, updating your game world and drawing your game world. Although you do this, you have the order mixed up. You seem to update your game world before handling user input.
Drawing in SFML consists of cleaning the surface, drawing and presenting in that order.
okno.clear();
okno.draw(turret);
okno.draw(lufa);
okno.draw(gracz);
okno.display();
Notice how I put the display call to the end.
You don't need to call your detection method twice. Call it once and store the result in a variable, then use that variable.
Fix those things first, because they will cause a lot of problems that may hide your real problem or cause problems when your code is fine.