sfml animation of explosion after mouseclick - c++

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.

Related

How do I handle mouse events in general in Imgui with glfw?

I used the glfw callback function to move the camera with the mouse.
mouse callback function is:
void mouse_callback(GLFWwindow *window, double xposIn, double yposIn)
{
if (is_pressed)
{
camera.ProcessMouseMovement((static_cast<float>(yposIn) - prev_mouse.y) / 3.6f, (static_cast<float>(xposIn) - prev_mouse.x) / 3.6f);
prev_mouse.x = xposIn;
prev_mouse.y = yposIn;
}
cur_mouse.x = xposIn;
cur_mouse.y = yposIn;
}
void mouse_btn_callback(GLFWwindow *window, int button, int action, int mods)
{
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS)
{
prev_mouse.x = cur_mouse.x;
prev_mouse.y = cur_mouse.y;
is_pressed = true;
}
else
{
is_pressed = false;
}
}
However, in this case, the camera will move even when operated in other imgui windows as shown below.
I don't know how to handle this.
Should I put this logic between begin and end of IMGUI, using something like ImGui::IsWindowHovered()?
like this:
ImGui::Begin("scene");
{
if(ImGui::IsWindowHovered())
{
//camera setting
}
}
ImGui::End()
I had the same problem today.
For anyone seeing this now, you have to define your glfw callbacks before initializing ImGui. ImGui sets its own callbacks up at this point and handles sending inputs to already existing ones, if not consumed before. If you define your callbacks afterwards you overwrite those created by ImGui.
Answer above are wrong.
This is answered in the Dear ImGui FAQ:
https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#q-how-can-i-tell-whether-to-dispatch-mousekeyboard-to-dear-imgui-or-my-application
TL;DR check the io.WantCaptureMouse flag for mouse.
I'm not familiar with ImGui, so I don't know what functions might or might not need to be called in ImGui.
But, GLFW is a relatively low level windowing API that has no regard for the higher level abstractions that might exist on the window. When you pass the callback to glfwSetCursorPosCallback, that callback will be called on any accessible part of the window.
If you need to have the mouse movements (or any mouse interactions) only respond when the mouse is hovered over the relevant part of the interface, you need some kind of mechanism to define what that part is. Again: I don't know how you'd do that in ImGui, but it'll probably look something like this:
void mouse_callback(GLFWwindow *window, double xposIn, double yposIn)
{
//Structured Binding; we expect these values to all be doubles.
auto [minX, maxX, minY, maxY] = //Perhaps an ImGui call to return the bounds of the openGL surface?
if(xposIn < minX || xposIn > maxX || yposIn < minY || yposIn > maxY) {
return; //We're outside the relevant bounds; do not do anything
}
//I'm assuming setting these values should only happen if the mouse is inside the bounds.
//Move it above the first if-statement if it should happen regardless.
cur_mouse.x = xposIn;
cur_mouse.y = yposIn;
if (is_pressed)
{
camera.ProcessMouseMovement((static_cast<float>(yposIn) - prev_mouse.y) / 3.6f, (static_cast<float>(xposIn) - prev_mouse.x) / 3.6f);
prev_mouse.x = xposIn;
prev_mouse.y = yposIn;
}
}

C++ and SDL2 Grid movement is too fast

My grid movement is too fast. This problem is due to speed, but I can't change it. Because then the grid doesn't work.
if (Game::event.type == SDL_KEYDOWN) {
if (Game::event.key.keysym.sym == SDLK_w || Game::event.key.keysym.sym == SDLK_UP) {
transform->velocity.y = -1;
}
else if (Game::event.key.keysym.sym == SDLK_s || Game::event.key.keysym.sym == SDLK_DOWN) {
transform->velocity.y = 1;
}
else if (Game::event.key.keysym.sym == SDLK_d || Game::event.key.keysym.sym == SDLK_RIGHT) {
transform->velocity.x = 1;
}
else if (Game::event.key.keysym.sym == SDLK_a || Game::event.key.keysym.sym == SDLK_LEFT) {
transform->velocity.x = -1;
}
} else if (Game::event.type == SDL_KEYUP) {
transform->velocity.x = 0;
transform->velocity.y = 0;
}
And update for player position:
void update() override {
position.x += round(velocity.x * 32);
position.y += round(velocity.y * 32);
}
Problem is in update player position, but if there weren't *32 player gets out from grid. (32 is grid size)
Any idea how to solve it?
And yes, I use SDL_Delay.
If you want only one step per press then in your update() function you need to zero your velocity vector after you're done using it, that will make it step only once, but you also have to check event.key.repeat and only accept events when it's 0 to avoid repeating the event ~30 times/second as long as the key is held down. If you want the stepping to repeat after a given delay then you'll need to store the return value (a uint32_t or Uint32 if you prefer) of SDL_GetTicks(); taken the moment a SDL_KEYUP event was received (with repeats ignored) then check if enough milliseconds have passed to repeat the stepping then add the time threshold to the stored time value to properly wait for the next repeating.
Also you're zeroing your velocity vector every time any key is released, which in some cases you don't want. It might be better to add to the vector for each SDL_KEYDOWN event and subtract to under for each SDL_KEYUP, for matching keys of course. Also if you're gonna use WASD you should use scancodes instead of keysym, or your control scheme will be quite awkward on non-QWERTY layouts.
Also there's probably no need for SDL_Delay(), just use Vsync which is set when you initialise your renderer with something like renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);.

SDL2 "skip" frames when mouse no moving

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
}

SFML extremly slow/irregular framerate

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

Zooming in and out a picture (sfml) using recursion in c++

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".