Notícia
Sunag Engine 1.0 & Jogo do Patrimônio Cultural
Detalhes do desenvolvimento do jogo e código de colisão da Sunag Engine.
Enviado por Jean Deconto em 10/5/2010 0:00:00

Olá Pessoal.


Em 2009 foi a época que mais elaboramos projetos a serviços de outras empresas, embora atualmente nós estamos focados nos projetos próprios, lembrei de algo interessante para a comunidade, a Sunag Engine para o XNA, e o Jogo do Património Cultural para PC.


Making-off:





1. Shader


O Jogo do Património Cultura foi totalmente desenvolvida aqui na Sunag, desde suas trilhas sonoras a seus algoritmos de colisão, utilizamos Pixel e Vertex Shader 2.0, trabalhamos com uma fusão do ShaderFx  com nossos próprios algoritmos de Shader para a composição matemática visual do game.






2. Modelagem e Animação

Embora a animação seja um dos fatores mais importantes do jogo, e nem sempre é tão difícil, como previsto, foi um tanto complicado a adaptação com o XNA. Utilizamos o 3Ds Max para a animação e modelagem, e para exportação usamos o plug-in Pandasofts Directx Exporter, que exporta para o formato .X, que por ultimo passou pela biblioteca animação XNAmation criado por Bruno Evangelista, disponível para download no site dele
http://www.brunoevangelista.com/;





3. Algoritmos

Muitos de nossos algoritmos criado nos outros games foi reaproveitado, como sempre contando com a famosa equação de interpolação:

X += ((Y - X) * T) * P;
Onde X é o posição posição atual, Y a posição que você quer que ele vai, T o TimeStep do jogo (Que nada mais é que a diferença de tempo de um quadro para o outro) e o P que seria que o tempo de propagação em milisegundos.

Como tudo isso você tem um curvatura exponencial de suavização de movimento, muito usado nos jogos profissionais. Mais um exemplo;

(Vector3)
Camera.Position += ((Pos - Camera.Position) * TimeStep) * 10.0f;

A Camera.Position vai estar nas coordenadas da variável Pos em um tempo de 10 milisegundos.




4. Colisão

Uma etapa muito complicada também, mais complicado pelo fato de elaborar a engine de colisão são fatores que prende você muitas horas na frente do mesmo projeto, se você não estiver com os algorítimos na mão.

No nosso caso não foi muito diferente, tivemos que elaborar boa parte dos cálculos e colisão, até a presença de um físico foi contratada por nós para podemos entender melhor a analogia e fazer algo coerente e sem bugs.

A metodologia de colisão é estática, e pode ser usado também para optimização do render, o método foi a Octree, algorítimo de optimização, embora agente tenha criado nosso próprio implemento, essa tecnologia já é bem conhecida no mundo dos games.

O funcionamento dela é bem simples, ela cria um BoundingBox, caso tenha alguma vértice dentro dessa BoundingBox ela criara outras BoundingBox dentro da mesma, seguindo uma função recursiva, baseado em um limite de escala, você não pode deixar a BoudingBox muito pequena, ou com pouco vértices implementas, se não você não irar ganhar no desempenho, por isso a sempre a necessidade de trabalhar com escala no game, é recomendado o tamanho mínimo que uma BoundingBox seja pelo menos o tamanho ou metade do personagem do principal.

Nunca se esqueçam, use apenas os metodos BSP ou Octree na ContentPipeline (O pré-processador do Visual Studio (XNA)) se for fazer em tempo de execução vai demorar muito para carregar o jogo.

Para saber mais sobre acesse:

http://en.wikipedia.org/wiki/Octree

Código Octree não recursivo. (ContentPipeline) (Incluso no Source)

//About Jean Carlo Deconto - (c) Sunag Entertainment 2009

using System.Collections.Generic;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;

namespace ModelContentProcessor
{
    [ContentProcessor]
    public class ModelContentProcessor : ModelProcessor
    {
        public List<Vector3> vertices = new List<Vector3>();
        public List<int> Cont = new List<int>();

        public override ModelContent Process(NodeContent input,
                                             ContentProcessorContext context)
        {
            FindVertices(input);

            ModelContent model = base.Process(input, context);

            Dictionary<string, object> tagData = new Dictionary<string, object>();
            model.Tag = tagData;
           
            BoundingBox BBox = BoundingBox.CreateFromPoints(vertices);

            int part = 2;


            BoundingBox[] Bounding = BSP_BoundingBox(BBox, part);

            tagData.Add("Bounding", Bounding);

            tagData.Add("Bounding0", BSP_BoundingBox(Bounding[0], part));
            tagData.Add("Bounding1", BSP_BoundingBox(Bounding[1], part));
            tagData.Add("Bounding2", BSP_BoundingBox(Bounding[2], part));
            tagData.Add("Bounding3", BSP_BoundingBox(Bounding[3], part));
            tagData.Add("Bounding4", BSP_BoundingBox(Bounding[4], part));
            tagData.Add("Bounding5", BSP_BoundingBox(Bounding[5], part));
            tagData.Add("Bounding6", BSP_BoundingBox(Bounding[6], part));
            tagData.Add("Bounding7", BSP_BoundingBox(Bounding[7], part));

            tagData.Add("Vertex0", BSP_Vertex(Bounding[0], part));
            tagData.Add("Vertex1", BSP_Vertex(Bounding[1], part));
            tagData.Add("Vertex2", BSP_Vertex(Bounding[2], part));
            tagData.Add("Vertex3", BSP_Vertex(Bounding[3], part));
            tagData.Add("Vertex4", BSP_Vertex(Bounding[4], part));
            tagData.Add("Vertex5", BSP_Vertex(Bounding[5], part));
            tagData.Add("Vertex6", BSP_Vertex(Bounding[6], part));
            tagData.Add("Vertex7", BSP_Vertex(Bounding[7], part));           

            tagData.Add("Vertices", vertices.ToArray());

            tagData.Add("BoundingBox", BBox);
           
            return model;
        }


        BoundingBox[] BSP_BoundingBox(BoundingBox bounding, int part)
        {
            Vector3 Dist = new Vector3
                (
                MathHelper.Distance(bounding.Min.X, bounding.Max.X),
                MathHelper.Distance(bounding.Min.Y, bounding.Max.Y),
                MathHelper.Distance(bounding.Min.Z, bounding.Max.Z)
                );

            BoundingBox[] Bounding = new BoundingBox[part * part * part];

            int count = 0;
            for (int bX = 0; bX < part; bX++)
            {
                for (int bY = 0; bY < part; bY++)
                {
                    for (int bZ = 0; bZ < part; bZ++)
                    {
                        Vector3 offset = new Vector3(Dist.X, Dist.Y, Dist.Z);
                        offset /= part;

                        offset.X *= bX;
                        offset.Y *= bY;
                        offset.Z *= bZ;

                        Vector3 Min = bounding.Min + offset;
                        Vector3 Max = Min + (Dist / part);

                        Bounding[count] = new BoundingBox(Min, Max);

                        count++;
                    }
                }
            }

            return Bounding;
        }

        List<Vector3>[] BSP_Vertex(BoundingBox bounding, int part)
        {
            List<Vector3>[] Vertex = new List<Vector3>[part * part * part];
            BoundingBox[] Bounding = new BoundingBox[part * part * part];

            Vector3 Dist = new Vector3
            (
            MathHelper.Distance(bounding.Min.X, bounding.Max.X),
            MathHelper.Distance(bounding.Min.Y, bounding.Max.Y),
            MathHelper.Distance(bounding.Min.Z, bounding.Max.Z)
            );

            int count = 0;
            for (int bX = 0; bX < part; bX++)
            {
                for (int bY = 0; bY < part; bY++)
                {
                    for (int bZ = 0; bZ < part; bZ++)
                    {
                        Vertex[count] = new List<Vector3>();

                        Vector3 offset = new Vector3(Dist.X, Dist.Y, Dist.Z);
                        offset /= part;

                        offset.X *= bX;
                        offset.Y *= bY;
                        offset.Z *= bZ;

                        Vector3 Min = bounding.Min + offset;
                        Vector3 Max = Min + (Dist / part);

                        Bounding[count] = new BoundingBox(Min, Max);

                        for (int i = 0; i < vertices.Count; i += 3)
                        {
                            List<Vector3> triangle = new List<Vector3>();

                            triangle.Add(vertices[i]);
                            triangle.Add(vertices[i + 1]);
                            triangle.Add(vertices[i + 2]);

                            BoundingBox box = BoundingBox.CreateFromPoints(triangle);

                            if (Bounding[count].Intersects(box))
                            {
                                Vertex[count].AddRange(triangle);
                            }
                        }
                        count++;
                    }
                }
            }

            return Vertex;
        }

        void FindVertices(NodeContent node)
        {
            MeshContent mesh = node as MeshContent;
          
            if (mesh != null)
            {
                string Name = mesh.Name;
                bool CollisionModel = true;

                string baseName = "Type06";
                if (Name.Length > baseName.Length)
                {                   
                    string modelName = Name.Substring(0, baseName.Length);
                    CollisionModel = (baseName != modelName);
                }

                if (CollisionModel)
                {
                    Matrix absoluteTransform = mesh.AbsoluteTransform;
                    foreach (GeometryContent geometry in mesh.Geometry)
                    {
                        foreach (int index in geometry.Indices)
                        {
                            Vector3 vertex = geometry.Vertices.Positions[index];
                            vertex = Vector3.Transform(vertex, absoluteTransform);
                            vertices.Add(vertex);
                        }
                    }
                }
            }
            foreach (NodeContent child in node.Children)
            {
                FindVertices(child);
            }
        }
    }
}

Não recursivo fica mais fácil o entendimento, e fazelo a modificação também não é tarefa tão difícil.


5. Colisão/Mouse

Como o Jogo do Patrimônio é um RPG Multiplayer, foi obvio que precisaríamos implementar o sistema de colisão pela projeção do mouse diante a projeção angular da câmera.

O método implementado foi o seguinte:

Agora que já temos os BoundingBox dos objetos projetados logo na explicação acima, o raio do mouse (MouseRay) vai buscar cada detectar as colisões com as BoundingBox dos objetos, e depois vai analisar em qual Octree ele está implementado, para só depois disso vereficar a colisão com triangulo, e finalmente retornar o vetor de intercessão.

Parece complicado, mais é um dos métodos mais preciso e usado de colisão do mundo dos games.






6. Render/Otimização

Um dos meios mais práticos para esse game, e digo em geral, para otimização de render é a tecnica BoundingFrustum.

Que nada mais é que a projeção matemática criada pelos dados da câmera, com isso você pode criar conexões com os BoundingBox , como explicado acima, e renderizar apenas uma parte do cenário, a qual a câmera esta enxergando, evitando o processamento de modelos que estão fora do frustrum ou do campo de visão da câmera.






Sunag Engine

Estou liberando source da engine, porem, apenas para fins acadêmicos, não existe nada que impeça o uso para fins profissionais, mais por falta de tempo não posso elaborar nada adequado explicando detalhadamente o processo, contando apenas com a criatividade de vocês acharem os melhores caminhos para os algorítimos implementados nessa engine.

Sunag Entertainment:

http://www.sunag.com.br/
http://www.sunag.com.br/labs/

Demo Reel da Sunag 2009, contem vídeo do Jogo do Patrimonio Cultural:

http://www.youtube.com/user/sunagbrasil#p/a/u/1/lyIZC10k74M

Jogo do Patrimonio Cultural: (Apenas para Windows XP)

Parte 1: http://rapidshare.com/files/385170048/patrimonio_cultural_1.0.part1.rar.html
Parte 2: http://rapidshare.com/files/385177795/patrimonio_cultural_1.0.part2.rar.html

Sunag Engine 1.0 - (public-source):

http://www.sunag.com.br/downloads/sunagxnaengine_1.0_publicsource.rar

Nóticias do Jogo no Paraná-Online:

http://www.parana-online.com.br/editoria/almanaque/news/256075/?noticia=LAPA+SERA+PALCO+DO+JOGO+DO+PATRIMONIO+CULTURAL

--
Nós só criamos limites para poder vencer a nós mesmos um dia. (Jean Carlo Deconto)

Abraço a todos.

Sobre o Autor

sunag
Jean Deconto
Não Definido

Clique para avaliar:

Comentários
" Boa Jean, parabéns pelo game. =D Abraços"
Enviado por Flexa em 10/5/2010 12:15:34:
 

Adicione seu Comentário  Voltar
Logos do XBox 360, XNA e Games For Windows
Copyright 2010 por SharpgamesPolítica de Privacidade  |  Termos de Uso