Creazione della classe GamePieceCreating the GamePiece Class

La classe GamePiece incapsula tutte le funzionalità necessarie per caricare l'immagine di una parte del gioco Microsoft XNA, tenere traccia dello stato del mouse in relazione alla parte del gioco, acquisire il mouse, fornire l'elaborazione delle manipolazioni e dell'inerzia e fornire funzionalità di rimbalzo quando la parte del gioco raggiunge i limiti del viewport.The GamePiece class encapsulates all the functionality required to load a Microsoft XNA game piece image, track the state of the mouse in relation to the game piece, capture the mouse, provide manipulation and inertia processing, and provide bouncing capability when the game piece reaches the limits of the view port.

Membri privatiPrivate Members

All'inizio della classe GamePiece vengono dichiarati diversi membri privati.At the top of the GamePiece class, several private members are declared.

#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

Proprietà pubblichePublic Properties

Tre di questi membri privati vengono esposti tramite proprietà pubbliche.Three of these private members are exposed through public properties. Le proprietà Scale e PieceColor consentono all'applicazione di specificare, rispettivamente, le proporzioni e il colore della parte.The Scale and PieceColor properties enable the application to specify the scale and the color of the piece, respectively. La proprietà Bounds viene esposta per consentire a una parte di usare i limiti di un'altra parte per eseguire il rendering, come nei casi in cui una parte deve sovrapporsi a un'altra.The Bounds property is exposed to enable one piece to use the bounds of another to render itself, such as when one piece should overlay another. Il codice seguente illustra la dichiarazione delle proprietà pubbliche.The following code shows the declaration of the public properties.

#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

Costruttore di classeClass Constructor

Il costruttore per la classe GamePiece accetta i parametri seguenti:The constructor for the GamePiece class accepts the following parameters:

  • Un tipo SpriteBatch.A SpriteBatch type. Il riferimento passato in questo caso viene assegnato al membro privato spriteBatch e usato per accedere al metodo SpriteBatch.Draw durante il rendering della parte del gioco.The reference passed here is assigned to the private member spriteBatch, and is used to access the SpriteBatch.Draw method when the game piece renders itself. La proprietà GraphicsDevice, poi, viene usata per creare l'oggetto Texture associato alla parte del gioco e per ottenere la dimensione del viewport, per individuare il momento in cui la parte del gioco rileva un limite della finestra e fa in modo che la parte possa rimbalzare.In addition, the GraphicsDevice property is used to create the Texture object associated with the game piece, and to obtain the size of the view port in order to detect when the game piece encounters a window boundary so that the piece can bounce.

  • Una stringa che specifica il nome del file dell'immagine da usare per la parte del gioco.A string that specifies the file name of the image to use for the game piece.

Il costruttore crea anche un oggetto ManipulationProcessor2D e un oggetto InertiaProcessor2D e stabilisce i gestori eventi per gli eventi corrispondenti.The constructor also creates a ManipulationProcessor2D object and an InertiaProcessor2D object, and establishes event handlers for their events.

Il codice seguente illustra il costruttore per la classe GamePiece.The following code shows the constructor for the GamePiece class.

#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

Acquisizione dell'input del mouseCapturing Mouse Input

Il metodo UpdateFromMouse è responsabile del rilevamento del momento in cui un pulsante del mouse viene premuto mentre il mouse è all'interno dei limiti della parte del gioco e del momento in cui il pulsante del mouse viene rilasciato.The UpdateFromMouse method is responsible for detecting when a mouse button is pressed while the mouse is within the boundaries of the game piece, and for detecting when the mouse button has been released.

Quando viene premuto il pulsante sinistro del mouse (mentre il mouse è all'interno dei limiti della parte), il metodo imposta un flag per indicare che questa parte del gioco ha acquisito il mouse e che l'elaborazione della manipolazione ha inizio.When the left mouse button is pressed (while the mouse is inside the piece boundaries), this method sets a flag to indicate that this game piece has captured the mouse, and begins manipulation processing.

L'elaborazione della manipolazione viene iniziata creando una matrice di oggetti Manipulator2D e passandoli all'oggetto ManipulationProcessor2D.Manipulation processing is started by creating an array of Manipulator2D objects and passing them to the ManipulationProcessor2D object. Questo fa in modo che il processore di manipolazione valuti i manipolatori (in questo caso un solo manipolatore) e generi eventi di manipolazione.This causes the manipulation processor to evaluate the manipulators (in this case a single manipulator), and raise manipulation events.

Inoltre, viene salvato il punto in cui si verifica il trascinamento.In addition, the point at which the drag is occurring is saved. Questo elemento viene usato in un secondo momento, durante l'evento Delta, per regolare i valori della conversione delta in modo che la parte del gioco si allinei dietro al punto del trascinamento.This is used later during the Delta event to adjust the delta translation values so that the game piece swings into line behind the drag point.

Infine, il metodo restituisce lo stato di acquisizione del mouse.Finally, this method returns the state of the mouse capture. Questo consente all'oggetto GamePieceCollection di gestire l'acquisizione quando sono presenti più parti del gioco.This enables the GamePieceCollection object to manage capturing when there are multiple game pieces.

Il codice seguente illustra il metodo UpdateFromMouse.The following code shows the UpdateFromMouse method.

#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

Elaborazione delle manipolazioniProcessing Manipulations

Quando inizia la manipolazione viene generato l'evento Started.When manipulation begins, the Started event is raised. Il gestore per questo evento interrompe l'elaborazione dell'inerzia, se in corso, e imposta il flag processInertia su false.The handler for this event stops inertia processing if it is occurring, and sets the processInertia flag to false.

#region OnManipulationStarted
private void OnManipulationStarted(object sender, Manipulation2DStartedEventArgs e)
{
    if (inertiaProcessor.IsRunning)
    {
        inertiaProcessor.Complete(Timestamp);
    }
    processInertia = false;
}
#endregion

Quando i valori associati alla manipolazione cambiano viene generato l'evento Delta.As the values associated with the manipulation change, the Delta event is raised. Il gestore per questo evento usa i valori delta passati negli argomenti dell'evento per apportare modifiche ai valori della posizione e della rotazione della parte del gioco.The handler for this event uses the delta values passed in the event arguments to make changes to the position and rotation values of the game piece.

#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 tutti i manipolatori (in questo caso un solo manipolatore) associati a una manipolazione vengono rimossi, il processore di manipolazione genera l'evento Completed.When all of the manipulators (in this case, a single manipulator) that are associated with a manipulation are removed, the manipulation processor raises the Completed event. Il gestore per questo evento inizia l'elaborazione dell'inerzia impostando le velocità iniziali del processore di inerzia su quelle indicate dagli argomenti dell'evento e imposta il flag processInertia su true.The handler for this event begins inertia processing by setting the initial velocities of the inertia processor to those reported by the event arguments, and sets the processInertia flag to 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

Elaborazione dell'inerziaProcessing Inertia

Mentre l'elaborazione dell'inerzia estrapola i nuovi valori per le velocità angolari e lineari, le coordinate della posizione (traslazione) e la rotazione, viene generato l'evento Delta.As inertia processing extrapolates new values for angular and linear velocities, position (translation) coordinates, and rotation, the Delta event is raised. Il gestore per questo evento usa i valori delta passati negli argomenti dell'evento per modificare la posizione e della rotazione della parte del gioco.The handler for this event uses the delta values passed in the event arguments to modify the position and rotation of the game piece.

Se le nuove coordinate comportano lo spostamento della parte del gioco oltre i limiti del viewport, la velocità di elaborazione dell'inerzia viene invertita.If the new coordinates result in the game piece moving beyond the view port boundaries, the velocity of the inertia processing is reversed. Ciò fa in modo che la parte del gioco rimbalzi in corrispondenza del limite del viewport che ha incontrato.This causes the game piece to bounce off the view port boundary that it has encountered.

Non è possibile modificare le proprietà di un oggetto InertiaProcessor2D mentre è in corso l'estrapolazione.You cannot change the properties of an InertiaProcessor2D object while it is running extrapolation. Pertanto, durante l'inversione della velocità X o Y, il gestore dell'evento interrompe innanzi tutto l'inerzia chiamando il metodo Complete.Therefore, when reversing the X or Y velocity, the event handler first stops inertia by calling the Complete method. Assegna quindi i nuovi valori di velocità iniziale perché corrispondano ai valori di velocità correnti (regolati per il comportamento di sponge) e imposta il flag processInertia su true.It then assigns the new initial velocity values to be the current velocity values (adjusted for sponge behavior), and sets the processInertia flag to true.

Il codice seguente illustra il gestore dell'evento Delta.The following code shows the event handler for the Delta event.

#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

Al termine dell'elaborazione dell'inerzia. il processore di inerzia genera l'evento Completed.When inertia processing is complete, the inertia processor raises the Completed event. Il gestore per questo evento imposta il flag processInertia su false.The handler for this event sets the processInertia flag to false.

#region OnInertiaCompleted
private void OnInertiaCompleted(object sender, Manipulation2DCompletedEventArgs e)
{
    processInertia = false;
}
#endregion

Nessun elemento della logica presentato finora consente l'estrapolazione dell'inerzia vera e propria.None of the logic presented so far actually causes inertia extrapolation to occur. A questo scopo viene usato il metodo ProcessInertia.This is accomplished in the ProcessInertia method. Questo metodo, che viene chiamato ripetutamente dal ciclo di aggiornamento del gioco (metodo Game.Update) verifica se il flag processInertia è impostato su true e, in tal caso, chiama il metodo Process.This method, which is called repeatedly from the game update loop (the Game.Update method) checks to see if the processInertia flag is set to true, and if so, calls the Process method. La chiamata di questo metodo fa in modo che si verifichi l'estrapolazione e genera l'evento Delta.Calling this method causes extrapolation to occur, and raises the Delta event.

#region ProcessInertia
public void ProcessInertia()
{
    if (processInertia)
    {
        inertiaProcessor.Process(Timestamp);
    }
}
#endregion

Il rendering effettivo della parte del gioco non viene eseguito finché non viene chiamato uno degli overload del metodo Draw.The game piece is not actually rendered until one of the Draw method overloads is called. Il primo overload di questo metodo viene chiamato ripetutamente dal ciclo di disegno del gioco (metodo Game.Draw).The first overload of this method is called repeatedly from the game draw loop (the Game.Draw method). Questo consente di eseguire il rendering della parte del gioco con la posizione, la rotazione e i fattori di scala correnti.This renders the game piece with the current position, rotation, and scale factors.

#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

Proprietà aggiuntiveAdditional Properties

La classe GamePiece usa tre proprietà private.Three private properties are used by the GamePiece class.

  1. Timestamp: ottiene un valore di timestamp che verrà usato dai processori di manipolazione e inerzia.Timestamp – Gets a timestamp value to be used by the manipulation and inertia processors.

  2. X: ottiene o imposta la coordinata X della parte del gioco.X – Gets or sets the X coordinate of the game piece. Durante l'impostazione, regolare i limiti usati per l'hit test e il percorso di rotazione del processore di manipolazione.When setting, adjusts the bounds used for hit testing and the pivot location of the manipulation processor.

  3. Y: ottiene o imposta la coordinata Y della parte del gioco.Y – Gets or sets the Y coordinate of the game piece. Durante l'impostazione, regolare i limiti usati per l'hit test e il percorso di rotazione del processore di manipolazione.When setting, adjusts the bounds used for hit testing and the pivot location of the manipulation processor.

#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

Vedere ancheSee Also

Modifiche e inerziaManipulations and Inertia
Uso delle modifiche e dell'inerzia in un'applicazione XNAUsing Manipulations and Inertia in an XNA Application
Creazione della classe GamePieceCollectionCreating the GamePieceCollection Class
Creazione della classe Game1Creating the Game1 Class