Adicione uma pasta a GameBase e chame-a de "Cameras", adicione um novo item a "Cameras", selecione Class e dê o nome de "CameraBase.cs". Como falamos anteriormente, essa classe vai ser a base para todas as outras então vamos alteralá para uma classe pública e do tipo abstrata, ela não poderá ser implementada. Adicone a linha using Microsoft.Xna.Framework; O código fica assim:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
namespace Tutorial1.GameBase.Cameras
{
public abstract class CameraBase
{
}
}
Adicione uma nova variável, to tipo float com o nove fovy:
float fovy;
Clique com o botão direito em "fovy" escolha o menu "refactor" -> "Encapsulate Field". Dê aceite e depois aplique sem mudar nada. O que o VS fez foi criar dois métodos para que possamos trabalhar melhor essa variável, depois de foat fovy; adicione mais uma variavel, float aspectRatio; faça o mesmo procedimento acima. Faça isso mais duas vezes, agora com as vaiáveis nearPlane e farPlane, ambas do tipo float.
Depois dessas 4 variaveis adicione outras duas do tipo Vector3, são elas position e target. Use o sis tema de encapsulate field mostrado anteriormente nas duas.
Até agora são variáveis que você já sabia que existiam, logo na sequência você vai adicionar mais 3 variáveis do tipo Vector3, headingVec, strafeVec e upVec. Utilize de novo o "Encapsulate field". Essas 3 variáveis são variáveis de orientação, headingVec é a direção entre a posição da câmera e a posição do alvo. o VecUp é utilizado para mostra onde fica o "pra cima" e o strafe é a perpendicular a headingVec e upVec.
Agora uma jogada para organizar o código, na linha abaixo de "Vector3 upVec;" adicione o seguinte código: "#region Propriedades" e no final do ultimo método coloque: "#endregion" suba até o "#region Propriedades" e bem do lado esquerdo, ao lado de uma linha verde, terá um quadrado com o simbolo de menos, clique nele e todas as propriedades vão se esconder dentro de uma caixa cinza escrito Propriedades, faça o mesmo mas agora pegando todas as variáveis e troque o nome de "Propriedades" para "Variaveis". No final vou fornecer o arquivo inteiro.
Logo na sequência adicone mais um bloco "#region Matrizes e Controles" e depois um "endregion", não feche ainda. Vamos adicionar mais 3 variáveis:
protected bool needUpdateProjection;
protected bool needUpdateFrustum;
protected bool needUpdateView;
Essas variáveis são protegidas, então nada de fora da classe vai poder acessar elas diretamente, são usadas quando elas devem sofrer algum tipo de manipulação antes de receber algum valor. Agora mais duas variáveis do tipo matrix:
protected Matrix viewMatrix;
protected Matrix projectionMatrix;
Já pode fechar o bloco de código Matrixes e Controles. Na sequência vamos criar mais dois métodos, esses métodos só terão o "get" dentro deles.
public Matrix Projection
{
get
{
if (needUpdateProjection) UpdateProjection();
return projectionMatrix;
}
}
public Matrix View
{
get
{
if (needUpdateView) UpdateView();
return viewMatrix;
}
}
Dentro de cada método temos putros métodos no primeiro o método UpdateProjection(); e no segundo o método UpdateView(); vamos cria-los agora:
protected virtual void UpdateProjection()
{
//Cria a matriz de visão da perspectiva
projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(45.0f), aspectRatio, 0.1f, 10.0f);
needUpdateProjection = false;
needUpdateFrustum = true;
}
protected virtual void UpdateView()
{
viewMatrix = Matrix.CreateLookAt(position, target, upVec);
needUpdateView = false;
needUpdateFrustum = true;
}
Nós ja os vimos anteriormente, agora mudamos nossas variavéis para que atualizem ou deixem de atualizar a nossa matriz View e Projection.
Agora vamos criar mais dois métodos para inicializar nossas variaávieis e definir corretamente nossas matrizes
//Atribui Projeção a Camera
public void SetPerspectiveFov(float fovy, float aspectratio, float nearPlane, float farPlane)
{
this.fovy = fovy;
this.aspectRatio = aspectratio;
this.nearPlane = nearPlane;
this.farPlane = farPlane;
needUpdateProjection = true;
}
//Configura a visão da Camera
public void SetLookAt(Vector3 cameraPos, Vector3 cameraTarget, Vector3 CameraUp)
{
this.position = cameraPos;
this.target = cameraTarget;
this.upVec = CameraUp;
//Calcura os eixos da Camera
headingVec = cameraTarget - cameraPos;
headingVec.Normalize();
upVec = CameraUp;
strafeVec = Vector3.Cross(headingVec, upVec);
needUpdateView = true;
}
E para finalizar mais dois métodos, um que atualiza tudo o que foi feito e outro para atualizar a posição da câmera, assim como target e fovy.
public virtual void Update(GameTime gametime)
{
UpdateView();
}
public virtual void addToCameraPosition(Vector3 vectorToAdd)
{
//A implementar
}
o método addToCameraPosition(Vector3 vectorToAdd) só tem uma linha //A implementar, o que significa que teremos que definilo na hora de criar os outros tipos de câmera. Meu jogo vai ter "mapas" de todo o mundo e em alguns lugares existem terremotos, tremores de terra, mas você pode usar as seguintes implementações pra tremer a câmera para representar que o personagem levou um tiro, ou ocorreu uma explosão... O arquivo completo.
No caso do terremoto é mais fácil mover a câmera do que o mundo todo. No incio das variáveis declare:
//Variáveis do Terremoto
protected static readonly Random random = new Random(); // Criará um número aleatório
private bool shaking = false; //Controle se já esta tremendo
private float shakeMagnitude; //Magnitude do Terremoto
private float shakeDuration; //Duração do Terremoto
private float shakeTimer; //Timer de terremotos
private Vector3 shakeOffset; //Vector de distancia
Dentro do método UpdateView adicione
if (shaking)
{
position += shakeOffset;
target += shakeOffset;
}
No método Update() insira:
// Se estamos tremendo
if (shaking)
{
// Ajusta nosso timer de acordo com o tempo passado
shakeTimer += (float)gametime.ElapsedGameTime.TotalSeconds;
// Se estivermos no limite de tempo desliga o terremoto
if (shakeTimer >= shakeDuration)
{
shaking = false;
shakeTimer = shakeDuration;
}
// Compute our progress in a [0, 1] range
float progress = shakeTimer / shakeDuration;
// Compute our magnitude based on our maximum value and our progress. This causes
// the shake to reduce in magnitude as time moves on, giving us a smooth transition
// back to being stationary. We use progress * progress to have a non-linear fall
// off of our magnitude. We could switch that with just progress if we want a linear
// fall off.
float magnitude = shakeMagnitude * (1f - (progress * progress));
// Generate a new offset vector with three random values and our magnitude
shakeOffset = new Vector3(NextFloat(), NextFloat(), NextFloat()) * magnitude;
}
Por fim adicione mais 2 métodos:
public void Shake(float magnitude, float duration)
{
// Estamos no meio de um terremoto
shaking = true;
// Guarda nossa magnitude e Duração
shakeMagnitude = magnitude;
shakeDuration = duration;
// Reseta nosso Timer
shakeTimer = 0f;
}
//Gera um número entre -1 e 1;
private float NextFloat()
{
return (float)random.NextDouble() * 2f - 1f;
}
E o código final fica assim:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
namespace Tutorial1.GameBase.Cameras
{
public abstract class CameraBase
{
#region Variáveis
//Variáveis do Terremoto
protected static readonly Random random = new Random(); // Criará um número aleatório
private bool shaking = false; //Controle se já esta tremendo
private float shakeMagnitude; //Magnitude do Terremoto
private float shakeDuration; //Duração do Terremoto
private float shakeTimer; //Timer de terremotos
private Vector3 shakeOffset; //Vector de distancia
//Parametros para a projeção
float fovy;
float aspectRatio;
float nearPlane;
float farPlane;
//posição e alvo
Vector3 position;
Vector3 target;
//Vetore de orientação
Vector3 headingVec;
Vector3 strafeVec;
Vector3 upVec;
#endregion
#region Propriedades
public Vector3 Target
{
get { return target; }
set { target = value; }
}
public Vector3 Position
{
get { return position; }
set { position = value; }
}
public float FarPlane
{
get { return farPlane; }
set { farPlane = value; }
}
public float NearPlane
{
get { return nearPlane; }
set { nearPlane = value; }
}
public float AspectRation
{
get { return aspectRatio; }
set { aspectRatio = value; }
}
public float Fovy
{
get { return fovy; }
set { fovy = value; }
}
#endregion
#region Matrizes e Controles
//Controles/Flags
protected bool needUpdateProjection;
protected bool needUpdateFrustum;
protected bool needUpdateView;
//Matrizes
protected Matrix viewMatrix;
protected Matrix projectionMatrix;
#endregion
#region Atualizar View e Projection
//Obtem Matriz de Projeção
public Matrix Projection
{
get
{
if (needUpdateProjection) UpdateProjection();
return projectionMatrix;
}
}
//Obtem a Matriz de Visão da CAmera
public Matrix View
{
get
{
if (needUpdateView) UpdateView();
return viewMatrix;
}
}
//Atualiza a matriz de projeção Perspectiva
protected virtual void UpdateProjection()
{
//Cria a matriz de visão da perspectiva
projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(45.0f), aspectRatio, 0.1f, 10.0f);
needUpdateProjection = false;
needUpdateFrustum = true;
}
//Atualiza a visão da Camera
protected virtual void UpdateView()
{
if (shaking)
{
position += shakeOffset;
target += shakeOffset;
}
viewMatrix = Matrix.CreateLookAt(position, target, upVec);
needUpdateView = false;
needUpdateFrustum = true;
}
#endregion
#region Defini nossos View e Projection
//Atribui Projeção a Camera
public void SetPerspectiveFov(float fovy, float aspectratio, float nearPlane, float farPlane)
{
this.fovy = fovy;
this.aspectRatio = aspectratio;
this.nearPlane = nearPlane;
this.farPlane = farPlane;
needUpdateProjection = true;
}
//Configura a visão da Camera
public void SetLookAt(Vector3 cameraPos, Vector3 cameraTarget, Vector3 CameraUp)
{
this.position = cameraPos;
this.target = cameraTarget;
this.upVec = CameraUp;
//Calcura os eixos da Camera
headingVec = cameraTarget - cameraPos;
headingVec.Normalize();
upVec = CameraUp;
strafeVec = Vector3.Cross(headingVec, upVec);
needUpdateView = true;
}
#endregion
#region Update, addToCameraPosition, Shack e nemDouble
//Atualiza todos os Nossos componentes
public virtual void Update(GameTime gametime)
{
// Se estamos tremendo
if (shaking)
{
// Ajusta nosso timer de acordo com o tempo passado
shakeTimer += (float)gametime.ElapsedGameTime.TotalSeconds;
// Se estivermos no limite de tempo desliga o terremoto
if (shakeTimer >= shakeDuration)
{
shaking = false;
shakeTimer = shakeDuration;
}
// Compute our progress in a [0, 1] range
float progress = shakeTimer / shakeDuration;
// Compute our magnitude based on our maximum value and our progress. This causes
// the shake to reduce in magnitude as time moves on, giving us a smooth transition
// back to being stationary. We use progress * progress to have a non-linear fall
// off of our magnitude. We could switch that with just progress if we want a linear
// fall off.
float magnitude = shakeMagnitude * (1f - (progress * progress));
// Generate a new offset vector with three random values and our magnitude
shakeOffset = new Vector3(NextFloat(), NextFloat(), NextFloat()) * magnitude;
}
UpdateView();
}
//Atualiza a nossa posição de câmera, assim como target, fovy...
public virtual void addToCameraPosition(Vector3 vectorToAdd)
{
//A implementar - Devemos implementar na hora de criar os diferentes tipos de câmera
}
public void Shake(float magnitude, float duration)
{
// Estamos no meio de um terremoto
shaking = true;
// Guarda nossa magnitude e Duração
shakeMagnitude = magnitude;
shakeDuration = duration;
// Reseta nosso Timer
shakeTimer = 0f;
}
//Gera um número entre -1 e 1;
private float NextFloat()
{
return (float)random.NextDouble() * 2f - 1f;
}
#endregion
}
}