.:: Menu Rápido ::.

Linux | C/C++ | Downloads | SDL | [×]

sábado, 22 de dezembro de 2007

SDL_Collide - Colisão 2D para jogos

A primeira regra de qualquer jogo é a colisão. É claro que isso só inclui 99,99% dos jogos.
Como será o sistema de colisão em seu jogo é um fator determinante pra conclusão do projeto.
Como sempre tenho a SDL sempre a mão e para resolver este problema vou apresentar a SDL_Collide.

» Post Completo...

return (pixelcolor == surface->format->colorkey);


int SDL_CollidePixel(SDL_Surface *as, int ax, int ay, SDL_Surface *bs, int bx, int by, int skip = 4);
Esta função faz um teste de colisão em pixel precisão. É muito lenta.
Por exemplo: SDL_CollidePixel("verde_claro",0,0,"verde_escuro",4,2,2);
Como é mostrado acima, o exemplo faria teste de colisão nos pixels de intercessão entre as imagens. O parâmetro skip (2) indica quantos pixels saltar (1 faz teste completo).
O exemplo retornou verdadeiro. Branco é nossa cor de transparência e a colisão foi detectada apenas uma vez.

int SDL_CollideBoundingBox(SDL_Surface *sa, int ax, int ay, SDL_Surface *sb, int bx, int by);
e
int SDL_CollideBoundingBox(SDL_Rect a, SDL_Rect b);

São responsáveis pela colisão entre retângulos e são muito rápidas.

int SDL_CollideBoundingBox(SDL_Rect a, SDL_Rect b){
if(b.x + b.w < a.x) //- b.direita < a.esquerda
return 0;
if(b.x > a.x + a.w) //- b.esquerda > a.direita
return 0;
if(b.y + b.h < a.y) //- b.baixo < a.cima
return 0;
if(b.y > a.y + a.h) //- b.cima > a.baixo
return 0;

return 1; //- retângulos se colidem
}

O primeiro teste "if(b.x + b.w < a.x)" é representado na figura pelas barras vermelhas.
Repare que a colisão só existe quando todos os testes falham. O algoritmo procura por algum lado que possa eliminar a colisão totalmente.

int SDL_CollideBoundingCircle(int x1, int y1, int r1, int x2, int y2, int r2, int offset);
Faz a comparação de colisão entre circulos. x e y são coordenadas do centro. use offset como 0 (deve ser alguma medida de precisão).

int SDL_CollideBoundingCircle(int x1, int y1, int r1, int x2, int y2, int r2, int offset){
int xdiff = x2 - x1; //- diferença no eixo x
int ydiff = y2 - y1; //- diferença no eixo y

/* Distância entre os centros dos círculos ao quadrado */
int dcentre_sq = (ydiff*ydiff) + (xdiff*xdiff);

/* Calcula a soma dos raios ao quadrado */
int r_sum_sq = r1 + r2;
r_sum_sq *= r_sum_sq; //- melhor que (r1+r2)*(r1+r2)

return (dcentre_sq - r_sum_sq <= (offset*offset));
}

Existe colisão quando a soma dos raios for maior que a distância.
Esta função está bem otimizada, tanto é que, para calcular a distância não foi utilizada a raiz quadrada.
A distância não é realmente necessária, ela serve apenas como comparativo. Como o cálculo da raiz quadrada não é uma operação trivial, é preferível comparar todas as somas (raios e dx dy) em potência de 2.

int SDL_CollideBoundingCircle(SDL_Surface *a, int x1, int y1, SDL_Surface *b, int x2, int y2, int offset);
É uma variação da função anterior onde o raio do circulo é calculado por aproximação (r1 = (a.w+a.h) / 4).

Download: SDL_Collide (o código fonte já vem com a versão )

*** O exemplo ***
Aproveitando o código anterior, criei um jogo de labirinto com itens (quase um Pac-Man) para que ficasse bem claro como pode ser feita a implementação de um jogo com colisões.
Para que ficasse funcional acabei complicando o código. Por isso vou explicar algumas das funções que realmente importam.
Não é sempre que se pode mover em todas as direções que pede o jogador, e é isso que a função mov() irá tratar:

bool Play::mov(int movX,int movY,Mapa *mapa){
if( !mov2(movX,movY,mapa) ){ //- teste de movimento em x e y
if( !mov2(movX,0,mapa) ){ //- teste de movimento em x
if( !mov2(0,movY,mapa) ) //- teste de movimento em y
return 0;
}
}
pegaItem(mapa);
return 1;
}

A função mov2() é a função que realmente move o nosso smiley. Ela cria um retângulo auxiliar com a nova posição e testa colisão com todos os retângulos vizinhos:

bool Play::mov2(int movX,int movY,Mapa *mapa){
SDL_Rect aux = {box.x+movX,box.y+movY,box.w,box.h}, ret;
int x,y,i,j,colisao;
char t; //- tile

x = aux.x/W; //- coordenada x no mapa (aproximada)
y = aux.y/H; //- coordenada y no mapa (aproximada)

//- loop nos tiles vizinhos
for(j=-1;j<2;j++){ //- -1,0,1
for(i=-1;i<2;i++){ //- -1,0,1
try{
t = mapa->tile(x+i,y+j);
}catch(...){ //- fora da grade
continue;
}
mapa->getRect(x+i,y+j,ret);
colisao = SDL_CollideBoundingBox(aux,ret);
if(colisao && (t==1 || t>3) ){ //- colisao com paredes
//printf("colisao> %d,%d x %d,%d\n",aux.x,aux.y,ret.x,ret.y);
return 0;
}
}
}
box.x += movX;
box.y += movY;
return 1;
}

Como eu disse, é possível otimizar as funções da SDL_Collide à sua maneira, e foi o que eu fiz:

int SDL_CollideBoundingBox(SDL_Rect a, SDL_Rect b){
if(b.x + b.w <= a.x) return 0;
if(b.x >= a.x + a.w) return 0;

if(b.y + b.h <= a.y) return 0;
if(b.y >= a.y + a.h) return 0;

return 1;
}


Fazer colisão usando o retângulo da própria imagem pode não ser uma boa. Meu personagem tem 32x32 e seu retêngulo de colisão apenas 20x20.
A movimentação não ficou muito boa. A explicação para isso é que não tratamos as colisões da melhor forma, pois sempre que há uma colisão não se faz absolutamente nada para corrigir.
Então é só ! Analise o código fonte para entender os detalhes.
Download: » código fonte

*** SinucaOS ***
Não tem como a gente falar de colisão de círculos sem lembrar de um jogo de sinuca.
Acredite, fazer um jogo de sinuca é realmente difícil. A física de um jogo de sinuca exige muito mais do que se pode ver. A tacada inicial, por exemplo, é uma explosão de colisões e isto fragiliza a implementação.
SinicaOS é um grupo que está desenvolvendo um jogo de sinuca open source em C e Allegro. Por enquanto, o foco principal é ter um simulador de física funcional.
A imagem acima(animação gif) mostra duas bolas grudadas no terceiro quadro. Isto mostra que não são poucos os problemas enfrentados.
Pode ser um bug do jogo, mas o grande causador deste problema é a quantidade de bolas, a proximidade entre elas e a alta precisão exigida. A maioria dos bons jogos de sinuca sobrem deste mesmo mal.

*** Link's ***
Download: » código fonte
» SDL - Site oficial
» SDL_Collide - Site do projeto

---
Feliz Natal !!!

quarta-feira, 12 de dezembro de 2007

1.000 Visitantes \o/

Agradeço a força de todos vocês ! Pois hoje a marca de 1.000 visitantes foi atingida !!!
Foram apenas 14 motivos para visitarem meu blog e são mais de 1.000 motivos para que eu continue com meu trabalho feito em horas vagas !!!

É com grande satisfação que apresento:

Mais uma vez... parabéns ao visitante milenar... só não tem prêmio !!!

Tocando música e sons com SDL_Mixer

SDL é uma biblioteca básica, mas possui muitas bibliotecas de apoio. Uma delas é a SDL_Mixer.
De acordo com alguns tutoriais que andei vendo por aí, a forma mais fácil de tocar música e efeitos sonoros em seu jogo feito em SDL é com SDL_Mixer.
Além de suportar mais formatos (wav, mp3, ogg, mid(Timidity instalado), ...) você não tem que se preocupar com a função callback que é utilizada na SDL.
Siga este passo a passo de como deixar o seu jogo mais divertido !!!

» Post Completo...

#include <SDL/SDL_mixer.h>
Mix_Music *musica = NULL; //- música de fundo
Mix_Chunk *bomba = NULL; //- efeito sonoro mixável.
Mix_Chunk *tiro = NULL;

Você deve inicializar e finalizar a SDL_Mixer assim como faz com a SDL:

void init(){
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
atexit(SDL_Quit);
screen = SDL_SetVideoMode( W, H, 32, SDL_SWSURFACE);
Mix_OpenAudio( 22050, MIX_DEFAULT_FORMAT, 2, 1024 ); //- inicializa SDL_Mixer
atexit(Mix_CloseAudio);
}

Carrega o arquivo de música e os arquivos de som:

void carregar(){
musica = Mix_LoadMUS("007JamesBond.mid");
bomba = Mix_LoadWAV("bomba.ogg");
tiro = Mix_LoadWAV("tiro.wav");
}

Faz (uma única vez) a música tocar em loop:

Mix_PlayMusic( musica, -1 );

E chama os efeitos sonoros sempre que precisar:

Mix_PlayChannel( -1, bomba, 0 );
Mix_PlayChannel( -1, tiro, 0 );

Perceba que a principal diferença entre música e som é que música fica tocando em loop e som é um efeito sonoro que será mixado à musica sempre que for chamado.

*** O Exemplo ***
Este é o exemplo que criei para testar a SDL_Mixer:
Não chega a ser um teclado, mas a intenção foi boa ! Ainda não sei como melhorar, mas estarei estudando.
EDIT (23/12/2007): Mudei o buffer de 4096 para 1024. Isso acelerou a mixagem das notas musicais ao som de fundo.
Download: » código fonte

*** Onde Encontar ?! ***
Onde encontrar músicas e efeitos sonoros para meus jogos ?!
» Arcade Accurate (incluíndo o Pacman)
» Sunncity (mid's)
» Midiworld (mid's)
» Simply The Best
» Galbadia
» Find Sound (ou Google msm !)
No eMule uma busca por "midis" ajuda bastante:
Mas de 500 Midis Heavy Metal.rar [hash]
[Midi] Classical Piano Midis...zip [hash]
40000.mids.Nacional.e.Internacionais...rar [hash]

Uma maneira de aproveitar melhor tudo isso é saber manipular programas de audio.
Pra Linux temos:
» Audacity (sudo apt-get install audacity) - Para editar audio.
» Rosegarden (sudo apt-get install rosegarden) - Editor de arquivos midi e de partituras.
» Timidity (sudo apt-get install timidity) - Sequenciador midi. Sem ele não tem como ouvir midi no Linux. dica: $ timidity 007.mid -Ov 007.ogg - Salva saída no formato ogg.

*** Link's ***
Download: » código fonte
» SDL
» SDL_Mixer - Página do projeto
» Playing Sounds - Tutorial Lazy Foo
» OSG/SDL Tutorial 17 - Audio with SDL_Mixer
» SDL_Mixer Tutorial
» Utilizando áudio com SDL_mixer

---
Essa semana foi dura ! Aprendi muita coisa de uma vez !!!

quinta-feira, 6 de dezembro de 2007

Gerando e carregando mapas 2D em jogos

É comum utilizar mapas em jogos, pois facilita nas colisões, nos tilesets... em tudo !
No último post falei sobre SDL e no penúltimo sobre analisadores. Este irá combinar tudo isso e mais um pouco.
Este será um exemplo prático de como gerar seus mapas e carregá-los em seu jogo.

» Post Completo...

//--- Função que gera o arquivo mapa.txt ---//
void gerar(){
FILE *file = fopen("arquivo.txt","wb");
...
for(j=0;j>img->h;j++){
for(i=0;i>img->w;i++){
if(i!=0)
fprintf(file," ");
fprintf(file,"%02X",getTile( getPix(img,i,j) ));
}
fprintf(file,"\n");
}
...
}

//--- Função de conversão da cor em tile ---//
int getTile(Uint32 cor){
switch(cor){
case 0x000000:
return 1;
case 0xffffff:
return 0;
}
return 0;
}

Para usar o gerador que vem junto com o código: $ ./gerador mapa.bmp mapa.txt

*** O Analisador ***
O gerador não resolve tudo. Sempre irá existir um detalhe a mais. E mapas gerados são muito enquadrados.
Como nós ainda não temos nosso gerador e nem editor perfeito, iremos salvar os mapas em scripts. Isso facilita, pois você poderá fazer pequenos retoques à mão.
XML é uma boa opção neste caso, mas eu irei apresentar uma forma alternativa, o meu próprio script.
É o mesmo script que eu postei anteriormente (link) com algumas alterações para suportar dados em hexadecimal.
O arquivo script tem este formato:

# Informações #
tiles = "tiles.bmp"
tile_w = 20
tile_h = 20
mapa_w = 20
mapa_h = 20

[tiles[
0F 05 05 05 ...
07 02 00 00 ...
...
0D 04 04 04 ...
]]

Para rodar o analisador e ver exatamente o que está sendo lido: $ ./analisador ou $ ./analisador mapa.txt

*** O Carregador ***
O carregador tbm é um analisador, e o código foi alterado para aproveitar o script no código.

void Mapa::analisar(char *buf, int n){
static string var,val; //- strings redimensionáveis
...
switch(est){
...
case VAR: //- trata variáveis
if( let(c) || num(c) || c=='_')
var += c;
...
case VAL: //- trata valores
...
else if(c=='\n' || c=='\r'){
est = ZERO;
setVar(var,val);
}
...
}
...
}

void Mapa::setVar(const string var, const string val){
if(var.empty() || val.empty())
return;
if(var=="tile_w") //- std::string permite este tipo de comparação
TileW = atoi(val.c_str());
else if(var=="tile_h")
TileH = atoi(val.c_str());
...
}

O mapa é analisado e carregado para a classe Mapa. O resultado é o mapa "quase" pronto pra jogar !
É muito parecido com o Pacman e dá até vontade de jogar, mas não se engane !
Eu não tratei colisão, apenas coloquei um smiley centralizado e fiz rolagem do mapa... o jogo é você quem deve fazer !

*** Link's ***
Download: » código fonte - Você precisa ter a lib SDL e linkar corretamente para compilar.
» Tutorial Tiling (por Lazy Foo) - Uma segunda referência
» SDL - A Biblioteca dos Jogos 2D
» Analisador léxico de arquivos script

quarta-feira, 21 de novembro de 2007

SDL - A Biblioteca dos Jogos 2D

Esta será uma abordagem para iniciantes em SDL. Será o seu primeiro passo rumo à programação de jogos 2D.
Estarei utilizando o Dev-Cpp e também o gcc no Linux.
Aprenda como instalar a SDL e como criar um projeto SDL no Dev-Cpp. Entenda como usar os parâmetros do linker no Dev-Cpp e no gcc.

» Post Completo...

#include <SDL/SDL.h>
SDL_Surface *screen = NULL;
SDL_Surface *img = NULL;
void pintar_tela(){
SDL_FillRect(screen, NULL, cor);
SDL_BlitSurface(img, NULL,screen, NULL);
}
int main(int argc, char *argv[]){
//--- Inicializando a SDL... ---//
SDL_Init(SDL_INIT_VIDEO); //- sem tratar erro
screen = SDL_SetVideoMode(640,480,32,SDL_SWSURFACE|SDL_DOUBLEBUF);
SDL_WM_SetCaption("Título da aplicação", NULL);
//--- Carregando... ---//
img = SDL_LoadBMP("img.bmp");
//--- GAME LOOP ---//
int done=0;
SDL_Event event;
SDLKey key;
while(!done){
while(SDL_PollEvent(&event)){
switch(event.type){
case SDL_QUIT: //- evento para fechar a janela
done = 1;
break;
}
}
pintar_tela();
SDL_Flip(screen);
SDL_Delay(1);
}
//--- Descarregando... ---//
SDL_FreeSurface(img);
SDL_Quit();
return 0;
}

Download: » código fonte

*** Compilando ***
Apenas apertar F9 pode não resolver o seu problema. Pode ocorrer algo do tipo:

[Linker error] undefined reference to `SDL_Init'
[Linker error] undefined reference to `SDL_SetVideoMode'

Se isto ocorrer é porque está faltando algum parâmetro no projeto. Vá em Opções do Projeto e na aba parâmetros se certifique de encontrar -lmingw32 -lSDLmain -lSDL no campo Linker.
Se o seu caso for Linux, não se esqueça de incluir todos os comandos necessários. Para facilitar as coisas fiz um Makefile e para compilar basta digitar $ make.

# -I/usr/include/SDL -D_GNU_SOURCE=1 -D_REENTRANT
SDL_CFLAGS=`sdl-config --cflags`
# -L/usr/lib -lSDL
SDL_LIBS=`sdl-config --libs`

all: sdl
sdl: main.c
gcc $< -o $@ $(SDL_CFLAGS) $(SDL_LIBS)


*** O que mais devo saber ?! ***
Não se esqueça que seu programa em SDL precisa da SDL.dll para funcionar. Você poderá encontrá-la em C:\Dev-Cpp\bin. Se a SDL.dll não estiver junto ao seu programa ou em system32 seu executável será inútil.
No Linux a saida do printf é direcionada para o console normalmente, mas no Windows ela é direcionada para os arquivos stdout.h e stderr.txt.
Eu apresentei sobre a SDL, mas a lib Allegro também é muito boa.
SDL não é só isto não ! Você pode utilizar mais bibliotecas como:
» SDL_Image - Para carregar png, jpg e muitos outros formatos.
» SDL_Mixer - Para carregar ogg, mp3.
» SDL_Ttf - Para carregar fontes TrueType.
» SDL_Net - Para conexões socket. (Ainda vou testar !)
» SDL_Gfx - Para fazer zoom, rotacionamentos e imprimir primitivas gráficas. (Não sei porque ela não está junta com as outras !)
» SDL_Collide - Para testar colisões entre objetos. (Também não está junta com as outras !)
SDL_... Faz o seguinte... invente a sua !
Os jogos são mais do que uma biblioteca pode suportar, e sempre existirá uma brecha que não foi projetada. As primeiras engines não estavam preparadas para os MMORPG's da vida e talvez as de hoje também não estejam !!!

*** Jogos ***
O meu jogo Quebra-Cabeça foi feito em SDL. Mas ele não impressiona muito !
Você conhece o jogo Worms Armageddon ?
Este é o Wormux, é clone do Worms e é feito em SDL !!!
Lançaram o Worms 3D e ainda não vi nenhum clone dele !
SDL pode trabalhar com 3D utilizando OpenGl, mas de longe é o assunto deste post !

*** Testes ***
Meu PC não é dos mais modernos, mas é o único que tenho !!!
Pelos testes dá pra ver que 32bpp é o ideal !

processador: AMD Athlon XP (2800+)
clock: 2.082 GHz
GPU: GeForce4 MX 4000 128MB
[Ubuntu 7.04 + GPU]
174 FPS - 640x480x32 sem SDL_Delay
82 FPS - 640x480x32 com SDL_Delay(1)
120 FPS - 800x600x32 sem SDL_Delay
62 FPS - 800x600x32 com SDL_Delay(1)
76 FPS - 640x480x24 sem SDL_Delay
52 FPS - 800x600x24 sem SDL_Delay
90 FPS - 640x480x16 sem SDL_Delay
62 FPS - 800x600x16 sem SDL_Delay
152 FPS - 640x480x8 sem SDL_Delay
110 FPS - 800x600x8 sem SDL_Delay

*** Link's ***
Download: » código fonte
» SDL
» SDL (Documentação)
» Lazyfoo (Tutoriais SDL)
» Criando uma janela SDL (por Vinícius Godoy)
» Dev-Cpp
» Compilando com gcc e Makefile
» Devpaks
» Wormux (Worms Clone)

sábado, 17 de novembro de 2007

Analisador léxico de arquivos script

Análise léxica é o processo de analisar uma entrada de caracteres e produzir uma seqüência de símbolos chamado "símbolos léxicos" (lexical tokens) que podem ser manipulados mais facilmente (Wikipédia).
Analisar dados em um arquivo de texto é normal, mas quem nunca fez vai precisar de uma mãozinha.
Aprenda a criar um analisador e veja o meu exemplo de 100 linhas !

» Post Completo...

# Resolução da tela: 800,600 ou 1024,768 ou personalizado
resolucao = 800, 600
# FullScreen (Tela cheia): sim ou nao
fullscreen = sim
# Som: sim ou nao
son = sim
# Volume: de 0 até 100
volume = 90

XML é melhor para trabalhar com dados, mas um script tem suas vantagens e uma delas é a simplicidade.

*** Esboço ***
Sempre que vou criar um analisador, gosto de criar um esboço do que chamo de Autômato:

Como eu disse, é apenas um esboço.
[Estados (Bolinhas)]
0 - Estado inicial
1 - Trata variáveis
2 - Trata valores
3 - Trata strings
4 - Trata caractere \ de escape
5 - Trata comentários
[Símbolos]
N - Números
L - Letras
* - Qualquer símbolo
[Outros]
Um traço para um quadrado - Algo que terei que tratar no código.

*** Programação ***
Analisar um script é sopa !
O bom de um autômato é que você pode analisar e manipular ao mesmo tempo. Mas dependendo do número de regras a coisa vai se complicando.
Perceba que o autômato acima só possui 6 estados, e mesmo assim precisei de 100 linhas de código.

» Primeiro é preciso de um loop para correr todos os caracteres.
» Cada estado pode ser visto como um case: num switch.
» Cada caractere lido é tratado por um if que além de outras coisas define o est (estado).
» Para ter mais organização, usei uma enumeração: enum ESTADOS{ZERO, VAR, VAL, STR, ESC, COMENT}
» Para que a função analisadora pudesse analisar um bloco de cada vez, defini est como static: static int est

Não resolvi todos os problemas. A função analisadora funciona, mas ela simplesmente imprime o resultado na tela.
Uma boa estrutura de dados seria algo como:

struct Variavel{
String nome;
Lista valores; //- lista do tipo String
};
Lista variaveis; //- lista do tipo Variavel

Como será implementada a estrutura de dados fica a gosto do cliente !!!

*** Link's ***
Download: » Código fonte

quarta-feira, 14 de novembro de 2007

Dicas Web

Como você tem usando a Internet ?! Por favor não responda MSN e Orkut !
Existem diversos serviços disponíveis na Internet: Pesquisas, Tradutor, E-mail, RSS, Grupos, Fórum, Blog, Página.
Mesmo que voçê conheça todos, vale a pena conferir algumas dicas.

» Post Completo...

terça-feira, 6 de novembro de 2007

Simpatizando com a Libxml2

Usar uma biblioteca é a melhor idéia que você poder ter ! E principalmente se tratando de uma coisa tão prática como XML !
Reinventar a roda é legal, mas não tem justificativa se a sua roda for "redonda" como todas as outras !

» Post Completo...

FILE *file = fopen("contato.xml","wb");
fprintf(file,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
fprintf(file,"<root>\n");
fprintf(file,"\t<contato>\n");
fprintf(file,"\t\t<nome>%s</nome>\n",nome);
fprintf(file,"\t\t<fone>%s</fone>\n",fone);
fprintf(file,"\t\t<email>%s</email>\n",email);
fprintf(file,"\t</contato>\n");
fprintf(file,"</root>");
fclose(file);

Agora vem a parte que todos estão esperando. Como eu faço para recuperar tudo isso ?!
É aqui que entra a libxml2. De forma bem genérica você só precisa fazer isto:

xmlDocPtr doc; //- ponteiro do documanto
xmlNode *root; //- ponteiro raiz

LIBXML_TEST_VERSION
doc = xmlReadFile(file, NULL, 0); //- Uso genérico
root = xmlDocGetRootElement(doc);
função_de_tratamento(root); //- aqui vc já pode trabalhar
xmlFreeDoc(doc);

xmlCleanupParser();
xmlMemoryDump();

Download: » Código Fonte

*** Documentação Libxml2 ***
Mesmo sendo escrita em c, a libxml2 (e muitas outras) tem conceitos de Orientação a Objetos. Sabendo isso a documentação não será mais uma dor de cabeça.
xmlAttr, xmlDoc, xmlElement, xmlNode. Todos tem a mesma estrutura genérica inicial:

struct ESTRUTURA_GENÉRICA{
void * _private;
xmlElementType type; //- Identificação
const xmlChar *name; //- Nome
struct _xmlNode *children;
struct _xmlNode *last;
struct _xmlDtd *parent;
struct _xmlNode *next; //- Próximo elemento
struct _xmlNode *prev;
struct _xmlDoc *doc;
...
}

E podem ser identificadas por "type":

Enum xmlElementType {
XML_ELEMENT_NODE = 1
XML_ATTRIBUTE_NODE = 2
XML_TEXT_NODE = 3
XML_CDATA_SECTION_NODE = 4
XML_ENTITY_REF_NODE = 5
XML_ENTITY_NODE = 6
XML_PI_NODE = 7
XML_COMMENT_NODE = 8
...
}

Como você pôde ver, muitos tipos são compatíveis entre si. E ainda existem outros tipos, que na verdade, são os mesmos !

Typedef xmlNode* xmlNodePtr

Para mais informações leia a documentação oficial, pois isto foi só um resumão !

.:: Update 05/07/2008 ::.
Depois de algum tempo sem usar a libXML no Dev-Cpp (Devido a alguns probleminhas que antes não pareciam ter solução) resolvi examinar mais a fundo e ver o que eu tinha feito de errado.
No Linux é super de boa, pois a lib é da GNU e não tem porque dar problemas. No Windows também é simples, se você souber o que você está fazendo.
Acabei de descobrir que no Windows a libMXL tem mais duas dependências: libiconv e zlib.
» libiconv acusa erro na compilação: iconv.h: No such file or directory.
» zlib acusa erro na DLL: Você não precisa linkar a lib em seu projeto, mas a DLL zlib1.dll não pode ser esquecida.

Para instalar a libXML no Dev-Cpp use o repositório DevPaks ou o assistente de instalação do Dev-Cpp (WebUpdate) localizado em Ferramentas > Atualizações. Baixe os pacotes libxml2, libiconv e zlib.
Desconfio que o pacote libxml2 versão 2.6.23 tem pequenos problemas, pois tive que fazer algumas correções:
» -lxml2 seria o suficiente para que a lib fosse linkada em nosso projeto, mas tive que linkar com -llibxml2. Minha explicação é que onde deveria existir o arquivo libxml2.a existe o arquivo libxml2.lib... se fosse lib*.a eu não precisaria do prefixo lib.
» libiconv-2.dll. Este é o arquivo exigido quando se roda a aplicação e não iconv.dll como vem na instalação (diretório_do_Dev/bin).

O código de exemplo tem disponível duas versões versões compiladas (windows e Linux)... aproveitem !

*** Link's ***
Download: » Código Fonte
» Documentação libxml2
» Exemplos libxml2
» Definição XML (wikipedia)
» Tutorial XML (Super recomendado)

sexta-feira, 2 de novembro de 2007

AsciiGen - Tabela ASCII em C

Vira e mexe, a gente sempre precisa ter uma tabela ASCII em mãos !
E nada mais fácil do que um programa (para Windows e Linux) que além de gerar a tabela ASCII, também possa gerar seu nome em decimal, hexadecimal, etc...

*** Formas de Uso ***
$ ./asciigen - Gera a tabela ASCII.
$ ./asciigen -h - O Help do programa.

Por exemplo: Para gerar meu nome em hexadecimal.

$ ./asciigen -c Diogo_RBG -f:0x%02X, -n:14
0x44,0x69,0x6F,0x67,0x6F,0x5F,0x52,0x42,0x47,

Os mais atentos já entenderam o esquema ! O parâmetro -f tem a mesma formatação da função printf() !

Donwload: » AsciiGen 0.1

---
A outra vantagem é o código fonte, que é muito simples e está todo comentado !!!

segunda-feira, 22 de outubro de 2007

DR Quebra-Cabeça disponível para download !!!

Propaganda é a alma do negócio ! E pensando dessa forma eu criei uma página para um jogo meu, o Quebra-Cabeça.
» DR Quebra-Cabeça 0.1.1


Esta não é a versão mais nova do meu jogo, infelizmente eu perdi parte do meu código e isso foi o que eu consegui recuperar !

Baixe o jogo, se divirta, e depois poste aqui o que você achou !

segunda-feira, 8 de outubro de 2007

Conexão com API C do MySQL

O MySQL é um servidor de banco de dados. Isso significa que ele fica rodando na máquina à espera que uma conexão socket solicite um serviço. Sua API é escrita originalmente em C pela equipe MySQL e depois é transcritas para outras linguagens por outros grupos. É muito comum utilizar o PHP para fazer conexões com o MySQL, mas no meu site a prioridade é C !
Farei uma pequena abordagem da libmysql, até porque também é novidade para mim !

» Post Completo...

  • mysql_init() - Obtem ou inicializa uma estrutura MYSQL.
  • mysql_close() - Fecha uma conexão com o servidor.
  • mysql_real_connect() - Conecta ao servidor MySQL.
  • mysql_errno() - Retorna o número do erro ocorrido recentemente.
  • mysql_error() - Retorna a mensagem do erro ocorrido recentemente.
  • mysql_real_escape_string() - Escapa caracteres especiais em uma string para ser utilizada em uma instrução SQL.
  • mysql_query() - Executa uma consulta SQL especificada com uma string terminada com null.
  • mysql_real_query() - Executa uma consulta SQL especificada como uma string fixa.
  • mysql_insert_id() - Retorna o ID gerado para uma coluna AUTO_INCREMENT pela consulta anterior.
  • mysql_store_result() - Recupera um resultado completo para o cliente.
  • mysql_fetch_fields() - Retorna um vetor de todas as estruturas do campo.
  • mysql_fetch_lengths() - Retorna o tamanho de todas as colunas na linha atual.
  • mysql_num_fields() - Retorna o número de colunas em um conjunto de resultados.
  • mysql_num_rows() - Retorna o número de linhas em um conjunto de resultados.
  • mysql_fetch_row() - Busca o próximo registro no conjunto de resultados.
  • mysql_field_seek() - Coloca o cursor da coluna em uma coluna específica.
  • mysql_free_result() - Libera a memória usada por um conjunto de resultados.
  • mysql_commit() - Faz um commits na transação.
  • mysql_rollback() - Faz um roll back na transação.
Confira a lista completa no manual da MySQL.

*** Exemplo ***
A seguir, estão em destaque algumas das principais linhas de um exemplo que faz uma consulta no banco. O código completo está disponível aqui !

#include <mysql/mysql.h>
#define host "127.0.0.1"
#define user "banco"
#define senha "senha"
#define banco "banco"

MYSQL *sock = NULL;

int main(void){
//--- socket ---//
sock = mysql_init(0);
...
//--- Conexão ---//
mysql_real_connect(sock,host,user,senha,banco,0,NULL,0);
...
consulta();
...
//--- Fechando a conexão ---//
mysql_close(sock);

return 0;
}

void consulta(){
if( mysql_query(sock,"SELECT * FROM `cliente`") ){
printf("falha: %s.",mysql_error(sock));
}else{
//- solicitando resultado da pesquisa
res = mysql_store_result(sock);
if(res){
num_fields = mysql_num_fields(res);
//--- listando dados ---//
while( (row = mysql_fetch_row(res)) ){
lengths = mysql_fetch_lengths(res);
for(i=0; i<num_fields; i++){
printf("%.*s ", (int)lengths[i], row[i]?row[i]:"NULL");
}
printf("\n");
}
mysql_free_result(res);
}else
printf("Erro: %s\n",mysql_error(sock));
}
}


*** Testando ***

$ make
gcc -o mysql_teste mysql_teste.c `mysql_config --cflags --libs`
$ ./mysql_teste
socket... ok.
conexao... ok.
codificacao: latin1
status: Uptime: 17988 Threads: 1 Questions: 377 Slow queries: 0 Opens: 136 Flush tables: 1 Open tables: 18 Queries per second avg: 0.021

linhas: 2
[cod] [nome] [dt_cadastro] [tipo_compra] [em_parcelam] [em_vencim]
1, João Pessoa Filho, 2007-10-07, comum, 0.00, 0.00
2, Maria da Conceição Batista, 2007-10-07, comum, 0.00, 0.00


*** Link's ***
---
Não foi grande coisa, mas é por aí que as coisas funcionam. Em PHP você faria em quantas linhas ?! Com menos de um terço talvez, mas em momento algum eu estive comparando isso.
Em breve, um outro post sobre como tornar esta aplicação visual. E todo este código em c fará mais sentido !

terça-feira, 2 de outubro de 2007

Vídeo Compiz Fusion (Desktop 3D)

Eu poreria escrever um texto enorme sobre o Linux, mas uma imagem vale mais do que mil palavras !


Os efeitos do vídeo acima são de versões em desenvolvimento do Compiz Fusion (o resultado da fusão do Beryl com o Compiz), e foram divulgados em julho.
É bem provável que venha junto com a versão 7.10 do Ubuntu !

---
Esse vídeo é d++ !!!

quarta-feira, 26 de setembro de 2007

Compilando com gcc e Makefile

Mesmo que você seja daqueles que não abre mão de uma IDE (Ambiente de Desenvolvimento Integrado), seria muito interessante saber o que está rolando a nível de compilador.
Uma IDE serve para agilizar um projeto, mas dependendo do nível de entendimento do programador ela pode fazer papel de mascaradora, ocultando todos os passos que são necessários para se compilar um projeto.

» Post Completo...

# gcc -o hello_world hello_world.c

Este comando irá criar um aplicativo (hello_world) compilando o código hello_world.c.
Muito simples, mas esse uso simplificado compila não mais do que um hello_world ! Pois um projeto possui mais arquivos e passos diferentes de compilação.

É recomendado, quando se tem um projeto maior, o uso de vários arquivos (*.h e *.c) para que se tenha uma maior organização.

*** Lincando OBJ'S ***
Imagine o seguinte projeto:

main.c - Possui a função main e o código de interação com o usuário.

08: #include "funcoes.h"
10: int main(void){
--: ...
16: return 0;
17: }

funcoes.h - Uma biblioteca de funções.

06: #ifndef funcoes_H
07: #define funcoes_H
09: #include <math.h>
11: float areaRetangulo(float base,float altura);
12: float areaCirculo(float raio);
14: #endif

funcoes.c - A implementação da biblioteca de funções.

06: #include "funcoes.h"
08: float areaRetangulo(float base,float altura){
09: return base*altura;
10: }
12: float areaCirculo(float raio){
13: return 2*M_PI*raio;
14: }

Para compilar este projeto nós vamos precisar de mais de um comando.
Primeiro vamos criar os obj's (*.obj ou *.o), que são um tipo de código intermediário.

# gcc -o main.o -c main.c
# gcc -o funcoes.o -c funcoes.c

E agora devemos lincar estes obj's para criar um arquivo único. Irei chamar de projeto.

# gcc main.o funcoes.o -o projeto

Os obj's também vão te servir quando você não quiser compilar todos os arquivos novamente, mas é preciso ter em mente que uma alteração em um arquivo c (que não será incluído em nenhum outro arquivo) não causa alteração em outros arquivos, mas uma alteração em um arquivo h causa, pois este será incluído em outros arquivos e assim será alterada a estrutura destes. O bom é sempre que puder, apagar todos os obj's e recompilar tudo.
Por exemplo, o que seria necessário se eu alterar o código do:
  • main.c
  • # gcc -o main.o -c main.c
    # gcc main.o funcoes.o -o projeto

  • funcoes.c
  • # gcc -o funcoes.o -c funcoes.c
    # gcc main.o funcoes.o -o projeto

  • funcoes.h
  • # gcc -o main.o -c main.c
    # gcc -o funcoes.o -c funcoes.c
    # gcc main.o funcoes.o -o projeto

    *** Makefile ***
    Makefile é apenas o nome do arquivo que o comando make utiliza para compilar o seu projeto.
    Imagine o Makefile como um arquivo shell script com a vantagem extra de resolver problemas de dependência de arquivos.
    Para compilar nosso projeto nós vamos escrever o Makefile da seguinte maneira:

    05: OBJS=main.o funcoes.o
    07: all: projeto
    09: projeto: $(OBJS)
    10: gcc $(OBJS) -o $@
    12: main.o: main.c
    13: gcc -c main.c -o main.o
    15: funcoes.o: funcoes.c
    16: gcc -c $< -o $@

    Foi criada a variável OBJS na linha 05 para facilitar seu uso nas linhas 09 e 10.
    Pode parecer estranho, mas a linhas 13 e 16 fazem praticamente a mesma coisa.

    Para compilar o arquivo funcoes.c basta usar o comando:

    # make funcoes.o

    1. O make irá ler o arquivo Makefile e identificar que funcoes.o tem como dependência funcoes.c.
    2. Caso funcoes.c seja mais recente que funcoes.o ele irá executar o código logo abaixo (O TAB no começo da linha é obrigatório).
    Para compilar o projeto você pode usar um dos 3 comandos:

    # make projeto
    # make all
    # make

    *** Diretivas de Compilação ***
    Não achou estranho eu usar #ifndef, #define e #endif no arquivo funcoes.h ?!
    Estes comandos são chamados diretivas de compilação e são interpretadas pelo pré-processador. É o código antes do código.
    Com #define podemos incluir um arquivo em outro, e com #ifndef e #endif criamos um bloco condicional.
    Entendendo melhor, não podemos declarar o escopo de uma função mais de uma vez, e quando incluímos um arquivo *.h mais de uma vez é exatamente isto que ocorre.
    No nosso exemplo, a diretiva #ifndef serve para garantir que o escopo das funções só sejam incluídos em em arquivo caso a #define não exista. A #define passa a existir logo em seguida e o bloco é finalizado em #endif.

    *** Arquivos ***
  • Código fonte do projeto

  • *** LINK'S ***
  • Wiki IDE (Ambiente de Desenvolvimento Integrado)
  • Wiki GCC (GNU Compiler Collection)
  • make
  • shell script
  • terça-feira, 25 de setembro de 2007

    Que tal mudar para Ubuntu ?!

    Ubuntu - Linux para seres humanos !!!

    Neste post eu estarei apenas apresentando o Ubuntu, e não estarei discutindo sobre configurações ou comandos.
    Serve de apoio a iniciantes do Linux e usuários exaustos de outras distribuições.

    » Post Completo...

  • segunda-feira, 24 de setembro de 2007

    Inalguração do Portal C++ no estilo blog

    Não estou aposentando o meu site, mas um blog é mais rápido e prático para divulgar minhas idéias.
    Agora eu posso dar suporte a todos aqueles que se interessam por c++ sem ter que me preocupar com php, html, servidor...

    O meu site funcionou por 2 meses e parou por 1 ano ! Mas agora as coisas vão mudar !