I'm working on a little SFML game in C++. What I'm trying to do is start the game with an introductory text that flashes two different colors, like "Arturo Girona Presents." I made it work by using a counter that incremented inside the while (window.isOpen()) loop, but then I tried using an sf::Clock to see if it made the flashing look less random. However, for some reason, the clock keeps resetting after each loop when I haven't explicitly told it too, so it's not transitioning from the text screen. How's the clock resetting by itself and how do I fix it?
Here's my while loop:
int count = 0;
sf::Clock clock;
int timer = clock.getElapsedTime().asMilliseconds();
while (window.isOpen())
{
count++;
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
while (timer < 5000)
{
if (timer < 200 && introText1.getColor() == sf::Color::Red)
{
introText1.setColor(sf::Color::White);
window.draw(introText1);
window.display();
}
if (timer < 200 && introText1.getColor() == sf::Color::White)
{
introText1.setColor(sf::Color::Red);
window.draw(introText1);
window.display();
}
break;
}
if (timer == 5000) {
window.clear();
window.draw(door);
window.draw(doorWindow1);
window.display();
}
cout << timer << endl;
if (timer > 0)
cout << "Time is moving" << endl;
}
The clock is not being reset, you simply never read the new value. ;-)
Move int timer = clock.getElapsedTime().asMilliseconds(); inside the main loop.
Additionally, you don't want the while (timer < 5000) loop especially with a break as last statement.
Related
I wanted to disable repetitive key presses on holding so I used this command
window.setKeyRepeatEnabled(false)
It does not seem to work
#include<iostream>
#include<SFML/Graphics.hpp>
int main(int argc, char const *argv[])
{
sf::RenderWindow window(sf::VideoMode(800,600),"shape change color",sf::Style::Close);
window.setKeyRepeatEnabled(false);
int a=0;
while (window.isOpen())
{
sf::Event event;
while(window.pollEvent(event))
{
if (event.key.code == sf::Keyboard::Escape)
window.close();
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::A))
a++;
std::cout << a << std::endl;
window.clear();
window.display();
}
}
It is recommended to check the following sections in the documentation such as Events and Keyboard
If a key is held, multiple KeyPressed events will be generated, at the default operating system delay (ie. the same delay that applies when you hold a letter in a text editor). To disable repeated KeyPressed events, you can call window.setKeyRepeatEnabled(false). On the flip side, it is obvious that KeyReleased events can never be repeated.
sf::Keyboard::isKeyPressed is the real-time input interface, it has nothing to do with events and it doesn't make sense to mix them.You can try:
while(window.pollEvent(event))
{
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape)
window.close();
}
and
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::A)
std::cout << a << std::endl;
Please:
read the documentation and tutorials
use the forum when you have problems
I am new to Game Development. I managed to create a simple game (like Space Invaders) as well as a simple start Menu using C++ and SFML. However, upon pressing "Enter" on the main menu, the game is not being launched. How do I link it properly? I appreciate your help. This is not homework.
main.cpp codes
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include "GameObjectManager.h"
#include "Menu.h"
using namespace std;
int main()
{
sf::Texture galaxyBackgroundTexture;
sf::Sprite galaxyBackground;
if (!galaxyBackgroundTexture.loadFromFile("Textures/galaxybackground.png")) {
cout << "Failed to load Image" << endl;
}
galaxyBackground.setTexture(galaxyBackgroundTexture);
sf::RenderWindow window(sf::VideoMode(1200, 800), "Space Invader Test");
Menu menu(window.getSize().x, window.getSize().y);
window.setFramerateLimit(144);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Key::Return)
{
menu.GetPressedItem();
cout << "Play button has been pressed." << endl;
GameObjectManager* gameObjectManagerManager = new GameObjectManager(&window);
gameObjectManager->update();
gameObjectManager->render(window);
}
else if (event.type == sf::Event::Closed)
{
window.close();
}
else if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape)
{
window.close();
}
}
window.clear();
window.draw(galaxyBackground);
menu.draw(window);
window.display();
}
return 0;
}
Menu.h
#pragma once
#include "SFML/Graphics.hpp"
#define MAX_NUMBER_OF_ITEMS 2
class Menu
{
public:
Menu(float width, float height);
~Menu();
void draw(sf::RenderWindow& window);
int GetPressedItem() { return selectedItemIndex; }
private:
int selectedItemIndex;
sf::Font font;
sf::Text menu[MAX_NUMBER_OF_ITEMS];
};
Menu.cpp
#include "Menu.h"
Menu::Menu(float width, float height)
{
if (!font.loadFromFile("arial.ttf"))
{
cout << "can't load font" << endl;
}
// initialise Menu items
menu[0].setFont(font);
menu[0].setColor(sf::Color::Red);
menu[0].setString("Play");
menu[0].setPosition(sf::Vector2f(width / 2, height / (MAX_NUMBER_OF_ITEMS + 1) * 1));
// EXIT
menu[1].setFont(font);
menu[1].setColor(sf::Color::White);
menu[1].setString("Exit");
menu[1].setPosition(sf::Vector2f(width / 2, height / (MAX_NUMBER_OF_ITEMS + 1) * 2));
}
selectedItemIndex = 0;
Menu::~Menu()
{
}
void Menu::draw(sf::RenderWindow &window)
{
for (int i = 0; i < MAX_NUMBER_OF_ITEMS; i++)
{
window.draw(menu[i]);
}
}
The console window would print:
"Play button has been pressed"
But it does not proceed to the game. Nothing else happens.
"window redefinition" error occurs because both your RenderWindow objects have the same identifiers (i.e. window). You might want to change the name of the second window or better yet use the same window.
The second error, sf::Text::setColor() is deprecated means that it is no longer "useable" or "is not suggested to be used". SFML has two new better functions for this:
sf::Text::setFillColor() : to set the fill of your text.
sf::Text::setOutlineColor() : to give your text an outline (you also need to do change the thickness using setOutlineThickness()).
Moreover, I'd suggest you to use a State Machine for different scenes instead of two separate windows. It really isn't that difficult and will help you learn a few more things. You're somewhat already achieving this with your gameObjectManager. You just need to abstract it and implement it for your menu class as well. And since you have only two scenes you can simply use an integer or boolean to switch between these two.
EDIT: an idea of what you need to do to your main.cpp file
int main()
{
sf::RenderWindow window(sf::VideoMode(1200, 800), "Space Invader Test");
GameObjectManager* gameObjectManagerManager = nullptr;
bool inGame = false; //true = game has started, false = menu screen
while (window.isOpen())//this is the main loop
{
sf::Event event;
while (window.pollEvent(event)) //this is the event loop
{
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Key::Return)
{
if(!inGame)
{
if(menu.GetPressedItem() == PlayButton) //assuming your function returns which button in the menu has been pressed
{
cout << "Play button has been pressed." << endl;
inGame = true;
gameObjectManagerManager = new GameObjectManager(&window);
}
else
{
window.close(); //since the other button is exit
}
}
}
if (event.type == sf::Event::Closed) window.close();
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) window.close();
}
//this is the place where you call your updates
if(inGame) gameObjectManagerManager->update();
window.clear();
window.draw(galaxyBackground);
if(!inGame)menu.draw(window);
if(inGame) gameObjectManagerManager->render();
window.display();
}
return 0;
}
I am trying to use sfml to build a app that is functionally running multiple windows at once. In order to do this I am trying to do what would usually be done from the while running loop inside of a function that has a for loop that goes through a vector of RenderWindows. There is another function that adds to that vector. The issue is that RenderWindows are non-copyable. Any help is appreciated.
My Vectors
vector <MakeKey::NewKey> KeyArray;
vector <sf::RenderWindow> WindowArray;
int VectorSize;
May Main
int main()
{
sf::RenderWindow window(sf::VideoMode(100, 100, 32), "Main Window", sf::Style::Default);
MakeKey MakeKey;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
//Key Presses
if (event.type == sf::Event::KeyPressed) {
if (event.key.code == sf::Keyboard::A)
MakeKey.DrawKey("A");
else if (event.key.code == sf::Keyboard::D)
MakeKey.DrawKey("D");
//Ect
}
if (event.type == sf::Event::Closed)
window.close();
}
MakeKey.StepWindows();
}
return EXIT_SUCCESS;
}
My StepWindows Function
void MakeKey::StepWindows()
{
for (int i{ 0 }; i > VectorSize; i++)
{
cout << "Inside Step Windows For Loop" << endl;
WindowArray[i].setActive(true);
WindowArray[i].clear(sf::Color::Transparent);
WindowArray[i].draw(KeyArray[i].Sprite);
WindowArray[i].display();
}
}
My DrawKey Function
void MakeKey::DrawKey(string input)
{
MakeKey::NewKey Key;
if (input == "A")
Key.Img.loadFromFile("Assets/Images/A.png");
else if (input == "D")
Key.Img.loadFromFile("Assets/Images/D.png");
VectorSize++;
//WindowArray.reserve(VectorSize);
//attempting to reference deleted function error
//WindowArray.emplace_back();
//same error
WindowArray[VectorSize].create(sf::VideoMode(Key.Img.getSize().x, Key.Img.getSize().y, 32), "Key", sf::Style::None);
Key.Tex.loadFromImage(Key.Img);
Key.Sprite.setTexture(Key.Tex);
KeyArray.emplace_back(move(Key));
cout << "KeyArray Has " << KeyArray.size() << " Elements\n" << "WindowArray Has " << WindowArray.size() << " Elements" << endl;
}
And My Key struct
typedef struct KeyStruct {
sf::Image Img;
sf::Texture Tex;
sf::Sprite Sprite;
}NewKey;
Store a pointer instead. std::unique_ptr<sf::RenderWindow> should be suitable. Initialize it with new or std::make_unique<sf::RenderWindow> (C++14).
Note that your main loop will need to be radically different as you would need to poll several windows. pollEvent is unsuitable as it blocks: if you call it on wnd1, you won’t be able to process events arriving to other windows until some event arrives to wnd1 as well.
I want to give my Sprite some extra speed when you press the spacebar for 3 seconds. After that you should wait 10 Seconds before using it again.
I tried using SFML's Time, but the clock starts right away as the program starts so it starts anyway....
Short Question: How can I delay a function without freezing the program like Sleep() does.
Code(console only for dev progess as it slows):
#include "SFML/Graphics.hpp"
#include <iostream>
#include <Windows.h>
int main()
{
using namespace std;
sf::Clock ClockSpeedFunc, Cooldown;
ClockSpeedFunc.restart();
bool Timer, CooldownTimer = false;
sf::RenderWindow window(sf::VideoMode(600, 600), "SFML WORK!");
sf::Texture texture;
if (!texture.loadFromFile("PlayerTexture.jpg"))
{
std::cout << "Error loading PlayerTexture.jpg" << std::endl;
}
sf::Sprite sprite;
sprite.setTexture(texture);
sprite.setPosition(sf::Vector2f(50, 40));
sprite.setPosition(sf::Vector2f(50, 40));
sprite.setPosition(sf::Vector2f(50, 40));
sprite.setPosition(sf::Vector2f(50, 40));
float PlayerSpeed = 0.1;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
switch (event.type)
{
case sf::Event::Closed:
window.close();
break;
}
}
//###Controls ::
//If W is pressed, move upwards
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W)) {
sprite.move(sf::Vector2f(0, -PlayerSpeed));
}
//If A is pressed, move left
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) {
sprite.move(sf::Vector2f(-PlayerSpeed, 0));
}
//If S is pressed, move downwards
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) {
sprite.move(sf::Vector2f(0, PlayerSpeed));
}
//If D is pressed, move right
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) {
sprite.move(sf::Vector2f(PlayerSpeed, 0));
}
//If Space is pressed, set Timer to true and set Speed to 0.5
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space) && CooldownTimer == false) {
cout << "Turbo started" << endl;
Timer = true;
CooldownTimer == false;
PlayerSpeed = 0.5;
}
//If Timer = true, start a clock
if (Timer = true) {
sf::Clock ClockSpeedFunc;
}
//aafter 3 Seconds set Playerspeed back to 0.1
//Set Cooldowntimer to true and restart timer clock.
sf::Time elapsed1 = ClockSpeedFunc.getElapsedTime();
sf::Time elapsed2 = Cooldown.getElapsedTime();
if (elapsed1.asSeconds() >= 3) {
PlayerSpeed = 0.1;
cout << "Turbo ended." << endl;
ClockSpeedFunc.restart();
CooldownTimer = true;
}if (CooldownTimer == true) {
cout << "Cooldown started" << endl;
sf::Clock Cooldown;
}if (elapsed2.asSeconds() >= 10) {
cout << "Cooldown ended" << endl;
Cooldown.restart();
CooldownTimer = false;
}
//Draw everything
window.clear();
window.draw(sprite);
window.display();
}
}`
First of all make sure you do not use "using namespace". That is a bad habit. And why did you have a #include there. I did not need it.
It is not part of the problem, but I highly recommend to have a look on https://gafferongames.com/post/fix_your_timestep/ and manage your delta time.
I think I would use a class for this. But here you can see how to use it with states. I added some comments in the code so you can see what I did.
#include "SFML/Graphics.hpp"
#include <iostream>
// Define the different states of the player
enum class m_State{
POWERUP, NORMAL, COOLDOWN
};
int main(){
sf::Clock m_Clock;
// Record elapsed time of power up
float m_SecondsSincePowerUp;
// Set initial state
m_State m_State = m_State::NORMAL;
sf::RenderWindow window(sf::VideoMode(600, 600), "SFML WORK!");
sf::Texture texture;
if (!texture.loadFromFile("PlayerTexture.jpg")){
std::cout << "Error loading PlayerTexture.jpg" << std::endl;
}
sf::Sprite sprite;
sprite.setTexture(texture);
sprite.setPosition(sf::Vector2f(50, 40));
sprite.setPosition(sf::Vector2f(50, 40));
sprite.setPosition(sf::Vector2f(50, 40));
sprite.setPosition(sf::Vector2f(50, 40));
float PlayerSpeed = 0.1;
while (window.isOpen()){
sf::Time elapsedTime = m_Clock.restart();
sf::Event event;
while (window.pollEvent(event)){
switch (event.type){
case sf::Event::Closed:
window.close();
break;
}
}
//###Controls ::
//If W is pressed, move upwards
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W)) {
sprite.move(sf::Vector2f(0, -PlayerSpeed));
}
//If A is pressed, move left
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) {
sprite.move(sf::Vector2f(-PlayerSpeed, 0));
}
//If S is pressed, move downwards
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) {
sprite.move(sf::Vector2f(0, PlayerSpeed));
}
//If D is pressed, move right
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) {
sprite.move(sf::Vector2f(PlayerSpeed, 0));
}
//If Space is pressed, set Timer to true and set Speed to 0.5
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space) && (m_State == m_State::NORMAL)){
std::cout << "Turbo started" << std::endl;
// Set state to PowerUp
m_State = m_State::POWERUP;
PlayerSpeed = 0.5;
}
// If state is PowerUp count the seconds in a variable
if (m_State == m_State::POWERUP){
// Add elapsed time in seconds
m_SecondsSincePowerUp += elapsedTime.asSeconds();
// After 3 seconds switch the state to Cooldown
if (m_SecondsSincePowerUp > 3.0f){
std::cout << "Turbo ended." << std::endl;
m_State = m_State::COOLDOWN;
PlayerSpeed = 0.1;
}
}
// After 10 seconds switch to normal
if (m_State == m_State::COOLDOWN){
m_SecondsSincePowerUp += elapsedTime.asSeconds();
if (m_SecondsSincePowerUp > 10.0f){
std::cout << "Cooldown ended." << std::endl;
m_State = m_State::NORMAL;
m_SecondsSincePowerUp = 0;
}
}
//Draw everything
window.clear();
window.draw(sprite);
window.display();
}
}
You can use a thread to create a sleep while normal code continues to execute in unison. They are quite complex for a full explanation but look up an example and see if you think it's worth trying to implement for use with this.
This is my code:
#include <SFML/Graphics.hpp>
#include <iostream>
#include <Windows.h>
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600), "SFML Game");
window.setFramerateLimit(200);
//Variable that keeps the game loop running
bool play = true;
//Event object holding all events
sf::Event event;
//States for button/events
bool Left = false;
bool Right = false;
bool space = false;
//Variables
int rectXPosition = 375; //Rectangles X position
int rectYPosition = 400; //Rectangles Y position
//Images
sf::Texture image1;
if (image1.loadFromFile("Images/GreekSoldier.png") == -1)
{
std::cout << "FAILED!!!" << "\n";
return 1;
}
//Render shapes
sf::RectangleShape rect;
rect.setSize(sf::Vector2f(77, 150)); //Width and height
rect.setPosition(375, 400); //Position
rect.setFillColor(sf::Color::White); //Color
rect.setTexture(&image1);
//Game loop
while (play == true)
{
//EVENTS
while (window.pollEvent(event))
{
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Left)
{
//Set the state to true
Left = true;
}
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Right)
{
Right = true;
}
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Space)
{
space = true;
}
//Event type is window closed
if (event.type == sf::Event::Closed)
{
//Set play to false in order to stop the game loop
play = false;
}
}
//LOGIC
if (Left == true)
{
int x = 0;
for (x = 1; x < 2; x++)
{
rectXPosition-=5; //X position variable of the rectangle
rect.setPosition(rectXPosition, rectYPosition);
window.clear();
window.draw(rect);
window.display();
Sleep(5);
}
Left = false;
}
if (Right == true)
{
int x = 0;
for (x = 1; x < 2; x++)
{
rectXPosition+= 5; //X position variable of the rectangle
rect.setPosition(rectXPosition, rectYPosition);
window.clear();
window.draw(rect);
window.display();
Sleep(5);
}
Right = false;
}
if (space == true)
{
int x = 0;
for (x = 1; x < 15; x++)
{
rectYPosition-= 3; //X position variable of the rectangle
rect.setPosition(rectXPosition, rectYPosition);
window.clear();
window.draw(rect);
window.display();
Sleep(10);
}
for (x = 1; x < 15; x++)
{
rectYPosition+= 3; //X position variable of the rectangle
rect.setPosition(rectXPosition, rectYPosition);
window.clear();
window.draw(rect);
window.display();
Sleep(10);
}
space = false;
}
//RENDERING
window.clear();
//Draw the rectangle shape
window.draw(rect);
window.display();
}
//Clean up and close the window
window.close();
return 0;
}
How would I make the movement more smoother? How do I make it so the rectangle(the soldier) can jump and move at the same time? And how do I stop it so, when holding down left it doesn't take 3 seconds for it to actually move?
I've added a youtube video if you guys can't be bothered to compile it and just want to see what I'm talking about.
https://www.youtube.com/watch?v=g_JLwGh1Wgo
The KeyPressed events are generated when someone presses a key and then repeat every so often (but not every frame, which makes it not smooth). They are intended to be used for text input, for example.
Pressing another key will also interrupt the repetition of the events for the previous key, so your other problem has the same cause.
You should handle the KeyPressed events only for some instant actions, like pressing a menu button. In this particular example you should get rid of this altogether and use only sf::Keyboard by replacing the line:
if (Left == true)
with:
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
and so on...
I would explain why sf::Keyboard is the right thing here, but the documentation does it very well.
Another big problem is that you have multiple loops that don't let anything else happen in the game. You should get yourself familiar with the concepts of event loops, velocity and gravity (and make corresponding variables).
When the player presses Space you shouldn't hardcode the character to move N pixels up and N pixels down while stopping every other possible action in the game, but instead change the character's vertical velocity and use it to change the character's position in every frame (and also have that velocity be reduced by gravity).
The most important point to take from this is you must have only 1 main event loop that calls window.display.