You want to create sprites, small graphics objects that display in front of a background and can detect collisions with other sprites.
Sample code folder: Chapter 10\ Sprites
The Sprite class presented here provides a very simple but useful starting point for creating sprites as complicated as you desire.
The Sprite class exposes public properties for its bitmap, position, and velocity; a method for drawing itself on a graphics surface; and a function that determines if another sprite is currently in collision with this one. This rudimentary sprite class provides a good start at understanding how sprites work. You can add new functionality to enhance your sprites as desired.
This class doesn't define exactly what the sprite will look like or even its size. It provides a public Bitmap property, which the calling program can fill with any desired image. Likewise, the location and velocity properties are very flexible and can take on any signed integer values. The calling program is responsible for setting these properties and for determining when they might change. The Draw() method uses the velocity values to update the position values, which minimizes the overhead in the calling program each time the sprite is redrawn.
Create a new Windows Forms application. Add a new class to the project named Sprite.vb, and use the following code for its definition:
Public Class Sprite Public SpriteImage As Bitmap Public X As Integer Public Y As Integer Public VelocityX As Integer Public VelocityY As Integer Public Sub Draw(ByVal g As Graphics) ' ----- Update the location. X += VelocityX Y += VelocityY ' ----- Draw the sprite. g.DrawImage(SpriteImage, X, Y) End Sub Public Function Collision(ByVal targetSprite As Sprite) _ As Boolean ' ----- See if two sprites overlap each other. On Error Resume Next Dim s1Left As Integer = X Dim s1Top As Integer = Y Dim s1Right As Integer = s1Left + SpriteImage.Width Dim s1Bottom As Integer = s1Top + SpriteImage.Height Dim s2Left As Integer = targetSprite.X Dim s2Top As Integer = targetSprite.Y Dim s2Right As Integer = s2Left + _ targetSprite.SpriteImage.Width Dim s2Bottom As Integer = s2Top + _ targetSprite.SpriteImage.Height ' ----- Compare the positions. If (s1Right < s2Left) Then Return False If (s1Bottom < s2Top) Then Return False If (s1Left > s2Right) Then Return False If (s1Top > s2Bottom) Then Return False ' ----- No collision. Return True End Function End Class
There are a lot of ways you can enhance this Sprite class. For example, you can add code to the Draw() method to create and maintain a bitmap image within the sprite object, perhaps creating a unique Sprite class for each type of sprite image. The collision-detection code shown here simply looks for overlapping rectangular areas; that is, if any parts of the bitmaps for the two sprites are touching, they are in collision. However, you might want to make the collision detection more sophisticated. For example, the code added next uses sprites with transparent corners, yet these transparent corners still count as collision areas. An enhanced version of collision detection might let the sprites overlap in the transparent areas, "bouncing" only when the visible portions touch each other.
To demonstrate the Sprite class, the following code creates two instances, draws colored solid circles with transparent backgrounds to define their bitmaps (that is, everything between the circle and the rectangular border is transparent), and sets them in motion against a background comprised of stripes. This background lets you see clearly how the transparent colors in the rectangular bitmaps make the sprites appear as solid circles only. These sprites and their bitmaps are created just once, as the form loads.
Return to Form1, and set its DoubleBuffered property to TRue. Add a Timer named Timer1. Now add the following code to the form's code template:
Private MySprites(1) As Sprite Private Sub SpriteDemo_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' ----- Prepare the sprites. Dim canvas As Graphics ' ----- Create the first sprite. MySprites(0) = New Sprite MySprites(0).X = 37 MySprites(0).Y = 37 MySprites(0).VelocityX = 2 MySprites(0).VelocityY = 1 MySprites(0).SpriteImage = New Bitmap(30, 30) canvas = Graphics.FromImage( MySprites(0).SpriteImage) canvas.Clear(Color.FromArgb(0, 0, 0, 0)) canvas.FillEllipse(Brushes.Red, 0, 0, 30, 30) canvas.Dispose( ) ' ----- Create the second sprite. MySprites(1) = New Sprite MySprites(1).X = 97 MySprites(1).Y = 57 MySprites(1).VelocityX = 1 MySprites(1).VelocityY = -2 MySprites(1).SpriteImage = New Bitmap(30, 30) canvas = Graphics.FromImage(MySprites(1).SpriteImage) canvas.Clear(Color.FromArgb(0, 0, 0, 0)) canvas.FillEllipse(Brushes.Green, 0, 0, 30, 30) canvas.Dispose( ) ' ----- Start the action. Timer1.Interval = 10 Timer1.Enabled = True End Sub
With each tick of the timer, the two sprites are each checked to see if they've come in contact with the walls of the form. If so, their appropriate velocity properties are reversed to cause them to bounce back into the display area of the form. A quick check is also made to see if the two sprites are in collision with each other. If they are, the velocity properties for both sprites are reversed, causing them to bounce away from each other. This simple action provides a starting point for creating more complex sprite interaction.
To see the animated sprites in action, add the following code to the form:
Private Sub Timer1_Tick(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Timer1.Tick ' ----- Trigger a redraw of the form. Me.Refresh( ) End Sub Private Sub SpriteDemo_Paint(ByVal sender As Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) _ Handles Me.Paint ' ----- Draw the sprites on a background. Dim counter As Integer ' ----- Redraw the striped background. For counter = 0 To Me.ClientSize.Width * 2 Step 20 e.Graphics.DrawLine(New Pen(Color.LightBlue, 5), _ counter, -5, counter - Me.ClientSize.Height - 10, _ Me.ClientSize.Height + 5) Next counter ' ----- Draw the sprites. MySprites(0).Draw(e.Graphics) MySprites(1).Draw(e.Graphics) ' ----- See if the sprites have hit each other. If MySprites(0).Collision(MySprites(1)) Then MySprites(0).VelocityX = -MySprites(0).VelocityX MySprites(0).VelocityY = -MySprites(0).VelocityY MySprites(1).VelocityX = -MySprites(1).VelocityX MySprites(1).VelocityY = -MySprites(1).VelocityY End If ' ----- Move the sprites for the next update. For counter = 0 To 1 If (MySprites(counter).X < 0) Then MySprites(counter).VelocityX = _ Math.Abs(MySprites(counter).VelocityX) End If If (MySprites(counter).Y) < 0 Then MySprites(counter).VelocityY = _ Math.Abs(MySprites(counter).VelocityY) End If If (MySprites(counter).X > _ (Me.ClientSize.Width - 30)) Then MySprites(counter).VelocityX = _ -Math.Abs(MySprites(counter).VelocityX) End If If (MySprites(counter).Y > _ (Me.ClientSize.Height - 30)) Then MySprites(counter).VelocityY = _ -Math.Abs(MySprites(counter).VelocityY) End If Next counter End Sub
Figure 10-10 shows the two sprites in action, just after bouncing away from each other. Notice that the bitmaps are created outside each sprite object, so the colors are easily set to something unique. In fact, the bitmaps could easily be made much more unique, with the sprites appearing in different sizes and shapes if desired.
Figure 10-10. These simple sprites drift over a background image, interacting with each other and with the walls of the form