C++ and SFML - Transforming by decimal causes artifacts - c++

I’m making a game with C++ and SFML. I have delta time in my game by setting it to clock.restart().asSeconds() and then multiplying that by a movement offset. The only issue is that when the final movement value is not an integer (or .0f), it causes rendering artifacts between my sprites. I have a grid of sprites that are all lined up next to each other. When I move by an integer offset, it looks good. However, when moving with a decimal, I can see strange lines between the sprites. Is there any way of fixing this?
note: I don't want to round the deltatime * offset value because when fps is too high, it will be rounded to 0 and the player will not move at all.
delta time code:
void Game::run()
{
init();
Gui::init();
Gui::mainMenu();
sf::Clock deltaClock;
while (window.isOpen())
{
sf::Event loopEvent;
while (window.pollEvent(loopEvent))
{
switch (loopEvent.type)
{
case sf::Event::Closed:
saveGame();
window.close();
break;
case sf::Event::KeyPressed:
if (loopEvent.key.code == Gui::keybinds[Action::EXIT_MENU] && Player::hasSpawned)
{
Gui::inMenu = false;
Gui::currentGameState = GameState::SETTINGS;
}
}
}
deltaTime = deltaClock.restart().asSeconds();
update();
window.setView(camera);
window.clear(sf::Color::White);
draw();
window.display();
Gui::checkGameState();
}
}
const float Game::MAKE_DELTA(const float OFFSET)
{
return OFFSET * deltaTime;
}
Moving with integer offset (or when I round the return value of MAKE_DELTA()):
Moving with decimal offset (unrounded return value from MAKE_DELTA()):

Related

Motion After Rotation In Sdl C++

Here are my functions:
void main_loop(){
SDL_Surface* image;
image=SDL_LoadBMP("plane.bmp");
plane=SDL_CreateTextureFromSurface(renderer,image);
double angle=90;
int speed=1;
SDL_Event event;
bool quit=false;
while(!quit){
while(SDL_PollEvent(&event)!=0){
if(event.type==SDL_QUIT)
quit=true;
if(event.type==SDL_KEYDOWN){
switch(event.key.keysym.sym){
case SDLK_RIGHT:
angle+=90*delta;
break;
case SDLK_LEFT:
angle-=90*delta;
break;
}
}
}
//update_delta();
int angle_rad=angle*PI/180;
x+=sin(angle_rad)*speed*delta;
y-=cos(angle_rad)*speed*delta;
draw((int)x,(int)y,angle);
}
last_time=SDL_GetTicks()/1000;
return;
}
void draw(Uint32 x,Uint32 y,double angle){
SDL_RenderClear(renderer);
SDL_Rect rect;
rect.x=x;
rect.y=y;
rect.h=100;
rect.w=100;
SDL_RenderCopyEx(renderer,plane,NULL,&rect,angle,NULL,SDL_FLIP_NONE);
SDL_SetRenderDrawColor(renderer,0,0,0,255);
SDL_RenderPresent(renderer);
}
Basically what I want to do is to rotate a texture, and move it a fixed speed in the direction it is facing. The delta value is the time between two frames, and i use this to figure out the increment of x and y coordinates per frame. However , there is some problem with the angles. The texture does not move properly, but at some awkward angle which makes it look like it is drifting. So far I have tried changing trig functions, negating x and y, but it simply does not work.
I am using SDL 2.0 with C++.
int angle_rad=angle*PI/180;
Your radians are being stored in an integer, so they will get truncated to the nearest whole number. Store them in a float or a double instead.

Sprites aren't animating

I've been trying to update enemy sprites in my game by creating a vector of pointers to enemy objects and then using an update function to animate the sprites belonging to the objects. Although the enemy sprites are displayed on the screen, they won't get updated so they look as though they're frozen.
Here's how I've written my code:
#include<iostream>
#include<SFML/Graphics.hpp>
#include<math.h>
#include<vector>
#include<cstdlib>
#include "Enemy.h"
sf::RenderWindow window(sf::VideoMode(1920, 1080), "Zombie game", sf::Style::Default);
std::vector<Enemy*>enemies;
int main()
{
window.setFramerateLimit(60);
sf::Clock clock;
Enemy *enemy = new Enemy();
enemy->init("Assets/graphics/zombieSpriteSheetWalk.png", 4, 1.0f, sf::Vector2f(200.0f, 200.0f), sf::Vector2i(100, 107));
enemies.push_back(enemy);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed || event.key.code == sf::Keyboard::Escape) window.close();
}
sf::Time dt = clock.restart();
for (int i = 0; i < enemies.size(); i++)
{
Enemy *enemy = enemies[i];
enemy->update(dt.asSeconds());
}
window.clear();
for (Enemy* enemy : enemies) window.draw(enemy->getSprite());
window.display();
}
return 0;
}
Enemy.h file:
#pragma once
#include<SFML/Graphics.hpp>
#include <iostream>
class Enemy
{
public:
Enemy();
~Enemy();
void init(std::string textureName, int frameCount, float animDuration, sf::Vector2f position, sf::Vector2i spriteSize);
void update(float dt);
sf::Sprite& getSprite();
void test();
private:
sf::Texture m_texture;
sf::Sprite m_sprite;
sf::Vector2f m_position;
int m_frameCount; //no. of frames in animation
float m_animDuration; //How long animation lasts (speed)
float m_elapsedTime; //keeps track of how long game has been running
sf::Vector2i m_spriteSize; //Size of each frame
};
Enemy.cpp file:
#include "Enemy.h"
Enemy::Enemy()
{
}
Enemy::~Enemy()
{
}
void Enemy::init(std::string textureName, int frameCount, float animDuration, sf::Vector2f position, sf::Vector2i spriteSize)
{
m_position = position;
m_frameCount = frameCount;
m_animDuration = animDuration;
m_spriteSize = spriteSize;
m_texture.loadFromFile(textureName.c_str());
m_sprite.setTexture(m_texture);
m_sprite.setTextureRect(sf::IntRect(0, 0, m_spriteSize.x, m_spriteSize.y));//sets which part of sprite sheet we want to display
m_sprite.setPosition(m_position);
m_sprite.setOrigin((m_texture.getSize().x / frameCount) - 25.0f, m_texture.getSize().y / 2);
}
void Enemy::update(float dt)
{
m_elapsedTime += dt;
int animFrame = static_cast<int>((m_elapsedTime / m_animDuration) * m_frameCount) % m_frameCount; //calculates current animation frame number. static_class converts float to int
m_sprite.setTextureRect(sf::IntRect(animFrame * m_spriteSize.x, 0, m_spriteSize.x, m_spriteSize.y)); //updates part of sprite sheet to be displayed
}
sf::Sprite& Enemy::getSprite()
{
return m_sprite;
}
You're always at time 0
From the SFML Clock header:
////////////////////////////////////////////////////////////
/// \brief Restart the clock
///
/// This function puts the time counter back to zero.
/// It also returns the time elapsed since the clock was started.
///
/// \return Time elapsed
///
////////////////////////////////////////////////////////////
Time restart();
Possible Fix
Use sf::Clock for dt instead of sf::Time and move it out of the event loop (you want it to accumulate as the program runs, not reset to 0 every time through.
Replace:
enemy->update(dt.asSeconds());
with
enemy->update(dt.getElapsedTime());
More Notes
I'm making some assumptions in this answer of types because your example was incomplete. One big omission you made was not including the class Enemy definition (especially the types of the member variables).
Based on the definition of Enemy::init, I would assume it would contain the following:
class Enemy
{
float m_elapsedTime;
float m_animDuration;
int m_frameCount;
...
However, those definitions result in the following compilation error in your version of Enemy::update:
enemy.cpp: In member function ‘void Enemy::update(float)’:
enemy.cpp:14:88: error: invalid operands of types ‘float’ and ‘int’ to binary ‘operator%’
int animFrame = static_cast<int>(((m_elapsedTime / m_animDuration) * m_frameCount) % m_frameCount);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~
floats
You don't want to run animations that are a function of game time if game time is a float. The reason why not is that floating point numbers have different precision in different value ranges, the closer the number is to 0, the more precision you have, and the farther you are from 0, the less precision you have. This means that as game time progresses, your animations will not run at a constant rate (they will appear to be choppy, inconsistent, not smooth after enough time has elapsed).
A better approach would be to have an wrapping animation counter that adds the delta times and then wraps back to zero (or perhaps wraps back to 0 + some remainder, perhaps using a function like fmod). This will make sure your animation frame rate is consistent for the entire duration of the game.

sfml animation of explosion after mouseclick

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.

Make Object Move Smoothly In C++-SFML

Hey Guys I'm a Beginner in Game Development with C++ and Sfml I Wrote this code to make that purple Object move,but the problem is that it doesn't move smoothly, it's like the text input, How to fix That ?
Here Is My code :
#include <SFML/Graphics.hpp>
int main()
{
sf::ContextSettings settings;
settings.antialiasingLevel = 12;
sf::RenderWindow window(sf::VideoMode(640, 480), "Ala Eddine", sf::Style::Default, settings);
sf::CircleShape player(20, 5);
player.setFillColor(sf::Color(150, 70, 250));
player.setOutlineThickness(4);
player.setOutlineColor(sf::Color(100, 50, 250));
player.setPosition(200, 200);
while(window.isOpen())
{
sf::Event event;
while(window.pollEvent(event))
{
if(event.type == sf::Event::Closed || sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
{
window.close();
}
//MOOVING PLAYER////////////////////////////////////////
// moving our player to the right //
if(sf::Keyboard::isKeyPressed(sf::Keyboard::D)){ //
//
//
player.move(3, 0);
}
// moving our player to the left
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Q)){
player.move(-3, 0);
}
// moving our player to the UP
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Z)){
player.move(0, -3);
}
// moving our player to DOWN
if(sf::Keyboard::isKeyPressed(sf::Keyboard::S)){
player.move(0, 3);
}
}
window.clear();
window.draw(player);
window.display();
}
return 0;
}
I assume your player.move() method simply adds the offset to the player position. This means that your object will always move with the same constant velocity (assuming frame rate is constant). What you want instead is to have an acceleration which updates the velocity in every frame.
Here's the basic idea (for one direction; the y direction will work accordingly, although using vectors would be better):
Give your object both a velocity and an acceleration.
If a key is held down, set the acceleration to a constant term, otherwise set it to zero.
In every frame, add timestep * acceleration to the velocity.
In every frame, add timestep * velocity to the object position.
In every frame, multiply the velocity with some decay factor, say 0.99.
This is assuming you have a fixed timestep (say, 1/60 s for 60 fps). Timestepping is slightly more advanced, and I'll refer you to this article on the topic.

How can I set gravity using this code? SFML/C++ [duplicate]

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);
}