Указатели на функции и их вызываемые параметры - PullRequest
0 голосов
/ 03 октября 2010

Это может быть неправильный подход, но, углубляясь в разработку игрового движка, я столкнулся с проблемой синхронизации.

Итак, допустим, у меня есть ряд утверждений, таких как:

for(int i = 0; i < 400; i++)
{
     engine->get2DObject("bullet")->Move(1, 0);
}

Пуля будет двигаться, однако на экране не будет никакой анимации.В основном он будет «перекоситься» с одной части экрана на другую.

Итак, я думаю ... создать вектор указателей на функции для каждого базового объекта (от которого наследуется пользователь) и когда онивызов "Move" Я на самом деле не перемещаю объект до следующей итерации игрового цикла.Так что-то вроде:

while(game.running())
{
     game.go();
}

go()
{
     for(...)
          2dobjs.at(i)->action.pop_back();
}

или что-то в этом роде, и таким образом он выполняет только одно действие каждого объекта во время каждой итерации (конечно, я добавлю проверку, чтобы увидеть, есть ли на самом деле "действия ", которые будут выполняться).

И если это хорошая идея, мой вопрос, как мне сохранить параметры.Поскольку каждый объект может делать больше, чем один тип «действия», а не двигаться (rotateby является одним из примеров), я думаю, было бы бессмысленно создавать структуру, похожую по моде на:

struct MoveAction {
     MovePTR...;
     int para1;
     int para2;
};

Мысли?Или это совершенно неверное направление?

Ответы [ 4 ]

3 голосов
/ 03 октября 2010

Мысли? Или это совершенно неверное направление?

Это неправильное направление.

На самом деле, идея возможности ставить в очередь действия или приказы имеет некоторую ценность. Но обычно это относится к таким вещам, как ИИ и к более общим задачам (например, «перейти к локации Х», «атаковать все, что вы видите» и т. Д.). Так что, как правило, она не охватывает проблему покадрового перемещения, с которой вы здесь сталкиваетесь.

Для очень простых объектов, таких как пули, задачи очередей довольно излишни. В конце концов, что еще будет делать пуля, кроме как двигаться вперед каждый раз? Это также неловкий способ реализовать такое простое движение. Это поднимает такие вопросы, как, почему только 400 шагов вперед? Что если площадь впереди длиннее? Что делать, если оно короче? Что, если какой-то другой объект будет мешать только после, скажем, 50 шагов? (Тогда 350 ходов в очереди были пустой тратой.)

Другая проблема с этой идеей (по крайней мере, в том упрощенном виде, который вы представили) заключается в том, что ваши пули будут перемещаться на фиксированную величину за каждую итерацию игрового цикла. Но не все итерации будут занимать одинаковое количество времени. Компьютеры некоторых людей, очевидно, смогут выполнять код быстрее, чем другие. И даже игнорируя это, иногда игровой цикл может выполнять значительно больше работы, чем в другое время (например, когда в игре активно больше объектов). Таким образом, вам действительно нужен способ учета времени, которое занимает каждая итерация, чтобы отрегулировать движение и чтобы все казалось движущимся с постоянной скоростью для пользователя, независимо от того, насколько быстро проходит игровой цикл.

Таким образом, вместо того, чтобы каждое действие перемещения было «ходом на X», они были бы «двигаться на скорости X», а затем умножались бы на что-то вроде времени, прошедшего с последней итерации. И тогда вы действительно никогда не узнаете, сколько действий перемещается в очередь, потому что это будет зависеть от того, насколько быстро ваш игровой цикл выполняется в этом сценарии. Но опять же, это еще один признак того, что действия по перемещению в очереди являются неудобным решением для покадрового движения.

То, как большинство игровых движков делает это, это просто вызывает какую-то функцию на каждом объекте каждого кадра, которая оценивает его движение. Может быть, такая функция, как void ApplyMovement(float timeElapsedSinceLastFrame);. В случае пули она умножит скорость, с которой пуля должна двигаться в секунду, на время, прошедшее с последнего кадра, чтобы определить величину, по которой пуля должна двигаться в этом кадре. Для более сложных объектов вы можете захотеть сделать математику с вращениями, ускорениями, замедлениями, поиском цели и т. Д. Но общая идея та же: вызывайте функцию на каждой итерации игрового цикла, чтобы оценить, что должно произойти для этой итерации.

1 голос
/ 03 октября 2010

Я не думаю, что это правильное направление. После того, как вы сохраните 400 ходов в векторе указателей на функции, вам все равно придется выскочить и выполнить перемещение, перерисовать экран и повторить. Разве не проще просто переместить (), перерисовать и повторить?

Я бы сказал, что ваша пуля деформируется, потому что она движется на 400 пикселей / кадр, а не потому, что вам нужно отложить вычисления перемещения.

Но если это правильное решение для вашей архитектуры, в C ++ вы должны использовать классы, а не указатели на функции. Например:

class Action // an abstract interface for Actions
{
public:
    virtual void execute() = 0; // pure virtual function
}

class MoveAction: public Action
{
public:
    MoveAction(Point vel) : velocity(vel) {}
    virtual void execute();
    Point velocity;
    ...
}

std::vector<Action*> todo;

gameloop
{
    ...
    todo.push_back(new MoveAction(Point(1,0))
    ...
}
0 голосов
/ 03 октября 2010

Я думаю, что общий подход может быть другим, чтобы получить то, что вы хотите. Я лично предпочел бы установку, в которой каждый цикл go вызывал функции, чтобы установить положение для всех объектов, включая маркер, а затем рисовать сцену. Таким образом, каждая петля будет помещать пулю в нужное положение.

Если вы решите пойти своим путем, вам, скорее всего, придется реализовать свою идею структуры. Это не приятно, но пока мы не получим закрытия, вам придется делать.

0 голосов
/ 03 октября 2010

Так откуда же взялся 400? Почему бы просто не сделать это так:

go()
{
    engine->get2DObject("bullet")->Move(1, 0);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...