C# Space Shooter Game

Originally published at: C# Space Shooter Game – BrainPad

Project Overview

C# Space Shooter with BrainPad Pulse

Embark on an exciting journey into the Games domain with our C# Space Shooter, seamlessly integrated with the BrainPad Pulse microcomputer. This project showcases the compatibility between C# programming and BrainPad microcomputers, bringing the game development field’s timeless excitement with the electronic circuits and microcomputers.

How It Works

the spaceship will move on the X-axis, navigating randomly while taking on three aliens. Armed with one bullet at a time, you must shoot the aliens to prevent them from reaching your space. Be cautious; each alien breach costs you one of your three lives.

Hardware Requirements

The Enjoy a straightforward setup with minimal hardware requirements – all you need is a BrainPad Pulse and a standard USB cable for a quick and easy connection.

Software Requirements

Executing this project successfully requires installing the DUE Link library, from NuGet.org so go to Package Manager Console then using the command:

install-package GHIElectronics.DUELink

The provided library is implemented in C#, but the user can use any .NET system, such as Visual Basic, Visual Studio.

Code Overview

The first C# module named Sprite.cs within the SpriteMasterGame namespace. Let’s break down the key components of this class:

Properties:

  • Name: A property representing the name of the sprite.
  • X and Y: Properties representing the coordinates of the sprite on a 2D plane.
  • Height and Width: Properties representing the height and width of the sprite.
  • Image: An array of integers representing the image data of the sprite.
  • Character: A property representing a character associated with the sprite.

Constructors:

  • Sprite(): Default constructor with no parameters.
  • Sprite(string name, int x, int y, int height, int width, int[] image): A constructor with parameters to initialize the Name, X, Y, Height, Width, and Image properties.
  • Sprite(string name, int x, int y, int height, int width): Another constructor with parameters to initialize Name, X, Y, Height, and Width properties. However, it does not initialize the Image property.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SpriteMasterGame
{
    internal class Sprite
    {
        public string Name { get; set; }
        public int X { get; set; }
        public int Y { get; set; }

        public int Height { get; set; }
        public int Width { get; set; }

        public int[] Image { get; set; }

        public char Character { get; set; }

        public Sprite() { }

        public Sprite(string name, int x, int y, int height, int width, int[] image)

        {
            name = Name;
            x = X;
            y = Y;
            height = Height;
            width = Width;

        }
        public Sprite(string name, int x, int y, int height, int width)

        {
            name = Name;
            x = X;
            y = Y;
            height = Height;
            width = Width;

        }
    }
}

The second module is SpriteMaster.cs within the SpriteMasterGame namespace

using GHIElectronics.DUELink This statement includes the necessary namespace for the DUELink library.

Fields:

  • DUELinkController BP: An instance of the DUELinkController class, which is presumably used for controlling hardware components.
  • SpriteMaster(DUELinkController bp): Constructor that takes a DUELinkController instance as a parameter and initializes the BP field with it.

Methods:

  • DoGame(): The main method that runs the game loop.
  • DrawSprite(Sprite sprite): A method for drawing a sprite on the display.

Static Fields:

  • enemy, enemy2, enemy3: Static instances of the Sprite class representing enemy sprites.
  • enemyAlive, enemyAlive2, enemyAlive3: Boolean flags indicating whether the corresponding enemy sprites are alive.
  • bullet: Static instance of the Sprite class representing the player’s bullet.
  • player: Static instance of the Sprite class representing the player.
  • score: An integer representing the player’s score.
  • white and black: Colors represented as unsigned integers.
  • BulletIsOut: Boolean flag indicating whether the bullet is currently in motion.
  • enemySpeed1, enemySpeed2, enemySpeed3: Integers representing the speed of the enemy sprites.
  • lives: Integer representing the player’s remaining lives.

Game Logic:

  • The DoGame() method implements the game logic within a while loop. Here’s a summary of the key elements:

Initialization:

  • Initialization of various sprites, including player, enemies, bullets, and their corresponding attributes.
  • Setting up initial positions, dimensions, and images for each sprite.

Game Loop:

  • Continuously runs the game loop as long as the player has lives (lives > 0).
  • Clears the display and draws the game elements, including the player’s lives and score.
  • Processes the bullet movement, collision detection with enemies, and updates the score.
  • Handles the movement and behavior of enemy sprites.
  • Checks for collisions with the player and updates the player’s position.
  • Updates the display and continues the loop.
using GHIElectronics.DUELink;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace SpriteMasterGame
{
    public class SpriteMaster
    {
        DUELinkController BP;
      
        public SpriteMaster(DUELinkController bp)
        {
            this.BP = bp;
        }
        public void DoGame()
        {
            int[] frame1 = new int[] { 1,0,0,1,1,1,1,1,0,
                                       0,1,1,1,1,1,1,1,1,
                                       1,0,0,0,0,1,0,0,0,
                                       1,1,1,1,1,1,1,1,1,
                                       0,0,1,0,0,0,0,0,1
            };
            int[] frame2 = new int[] { 1,0,0,1,1,1,1,1,0,
                                       0,1,1,1,1,1,1,1,1,
                                       1,0,1,0,0,1,1,0,0,
                                       1,1,1,1,1,1,1,1,1,
                                       0,0,1,0,0,0,0,0,1
            };

            int[] bulletImage = new int[] {1, 0,
                                           0, 1,
                                           1, 0,
                                           0, 1
            };


            int[] playerImage = new int[] {0,0,0,1,1,1,1,0,0,0,0,
                                           0,0,0,1,1,1,1,0,0,0,0,
                                           1,1,1,1,1,1,1,1,1,1,1,
                                           1,1,1,1,1,1,1,1,1,1,1
            };

            bullet.X = 150;
            bullet.Y = 150;
            bullet.Width = 2;
            bullet.Height = 4;
            bullet.Image = bulletImage;

            enemy.X = 10;
            enemy.Y = 10;
            enemy.Width = 9;
            enemy.Height = 5;
            enemy.Image = frame1;

            enemy2.X = 20;
            enemy2.Y = 10;
            enemy2.Width = 9;
            enemy2.Height = 5;
            enemy2.Image = frame2;

            enemy3.X = 30;
            enemy3.Y = 10;
            enemy3.Width = 9;
            enemy3.Height = 5;
            enemy3.Image = frame1;

            player.X = 75;
            player.Y = 60;
            player.Width = 11;
            player.Height = 4;
            player.Image = playerImage;

            var start = DateTime.Now.Ticks;
            var end = DateTime.Now.Ticks;

            while (lives > 0)
            {
                start = DateTime.Now.Ticks;
                BP.Display.Clear(0);
                BP.Display.DrawLine(white, 103, 0, 103, 64);

                for (var i = 0; i < lives; i++)
                {
                    BP.Display.DrawText("^", white, 110, i * 10);
                }

                BP.Display.DrawText(score.ToString(), white, 110, 55);

                if (BulletIsOut)
                {
                    bullet.Y -= 12;

                    if (bullet.Y < 0)
                    {
                        BulletIsOut = false;
                    }
                    else
                    {
                        DrawSprite(bullet);
                    }

                    if (bullet.X >= enemy.X - 3 && bullet.X <= enemy.X + enemy.Width + 3 &&
                        bullet.Y >= enemy.Y - 3 && bullet.Y <= enemy.Y + enemy.Height + 3)
                    {
                        score += 10;
                        enemyAlive = false;
                    }
                    if (bullet.X >= enemy2.X - 3 && bullet.X <= enemy2.X + enemy2.Width + 3 &&
                       bullet.Y >= enemy2.Y - 3 && bullet.Y <= enemy2.Y + enemy2.Height + 3)
                    {
                        score += 10;
                        enemyAlive2 = false;
                    }

                    if (bullet.X >= enemy3.X - 3 && bullet.X <= enemy3.X + enemy3.Width + 3 &&
                      bullet.Y >= enemy3.Y - 3 && bullet.Y <= enemy3.Y + enemy3.Height + 3)
                    {
                        score += 10;
                        enemyAlive3 = false;
                    }
                    if (enemyAlive == false)
                    {
                        enemyAlive = true;
                        enemy.X = 10;
                        enemy.Y = 10;
                    }
                    if (enemyAlive2 == false)
                    {
                        enemyAlive2 = true;
                        enemy2.X = 20;
                        enemy2.Y = 10;
                    }
                    if (enemyAlive3 == false)
                    {
                        enemyAlive3 = true;
                        enemy3.X = 30;
                        enemy3.Y = 10;
                    }
                }
                else
                {
                    var buttonshotB = BP.Button.JustPressed('b');

                    if (buttonshotB)
                    {
                        BP.System.Beep('p', 80, 100);
                        bullet.Y = 64;

                        bullet.X = player.X + 5;

                        BulletIsOut = true;
                    }
                }

                if (enemyAlive) DrawSprite(enemy);
                if (enemyAlive2) DrawSprite(enemy2);
                if (enemyAlive3) DrawSprite(enemy3);
                DrawSprite(player);

                var random_rocket = new Random();

                var rockerX = random_rocket.Next(0, 100);

                if (rockerX < 50) player.X += 5;
                if (rockerX > 50) player.X -= 5;


                if (player.X < 0) player.X = 0;
                if (player.X > 90) player.X = 90;

                enemy.X += enemySpeed1;
                enemy2.X += enemySpeed2;
                enemy3.X += enemySpeed3;


                if (enemy.X < 5 || enemy.X > 85)
                {
                    enemySpeed1 *= -1;
                    enemy.Y += 5;
                }
                if (enemy2.X < 5 || enemy2.X > 85)
                {
                    enemySpeed2 *= -1;
                    enemy2.Y += 5;
                }
                if (enemy3.X < 5 || enemy3.X > 85)
                {
                    enemySpeed3 *= -1;
                    enemy3.Y += 5;
                }

                if (enemy.Y > 56)
                {
                    lives--;
                    enemy.X = 10;
                    enemy.Y = 10;
                }
                if (enemy2.Y > 56)
                {
                    lives--;
                    enemy2.X = 20;
                    enemy2.Y = 10;
                }
                if (enemy3.Y > 56)
                {
                    lives--;
                    enemy3.X = 30;
                    enemy3.Y = 10;
                }
                BP.Display.Show();             
            }
        }

        void DrawSprite(Sprite sprite)
        {
            var index = 0;
            while (index <= sprite.Image.Length - 1)
            {
                for (int y = 0; y < sprite.Height; y++)
                {
                    for (int x = 0; x <= sprite.Width - 1; x++)
                    {
                        if (sprite.Image[index] == 1)
                        {
                            BP.Display.SetPixel(white, sprite.X + x, sprite.Y + row);
                        }
                        else
                        {
                            BP.Display.SetPixel(black, sprite.X + x, sprite.Y + row);
                        }
                        index++;
                    }
                    row++;
                }
                row = 0;
            }

        }
        static int row = 0;
        static Sprite enemy = new Sprite();
        static Sprite enemy2 = new Sprite();
        static Sprite enemy3 = new Sprite();
        static bool enemyAlive = true;
        static bool enemyAlive2 = true;
        static bool enemyAlive3 = true;
        static Sprite bullet = new Sprite();
        static Sprite player = new Sprite();
        static int score = 0;
        static uint white = 1;
        static uint black = 0;
        static bool BulletIsOut = false;
        static int enemySpeed1 = 5;
        static int enemySpeed2 = 5;
        static int enemySpeed3 = 5;
        static int lives = 3;
    }
}
        

The last module is the program.cs

Main Method:

  • Main(string[] args): The entry point of the program.
  • DUELinkController.GetConnectionPort(): Retrieves the connection port for the DUELink controller.
  • new DUELinkController(availablePort): Initializes a new instance of the DUELinkController class with the obtained connection port.
  • BP.Button.Enable(‘b’, true): Enables the ‘b’ button on the brainpad controller for use in the game.

Game Initialization:

  • var spritemasterdisplay = new SpriteMaster(BP) Creates a new instance of the SpriteMaster class, passing the DUELinkController instance (BP) to it.

Game Execution:

  • spritemasterdisplay.DoGame(): Calls the DoGame() method on the SpriteMaster instance to start the game loop.
  • Thread.Sleep(-1) Pauses the program indefinitely, allowing the game loop to continue running until manually interrupted.
using GHIElectronics.DUELink;

namespace SpriteMasterGame
{
    public class Program
    {
        static DUELinkController BP;
        static void Main(string[] args)
        {
            var availablePort = DUELinkController.GetConnectionPort();
            var BP = new DUELinkController(availablePort);

            BP.Button.Enable('b', true);

            var spritemasterdisplay = new SpriteMaster(BP);

            spritemasterdisplay.DoGame();

            Thread.Sleep(-1);
        }
    }
}

Customization:

  • Enhance Game Ending:

After losing all three lives, let’s make something special happen in the game—like showing the final score and playing a sound. Check out the .NET DUE library here.

  • Change Spaceship Movement:

Instead of moving randomly, let’s add buttons to control the spaceship. Connect them using alligator clips to the edge connector. this way, players can enjoy a more interactive gaming experience. See the BrainClip kit here for reference.

  • Explore Different Programming Languages with BrainPad Pulse:

If C# and .NET aren’t your go-to choices, you can create the same game using alternative languages such as Python, JavaScript, Due, and more. Discover the various coding options available here.