Compartilhar via


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.

  1. Carimbo de hora – obtém um valor de carimbo de hora a ser usado por processadores de manipulação e inércia.

  2. 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.

  3. 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

Criar a classe Game1

Outros recursos

Manipulações e inércia