i'm getting started with the SFML library for C++,and i've come along a place where,everything is working,but not the way i'd like it to.
My question is,how can i move an object with keyboard,smoothly,without that little delay?
#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
using namespace sf;
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
int main(){
VideoMode VMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32);
RenderWindow screen(VMode, "Empty Window");
Event event;
Keyboard key;
Color screencolor(0,150,0);
Texture pietexture;
pietexture.loadFromFile("image.png");
Sprite pie(pietexture);
pie.setPosition(150,150);
screen.draw(pie);
screen.display();
bool on = true;
while(on){
screen.clear(Color(0,255,0));
while(screen.pollEvent(event)){
if(event.type == Event::Closed){
screen.close();
on = false;
}
else if(event.type == Event::KeyPressed){
if(key.isKeyPressed(Keyboard::Left)){
pie.move(-10,0);
}
if(key.isKeyPressed(Keyboard::Right)){
pie.move(10,0);
}
if(key.isKeyPressed(Keyboard::Up)){
pie.move(0,-10);
}
if(key.isKeyPressed(Keyboard::Down)){
pie.move(0,10);
}
}
}
screen.draw(pie);
screen.display();
}
}
What it currently does,is makes a little pause after a keypress,and then goes normally,but how can i make it without that little pause at the beggining.
Instead of moving the object on keypress events, set a variable to indicate which direction it should be moving, and at what speed. Then, on key release events, reset the speed to 0. Then handle the movement separately from the event processing section, regulated by a combination of those variables and some kind of timing system.
Use sf::Keyboard::isKeyPressed(key) to poll the key status in real time. As a rule of thumb, don't use events where a highly responsive interface is needed.
You will also need a timing mechanism to regulate speed. There is a sf::Clock which you can use for this purpose.
Related
I'm new in allegro and trying to make a small, very basic painting program. I want this program to stop painting when in the area from A(0, 0) to B(100, 600) on 800x600 plane. When I hover my cursor in this area the program suddenly closes itself. Here is the code:
#include <allegro5/allegro.h>
#include <allegro5/allegro_native_dialog.h>
#include <allegro5/allegro_primitives.h>
#define screen_width 800
#define screen_height 600
int main()
{
al_init();
ALLEGRO_DISPLAY* display = al_create_display(screen_width, screen_height);
al_install_mouse();
al_install_keyboard();
al_init_primitives_addon();
ALLEGRO_EVENT_QUEUE* event_que = al_create_event_queue();
al_register_event_source(event_que, al_get_display_event_source(display));
al_register_event_source(event_que, al_get_keyboard_event_source());
al_register_event_source(event_que, al_get_mouse_event_source());
bool game_over = false, hold = false;
int x = 10, y = 10;
while (!game_over){
ALLEGRO_EVENT event;
al_wait_for_event(event_que, &event);
if (event.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN){
hold = true;
}
if (event.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP){
hold = false;
}
if (event.keyboard.keycode == ALLEGRO_KEY_ESCAPE){
game_over = true;
}
if (hold){
x = event.mouse.x;
y = event.mouse.y;
if (x > 100){
al_draw_filled_circle(x, y, 4, al_map_rgb(210, 0, 0));
}
}
al_flip_display();
}
al_destroy_event_queue(event_que);
al_destroy_display(display);
return 0;
}
Edit:
I've found that whenever i hover in that area from (0, 0) to (100, 600)
this if statement is being executed:
if (event.keyboard.keycode == ALLEGRO_KEY_ESCAPE)
I don't know why it is executing.
Let's look at what ALLEGRO_EVENT event is, via the docs:
An ALLEGRO_EVENT is a union of all builtin event structures, i.e. it is an object large enough to hold the data of any event type. All events have the following fields in common:
type (ALLEGRO_EVENT_TYPE)
Indicates the type of event.
[...]
By examining the type field you can then access type-specific fields
You can't do stuff that accesses event.keyboard members if the event.type is not a keyboard event, because then you're reading from an inactive union member. That is undefined behaviour, so speculation is not very useful - but in this case, it looks like you read some bit of a mouse coordinate as if it were a key, but it's not. Results due to UB are meaningless, but it seems that, in this case, they correlated with coordinates to make your code think Esc was pressed.
Check you have a keyboard event type before reading the keyboard members:
if (event.type == ALLEGRO_EVENT_KEY_DOWN &&
event.keyboard.keycode == ALLEGRO_KEY_ESCAPE)
{
game_over = true;
}
You need to be extremely careful, anywhere you use a union, to know any member is valid when you read from it. About the only real way to be sure is to tag it with a type - and even that requires that you remember to check the type first!
A superior way is std::variant, which lets you check the currently held type or, if casting to it, throws if it doesn't hold that. Of course, you can't use that here, unless someone creates a wrapper for ALLEGRO_EVENT - which you might have time for! - but I mention it to replace unions in your own code.
I'm trying to make some animation with SDL2.
SDL2 has some bad performance, and my simple chess game was running at 10fps.
I don't want to redraw the whole screen every frames, it takes way to long. So in order to optimize my game I decided to code an Animation class ( a simple fade in/out effect) which only redraw some part of the screen everyframe (basicly redraw the selected piece)
void myGame::sdlLoop() {
SDL_Event events;
bool quit = false;
while (!quit) {
bool redraw = false; //We assume we don't want to redraw everything (yet)
while (SDL_PollEvent(&events)) {
switch (events.key.keysym.scancode) {
....
}
if(redraw) draw(); // Only redraw whole screen IF NEEDED
else drawAnimations(); // Better use that for better performance
}
}
}
void myGame::drawAnimations(){
int i = 0;
while(i < arr.size()){
....
drawThingsAtCertainsPixels(now_time, animation_start_time, animation_duration); //Basicly a simple fade effect, something like
//pixelColor = color1 * advancement + color2 * (1 - advancement)
}
// Show the window
SDL_RenderPresent( m_renderer );
}
So far so good, but I noticed a weird behavior.
The animation is "jerky", most of the frames are skipped
I ended up avec all of my fadeout unfinished because the last frame where skipped.
But, when I constantly move the mouse, everything goes right and no frame are dropped
I think it is linked to SDL wanting to optimize performance, and only run at 100% speed when someting important is going on (user inputting things or windows interacted).
Do you know why is this happening, and how to fix that ?
I mean how to have SDL2 computing every frame even if I don't move the mouse.
I feel a bit guilty for finding the solution right after posting this question (but I swear I tore my hair out a lot)
The solution is quite simple
while (!quit) {
bool redraw = false; //We assume we don't want to redraw everything (yet)
while (SDL_PollEvent(&events)) {
switch (events.key.keysym.scancode) {
....
}
if(redraw) draw(); // Only redraw whole screen IF NEEDED
else drawAnimation(); // Better use that for better performance
}
}
Just move the drawAnimations() function a bit lower, out of the while (SDL_PollEvent(&events)) {
while (!quit) {
bool redraw = false; //We assume we don't want to redraw everything (yet)
while (SDL_PollEvent(&events)) {
switch (events.key.keysym.scancode) {
....
}
}
if(redraw) draw(); // Only redraw whole screen IF NEEDED
else drawAnimation(); // Better use that for better performance
}
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.
I've been working on this little framework for my game today and realized that moving delays when moving around. I've used SFML before and have had the same problem, but never bothered to fix it. I'm wondering if anyone has had a similar issue and has found a solution.
Here's an example of the code which is giving me the slight pause in movement:
int main(){
sf::RenderWindow window (sf::VideoMode(640, 480), "Window");
//window.setVerticalSyncEnabled(true);
sf::Texture tex;
tex.loadFromFile("Assets/Textures/player.png");
sf::Sprite s;
s.setTexture(tex);
while (window.isOpen()){
sf::Event event;
while (window.pollEvent(event)){
if (event.type == sf::Event::Closed)
window.close();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)){
s.move(0.01f, 0.0f);
}
window.clear();
window.draw(s);
window.display();
}
}
The delay/pause/whatever I'm experience is very slight but I can notice it and it's really bugging me. Just seeing the slight break in movement when none of the values alter makes my programming mind extremely sad.
The keyboard input will have a delay just like if you hold a key in Microsoft Word. To get around this, you could create a variable and set it to true when a key is pressed.
int main(){
sf::RenderWindow window (sf::VideoMode(640, 480), "Window");
//window.setVerticalSyncEnabled(true);
sf::Texture tex;
tex.loadFromFile("Assets/Textures/player.png");
sf::Sprite s;
s.setTexture(tex);
bool moving = false;
while (window.isOpen()){
sf::Event event;
while (window.pollEvent(event)){
if (event.type == sf::Event::Closed)
window.close();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)){
moving = true;
}
if (moving) {
s.move(0.01f, 0.0f);
}
window.clear();
window.draw(s);
window.display();
}
}
This sounds like a framerate/timing issue. One frame in a 60hz game is 16.666.. milliseconds, but on Windows at least, the system clock only has a resolution of 1ms, so your delays are going to be either 16ms or 17ms. This inaccuracy results in occasional jitter as some frames are skipped (or rendered twice, in your case, giving the appearance of a short pause) even if vsync is enabled.
If you can get manual control of frame timing, e.g. by disabling sf::RenderWindow's framerate limit and calling sf::sleep directly, one solution is to delay each frame in the sequence: 17, 17, 16, 17, 17, 16, so that the long-term average is closer to 60hz, minimizing jitter.
I have never used SFML so I can't provide any code but I hope this points you in the right direction.
You should also ensure that your monitor's refresh rate is the same as your game's framerate. SFML might already be doing this automatically.
I've done a very basic window with SDL and want to keep it running until I press the X on window.
#include "SDL.h"
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
int main(int argc, char **argv)
{
SDL_Init( SDL_INIT_VIDEO );
SDL_Surface* screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, 0,
SDL_HWSURFACE | SDL_DOUBLEBUF );
SDL_WM_SetCaption( "SDL Test", 0 );
SDL_Event event;
bool quit = false;
while (quit != false)
{
if (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
quit = true;
}
}
SDL_Delay(80);
}
SDL_Quit();
return 0;
}
I tried adding SDL_Delay() at the end of the while-clause and it worked quite well.
However, 80 ms seemed to be the highest value I could use to keep the program running smoothly and even then the CPU usage is about 15-20%.
Is this the best way to do this and do I have to just live with the fact that it eats this much CPU already on this point?
I know this is an older post, but I myself just came across this issue with SDL when starting up a little demo project. Like user 'thebuzzsaw' noted, the best solution is to use SDL_WaitEvent to reduce the CPU usage of your event loop.
Here's how it would look in your example for anyone looking for a quick solution to it in the future. Hope it helps!
#include "SDL.h"
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
int main(int argc, char **argv)
{
SDL_Init( SDL_INIT_VIDEO );
SDL_Surface* screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, 0,
SDL_HWSURFACE | SDL_DOUBLEBUF );
SDL_WM_SetCaption( "SDL Test", 0 );
SDL_Event event;
bool quit = false;
while (quit == false)
{
if (SDL_WaitEvent(&event) != 0) {
switch (event.type) {
case SDL_QUIT:
quit = true;
break;
}
}
}
SDL_Quit();
return 0;
}
I would definitely experiment with fully blocking functions (such as SDL_WaitEvent). I have an OpenGL application in Qt, and I noticed the CPU usage hovers between 0% and 1%. It spikes to maybe 4% during "usage" (moving the camera and/or causing animations).
I am working on my own windowing toolkit. I have noticed I can achieve similar CPU usage when I use blocking event loops. This will complicate any timers you may depend on, but it is not terribly difficult to implement timers with this new approach.
I just figured out how to reduce CPU usage in my game from 50% down to < 10%.
Your program is much more simple and simply using SDL_Delay() should be enough.
What I did was:
Use SDL_DisplayFormat() when loading images, so the blitting would be faster. This brought its CPU usage down to about 30%.
So I found out that blitting the games background (big one-piece .png file) was eating the most out of my CPU. I searched the Internet for a solution, but all I found was the same answer - just use SDL_Delay(). Finally, I found out that the problem was embarrassingly simple - the SDL_DisplayFormat() was converting my 24-bit images to 32-bit. So I set my display BPP to 24, which brought CPU usage to ~20%. Bringing it down to 16 bit solved the problem for me and the CPU usage is under 10% now.
Of course this means loss of color detail, but as my game is a simplistic 2D game with not too detailed graphics, this was OK.
In order to really understand this, you need to understand threading. In a threaded application, the program runs until it is waiting for something, then it tells the OS that something else can run. In essence, you are doing this with the SDL_Delay command. If there was no delay at all, I suspect your program would be running at near 100% capacity.
The amount of time that you should put in the delay statement only matters if the other commands are taking a significant amount of time. In general, I would put the delay to be a similar amount of time that it takes to test the poll command, but not more than, say, 10 ms. What will happen is that the OS will wait at least that length of time, allowing other applications to run in the background.
As to what you can do to improve this, well, it looks like there isn't a whole lot that you can do. However, take note that if there was another process running taking a significant amount of CPU power, your program's share would decrease.