为无尽的跑酷游戏生成和摧毁物体的最有效方式?
Most efficient way to Spawn and destroy objects for endless runner game?
我目前对 SFML 还很陌生,我想知道根据相机位置在向量中生成和销毁对象的最有效方法是什么?目前我正在实例化 wall "points" 的向量,调试后将链接在一起。我应该根据凸轮位置创建和销毁它们还是将现有的移动到正确的位置?
我也不介意为此对我的代码提出一些反馈。
洞穴Chaos.cpp
int main()
{
//get classes from CaveChaos.h
CaveChaos::Wall objWall;
CaveChaos::Player objPlayer;
//set render window size and name
sf::RenderWindow window(sf::VideoMode(800, 600), "CaveChaos");
//set the view
sf::View view1(sf::Vector2f(0.f, 0.f), sf::Vector2f(window.getSize().x, window.getSize().y));
//create the player object as a triangle
sf::CircleShape Player(20, 3);
//initialise random
srand(time(0));
//sets the player position to a good position based on camera size
objPlayer.posx = window.getSize().x / 2;
objPlayer.posy = window.getSize().y / 1.1;
Player.setPosition(objPlayer.posx, objPlayer.posy);
//used to instantiate wall points
int locationsX[numofpoints];
//add random x values to locations
for (int i = 0; i < numofpoints; i++)
{
locationsX[i] = (rand() % 50) + 1;
}
while (window.isOpen())
{
sf::Event event;
//clear the window with black color
window.clear(sf::Color::Black);
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
//calls wallspawn in Walls.cpp which creates the wall points
WallSpawn(window, locationsX );
//calls playermove in Player.cpp which move the player
PlayerMove(window, Player);
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
//set the player as the cam follow point
view1.setCenter(Player.getPosition().x, Player.getPosition().y - objPlayer.posy / 3);
//set the view to the player
window.setView(view1);
window.display();
}
return 0;
}
目前,当我调用 WallSpawn() 时,它循环遍历 10 个对象并生成它们,是否有一种方法可以生成无限数量的对象?
Walls.cpp
void WallSpawn(sf::RenderWindow& window, int locationsX[] )
{
//initiate random
srand(time(0));
//class objects
CaveChaos::Wall objWall;
CaveChaos::Player objPlayer;
//creates a vector of circleshapes
std::vector<sf::CircleShape> points;
//defines and circle of size 5
sf::CircleShape shape(5);
//loop through all of the points
for (int i = 0; i < numofpoints; i++)
{
//set the shape position to the random variable from CaveChaos.cpp
shape.setPosition(locationsX[i], objPlayer.posy + i * 55);
//set shape color to green
shape.setFillColor(sf::Color(100, 250, 50));
// copy shape to vector
points.push_back(shape);
}
// iterate through vector
for (std::vector<sf::CircleShape>::iterator it = points.begin(); it != points.end(); ++it)
{
//draw wall points on screen
window.draw(*it);
}
}
生成这些对象后移动或销毁它们的最佳方法是什么?
我希望最后一点在我前进时被摧毁或移到前面:
当我向前迈进时,会发生这样的事情:
如果此方法完全错误或我根本不应该使用此方法,请告诉我。
感谢您的帮助。
代码审查
所以首先,Whosebug 不是代码审查的正确站点。如果您寻求完整的评论,请在 codereview 上询问。但是,这里有一些我注意到的事情:
- 不要使用
rand
或 srand
。如果你想要 RNG 使用 <random>
(usually uniform_int_distribution
or uniform_real_distribution
).
- 为什么要为
locationsX
使用本机数组? std::vector
似乎更合理。
- 避免在性能关键循环内频繁分配(例如,您在每个游戏循环迭代中调用
WallSpawn
,而 WallSpawn
本身每次都会创建一个新向量 sf::CircleShape
,这是可以避免的)。稍后会详细介绍。
- 如果您对最终矢量大小有估计,请使用
vector.reserve()
为 std::vector
预分配 space。
- 为什么 window 循环中有两个事件循环而不是一个?
- 使用
auto
而不是 std::vector<sf::CircleShape>::iterator
提高了可读性。
设计
通常游戏循环是这样的:
- 轮询 window 事件并处理用户输入,
- 更新游戏状态(对输入做出反应、应用物理、角色移动……),以及
- 绘制新的游戏状态。
例如,您的 WallSpawn
函数显示了不应该这样做的方式。您正在生成新的游戏实体(墙点),然后在同一个函数中直接绘制它们。
我对 SFML 的经验有限,但我知道有一个 sf::Drawable
基础 class 可用于需要在 window 上绘制的所有内容。您不必使用它,但它是为更轻松的 OOP 开发而设计的。例如,您可以为 墙点 或整个墙创建一个类型(取决于您实际想要对这些点做什么):
class WallPoint : public sf::Drawable
{
public:
WallPoint(int x, int y) :
position(x, y), circle(x, y, 5)
{
}
/*
* Getter and setter for position
*/
void draw(RenderTarget& target, RenderStates states) const override
{
target.draw(circle);
}
private:
sf::Point position;
sf::CircleShape circle;
};
然后将这些点的集合保存在游戏循环之外的变量中。
实体管理
具有数千或数百万游戏实体(车辆、树木、岩石、墙壁、武器、弹药、抛射物、人群、NPC...)的大型游戏存在的问题是它们必须非常高效来管理他们。但是,在您使用一些简单的圆形形状的情况下,高效管理并不是真正的问题。如果不需要,请不要优化。
你提供的关于游戏行为的信息太少,无法做出深刻的推荐,所以这里是一些猜测。
游戏进度如何影响您的实体?你的案例( 一个跑步游戏)表明环境不断地沿着一个轴移动(左 <> 右,或上 <> 下)。如果这是真的,这意味着您的墙点总是在当前环境的边缘生成和消失。在这种情况下,std::deque
是存储数据的理想容器。
通常 std::vector
在容器方面是一个可靠的多面手。
总是在不再需要实体时销毁它们。例如,如果您的墙点离开视口(并且您确定它们不会再次进入),请将它们移除以用于容器。
把东西放在一起
考虑到我上面写的所有内容,您的游戏循环现在可以像这样:
int main()
{
// Create window and init everything you need
std::deque<WallPoint> wallPoints;
// create some initial points
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
// event handling
}
// update game state
// add new wall points
// remove old points
// move the player
// draw the new game state
window.Clear();
for (const auto& point : wallPoints)
window.draw(point);
window.draw(player);
}
}
我目前对 SFML 还很陌生,我想知道根据相机位置在向量中生成和销毁对象的最有效方法是什么?目前我正在实例化 wall "points" 的向量,调试后将链接在一起。我应该根据凸轮位置创建和销毁它们还是将现有的移动到正确的位置?
我也不介意为此对我的代码提出一些反馈。
洞穴Chaos.cpp
int main()
{
//get classes from CaveChaos.h
CaveChaos::Wall objWall;
CaveChaos::Player objPlayer;
//set render window size and name
sf::RenderWindow window(sf::VideoMode(800, 600), "CaveChaos");
//set the view
sf::View view1(sf::Vector2f(0.f, 0.f), sf::Vector2f(window.getSize().x, window.getSize().y));
//create the player object as a triangle
sf::CircleShape Player(20, 3);
//initialise random
srand(time(0));
//sets the player position to a good position based on camera size
objPlayer.posx = window.getSize().x / 2;
objPlayer.posy = window.getSize().y / 1.1;
Player.setPosition(objPlayer.posx, objPlayer.posy);
//used to instantiate wall points
int locationsX[numofpoints];
//add random x values to locations
for (int i = 0; i < numofpoints; i++)
{
locationsX[i] = (rand() % 50) + 1;
}
while (window.isOpen())
{
sf::Event event;
//clear the window with black color
window.clear(sf::Color::Black);
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
//calls wallspawn in Walls.cpp which creates the wall points
WallSpawn(window, locationsX );
//calls playermove in Player.cpp which move the player
PlayerMove(window, Player);
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
//set the player as the cam follow point
view1.setCenter(Player.getPosition().x, Player.getPosition().y - objPlayer.posy / 3);
//set the view to the player
window.setView(view1);
window.display();
}
return 0;
}
目前,当我调用 WallSpawn() 时,它循环遍历 10 个对象并生成它们,是否有一种方法可以生成无限数量的对象?
Walls.cpp
void WallSpawn(sf::RenderWindow& window, int locationsX[] )
{
//initiate random
srand(time(0));
//class objects
CaveChaos::Wall objWall;
CaveChaos::Player objPlayer;
//creates a vector of circleshapes
std::vector<sf::CircleShape> points;
//defines and circle of size 5
sf::CircleShape shape(5);
//loop through all of the points
for (int i = 0; i < numofpoints; i++)
{
//set the shape position to the random variable from CaveChaos.cpp
shape.setPosition(locationsX[i], objPlayer.posy + i * 55);
//set shape color to green
shape.setFillColor(sf::Color(100, 250, 50));
// copy shape to vector
points.push_back(shape);
}
// iterate through vector
for (std::vector<sf::CircleShape>::iterator it = points.begin(); it != points.end(); ++it)
{
//draw wall points on screen
window.draw(*it);
}
}
生成这些对象后移动或销毁它们的最佳方法是什么?
我希望最后一点在我前进时被摧毁或移到前面:
当我向前迈进时,会发生这样的事情:
如果此方法完全错误或我根本不应该使用此方法,请告诉我。
感谢您的帮助。
代码审查
所以首先,Whosebug 不是代码审查的正确站点。如果您寻求完整的评论,请在 codereview 上询问。但是,这里有一些我注意到的事情:
- 不要使用
rand
或srand
。如果你想要 RNG 使用<random>
(usuallyuniform_int_distribution
oruniform_real_distribution
). - 为什么要为
locationsX
使用本机数组?std::vector
似乎更合理。 - 避免在性能关键循环内频繁分配(例如,您在每个游戏循环迭代中调用
WallSpawn
,而WallSpawn
本身每次都会创建一个新向量sf::CircleShape
,这是可以避免的)。稍后会详细介绍。 - 如果您对最终矢量大小有估计,请使用
vector.reserve()
为std::vector
预分配 space。 - 为什么 window 循环中有两个事件循环而不是一个?
- 使用
auto
而不是std::vector<sf::CircleShape>::iterator
提高了可读性。
设计
通常游戏循环是这样的:
- 轮询 window 事件并处理用户输入,
- 更新游戏状态(对输入做出反应、应用物理、角色移动……),以及
- 绘制新的游戏状态。
例如,您的 WallSpawn
函数显示了不应该这样做的方式。您正在生成新的游戏实体(墙点),然后在同一个函数中直接绘制它们。
我对 SFML 的经验有限,但我知道有一个 sf::Drawable
基础 class 可用于需要在 window 上绘制的所有内容。您不必使用它,但它是为更轻松的 OOP 开发而设计的。例如,您可以为 墙点 或整个墙创建一个类型(取决于您实际想要对这些点做什么):
class WallPoint : public sf::Drawable
{
public:
WallPoint(int x, int y) :
position(x, y), circle(x, y, 5)
{
}
/*
* Getter and setter for position
*/
void draw(RenderTarget& target, RenderStates states) const override
{
target.draw(circle);
}
private:
sf::Point position;
sf::CircleShape circle;
};
然后将这些点的集合保存在游戏循环之外的变量中。
实体管理
具有数千或数百万游戏实体(车辆、树木、岩石、墙壁、武器、弹药、抛射物、人群、NPC...)的大型游戏存在的问题是它们必须非常高效来管理他们。但是,在您使用一些简单的圆形形状的情况下,高效管理并不是真正的问题。如果不需要,请不要优化。
你提供的关于游戏行为的信息太少,无法做出深刻的推荐,所以这里是一些猜测。
游戏进度如何影响您的实体?你的案例( 一个跑步游戏)表明环境不断地沿着一个轴移动(左 <> 右,或上 <> 下)。如果这是真的,这意味着您的墙点总是在当前环境的边缘生成和消失。在这种情况下,std::deque
是存储数据的理想容器。
通常 std::vector
在容器方面是一个可靠的多面手。
总是在不再需要实体时销毁它们。例如,如果您的墙点离开视口(并且您确定它们不会再次进入),请将它们移除以用于容器。
把东西放在一起
考虑到我上面写的所有内容,您的游戏循环现在可以像这样:
int main()
{
// Create window and init everything you need
std::deque<WallPoint> wallPoints;
// create some initial points
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
// event handling
}
// update game state
// add new wall points
// remove old points
// move the player
// draw the new game state
window.Clear();
for (const auto& point : wallPoints)
window.draw(point);
window.draw(player);
}
}