Как заставить неглобальные объекты взаимодействовать внутри функции? - PullRequest
1 голос
/ 11 сентября 2010

Я пытаюсь создать клон Breakout с помощью C ++, поэтому у меня есть несколько объектов (например, ball, paddle, powerupicon, block и т. Д.). Я понимаю, что плохая практика иметь их в глобальной области видимости, поэтому они инициализируются внутри main (). Проблема возникает с необходимостью что-то делать с этими объектами из других функций (например, redraw () или reset_game ()). Единственный способ сделать это - передать ссылки объектов в каждую функцию, но я не уверен, что это лучший подход.

В принципе, как лучше всего делать вещи с этими объектами внутри функции? Передать их по ссылке? Сделать их глобальными, но в пространстве имен? Или что-то совершенно другое?

Ответы [ 6 ]

2 голосов
/ 11 сентября 2010

Простой (не обязательно гибкий или мощный) подход заключается в определении базового game_object класса, который определяет ваш интерфейс с игровыми объектами, и хранит их.Ваши объекты наследуют от него .:

class game_object
{
public:
    virtual ~game_object() {}

    virtual void update() = 0;
    virtual void draw() = 0;

    // get and set position
    virtual vector3 position() = 0;
    virtual void position(const vector3&) = 0;

    // etc...
};

class ball : public game_object
{
public:
    void update() { /* update the ball */ }
    void draw() { /* draw the ball */ }

    // etc
};

// etc

Теперь у вас есть общий способ использования игрового объекта.Затем вы можете сохранить их на карте (предпочтительно unordered_map, если у вас есть Boost, TR1 или C ++ 0x) и сделать эту карту доступной глобально:

// game_object_manager.h
typedef std::map<std::string, game_object*> game_object_manager;

extern game_object_manager manager;

Вы определяете это вединица перевода (либо main.cpp, либо game_object_manager.cpp) где-нибудь.Наконец, вы используете это, вставляя вещи по имени:

// somewhere, add the ball
manager.insert(std::make_pair("ball", new ball()));

И используйте это:

game_object* b = manager["ball"];
if (!b) /* there is no ball object */

// use b as ball */

Опять же, это простое решение и не обязательно надежное, лучшая практикаили гибкий.Но для первой игры все будет хорошо.(Чтобы улучшить его, вам нужно взглянуть на безопасность исключений, умные указатели и другие вещи (книги об играх). Я рекомендую вам сделать это до того, как вы попытаетесь сделать игры.)

1 голос
/ 11 сентября 2010

Цитирование из cppreference :

Ключевое слово static можно использовать четырьмя различными способами:

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

Есть и другие примеры.

void foo()
{
    static int counter = 0;
    cout << "foo has been called " << ++counter << " times\n";
}

int main() 
{
    for( int i = 0; i < 10; ++i )
        foo();
}
0 голосов
/ 15 сентября 2010

Вам может помочь глава Одиночки в шаблонах игрового программирования, в частности, раздел «Что мы можем сделать вместо» .

0 голосов
/ 11 сентября 2010

«Лучшее» является своего рода субъективным;Вы назвали стандартные подходы (т. е. глобальные в пространстве имен или эффективном эквиваленте, или переходите в каждую функцию).

То, что я склонен делать в плане дизайна, это группировать объекты вместе посредством логической группировки (например: некоторые вещи внутри объекта "gamestate", другие вещи внутри объекта "UI assets" и т. д.), а затем эффективные глобальные экземпляры этих объектов.Я бы порекомендовал всегда иметь эффективное пространство имен для объектов: это может быть буквально, или внутри чего-то вроде парадигмы одноэлементной или именованной фабрики экземпляров.Как правило, чем более «локальным» является объект, тем больше я склонен передавать их явно в функции, которые используют / манипулируют ими;чем больше «прикладного» уровня объектов, тем больше я стремлюсь сделать их эффективными глобалами.

Надеюсь, это поможет.

0 голосов
/ 11 сентября 2010

Я бы создал объект менеджера, который имеет всю эту информацию.Как класс Game с членом paddle, bricks, items и т. Д. Затем вы можете, например, иметь Game, иметь метод класса currentGame, который возвращает текущий экземпляр игры.Это похоже на глобальную область видимости, но в большей степени ООП.

Например:

class Game {
  public:
    Paddle *paddle;
    vector<Brick> *bricks;
    vector<Item> *items;

    static Game *currentGame();
    static void setCurrentGame(Game *newGame);

  private:
    static Game *currentGameInstance;
}

static Game *Game::currentGame() {
  return currentGameInstance;
}

static void Game::setCurrentGame(Game *newGame) {
  currentGameInstance = newGame;
}

(Я очень устал с C ++, надеюсь, синтаксис правильный)

Таким образом, вы можете сделать Game::currentGame()->paddle везде, где захотите.

0 голосов
/ 11 сентября 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...