-
OS: Windows 7x64
Graphics Card: Radeon 7950
SFML: 2.3, static
I'm having an issue which may just stem from my lack of understanding of how this should be set up. Due to the amount of classes I have, it would be a pain to put all my code here but I'll do my best to simplify it so it's post friendly.
Edit: Created a new project with just a main.cpp to isolate the issue. Attached the images I used.
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(1024, 768), "Game");
sf::View gameView;
sf::Texture texture, texture2;
gameView.setSize(sf::Vector2f(1024, 768));
texture.loadFromFile("image.png");
texture2.loadFromFile("image2.png");
int playerX = 5;
int playerY = 5;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::Up)
{
playerY--;
}
else if (event.key.code == sf::Keyboard::Down)
{
playerY++;
}
else if (event.key.code == sf::Keyboard::Left)
{
playerX--;
}
else if (event.key.code == sf::Keyboard::Right)
{
playerX++;
}
}
}
window.clear();
window.setView(gameView);
for (int x = 0; x < 10; ++x)
{
for (int y = 0; y < 10; ++y)
{
// Note: This is not actually how I handle sprites, it's done in another class but in order to simplify it I'm just putting it here.
sf::Sprite sprite;
sprite.setTexture(texture);
sprite.setPosition((x - y) * 64, (x + y) * 32);
window.draw(sprite);
if ((x == playerX) & (y == playerY))
{
sf::Sprite player;
player.setTexture(texture2);
player.setPosition((x - y) * 64, (x + y) * 32 - 64);
window.draw(player);
gameView.setCenter(player.getPosition());
}
}
}
window.display();
}
return 0;
}
So, it's a 10x10 isometric 2D map of "texture" sprites. When it gets to the coordinates the player is on, it draws the player then sets the view to be centered on the player. I have no idea if this is the normal way to go about handling this, I'm kinda just figuring it out as I go (I prefer this method to following full tutorials as I enjoy encountering problems and going through the steps to fix them. Helps me learn better).
The problem is when I move around, the player sprite stutters a bit. Maybe every 5-10 moves, it will draw the player one step ahead of where they should be then correct it immediately. I tried to capture this on video but the screen corrects itself so quickly that the FPS wasn't high enough on the video to catch it.
Instead, I made a series of screenshots to show this
1) Start position
(http://i.imgur.com/156ASOz.png)
2) I move once, everything normal. The player moves, the view follows
(http://i.imgur.com/ULuVyvH.png)
3) I move again, and for a split second, I see this:
(http://i.imgur.com/aPTNJaT.png)
Somehow, the player sprite is drawn in the new position but the view did not get updated.
4) Immediately after 3, the view is corrected and it looks normal again
(http://i.imgur.com/9HiJv4S.png)
Also, yes, I am no artist and these images are placeholders. Don't laugh.
Any idea what might be causing this?
Thanks
Edit: I can see the images are a bit bigger than the space allows here, might be easier if they're viewed somewhere else. Here's the link to the album: http://imgur.com/156ASOz,ULuVyvH,aPTNJaT,9HiJv4S#0
-
this.gameView.setCenter(player.gerPosition());
1) I believe there is a typo.
2) The use of this-> in a class is redundant.
3) What does the function I quoted's implementation?
4) Have you tried debugging it?
5) Did you try moving what I quoted to outside of the for loops?
6) Use the spoiler BBC tag around the images.
7) this (http://en.sfml-dev.org/forums/index.php?topic=5559.msg36368#msg36368)
-
this.gameView.setCenter(player.gerPosition());
1) I believe there is a typo.
2) The use of this-> in a class is redundant.
3) What does the function I quoted's implementation?
4) Have you tried debugging it?
5) Did you try moving what I quoted to outside of the for loops?
6) Use the spoiler BBC tag around the images.
7) this (http://en.sfml-dev.org/forums/index.php?topic=5559.msg36368#msg36368)
1) Yep, it's a typo. Two typos, actually. Fixed em.
2) That was my understanding prior to reading through some tutorials where it was plastered everywhere.
3) Not sure what you're looking for here, setCenter is a standard function of sf::View. The doc is http://www.sfml-dev.org/documentation/2.0/classsf_1_1View.php#ab0296b03793e0873e6ae9e15311f3e78 (http://www.sfml-dev.org/documentation/2.0/classsf_1_1View.php#ab0296b03793e0873e6ae9e15311f3e78)
4) Guess I'm not sure how I would go about that.
5) I did. It was originally just prior to the window.display(), I was moving it to ensure it got called when it was supposed to. Did not make a difference.
6) Done
7) Yeah, got halfway there but I wanted to avoid re-writing an entirely new program to isolate this thinking it might be a more known and obvious setup issue. I will put something functional together.
Edit: I changed the original post so it's all in one runable program. The problem is very sporadic but it happens, though it seems to happen less often in this example than my actual program.
-
3) Not sure what you're looking for here, setCenter is a standard function of sf::View. The doc is http://www.sfml-dev.org/documentation/2.0/classsf_1_1View.php#ab0296b03793e0873e6ae9e15311f3e78 (http://www.sfml-dev.org/documentation/2.0/classsf_1_1View.php#ab0296b03793e0873e6ae9e15311f3e78)
I assumed that gameView was a reference to another class you implemented, because I mostly just skimmed through it.
The reason that it is having a slight "stutter", is because you are changing gameView's center, but not the RenderWindow's View's center.
Example:
sf::View testView;
renderWindow.setView( testView );
testView.setCenter( sf::Vector2f( 100, 100 ) ); //<-- does not update the renderWindow's view
The RenderWindow does not store a reference to the view, it copies it. Therefor:
sf::View testView;
testView.setCenter( sf::Vector2f( 100, 100 ) );
renderWindow.setView( testView ); //<-- sets the correct center for the renderWindow
Edit: Forgot the solution.
If you put this line:
window.setView(gameView);
To right before you update the render window, it should be solved.
-
That sounded incredibly promising and made perfect sense, but I moved my setView to be after I changed the center and it still happens.. here's the updated code
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(1024, 768), "Game");
sf::View gameView;
sf::Texture texture, texture2;
gameView.setSize(sf::Vector2f(1024, 768));
texture.loadFromFile("image.png");
texture2.loadFromFile("image2.png");
int playerX = 5;
int playerY = 5;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::Up)
{
playerY--;
}
else if (event.key.code == sf::Keyboard::Down)
{
playerY++;
}
else if (event.key.code == sf::Keyboard::Left)
{
playerX--;
}
else if (event.key.code == sf::Keyboard::Right)
{
playerX++;
}
}
}
window.clear();
sf::Sprite player;
player.setTexture(texture2);
for (int x = 0; x < 10; ++x)
{
for (int y = 0; y < 10; ++y)
{
sf::Sprite sprite;
sprite.setTexture(texture);
sprite.setPosition((x - y) * 64, (x + y) * 32);
window.draw(sprite);
if ((x == playerX) & (y == playerY))
{
player.setPosition((x - y) * 64, (x + y) * 32 - 64);
window.draw(player);
}
}
}
gameView.setCenter(player.getPosition());
window.setView(gameView);
window.display();
}
return 0;
}
-
I think you're meant to set the view before drawing to it, so put
window.draw(sprite);
and window.draw(player);
after window.setView(gameView);
Also, why are you making a new instance of player every loop and therefore setting the texture every loop?
-
I'm not following you here..
I could have two calls to
window.setView(gameView);
one before the loop and one after I run
gameView.setCenter(player.getPosition());
(I tried this for a quick test and it made no difference).
But if I have just the one call to setView prior to the loop, then it kinda contradicts what kitteh-warrior posted.
Also, why are you making a new instance of player every loop and therefore setting the texture every loop?
It's not really clean code, it was just a quick example project I made to show the problem. I can move the player outside the loop but I don't think it matters, this isn't my actual project.
Updated code for readability
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(1024, 768), "Game");
sf::View gameView;
sf::Texture tileTexture, playerTexture;
gameView.setSize(sf::Vector2f(1024, 768));
tileTexture.loadFromFile("image.png");
playerTexture.loadFromFile("image2.png");
int playerX = 5;
int playerY = 5;
sf::Sprite tile;
tile.setTexture(tileTexture);
sf::Sprite player;
player.setTexture(playerTexture);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::Up)
{
playerY--;
}
else if (event.key.code == sf::Keyboard::Down)
{
playerY++;
}
else if (event.key.code == sf::Keyboard::Left)
{
playerX--;
}
else if (event.key.code == sf::Keyboard::Right)
{
playerX++;
}
}
}
window.clear();
for (int x = 0; x < 10; ++x)
{
for (int y = 0; y < 10; ++y)
{
tile.setPosition((x - y) * 64, (x + y) * 32);
window.draw(tile);
if ((x == playerX) & (y == playerY))
{
player.setPosition((x - y) * 64, (x + y) * 32 - 64);
window.draw(player);
}
}
}
gameView.setCenter(player.getPosition());
window.setView(gameView);
window.display();
}
return 0;
}
Also uploaded a quick video of it using this code. It happened 3 times when recording this but it only captured one of them (at about the 14 second mark)
http://www.fastswf.com/BMyezm4
-
As shadowmouse said, you need to set the view of the render window, prior to drawing any drawable objects.
Example:
sf::Sprite testSpr;
sf::View testView;
testView.setCenter( sf::View( 100, 100 ) );
renderWindow.setView( testView ); //<-- must be set prior to drawing objects!
renderWindow.draw(testSpr);
-
So, I need to use setView before I use draw but after I use setCenter.
The way the view is centered, I need to call setView inside the player condition.. so I end up with this:
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(1024, 768), "Game");
sf::View gameView;
sf::Texture tileTexture, playerTexture;
gameView.setSize(sf::Vector2f(1024, 768));
gameView.setCenter(0, 0);
tileTexture.loadFromFile("image.png");
playerTexture.loadFromFile("image2.png");
int playerX = 5;
int playerY = 5;
sf::Sprite tile;
tile.setTexture(tileTexture);
sf::Sprite player;
player.setTexture(playerTexture);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::Up)
{
playerY--;
}
else if (event.key.code == sf::Keyboard::Down)
{
playerY++;
}
else if (event.key.code == sf::Keyboard::Left)
{
playerX--;
}
else if (event.key.code == sf::Keyboard::Right)
{
playerX++;
}
}
}
window.clear();
for (int x = 0; x < 10; ++x)
{
for (int y = 0; y < 10; ++y)
{
tile.setPosition((x - y) * 64, (x + y) * 32);
window.draw(tile);
if ((x == playerX) & (y == playerY))
{
player.setPosition((x - y) * 64, (x + y) * 32 - 64);
gameView.setCenter(player.getPosition());
window.setView(gameView);
window.draw(player);
}
}
}
window.display();
}
return 0;
}
Which .. appears to have fixed one problem and caused another. The player sprite no longer jumps. Instead, the column of tiles under the player will flicker, not drawing the texture so it's black for a moment. I'm assuming this is caused by the inner if statement being called, updating the center and setting the view. I think I was able to fix this by calling setCenter and setView prior to drawing the tile as well, but this seems like a really really hack-y crude way to go about doing this... doesn't seem right.
Edit: And after further testing, I see this hasn't actually fixed the new issue.
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(1024, 768), "Game");
sf::View gameView;
sf::Texture tileTexture, playerTexture;
gameView.setSize(sf::Vector2f(1024, 768));
gameView.setCenter(0, 0);
tileTexture.loadFromFile("image.png");
playerTexture.loadFromFile("image2.png");
int playerX = 5;
int playerY = 5;
sf::Sprite tile;
tile.setTexture(tileTexture);
sf::Sprite player;
player.setTexture(playerTexture);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::Up)
{
playerY--;
}
else if (event.key.code == sf::Keyboard::Down)
{
playerY++;
}
else if (event.key.code == sf::Keyboard::Left)
{
playerX--;
}
else if (event.key.code == sf::Keyboard::Right)
{
playerX++;
}
}
}
window.clear();
for (int x = 0; x < 10; ++x)
{
for (int y = 0; y < 10; ++y)
{
tile.setPosition((x - y) * 64, (x + y) * 32);
gameView.setCenter(player.getPosition());
window.setView(gameView);
window.draw(tile);
if ((x == playerX) & (y == playerY))
{
player.setPosition((x - y) * 64, (x + y) * 32 - 64);
gameView.setCenter(player.getPosition());
window.setView(gameView);
window.draw(player);
}
}
}
window.display();
}
return 0;
}
-
for (int x = 0; x < 10; ++x)
{
for (int y = 0; y < 10; ++y)
{
tile.setPosition((x - y) * 64, (x + y) * 32);
gameView.setCenter(player.getPosition());
window.setView(gameView);
window.draw(tile);
if ((x == playerX) & (y == playerY))
{
player.setPosition((x - y) * 64, (x + y) * 32 - 64);
gameView.setCenter(player.getPosition());
window.setView(gameView);
window.draw(player);
}
}
}
Why don't you just update the position of the player prior to the nested for loops, and draw the player after the nested for loops? It would avoid recalculating it 100 times per frame.
player.setPosition((playerX - playerY) * 64, (playerX + playerY) * 32 - 64);
gameView.setCenter(player.getPosition());
window.setView(gameView);
for (int x = 0; x < 10; ++x){
for (int y = 0; y < 10; ++y){
tile.setPosition((x - y) * 64, (x + y) * 32);
window.draw(tile);
}
}
window.draw(player);
Note: I did not test this code, it is meant to show the idea of a more efficient way.
Edit: Also, I never really noticed that "else if" chain in the event loop until now, I would recommend a switch statement for that.
-
The player's position is based on the x and y of the loops, so it can't be done before x and y are set.
Also, the player needs to be drawn in the correct draw order so that it will show up behind or in front of other tiles appropriately. That's the purpose of the inner if. That also ensures it only calculates the player's position once per drawScreen, not 100.
-
The player's position is based on the x and y of the loops, so it can't be done before x and y are set.
Also, the player needs to be drawn in the correct draw order so that it will show up behind or in front of other tiles appropriately. That's the purpose of the inner if. That also ensures it only calculates the player's position once per drawScreen, not 100.
But you added
gameView.setCenter(player.getPosition());
to outside of the if. I would still recommend the changing of position and view prior to the nested for loops. Therefor, the view will have the correct position.
EDIT: Use a debugger to go through your code, step by step and watch the position of the player.
The player's position (and therefor the view's position) is not being updated until the iterations reach the player's position. Which is why only some tiles are getting messed up. If you use this:
player.setPosition( ( playerX - playerY ) * 64, ( playerX + playerY ) * 32 - 64 );
gameView.setCenter( player.getPosition() );
window.setView( gameView );
for( int x = 0; x < 10; ++x ) {
for( int y = 0; y < 10; ++y ) {
tile.setPosition( ( x - y ) * 64, ( x + y ) * 32 );
window.draw( tile );
if( ( x == playerX ) && ( y == playerY ) ) {
window.draw( player );
}
}
}
The player's position (and therefor the view) is updated before drawing of any tiles.
-
As mentioned, change the view before the drawing which includes setting the center. It is not necessary to wait until drawing the tiles to calculate the player's position so why not do all of that before drawing?
window.clear();
sf::Sprite player;
player.setTexture(texture2);
const sf::Vector2f currentPlayerPosition((playerX - playerY) * 64, (playerX + playerY) * 32 - 64);
player.setPosition(currentPlayerPosition);
gameView.setCenter(currentPlayerPosition);
window.setView(gameView);
for (int x = 0; x < 10; ++x)
{
for (int y = 0; y < 10; ++y)
{
sf::Sprite sprite;
sprite.setTexture(texture);
sprite.setPosition((x - y) * 64, (x + y) * 32);
window.draw(sprite);
if ((x == playerX) && (y == playerY))
{
window.draw(player);
}
}
}
window.display();
Also, even though I didn't alter it in this code to show specifically what I'd changed, I'd highly recommend moving sf::Sprite player; and sf::Sprite sprite; to before the game loop. The setting of the texture each loop isn't really that bad but since they aren't expected to change (why would you need a different player texture each cycle and why would you need a different tile texture for each tile?), it would be better to move the two "setTexture" lines to before the main loop too (just after the sprite declarations would be ideal).
window.clear();
const sf::Vector2f currentPlayerPosition((playerX - playerY) * 64, (playerX + playerY) * 32 - 64);
player.setPosition(currentPlayerPosition);
gameView.setCenter(currentPlayerPosition);
window.setView(gameView);
for (int x = 0; x < 10; ++x)
{
for (int y = 0; y < 10; ++y)
{
sprite.setPosition((x - y) * 64, (x + y) * 32);
window.draw(sprite);
if ((x == playerX) && (y == playerY))
{
window.draw(player);
}
}
}
window.display();
Then, just put this line (or something like it) before the main loop:
sf::Sprite sprite(texture), player(texture2);
-
Huh... yeah, that makes sense. I have a tough time keeping all this stuff in my head at the same time and originally I wasn't even using playerX and Y for the view, I had a separate set of coordinates so that the view wasn't 100% locked to the player. That and still getting used to the two coordinate systems and the isometric drawing .. it was too much for me to see clearly, I guess.
Thanks for the help!
-
I added an example of what the code would look like without the excess sprite and texture setup to my previous post ;)
You could create a function that calculates the isometric position (the calculation that works out position of a tile) and re-use that for the tiles and player so avoid duplicating code, which will be great if/when you change the calculation - there wouldn't be player calculation that needed altering!
One other thing:
In your condition that tests if the player's location is the same as the current tile's location, you're using the bitwise operator & where you should be using the logical operator &&
#include <SFML/Graphics.hpp>
inline sf::Vector2f getTilePosition(const sf::Vector2i location)
{
return{ static_cast<float>((location.x - location.y) * 64), static_cast<float>((location.x + location.y) * 32) };
//return sf::Vector2f(static_cast<float>((location.x - location.y) * 64), static_cast<float>((location.x + location.y) * 32)); // for pre-C++11
}
int main()
{
sf::RenderWindow window(sf::VideoMode(1024, 768), "Game");
sf::View gameView;
gameView.setSize(sf::Vector2f(1024, 768));
sf::Texture texture, texture2;
texture.loadFromFile("image.png");
texture2.loadFromFile("image2.png");
sf::Sprite sprite(texture), player(texture2);
const sf::Vector2f playerOffsetFromTile(0, -64);
sf::Vector2i playerLocation(5, 5);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed || event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape)
window.close();
else if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::Up)
--playerLocation.y;
else if (event.key.code == sf::Keyboard::Down)
++playerLocation.y;
else if (event.key.code == sf::Keyboard::Left)
--playerLocation.x;
else if (event.key.code == sf::Keyboard::Right)
++playerLocation.x;
}
}
const sf::Vector2f currentPlayerPosition(getTilePosition(playerLocation) + playerOffsetFromTile);
player.setPosition(currentPlayerPosition);
gameView.setCenter(currentPlayerPosition);
window.setView(gameView);
window.clear(sf::Color(32, 32, 32));
for (int x = 0; x < 10; ++x)
{
for (int y = 0; y < 10; ++y)
{
const sf::Vector2i tileLocation{ x, y };
sprite.setPosition(getTilePosition(tileLocation));
window.draw(sprite);
if (tileLocation == playerLocation)
window.draw(player);
}
}
window.display();
}
return EXIT_SUCCESS;
}