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: - Quando a > 0, a concavidade está voltada para cima.
- Quando a < 0, a concavidade está voltada para baixo.
- Quanto menor o valor de a, maior será a abertura da parábola.
- 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.
Dúvidas, Críticas e sugestões são bem-vindas. Obrigado! |