C# OOP Tutorial Create a Butterfly Catching Game In Windows Forms and Visual Studio

Welcome to this tutorial from MOO ICT. In this tutorial we will be making a Butterfly Catching game in C# and Win Forms in Visual Studio. This tutorial will be a collection of all of the methods we have used in the previous tutorials and we will be able to spawn different GIF images using the Image and Image Animator class in C#. We will be using OOP programming principles to create a butterfly class and then use that class to create a Butterfly Object in the game and animate and move them using the timer component.  The butterflies we generate in the game will be completely transparent so when the images are overlapping you will be able to see the butterfly animation behind it. We will be creating this only using C# and Win Forms so no use for a games engine such as Unity or GODOT. Hope you enjoy this working on this project and lets get started.

Lesson Objectives-

  1. Create a Butterfly catching game using C#
  2. Create a Custom Butterfly Class in C#
  3. Dynamically Paint the images to the form
  4. Create Multiple Forms in Visual Studio. 1 for the title screen with the start button and 2 for the games window
  5. Design both forms using the visual studio components such as picture boxes, labels and timer
  6. Use public functions from the class to move the butterfly randomly across the screen and change directions when needed.
  7. Use variety of variables such as integers, float, string and Boolean.
  8. Since Image class doesn’t have a CLICK event we will need to make one ourselves to register when we click on a butterfly image
  9. Use Image animator class to animate the GIF images
  10. Use an Image array to randomly assignment a different colour butterfly
  11. Create custom functions to display game and restart the game after the time limit has been reached

 

Download the butterfly catching game images

Get C# Butterfly Catching Game Tutorial on GitHub

Butterfly Class Source Code Explained –

The Variables of Butterfly Class

public Image butterfly_image;
public int positionX;
public int positionY;
public int height;
public int width;
public int speedX, speedY, limit, moveLimit;
Random rand = new Random();

The Butterfly class represents a butterfly entity in a catching game. It encapsulates various properties and behaviors necessary for managing the butterfly’s movement and appearance.

Firstly, the butterfly_image variable holds the graphical representation of the butterfly. This image will be displayed on the game screen to represent the butterfly visually. The positionX and positionY variables determine the current position of the butterfly on the game screen, representing its horizontal and vertical coordinates, respectively. These coordinates are essential for rendering the butterfly at the correct location on the screen.

The height and width variables store the dimensions of the butterfly image. These dimensions are used for collision detection and rendering purposes to ensure that the butterfly is displayed correctly and interacts properly with other game elements.

The speedX and speedY variables control the velocity of the butterfly in the horizontal and vertical directions, respectively. These values determine how fast the butterfly moves across the screen. The limit variable sets a threshold for how frequently the butterfly changes its movement direction. It is initialized with a random value between 200 and 400, providing variability in the butterfly’s behavior.

The moveLimit variable keeps track of the remaining movement before the butterfly changes its direction. It starts with the same value as limit and decrements with each movement iteration. Once moveLimit reaches zero, the butterfly randomly selects new speeds for both horizontal and vertical directions, ensuring unpredictable movement patterns.

Lastly, the rand variable is an instance of the Random class used for generating random values. It’s utilized to initialize properties such as limit, speedX, and speedY, adding randomness to the butterfly’s behavior and enhancing the game’s variability.

Butterfly Class Constructor

public Butterfly()
{
    limit = rand.Next(200,400);
    moveLimit = limit;

    speedX = rand.Next(-5, 5);
    speedY = rand.Next(-5, 5);

    height = 43;
    width = 60;

}

The Butterfly class constructor initializes a new instance of the Butterfly class. Let’s break down the actions it performs:

  1. Random Initialization: The constructor creates an instance of the Random class named rand. This object is used to generate random numbers for various properties of the butterfly.
  2. Limit Initialization: The limit variable is assigned a random value between 200 and 400 using the rand.Next() method. This value represents the threshold for how often the butterfly changes its movement direction.
  3. Speed Initialization: The speedX and speedY variables are initialized with random values between -5 and 5. These values determine the initial velocity of the butterfly in both the horizontal and vertical directions.
  4. Size Initialization: The height and width variables are set to specific values (43 and 60, respectively) representing the height and width of the butterfly image. These values are likely predefined based on the graphical representation of the butterfly.

Overall, the constructor sets up the initial state of the Butterfly object by assigning random values to control its movement (speedX, speedY) and behavior (limit), as well as defining its graphical representation (height, width). These initializations provide variability and ensure that each instance of the Butterfly class starts with unique characteristics, contributing to the diversity and unpredictability of the butterflies in the game.

Move Butterfly Function

public void MoveButterfly()
  {
      moveLimit--;

      if (moveLimit < 0)
      {
          if (speedX < 0)
          {
              speedX = rand.Next(2, 5);
          }
          else
          {
              speedX = rand.Next(-5, -2);
          }
          if (speedY < 0)
          {
              speedY = rand.Next(2, 5);
          }
          else
          {
              speedY = rand.Next(-5, -2);
          }

          moveLimit = rand.Next(200, limit);
      }
  }

The MoveButterfly() function within the Butterfly class is responsible for updating the position and movement behavior of the butterfly. Let’s break down its functionality:

  1. Decrementing Move Limit: At the beginning of the function, the moveLimit variable is decremented by one. This variable tracks the remaining movement before the butterfly changes its direction.
  2. Direction Change Check: If moveLimit becomes less than zero (indicating that the butterfly has exhausted its current movement allowance), the function proceeds to change the butterfly’s movement direction.
  3. Random Direction Change: The function randomly selects new speeds for both horizontal and vertical movement (speedX and speedY). If the current speed is negative, the new speed is selected within a positive range to ensure a change in direction. Conversely, if the current speed is positive, the new speed is chosen within a negative range. This randomization adds unpredictability to the butterfly’s movement pattern.
  4. Reset Move Limit: After changing the movement direction, the moveLimit is reset to a new random value between 200 and the previously set limit. This action ensures that the butterfly maintains a dynamic movement pattern over time, with periodic changes in direction.

Overall, the MoveButterfly() function governs the movement behavior of the butterfly by periodically changing its direction in a randomized manner. This randomness adds challenge and variability to the butterfly’s movements in the game, making it more engaging for players trying to catch them.

Game Window Source Code Explained

The Variables of the game window

float timeLeft = 10f;
        int caught = 0;
        int spawnTime = 0;
        int spawnLimit = 30;
        List<Butterfly> butterfly_list = new List<Butterfly>();
        Random rand = new Random();

        Image[] butterfly_images = {Properties.Resources._01, Properties.Resources._02, Properties.Resources._03, Properties.Resources._04, Properties.Resources._05, Properties.Resources._06, Properties.Resources._07, Properties.Resources._08, Properties.Resources._09, Properties.Resources._10 };


The GameWindow class serves as the main window of the butterfly catching game. Let’s delve into its variables and their roles:

  1. timeLeft: This float variable keeps track of the remaining time for the game. Initialized to 10 seconds, it steadily decreases as the game progresses. Its value is displayed to the player to inform them of the time remaining to catch butterflies.
  2. caught: An integer variable representing the number of butterflies caught by the player. It increments each time the player successfully catches a butterfly and is displayed on the game interface to indicate the player’s progress.
  3. spawnTime: This variable acts as a countdown timer for spawning new butterflies. As the game progresses, it decrements, and when it reaches zero, a new butterfly is created, adding to the game’s challenge and pace.
  4. spawnLimit: Determines how often new butterflies are spawned. It’s set to 30, meaning a new butterfly spawns every 30 game ticks. Adjusting this value can alter the game’s difficulty by controlling the frequency of butterfly appearances.
  5. butterfly_list: A list that holds instances of the Butterfly class. Each element in this list represents an active butterfly in the game. It allows for the management and manipulation of individual butterflies during gameplay.
  6. rand: An instance of the Random class used for generating random numbers. It’s utilized to introduce randomness into various aspects of the game, such as selecting butterfly images and determining spawn positions, contributing to the game’s unpredictability and replay-ability.
  7. butterfly_images: An array containing different butterfly images. These images are drawn on the game window to visually represent the butterflies. Each time a butterfly is spawned, a random image from this array is assigned to it, adding visual variety to the game.

These variables collectively govern the game’s mechanics, including time management, butterfly spawning, player scoring, and graphical representation. They play essential roles in orchestrating the gameplay experience, ensuring a dynamic and immersive environment for players to enjoy.

Make Butterfly Function in the game window

private void MakeButterfly()
{
    int i = rand.Next(butterfly_images.Length);

    Butterfly newButterFly = new Butterfly();
    newButterFly.butterfly_image = butterfly_images[i];
    newButterFly.positionX = rand.Next(50, this.ClientSize.Width - 200);
    newButterFly.positionY = rand.Next(50, this.ClientSize.Height - 200);
    butterfly_list.Add(newButterFly);
    ImageAnimator.Animate(newButterFly.butterfly_image, this.OnFrameChangedHandler);
}

The MakeButterfly() function is responsible for creating a new butterfly and adding it to the game. Here’s a breakdown of its functionality:

  1. Random Image Selection: Inside the function, a random index (i) is generated using the rand.Next() method applied to the length of the butterfly_images array. This index is used to select a random butterfly image from the array.
  2. Butterfly Creation: A new instance of the Butterfly class is created and assigned to the newButterfly variable. This instance will represent the newly spawned butterfly in the game.
  3. Image and Position Assignment: The butterfly_image property of the newButterfly instance is set to the randomly selected butterfly image from the butterfly_images array. Additionally, the positionX and positionY properties are assigned random coordinates within the game window’s bounds using the rand.Next() method. These coordinates determine the initial position of the butterfly on the screen.
  4. Adding Butterfly to List: The newly created newButterfly instance is added to the butterfly_list, which keeps track of all active butterflies in the game. This ensures that the butterfly will be included in subsequent game logic and rendering processes.
  5. Animation Initiation: The ImageAnimator.Animate() method is called to initiate animation for the butterfly image. This method is passed the butterfly image and a handler (this.OnFrameChangedHandler) that will be invoked each time the image frame changes. This allows for smooth animation of the butterfly’s image during gameplay.

Overall, the MakeButterfly() function is essential for dynamically introducing new butterflies into the game at random positions with random images, contributing to the game’s variety and challenge.

private void FormPaintEvent(object sender, PaintEventArgs e)
{
    ImageAnimator.UpdateFrames();

    foreach (Butterfly butterfly in butterfly_list)
    {
        e.Graphics.DrawImage(butterfly.butterfly_image, butterfly.positionX, butterfly.positionY, butterfly.width, butterfly.height);
    }

}

The FormPaintEvent() method is an event handler for the Paint event of the GameWindow form. This event occurs whenever the form needs to be repainted, such as when it is first displayed, resized, or invalidated manually. Here’s a breakdown of its functionality:

  1. Parameter Explanation: The method takes two parameters: sender, which represents the object that raised the event (in this case, the GameWindow form), and e, which contains information about the event.
  2. Image Animation Update: The ImageAnimator.UpdateFrames() method is called at the beginning of the event handler. This method updates the frames of any animated images that have been registered for animation. In this context, it ensures that any animated butterfly images are updated before being drawn to the screen, ensuring smooth animation.
  3. Butterfly Rendering: The event handler iterates through each Butterfly object in the butterfly_list. For each butterfly, it uses the Graphics.DrawImage() method to draw its corresponding butterfly image onto the form’s graphics surface. The DrawImage() method requires the butterfly’s image, position (positionX and positionY), and dimensions (width and height) to specify where and how to draw the image.
  4. Graphics Object: The Graphics object used for drawing is provided by the PaintEventArgs parameter (e.Graphics). This object represents the drawing surface of the form and provides methods for rendering graphics, such as drawing images and shapes.

Overall, the FormPaintEvent() method ensures that the butterflies are correctly rendered on the game window whenever a repaint is required. It updates the animation frames of the butterfly images and draws them at their respective positions, providing the visual representation of the butterflies during gameplay.

private void OnFrameChangedHandler(object? sender, EventArgs e)
{
    this.Invalidate();
}

The OnFrameChangedHandler method serves as an event handler for the FrameChanged event, which occurs when the frame of an animated image changes. Here’s an explanation of its functionality:

  1. Parameter Explanation: The method takes two parameters: sender, which represents the object that raised the event, and e, which contains information about the event. In this case, sender would be the animated image whose frame has changed.
  2. Redrawing Trigger: Inside the method, Invalidate() is called on the GameWindow form. This method triggers a repaint of the form, causing the Paint event to be raised. As a result, the FormPaintEvent() method is executed, and all butterflies are redrawn on the form’s graphics surface.
  3. Animation Synchronization: By invalidating the form upon each frame change of an animated image, the OnFrameChangedHandler ensures that the form is repainted promptly to reflect any updates to the animated image’s frame. This synchronization results in smooth and continuous animation of the butterflies during gameplay.

In summary, the OnFrameChangedHandler method facilitates the synchronization of animated image frames with the game’s rendering process by triggering a repaint of the form whenever a frame change occurs. This ensures that the animated butterflies are redrawn promptly and appear fluid in their movement on the game window.

private void RestartGame()
{
    this.Invalidate();
    butterfly_list.Clear();
    caught = 0;
    timeLeft = 60f;
    spawnTime = 0;
    lblTime.Text = "Time: 00";
    lblCaught.Text = "Caught: 0";
    GameTimer.Start();
}

The RestartGame() function in the GameWindow class is responsible for resetting the game state to its initial conditions, allowing the player to start a new game after the current one has ended. Here’s how it works:

  1. Form Redrawing: The function starts by calling Invalidate(). This method triggers the Paint event, which leads to the redrawing of the form. This step ensures that any visual changes from the previous game, such as butterfly positions or the player’s score, are cleared from the game window.
  2. Game State Reset: Next, the function resets various game-related variables to their initial values. These variables include butterfly_list, caught, timeLeft, and spawnTime.
  3. User Interface Updates: Labels displaying the remaining time (lblTime) and the number of butterflies caught (lblCaught) are updated to reflect the new game state. The time is set back to 60 seconds, and the “Caught” count is reset to 0.
  4. Game Timer Restart: Finally, the GameTimer is restarted using the Start() method. This ensures that the game timer begins counting down from the initial time (60 seconds) and resumes spawning butterflies according to the specified spawn frequency.

Overall, the RestartGame() function provides the necessary functionality to reset the game and prepare it for a new round of gameplay. It clears the game window, resets game variables, updates the user interface, and restarts the game timer, ensuring a seamless transition between game sessions for the player.

private void GameOver()
{
    GameTimer.Stop();
    MessageBox.Show("Times Up!!, You've Caught " + caught + " butterflies. Click ok to try again.", "MOO says: ");
    RestartGame();
}

The GameOver() function in the GameWindow class is responsible for handling the end of the game when the time runs out. Let’s break down its functionality:

  1. Game Timer Stop: The first action taken in the function is to stop the game timer (GameTimer) using the Stop() method. This halts the countdown and prevents any further updates to the game state.
  2. Message Box Display: A message box is displayed to inform the player that the game is over. The message box contains information about the game’s end, such as the total number of butterflies caught (caught). This provides feedback to the player about their performance in the game.
  3. Game Reset: After the message box is dismissed, the RestartGame() function is called. This function resets the game state, clearing any remaining butterflies, resetting the score (caught) to zero, and restarting the game timer for a new round of gameplay.

Overall, the GameOver() function handles the necessary actions to gracefully conclude the game when the time limit is reached. It stops the game timer, informs the player of the game’s end, and initiates the process of resetting the game for another playthrough. This ensures a smooth transition between game sessions and allows the player to start a new game immediately after the previous one ends.

private void FormClickEvent(object sender, EventArgs e)
{
    foreach (Butterfly butterfly in butterfly_list.ToList())
    {
        MouseEventArgs mouse = (MouseEventArgs)e;

        if (mouse.X >= butterfly.positionX && mouse.Y >= butterfly.positionY && mouse.X < butterfly.positionX + butterfly.width && mouse.Y < butterfly.positionY + butterfly.height)
        {
            butterfly_list.Remove(butterfly);
            caught++;
        }
    }
}

The FormClickEvent event in the GameWindow class is an event handler that responds to mouse clicks on the game window. Here’s a breakdown of its functionality:

  1. Event Trigger: This event is triggered whenever the player clicks anywhere within the game window.
  2. Parameter Explanation: The event handler takes two parameters: sender, representing the object that raised the event (in this case, the GameWindow form), and e, which contains information about the event, specifically the details of the mouse click (MouseEventArgs).
  3. Iterating Through Butterflies: Inside the event handler, a loop iterates through each Butterfly object in the butterfly_list collection.
  4. Collision Detection: For each butterfly, the event handler checks whether the coordinates of the mouse click (mouse.X and mouse.Y) intersect with the bounding box of the butterfly’s image (butterfly.positionX, butterfly.positionY, butterfly.width, and butterfly.height). If there’s a collision, it indicates that the player has successfully clicked on the butterfly.
  5. Butterfly Removal: If a collision is detected, the clicked butterfly is removed from the butterfly_list using the Remove() method. This effectively removes the butterfly from the game.
  6. Score Update: Additionally, the caught variable is incremented to track the number of butterflies caught by the player. This score update is reflected in the game interface to inform the player of their progress.

By handling mouse clicks on the game window, the FormClickEvent event enables player interaction with the butterflies. When a player clicks on a butterfly, it triggers the removal of the butterfly from the game and updates the player’s score accordingly. This event mechanism adds an element of engagement and challenge to the gameplay, as players must aim and click accurately to catch butterflies and earn points.

private void GameTimerEvent(object sender, EventArgs e)
{
    lblTime.Text = "Time Left: " + timeLeft.ToString("#") + ".s";
    lblCaught.Text = "Caught: " + caught;
    timeLeft -= 0.03f;

    if (butterfly_list.Count < spawnLimit)
    {
        spawnTime--;

        if (spawnTime < 1)
        {
            MakeButterfly();
            spawnTime = spawnLimit;
        }
    }

    foreach (Butterfly butterfly in butterfly_list)
    {
        butterfly.MoveButterfly();

        butterfly.positionX += butterfly.speedX;

        if (butterfly.positionX < 0 || butterfly.positionX + butterfly.width  > this.ClientSize.Width)
        {
            butterfly.speedX = -butterfly.speedX;

            if (butterfly.positionX < 0)
            {
                butterfly.positionX = butterfly.positionX + 10;
            }
            else if (butterfly.positionX + butterfly.width > this.ClientSize.Width)
            {
                butterfly.positionX = butterfly.positionX - 10;
            }
        }

        butterfly.positionY += butterfly.speedY;

        if (butterfly.positionY < 0 || butterfly.positionY + butterfly.height > this.ClientSize.Height - 50 )
        {
            butterfly.speedY = -butterfly.speedY;

            if (butterfly.positionY < 0)
            {
                butterfly.positionY = butterfly.positionY + 10;
            }
            else if (butterfly.positionY + butterfly.height > this.ClientSize.Height - 50)
            {
                butterfly.positionY = butterfly.positionY - 10;
            }
        }


    }


    if (timeLeft < 1)
    {
        GameOver();
    }

    this.Invalidate();

}

The GameTimerEvent event in the GameWindow class is an event handler that responds to the tick of a timer, controlling the progression of the game. Here’s an explanation of its functionality:

Event Trigger: This event is triggered at regular intervals by a timer component (GameTimer) within the game window. The interval is set to a specific duration, typically a fraction of a second, controlling the frequency at which the event handler is executed.

Updating Time Display: At the beginning of the event handler, the remaining time (timeLeft) is decremented by a small fraction. This reflects the passage of time in the game and updates the time display (lblTime) to inform the player about the time remaining to play.

Butterfly Spawning Logic: The event handler includes logic for spawning new butterflies based on certain conditions. It checks if the number of butterflies currently active in the game (butterfly_list.Count) is less than a specified spawn limit (spawnLimit). If the condition is met, it decrements the spawn timer (spawnTime), and if it reaches zero, a new butterfly is created using the MakeButterfly() method.

Butterfly Movement: For each butterfly in the butterfly_list, the MoveButterfly() method is called to update its movement behavior based on the current game state.

Boundary Checking: After updating the movement of each butterfly, the event handler checks whether any butterfly has reached the boundaries of the game window. If a butterfly exceeds the boundaries, its movement direction is reversed, and its position is adjusted to prevent it from moving outside the window.

Time Check: The event handler also checks whether the time left (timeLeft) has reached zero, indicating that the game is over. If the time is up, the GameOver() method is called to handle the end of the game.

Graphics Update: Finally, the Invalidate() method is called to trigger the repainting of the game window. This ensures that any changes to the game state, such as butterfly movement or time updates, are reflected visually on the screen.

In summary, the GameTimerEvent event handler controls the main game loop, managing time progression, butterfly spawning and movement, boundary checking, and handling the end of the game. It orchestrates the dynamic aspects of the game and ensures that the game state is updated and displayed to the player at regular intervals.




Comment on this tutorial and let us know how you got on -