Criar a classe GamePiece
O GamePiece classe encapsula toda a funcionalidade necessária para carregar uma imagem do jogo parte Microsoft XNA, controlar o estado do mouse em relação à parte do jogo, captura o mouse, fornecer manipulação e processamento de inércia e oferecer a capacidade de saltitante quando a parte do jogo atinge os limites de porta de visualização.
Membros particulares
Na parte superior do GamePiece classe, vários membros de particulares são declarados.
#region PrivateMembers
// The sprite batch used for drawing the game piece.
private SpriteBatch spriteBatch;
// The position of the game piece.
private Vector2 position;
// The origin used for rendering the game piece.
// Gets set to be the center of the piece.
private Vector2 origin;
// The texture for the piece.
private Texture2D texture;
// The bounds of the game piece. Used for hit testing.
private Rectangle bounds;
// The rotation of the game piece, in radians.
private float rotation;
// The scale, in percentage of the actual image size. 1.0 = 100%.
private float scale;
// The view port, used to detect when to bounce.
private Viewport viewport;
// The manipulation processor for this game piece.
private ManipulationProcessor2D manipulationProcessor;
// The inertia processor for this game piece.
private InertiaProcessor2D inertiaProcessor;
// Flag to indicate that inertia processing should start or continue.
private bool processInertia;
// Flag to indicate whether this piece has captured the mouse.
private bool isMouseCaptured;
// Used during manipulation to indicate where the drag is occurring.
private System.Windows.Point dragPoint;
// The color of the game piece.
private Color pieceColor;
// Represents how spongy the walls act when the piece bounces.
// Must be <= 1.0 (if greater than 1.0, the piece will accelerate on bounce)
// 1.0 = no slowdown during a bounce.
// 0.0 (or less) = won't bounce.
private float spongeFactor = 0.925f;
#endregion
Public Properties
Três desses membros particulares são expostas por meio de propriedades públicas. O escala e PieceColor propriedades permitem que o aplicativo especificar a escala e a cor da peça, respectivamente. O limites propriedade é exposta para habilitar uma peça usar os limites do outro para processar propriamente dito, como, por exemplo, quando uma peça deve sobrepor outro. O código a seguir mostra a declaração de propriedades públicas.
#region PublicProperties
public float Scale
{
get { return scale; }
set
{
scale = value;
bounds.Width = (int)(texture.Width * value);
bounds.Height = (int)(texture.Height * value);
// Setting X and Y (private properties) causes
// bounds.X and bounds.Y to adjust to the scale factor.
X = X;
Y = Y;
}
}
public Color PieceColor
{
get { return pieceColor; }
set { pieceColor = value; }
}
public Rectangle Bounds
{
get { return bounds; }
}
#endregion
Construtor de classe
O construtor para o GamePiece classe aceita os seguintes parâmetros:
A SpriteBatch tipo. A referência passada aqui é atribuída ao membro privado spriteBatche é usado para acessar o SpriteBatch.Draw método quando a parte do jogo processa a mesmo. Além disso, o GraphicsDevice propriedade é usada para criar o textura objeto associado com a parte do jogo e para obter o tamanho da porta de visualização para detectar quando a parte do jogo encontra um limite de janela para que a peça pode saltar.
Uma seqüência de caracteres que especifica o nome do arquivo da imagem a ser usado para a parte do jogo.
O construtor também cria um ManipulationProcessor2D objeto e um InertiaProcessor2D o objeto e estabelece a manipuladores de eventos para seus eventos.
O código a seguir mostra o construtor para o GamePiece classe.
#region Constructor
public GamePiece(SpriteBatch spriteBatch, string fileName)
{
// For brevity, omitting checking of null parameters.
this.spriteBatch = spriteBatch;
// Get the texture from the specified file.
texture = Texture2D.FromFile(spriteBatch.GraphicsDevice, fileName);
// Initial position set to 0,0.
position = new Vector2(0);
// Set the origin to be the center of the texture.
origin = new Vector2(texture.Width / 2.0f, texture.Height / 2.0f);
// Set bounds. bounds.X and bounds.Y are set as the position or scale changes.
bounds = new Rectangle(0, 0, texture.Width, texture.Height);
// Create manipulation processor.
Manipulations2D enabledManipulations =
Manipulations2D.Translate | Manipulations2D.Rotate;
manipulationProcessor = new ManipulationProcessor2D(enabledManipulations);
manipulationProcessor.Pivot = new ManipulationPivot2D();
manipulationProcessor.Pivot.Radius = texture.Width / 2;
manipulationProcessor.MinimumScaleRotateRadius = 10.0f;
manipulationProcessor.Started += OnManipulationStarted;
manipulationProcessor.Delta += OnManipulationDelta;
manipulationProcessor.Completed += OnManipulationCompleted;
// Create inertia processor.
inertiaProcessor = new InertiaProcessor2D();
inertiaProcessor.Delta += OnInertiaDelta;
inertiaProcessor.Completed += OnInertiaCompleted;
inertiaProcessor.TranslationBehavior.DesiredDeceleration = 0.0001F;
inertiaProcessor.RotationBehavior.DesiredDeceleration = 1e-6F;
inertiaProcessor.ExpansionBehavior.DesiredDeceleration = 0.0001F;
// Save the view port. Used to detect when the piece needs to bounce.
viewport = spriteBatch.GraphicsDevice.Viewport;
// Set the piece in a random location.
Random random = new Random((int)Timestamp);
X = random.Next(viewport.Width);
Y = random.Next(viewport.Height);
// Set a random orientation.
rotation = (float)(random.NextDouble() * Math.PI * 2.0);
dragPoint = new System.Windows.Point(double.NaN, double.NaN);
pieceColor = Color.White;
// Set scale to normal (100%)
Scale = 1.0f;
}
#endregion
Captura da entrada do Mouse
O UpdateFromMouse método é responsável por detectar quando um botão do mouse é pressionado enquanto o mouse está dentro dos limites do jogo material e para detectar quando o botão do mouse foi lançado.
Quando o botão esquerdo do mouse é pressionado (enquanto o mouse está dentro dos limites de informação), este método define um sinalizador para indicar que essa parte do jogo tenha capturado o mouse e começa o processamento de manipulação.
Início do processamento de manipulação, criando uma matriz de Manipulator2D de objetos e transmiti-los para o ManipulationProcessor2D objeto. Isso faz com que o processador de manipulação avaliar as manipulators (no caso um único manipulator) e manipulação de eventos.
Além disso, o ponto em que o arrasto está ocorrendo é salvo. Isso é usado mais tarde durante a Delta evento para ajustar os valores de conversão de delta, para que a peça de jogo se desloca em linha atrás do ponto de arrastar.
Finalmente, este método retorna o estado de captura do mouse. Isso permite que o GamePieceCollection o objeto para gerenciar a captura quando há várias partes de jogos.
O código a seguir mostra a UpdateFromMouse método.
#region UpdateFromMouse
public bool UpdateFromMouse(MouseState mouseState)
{
if (mouseState.LeftButton == ButtonState.Released)
{
if (isMouseCaptured)
{
manipulationProcessor.CompleteManipulation(Timestamp);
}
isMouseCaptured = false;
}
if (isMouseCaptured ||
(mouseState.LeftButton == ButtonState.Pressed &&
bounds.Contains(mouseState.X, mouseState.Y)))
{
isMouseCaptured = true;
Manipulator2D[] manipulators = new Manipulator2D[]
{
new Manipulator2D(0, mouseState.X, mouseState.Y)
};
dragPoint.X = mouseState.X;
dragPoint.Y = mouseState.Y;
manipulationProcessor.ProcessManipulators(Timestamp, manipulators);
}
// If the right button is pressed, stop the piece and move it to the center.
if (mouseState.RightButton == ButtonState.Pressed)
{
processInertia = false;
X = viewport.Width / 2;
Y = viewport.Height / 2;
rotation = 0;
}
return isMouseCaptured;
}
#endregion
Processamento de manipulações
Quando começa a manipulação, o Started evento é gerado. O manipulador para este evento interrompe o processamento de inércia se ele está ocorrendo e define o processInertia sinalizador para false.
#region OnManipulationStarted
private void OnManipulationStarted(object sender, Manipulation2DStartedEventArgs e)
{
if (inertiaProcessor.IsRunning)
{
inertiaProcessor.Complete(Timestamp);
}
processInertia = false;
}
#endregion
Como os valores associados com a alteração de manipulação de Delta evento é gerado. O manipulador para este evento usa os valores de delta, além de eventos passados argumentos para fazer alterações para os valores de posição e rotação do material de jogo.
#region OnManipulationDelta
private void OnManipulationDelta(object sender, Manipulation2DDeltaEventArgs e)
{
//// Adjust the position and rotation of the game piece.
float deltaX = e.Delta.TranslationX;
float deltaY = e.Delta.TranslationY;
if (dragPoint.X != double.NaN || dragPoint.Y != double.NaN)
{
// Single-manipulator-drag-rotate mode. Adjust for drag / rotation
System.Windows.Point center = new System.Windows.Point(position.X, position.Y);
System.Windows.Vector toCenter = center - dragPoint;
double sin = Math.Sin(e.Delta.Rotation);
double cos = Math.Cos(e.Delta.Rotation);
System.Windows.Vector rotatedToCenter =
new System.Windows.Vector(
toCenter.X * cos - toCenter.Y * sin,
toCenter.X * sin + toCenter.Y * cos);
System.Windows.Vector shift = rotatedToCenter - toCenter;
deltaX += (float)shift.X;
deltaY += (float)shift.Y;
}
X += deltaX;
Y += deltaY;
rotation += e.Delta.Rotation;
}
#endregion
Quando todos os manipulators (no caso, um único manipulator) que estão associados uma manipulação são removidos, o processador de manipulação eleva o Completed de evento. O manipulador para este evento começa definindo o sprite inicial do processador inércia aos relatados pelos argumentos de evento de processamento de inércia e define o processInertia sinalizador para true.
#region OnManipulationCompleted
private void OnManipulationCompleted(object sender, Manipulation2DCompletedEventArgs e)
{
inertiaProcessor.TranslationBehavior.InitialVelocityX = e.Velocities.LinearVelocityX;
inertiaProcessor.TranslationBehavior.InitialVelocityY = e.Velocities.LinearVelocityY;
inertiaProcessor.RotationBehavior.InitialVelocity = e.Velocities.AngularVelocity;
processInertia = true;
}
#endregion
Inércia de processamento
Como o processamento da inércia extrapolates novos valores para o sprite angular e linear, coordenadas de posição (conversão) e rotação, o Delta evento é gerado. O manipulador para este evento usa os valores de delta, além de eventos passados argumentos para modificar a posição e a rotação da parte do jogo.
Se as novas coordenadas gerar a peça jogo movendo além dos limites de porta de visualização, a velocidade do processamento inércia é invertida. Isso faz com que a peça de jogo para salte fora do limite de porta de modo que ele encontrou.
Você não pode alterar as propriedades de um InertiaProcessor2D objeto enquanto ele é executado extrapolação. Portanto, ao inverter a velocidade de x ou Y, o manipulador de eventos pára inércia chamando o Complete() método. Ele atribui novos valores de velocidade inicial para os valores atuais do velocity (ajustada para o comportamento de esponja) e define o processInertia sinalizador para true.
O código a seguir mostra um manipulador de eventos para o Delta de evento.
#region OnInertiaDelta
private void OnInertiaDelta(object sender, Manipulation2DDeltaEventArgs e)
{
// Adjust the position of the game piece.
X += e.Delta.TranslationX;
Y += e.Delta.TranslationY;
rotation += e.Delta.Rotation;
// Check to see if the piece has hit the edge of the view port.
bool reverseX = false;
bool reverseY = false;
if (X > viewport.Width)
{
reverseX = true;
X = viewport.Width;
}
else if (X < viewport.X)
{
reverseX = true;
X = viewport.X;
}
if (Y > viewport.Height)
{
reverseY = true;
Y = viewport.Height;
}
else if (Y < viewport.Y)
{
reverseY = true;
Y = viewport.Y;
}
if (reverseX || reverseY)
{
// Get the current velocities, reversing as needed.
// If reversing, apply sponge factor to slow the piece slightly.
float velocityX = e.Velocities.LinearVelocityX * ((reverseX) ? -spongeFactor : 1.0f);
float velocityY = e.Velocities.LinearVelocityY * ((reverseY) ? -spongeFactor : 1.0f);
// Must stop inertia processing before changing parameters.
if (inertiaProcessor.IsRunning)
{
inertiaProcessor.Complete(Timestamp);
}
// Assign the new velocities.
inertiaProcessor.TranslationBehavior.InitialVelocityX = velocityX;
inertiaProcessor.TranslationBehavior.InitialVelocityY = velocityY;
// Set flag so that inertia processing will continue.
processInertia = true;
}
}
#endregion
Quando o processamento da inércia estiver concluído, o processador inércia eleva o Completed de evento. O manipulador para este evento define a processInertia sinalizador para false.
#region OnInertiaCompleted
private void OnInertiaCompleted(object sender, Manipulation2DCompletedEventArgs e)
{
processInertia = false;
}
#endregion
Nenhuma parte da lógica apresentada até o momento realmente faz extrapolação inércia ocorra. Isso é realizado da ProcessInertia método. Esse método, é chamado repetidamente a partir do loop de atualização de jogo (o Game.Update método) verifica se a processInertia sinalizador estiver definido como truee nesse caso, chama o Process() método. Chamar esse método extrapolação de causas para ocorrer e gera a Delta de evento.
#region ProcessInertia
public void ProcessInertia()
{
if (processInertia)
{
inertiaProcessor.Process(Timestamp);
}
}
#endregion
A parte do jogo não é processada, na verdade, até que uma das sobrecargas de método Draw é chamada. A primeira sobrecarga desse método é chamada repetidamente a partir do loop de empate (o Game.Draw método). Isso torna a informação de jogo com fatores de escala, rotação e posição atual.
#region Draw
public void Draw()
{
spriteBatch.Draw(
texture, position,
null, pieceColor, rotation,
origin, scale,
SpriteEffects.None, 1.0f);
}
public void Draw(Rectangle bounds)
{
spriteBatch.Draw(texture, bounds, pieceColor);
}
#endregion
Propriedades adicionais
Três propriedades particulares são usadas pela GamePiece classe.
Carimbo de hora – obtém um valor de carimbo de hora a ser usado por processadores de manipulação e inércia.
X – obtém ou define a coordenada x do que a parte do jogo. Ao definir, ajusta os limites usados para teste de acertos e a localização dinâmica do processador de manipulação.
Y – obtém ou define a coordenada y do que a parte do jogo. Ao definir, ajusta os limites usados para teste de acertos e a localização dinâmica do processador de manipulação.
#region PrivateProperties
private long Timestamp
{
get
{
// Get timestamp in 100-nanosecond units.
double nanosecondsPerTick = 1000000000.0 / System.Diagnostics.Stopwatch.Frequency;
return (long)(System.Diagnostics.Stopwatch.GetTimestamp() / nanosecondsPerTick / 100.0);
}
}
private float X
{
get { return position.X; }
set
{
position.X = value;
manipulationProcessor.Pivot.X = value;
bounds.X = (int)(position.X - (origin.X * scale));
}
}
private float Y
{
get { return position.Y; }
set
{
position.Y = value;
manipulationProcessor.Pivot.Y = value;
bounds.Y = (int)(position.Y - (origin.Y * scale));
}
}
#endregion
Consulte também
Conceitos
Usando as manipulações e inércia em um aplicativo XNA
Criar a classe GamePieceCollection