I have a programming exam tomorrow and for me, the best way to practice is to make games! It's fun, challenging, and quick enough to keep my brain fresh. So today I thought, why not try and make a top-down shooter game in (yet another) Windows Form App? (Check out my Flappy Bird tutorial!)
Our game will surround a retired hitman who has been hired to take out a bunch of evil robots (every game needs a backstory, okay?). We'll be using C#, and free assets from Kenney. I do need to thank MooICT for the guidance along the way. Without further diddle-daddling, let's sharpen our C# skills! 😁
Here is a low-budget demo of our game. 👀
Step One: Creating The Game Interface
Before we jump into the code itself, we need to create the game interface. Open up Visual Studio and follow the process to create a Windows Forms App with C# (be careful not to choose the vb.net option!).
Then, download the assets from my GitHub repository and extract it. Import these assets into your project (you can drag in a picture box and import there if you are unsure).
Once imported, we need to start adding our objects onto our interface. Don't worry too much about where you place these objects, you can always come back later to change it around.
You need to insert the following:
Five Labels:
- Three default labels.
- A
textAmmo
label where we will display the amount of bullets we have. - A
textKills
label to show the amount of kills we have.
Two Picture Boxes:
- One named
playerController
. - One named
robotController
.
One Timer:
- Named
gameStart
.
One Progress Bar:
- Named
playerHealth
.
For the gameStart timer, make sure you change the following properties and add a gameStartEvent
event to it.
Then for the robot add the "robot
" tag, and for the player add the "player
" tag. We will need these tags later for game handling.
The, we need to add our input listeners (keyUp and keyDown
) so that we can move the player around using our keyboard.
Finally, let's add a new class to our project labeled Ammo.cs
- we will need this later on to make bullets and create bullet spreads for our shooting function.
Step Two: Forms.cs
Before we do anything, let's create three functions that we will complete later on: spawnAmmo
, shootMain
, and spawnRobots
. Please note that I renamed my Form to RoboShooter. The gameStartEvent
, keyDown
, and keyUp
functions are declared automatically by our form and timer components (remember, we added it in the first step).
//Forms.cs
namespace RoboShooter
{
public partial class RoboShooter : Form
{
public RoboShooter()
{
InitializeComponent();
}
//moves player when keys are pressed down
private void keyDown(object sender, KeyEventArgs e)
{
}
//stops player movement when keys arent pressed
private void keyUp(object sender, KeyEventArgs e)
{
}
//timer event on game start
private void gameStartEvent(object sender, EventArgs e)
{
}
//function needed to spawn more ammo during game
private void spawnAmmo()
{
}
//allows the player to shoot the robots in the direction it faces
private void shootMain(string direction)
{
}
//spawns robots
private void spawnRobots()
{
}
}
}
Once you've done that, lets declare our public variables. We will make them public because they will be called by multiple functions (put simply). Read more on access modifiers if needed.
namespace RoboShooter
{
public partial class RoboShooter : Form
{
//public variables needed for our functions
//controls player position on screen
bool keyup;
bool keydown;
bool keyleft;
bool keyright;
string playerDirection = "right";
//state of game at game start
bool gameOver = false;
//player stats at game start
double health = 100;
int movementSpeed = 10;
int ammoLevel = 7;
int robotSpeed = 3;
int annihilations = 0;
Random random = new Random();
public RoboShooter()
{
InitializeComponent();
}
//moves player when keys are pressed down
private void keyDown(object sender, KeyEventArgs e)
{
}
//stops player movement when keys arent pressed
private void keyUp(object sender, KeyEventArgs e)
{
}
//timer event on game start
private void gameStartEvent(object sender, EventArgs e)
{
}
//function needed to spawn more ammo during game
private void spawnAmmo()
{
}
//allows the player to shoot the robots in the direction it faces
private void shootMain(string direction)
{
}
//spawns robots
private void spawnRobots()
{
}
private void label3_Click(object sender, EventArgs e)
{
}
private void pictureBox1_Click(object sender, EventArgs e)
{
}
private void pictureBox4_Click(object sender, EventArgs e)
{
}
}
}
Now, in our keydown()
function, let's add the necessary code to move the game character around when the player presses on their arrow keys. We will not add the space key's listener (which we will use to shoot the robots) here to prevent the player from spamming bullets! Remember, the assets ml, mu, md, and mr come from the assets we imported (the pictures to use for each player direction).
//moves player when keys are pressed down
private void keyDown(object sender, KeyEventArgs e)
{
//player won't be able to move if game is over
if (gameOver)
{
return;
}
//moves player left with left arrow key and sets image as "man-left"
if (e.KeyCode == Keys.Left)
{
keyleft = true;
playerDirection = "left";
playerController.Image = Properties.Resources.ml;
}
//moves player right with right arrow key and sets image as "man-right"
if (e.KeyCode == Keys.Right)
{
keyright = true;
playerDirection = "right";
playerController.Image = Properties.Resources.mr;
}
//moves player down with right down key and sets image as "man-down"
if (e.KeyCode == Keys.Down)
{
keydown = true;
playerDirection = "down";
playerController.Image = Properties.Resources.md;
}
//moves player up with right up key and sets image as "man-up"
if (e.KeyCode == Keys.Up)
{
keyup = true;
playerDirection = "up";
playerController.Image = Properties.Resources.mu;
}
}
Then, in our keyUp()
function, we will add the necessary code that will stop the game character from moving when the player releases their arrow and space bar keys. We will also add our space key's listener here to decrease our ammo and initiate our "shooting" function.
//stops player movement when keys arent pressed
private void keyUp(object sender, KeyEventArgs e)
{
//player won't be able to move if game is over
if (gameOver)
{
return;
}
//disables keys when not pressed
if (e.KeyCode == Keys.Left)
{
keyleft = false;
}
if (e.KeyCode == Keys.Right)
{
keyright = false;
}
if (e.KeyCode == Keys.Down)
{
keydown = false;
}
if (e.KeyCode == Keys.Up)
{
keyup = false;
}
//if the space bar is pressed down - it's here to prevent spamming of key
if (e.KeyCode == Keys.Space && ammoLevel > 0)
{
//reduce ammo and shoots in the direction the player faces
ammoLevel--;
shootMain(playerDirection);
//spawn ammo boxes if ammo is low (defined later)
if (ammoLevel < 3)
{
spawnAmmo();
}
}
}
Okay, now we can go ahead and add the necessary code in our gameStartEvent()
.
First, we need to alter our health bar (progress bar) according to the state of our player.
private void gameStartEvent(object sender, EventArgs e)
{
//if the player has enough health left, the progressbar will be shown
if (health > 1)
{
progressHealth.Value = Convert.ToInt32(health);
progressHealth.ForeColor = Color.Orange;
}
//else the player will be killed and game over is called
else
{
playerController.Image = Properties.Resources.death;
gameStart.Stop();
gameOver = true;
}
//if health is less than zero make bar red as warning
if (health < 20)
{
progressHealth.ForeColor = Color.Red;
}
}
Then, below our code above, we need to add our code that will display our players stats when they've killed robots or if their ammo levels change.
private void gameStartEvent(object sender, EventArgs e)
{
...
//displays our player stats value on the form
textAmmo.Text = ammoLevel.ToString();
textKills.Text = annihilations.ToString();
}
Then we need to alter our movement speed depending on where we are in the form (so that we can actually move around instead of just turning in one place).
private void gameStartEvent(object sender, EventArgs e)
{
...
//affect speed of payer according to their direction
if (keyleft && playerController.Left > 0)
{
playerController.Left -= movementSpeed;
}
if (keyright && playerController.Left + playerController.Width < 930)
{
playerController.Left += movementSpeed;
}
if (keyup && playerController.Top > 60)
{
playerController.Top -= movementSpeed;
}
if (keydown && playerController.Top + playerController.Height < 700)
{
playerController.Top += movementSpeed;
}
}
Okay, now the long piece of code starts (ouch for you). We will need to create a foreach loop that will affect our player and robot controls.
First, we will need to loop through our game to see if our game controllers (player and robot) are colliding with anything. If the player collides with the robots, our health decreases and we die. If the robot collides with the bullets (defined later in Ammo.cs), they die, our kill streak increases, and the robots need to respawn. If our bullets hit the frame, they disappear. Lastly, if the player collides with the ammo crates (defined later), we increase our ammo. Read more on the Control class. Are you ready?
private void gameStartEvent(object sender, EventArgs e)
{
...
foreach (Control x in this.Controls)
{
//if the player runs over the ammo picture, they will pick it up (and the picture will dissapear), and their ammo level will increase
if (x is PictureBox && x.Tag == "ammo")
{
if (((PictureBox)x).Bounds.IntersectsWith(playerController.Bounds))
{
this.Controls.Remove(((PictureBox)x)); // remove the ammo picture box
((PictureBox)x).Dispose();
ammoLevel += 7;
}
}
//if our players bullet hits the frame of the form, the bullet will "dissapear"
if (x is PictureBox && x.Tag == "bullet")
{
if (((PictureBox)x).Left < 1 || ((PictureBox)x).Left > 930 || ((PictureBox)x).Top < 10 || ((PictureBox)x).Top > 700)
{
this.Controls.Remove(((PictureBox)x));
((PictureBox)x).Dispose();
}
}
//player-robot interaction
if (x is PictureBox && x.Tag == "robot")
{
//if the robot runs into the player, our health goes down and we get a red background to show injury
if (((PictureBox)x).Bounds.IntersectsWith(playerController.Bounds) && gameOver == false)
{
health -= 1;
playerController.BackColor = Color.Red;
}
else
{
playerController.BackColor = Color.Transparent;
}
//will allow the robots to move towards the player (just like the player keys above, it will change the robots direction and image)
if (((PictureBox)x).Left > playerController.Left)
{
((PictureBox)x).Left -= robotSpeed;
((PictureBox)x).Image = Properties.Resources.rl;
}
if (((PictureBox)x).Top > playerController.Top)
{
((PictureBox)x).Top -= robotSpeed;
((PictureBox)x).Image = Properties.Resources.ru;
}
if (((PictureBox)x).Left < playerController.Left)
{
((PictureBox)x).Left += robotSpeed;
((PictureBox)x).Image = Properties.Resources.rr;
}
if (((PictureBox)x).Top < playerController.Top)
{
((PictureBox)x).Top += robotSpeed;
((PictureBox)x).Image = Properties.Resources.rd;
}
}
//if our player hits the robot with our bullets, then the robots is killed and more are spawned
foreach (Control j in this.Controls)
{
if ((j is PictureBox && j.Tag == "bullet") && (x is PictureBox && x.Tag == "robot"))
{
if (x.Bounds.IntersectsWith(j.Bounds))
{
//up our kill streak
annihilations++;
//removes the robot(x) and the ammo(j)
this.Controls.Remove(j);
j.Dispose();
this.Controls.Remove(x);
x.Dispose();
//spawns more robots
spawnRobots();
}
}
}
}
}
We are finally done with our gameStartEvent() function! 🥴
Now, we can move on to our spawnAmmo()
function, which will spawn ammo crates when our bullets run low!
//function needed to spawn more ammo during game
private void spawnAmmo()
{
//creates new PictureBox for ammo that will spawn randomly across screen
PictureBox ammo = new PictureBox();
ammo.BringToFront();
ammo.Image = Properties.Resources.ammo;
ammo.SizeMode = PictureBoxSizeMode.AutoSize;
ammo.BackColor = Color.Transparent;
ammo.Left = random.Next(10, 890);
ammo.Top = random.Next(50, 600);
ammo.Tag = "ammo";
//adds the controls needed to pick it up and dispose of it above
this.Controls.Add(ammo);
playerController.BringToFront();
}
Then, we need to do the same for our shootMain()
function, which will allow us to shoot "bullets" at the robots. In other words, it spawns the bullets.
//allows the player to shoot the robots in the direction it faces (by spawning bullets)
private void shootMain(string direction)
{
bullet shoot = new bullet();
shoot.direction = direction;
shoot.bulletLeft = playerController.Left + (playerController.Width / 2);
shoot.bulletTop = playerController.Top + (playerController.Height / 2);
shoot.spawnBullets(this);
}
Finally, we need to spawn our robots when they run low. We do this in our spawnRobots()
function.
//spawns robots
private void spawnRobots()
{
PictureBox robots = new PictureBox();
robots.Tag = "robot";
robots.Image = Properties.Resources.rl;
robots.Left = random.Next(0, 900);
robots.Top = random.Next(0, 800);
robots.SizeMode = PictureBoxSizeMode.AutoSize;
robots.BackColor = Color.Transparent;
this.Controls.Add(robots);
playerController.BringToFront();
robots.BringToFront();
}
Okay, so we can't test yet because our bullets object is not defined. We'll do that next, but for now, your code should look like the code below.
Full Forms.cs
namespace RoboShooter
{
public partial class RoboShooter : Form
{
//public variables needed for our custom functions
//controls player position on screen
bool keyup;
bool keydown;
bool keyleft;
bool keyright;
string playerDirection = "right";
//state of game at game start
bool gameOver = false;
//player stats at game start
double health = 100;
int movementSpeed = 10;
int ammoLevel = 7;
int robotSpeed = 3;
int annihilations = 0;
Random random = new Random();
public RoboShooter()
{
InitializeComponent();
}
//moves player when keys are pressed down
private void keyDown(object sender, KeyEventArgs e)
{
//player won't be able to move if game is over
if (gameOver)
{
return;
}
//moves player left with left arrow key and sets image as "man-left"
if (e.KeyCode == Keys.Left)
{
keyleft = true;
playerDirection = "left";
playerController.Image = Properties.Resources.ml;
}
//moves player right with right arrow key and sets image as "man-right"
if (e.KeyCode == Keys.Right)
{
keyright = true;
playerDirection = "right";
playerController.Image = Properties.Resources.mr;
}
//moves player down with right down key and sets image as "man-down"
if (e.KeyCode == Keys.Down)
{
keydown = true;
playerDirection = "down";
playerController.Image = Properties.Resources.md;
}
//moves player up with right up key and sets image as "man-up"
if (e.KeyCode == Keys.Up)
{
keyup = true;
playerDirection = "up";
playerController.Image = Properties.Resources.mu;
}
}
//stops player movement when keys arent pressed
private void keyUp(object sender, KeyEventArgs e)
{
//player won't be able to move if game is over
if (gameOver)
{
return;
}
//disables keys when not pressed
if (e.KeyCode == Keys.Left)
{
keyleft = false;
}
if (e.KeyCode == Keys.Right)
{
keyright = false;
}
if (e.KeyCode == Keys.Down)
{
keydown = false;
}
if (e.KeyCode == Keys.Up)
{
keyup = false;
}
//if the space bar is pressed down - it's here to prevent spamming of key
if (e.KeyCode == Keys.Space && ammoLevel > 0)
{
//reduce ammo and shoot in direction player faces
ammoLevel--;
shootMain(playerDirection);
//spawn ammo if ammo is low
if (ammoLevel < 3)
{
spawnAmmo();
}
}
}
//timer event on game start
private void gameStartEvent(object sender, EventArgs e)
{
//if the player has enough health left, the progressbar will be shown
if (health > 1)
{
progressHealth.Value = Convert.ToInt32(health);
progressHealth.ForeColor = Color.Orange;
}
//else the player will be killed and game over is called
else
{
playerController.Image = Properties.Resources.death;
gameStart.Stop();
gameOver = true;
}
//displays our player stats value on the form
textAmmo.Text = ammoLevel.ToString();
textKills.Text = annihilations.ToString();
//if health is less than zero make bar red as warning
if (health < 20)
{
progressHealth.ForeColor = Color.Red;
}
//affect speed of payer according to their direction
if (keyleft && playerController.Left > 0)
{
playerController.Left -= movementSpeed;
}
if (keyright && playerController.Left + playerController.Width < 930)
{
playerController.Left += movementSpeed;
}
if (keyup && playerController.Top > 60)
{
playerController.Top -= movementSpeed;
}
if (keydown && playerController.Top + playerController.Height < 700)
{
playerController.Top += movementSpeed;
}
foreach (Control x in this.Controls)
{
//if the player runs over the ammo picture, they will pick it up (and the picture will dissapear), and their ammo level will increase
if (x is PictureBox && x.Tag == "ammo")
{
if (((PictureBox)x).Bounds.IntersectsWith(playerController.Bounds))
{
this.Controls.Remove(((PictureBox)x)); // remove the ammo picture box
((PictureBox)x).Dispose();
ammoLevel += 7;
}
}
//if our players bullet hits the frame of the form, the bullet will "dissapear"
if (x is PictureBox && x.Tag == "bullet")
{
if (((PictureBox)x).Left < 1 || ((PictureBox)x).Left > 930 || ((PictureBox)x).Top < 10 || ((PictureBox)x).Top > 700)
{
this.Controls.Remove(((PictureBox)x));
((PictureBox)x).Dispose();
}
}
//player-robot interaction
if (x is PictureBox && x.Tag == "robot")
{
//if the robot runs into the player, our health goes down and we get a red background to show injury
if (((PictureBox)x).Bounds.IntersectsWith(playerController.Bounds) && gameOver == false)
{
health -= 1;
playerController.BackColor = Color.Red;
}
else
{
playerController.BackColor = Color.Transparent;
}
//will allow the robots to move towards the player (just like the player keys above, it will change the robots direction and image)
if (((PictureBox)x).Left > playerController.Left)
{
((PictureBox)x).Left -= robotSpeed;
((PictureBox)x).Image = Properties.Resources.rl;
}
if (((PictureBox)x).Top > playerController.Top)
{
((PictureBox)x).Top -= robotSpeed;
((PictureBox)x).Image = Properties.Resources.ru;
}
if (((PictureBox)x).Left < playerController.Left)
{
((PictureBox)x).Left += robotSpeed;
((PictureBox)x).Image = Properties.Resources.rr;
}
if (((PictureBox)x).Top < playerController.Top)
{
((PictureBox)x).Top += robotSpeed;
((PictureBox)x).Image = Properties.Resources.rd;
}
}
//if our player hits the robot with our bullets, then the robots is killed and more are spawned
foreach (Control j in this.Controls)
{
if ((j is PictureBox && j.Tag == "bullet") && (x is PictureBox && x.Tag == "robot"))
{
if (x.Bounds.IntersectsWith(j.Bounds))
{
//up our kill streak
annihilations++;
//removes the robot(x) and the ammo(j)
this.Controls.Remove(j);
j.Dispose();
this.Controls.Remove(x);
x.Dispose();
//spawns more robots
spawnRobots();
}
}
}
}
}
//function needed to spawn more ammo during game
private void spawnAmmo()
{
//creates new PictureBox for ammo that will spawn randomly across screen
PictureBox ammo = new PictureBox();
ammo.BringToFront();
ammo.Image = Properties.Resources.ammo;
ammo.SizeMode = PictureBoxSizeMode.AutoSize;
ammo.BackColor = Color.Transparent;
ammo.Left = random.Next(10, 890);
ammo.Top = random.Next(50, 600);
ammo.Tag = "ammo";
//adds the controls needed to pick it up and dispose of it above
this.Controls.Add(ammo);
playerController.BringToFront();
}
//allows the player to shoot the robots in the direction it faces (by spawning bullets)
private void shootMain(string direction)
{
bullet shoot = new bullet();
shoot.direction = direction;
shoot.bulletLeft = playerController.Left + (playerController.Width / 2);
shoot.bulletTop = playerController.Top + (playerController.Height / 2);
shoot.spawnBullets(this);
}
//spawns robots
private void spawnRobots()
{
PictureBox robots = new PictureBox();
robots.Tag = "robot";
robots.Image = Properties.Resources.rl;
robots.Left = random.Next(0, 900);
robots.Top = random.Next(0, 800);
robots.SizeMode = PictureBoxSizeMode.AutoSize;
robots.BackColor = Color.Transparent;
this.Controls.Add(robots);
playerController.BringToFront();
robots.BringToFront();
}
}
}
Step Three: Ammo.cs
Now head into your Ammo.cs file and define our base class: bullet
.
//Ammo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Windows.Forms;
using Timer = System.Windows.Forms.Timer;
namespace RoboShooter
{
//will spawn bullets on our screen
class bullet
{
}
}
Then, let's create our public variables as well as our two functions: spawnBullets()
and bulletSpreadTick()
. Remember to import the following libraries: Timer = System.Windows.Forms.Timer, System.Drawing, and System.Windows.Forms
.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Windows.Forms;
using Timer = System.Windows.Forms.Timer;
namespace RoboShooter
{
//will create bullets on our screen
class bullet
{
//pulic variables
public string direction;
public int bulletSpeed = 20;
public int bulletLeft;
public int bulletTop;
PictureBox Bullet = new PictureBox();
Timer bulletSpread = new Timer();
public void spawnBullets(Form form)
{
}
//direction of the bullet
public void bulletSpreadTick(object sender, EventArgs e)
{
}
}
}
In your spawnBullets()
function, add the necessary code to create the bullets, as well as add the event handler to spread out the bullets (so it sprays like little cubes).
namespace RoboShooter
{
//will create bullets on our screen
class bullet
{
...
public void spawnBullets(Form form)
{
//creates the bullets, which will be little cubes when space bar is pressed
Bullet.BackColor = Color.Gold;
Bullet.Size = new Size(5, 5);
Bullet.Tag = "bullet";
Bullet.Left = bulletLeft;
Bullet.Top = bulletTop;
Bullet.BringToFront();
//adds bullet to screen at x speed when button is pressed
form.Controls.Add(Bullet);
bulletSpread.Interval = bulletSpeed;
bulletSpread.Tick += new EventHandler(bulletSpreadTick);
bulletSpread.Start();
}
}
}
Then, lastly, we can add the necessary code to our bulletSpreadTick()
function so that we can move our bullets across the screen (spray) as well as make it disappear when it reaches a certain point.
namespace RoboShooter
{
//will create bullets on our screen
class bullet
{
//spread of the bullet
public void bulletSpreadTick(object sender, EventArgs e)
{
if (direction == "left")
{
Bullet.Left -= bulletSpeed;
}
if (direction == "right")
{
Bullet.Left += bulletSpeed;
}
if (direction == "up")
{
Bullet.Top -= bulletSpeed;
}
if (direction == "down")
{
Bullet.Top += bulletSpeed;
}
//if the bullets have reached a certain point from the player, it dissapears
if (Bullet.Left < 16 || Bullet.Left > 860 || Bullet.Top < 10 || Bullet.Top > 616)
{
bulletSpread.Stop();
bulletSpread.Dispose();
Bullet.Dispose();
bulletSpread = null;
Bullet = null;
}
}
}
}
And with that, all the coding is done.
Full Ammo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Windows.Forms;
using Timer = System.Windows.Forms.Timer;
namespace RoboShooter
{
//will create bullets on our screen
class bullet
{
//pulic variables
public string direction;
public int bulletSpeed = 20;
public int bulletLeft;
public int bulletTop;
PictureBox Bullet = new PictureBox();
Timer bulletSpread = new Timer();
//will spawn the bullets
public void spawnBullets(Form form)
{
//creates the bullets, which will be little cubes when space bar is pressed
Bullet.BackColor = Color.Gold;
Bullet.Size = new Size(5, 5);
Bullet.Tag = "bullet";
Bullet.Left = bulletLeft;
Bullet.Top = bulletTop;
Bullet.BringToFront();
//adds bullet to screen at x speed when button is pressed
form.Controls.Add(Bullet);
bulletSpread.Interval = bulletSpeed;
bulletSpread.Tick += new EventHandler(bulletSpreadTick);
bulletSpread.Start();
}
//direction of the bullet
public void bulletSpreadTick(object sender, EventArgs e)
{
if (direction == "left")
{
Bullet.Left -= bulletSpeed;
}
if (direction == "right")
{
Bullet.Left += bulletSpeed;
}
if (direction == "up")
{
Bullet.Top -= bulletSpeed;
}
if (direction == "down")
{
Bullet.Top += bulletSpeed;
}
//if the bullets have reached a certain point from the player, it dissapears
if (Bullet.Left < 16 || Bullet.Left > 860 || Bullet.Top < 10 || Bullet.Top > 616)
{
bulletSpread.Stop();
bulletSpread.Dispose();
Bullet.Dispose();
bulletSpread = null;
Bullet = null;
}
}
}
}
Conclusion
To wrap things up, you can now make it your own by adding more features, and making the interface of the game a lot more interesting. You can find cool assets from Kenney's site above, or even Freepik. I did something basic. 🤔
I hope this was easy enough to follow. I'm no C# master, but for me this was good enough to practice a bit. I do want to improve on this game, maybe smoothen out the movement and add sound, but hey, that's for future me to worry about! Until next time, happy coding. 😁
The full source code can be found on my GitHub.
Top comments (1)
Damn thats dope! One day I want to do stuff like this thats why I learned C#.