在 sfml 中包装地图可见性问题
Wrapping map visibility issues in sfml
我正在实现一个由两部分组成的环绕贴图,只有第一个视图可见并且按预期工作。
第二个视图是看不见的,我不知道为什么。如果你向左走,就会有一个黑色区域,而不是第二个视图。
在做了很多 changes/tests 之后,第二个视图似乎仅限于第一个视图的区域。
使用不大于屏幕尺寸的平铺地图,它就像一个魅力,不像这里。非常感谢您的提前帮助。
#include <SFML/Graphics.hpp>
#include <iostream>
int main()
{
sf::RenderWindow mMainWindow(sf::VideoMode(1000,600), "Map", sf::Style::Close);
sf::View view(sf::Vector2f(500, 300), sf::Vector2f(1000, 600));
sf::View view2(sf::Vector2f(500, 300), sf::Vector2f(1000, 600));
sf::Image mapimage;
mapimage.loadFromFile("world1.jpg"); //1000*600 px
sf::Texture maptexture;
maptexture.loadFromImage(mapimage);
sf::Sprite mapsprite(maptexture);
sf::Sprite mapsprite2(maptexture);
mapsprite.setPosition(0, 0);
mapsprite2.setPosition(1000, 0);
sf::RectangleShape viewrect;
viewrect.setSize(sf::Vector2f(2000, 600));
viewrect.setPosition(0, 0);
viewrect.setFillColor(sf::Color(250,0,0,40));
sf::RectangleShape viewrect2;
viewrect2.setSize(sf::Vector2f(2000, 600));
viewrect2.setPosition(0, 0);
viewrect2.setFillColor(sf::Color(0,0,250,40));
float fview2 = 1;
view2.setViewport(sf::FloatRect(fview2, 0, 1, 1));
int mapmovementvar = 0;
while (mMainWindow.isOpen())
{
sf::Event event;
while (mMainWindow.pollEvent(event))
{
switch (event.type)
{
case sf::Event::Closed:
mMainWindow.close();
break;
}
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
system("cls");
view.move(-100, 0);
fview2=fview2+0.1f;
mapmovementvar--;
if(mapmovementvar<0)
{
mapmovementvar=19;
fview2=-0.9f;
view.reset(sf::FloatRect(1900, 0, 1000, 600));
view2.reset(sf::FloatRect(-1000, 0, 1000, 600));
}
view2.setViewport(sf::FloatRect(fview2, 0, 1, 1));
std::cout << "fview2 " << fview2 << std::endl;
std::cout << "mapmovementvar " << mapmovementvar << std::endl;
}
mMainWindow.clear();
mMainWindow.setView(view);
mMainWindow.draw(mapsprite);
mMainWindow.draw(mapsprite2);
mMainWindow.draw(viewrect);
mMainWindow.setView(view2);
mMainWindow.draw(mapsprite);
mMainWindow.draw(mapsprite2);
mMainWindow.draw(viewrect2);
mMainWindow.display();
}
return 0;
}
我没有解决您所拥有的视图的问题,但我认为在您的情况下甚至不需要使用视图。请参阅下面我认为可以实现您想要的代码。
初始化
如您所见,如果您不是绝对需要 Image 对象来处理其他内容,则可以直接从文件加载 Texture。视图在这里有点矫枉过正,我认为这不是视图的目标,就像您想要的那样。
#include <SFML/Graphics.hpp>
#include <iostream>
int main()
{
float screenWidth = 1000;
sf::RenderWindow mMainWindow(sf::VideoMode(screenWidth, 600), "Map",
sf::Style::Close);
// no need to use image, you can load texture directly from a file
sf::Texture maptexture;
maptexture.loadFromFile("world1.jpg"); //1000*600 px
// you only really need one sprite, but that's not really a problem.
sf::Sprite mapsprite(maptexture);
// you don't need views at all, just a position.
sf::Vector2f pos;
应用循环
这里没有任何变化。
while (mMainWindow.isOpen())
{
sf::Event event;
while (mMainWindow.pollEvent(event))
{
switch (event.type)
{
case sf::Event::Closed:
mMainWindow.close();
break;
default:
break;
}
}
键盘输入处理
处理输入时,只更改依赖于输入的内容。在这里,只有位置发生变化。 (还有用于缩放的 sprite 比例,但这是额外的东西)
// you may as well test with all directions because f*ck One Direction.
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
pos.x += 1.0f;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
pos.x -= 1.0f;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
{
pos.y -= 1.0f;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
pos.y += 1.0f;
}
// bonus zoom
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Equal)) // + zoom
{
mapsprite.setScale(mapsprite.getScale().x + 0.001f,
mapsprite.getScale().y + 0.001f);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Dash)) // - zoom
{
mapsprite.setScale(mapsprite.getScale().x - 0.001f,
mapsprite.getScale().y - 0.001f);
}
调整后的位置
因为我们总是使用同一张地图,所以我们只需要跟踪一个地图矩形内的位置。因此,如果我们通过该矩形,我们只需调整位置以返回地图内部,我们在渲染之前执行此操作,因此我们刚刚更改地图位置是不可见的。
// Here's all the magic happening, you loop your position around the width.
float width = mapsprite.getGlobalBounds().width;
if (pos.x <= -width)
{
pos.x += width;
}
else if (pos.x > 0)
{
pos.x -= width;
}
std::cout << "X pos : " << pos.x << std::endl;
渲染循环
我们只需要一个 Sprite 对象来实现渲染循环,因为我们使用 Sprite 引用的相同纹理。我们只是给 Sprite 一个新的位置,然后绘制它,然后重复。您仍然可以毫无问题地使用多个 Sprite 对象,因为 Sprite 是一个轻量级对象,但是在这里使用多个对象是没有用的。
mMainWindow.clear();
for (int i = 0; pos.x + (i * width) < screenWidth; ++i)
{
// display each sprite with a new position according to the last one.
mapsprite.setPosition(pos.x + (i * width), pos.y);
mMainWindow.draw(mapsprite);
}
mMainWindow.display();
}
return 0;
}
我正在实现一个由两部分组成的环绕贴图,只有第一个视图可见并且按预期工作。 第二个视图是看不见的,我不知道为什么。如果你向左走,就会有一个黑色区域,而不是第二个视图。 在做了很多 changes/tests 之后,第二个视图似乎仅限于第一个视图的区域。 使用不大于屏幕尺寸的平铺地图,它就像一个魅力,不像这里。非常感谢您的提前帮助。
#include <SFML/Graphics.hpp>
#include <iostream>
int main()
{
sf::RenderWindow mMainWindow(sf::VideoMode(1000,600), "Map", sf::Style::Close);
sf::View view(sf::Vector2f(500, 300), sf::Vector2f(1000, 600));
sf::View view2(sf::Vector2f(500, 300), sf::Vector2f(1000, 600));
sf::Image mapimage;
mapimage.loadFromFile("world1.jpg"); //1000*600 px
sf::Texture maptexture;
maptexture.loadFromImage(mapimage);
sf::Sprite mapsprite(maptexture);
sf::Sprite mapsprite2(maptexture);
mapsprite.setPosition(0, 0);
mapsprite2.setPosition(1000, 0);
sf::RectangleShape viewrect;
viewrect.setSize(sf::Vector2f(2000, 600));
viewrect.setPosition(0, 0);
viewrect.setFillColor(sf::Color(250,0,0,40));
sf::RectangleShape viewrect2;
viewrect2.setSize(sf::Vector2f(2000, 600));
viewrect2.setPosition(0, 0);
viewrect2.setFillColor(sf::Color(0,0,250,40));
float fview2 = 1;
view2.setViewport(sf::FloatRect(fview2, 0, 1, 1));
int mapmovementvar = 0;
while (mMainWindow.isOpen())
{
sf::Event event;
while (mMainWindow.pollEvent(event))
{
switch (event.type)
{
case sf::Event::Closed:
mMainWindow.close();
break;
}
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
system("cls");
view.move(-100, 0);
fview2=fview2+0.1f;
mapmovementvar--;
if(mapmovementvar<0)
{
mapmovementvar=19;
fview2=-0.9f;
view.reset(sf::FloatRect(1900, 0, 1000, 600));
view2.reset(sf::FloatRect(-1000, 0, 1000, 600));
}
view2.setViewport(sf::FloatRect(fview2, 0, 1, 1));
std::cout << "fview2 " << fview2 << std::endl;
std::cout << "mapmovementvar " << mapmovementvar << std::endl;
}
mMainWindow.clear();
mMainWindow.setView(view);
mMainWindow.draw(mapsprite);
mMainWindow.draw(mapsprite2);
mMainWindow.draw(viewrect);
mMainWindow.setView(view2);
mMainWindow.draw(mapsprite);
mMainWindow.draw(mapsprite2);
mMainWindow.draw(viewrect2);
mMainWindow.display();
}
return 0;
}
我没有解决您所拥有的视图的问题,但我认为在您的情况下甚至不需要使用视图。请参阅下面我认为可以实现您想要的代码。
初始化
如您所见,如果您不是绝对需要 Image 对象来处理其他内容,则可以直接从文件加载 Texture。视图在这里有点矫枉过正,我认为这不是视图的目标,就像您想要的那样。
#include <SFML/Graphics.hpp>
#include <iostream>
int main()
{
float screenWidth = 1000;
sf::RenderWindow mMainWindow(sf::VideoMode(screenWidth, 600), "Map",
sf::Style::Close);
// no need to use image, you can load texture directly from a file
sf::Texture maptexture;
maptexture.loadFromFile("world1.jpg"); //1000*600 px
// you only really need one sprite, but that's not really a problem.
sf::Sprite mapsprite(maptexture);
// you don't need views at all, just a position.
sf::Vector2f pos;
应用循环
这里没有任何变化。
while (mMainWindow.isOpen())
{
sf::Event event;
while (mMainWindow.pollEvent(event))
{
switch (event.type)
{
case sf::Event::Closed:
mMainWindow.close();
break;
default:
break;
}
}
键盘输入处理
处理输入时,只更改依赖于输入的内容。在这里,只有位置发生变化。 (还有用于缩放的 sprite 比例,但这是额外的东西)
// you may as well test with all directions because f*ck One Direction.
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
pos.x += 1.0f;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
pos.x -= 1.0f;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
{
pos.y -= 1.0f;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
pos.y += 1.0f;
}
// bonus zoom
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Equal)) // + zoom
{
mapsprite.setScale(mapsprite.getScale().x + 0.001f,
mapsprite.getScale().y + 0.001f);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Dash)) // - zoom
{
mapsprite.setScale(mapsprite.getScale().x - 0.001f,
mapsprite.getScale().y - 0.001f);
}
调整后的位置
因为我们总是使用同一张地图,所以我们只需要跟踪一个地图矩形内的位置。因此,如果我们通过该矩形,我们只需调整位置以返回地图内部,我们在渲染之前执行此操作,因此我们刚刚更改地图位置是不可见的。
// Here's all the magic happening, you loop your position around the width.
float width = mapsprite.getGlobalBounds().width;
if (pos.x <= -width)
{
pos.x += width;
}
else if (pos.x > 0)
{
pos.x -= width;
}
std::cout << "X pos : " << pos.x << std::endl;
渲染循环
我们只需要一个 Sprite 对象来实现渲染循环,因为我们使用 Sprite 引用的相同纹理。我们只是给 Sprite 一个新的位置,然后绘制它,然后重复。您仍然可以毫无问题地使用多个 Sprite 对象,因为 Sprite 是一个轻量级对象,但是在这里使用多个对象是没有用的。
mMainWindow.clear();
for (int i = 0; pos.x + (i * width) < screenWidth; ++i)
{
// display each sprite with a new position according to the last one.
mapsprite.setPosition(pos.x + (i * width), pos.y);
mMainWindow.draw(mapsprite);
}
mMainWindow.display();
}
return 0;
}