animation of sfml by using array of objects - c++

How to animate sprite with this code?I know what i should to add time delay,but how do it? I use array of objects to they's fast changing. or is it an irrational way of animating?
#include <SFML/Graphics.hpp>
#include <iostream>
using namespace std;
sf::RenderWindow window(sf::VideoMode(600, 400), "!!!");
void animation()
{
sf::Texture arrayOfTexture[9];
sf::Sprite imageOfLamp;
arrayOfTexture[0].loadFromFile("1.png");
arrayOfTexture[1].loadFromFile("2.png");
arrayOfTexture[2].loadFromFile("3.png");
arrayOfTexture[3].loadFromFile("4.png");
arrayOfTexture[4].loadFromFile("5.png");
arrayOfTexture[5].loadFromFile("6.png");
arrayOfTexture[6].loadFromFile("7.png");
arrayOfTexture[7].loadFromFile("8.png");
arrayOfTexture[8].loadFromFile("9.png");
for(;;)
{
for(int i= 0;i <=8;i++)
{
imageOfLamp.setTexture(arrayOfTexture[i]);
window.draw(imageOfLamp);
}
for(int i =8;i >=0;i--)
{
imageOfLamp.setTexture(arrayOfTexture[i]);
window.draw(imageOfLamp);
}
}
}

As said in the comments. For the animation you need to capture the time. Or you will not be able to calculate the duration per frame. And you need to store the current frame in a variable. I put some comment in the example code below. It is not a class anymore because I think we need a running code block here.
An alternative is the sprite sheet, see https://www.gamefromscratch.com/post/2015/10/26/SFML-CPP-Tutorial-Spritesheets-and-Animation.aspx I can re-write the answer with the sprite sheet, but I think you wanted to understand the basic mechanism of an animation first. Please indicate so in a comment to my post.
Btw, if you are not used to game loops, have a look on https://gafferongames.com/post/fix_your_timestep/
#include <SFML/Graphics.hpp>
int main(){
sf::RenderWindow renderWindow(sf::VideoMode(800, 600), "Color Animation");
// Duration to control animation speed
int currentFrame = 1;
float duration = float();
sf::Clock clock;
sf::Event event;
sf::Texture arrayOfTexture[9];
arrayOfTexture[0].loadFromFile("1.png");
arrayOfTexture[1].loadFromFile("2.png");
arrayOfTexture[2].loadFromFile("3.png");
arrayOfTexture[3].loadFromFile("4.png");
arrayOfTexture[4].loadFromFile("5.png");
arrayOfTexture[5].loadFromFile("6.png");
arrayOfTexture[6].loadFromFile("7.png");
arrayOfTexture[7].loadFromFile("8.png");
arrayOfTexture[8].loadFromFile("9.png");
// Assign basic texture to sprite
sf::Sprite imageOfLamp;
imageOfLamp.setTexture(arrayOfTexture[0]);
while (renderWindow.isOpen()){
// How much time since last loop?
sf::Time dt = clock.restart();
duration += dt.asSeconds();
while (renderWindow.pollEvent(event)){
//Handle events here
if (event.type == sf::Event::EventType::Closed)
renderWindow.close();
}
// Animation duration per frame (0.1f) reached
if (duration > 0.1f){
// Restart calculation of the duration
duration = 0;
// Loop through the animation frames
if (currentFrame < 9){
currentFrame += 1;
} else {
// Start from first frame if last frame reached
currentFrame = 0;
}
imageOfLamp.setTexture(arrayOfTexture[currentFrame]);
}
// Clear render window and draw sprite
renderWindow.clear(sf::Color::Black);
renderWindow.draw(imageOfLamp);
renderWindow.display();
}
}

Related

SFML multiple objects with random values and their collisions

I'm pretty new with SMFL and C++ for the moment and I've got stuck while I was creating very simple physics simulator with balls.
Its my main.cpp:
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Window/Event.hpp>
#include "ball.hpp"
int main()
{
/*create window settings*/
sf::ContextSettings settings;
/*create window*/
sf::RenderWindow window;
window.create(sf::VideoMode(600, 600), "Simple Physics", sf::Style::Default, settings);
/*create ball(s)*/
Ball ball;
/*Main loop*/
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear(sf::Color::Black);
// call ball.update(); and ball.draw();
balls.update();
balls.draw(window);
window.display();
}
}
And ball.hpp :
#include <SFML/Graphics/CircleShape.hpp>
class Ball
{
public:
int minXY = 0;
int maxXY = 600;
int ballRadius = rand() % 50 + 5;
int random = rand() % maxXY + 1; //random XY position
// vector for positions
//sf::Vector2f pos{random, random}; -> I Guess this should be included into loop
sf::Vector2f pos{100, 300};
// vector for velocity
sf::Vector2f vel{ 0.1, 0.1 };
void update()
{
// factors influence velocity
// update position based on velocity
pos.x += vel.x;
pos.y += vel.y;
if (pos.x + ballRadius*2 > maxXY || pos.x < minXY) vel.x = -vel.x; //boundary cond
if (pos.y + ballRadius*2 > maxXY || pos.y < minXY) vel.y = -vel.y; //boundary cond
}
void draw(sf::RenderWindow& window)
{
// draw ball to the window using position vector
sf::CircleShape circle(ballRadius);
circle.setPosition(pos.x, pos.y);
circle.setFillColor(sf::Color::White);
window.draw(circle);
}
};
Now, I want to draw multiple cirlcles, with random size, velocity and colors.
They should bounce on collision, i've got some ideas for that but can't really try them without multiple balls.
I've tried to make it with simple for loop, but it's not working.
After reading and watching many tutorials I'm still in this point so it would be great if somebody can help me up with ANY working example or some improvements to my code.
I will not give you working example but I think I can point you in the right direction:
You need some kind of Constructor for your Ball class, and given the example you should probably learn more about classes in general (for example here).
Then you should store balls you spawn from main loop in some kind of container. If you do know the number of balls you want to spawn std::array should be you choice, otherwise std::vector is the way to go.
You need some function to check collisions, static member function of Ball class should do the trick.

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.

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

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.

SFML 2.0 Looping a sprite to display more than once

I have asked a similar question in the past but I still can't get my head around this. I am doing an invaders game based on SFML 2.0. So far I have one sprite sheet which runs through using my clock. This part works just fine:
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <iostream>
#include <string>
int spriteWalkSpeed = 10;
int DownspriteWalkSpeed = 5;
int up=-spriteWalkSpeed, down=DownspriteWalkSpeed, left=-spriteWalkSpeed, right=spriteWalkSpeed;
int xVelocity =0, yVelocity=0;
const int SC_WIDTH=800;
const int SC_HEIGHT= 600;
const float REFRESH_RATE =0.01f; //how often we draw the frame in seconds
const double delay=0.1;
const int SPRITEROWS=1; //number of ROWS OF SPRITES
const int SPRITECOLS=2;//number of COLS OF SPRITES
std::string gameOver = "Game Over";
int main()
{
// Create the main window
sf::RenderWindow App (sf::VideoMode(SC_WIDTH, SC_HEIGHT, 32), "Space Invaders!",sf::Style::Close );
// Create a clock for measuring time elapsed
sf::Clock Clock;
//background texture
sf::Texture backGround;
backGround.loadFromFile("images/background.jpg");
sf::Sprite back;
back.setTexture(backGround);
//load the invaders images
sf::Texture invaderTexture;
invaderTexture.loadFromFile("images/invaders.png");
sf::Sprite invadersSprite(invaderTexture);
std::vector<sf::Sprite> invaderSprites(10, sf::Sprite(invaderTexture));
int invadersWidth=invaderTexture.getSize().x;
int invadersHeight=invaderTexture.getSize().y;
int spaceWidth=invadersWidth/SPRITECOLS;
int spaceheight=invadersHeight/SPRITEROWS;
//Sprites
sf::IntRect area(0,0,spaceWidth,spaceheight);
invadersSprite.setTextureRect(area);
invadersSprite.setPosition(30, NULL);
App.setKeyRepeatEnabled(false);
//Collision detection
// Start game loop
while (App.isOpen())
{
// Process events
sf::Event Event;
while (App.pollEvent(Event))
{
// Close window : exit
if (Event.type == sf::Event::Closed)
App.close();
}
// Create an array of 10 sprites (cannot initialise them with textures here)
for (int i = 0; i < 10; i++)
{
invaderSprites[i].setPosition(30,0);
if(Clock.getElapsedTime().asSeconds()>REFRESH_RATE)
{
//carry out updating tasks
static float spriteTimer=0.0; //keep track of sprite time
spriteTimer+=Clock.getElapsedTime().asSeconds();
static int count=0; //keep track of where the sub rect is
if(spriteTimer>delay)
{
invaderSprites[i].setTextureRect(area);
++count;
invaderSprites[i].move(xVelocity, yVelocity);
if(count==SPRITECOLS) //WE HAVE MOVED OFF THE RIGHT OF THE IMAGE
{
area.left=0; //reset texture rect at left
count=0; //reset count
}
else
{
area.left+=spaceWidth; //move texture rect right
}
spriteTimer=0; //we have made one move in the sprite tile - start timing for the next move
}
Clock.restart();
}
App.draw(back);
App.draw(invaderSprites[i]);
// Finally, display the rendered frame on screen
App.display();
}
}
return EXIT_SUCCESS;
}
The issue I am having is that the sprite only shows once, not 10 times (as the for loop states)
std::vector<sf::Sprite> invaderSprites(10, sf::Sprite(invaderTexture));
// Loop over the elements of the vector of sprites
for (int i = 0; i < invaderSprites.size(); i++)
{
invaderSprites[i].setPosition(30, NULL);
}
// Create an array of 10 sprites (cannot initialise them with textures here)
sf::Sprite invaderSprites[10]; // Loop over each sprite, setting their textures
for (int i = 0; i < 10; i++)
{
invaderSprites[i].setTexture(invaderTexture);
}
I am pretty sure it has something to do with the app drawing invadersSprite whereas the loop is setup for invaderSprites. Even just a little insight into what is going wrong would be such a big help.
I am pretty sure it has something to do with the app drawing
invadersSprite whereas the loop is setup for invaderSprites.
Yes, that certainly has something to do with it. You need to call App.draw(...) for each sprite that you want to draw. You're not calling it for any of the sprites in your vector. For that, you would want a loop:
for (int i=0; i<invaderSprites.size(); ++i)
App.draw(invaderSprites[i]);
There are other problems though. For example, why are you declaring an array of sprites called invaderSprites, when you already have a vector of sprites with that same name? The latter hides the former once it is declared.
Another thing is that you are setting all the sprites to the same position, so even if if you do manage to draw them all, they will all be in the same spot, and as such they will not appear as separate objects.