segunda-feira, 12 de novembro de 2007

Biblioteca Tecnodata3D.Utility - Classe Global

Opa! gente bacana,

Aqui estou entao mais uma vez para explicar a nossa engine, e começaremos pela assembly básica - Utility.

Como expliquei antes, a Utility contem classes que representam a base da engine, assim como várias classes de apoio. Vamos começar por ententer alguns conceitos

O que é um singleton?

Um singleton é uma classe da qual existe apenas uma instancia dela em toda a vida do projeto. Na nossa engine, poucas classes sao singletons, de bom exemplo a classe Global. Nesta classe ficam agregados os varios modulos da Utility necessarios para o uso da engine. Ela tem uma estrutura bem simples:

Tecnodata3D.Utility.Global
- private static Global _default;
- private BaseGame _game;
- private List _listModules;
- public AssetManager AssetManager { get(); set(); }
- public Camera Camera { get(); set(); }
- public static Global Default { get(); }
- public BaseGame Game { get(); }
- public Input Input { get(); set(); }
- public Log Log { get(); set(); }
- public ScreenManager { get(); set(); }
- public UserProfileManager UserProfileManager { get(); set(); }
- private Global ();
- public static void CreateInstance(BaseGame game);
- public void Initialize();
- public void AddModule ( AModule oModule );
- public AModule GetModule ( string sModule );
- public void ReplaceModule ( AModule oModule );
- public void RemoveModule ( AModule oModule );
- public void Update ( GameTime gameTime );
- public void Draw ( GameTime gameTime );

Como falei anteriormente, para usar a engine, deve-se criar uma classe de jogo que herda a classe BaseGame. A propria BaseGame se preocupa em criar a instancia de Global, entao nao existe necessidade de se preocupar com isso. Esta classe, entao, é uma interface global para uso da biblioteca Utility da engine - atravez dela temos acesso aos modulos principais, alem de possibilidade de criar e adicionar novos modulos, ou substituir os padrao por outros (por exemplo, voce pode criar uma classe CameraRTS, herdando-a da classe Camera, e utilizar do metodo ReplaceModule() para substituir a camera padrao pela customizada).

Isso nos dá uma poderosa biblioteca básica para usar para a maioria dos projetos (por exemplo, Camera vem já com 4 tipos de camera diferentes), e caso exista necessidade de substituir ou extender essas funcionalidades, temos a flexibilidade de o fazer facilmente.

Mas como usar a Global? É simples, em qualquer local do projeto, basta acessar a propriedade estática Default, a qual guarda a instancia "singleton" da classe Global. Entao, acessamos facilmente a Camera da biblioteca através do formato Global.Default.Camera. Embora possamos acessar qualquer módulo existente na Global atraves do metodo GetModule(), criamos algumas propriedades para facilitar a digitação para acesso a alguns modulos mais utilizados (internamente eles usam o metodo GetModule).

E como é a organização interna da Global?

Internamente, todos os módulos sao guardados numa lista, obedecendo a uma ordem definida por uma propriedade OrderID da classe abstrata AModule. Se um módulo necessita ser executado antes de outro, basta alterar sua propria OrderID antes de ser adicionado na Global. Por padrao, todos os módulos que tem uma propriedade de facil acesso já sao criados e adicionados na Global pela BaseGame.

Embora seja a BaseGame que recebe primeiramente as chamadas a Update() e Draw(), elas sao repassadas imediatamente para Global, que trata de iterar por todos os modulos em ordem correta, repassando assim as chamadas a cada um dos módulos. Este sistema é extremamente eficiente e simples de trabalhar - e remove a necessidade de chamar individualmente Update() e Draw() para os modulos necessarios - tudo é cuidado automaticamente pela engine.

E é isso aí gente - como tinha dito, a Global é uma classe bem simples, porém facilita muito a vida na hora de acessar dados globais do projeto. Próximo post estarei explicando detalhadamente o funcionamento da classe BaseGame - que é o coração da parte Utility da engine.

Abraço e até mais!

quarta-feira, 7 de novembro de 2007

Estrutura da engine Tecnodata3D

A engine tem uma estrutura simples, porém bem flexivel.

Divimos a engine em varias assemblies, sendo atualmente as seguintes:

- Tecnodata3D.Utility
Engloba metodos e classes básicas necessárias ao funcionamento da engine. Nela se encontram singletons como a Global ( disponibiliza uma interface geral de acesso a todos os subsistemas da utility ), Input, Camera, AssetManager (gerencia o conteúdo do jogo), Log, ScreenManager (gerencia as telas do jogo) e UserProfileManager. Possui classes que representam entidades, luzes, coleções (ex. Pair), perfis de usuário e configurações, exceções, efeitos e módulos. Além de tudo isso, inclui também a classe BaseGame que é necessário herdar para a criação de um aplicativo usando a engine;

- Tecnodata3D.Additional
Inclui vários componentes como contador de FPS, capturador de imagem de tela, e telas padrão de jogo (explicarei esta parte em mais detalhe num novo post);

- Tecnodata3D.ContentPipeline
Todas as classes necessárias para a criação de tipos próprios de dados, como por exemplo nossa T3DModel e Terrain;

- Tecnodata3D.Physics
Toda a lógica de mundo físico é definida aqui nesta assembly. Por hora estamos utilizando a biblioteca Newton para simulação de física, mas nossa estrutura de classes é flexivel ao ponto de nos permitir utilizar outras bibliotecas, ou mesmo criar uma lógica do zero;

- Tecnodata3D.Renderer
Aqui se encontram as classes responsáveis pelo desenho 2D e 3D na tela, incluindo um SceneManager (que utiliza de um SceneGraph internamente para controle de cena). Inclui também facilidades pra criação de interfaces gráficas 2D, e controle de cursor de mouse;

Esta estrutura nos possibilitou uma fácil visualização do escopo da engine, e uma flexibilidade enorme na hora de alterar e adicionar módulos de engine. Eu estarei explicando mais detalhadamente cada uma das assemblies em posts seguintes - então até breve.

Model do XNA = Martelo

Depois de considerar bastante as opções, decidimos por criar nossas próprias classes de modelo no XNA, em detrimento da classe Model padrão do framework. Aqui listo algumas das razões que nos levaram a tomar essa decisão:

- Suporte em tempo de compilação para geração do nosso próprio modelo de objeto físico - bounding box, sphere ou mesh;
- Nosso próprio formato de Vertices - isto é crucial para alguns dos shaders mais avançados que planejamos utilizar (por exemplo, bump mapping);
- Como criamos uma classe própria de shader, o modelo pode vir já do content pipeline com um efeito apropriado, e não o padrao BasicEffect;
- A classe Model padrão não inclui informações de animação, e utilizar a propriedade Tag para armazenamento desses dados não nos pareceu uma opção elegante;
- Extensibilidade - usando um modelo próprio, temos a possibilidade de extender nosso código com novas funcionalidades, sem necessidade de alterar (fora algumas exceções) a caixa preta que define nosso sistema de modelos, meshes, shaders e animações;

Por enquanto estamos em fase de planejar essas classes novas ( a engine está, por hora, baseada na Model padrão), assim como entender claramente o funcionamento do content pipeline. Em breve terei uma versão funcional que estarei explicando detalhadamente aqui neste blog.

Próximo post vou explicar um pouco sobre a estrutura da engine Tecnodata3D, e algumas das decisões que tomamos e porquê.

Tecnodata CFC

Como primeiro post, eu queria explicar um pouco sobre o meu trabalho aqui na Tecnodata.

A Tecnodata é uma empresa especializada em educação de trânsito, onde eu trabalho no setor de TI, mais especificamente no setor de desenvolvimento de software interativo e jogos educativos.

Neste blog eu irei postar informações e dicas sobre problemas e soluções encontradas no meu dia a dia de trabalho com C++, C# com XNA e Flash CS3. Estarei colocando links para outros blogs de meus colegas que trabalham com algumas tecnologias diferentes tambem (exemplo do Flávio com Flex e Java, ou do Jefferson com ASP.NET).

Espero que com este blog eu possa estar ajudando outras pessoas a superar problemas que eu tenha encontrado, alem de que possam aproveitar positivamente as dicas que planejo postar.