Простая игра цепной реакции


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

BubbleGame.ч

#ifndef __BUBBLEGAME_H__
#define __BUBBLEGAME_H__

using namespace std;
using namespace Render;


class Bubble
{
    friend class BubbleGame;    

    typedef void (*pfnBubbleUpdate)(Bubble *bubble, float elapsedTime);

private:

    FPoint m_velocity;

    FPoint m_position;

    Color m_color;

    float m_workTime;

    float m_radius;

    bool m_isAlive;

    bool m_isExploded;

    pfnBubbleUpdate m_pfnBubbleUpdate;

    ParticleEffect *m_particleTrail;

protected:

    void PreventOutbound(float elapsedTime);

public:

    static FRect Bound;

    static float BornTime;

    static float LiveTime;

    static float FadeTime;

    static float StartRadius;

    static float DiffRadius;

    static EffectsContainer Effects;


    static void FinishAllEffects();

    static bool IsIntersect(const Bubble *left, const Bubble *right);


    static void FreeBubbleUpdate(Bubble *bubble, float elapsedTime);

    static void BornBubbleUpdate(Bubble *bubble, float elapsedTime);

    static void LiveBubbleUpdate(Bubble *bubble, float elapsedTime);

    static void FadeBubbleUpdate(Bubble *bubble, float elapsedTime);


    void Update(float elapsedTime);

    void Reset();

    bool IsAlive() const { return m_isAlive; }

    bool IsExploded() const { return m_isExploded; }

    void Explode();
};


class BubbleGame
{
private:

    BubbleGame() { /* Never used */ };

    BubbleGame(const BubbleGame& bubbleGame) { /* Never used */ };

    vector<Bubble> m_bubbles;

    vector<int> m_explodedIndices;

    vector<int> m_freeIndicies;

    Texture *m_bubbleTexture;

    bool m_isActive;

    int m_pointCount;

protected:

    void Initialize();

    void CheckInteractions(int explodedBubbleIndex);

    void CheckInteractions();

    void RemoveDeadBubblesIndices();

public:

    BubbleGame(int pointCount, float bornTime, float liveTime, const FRect *bound);

    ~BubbleGame();

    void Trigger(const IPoint& position);

    void Update(float elapsedTime);

    void Draw();

};

#endif // __BUBBLEGAME_H__

BubbleGame.cpp

#include "stdafx.h"
#include "BubbleGame.h"


FRect Bubble::Bound;

float Bubble::BornTime;

float Bubble::LiveTime;

float Bubble::FadeTime;

float Bubble::StartRadius;

float Bubble::DiffRadius;

EffectsContainer Bubble::Effects;


void Bubble::FinishAllEffects()
{
    Bubble::Effects.Finish();
}

bool Bubble::IsIntersect(const Bubble *left, const Bubble *right)
{
    float xDistance = abs(left->m_position.x - right->m_position.x);
    float yDistance = abs(left->m_position.y - right->m_position.y);
    float distance = sqrt(xDistance * xDistance + yDistance * yDistance);
    float explodeDistance = left->m_radius + right->m_radius;

    if (distance <= explodeDistance)
    {
        return true;
    }
    else
    {
        return false;
    }   
}

void Bubble::FreeBubbleUpdate(Bubble *bubble, float elapsedTime)
{
    bubble->m_position.x += bubble->m_velocity.x * elapsedTime;
    bubble->m_position.y += bubble->m_velocity.y * elapsedTime;
    bubble->PreventOutbound(elapsedTime);
    bubble->m_particleTrail->posX = bubble->m_position.x;
    bubble->m_particleTrail->posY = bubble->m_position.y;
}

void Bubble::BornBubbleUpdate(Bubble *bubble, float elapsedTime)
{
    bubble->m_workTime += elapsedTime;

    if (bubble->m_workTime > Bubble::BornTime)
    {
        bubble->m_workTime = 0;
        bubble->m_pfnBubbleUpdate = Bubble::LiveBubbleUpdate;
    }
    else
    {
        bubble->m_radius = (bubble->m_workTime / Bubble::BornTime) * Bubble::DiffRadius + Bubble::StartRadius;
    }
}

void Bubble::LiveBubbleUpdate(Bubble *bubble, float elapsedTime)
{
    bubble->m_workTime += elapsedTime;

    if (bubble->m_workTime > Bubble::LiveTime)
    {
        bubble->m_workTime = 0;
        bubble->m_pfnBubbleUpdate = Bubble::FadeBubbleUpdate;
    }
}

void Bubble::FadeBubbleUpdate(Bubble *bubble, float elapsedTime)
{
    bubble->m_workTime += elapsedTime;

    if (bubble->m_workTime > Bubble::FadeTime)
    {
        bubble->m_workTime = 0;
        bubble->m_isAlive = false;
        bubble->m_pfnBubbleUpdate = NULL;
    }
    else
    {
        float progress = (1.0f - bubble->m_workTime / Bubble::BornTime); 

        bubble->m_radius = progress * (Bubble::DiffRadius + Bubble::StartRadius);
        bubble->m_color.alpha = (unsigned char)(255 * progress);
    }
}

void Bubble::PreventOutbound(float elapsedTime)
{
    bool makeStep = false;

    if (m_position.x < Bubble::Bound.xStart ||
        m_position.x > Bubble::Bound.xEnd)
    {
        m_velocity.x = -m_velocity.x;
        makeStep = true;
    }

    if (m_position.y < Bubble::Bound.yStart ||
        m_position.y > Bubble::Bound.yEnd)
    {
        m_velocity.y = -m_velocity.y;
        makeStep = true;
    }

    if (makeStep)
    {
        m_position.x += m_velocity.x * elapsedTime;
        m_position.y += m_velocity.y * elapsedTime;
    }
}

void Bubble::Update(float elapsedTime)
{
    if (m_isAlive)
    {
        m_pfnBubbleUpdate(this, elapsedTime);
    }
}

void Bubble::Reset()
{
    m_velocity.x = Random(20.0f, 100.0f);
    m_velocity.y = Random(20.0f, 100.0f);
    m_velocity = m_velocity.Rotate(Random(0.0f, 2.0f * math::PI));

    m_position.x = Random(Bubble::Bound.xStart, Bubble::Bound.xEnd);
    m_position.y = Random(Bubble::Bound.yStart, Bubble::Bound.yEnd);

    m_color.red = Random(255);
    m_color.green = Random(255);
    m_color.blue = Random(255);
    m_color.alpha = 255;

    m_workTime = 0.0f;
    m_radius = Bubble::StartRadius;
    m_isAlive = true;
    m_isExploded = false;
    m_pfnBubbleUpdate = Bubble::FreeBubbleUpdate;

    m_particleTrail = Bubble::Effects.AddEffect("Trail");
    m_particleTrail->posX = m_position.x;
    m_particleTrail->posY = m_position.y;
    m_particleTrail->Reset();
}

void Bubble::Explode()
{
    m_isExploded = true;
    m_particleTrail->Finish();
    m_pfnBubbleUpdate = Bubble::BornBubbleUpdate;
    ParticleEffect *effect = Bubble::Effects.AddEffect("Explode");
    effect->posX = m_position.x;
    effect->posY = m_position.y;
    effect->Reset();
}


void BubbleGame::Initialize()
{
    Bubble::FinishAllEffects();

    m_bubbles.resize(m_pointCount);
    m_explodedIndices.clear();
    m_freeIndicies.clear();

    for (int i = 0; i < (int)m_bubbles.size(); i++)
    {
        m_bubbles[i].Reset();
        m_freeIndicies.push_back(i);
    }   
}

void BubbleGame::CheckInteractions(int explodedBubbleIndex)
{
    vector<int>::iterator item = m_freeIndicies.begin();

    while (item != m_freeIndicies.end())
    {
        if (Bubble::IsIntersect(&m_bubbles[*item], &m_bubbles[explodedBubbleIndex]))
        {
            m_bubbles[*item].Explode();
            m_explodedIndices.push_back(*item);
            item = m_freeIndicies.erase(item);
        }
        else
        {
            item++;
        }
    }
}

void BubbleGame::CheckInteractions()
{
    vector<int>::iterator item = m_explodedIndices.begin();

    while (item != m_explodedIndices.end())
    {
        CheckInteractions(*item);
        item++;
    }
}

void BubbleGame::RemoveDeadBubblesIndices()
{
    vector<int>::iterator item = m_explodedIndices.begin();

    while (item != m_explodedIndices.end())
    {
        if (!m_bubbles[*item].IsAlive())
        {
            item = m_explodedIndices.erase(item);
        }
        else
        {
            item++;
        }
    }

    if (m_explodedIndices.size() == 0)
    {
        m_isActive = false;     
        Initialize();
    }
}

BubbleGame::BubbleGame(int pointCount, float bornTime, float liveTime, const FRect *bound)
{
    Bubble::Bound = *bound;
    Bubble::BornTime = bornTime;
    Bubble::FadeTime = bornTime;
    Bubble::LiveTime = liveTime;
    Bubble::StartRadius = 4.0f;
    Bubble::DiffRadius = 20.0f;

    m_pointCount = pointCount;
    m_isActive = false;
    m_bubbles = vector<Bubble>(pointCount);
    m_bubbleTexture = Core::resourceManager.getTexture("Circle");   

    m_explodedIndices.reserve(pointCount + 1);
    m_freeIndicies.reserve(pointCount + 1);

    Initialize();
}

BubbleGame::~BubbleGame()
{
    delete m_bubbleTexture; m_bubbleTexture = NULL;
}

void BubbleGame::Trigger(const IPoint &position)
{
    if (!m_isActive)
    {
        m_isActive = true;

        Bubble explodedBubble;
        explodedBubble.Reset();
        explodedBubble.m_position.x = (float)position.x + Bubble::DiffRadius;
        explodedBubble.m_position.y = (float)position.y - Bubble::DiffRadius;
        explodedBubble.Explode();       

        m_bubbles.push_back(explodedBubble);
        m_explodedIndices.push_back((int)(m_bubbles.size() - 1));
    }
}

void BubbleGame::Update(float elapsedTime)
{
    for (int i = 0; i < (int)m_bubbles.size(); i++)
    {
        m_bubbles[i].Update(elapsedTime);
    }

    if (m_isActive)
    {
        CheckInteractions();
        RemoveDeadBubblesIndices();
    }
}

void BubbleGame::Draw()
{
    Bubble::Effects.Draw();

    m_bubbleTexture->Bind();

    for (int i = 0; i < (int)m_bubbles.size(); i++)
    {
        if (m_bubbles[i].IsAlive())
        {
            Render::device.SetCurrentColor(m_bubbles[i].m_color);

            Render::DrawRect(
                (int)(m_bubbles[i].m_position.x - m_bubbles[i].m_radius), 
                (int)(m_bubbles[i].m_position.y - m_bubbles[i].m_radius), 
                (int)(m_bubbles[i].m_radius * 2.0f),
                (int)(m_bubbles[i].m_radius * 2.0f));
        }
    }   
}


5747
7
задан 15 августа 2011 в 08:08 Источник Поделиться
Комментарии
9 ответов

"Плохой стиль" может означать много вещей, но самое страшное, это довольно субъективно, что хорошо и плохой стиль. ИМХО, если они не дали вам какую-то литературу, что они считают "хорошим стилем" своей политикой, это было довольно несправедливо дисквалифицировать вас с такой причиной. Кроме того, они действительно имеют в виду стиль кода или дизайна код? Есть небольшое пятно между двумя местами.

Вот некоторые вещи, которые вы сделали, но они могут не понравиться:


  1. Даже если у вас есть только два занятия, вы уже начинаете играть вокруг с 'друг' атрибут. "друг" - это красивая поделка в большом проекте, где модернизация не-го, но в проекте такого масштаба люди будут удивляться, почему вы не просто придумать дизайн, который не требует "друг". Ведь, если у вас есть только два класса и один имеет доступ к закрытым членам другого, зачем их вообще?

  2. Если вы действительно хотите идти по книге ООП, вы должны
    не переменным-членам доступ из других классов напрямую, но использовать
    вместо методы getter и setter. Я часто нахожу это ненужным и
    непрактично, но некоторые просто обожаю.

  3. Вы используете статические переменные. Хотя они в основном безобидные с встроенного типа (например. поплавки), как в вашем случае, но чаще всего, в сложных проектах и пользовательские типы вы собираетесь работать в трудн-к-отладка проблем, вызванных статическим порядок инициализации. Я всегда дизайн, чтобы избежать статических переменных, он просто экономит много головной боли в будущем.

  4. У вас есть более одного класса, определенного в один и тот же заголовок. Опять же, вы можете сделать это, но это не считается "чистым".

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

  6. Некоторые также предпочитают списки инициализации в конструкторах, так как на большие массивы простых объектов (как мыльные пузыри?) они действительно могут дать вам лучшую производительность.

  7. Что говорили другие условия:

    1. Предваряя varibales-членов, если это не каждому по душе.
      Некоторые делают это просто с подчеркиванием, некоторые члены Марк переменных
      корпус по-разному, а некоторые вообще не отмечать их вовсе.

    2. с помощью пространства имен в заголовке-это плохая идея, она приведет к конфликтам имен и будет испортить функцию автозавершения ИД.

    3. Избегайте имен, зарезервированных стандартных/компилятора.

    4. Если вы хотите предотвратить копирование objetc, вам нужно скрыть/переписать как конструктор копирования и оператор присваивания. Только один сможет оставить вас склонны к ошибкам.


7
ответ дан 15 августа 2011 в 01:08 Источник Поделиться

Идентификатор __ BUBBLEGAME_H __ reerved является внедрение во всех контекстах. Точные правила комплекса смотрите здесь. Но стенографии никогда не использовать двойное подчеркивание никогда не начинать идентификатор с символа подчеркивания (если вы не знаете точных правил и помню их)

Никогда не кладите using в файл заголовка:

using namespace std;
using namespace Render;

Я предпочитаю не ставить их в исходный файл. Но абсолютно никогда не кладите их в заголовочном файле. Любой, кто использует ваш заголовочный файл теперь перевозят в другое пространство имен и они могут не осознавать этого, не читая код.

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

Не знаю, почему у вас есть или нужны многие открытые статические члены.

Почему вы проходя здесь указатели?

bool Bubble::IsIntersect(const Bubble *left, const Bubble *right)

Пройти по константной ссылке. Вы используете указатели везде. Указатель является большой нет-нет, как они могут быть null (таким образом, вы должны проверить, что они имеют значение null).

Использовать стандартные алгоритмы, чтобы помочь redability:

while (item != m_explodedIndices.end())
{
CheckInteractions(*item);
item++;
}

Может быть записан как:

 std::for_each(m_explodedIndices.begin(), m_explodedIndices.end(), std::bind(CheckInteractions));

Ручное управление ресурсами-это определенные легко определить, что это не реальный код C++ (более Локе C с классами). Научиться правильно использовать РАИИ.

delete m_bubbleTexture; m_bubbleTexture = NULL;

7
ответ дан 15 августа 2011 в 02:08 Источник Поделиться

Я предварить свой отзыв, хочу сказать: сортировать стиля кода-это очень субъективно.

Что сказал:


  • У вас есть директива using пространство имен в заголовочном файле. Никогда не делайте этого.

  • У вас есть с помощью пространства имен std; директива в файле header. Никогда не делай этого.

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

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

  • Можно использовать полностью, если блок вернуть истинные или ложные, просто вернуть результат выражения: возвращение (х <= г)

  • Ваш общеукрепляющее стиль может не соответствовать стандартам компании. Всегда проверяйте, чтобы увидеть, если ваша компания имеет документ стиль кода, который необходимо соблюдать.

4
ответ дан 15 августа 2011 в 01:08 Источник Поделиться

Хорошо. Прежде всего посмотрите на свой класс пузырь. Она включает в себя логику (...обновление методов) и презентации (цвета, вы жестко кружок рисования) и метаданных (статические переменные). Смешивать эти вещи-это плохо, потому что когда вас попросят изменить пузырь Алмаз, вам придется менять весь код, когда вам потребуется добавить даже небольшой-маленький-крошечный меняться, вам придется меняться нагрузок кода. Вам нужно разделить пузырь на несколько классов, потому что это нарушает единый принцип ответственности. Также взгляните на модель-представление-контроллер

Никаких абстракций, только реализация. Код вряд ли растяжимое.

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

слово const ключевое слово не используется вообще. Нет методов const, то никакие постоянные объекты.

Статические переменные. используя в заголовке файла.

Хм, основные нарушения, описанных. Так что проверить "чистый код: справочник Agile мастерство программного обеспечения"

3
ответ дан 15 августа 2011 в 01:08 Источник Поделиться

Два подчеркивания зарезервированы для компиляторов, так это плохой заголовок охранника: #ifndef __BUBBLEGAME_ч__

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

Очень много статических переменных, но я не могу видеть, что вы действительно нужны все эти переменные как static. Вы можете создать еще один класс (BubbleConfig, или подобное что-то) для конфигурации пузырь и передать его в пузырь.

1
ответ дан 15 августа 2011 в 12:08 Источник Поделиться

Для меня проблема с ваш код вы используете

typedef void (*pfnBubbleUpdate)(Bubble *bubble, float elapsedTime);

и связанные с ними функции:

static void XXXXBubbleUpdate(Bubble *bubble, float elapsedTime);

Вы должны сделать их функций-членов, например:

void Bubble::FreeBubbleUpdate(float elapsedTime)
{
m_position.x += m_velocity.x * elapsedTime;
m_position.y += m_velocity.y * elapsedTime;
PreventOutbound(elapsedTime);
m_particleTrail->posX = m_position.x;
m_particleTrail->posY = m_position.y;
}

Вместо явного задания СПП в ваш код, вы должны принять решение, какие функции для вызова только в функция Update (): я бы сделал это с помощью государства-члена вар, что делает отладку намного легче:

enum BubbleState {
BORN,
...
NUM_STATES
} m_state;

Теперь в Update() я использовать переключатель для выбора соответствующей функции-члена Update:

switch (m_state) {
case BORN: BornBubbleUpdate(elapsedTime);
...

или, если это привело к Проблемы с производительностью, я бы сделал массив указателей на функции-члены (см. http://www.goingware.com/tips/member-pointers.html) и использования государственного значения в качестве индекса:

typedef void (Bubble::*UpdateFunc)(float elapsedTime);
UpdateFunc m_UpdateFuncArray[NUM_STATES];
...
(*(m_UpdateFuncArray[m_state]))(elapsedTime);

Последний пример-это почти как ручного кодирования во время выполнения полиморфизм -- очень похожая история происходит за кулисами, когда вы использовать RTTI.

В двух словах: нет ничего плохого в достижении полиморфизм через указатели на функции, но вы действительно должны делать их члены указатели на функции.

1
ответ дан 22 августа 2011 в 11:08 Источник Поделиться

Несколько мелких замечаний :

В IsIntersect :


  • Вы не должны использовать ABS для xDistance и yDistance, как они будут в квадрате, прежде чем так или иначе используется.

  • Все переменные могут быть определены как const

  • В конце концов, вы можете "вернуться (расстояние <= explodeDistance);"

В FadeBubbleUpdate :


  • прогресс может быть const

И более обобщенно :


  • Я люблю эту игру!

1
ответ дан 1 января 2012 в 06:01 Источник Поделиться

Вы запрещаете копирование при закрытии конструктора копирования в отдельный раздел, но то, что о


константный BubbleGame& оператор=(константный BubbleGame&) ?

Он по-прежнему позволяет скопировать объект BubbleGame. Почему это плохо? - У вас есть указатель на текстуру, который будет просто скопирован с того же адреса.

То же самое для класса пузыря.

0
ответ дан 15 августа 2011 в 12:08 Источник Поделиться

Всю сделку с m_pfnBubbleUpdate действительно имеет место в ООП. Почему вы хотите заменить метод класса, таким образом, во время выполнения? Вы должны либо сделать абстрактный класс пузырь и выводят различные типы пузырь из него, или использовать одну функцию BubbleUpdate делать разные вещи в зависимости от состояния пузыря.

0
ответ дан 15 августа 2011 в 01:08 Источник Поделиться