Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
La classe GamePiece encapsule toutes les fonctionnalités requises pour charger une image de pièce de jeu Microsoft XNA, suivre l'état de la souris par rapport à la pièce de jeu, capturer la souris, fournir une manipulation et un traitement de l'inertie, et permettre à la pièce de jeu de rebondir lorsqu'elle atteint les limites du port d'affichage.
Membres privés
En haut de la classe GamePiece, plusieurs membres privés sont déclarés.
#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
Propriétés publiques
Trois de ces membres privés sont exposés via des propriétés publiques. Les propriétés Scale et PieceColor permettent respectivement à l'application de spécifier l'échelle et la couleur de la pièce. La propriété Bounds est exposée pour permettre à une pièce d'utiliser les limites d'une autre pour être rendue, comme lorsqu'une pièce doit se superposer à une autre. Le code suivant affiche la déclaration des propriétés publiques.
#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
Constructeurs de classe
Le constructeur pour la classe GamePiece accepte les paramètres suivants :
Un type SpriteBatch (page éventuellement en anglais). La référence passée ici est assignée au membre privé spriteBatch et permet d'accéder à la méthode SpriteBatch.Draw (page éventuellement en anglais) lorsque la pièce de jeu est rendue. De plus, la propriété GraphicsDevice (page éventuellement en anglais) est utilisée pour créer l'objet Texture (page éventuellement en anglais) associé à la pièce de jeu, mais également obtenir la taille du port d'affichage pour détecter lorsque la pièce de jeu rencontre une limite de fenêtre et la faire rebondir.
Chaîne qui spécifie le nom de fichier de l'image à utiliser pour la pièce de jeu.
Le constructeur crée également un objet ManipulationProcessor2D et un objet InertiaProcessor2D, et établit des gestionnaires d'événements pour leurs événements.
Le code suivant montre le constructeur pour la classe GamePiece.
#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
Capture de l'entrée de la souris
La méthode UpdateFromMouse est chargée de détecter lorsque l'utilisateur appuie sur un bouton de la souris pendant que la souris se trouve dans les limites de la pièce de jeu, et pour détecter lorsqu'il relâche le bouton de la souris.
Lorsque l'utilisateur appuie sur le bouton gauche de la souris (tandis que la souris se trouve dans les limites de la pièce), cette méthode définit un indicateur pour signaler que cette pièce de jeu a capturé la souris, et commence la manipulation.
La manipulation est démarrée en créant un tableau d'objets Manipulator2D et en les passant à l'objet ManipulationProcessor2D. Le processeur de manipulation évalue alors les manipulateurs (ici, un seul manipulateur) et déclenche des événements de manipulation.
De plus, le point où se produit le glisser-déplacer est enregistré. Cette opération est utilisée ultérieurement lors de l'événement Delta pour ajuster les valeurs de translation de delta afin que la pièce de jeu s'aligne derrière le point de glissement.
Enfin, cette méthode retourne l'état de la capture de la souris. Cela permet à l'objet GamePieceCollection de gérer la capture lorsqu'il existe plusieurs pièces de jeu.
Le code suivant montre la méthode UpdateFromMouse.
#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
Traitement des manipulations
Au début d'une manipulation, l'événement Started est déclenché. Le gestionnaire de cet événement arrête le traitement de l'inertie, le cas échéant, et affecte la valeur false à l'indicateur processInertia.
#region OnManipulationStarted
private void OnManipulationStarted(object sender, Manipulation2DStartedEventArgs e)
{
if (inertiaProcessor.IsRunning)
{
inertiaProcessor.Complete(Timestamp);
}
processInertia = false;
}
#endregion
Lorsque les valeurs associées à la manipulation sont modifiées, l'événement Delta est déclenché. Le gestionnaire de cet événement utilise les valeurs delta passées aux arguments de l'événement pour modifier les valeurs de rotation et de position de la pièce de jeu.
#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
Lorsque tous les manipulateurs (ici, un seul manipulateur) associés à une manipulation sont supprimés, le processeur de manipulation déclenche l'événement Completed. Le gestionnaire de cet événement commence le traitement de l'inertie en attribuant les rapidités initiales indiquées par les arguments de l'événement à celles du processeur d'inertie et affecte la valeur true à l'indicateur processInertia.
#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
Traitement de l'inertie
Lorsque le traitement de l'inertie extrapole de nouvelles valeurs pour les rapidités angulaires et linéaires, les coordonnées de position (translation) et la rotation, l'événement Delta est déclenché. Le gestionnaire de cet événement utilise les valeurs delta passées aux arguments de l'événement pour modifier les valeurs de rotation et de position de la pièce de jeu.
Si la pièce de jeu sort des limites du port d'affichage en raison des nouvelles coordonnées, la rapidité du traitement de l'inertie est inversée. Ainsi, la pièce de jeu rebondit sur la limite du port d'affichage qu'elle a rencontrée.
Vous ne pouvez pas modifier les propriétés d'un objet InertiaProcessor2D pendant qu'il exécute l'extrapolation. Par conséquent, lors de l'inversion de la rapidité X ou Y, le gestionnaire d'événements arrête d'abord l'inertie en appelant la méthode Complete(). Il assigne ensuite les nouvelles valeurs de rapidités initiales en tant que valeurs de rapidités actuelles (ajustées pour un comportement d'éponge) et affecte la valeur true à l'indicateur processInertia.
Le code suivant montre le gestionnaire d'événements pour l'événement Delta.
#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
Lorsque le traitement de l'inertie est terminé, le processeur d'inertie déclenche l'événement Completed. Le gestionnaire de cet événement affecte la valeur false à l'indicateur processInertia.
#region OnInertiaCompleted
private void OnInertiaCompleted(object sender, Manipulation2DCompletedEventArgs e)
{
processInertia = false;
}
#endregion
La logique présentée jusqu'à présent n'entraîne en aucun cas une extrapolation de l'inertie. Cette opération est accomplie dans la méthode ProcessInertia. Cette méthode, qui est appelée plusieurs fois à partir de la boucle de mise à jour du jeu (méthode Game.Update (page éventuellement en anglais)), vérifie si l'indicateur processInertia a la valeur true, et le cas échéant, appelle la méthode Process(). L'appel de cette méthode entraîne l'extrapolation et déclenche l'événement Delta.
#region ProcessInertia
public void ProcessInertia()
{
if (processInertia)
{
inertiaProcessor.Process(Timestamp);
}
}
#endregion
En réalité, la pièce de jeu n'est rendue qu'au moment où une des surcharges de la méthode Draw est appelée. La première surcharge de cette méthode est appelée plusieurs fois à partir de la boucle de dessin du jeu (méthode Game.Draw (page éventuellement en anglais)). Elle assure le rendu de la pièce de jeu avec les facteurs actuels de position, de rotation et d'échelle.
#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
Autres propriétés
Trois propriétés privées sont utilisées par la classe GamePiece.
Timestamp – Obtient une valeur timestamp utilisée par les processeurs d'inertie et de manipulation.
X – Obtient ou définit la coordonnée X de la pièce de jeu. Lors de la configuration, règle les limites utilisées pour les tests de positionnement et l'emplacement pivot du processeur de manipulation.
Y – Obtient ou définit la coordonnée Y de la pièce de jeu. Lors de la configuration, règle les limites utilisées pour les tests de positionnement et l'emplacement pivot du processeur de manipulation.
#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
Voir aussi
Concepts
Utilisation de manipulations et de l'inertie dans une application XNA
Création de la classe GamePieceCollection