Artigo
Aplicando Função quadrática com XNA
Criando um GameComponent para obtenção de Trajetórias Parabólicas
Enviado por Luciano José em 26/5/2008 0:00:00

Introdução
 
Uma função f: R -> R é dita quadrática quando existem números reais a, b, c, com o valor de a diferente de 0 (zero), tal que f(x) = ax² + bx + c para todo x pertencente aos reais.
 
Outra definição:
 
Uma função quadrática, é uma função polinomial na forma f(x) = ax² + bx + c, onde a é diferente de 0. O gráfico de uma função quadrática é uma parábola cujo maior eixo é paralelo ao eixo y.
 
No assunto em foco, Dois problemas são importantes:
 
  • Quando o x é dado, será necessário calcular o f(x);
  • Quando o f(x) é dado, será necessário calcular o x.
 
 
Gráfico da função quadrática
 
O gráfico de uma função quadrática é uma parábola.
 
Foto 1
 
O ponto azul na Foto 1, é o vértice. Vértice é o ponto médio; corresponde ao ponto mais extremo da parábola.
 
Algumas considerações:
 
  1. Quando a > 0, a concavidade está voltada para cima.
  2. Quando a < 0, a concavidade está voltada para baixo.
 
  1. Quanto menor o valor de a, maior será a abertura da parábola.
  2. Quanto maior o valor de a, menor será a abertura da parábola.
 
Em relação as consideração 1 e 2 abordadas acima. Na demonstração mostrada a seguir, essas idéias serão invertidas. O novo texto para as novas considerações coincidirem com a demonstração seria:
 
Quando a < 0, o percurso que o objeto da demonstração irá tomar, será de uma  concavidade voltada para cima.
 
Quando a > 0, o percurso que o objeto da demonstração irá tomar, será de uma  concavidade voltada para baxo.
 
Isso aconteceu, pelo fato de na demonstração, vai ser considerado o eixo do Y crescente na mesma direção em que o eixo Y, da superfície em que os objetos são renderizados, cresce.
 
Fórmulas para encontrar o Vértice de Parábola
 
O vértice da parábola, é o ponto máximo(Vx, Vy) da parábola.
A seguir é mostrado as fórmulas para encontrar o Vx e o Vy.
 
Vx =  - b 
        2*a
 
Vy =  - Δ 
        4*a
 
 
Introdução à demonstração
 
Esta demonstração terá como foco um simples jogo de plataforma, aonde a câmera do jogo - na maioria dos jogos do gênero – se movimenta para esquerda ou direita. Há também vários jogos do tipo plataforma, em que a câmera se move para qualquer lado.
 
O objeto percorrerá as trajetórias parabólicas nesta demonstração será uma maça.
 
Basicamente, existem 2 situações quanto a movimentação do objeto em relação aos eixos:
 
1º Situação
 
O objeto percorre a parabólica, com os valores do Eixo X ascendente.
 
Veja na foto a seguir todas os possíveis sentidos que o objeto poderá seguir:
 
Foto 2 – Todos os possíveis sentidos que a maçã poderá seguir.
 
A maçã, será “renderizada” com origem no canto superior esquerdo; veja na foto acima que esse ponto de origem, vai coincidir com a origem dos possíveis sentidos que a maça pode percorrer. Será feito assim para facilitar o processo da demonstração e também dos cálculos.
 
Neste caso, como a câmera e o personagem mover-se-á, principalmente, para a direita ou esquerda. O valor do X será submetido na função f(x) definida no inicio deste artigo, ou seja, o valor de X será definido(na classe Game1) previamente de algum modo - esse modo será exposto mais abaixo – assim, o valor de Y será obtido e a maça vai obtendo as coordenadas necessários para obter um resultado visual semelhante a de uma parábola.
 
2º Situação
 
O objeto percorre a parabólica, com os valores do Eixo Y ascendente.
 
Se, por exemplo, você quisesse que a maçã percorresse o gráfico de uma parabólica, como mostra na foto abaixo:
 

Foto 3
 
Você só precisaria inverter as idéias. Com o Y já é conhecido e seu valor sempre será ascendente, você faria: y = ax² + bx + c, obtendo assim, o valor de X. 
 
 
Demonstrando
 
Você poderá baixar esta demonstração no arquivo .zip em anexo ao final deste artigo.
 
Essa demonstração será baseada na construção de um GameComponent que servirá para concentrar todos os cálculos da Função Quadrática a fim de oferecer ao sprite(maçã) o direito de percorrer uma trajetória parabólica.
 
Introdução à construção do GameComponent
 
Nesta demonstração, para o objeto percorrer a trajetória parabólica, será necessário fazer uma chamada ao Método Move.
 
Para ele mover-se, será necessário conhecer qual o Quadrante que ele deve mover-se. E também, o GameComponent precisará conhecer o objeto(sprite) que mover-se-á com o auxílio deste GameComponent. Os quadrantes serão acessados através da enumeração Quadrants.
 
Começando a construção do GameComponent
 
public class QuadraticEquationComponent : Microsoft.Xna.Framework.GameComponent
    {
        ///<summary>
        /// Places where the object can go where the Method Move is challed.
        ///</summary>
        public enum Quadrants
        {
            ///<summary>
            /// Northeast
            /// In this Quadrant, the X Axis is positive.
            /// In this Quadrant, the Y Axis is negative.
            ///</summary>
            First,
 
            ///<summary>
            /// Northwest
            /// In this Quadrant, the X Axis is Negative.
            /// In this Quadrant, the Y Axis is Negative.
            ///</summary>
            Second,
           
            ///<summary>
            /// Southwest
            /// In this Quadrant, the X Axis is Negative.
            /// In this Quadrant, the Y Axis is Positive.
            ///</summary>
            Third,
           
            ///<summary>
            /// Southeast
            /// In this Quadrant, the X Axis is Positive.
            /// In this Quadrant, the Y Axis is Positive.
            ///</summary>
            Fourth
        }
 
Observe a foto abaixo com a representação do local dos quadrantes:

Foto 4
 
A função quadrática é definida por: F(x) = Ax² + bx + C
 
Este GameComponent vai ficar responsável por gerenciar esses valores.
 
 
///<summary>
        /// Coefient A of the Equation: F(x) = Ax² + Bx + C
        ///</summary>
        private float a;
 
        ///<summary>
        /// Coefient B of the Equation: F(x) = Ax² + Bx + C
        ///</summary>
        private float b;
 
        /* F(x) = Ax² + Bx + C
         *
         * In our calculations,
         * We consider the C of the Equation above equals 0(zero).
         * */
 
        ///<summary>
        /// x of the Equation: F(x) = Ax² + Bx + C
        ///</summary>
        private float x;
 
        ///<summary>
        /// The result of the Equation: F(x) = Ax² + Bx + C
        /// F(x) = y
        ///</summary>
        private float y;
 
Também será definida a posição inicial e final do movimento. Conhecendo essas posições facilita muito os cálculos e ainda pode ter várias utilidades!!!
 
///<summary>
        /// It will be necessary to store the initial
        /// Position where the method Move is called.
        ///</summary>
        private Vector2 initialPosition;
 
        ///<summary>
        /// It will be calculated from the initialPosition + MaxPoint
        ///</summary>
        private Vector2 finalPosition;
 
O componente também vai precisar do Vértice da Parábola para efetuar os cálculos. Os coeficientes a(coeficiente angular) e b, são obtidos a partir do x e y do vértice.
 
///<summary>
        /// The vertice of the parabolic at the trajectory of the object.
        /// It´s will be used outside of this Component.
        ///</summary>
        public Vector2 Vertex;
 
        ///<summary>
        /// Vertex used to the calculations in this component.
        ///</summary>
        private Vector2 vertexToCalculations;
 
Nesta demonstração, o campo Vertex será informado na classe Game1.
 
O componente também vai guardar uma referência do objeto que efetuou o requerimento, através do método Move.
 
///<summary>
        /// The object that will go the parabolic.
        ///</summary>
        public ISprite sprite;
 
Quando o método Move for acionado, o componente precisará conhecer que o objeto fez o requerimento e nos próximos instantes(no método Update), os cálculos necessários deverão ser feitos. O Campo a seguir ficou com essa responsabilidade:
 
///<summary>
        /// Indicate that the move started
        ///</summary>
        private bool startMove;
 
O campo abaixo vai guardar o quadrante que foi escolhido no momento em que o Método Move foi chamado. Também servirá para uma eventual consulta de qual quadrante o objeto está movendo-se.
 
///<summary>
        /// Quadrant where the movement was invoked
        ///</summary>
        private Quadrants quadrant;
 
Por fim, temos um campo para ajudar em cálculos internos da posição do objeto que fez a requisição.
 
///<summary>
        /// This object will help the calculations with the sprite.Position
        ///</summary>
        private Vector2 positionHelper;
 
Propriedades do GameComponent
 
Uma propriedade revalente é a Vertex.
 
Quando na classe Game1, por exemplo, for informado a quantidade de pixels, nos eixos x e y, em relação ao ponto de origem do movimento(initialPosition) - esses valores de x e y, devem ser maior que 0(zero), ou seja, devem ser positivos. Entretanto, se você informar algum valor de x ou y negativo, a implementação do método setda propriedade Vertex, vai encarregar-se de deixar os valores negativos de x ou y, positivos. Isso é relevante pelo fato da implementação do Método Move, está partindo da premissa que os valores x e y do vertices estarem positivos.
 
Essa regra dos valores positivos foi determinada assim, para que a pessoa que vai utilizar o GameComponent, não tenha a preocupação em montar algortimo informando para direção o objeto deve ir; isso vai ficar por conta da enumeração(Quadrant) que será escolhida no momento em que o método Move for acionado.
 
public Vector2 Vertex
        {
            get { return this.vertex; }
 
            set
            {
                this.vertex = value;
 
               if (value.X < 0)
                {
                    this.vertex.X *= -1;
                }
 
                if (vertex.Y < 0)
                {
                    this.vertex.Y *= -1;
                }
            }
        }
As outras propriedades não foram implementadas da forma que mereçam considerações, isto é, os métodos get/set estão da forma mais simples possível. Confira-as no projeto liberado ao final deste artigo.
 
O método Move
 
O método vai receber como parâmetro:
 
  • O quadrante, já discutido anteriormente.
 
  • Uma referência para o objeto que fará a requisição.
 
///<summary>
        /// Indicate that the movement should be initialized. And also,
        /// the ambient to this component is prepared.
        ///</summary>
        ///<param name="_quadrant">A place that the object will go</param>
        ///<param name="_ISprite">The Object that will go the Parabolic at
        /// the quadrant specified in the field _quadrant</param>
        public void Move(Quadrants _quadrant, ISprite _ISprite)
        {
 
Quando o método Move for chamado, o movimento será iniciado.
 
this.startMove = true;
 
Iniciando os campos discutidos anteriormente:
 
            this.quadrant = _quadrant;
 
            this.sprite = _ISprite;
 
            this.initialPosition = this.sprite.Position;
 
O valores x e y do Vertex, nunca pode ser (0, 0).
 
 Não faz sentido continuar com os cálculos, se o Vertex, que vai indicar quantos pixels a partir da posição inicial do movimento estará o vértice da parábola, estar com o valor (0, 0). O processo será “abortado” neste momento. O código a seguir transmiti o que passado neste parágrafo:
 
if (Vertex == Vector2.Zero)
            {
                //The field MaxPoint can´t be like a Vector2.Zero!
                this.startMove = false;
                return;
            }
 
Para entender todo o código a seguir, reveja a foto 4.
 
            if (quadrant == Quadrants.First || quadrant == Quadrants.Fourth)
            {
                this.vertexToCalculations.X = +Vertex.X;
            }
            else
            {
                this.vertexToCalculations.X = -Vertex.X;
            }
 
            if (quadrant == Quadrants.First || quadrant == Quadrants.Second)
            {
                this.vertexToCalculations.Y = -Vertex.Y;
            }
            else
            {
                this.vertexToCalculations.Y = +Vertex.Y;
            }
 
 
Em especial, nesta demonistração o campo this.finalPosition.Y não será utilizado e nem terá utilidade alguma. Isso pode ser explicado, pelo que foi apresentado no tópico discutido anteriormente: Demonstrando uma aplicação da Função Quadrática

O campo this.vertexToCalculations.X
está sendo multiplicado por 2, para que a posição final, no eixo X, acabe quando o objeto terminar toda a trajetória parabólica.
 
Considerações:
 
O que foi feito aqui, é optitativo, só foi uma regra criada por mim. Você também pode determinar aonde o movimento vai acabar. Ou até mesmo pode optar por não existir um ponto em que o movimento acabe.
 
            this.finalPosition = new Vector2();
            this.finalPosition.X =
                (int)(this.vertexToCalculations.X * 2 + this.initialPosition.X);
            this.finalPosition.Y = this.initialPosition.Y;
 
            this.prepareCoefficients();
       }
 
O Método prepareCoefficients
 
void prepareCoefficients()
        {
            this.a = -this.vertexToCalculations.Y /
                (this.vertexToCalculations.X * this.vertexToCalculations.X);
 
            this.b = -this.vertexToCalculations.X * 2 * a;
        }
 
Entendendo as fórmulas mostradas acima no método prepareCoefficients
 
Tendo em mente que o x e y do vértice é dado. Podemos ter:
 
Vx =  - b 
         2*a
 
Ou também:
 
b = - 2 * Xv * a;
 
Agora falta encontrar o valor de a. Basta submeter a formula de b na equação em foco:
 
Lembre-se:
A coordenada (x, y) também pode ser encarada como (x, f(x)).
 
F(x) = ax² + bx
 
Yv = a * (Xv)² + b * Xv
 
Yv = a * (Xv)² - 2 * a * (Xv)²
 
Yv = - a * (Xv)²
 
a = - Yv
       (Xv)²
 
Tenha em mente que o valor de C = 0(zero).
 
A Equação Quadrática
 
public float quadraticEquation()
        {
            return a * (this.x * this.x) + (b * this.x);
        }
 
O Método Update do GameComponent
 
Só há alguma atualização se o startMove for true.
 
public override void Update(GameTime gameTime)
        {
            if (this.startMove)
            {
 
Na classe Game1, o x da posição do sprite vai aumentar ou diminuir dependendo do quadrante. Então, se por exemplo o movimento foi iniciado no ponto (10, 10) e o movimento no eixo X estiver ascendente. Temos:
 
11 – 10 = 1
 
Na próxima passagem do método Update:
 
12 – 10 = 2
 
Na próxima passagem do método Update:
 
13 – 10 = 3
 
E assim sucessivamente.
 
O que foi mostrado acima, se repercuti na 1º linha de código abaixo:
 
 
                this.x = this.sprite.Position.X - initialPosition.X;
 
                this.y = quadraticEquation();
 
Atualizando o valor do Y da posição do objeto que efetuou a requisição:
 
                this.positionHelper = this.sprite.Position;
 
                this.positionHelper.Y = this.initialPosition.Y + this.y;              
 
                this.sprite.Position = this.positionHelper;
 
Este bloco de código abaixo, será responsável por determinar o fim do movimento que finalizará com o y, praticamente o mesmo da posição inicial. Em virtude de alguns castsfeitos, o valor do y final não será exatamente igual, mas a perca é muito pequena e visualmente não vai haver nenhuma distorção.
 
Como dito anteriormente, essa foi uma regra criada por mim. Escolha a que mais se adequa em seu jogo!!!
 
                if (this.finalPosition.X == (int)this.sprite.Position.X)
                {
                    this.startMove = false;
                }
            }
 
            base.Update(gameTime);
        }
 
 
A Classe Game1
 
public class Game1 : Microsoft.Xna.Framework.Game
    {
        ...
 
        QuadraticEquationComponent myQuadraticEquationComponent;
 
        ///<summary>
        /// Object that will move on the parabolic movement.
        ///</summary>
        Sprite apple;
 
        ///<summary>
        /// This object will help the calculations with the apple.Position
        ///</summary>
        Vector2 positionHelper;
 
Nesta Demonstração, o X e Y do vértice será conhecido. Vamos definir um objeto Vector2D para encarregar-se em definir o vértice(x, y) da parábola:
 
 
 
        ///<summary>
        /// Vertex of the Parabolic. It´s the Max Point of the Parabolic.
        ///</summary>
        Vector2 vertex;
 
...
 
O método Initialize
 
protected override void Initialize()
        {
            this.vertex = new Vector2();
 
            //How many pixels from the actual Position will be the vertex of the Parabolic.
            this.vertex.X = 150;
            this.vertex.Y = 150;
 
            this.apple = new Sprite();
            this.apple.Position = new Vector2(350, 250);
 
            this.myQuadraticEquationComponent = new QuadraticEquationComponent(this);
 
            this.myQuadraticEquationComponent.Vertex = vertex;
 
            this.Components.Add(myQuadraticEquationComponent);
 
            base.Initialize();
        }
 
O método Update
 
protected override void Update(GameTime gameTime)
        {
            this.actualState = Keyboard.GetState();
 
Para toda a região abaixo, tenha em mente o que foi abordado no tópico:
Demonstrando uma aplicação da Função Quadrática - 1º Situação
 
A maneira em que você irá interagir nesta demonstração, será através de 4 teclas do teclado:
 
  • Keys.NumPad7
  • Keys.NumPad9
  • Keys.NumPad1
  • Keys.NumPad3
 
 
Keys.NumPad7 – A maçã percorrerá um “caminho parabólico” no sentido Noroeste.
 
Keys.NumPad9 – A maçã percorrerá um “caminho parabólico” no sentido Nordeste.
 
Keys.NumPad1 – A maçã percorrerá um “caminho parabólico” no sentido Sudoeste.
 
Keys.NumPad3 – A maçã percorrerá um “caminho parabólico” no sentido Sudeste.
 
 
            #region Region of Key Verification
 
            if (verifyKeyOfKeyboard(Keys.NumPad7))
            {
                this.myQuadraticEquationComponent.Move(
                    QuadraticEquationComponent.Quadrants.Second, this.apple);
            }
 
            if (verifyKeyOfKeyboard(Keys.NumPad9))
            {
                this.myQuadraticEquationComponent.Move(
                    QuadraticEquationComponent.Quadrants.First, this.apple);
            }
 
            if (verifyKeyOfKeyboard(Keys.NumPad1))
            {
                this.myQuadraticEquationComponent.Move(
                    QuadraticEquationComponent.Quadrants.Third, this.apple);
            }
 
            if (verifyKeyOfKeyboard(Keys.NumPad3))
            {
                this.myQuadraticEquationComponent.Move(
                    QuadraticEquationComponent.Quadrants.Fourth, this.apple);
            }
            #endregion
 
            /*
            If the method FuncaoQuadraticaComponent.Move was called,
            FuncaoQuadraticaComponent.StartMove is true and so, the object
            will move on a parabolic trajectory
             * */
            if (this.myQuadraticEquationComponent.StartMove)
            {
                this.positionHelper = this.apple.Position;
 
                if (this.myQuadraticEquationComponent.Quadrant ==
                    QuadraticEquationComponent.Quadrants.First
                    || this.myQuadraticEquationComponent.Quadrant ==
                    QuadraticEquationComponent.Quadrants.Fourth)
                {
                    this.positionHelper.X++;
                }
                else
                {
                    this.positionHelper.X--;
                }
 
                this.apple.Position = this.positionHelper;
 
                ...
 
            }
 
            ...
        }

A demonstração mostrada acima, pode ser baixada no arquivo anexo ao final do artigo.
 
 
Todas as imagens utilizadas neste artigo, foram criadas ou editadas com a ferramenta Microsoft Expression Design 2 Beta.
 
 
Dúvidas, Críticas e sugestões são bem-vindas.
 
Obrigado! 

Arquivos Anexados
QuadraticEquation - Demonstração Download
Sobre o Autor

lucianoJose
Luciano José
Administrador do SharpGames. Blog: lucianojosefj.spaces.live.com

Clique para avaliar:

Comentários
" Eu dei uma ajeitada no tamanho dos titulos. Se alguém tiver mais sugestões sobre qualquer coisa, pode comentar. Obrigado aew Bruno Evangelista por todas as sugestões :D"
Enviado por lucianoJose em 27/5/2008 0:17:18:
 
" ahh eu pensava que tinha me esquecido de aumentar, mas nao foi esquecimento nao. No texto acima os titulos que estão bem pequenos, estão relativamente grandes no editor. Só que tá aparecendo pra gente pequeno... Nao entendi isso... Deve ser algum problema do site como você mencionou."
Enviado por lucianoJose em 26/5/2008 22:35:00:
 
" opa Bruno. Sobre as fontes pequenas eu me esqueci de aumentar quando passei do word pro editor de texto do artigo xD Eu vou tentar dá uma melhorada nas fontes. Em relação a 2º sugestão: A definição que eu botei ficou semelhante a do livro que eu fiz a consulta da definição. Aonde o livro começa: Uma função f: R -> R chama-se quadrática quando existem números reais a, b, c... e logo abaixo ele expõe a função. A única informação que omiti da definição foi o f: R -> R o resto só foi um jogo de palavras... Mas nao vejo problema nenhum em mudar o texto com a sua sugestão. Ahh o autor do livro é Luiz Roberto Dante - Edição 1 - página 126 - Nome do Livro: Matematica contexto & aplicações. Eu vou montar 2 denifições: a atual com mais detalhes e essa que você sugeriu. Obrigado ae Bruno Evangelista!!!!"
Enviado por lucianoJose em 26/5/2008 22:10:13:
 
" Outra coisa. Você podia adicionar um motion blur a esse exemplo, ficaria muito massa ir vendo o rastro da maça. Um jeito muito fácil de fazer é só desenhar em um RenderTarget, e depois mesclar ele com o BackBuffer usando AlphaChannel."
Enviado por Bruno Evangelista em 26/5/2008 21:44:32:
 
" Oi Luciano, Achei muito legal o tutorial, só algumas sugestões: 1 - Parece que alguns títulos estão pequenos, não sei se essa formatação é problema do site. Exemplo: Introdução está grande, mas "Começando a construção do GameComponent" estác com fonte inferior a fonte normal do texto. 2 - Eu acho na definição da função quadrática você deveria inverter a ordem que colocou as coisas, exemplo: É uma função na forma: ax² + bx + c = 0, _disibledevent= 0. Acho que é isso, muito legal, valeu!"
Enviado por Bruno Evangelista em 26/5/2008 21:24:03:
 

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