Я пытаюсь сделать игру на платформе 2D (на языке SFML) для своей работы в университете. Я не прошу никого писать код для меня, но если бы кто-нибудь мог предложить несколько указателей, я был бы чрезвычайно благодарен 🙂
В настоящее время у меня около 13 классов, в том числе:
BaseEntity (большинство игровых объектов происходит от этого)
Игрок (Наследует от BE)
Beetle (Наследует от BE — игра называется «Beetle Dodger», поэтому жуки будут перемещаться как угроза для игрока)
драгоценный камень
MagicGem (игроки нуждаются в них для продвижения по уровням)
Платформа
SolidBlock (наследуется от платформы)
DownBlock (наследуется от платформы — игрок может провалиться, но не может прыгнуть через этот блок)
UpBlock (как указано выше, но наоборот)
GameSound
Game (Менеджер игр)
Так сказать, я построил большинство «строительных блоков» игр — каждый класс имеет свою собственную функцию обновления, которая вызывается в Game :: Update. То же самое относится к функции Draw () каждого объекта. Каждый класс содержит указатель на игровое окно, поэтому он может достичь этих целей, и ему также передается ряд переменных из Game, например, какая клавиша нажата в настоящее время, а также истекшее время (для сигналов движения).
До этого все казалось хорошо и модно — тогда я встретил столкновения. Хотя я понимаю основы их работы, я попробовал два или три различных подхода к их реализации. Сначала я начал с того, что класс Player содержит несколько функций, таких как CollidesWith (Beetle).& б) и сталкивается с (платформа& Плат). Очевидно, что это чрезвычайно тяжело при тестировании каждого объекта моего уровня (включая, конечно, самоцветы), и я начал думать о том, как реализовать широкое обнаружение фазовых столкновений. Затем я попытался использовать класс AABB, определенный двумя 2DVectors (встроенный класс SFML). И вот тут я немного застрял и решил приехать и попросить помощи здесь. Я вернулся к тестированию коллизий, используя только размер спрайтов (как они определены в рамке — так же, как 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
};
Класс player унаследован от этого и имеет несколько дополнительных функций, таких как CheckBoundaries, CollidesWith, Jump, а также содержит несколько переменных — bool isColliding может представлять интерес в этом сценарии.
Ура ребята, и извините за сочинение!
В зависимости от количества сущностей, с которыми вы имеете дело, просто проверка столкновения для каждого объекта в игре может иметь огромные затраты, с точки зрения памяти и / или с точки зрения производительности.
Возможно, вы захотите предварительно обработать ваши объекты, чтобы классифицировать их по оси, например, увеличить координату x, чтобы упростить проверку столкновения. Может быть даже лучше подготовить все свои объекты и отсортировать их до того, как игра начнется, например, как начало уровня. Я думаю, что это был бы способ, которым я выбрал бы это, с первой попытки.
Как вы выяснили, решение проблемы столкновения нельзя рассматривать только на уровне одного игрового объекта. Вам нужен объект, который может отслеживать все объекты, которые участвуют в столкновении, и может читать из них все свойства, которые влияют на столкновение. Если это сделано, то он может разрешить столкновение глобально для всех объектов один раз за каждый тик обновления игры.
Я рекомендую создать интерфейс, через который вы сможете получить всю информацию, необходимую для обработки обнаружения столкновений. Все объекты, которые участвуют в столкновении, должны наследоваться от этого. Интерфейс позволит вам плавно переходить от индивидуального случая к глобальному.
Это всего лишь пример, который поможет вам понять. Вы должны адаптировать его к своему собственному коду.
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
,
И в качестве подсказки, если два объекта сталкиваются, я рекомендую немедленно отодвинуть их друг от друга, чтобы они не сталкивались в следующем игровом тике.