What I've got is
sf::RenderWindow Window(sf::VideoMode(1536, 768), "", sf::Style::Close);
sf::View View(Window.getDefaultView());
sf::FloatRect fBounds(0.f, 0.f, 10.f, 1000.f); // arbitrary > view height
sf::Texture Texture;
Texture.loadFromFile("C:/.../.png");
sf::IntRect iBounds(fBounds);
Texture.setRepeated(true); // repeat tile over sprite height
sf::Sprite Sprite(Texture, iBounds);
// move sprite 'up' by its height except the view height for start:
Sprite.setPosition(fBounds.left, fBounds.top - 1000.f + View.getSize().y);
while (Window.isOpen()) {
View.move(0.f, -1.f); // negative y to move 'up' along sprite height
Window.clear();
Window.setView(View);
Window.draw(Sprite);
Window.display();
}
Although I got confused with the y-axis pointing downward and the .png upward, this now repeatedly tiles the sprite (vertically), up to sprite height only however.
Is it possible, and how, to make the .png tiling truly infinite, or at least overcome the iBounds restriction?
Thanks
You should increase the size of the fBounds width.
EDIT: for infinity, you could move the sprite upwards by the size of the sprite everytime the view moves down by that amount.
EDIT2: adapted your code to use the idea I just suggested.
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow Window(sf::VideoMode(1000, 768), "", sf::Style::Close);
sf::View View(Window.getDefaultView());
sf::FloatRect fBounds(0.f, 0.f, 1000.f, 1000.f); // arbitrary > view height
sf::Texture Texture;
Texture.loadFromFile("image.png");
sf::IntRect iBounds(fBounds);
Texture.setRepeated(true); // repeat tile over sprite height
sf::Sprite Sprite(Texture, iBounds);
// move sprite 'up' by its height except the view height for start:
Sprite.setPosition(fBounds.left, fBounds.top - 1000.f + View.getSize().y);
float viewOffsetY = 0;
float spriteOffsetY = 0;
unsigned int textureHeight = Texture.getSize().y;
while (Window.isOpen()) {
sf::Event event;
while (Window.pollEvent(event)) {
if (event.type == sf::Event::Closed)
Window.close();
}
View.setCenter(500.f, 500.f - viewOffsetY); // negative y to move 'up' along sprite height
viewOffsetY += 0.3f; // speed of view movement
spriteOffsetY = floor(viewOffsetY / textureHeight) * textureHeight;
Sprite.setPosition(fBounds.left, fBounds.top /* - 1000.f + View.getSize().y */ - spriteOffsetY);
Window.clear();
Window.setView(View);
Window.draw(Sprite);
Window.display();
}
}
Many thanks to both of you - I was hoping for some kind of ray/open end option but this will do.
A second identical sprite involves moving just as well, so as per your initial suggestion I added a move counter
float accumulatemoves(View.getSize().y); // simply for speed of 1.f
which increments inside the game loop
++accumulatemoves; // must be adjusted for varying speeds
and is checked against the View's travelled distance
if (Sprite.getTextureRect().height <= accumulatemoves) {
Sprite.move(0.f, -Sprite.getTextureRect().height+View.getSize().y);
accumulatemoves = View.getSize().y;
}
(which should be similar to what you show, though only along y.)
Many thanks for your help
A second identical sprite involves moving just as well, so as per your initial suggestion I added a move counter
float accumulatemoves(View.getSize().y); // simply for speed of 1.f
which increments inside the game loop
++accumulatemoves; // must be adjusted for varying speeds
and is checked against the View's travelled distance
if (Sprite.getTextureRect().height <= accumulatemoves) {
Sprite.move(0.f, -Sprite.getTextureRect().height+View.getSize().y);
accumulatemoves = View.getSize().y;
}
(which should be similar to what you show, though only along y.)
Many thanks for your help
I changed your usage of "move" to explicit positions. It seems that you prefer to use "move" for the view. Since I still had the code I posted before, I adjusted it to automatically adjust based on where the view is - so you can "move" it wherever and the tiled sprite will follow!
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow Window(sf::VideoMode(1000, 768), "", sf::Style::Close);
sf::View View(Window.getDefaultView());
sf::FloatRect fBounds(0.f, 0.f, 1500.f, 1000.f); // arbitrary > view "SIZE"
sf::Texture Texture;
Texture.loadFromFile("image.png");
sf::IntRect iBounds(fBounds);
Texture.setRepeated(true);
sf::Sprite Sprite(Texture, iBounds);
const sf::Vector2f viewStart(fBounds.left + (fBounds.width / 2), fBounds.top + (fBounds.height / 2));
const sf::Vector2f spriteStart(fBounds.left, fBounds.top);
while (Window.isOpen()) {
sf::Event event;
while (Window.pollEvent(event)) {
if (event.type == sf::Event::Closed)
Window.close();
}
View.move(-0.2f, -0.3f); // just move the view here in any direction-the tiles will follow automatically
const sf::Vector2f viewOffset(viewStart - View.getCenter());
sf::Vector2f spriteOffset;
spriteOffset.x = floor(viewOffset.x / Texture.getSize().x) * Texture.getSize().x;
spriteOffset.y = floor(viewOffset.y / Texture.getSize().y) * Texture.getSize().y;
Sprite.setPosition(spriteStart - spriteOffset);
Window.clear();
Window.setView(View);
Window.draw(Sprite);
Window.display();
}
}
Hope you find this useful/interesting, but even if not, I found it relaxing :D
Hi there,
sorry to retrieve this old thread, but I've a question concerning the code that was posted in the last post.
Is it possible to get it working for multiple tiles, that are aligned horizontally to get a earth-like "map" (which means in 2D terms: If you leave it to the left side you will reappear from the right side and vice versa :-) )
Well, I've been trying to implement that for some days now (Visaual c++ 10/SFML 2.1) :-), but it produces strange efffects and doesn't work as intended. The "blue part" (s. Attachment) is somehow "finite". Many thanks in advance for any advice! Here is the code (the main part was taken from the last post where some modifications have been applied).
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow Window(sf::VideoMode(1000, 768), "", sf::Style::Close);
sf::View View(Window.getDefaultView());
sf::FloatRect fBounds(0.f, 0.f, 2000.f, 500.f);
sf::FloatRect fBounds2(0.f, 0.f, 2000.f, 500.f); // arbitrary > view "SIZE"
sf::Texture Texture;
sf::Texture Texture2;
Texture.loadFromFile("mapleft.png");
Texture2.loadFromFile("mapright.png");
sf::IntRect iBounds(fBounds);
sf::IntRect iBounds2(fBounds2);
Texture.setRepeated(true);
Texture2.setRepeated(true);
sf::Sprite Sprite(Texture, iBounds);
sf::Sprite Sprite2(Texture2, iBounds2);
const sf::Vector2f viewStart(fBounds.left + (fBounds.width / 2), fBounds.top + (fBounds.height / 2));
const sf::Vector2f viewStart2(fBounds2.left + (fBounds2.width / 2), fBounds2.top + (fBounds2.height / 2));
const sf::Vector2f spriteStart(fBounds.left, fBounds.top);
const sf::Vector2f spriteStart2(fBounds2.left, fBounds2.top);
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::Right))
{
View.move(+150, 0);
}
if((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Left))
{
View.move(-150, 0);
}
if((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Up))
{
View.move(0, -150);
}
if((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Down))
{
View.move(0, +150);
}
}
const sf::Vector2f viewOffset(viewStart - View.getCenter());
sf::Vector2f spriteOffset;
sf::Vector2f spriteOffset2;
spriteOffset.x = floor(viewOffset.x / Texture.getSize().x) * Texture.getSize().x;
spriteOffset2.x = floor(viewOffset.x / Texture.getSize().x) * Texture.getSize().x;
Sprite.setPosition(spriteStart - spriteOffset);
Sprite2.setPosition(spriteStart2-spriteStart - spriteOffset2+spriteOffset);
Window.clear();
Window.setView(View);
Window.draw(Sprite);
Window.draw(Sprite2);
Window.display();
}
}
Tilemap didn't work either, because the exe crashes instantly. I suppose my example map of 16200*16200 (half the size of my real map) is too big for him to handle. Is there no way "to learn him" accepting such big image file? This here is altered tutorial code:
#include <SFML/Graphics.hpp>
class TileMap : public sf::Drawable, public sf::Transformable
{
public:
bool load(const std::string& tileset, sf::Vector2u tileSize, const int* tiles, unsigned int width, unsigned int height)
{
if (!m_tileset.loadFromFile(tileset))
return false;
m_vertices.setPrimitiveType(sf::Quads);
m_vertices.resize(width * height * 10);
for (unsigned int i = 0; i < width; ++i)
for (unsigned int j = 0; j < height; ++j)
{
int tileNumber = tiles[i + j * width];
int tu = tileNumber % (m_tileset.getSize().x / tileSize.x);
int tv = tileNumber / (m_tileset.getSize().x / tileSize.x);
sf::Vertex* quad = &m_vertices[(i + j * width) * 10];
quad[0].position = sf::Vector2f(i * tileSize.x, j * tileSize.y);
quad[1].position = sf::Vector2f((i + 1) * tileSize.x, j * tileSize.y);
quad[2].position = sf::Vector2f((i + 1) * tileSize.x, (j + 1) * tileSize.y);
quad[3].position = sf::Vector2f(i * tileSize.x, (j + 1) * tileSize.y);
quad[0].texCoords = sf::Vector2f(tu * tileSize.x, tv * tileSize.y);
quad[1].texCoords = sf::Vector2f((tu + 1) * tileSize.x, tv * tileSize.y);
quad[2].texCoords = sf::Vector2f((tu + 1) * tileSize.x, (tv + 1) * tileSize.y);
quad[3].texCoords = sf::Vector2f(tu * tileSize.x, (tv + 1) * tileSize.y);
}
return true;
}
private:
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const
{
// apply the transform
states.transform *= getTransform();
// apply the tileset texture
states.texture = &m_tileset;
// draw the vertex array
target.draw(m_vertices, states);
}
sf::VertexArray m_vertices;
sf::Texture m_tileset;
};
int main()
{
// create the window
sf::RenderWindow window(sf::VideoMode(1200, 900), "Tilemap");
// define the level with an array of tile indices
const int level[] =
{
0, 2, 3, 4, 5, 6, 7, 8 , 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
};
TileMap map;
if (!map.load("world.jpeg", sf::Vector2u(1620, 1620), level, 10, 10))
return -1;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
window.close();
}
}
window.clear();
window.draw(map);
window.display();
}
return 0;
}
After some days of trial & error, contemplating and analysing, I've found a "sufficient" solution. It's even in some way an infinitely repeating sprite (or more precise: multiple sprites/tiles) and perfect for my prospective game. Here is some exemplaric code (at the moment only covering the right bound) Forgive the "magic" numbers, but I feel better with them than with constants, with which I'm not yet too familiar (only in theory):
#include <SFML/Graphics.hpp>
#include <iostream>
using namespace std;
int main()
{
sf::RenderWindow mMainWindow(sf::VideoMode(1200, 900), "Map");
sf::Image image;
image.loadFromFile("topleft.jpeg");
sf::Texture texture;
texture.loadFromImage(image);
sf::Sprite sprite(texture);
sprite.setPosition(0, 0);
sf::Image image2;
image2.loadFromFile("bottomleft.jpeg");
sf::Texture texture2;
texture2.loadFromImage(image2);
sf::Sprite sprite2(texture2);
sprite2.setPosition(0, 300);
sf::Image image3;
image3.loadFromFile("topright.jpeg");
sf::Texture texture3;
texture3.loadFromImage(image3);
sf::Sprite sprite3(texture3);
sprite3.setPosition(600, 0);
sf::Image image4;
image4.loadFromFile("bottomright.jpeg");
sf::Texture texture4;
texture4.loadFromImage(image4);
sf::Sprite sprite4(texture4);
sprite4.setPosition(600, 300);
sf::View view(sf::Vector2f(600, 300), sf::Vector2f(1200, 900));
view.setSize(1200,900);
sf::RectangleShape rectangle;
rectangle.setSize(sf::Vector2f(200, 2000));
rectangle.setFillColor(sf::Color(100,80,100,235));
rectangle.setPosition(1190, -500);
sf::FloatRect rightBound = rectangle.getGlobalBounds();
sf::RectangleShape colliderrectangle;
colliderrectangle.setSize(sf::Vector2f(20, 20));
colliderrectangle.setFillColor(sf::Color(255,250,255,235));
colliderrectangle.setPosition(1160, 450);
sf::FloatRect collider = colliderrectangle.getGlobalBounds();
while (mMainWindow.isOpen())
{
sf::Event event;
while (mMainWindow.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
mMainWindow.close();
}
if (event.type == sf::Event::Resized)
{
sf::FloatRect visibleArea(0, 0, event.size.width, event.size.height);
mMainWindow.setView(sf::View(visibleArea));
}
if((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Right))
{
view.move(+150, 0);
colliderrectangle.move(+150,0);
}
if((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Right))
if (colliderrectangle.getGlobalBounds().intersects(rightBound))
{
cout << "they touch each another" << endl;
view.setCenter(0,colliderrectangle.getPosition().y-450);
colliderrectangle.setPosition(100,colliderrectangle.getPosition().y);
}
if((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Left))
{
view.move(-150, 0);
colliderrectangle.move(-150,0);
}
if((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Up))
{
view.move(0, -150);
colliderrectangle.move(0,-150);
}
if((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Down))
{
view.move(0, +150);
colliderrectangle.move(0,+150);
}
}
mMainWindow.setView(view);
mMainWindow.draw(sprite);
mMainWindow.draw(sprite2);
mMainWindow.draw(sprite3);
mMainWindow.draw(sprite4);
mMainWindow.draw(rectangle);
mMainWindow.draw(colliderrectangle);
mMainWindow.display();
mMainWindow.clear();
}
return 0;
}