SFML community forums
Help => Graphics => Topic started by: irri on November 29, 2008, 05:55:13 pm
-
Hello!
I'm making a RPG-game for a project in school. My overworld is a tilebased one as you can see in the image. Which way is the best to SMOOTHLY SCROLL the world when I move the player? When am I supposed to load the new images and sprites and so on?
(http://www.irri.se/upload/irri/img/Sk%C3%A4rmbild-Namnl%C3%B6st%20f_929.png)
Thanks
Philip Irri
-
You should use an additional sf::View (http://www.sfml-dev.org/tutorials/1.3/graphics-views.php) to scroll the screen, like so:
float OffsetX = 0.f;
float OffsetY = 0.f;
if (Input.IsKeyDown(sf::Key::Up)) OffsetY = -128.f;
if (Input.IsKeyDown(sf::Key::Left)) OffsetX = -128.f;
if (Input.IsKeyDown(sf::Key::Down)) OffsetY += 128.f;
if (Input.IsKeyDown(sf::Key::Right)) OffsetX += 128.f;
View.Move(OffsetX, OffsetY); // Move the player
App.SetView(View); // Draw relative to the world
App.Draw(Map); // Draw the map
App.SetView(App.GetDefaultView()); // Draw relative to the GUI
App.Draw(Player); // Draw the player
Unfortunately, it's not that easy. This code will make the player run at different speeds depending on how fast the computer can cycle through the code.
float Offset = App.GetFrameTime() * 128.f;
float OffsetX = 0.f;
float OffsetY = 0.f;
if (Input.IsKeyDown(sf::Key::Up)) OffsetY = -Offset;
if (Input.IsKeyDown(sf::Key::Left)) OffsetX = -Offset;
if (Input.IsKeyDown(sf::Key::Down)) OffsetY += Offset;
if (Input.IsKeyDown(sf::Key::Right)) OffsetX += Offset;
There we go. If we multiply how fast our program is cycling through the code in nanoseconds with our movement, it works at the same speed on every computer.
There is one last problem you may want to fix, though. SFML is trying to position our sprites in between pixels because of our multiplication by nanoseconds. This will create some odd distortion on our sprites. We will need to prevent SFML from doing this by rounding the player's position to the nearest whole number right before SFML goes to draw the world. We can't permanently round the position because that would cause the position to be unpredictable and also bring back the problem that we just fixed above. So, we need to set the position back as soon as SFML is done with it.
const sf::Vector2f& Center = View.GetCenter(); // Store the player's original position
View.SetCenter(floorf(Center.x + 0.5f), floorf(Center.y + 0.5f)); // Round the player's position
App.SetView(View);
App.Draw(World);
App.Draw(Enemy);
View.SetCenter(Center); // Go back to the original position
Edit:
As for your sprites, create them on first use and destroy them when they're no longer being used.
-
Thank you very much!
I didn't realise that sf::View was capable of doing such a thing. I thought it only was for zooming and such. ^^
I have implemented it now.
Hmm, next thing to do is to load the map in via some file and make it draw only those sprites that shall be visible for the player.
Thanks
-
Thank you very much!
I didn't realise that sf::View was capable of doing such a thing. I thought it only was for zooming and such. ^^
I have implemented it now.
Hmm, next thing to do is to load the map in via some file and make it draw only those sprites that shall be visible for the player.
Thanks
use tinyxml (search on google for the lib) and the xml file format for loading things, you won't regret it.
for only drawing sprites visible to the player, simply check which sprites collide with your "view-rectangle" and which do not. you can even take this a step further and divide your world into virtual zones, of which everyone handles pointers to the sprites belonging to them. that way, you dont have to do a collision check for every sprite.
-
No, not xml. Try Google's protocol buffer. http://code.google.com/p/protobuf/
-
No, not xml. Try Google's protocol buffer. http://code.google.com/p/protobuf/
Thanks for the link, this could be very useful. I'm using XML at the moment.
-
Can someone explain to me what exactly you would use XML or protocol buffers for? I don't really understand the problem it's trying to solve.
-
Take a look at the following XML file:
world.xml
<map tileset="world.png">
<row>
<tile>0</tile>
<tile>0</tile>
</row>
<row>
<tile>0</tile>
<tile>1</tile>
</row>
</map>
The TinyXML library helps me easily take this XML file and turn it into a fully usable instance inside my application.
-
Hmm...
I'll think I will use TinyXML.
Let's say that I want to have event in my XML-file for overworlds and cities etc. Events that are going to be scripted using angelscript.
<map tileset="world.png">
<row>
<tile>0</tile>
<tile event="onPress: Dialog("Hello");">0</tile>
</row>
<row>
<tile>0</tile>
<tile>1</tile>
</row>
</map>
Shall I first read in the event as a String "onPress: Dialog("Hello");" and then give the string to angelscript? Or do you guys have a more simple or smarter way to do it?
-
That is a perfectly acceptable way to do it, but I would format it differently in the file to make it more readable:
<map tileset="world.png">
<row>
<tile>0</tile>
<tile>0</tile>
</row>
<row>
<tile>0</tile>
<tile>1</tile>
</row>
<script code="onPress: Dialog("Hello");" row="1" tile="2">
</map>
Also making sure to allow scripts to be in separate files:
<script file="hello.script">
-
Small piece of information: if you are looking for an XML library which can read (not write!) XML files way faster than TinyXML have a look at RapidXML.
-
Take a look at the following XML file:
world.xml
<map tileset="world.png">
<row>
<tile>0</tile>
<tile>0</tile>
</row>
<row>
<tile>0</tile>
<tile>1</tile>
</row>
</map>
The TinyXML library helps me easily take this XML file and turn it into a fully usable instance inside my application.
Hah, I never thought of using XML like that. That's quite nice.
-
Which is the best way to store the tiles?
I have a class which contains the sf::Sprite and some other variables.
Because when I move my player, lets say to the right, I want the most left column of Tiles to be erased and all the other columns shall be shifted one step left. And the the column farest to right shall be read in by my XML-map..
In what shall I store my Tiles?
Thanks
-
I recommend you store all sprites and tiles in vectors, only reading the XML file once when loading. Then, when it comes time to draw them, draw only the tiles that you need.
This could be optimized a bit more by only storing a sprite per frame in your tile set. Then, when it comes time to draw, you have to continually set the position of the correct sprite to where you want the tile to be.
-
What I do:
First I create an image in which 1 pixel represents 1 tile and in which each color in the image represents a tile texture (green pixel = grass texture, gray pixel = road texture, etc.). Such a landscape image can even be easily edited in every graphics program.
In addition I do have images with the ground textures itself of course. Those I load at program start and even create one sprite for each texture image. I use an enum to define the different texture types (TEXTURE_GRASS, TEXTURE_ROAD, etc.). For simplicity you might use a std::map<TextureTypeEnum, sf::Sprite> for that.
Then I load the landscape image and store it one in a simple 2D matrix, a custom class which has functions like loadFromLandscapeImage(const std::string& FileName) and getTextureType(int x, int y). Internally it is a simple 1D std::vector (index in vector = landscape image width * y + x; but care for boundaries!). Of course the vector already includes the texture type, based on the color code in the landscape image.
Each frame I calculate which tiles are visible (based on scrolling and zooming and window size and stuff) and afterwards calculate the width and height all the texture sprites must have (in pixel size). In case my current sprites don't have that width and height yet (and only in that case) I loop over all sprites of the textures and set their sprite scale according to the required width and height.
Then I loop over the visible tiles, call getTextureType(x, y), access the according sprite based on the texture type and finally render that sprite on the appropriate position on the screen.
After the landscape is drawn and I now loop over the visible creatures and other stuff to draw them.