I've recently been on the lookout for a C++ game-developement framework and decided to give SFML a try. I've implemented an early example from the book 'SFML game developement' to see if things work properly. Sadly they don't.
The code is supposed to make a circular object move rapidly when pressing one of the 'wasd' keys. In reality the circle begins moving slowly for a distance of maybe ten pixels, then stops and if I hold the respective key down further the circle jumps another few pixels whenever it feels like it.
//the header game.h
class Game
{
public:
Game();
void run();
private:
void processEvents();
void handlePlayerInput(sf::Keyboard::Key, bool);
void update();
void render();
sf::RenderWindow mWindow;
sf::CircleShape mPlayer;
bool mIsMovingUp;
bool mIsMovingDown;
bool mIsMovingLeft;
bool mIsMovingRight;
};
//the implementation
#include<SFML/Graphics.hpp>
#include "game.h"
Game::Game()
: mWindow(sf::VideoMode(640, 480), "SFML Application")
, mIsMovingUp(false), mIsMovingDown(false), mIsMovingLeft(false), mIsMovingRight(false)
, mPlayer()
{
mPlayer.setRadius(40.f);
mPlayer.setPosition(100.f, 100.f);
mPlayer.setFillColor(sf::Color::Cyan);
}
void Game::run()
{
while (mWindow.isOpen())
{
processEvents();
update();
render();
}
}
void Game::render()
{
mWindow.clear();
mWindow.draw(mPlayer);
mWindow.display();
}
void Game::processEvents()
{
sf::Event event;
while (mWindow.pollEvent(event))
{
switch (event.type)
{
case sf::Event::KeyPressed:
handlePlayerInput(event.key.code, true);
break;
case sf::Event::KeyReleased:
handlePlayerInput(event.key.code, false);
break;
case sf::Event::Closed:
mWindow.close();
break;
}
}
}
void Game::handlePlayerInput(sf::Keyboard::Key key, bool isPressed)
{
if (key == sf::Keyboard::W)
mIsMovingUp = isPressed;
else if (key == sf::Keyboard::S)
mIsMovingDown = isPressed;
else if (key == sf::Keyboard::A)
mIsMovingLeft = isPressed;
else if (key == sf::Keyboard::D)
mIsMovingRight = isPressed;
}
void Game::update()
{
sf::Vector2f movement(0.f, 0.f);
if (mIsMovingUp)
movement.y -= 1.0f;
if (mIsMovingDown)
movement.y += 1.0f;
if (mIsMovingLeft)
movement.x -= 1.0f;
if (mIsMovingRight)
movement.x += 1.0f;
mPlayer.move(movement);
}
//main file
#include <SFML/Graphics.hpp>
#include "game.h"
int main()
{
Game game;
game.run();
return 0;
}
I honestly dont't even know where to begin looking for a solution.
I guess this could be driver-related, I will test it on another Computer tomorrow.
Any advice on this would be appreciated.
Edit in response to Ike:
I got the SFML libraries from the Debian repository, running
`dpkg -l | grep sfml`
returns:
ii libsfml-audio2:amd64 2.1+dfsg2-1+b2
ii libsfml-dev:amd64 2.1+dfsg2-1+b2
ii libsfml-graphics2:amd64 2.1+dfsg2-1+b2
ii libsfml-network2:amd64 2.1+dfsg2-1+b2
ii libsfml-system2:amd64 2.1+dfsg2-1+b2
ii libsfml-window2:amd64 2.1+dfsg2-1+b2
I compiled the code above with g++ using the -O2 flag.
I can't imagine that this is an optimization problem since the game-loop seems to go through maybe two dozen iterations per second (at most). I can't say this for certain but I believe that number should be in the thousands.
Another Edit: Alright, so I've compiled the same code in an Ubuntu-VM again using the repo-libraries. The circle now moves reasonably fast which is better than nothing I guess (and also suggest some issue with debian, possibly driver-related?). On another Computer (which is admittedly faster than my laptop) running arch and using sfml 2.3.2 the program runs easily ten times as fast
A couple of things to try:
A) Set mWindow.setKeyRepeatEnabled(false). This will disable auto-repeat.
B) Try changing your Game::processEvents to use an if instead of a while, like so:
void Game::processEvents()
{
sf::Event event;
if (mWindow.pollEvent(event))
{
...
}
}
What I suspect you're seeing is the result of the event queue being spammed with auto-repeating key events. This will then cut into the frequency at which functions like update and render are being called and destroy the smooth frame rate.
The first suggestion tries to eliminate this potential of jamming up the queue. The second would make it so updates/refreshes are interleaved in between event processing and not only performed when the event queue becomes empty.
Judging from my related question https://gamedev.stackexchange.com/questions/185873/sfml-test-app-feels-slow-clunky/185880#185880 the issue could be that you were using keyboard events rather than real-time keyboard input.
i.e. sf::Keyboard::isKeyPressed
See https://www.sfml-dev.org/tutorials/2.5/window-inputs.php
Related
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.
I need your piece of advice. I'm using SFML and I need to play animation from the spritesheet(f.e. 64 frames and 40px width/height of each frame) after mouseclick event. The only solution I've come to is:
if (event.type == sf::Event::MouseButtonPressed) {
if (event.key.code == sf::Mouse::Left) {
float frame = 0;
float frameCount = 64;
float animSpeed = 0.005;
while (frame < frameCount) {
spriteAnimation->setTextureRect(sf::IntRect(int(frame)*w, 0, w, w));
frame += animSpeed;
window->draw(rect); // clear the area of displaying animation
window->draw(*spriteAnimation);
window->display();
}
...
But calling window->display() so many times is really not good;
Can you suggest better variants?
Instead of jamming all of your code for the animation into the event block you should spread it out.
Your design here is very inflexible in that if you ever want to display anything other than an animation you are going to have to call window->display() again outside of your event loop.
Generally in SFML your game loop proceeds similarly to as follows:
initialize();
while(running)
{
checkEvents();
clear();
update();
display();
}
Instead of performing all of the calculations and displaying for your animation inside the event's if statement you should set a bool or call a doAnimation() function of some sort. I've written a rough example below:
bool doAnimation = 0;
//declare frame, framespeed, etc
if (event.type == sf::Event::MouseButtonPressed)
{
if (event.key.code == sf::Mouse::Left)
{
doAnimation = true;
//reset frame, framespeed, etc
}
}
clear();
if(doAnimation)
{
sprite->setTexture(...);
if(frame == endFrame)
{
doAnimation = 0;
}
drawSprite();
}
window->display();
There are tons of ways to solve your problem. My example is less flexible than I would think is ideal but depending on the needs of your program it may work fine. If you wanted to take the next step moving the animation into a class of some sort would make your life a lot easier in the long run.
This question already has an answer here:
How can I set gravity using this code?
(1 answer)
Closed 8 years ago.
I am trying to make a game and am stuck on gravity..... In the following code a rectangle stands for a player and when I press up key it moves in y-axis but when I activate gravity on it (i.e resetting it's previous position) it does not animate (i.e. it does not jumps) instead it just stays in it's position.I know why it it happening. Because it is just staying in its position because when I press Up key it executes the code rectangle.setPosition(0, 350). Yeah I want it to do that but I also want to see my player in movement. I am using SFML library of C++. Please Help!
#include <SFML/Graphics.hpp>
int main(){
sf::RenderWindow window(sf::VideoMode(800, 600, 32), "Gravity");
sf::RectangleShape rectangle;
rectangle.setSize(sf::Vector2f(100, 100));
rectangle.setFillColor(sf::Color::Black);
rectangle.setPosition(sf::Vector2f(10, 350));
while(window.isOpen())
{
sf::Event Event;
while(window.pollEvent(Event))
{
if(Event.type == sf::Event::Closed)
{
window.close();
}
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
rectangle.move(0, -1);
}
if(rectangle.getPosition().y >= 350-1)
{
rectangle.setPosition(0, 350);
}
window.display();
window.clear(sf::Color::Cyan);
window.draw(rectangle);
}
}
Gravity is an acceleration: that is, the double derivative of displacement. So you can't directly set the displacement (as you're currently doing) to get a nice representation of gravity.
An approach would be to create an entity class of your own, adding members for velocity, acceleration; alongside sf::RectangleShape's internal displacement; then have member functions operate on initialization/every frame - a quick & dirty example (untested):
class SomeEntity {
public:
SomeEntity( float x_pos, float y_pos ) : position(x_pos, y_pos) {
m_shape.setSize(sf::Vector2f(100, 100));
m_shape.setFillColor(sf::Color::Black);
m_shape.setPosition(sf::Vector2f(x, y));
// Constants at the moment (initial velocity up, then gravity pulls down)
velocity = sf::Vector2f(0, -30);
acceleration = sf::Vector2f(0, 9.81); //Earth's acceleration
}
void Step() { // TODO: use delta time
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) {
velocity.y -= 1;
}
velocity.x += acceleration.x;
velocity.y += acceleration.y;
x += velocity.x;
y += velocity.y;
m_shape.setPosition(x, y);
}
void Draw(sf::RenderWindow &window) {
window.draw(m_shape);
}
private:
sf::RectangleShape m_shape;
sf::Vector2f position, velocity, acceleration;
}
Which also means you can rewrite your application so it's a little cleaner:
SomeEntity ent(360, 0);
while(window.isOpen()) {
sf::Event Event;
while(window.pollEvent(Event)) {
if(Event.type == sf::Event::Closed) {
window.close();
}
}
ent.Step();
window.display();
window.clear(sf::Color::Cyan);
ent.Draw();
}
Seeing as you're setting your rectangle to x = 0, y = 350 repeatedly, I'll work under the assumption that that's your 'ground plane'. To achieve that, you just want to check whether the entity is under the ground plane, and reset it's position to the ground plane if it is - either in the entity's 'Step' function or directly in your main loop. In the long run, you might be better off using an entity manager/third party physics engine to do this sort of thing (a la box2D, for example)
You can do something like this instead :
if(rectangle.getPosition().y > 350)
{
rectangle.move(0, 0.01);
}
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.
Using the following code I am to add a recursive function with out changing anything in the code for it to work:
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), TITLE);
int stop = 800;
sf::Color colour1(sf::Color::Black), colour2(sf::Color::Red);
// Start the main loop
while (window.isOpen()) {
// Process events
sf::Event event;
while (window.pollEvent(event)) {
// check the type of the event...
switch (event.type) {
// window closed
case sf::Event::Closed:
window.close();
break;
case sf::Event::MouseMoved: //mouse moved
stop = mapXtoMinSize(event.mouseMove.x); // convert x coordinate of mouse to minimum size of square to draw
break;
// we don't process other types of events
default:
break;
} //end switch
} //End-Process events
window.clear(colour2);
}
I just would like to know how to go about doing such.
A recursive function generally goes like this:
int recursive(int x) {
if (x == 0) {
return 0;
} else {
return (x + recursive(x - 1));
}
}
This will sum all the integers from 0 to x, by repeatedly calling itself to compute the sum of (x - 1) and adding this to the result. If the base case, x == 0, is encountered the recursion ends and the calls unwind until they reach the original, by which point the sum has been calculated. Please be more specific.
Without knowing what you are trying to achieve with your recursive function I cannot give you any more advice. I do not know what you mean with you comment "with out changing anything in the code".