The original code i posted has been edited, as it seems it caused confusion. I know it was wrong but i only wanted to use it as an example. Code i want to work with is this one:
https://github.com/SFML/SFML/wiki/Source:-AnimatedSprite
You can just focus on this part:
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape)
window.close();
}
sf::Time frameTime = frameClock.restart();
// if a key was pressed set the correct animation and move correctly
sf::Vector2f movement(0.f, 0.f);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
currentAnimation = &walkingAnimationUp;
movement.y -= speed;
noKeyWasPressed = false;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
{
currentAnimation = &walkingAnimationDown;
movement.y += speed;
noKeyWasPressed = false;
}
Let´s assume that instead of
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
i want this program to act from parameters received from outside this code.
This would be the scheme i want to follow:
From my C++ code (let´s call it Origin) i call this programme (lets call it AnimatedSprite.exe) with this command:
ShellExecute(NULL,NULL,"C:\\AnimatedSprite.exe",NULL ,NULL,SW_SHOWDEFAULT);
This will open the AnimatedSprite.exe without sending any command.
Now i want to keep it open and send him commands, like
ShellExecute(NULL,NULL,"C:\\Try.exe",MyCommandLetter.c_str() ,NULL,SW_SHOWDEFAULT);
So that when my already opened AnimatedSprite.exe receives "MyCommandLetter" as argv (yes, i know i have to declare argc and argv in main, i have to modify the original code)
it does something like:
if (argv[1]=='a')
{
currentAnimation = &walkingAnimationUp;
movement.y -= speed;
noKeyWasPressed = false;
}
Hope it got more clear.
It does not matter if i have to use other function which is not ShellExecute, as long as i can understand how it works (with this example it would be OK).
Thanks in advance.
Ok, as I understand the question, you want the called program react on multiple arguments. In that case you should build your main not as single "if else" statement, but rather a loop over arguments with "switch case" statement, processing all given arguments. Look for example here
Related
My question is:
I am trying to implement basic state management in my project and i stuck at changing states.
I have all my states in std::stack<State*> container, and push/pop them directly from Application class or from State class.
Problem is when i change current state from State class, it can be destroyed before render method called, whitch results in exeption. So how do i avoid this?
PS sorry for my english and please say me if something in my problem/code isn clear
Application class:
void Application::pushState(State* state)
{
this->m_states.push(state);
this->m_states.top()->open();//enter state
}
void Application::popState()
{
if (!this->m_states.empty())
{
this->m_states.top()->close();//leave state
delete this->m_states.top();
}
if (!this->m_states.empty())
this->m_states.pop();
}
void Application::changeState(State* state)
{
if (!this->m_states.empty())
popState();
pushState(state);
}
State* Application::peekState()
{
if (this->m_states.empty()) return nullptr;
return this->m_states.top();
}
void Application::mainLoop()
{
sf::Clock clock;
while (this->m_window.isOpen())
{
sf::Time elapsed = clock.restart();
float delta = elapsed.asSeconds();
if (this->peekState() == nullptr)
this->m_window.close();
this->peekState()->update(delta)//if i change state in State.update(), it may be that code below will now point to not existing state
if (this->peekState() == nullptr)
this->m_window.close();
this->peekState()->render(delta);
}
}
State class:
void EditorState::update(const float delta)
{
sf::Event event;
while (this->m_application->m_window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
this->m_application->popState();
return;
}
}
}
Okay maybe this is not really a problem, but something like "how to" question. As you can see in my code, i update and render states in mainLoop() method. What im tying to figure out is how to manage those updates, asuming that state can be changed from state itself, not only from stateManager (in my case Application class)
Ok, so I'm guessing this is for a game (but it doesn't have to be). Instead of doing what you're doing for switching between states, I use an enum.
enum class GameState {
MENU, PLAY, PAUSE
}
Then, in your main header
GameState m_gameState = GameState::MENU;
In your main loop, you can check what the current state is by simply doing
if (m_gameState == GameState::MENU)
{
...
}
or you can use a switch statement
switch (m_gameState)
{
case GameState::MENU:
...
break;
case GameState::PLAY:
...
break;
case GameState::PAUSE:
...
break;
}
And, if you ever want to switch the state, you can just do
m_gameState = GameState::PAUSE;
Hope this answered your question :D
If not, I must have misunderstood (sorry).
I have some trouble with events in SFML. I am making a turnbased game, when the mouse has moved or left mouse button is clicked i check whos turn it is and then spawn a projectile at that objects position, when the projectile has collided with either terrain or opponent it gets destroyed and the turn is changed.
The behaviour is not what i am expecting though. When clicking shoot the projectile sometimes wont spawn at all (and it changes the turn immedeately). I have disabled all collisions so it cant be that. Im 90% sure that the issue is with how i handle events, so id really appretiate input on how i can make it better.
Of what ive learned is that you should not execute the functions in the while poll event, its only for registering what has happend most recently,so i put them outside instead. This does not solve my problem though...
sf::Event event;
sf::Vector2i mousePos;
while (_window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
_window.close();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)) {
_window.close();
}
if(event.type == sf::Event::MouseMoved) { // <- this is how you check to see if an event
mousePos = sf::Mouse::getPosition();
moved = true;
}
if(event.type == sf::Event::MouseButtonPressed) { // <- this is how you check to see if an event
mousePressed = true;
}
}
if (mousePressed && tank1Turn)
{
sf::Vector2f spawnPos = _t1->getPos() + sf::Vector2f(0, -150);
sf::Vector2f initVel = _t1->getInitVel();
cout << endl;
_p = new Projectile(spawnPos, initVel);
tank1Turn = false;
tank1Firing = true;
mousePressed = false;
}
if (mousePressed && tank2Turn) {
sf::Vector2f spawnPos = _t2->getPos()+sf::Vector2f(0,-150);
sf::Vector2f initVel = _t2->getInitVel();
_p = new Projectile(spawnPos, initVel);
tank2Turn = false;
tank2Firing = true;
mousePressed = false;
}
if (tank1Turn && moved) {
_t1->aim(mousePos);
moved = false;
mousePressed = false;
}
if (tank2Turn && moved) {
_t2->aim(mousePos);
moved = false;
mousePressed = false;
}
}
Consider this a comment. This isn't an answer (I don't have enough reputation to comment so I have to make this an answer) but here are some things I noticed:
You should either replace the ifs in your event loop with if-elses, or use a switch
sf::Keyboard::isKeyPressed is for real time input (put it inside your game update loop). In your event loop it should be sf::Event::KeyPressed and check which key it was with evt.key.code
You aren't checking which mouse button was pressed. Do it with evt.mouseButton.button (sf::Mouse::Button::Left for the left mouse)
Of what ive learned is that you should not execute the functions in the while poll event, its only for registering what has happend most recently,so i put them outside instead.
Where did you read that? I don't think that's true
There shouldn't just be an array of ifs below the event loop. Code should be structured better
I'm currently stuck on some issue and don't know how to fix it.
I started working on a simple 2D engine using SFML for rendering stuff and Lua as my scripting language.
The engine starts with a splash screen first before Lua code is loaded ...
My problem is I don't know how to write a "good" draw loop for my Lua objects.
Maybe you'll understand when we take a look on my code:
Main.cpp:
...
int draw_stuff()
{
sf::RenderWindow window(sf::VideoMode(640, 480), TITLE);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
if (!success) {
window.draw(sp_splash_screen);
}
if (clock.getElapsedTime().asSeconds() > 2) {
for (static bool first = true; first; first = false)
{
std::thread thr(&lua_module);
thr.detach();
success = true;
}
}
for (sf::CircleShape obj : CircleDrawList) {
window.draw(obj);
//window.draw(CircleDrawList.front());
}
}
return 0;
}
My CircleShape wrapper class for Lua:
/////shapes.h
extern std::list<sf::CircleShape> CircleDrawList;
class Circle
{
public:
Circle();
...
void draw();
private:
sf::CircleShape circleShape;
protected:
//~Circle();();
};
/////shapes.cpp
std::list<sf::CircleShape> CircleDrawList;
...
void Circle::draw()
{
//std::cout << "DRAW IN LIST" << std::endl;
CircleDrawList.push_front(circleShape);
//if (drawableList.size() == 4)
//drawableList.pop_front();
}
...
int luaopen_shapes(lua_State *L)
{
luabridge::getGlobalNamespace(L)
.beginClass<Circle>("Circle")
.addConstructor <void(*) (void)>()
... "other stuff to register" ...
.addFunction("draw", &Circle::draw)
.endClass();
return 1;
}
and finally (if needed) my lua script:
local ball = Circle()
ball:setColor(255,0,0,255)
while true do
ball:draw()
end
Result when the Lua Circle is moving by increasing one of the Vector2 values:
Hope you can help and I described it good :x
Thanks :)
-- Updated code in main.cpp
I'm not sure what is happening, but I see some problems in the code.
First: you don't remove "old" CircleShapes from CircleDrawList.
In the function Circle::draw() you are pushing object to the front of the list with CircleDrawList.push_front(circleShape); but you never remove anything from it. Using CircleDrawList.front() gives you access to the first element but you have to use method pop_front() to remove it.
Second thing. Look at the code:
//iterating through whole list with range-for:
for (sf::CircleShape obj : CircleDrawList)
{
//why do you use CircleDrawList.front()?
//You should probably use obj here!
window.draw(CircleDrawList.front());
}
As for now this code draws first element of the list n times where n is the length of the CircleDrawList. Do you really want to do this?
Moreover you are drawing splash every frame. If you wish to display splash only at the beginning, then the line window.draw(splash_screen); // splash screen before Lua script is loaded should probably be in some sort of conditional instruction.
I don't know what is between drawing splash_screen and drawing circles. It could have some impact on what we see on the screen.
And one more thing: it is strongly recommended to avoid using global variables. CircleDrawList is a global variable and my advice would be to put it at least in some namespace, or even better to enclose it in some class.
I have a snake game in SFML C++, and I'm stuck between two options. If set the controls like this:
if(event.type == sf::Event::KeyPressed && (event.key.code == sf::Keyboard::Up
|| event.key.code == sf::Keyboard::W) && move != Down)
move = Up;
else if(event.type == sf::Event::KeyPressed && (event.key.code == sf::Keyboard::Down
|| event.key.code == sf::Keyboard::S) && move != Up)
move = Down;
else if(event.type == sf::Event::KeyPressed && (event.key.code == sf::Keyboard::Left
|| event.key.code == sf::Keyboard::A)&& move != Right)
move = Left;
else if(event.type == sf::Event::KeyPressed && (event.key.code == sf::Keyboard::Right
|| event.key.code == sf::Keyboard::D) && move != Left)
move = Right;
Here, the controls are very responsive. The problem is that if, for instance, the snake were going rightward and you were to press Up and then Left in extremely quick succession, the snake will not have moved upward yet, but the upward control will be buffered so the snake will be allowed to move leftward into itself, thus dying.
If instead I wrap these movement conditions in an if statement that only allows updating once per frame the bug is resolved, but the controls are much less smooth and responsive.
So my question is, how do I maintain the responsiveness of allowing the movement to update quickly while avoiding the bug of moving into itself?
Being that your making Snake; The problem lies not with your input(but those "if's" should be a switch statement) but within the Object that reacts with the input, ex. the "Snake_Object". The normal game forces a set number of positional movement when a direction is pressed. If you watch the game, if you press up then the snake is immediately forced up one collision box above where it was from before. This insures that you can not do your unwanted immediate collision and still keeps regular fps flow.
So when the move variable is sent to the Snake it should in the very first instance move a whole collision box in that direction and then move at normal speed.
I have no experience in game development, and I am not even sure I understand your question.
However, I wonder if the following structure for your code would help controlling the update process:
inline void UpdateMoveUp(Move& move) { if(move != Down) { move = Up; }}
inline void UpdateMoveDown(Move& move) { if(move != Up) { move = Down; }}
inline void UpdateMoveLeft(Move& move) { if(move != Right) { move = Left; }}
inline void UpdateMoveRight(Move& move) { if(move != Left) { move = Right; }}
inline void UpdateMoveInvalid(Move& move) { move = Invalid; }
inline void HandleUpdate(const Code& code, Move& move) {
switch(code) {
case sf::Keyboard::Up:
return UpdateMoveUp(move);
case sf::Keyboard::W:
return UpdateMoveUp(move);
case sf::Keyboard::Down:
return UpdateMoveDown(move);
case sf::Keyboard::S:
return UpdateMoveDown(move);
case sf::Keyboard::Left:
return UpdateMoveLeft(move);
case sf::Keyboard::A:
return UpdateMoveLeft(move);
case sf::Keyboard::Right:
return UpdateMoveRight(move);
case sf::Keyboard::D:
return UpdateMoveRight(move);
default:
return UpdateMoveInvalid(move);
}
}
void HandleInput(const Event& event, Move& move) {
// I assume that `move` comes from somewhere else
if(event.type == sf::Event::KeyPressed) {
return HandleUpdate(event.key.code, move);
}
}
The way I look at it is that you are isolating the update function to a single place, and you can add more conditions to the update there without having to test the whole condition again.
In other words, you could add further conditions to each UpdateMoveXXXX function, such as unbuffer and update or save timestamp and move (then move only if last timestamp minus current timestamp is more/less than some number).
So I've got this program that is supposed to imitate a console (with a little coding help from this user):
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <SFML/Window.hpp>
sf::Color fontColor;
sf::Font mainFont;
sf::Clock myClock;
bool showCursor = true;
void LoadFont() {
mainFont.loadFromFile("dos.ttf");
fontColor.r = 0;
fontColor.g = 203;
fontColor.b = 0;
}
int main() {
sf::RenderWindow wnd(sf::VideoMode(1366, 768), "SFML Console");
wnd.setSize(sf::Vector2u(1366, 768));
LoadFont();
sf::Text myTxt;
myTxt.setColor(fontColor);
myTxt.setString("System Module:");
myTxt.setFont(mainFont);
myTxt.setCharacterSize(18);
myTxt.setStyle(sf::Text::Regular);
myTxt.setPosition(0, 0);
while(wnd.isOpen()) {
sf::Event myEvent;
while (wnd.pollEvent(myEvent)) {
if (myEvent.type == sf::Event::Closed) {
wnd.close();
}
if (myEvent.type == sf::Event::KeyPressed) {
if (myEvent.key.code == sf::Keyboard::Escape) {
wnd.close();
}
}
}
wnd.clear();
if (myClock.getElapsedTime() >= sf::milliseconds(500)) {
myClock.restart();
showCursor = !showCursor;
if(showCursor == true) {
myTxt.setString("System Module:_");
} else {
myTxt.setString("System Module:");
}
}
wnd.draw(myTxt);
wnd.display();
}
}
I need to be able to let the user type a key on the keyboard, and then render that key on the screen. I'm thinking about using an std::vector of sf::Keyboard::Key, and use a while loop to check what the key is (looping through the std::vector<sf::Keyboard::Key>) without using a whole bunch of if statements, but I don't exactly know how to handle that yet, so I'd like to know if there is an easier way to accomplish my main goal. Suggestions? Comments?
Thank you for your time,
~Mike
SFML has a nice feature for this, sf::Event::TextEntered (tutorial). That is typically what you want and it avoids you to do crazy things to interpret the text entered by the user.
Stock your text entered by adding every character into a sf::String (rather than std::string, it may deal better with sfml's unicode types ~ not sure, but that would need a little check) which is then the perfect type for sf::Text::setString !
Don't hesitate to look at the docs, it has further documentation in every classes' page.
Example:
sf::String userInput;
// ...
while( wnd.pollEvent(event))
{
if(event.type == sf::Event::TextEntered)
{
/* Choose one of the 2 following, and note that the insert method
may be more efficient, as it avoids creating a new string by
concatenating and then copying into userInput.
*/
// userInput += event.text.unicode;
userInput.insert(userInput.getSize(), event.text.unicode);
}
else if(event.type == sf::Event::KeyPressed)
{
if(event.key.code == sf::Keyboard::BackSpace) // delete the last character
{
userInput.erase(userInput.getSize() - 1);
}
}
}