Este documento descreve as convenções adotadas para o desenvolvimento do jogo Seven Keys.
You also can read in english at Stylesheet.
Todo arquivo .cpp deve ter um .hpp correspondente, com mesmo nome. Ambos devem ter o nome da classe a qual eles implementam.
Nome da Classe:
class Guard : public Object
Nome dos Arquivos:
guard.cpp
guard.hpp
-Primeiro o header do arquivo fonte
-depois as bibliotecas da engine
-depois as bibliotecas especificas do jogo
-depois as bibliotecas padroes do c/c++
#include "boss.hpp"
#include "core/level.hpp"
#include "core/environment.hpp"
#include "core/keyboardevent.hpp"
#include "core/animation.hpp"
#include <iostream>
Namespaces should be used whenever possible Namespaces should always be named
using namespace std;
Declare as variaveis o mais localmente possivel As variaveis devem ser declaradas o mais proximo do seu uso Todas as variaveis devem ser inicializadas.
for(int id = 1; id < quantidade_salas; id++)
{
Room * aux = room_list.at(rand() % id);
CreateRoom(aux, &id, aux->pos_x, aux->pos_y, quantidade_salas, stage_id);
aux ++;
}
Variaveis estaticas devem ser usadas somente quando necessario devido a seus riscos de variacoes e mau uso ao longo do codigo, quando nao utilizada como constante (const).
static Font_Manager *instance;
A Nomenclatura deve seguir o padrão "CamelCase" para Classes e Enums. Nomes de classes devem ser compostos por substantivos.
class Guard : public Object
Para atributos o padrão adotado é o "lowerCamelCase".
static ActionID removeGuardID;
Para metodos, funções, variaves e namespaces o padrão usado deve ser o "snake_case". Metodos e Funções devem conter pelo menos um verbo e um substantivo.
void update_vision();
Para constantes e macros deverá ser usado o "SCREAMING_SNAKE_CASE", exceto em parametros que deve ser adotado o estilo "snake_case".
#define SPEED 150.5
Em parametros:
void Player::get_weapon(string weapon_id)
Em structs será usado "snake_case", o mesmo deverá ser acompanhado de um typedef.
typedef struct _Area
{
double left, right;
double top, bottom;
} Area;
Para typedef o padrão a ser seguido é o padrão CamelCase em tipos compostos como uma struct e enum.
typedef struct _Area
{
...
} Area;
Para tipos simples como int e double deve ser usado o padrão snake_case.
typedef unsigned int natural_numbers;
Na declaração de ponteiros deve-se usar o asterisco junto ao nome da variavel, e deve seguir o modelo "snake_case" assim como o exemplo.
Environment *env = Environment::get_instance();
Classes devem possuir metodos e atributos condizentes com o que seu nome especifica.
As declarações devem vir na ordem do mais aberto (public) para o mais fechado (private) no contexto de encapsulamento. public antes de protected, protected antes de private. As primeiras declarações devem ser de Construtores e Destrutores, logo depois os atributos, seguidos dos metodos.
class Font_Manager
{
public:
static Font_Manager *Instance();
static void init() throw (Exception);
void load_font(string path, unsigned int font_size) throw (Exception);
void close_font();
SDL_Texture* message()const;
void make_message(SDL_Renderer *renderer, string message, Color color) throw (Exception);
protected:
Font_Manager();
~Font_Manager();
private:
static Font_Manager *instance;
SDL_Texture *m_message;
TTF_Font *m_font;
};
Construtores devem sempre ser definidos em cada classe.
.hpp
Animation(const string& image, double x, double y, double w, double h,
int frames, unsigned long speed_in_ms, bool loop = false);
~Animation();
.cpp
Animation::Animation(const string& texture, double x, double y, double w,
double h, int frames, unsigned long speed_in_ms, bool loop)
: m_impl(new Animation::Impl(texture, x, y, w, h, frames, speed_in_ms, loop)) {
}
Apenas destrutores virtuais sao permitidos, quando necessarios.
Os metodos devem possuir as funcionalidades características de sua nomenclatura. Eles devem ser separados de outras declarações da classe por linhas em branco antes e depois, formando paragrafos.
...
void show_health()
{
Environment * env = Environment::get_instance();
Rect healthbar {(double)env->canvas->w()/15, (double)env->canvas->h()/24, m_player->health() * 2, 12};
env->canvas->fill(healthbar, Color::RED);
Rect borda {(double)env->canvas->w()/15, (double)env->canvas->h()/24, 100*2, 12};
env->canvas->draw(borda, Color::RED);
}
...
O tipo de retorno deve ser declarado sempre antes do nome da função na mesma linha.
...
void show_health()
{
...
}
...
Para identação sera usado o padrão de tabulação de 4 espaços.
Não fazer muito coisa na mesma linha. Para isso não acontecer, você deve evitar ultrapassar o limite de 80 caracteres por linha. Para quebras de linha, em casos de expressões muito grandes, deve-se usar os seguintes principios:
- Quebrar após virgula;
Impl(Button *button, const string& idle_image_id,
const string& active_image_id)
: m_button(button), m_text(nullptr), m_idle(Color::GREEN),
m_active(Color::BLUE), m_border(Color::BLACK), m_thickness(1),
m_state(IDLE)
- Quebrar antes de operadores;
Item* pill = new Item(m_player, "icon_pill", path, (double)env->canvas->w()
* 1/35 + 2, (double)env->canvas->h() * 25/30 + 2, 9999, true);
m_player->add_child(pill);
- Alinhar a nova linha com o mesmo nivel do inicio da expressão da linha anterior
Para operadores deve-se dar um espaço antes e um depois.
m_player->set_stamina(m_player->stamina() + 0.05);
Deve-se usar chaves sempre que possível, mesmo em situações em que a linguagem permita que não seja usada. As chaves devem ser abertas uma linha abaixo do termino da expressão ou declaração e deve ser fechada uma linha abaixo do bloco de instruções:
if(x + m_player->w() > env->canvas->w())
{
x = env->canvas->w() - m_player->w();
}
Sempre usar 'else' em estruturas de controle do tipo 'if'.Em estruturas do tipo 'switch', os 'cases' que representam o fluxo principal devem vir antes dos cases que representam fluxo alternativo ou mensagens de erro. O padrão a ser seguido é de sem espaços entre a estrutura de controle e o parentese da condição, e entre as operações das condições. É preferível usar operações utilizando os comandos 'and', 'or', 'not', pertencentes à linguagem c++ ao invés de comandos menos intuitivos como '&&', '||', '!='.
Codigo ruim:
if(id != Button::clickedID)
{
return false;
}
codigo bom:
if(id not Button::clickedID)
{
return false;
}
else
{
//nothing to do.
}
O padrão a ser seguido é sem espaços entre a estrutura de repetição e o parentese, e nas condições de repetição espaços apos os , e entre os sinais de relação e operação espaço antes e depois.
for(int id = 1; id < quantidade_salas; id++)
{
Room * aux = room_list.at(rand() % id);
CreateRoom(aux, &id, aux->pos_x, aux->pos_y, quantidade_salas, stage_id);
aux ++;
}
As variaveis e atributos devem ser usadas no padrão snake_case, as variaveis devem ser por padrão declaradas com o tipo seguido por um espaco o nome da variavel e a mesma deve ser inicializada. {espaço}{espaço}={espaço}<inicialização>;
double w = env->canvas->w();
As funções e metodos devem seguir o seguinte formato: ({espaço}{virgula}{espaço}...);
Animation(const string& image, double x, double y, double w, double h,
int frames, unsigned long speed_in_ms, bool loop = false);
Structs devem possuir apenas dados primarios e nao podem ser implementadas quaisquer tipo de funcionalidades ou afins.
typedef struct _ItemInfo {
string name;
string type;
int variations;
int weight;
bool walkable;
bool unique;
double mass;
int x, y;
} ItemInfo;
A declaração de enum deve ser usada com um typedef seguida por seus parametros entre chaves. {espaço}{espaço}<{PARAMETRO1, PARAMETRO2}>{espaço};
typedef enum { NONE, BLEND } BlendMode;
Para comentários de uma única linha devem ser utilizadas barras duplas
// Room Criation
Para comentários de duas ou mais linhas devem ser utilizadas a barra com o asterisco
/*
string *str;
str = (string*)(&id);
*str = "stage1";
*/
A documentação no estilo "JavaDoc" deve ser usada para comentarios de classe ou metodos. É aconselhavel apenas o uso de @param, @return, @throws e em alguns casos @deprecated. Para qualquer comentario ao estilo JavaDoc de classe e metodos deve se usar comentarios de @param, @return, @throws e @deprecated se necessario. O padrão adotado foi o seguinte:
/**
* [Weapon::Weapon Receives the object parent (weapon), your identifier and
* all your attributes(resistance, damage and attack speed)]
* @param id [identifier of object]
* @param resistance [Resistance of the Weapon, may change depending the weapon]
* @param damage [Damage of the Weapon, may change depending the weapon]
* @param attack_speed [Attack speed of the Weapon, may change depending the weapon]
*/
Assertivas devem ser usadas sempre que houver um comportamento inesperado, condições impossiveis e dados corrompidos que não podem ser tratados. Seus parametros devem conter no minimo uma condição e uma mensagem de erro ou com o estado e/ou valor esperado.
assert(percent >= 0 && "Must be >= 0")
Exceções devem ser tratadas no contexto mais adequado usando catch, se o contexto não for adequado a exceção deve ser propagada usando throw.
Código ruim:
...
try
{
srand(time(NULL));
SevenKeys keys;
keys.init("7 Keys", 1280, 720, 1, true);
keys.run();
} catch (Exception ex)
{
cerr << ex.message() << endl;
return -1;
}
...
try
{
audio_manager = new AudioManagerWrapper();
if (not audio_manager)
{
throw AudioManagerSfxAllocationException("Out of memory for a new AudioManagerSfx");
}
audio_manager->init();
} catch (AudioManagerMusicAllocationException audio_manager_music_allocation_exception)
{
// Do something significant
...
}
...
A exceção "AudioManagerSfxAllocationException" não tem um catch para trata-la e é propagada para quem chamou o metodo.
Deve-se evitar o cast forçado como o seguinte:
m_player->x() + (moviment.first * delta)/1000.0;
A maneira correta de se fazer é usando o cast da propria linguagem:
m_player->x() + (moviment.first * delta) / (double)1000;