0

我正在尝试为我的大学工作制作基于 2D 平台的游戏(在 SFML 中)。我不是要求任何人为我编写一些代码,但如果有人能提供一些指示,我将非常感激:)

目前我有大约 13 节课,包括:

BaseEntity(大多数游戏对象都由此派生)

玩家(继承自BE)

甲虫(继承自 BE - 游戏被称为“甲虫道奇”,因此会有移动的甲虫对玩家构成威胁)

宝石

MagicGem(玩家需要这些才能通过关卡)

平台

SolidBlock(从平台继承)

DownBlock(从 Platform 继承 - 玩家可以跌倒但不能跳过此块)

UpBlock(如上,反之亦然)

游戏之声

游戏(游戏管理员)

可以这么说,我已经构建了大部分游戏“积木”——每个类都有自己的更新函数,在 Game::Update 中调用。这同样适用于每个对象的 Draw() 函数。每个类都有一个指向游戏窗口的指针,因此它可以实现这些事情,并且它们还从 Game 传递了许多变量,例如当前按下的键以及经过的时间(用于移动计算)。

到这里为止一切看起来都很好而且很漂亮——然后我遇到了碰撞。虽然我了解它们的工作原理,但我尝试了两种或三种不同的方法来实现它们。起初我只是让 Player 类拥有一堆函数,例如 CollidesWith( Beetle& b ) 和 CollidesWith( Platform& plat )。显然,在对我关卡中的每个对象(当然包括宝石)进行测试时,这非常费力,我开始考虑如何实现广泛的相位碰撞检测。然后我尝试使用由 2 个 2DVectors(SFML 的内置类)定义的 AABB 类。这就是我有点卡住并决定来这里寻求帮助的地方。我回到纯粹使用精灵的大小来测试碰撞(因为它们是由一个盒子定义的 - 与 AABB 的一样,对吧?)但我

在我严重搞砸游戏的良好基础之前,任何人都可以就实现宽相位和窄相位碰撞检测的好方法提供一些友好的建议吗?我在一个阶段的工作非常好,然后我意识到玩家仍然可以通过平台的一侧移动,呵呵。

我应该为碰撞创建一个专用类吗?或者我应该继续使用每个对象的精灵的大小(每个对象都有自己的精灵和图像 - 实际上我将展示一个示例):

class BaseEntity
{
public:
// Constructors
BaseEntity();
BaseEntity(sf::RenderWindow* gameWin, string imgPath, sf::Vector2f position = sf::Vector2f(0,0), sf::Vector2f velocity = sf::Vector2f(0,0));
virtual ~BaseEntity();
// Setters
void SetCurrentPos(sf::Vector2f newPos); // Set the current position 
void SetPreviousPos(sf::Vector2f newPrevPos); // Shouldn't be needed but there may be rare circumstances
void SetCurrentVel(sf::Vector2f newVel); // Set the velocity

// Getters
sf::Vector2f GetCurrentPos(); // Returns the current position values
sf::Vector2f GetPreviousPos(); // Returns the previous position values
sf::Vector2f GetCurrentVel(); // Returns the current velocity values 

void virtual SetSprite(string imgPath); // Set up the images for the sprite

void virtual Update(float elapsedTime); // The function that handles the updating of movement
void virtual Draw(); // The function that handles the 'Draw' aspect of this object

protected:


sf::RenderWindow* p_GameWin; // A pointer to the game window (used for drawing)
sf::Vector2f currentPos;
sf::Vector2f previousPos;
sf::Vector2f currentVel;
sf::Sprite mySprite; // This objects sprite
sf::Image myImage; // This objects image
};

播放器类继承自此并具有一些额外的功能,例如 CheckBoundaries、CollidesWith、Jump,并且还包含一些变量 - bool isColliding 可能在这种情况下感兴趣。

干杯伙计们,对这篇文章感到抱歉!

4

2 回答 2

1

正如您发现的那样,解决碰撞不能仅在单个游戏对象的级别上考虑。您需要一个对象,它可以跟踪所有参与碰撞的对象,并可以从它们中读取所有影响碰撞的属性。如果这样做了,那么它可以在每次游戏更新滴答时为所有对象全局解决碰撞。

我建议创建一个界面,通过它您可以获得处理碰撞检测所需的所有信息。所有参与碰撞的对象都需要从这里继承。该界面将允许您从每个对象案例和全局案例平滑过渡。

这只是一个帮助你理解的例子。您需要将其调整为您自己的代码。

class ICollidable
{
public:
    // we use these function to retrieve collision relevant information
    // (can be optimised)
    virtual sf::Vector2f GetPosition() = 0; // objects have position
    virtual sf::Vector2f GetSize() = 0; // objects have a size

    // using this function, we notify the object that it collided with something else
    virtual void ProcessCollision(ICollidable* other) = 0;

    // if you use virtual methods, you need a virtual destructor
    virtual ~ICollidable{};
};

现在您可以创建一个碰撞系统。碰撞系统将保存ICollidable可以相互交互的对象列表。您甚至可以选择一些对象根本不参与碰撞。这将负责在全球层面解决冲突。

class CollisionSystem
{
private:
    // hold all objects that participate in collision
    std::vector<ICollidable*> objectList; 
public:
    void AddToCollisionList(ICollidable* obj);
    void RemoveFromCollisionList(ICollidable* obj);
    void ProcessCollisionList();
}

包含碰撞检查算法的CollisionSystem::ProcessCollisionList();实现。它将获取每个对象的位置和大小。基于该信息,它将决定两个对象发生碰撞并调用ICollidable::ProcessCollision(ICollidable& other);每个发生碰撞的对象。此方法将被子类覆盖以提供特定于类的功能。

在内部CollisionSystem,您可以使用四叉树或二叉树等数据结构来加快解决所有冲突所需的时间。作为第一步,我建议只在 X 轴上进行排序。通过保持列表排序,您只需检查不超过对象大小的邻居。

如果您的游戏需求发生变化,您可以使用更好的碰撞检查算法进行更新,您可以将更多属性添加到ICollidable. 一般来说,你还需要处理物理,你也可以通过ICollidable.

作为提示,如果两个物体发生碰撞,我建议立即将它们移开,以免它们在下一个游戏时间点发生碰撞。

于 2016-09-26T17:03:47.777 回答
0

根据您正在处理的实体数量,仅检查游戏中每个对象的碰撞可能会在内存和/或性能方面产生巨大的成本。

您可能希望对对象进行预处理以按轴对它们进行分类,例如增加 x 坐标,以简化碰撞检查。甚至在游戏开始之前准备好所有对象并对其进行分类可能会更好,例如作为一个级别的启动。我认为这将是我第一次尝试的方式。

于 2013-01-09T02:24:33.540 回答