C# Tutorial – Create a Simple Battle Ship Game in Visual Studio
- Subject: C# Tutorials
- Learning Time: 5 hours
In this tutorial we will show you how to create a Simple old school battle ship game in visual studio using C# programming language. This game is a complicated one and can be a little daunting for the beginners. If you are unclear about some of the concepts then try out few other tutorials here and come back to this, it will still be here when you are ready. In this game you will create an AI player where you can play against them to win your sea battles. The old joke “you sunk my battle ship”, may apply here but it’s entirely up to you.
Lesson Objectives –
- Complete the simple battle ship game in visual studio
- Use custom background on the windows form
- AI make decisions in the game
- Player controls their ships from list of moves
- Keep track of AI movements
- Using windows GUI to create a game
- Using LIST arrays in C#
- Using LOOPs in C#
- Searching indexes of the list arrays and matching them to integers
- Using System Diagnostics show system debug information in the outputs window
- Using multiple timers and controlling them dynamically
- Showing customised win, lose and draw message box
Full Video Tutorial
Download the BattlShip game assets here.
Download Battleship Project on GitHub
Assets | Description |
This is the custom-made background image we will use in our game. This has the positions for the player and the enemy. It also has the player move and enemy move tabs on the top. We will go through this tutorial showing you how to incorporate this image to the game | |
This is the HIT image. When an attack was successful then we will change the buttons image to this, meaning if the player or enemy hits the target this image will show as feedback that attack was successful. | |
This is the MISS image. This image will show when an attack was unsuccessful. |
To start let’s load up visual studio. We will be using Visual Studio 2017, you can use any of the version you choose it should work the same. Since we are not using any external libraries or game engines there no need to worry about compatibilities.
To start lets make a new Windows Form Application, name this project BattleShip-Game and click OK.
Properties Window is located on the bottom left of the screen, in this window we are able to set different options and change settings for the current form and elements we chose to work with. every single component which is available inside of visual studio has its own properties and events that can be changed inside of this window. So lets this window be your friend in the development.
While the form is selected let’s make some changes to the Form properties.
Change the Size of the Form to 1081, 624 and Text to BattlShip Game MOOICT.
With that done you will notice the form changes to the settings you inputted in the properties window. Now it’s time to import the resources for this game. In the same properties window find the background image option.
You will see it states (none) in the box, now click on the three dotted button to open the dialog box below.
This is the resource importer window. We can click on the import button here to load up the images you downloaded from MOOICT.
Locate the images you downloaded from MOOICT, select them all and click open.
This will import all of the images for this game, now select the background image and click OK.
As you can see the background has now been applied to the game, but it doesn’t look right, its repeating itself and also doesn’t fit properly in the form we created.
Under the background image option you will see the Background Image Layout option, change it to Stretch and it will fit the image to full screen on the form. Try it out.
This properties window is very helpful and we will be using it a lot in this project.
We need the ToolBox to add the necessary components for this game, now often times the tool box is visible in the left of the screen sometimes it hiding. If you can’t find it then click on View –> ToolBox and it will show up on the screen.
Caution – Before setting off and adding lots of components for this game, keep in mind we need lots of buttons and other components to be added so we can get the desired effects from it. Since there are lots of them please take your time to add them and make sure you follow this tutorial slowly, if you rush it and miss something, that might come back to cause more problems as you move further.
This we need to add to this game
Labels | 5 Labels Total |
Buttons | 33 Buttons Total [Yea for Real lol]
16 for the player 16 for the enemy 1 for the Attack |
Combo Box | Only 1 |
Timer | 2 Timers Total |
Find the label from the Tool Box and drag 4 of them to the screen and position them to the screen shot below
Now we need to make the following changes to each of these labels. Click on individual labels and go to the properties window to make the following changes.
Label 1 –Select it and Change the following in the properties for it
Name – playerScore
Font – Size 16 BOLD
Text – 00
Label 2 – Select it and Change the following in the properties for it
Name – enemyScore
Font – Size 16 BOLD
Text – 00
Label 3 – Select it and Change the following in the properties for it
Name – enemyMoves
Back Colour – Transparent
Font – Size 16 BOLD
Fore Colour – White
Text – A1
Label 4 – Select it and Change the following in the properties for it
Name – roundsText
Back Colour – Transparent
Font – Size 12 BOLD
Fore Colour – White
Text – Rounds
Label 4 – Select it and Change the following in the properties for it
Name – helpText
Back Colour – Transparent
Font – Size 9 BOLD
Fore Colour – Light Green
Text – 1) Click on 3 Different Buttons Above to Start
This is the view on the game screen so far.
Now let’s add a Combo Box
Combo Box– Select it and Change the following in the properties for it
Name – enemyLocationList
Drop Down Style – Drop Down List
Drop Down Width – 95
Font – Size 12 Bold
Now lets add our first button to the game. This button will be used to allow the player to attack a enemy location from the drop down list we did earlier.
Button 1– Select it and Change the following in the properties for it
Name – attackButton
Size – 80, 40
Font – BOLD
Text – Attack
Now its time to Add the player buttons. There will be 16 buttons added for the player, so make sure you are thorough and if you feel you have missed something come back to this double check.
Let’s drag and drop a button to the W1 location of the player section
Button – Select it and Change the following in the properties for it
Name – w1 – (make sure its lowercase)
Size – 75, 55
Text – W1 – (make sure its uppercase)
Now you can simply copy and paste it to the other locations changing its name and text only without needing to do much else. Now when we move it to the W2 location the name will change to w2 and text will change to W2.
Here we added all the buttons, changed the NAME and TEXT properties according to the buttons location presented in the game. Make sure you place them within the boundaries of the background image grid.
Now we follow the similar instruction to make the enemy position buttons.
Once again remember to follow these instructions very carefully because you do not want to mess up the names for the buttons. This issue can lead to more issues further down so road so fix any you might have right now.
Lets add our first button to the enemy’s position grid.
Below the A1 button has been added its name is a1, Size – 75, 55 and Text is A1. Its like what we did for the player earlier.
See the table below to see how we did it. Change the name and text options in the properties menu for each button. Do them one by one to reduce the chance of an error.
Now all the enemy’s buttons been added.
Reminder
Make sure you did the following for each button
1 – changed its name to a lowercase a1, a2, b1, b2, or so on
2 – changed its text to the uppercase letters of the button
3 – placed the button in the appropriate grid
With all this done we can move on to our next objective
Now we need to add 2 timers to the form.
From the tool box drag 1 timer to the form
This is the first time added to the form. Change the following in the properties window for this timer.
Change the name to enemyPlayTimer and interval to 1000. We want this time to run once every second when activated. The lower the number faster the timer events will execute.
Now add another timer to the form, change the following for this timer.
Change the name to enemyPositionPicker, change enabled to True and change interval to 500. We want this timer to run when the form loads and run faster than the last one.
Now you will see that we have two timers added to the form and they should both looks like the screen shot above.
Now its time to start adding the events to the game.
First lets select all of the player buttons on the screen.
Drag across them and select them all.
While all of the buttons are selected – click on that little lightning bolt icon and you see the events window relative to the components picked on the form, in this case it’s all these buttons we selected. We want them all to fire up an event when one of them is clicked.
Find the click event and type playerPicksPosition and press enter. This will take you to the code view window, come back to the design view we need to add more events.
Select the attack button from the form, once again go back to the events window.
Type in attackEnemyPosition and press enter. Once again come back to the design view once you have done so.
Now lets add the events for the timers
First click on the and go to the events window
Inside the Tick event type enemyAttackPlayer and press enter.
We need to add an event for the second timer have in this game tool. Click on the enemyPositionPicker timer and go to the events window.
In the Tick event type enemyPicksPositions and press enter. This will conclude our journey on adding events for this game.
With these events added this is what the code view looks like now.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace BattleShip_Game { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void playerPicksPosition(object sender, EventArgs e) { } private void attackEnemyPosition(object sender, EventArgs e) { } private void enemyAttackPlayer(object sender, EventArgs e) { } private void enemyPicksPositions(object sender, EventArgs e) { } } }
This is the empty functions at the moment. You can run the game and it will not do anything because no instructions been given for this game yet. We are going to start writing some of it now.
Add the highlighted code into the places you see below –
// each line with the double slashes in-front is a comment line. They will be used to explain the code.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Diagnostics; // allows us to create a debug log in the outputs window namespace BattleShip_Game { public partial class Form1 : Form { List<Button> playerPosition; // create a list for all the player position buttons List<Button> enemyPosition; // create a list for all the enemy position buttons Random rand = new Random(); // create a new instance for the random class called rand int totalShips = 3; // number of total player ships int totalenemy = 3; // number of total enemy ships int rounds = 10; // total rounds to play each will play 5 rounds int playerTotalScore = 0; // default player score int enemyTotalScore = 0; // default enemy score public Form1() { InitializeComponent(); } private void playerPicksPosition(object sender, EventArgs e) { } private void attackEnemyPosition(object sender, EventArgs e) { } private void enemyAttackPlayer(object sender, EventArgs e) { } private void enemyPicksPositions(object sender, EventArgs e) { } private void loadbuttons() { } } }
using System.Diagnostics; This line will allow us to use the System Diagnostics class from C#, we will be asking our AI to pick its position on the array of buttons, but we will not know which buttons it picked to test out the game mechanics. C# has just the thing for us we can call the diagnostics class and use the debug.log to output the moves AI picked in the outputs window and test the game. It’s an useful tool for programmers and I recommend you use it in your big projects that can save lots of time and energy.
List<Button> playerPosition and enemyPosition are both dynamic array of buttons. Notice we mention <Button> in the list we can call anything we want to make an array but for this game we have lots of buttons and we need a way to organise and use them efficiently. This is why we are using the LIST data type for the buttons.
Random rand = new Random(); This is a Random number generator class build into C#, we will be using this to allow the AI to randomly pick 3 positions for the enemyPosition array.
int totalShips is for the user. By using this golabl int variable we will be able to allow the user to only pick 3 buttons from the playerPosition array. The same logic will be used for the int totalenemy.
int rounds, int playerScore and enemyScore are to keep track of the rounds played and how much the player or the enemy scored in the game.
loadbuttons() function will be used to load all of the buttons into the playerPosition and enemyPosition arrays. We need a function to do it so it’s more organised in the code and not scattered around.
Form1() – Add the highlighted code into the form1 function.
public Form1() { InitializeComponent(); loadbuttons(); // load the buttons for enemy and player to the system attackButton.Enabled = false; // disable the player attack button enemyLocationList.Text = null; // nullify the enemy location drop down box }
The public Form1() is a default function for the Windows Form Application. It loads up all of the necessary components for the Application. We will use this to set up the default values for the game.
First we are running the loadbuttons() function which will load all of the buttons to the array, then we are disabling the attack button because we want the player to pick their position before attacking enemy locations and we are emptying the enemyLocationList combo box so there is nothing in it.
loadButtons() – Add the highlighted code into the loadButtons() function
private void loadbuttons() { // this function will load all the buttons into the lists we declared above // we load all of the player and enemy buttons first playerPosition = new List<Button> { w1, w2, w3, w4, x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4 }; enemyPosition = new List<Button> { a1, a2, a3, a4, b1, b2, b3, b4, c1, c2, c3, c4, d1, d2, d3, d4 }; // this loop will go through each of the enemy position button // then it will add them to the enemy location drop down list for us // it will also remove all Tags from the enemy location buttons for (int i = 0; i < enemyPosition.Count; i++) { enemyPosition[i].Tag = null; enemyLocationList.Items.Add(enemyPosition[i].Text); } }
Above is the load button function. As you can we are loading all of the player and enemy buttons to the array. Each array holds 16 buttons. By doing this we can automate many of the instruction for example the loop below buttons list is the loop that’s adding all of the enemy positions to the enemyLocationList combo box. We can do that if we have the information organized.
playerPicksPosition Event
private void playerPicksPosition(object sender, EventArgs e) { //this function will let the player pick 3 positions on the map // in the beginning of the game this is how we allow the player to pick 3 positions if (totalShips > 0) { // if total ships is more than 0 then we check var button = (Button)sender; // which button was clicked button.Enabled = false; // disable that button button.Tag = "playerShip"; // put a tag on it called playership button.BackColor = System.Drawing.Color.Blue; // change the colour to blue totalShips--; // decrease the total ships by 1 } if (totalShips == 0) { // if the player has picked their all 3 ships // then we do the following attackButton.Enabled = true; // activate the attack button attackButton.BackColor = System.Drawing.Color.Red; // give it a background colour of red helpText.Top = 55; // move the help text to top 55 helpText.Left = 230; // move the help text to left 230 helpText.Text = "2) Now pick a attack position from the drop down"; // change the help text to above } }
Above is the playerPicksPosition event function. This function will run each time the player will click on their own button. In this function we have two IF statements, let go through them first. The first IF statement is checking is the totalShips integer has a greater value than 0 meaning it has more than 1 then we allow the instructions inside to run. First it will check which button was clicked and store it in the local variable called button. Then we will disable that button and give it a tag of playership. Tags are useful to identify an object when we have multiple objects in the program. In this if statement when the button is clicked we will give it a background colour of blue so its easier to identify it and then we will reduce 1 from the total ships integer. The logic here is we want the player to only be able to select 3 buttons so we gave total ships integer a value of 3, each time the player picks a button total ships integer reduces by 1 and when it gets to 0 it will fire up the second if statement.
The second if statement is checking if the total ships 0 then we enable the attack button, give it a background colour RED. The help text which shows that player needs to select 3 buttons now will be moved to the top and will say 2) Now Pick a attack position from the drop down. In any application you need to have a way for the player to understand and use your application. They can’t guess it really so lets give them some information to play the game better.
attackEnemyPosition event
private void attackEnemyPosition(object sender, EventArgs e) { // this function will allow the player to make the moves on the enemy location // we need to check if the player can choose a location from the drop down list if (enemyLocationList.Text != "") { // if the location is appropriately picked then we do the following var attackPos = enemyLocationList.Text; // create a variable called attack pos and give it the value of the text selected from the drop down menu attackPos = attackPos.ToLower(); // change the string to a lower case to match the button name int index = enemyPosition.FindIndex(a => a.Name == attackPos); // in this int we will run the index of the enemy location and search for the string the player picked // once its found it will be saved inside the index local variable // in the if statement below we will link that index number to the enemy position list // and we need to check if we have more rounds to player // if so we do the following if (enemyPosition[index].Enabled && rounds > 0) { rounds--; //reduce 1 from the rounds roundsText.Text = "Rounds " + rounds; // update the rounds text if (enemyPosition[index].Tag == "enemyship") { // if that location the player picked has a enemyship tag in it then we do the following enemyPosition[index].Enabled = false; // disable that button enemyPosition[index].BackgroundImage = Properties.Resources.fireIcon; //change the background image to the fire icon enemyPosition[index].BackColor = System.Drawing.Color.DarkBlue; // change the background colour to dark blue playerTotalScore++; // increase 1 to the player score playerScore.Text = "" + playerTotalScore; // update the player score on the player label enemyPlayTimer.Start(); // start the cpu timer so the enemy can make its move } else { // if player picks a location that isn't where the enemy ship is // then do the following enemyPosition[index].Enabled = false; // we disable that button enemyPosition[index].BackgroundImage = Properties.Resources.missIcon; // change the background image to miss icon enemyPosition[index].BackColor = System.Drawing.Color.DarkBlue; // change the background to dark blue enemyPlayTimer.Start(); // start the cpu time so the enemy can make its move } } } else { // if player doesn't pick a location from the drop down list, alert them to do so MessageBox.Show("Choose a location from the drop down list. "); } }
Attack enemy position event is triggered when the attack button is clicked by the player. We are going to put this whole thing inside an if statement. Logic behind this is when the player selects an option from the drop down menu we can attack that position but if the drop down list position is empty then we want to remind the player to select a location from the list. We are using the lists find index function to search for the button location selected from the list. You can read more about lists and how to use them here – http://www.csharp-examples.net/list/ .
There is a if statement in this function the logic is when the player attacks a position we need to check if that attack position has a tag of enemyship if it does then we change the icon to the fire image we imported earlier, disable that button, add 1 to the player score and we will start the enemy play timer.
If that tag doesn’t match the position the player attacked then we will disable that button and we will give it a miss icon as a background and then we will star the enemy play timer.
enemyAttackPlayer event
private void enemyAttackPlayer(object sender, EventArgs e) { // this function is for the CPU to make a move on the player // if the player position is more than 0 and there are more rounds to play // then we will do the following inside this if statement if (playerPosition.Count > 0 && rounds > 0) { rounds--; // reduce a round from the total roundsText.Text = "Rounds " + rounds; // show the updated number to the rounds label int index = rand.Next(playerPosition.Count); // create a new int index and place a random player button if (playerPosition[index].Tag == "playerShip") { // if the index has a tag of playership then we do the following playerPosition[index].BackgroundImage = Properties.Resources.fireIcon; // change its icon to the fire icon enemyMoves.Text = "" + playerPosition[index].Text; // show which button was attacked playerPosition[index].Enabled = false; //disable the button playerPosition[index].BackColor = System.Drawing.Color.DarkBlue; //change the background colour to dark blue playerPosition.RemoveAt(index); // remove this button from the player position list so it won't get attacked again by the CPU enemyTotalScore++; // add 1 to the enemy score enemyScore.Text = "" + enemyTotalScore; // show the enemy score on the label enemyPlayTimer.Stop(); //stop the time for the CPU } else { // if the player tag isn't of playership // then we do the following playerPosition[index].BackgroundImage = Properties.Resources.missIcon; //show the miss icon on the button enemyMoves.Text = "" + playerPosition[index].Text; // update the enemy attack location on label 2 playerPosition[index].Enabled = false; // diable the button cpu attacked playerPosition[index].BackColor = System.Drawing.Color.DarkBlue; //change the background colour to dark blue playerPosition.RemoveAt(index); // remove this button from the list so it wont get attacked again by the cpu enemyPlayTimer.Stop(); // stop the cpu time. } } // below is the if statement thats checking whether we won, drew or lost the game // if rounds are less than 1 OR player score is more than 2 OR enemy score is more than 2 if (rounds < 1 || playerTotalScore > 2 || enemyTotalScore > 2) { if (playerTotalScore > enemyTotalScore) { // if player score is more than enemy score player wins MessageBox.Show("You Win", "Winning"); } if (playerTotalScore == enemyTotalScore) { // if player and enemy scores the the same its a draw MessageBox.Show("No one wins this", "Draw"); } if (enemyTotalScore > playerTotalScore) { // if enemy score is more than player then enemy winds MessageBox.Show("Haha! I Sunk Your Battle Ship", "Lost"); } } }
Above is the enemy attack player event. This event will be triggered by the enemy play timer. We want this timer to select a random location from the play positions array and attack it. If that position is where the player hid their ships then it will sink or it will show the miss icon. That’s the overall idea behind this event. This function is also responsible for checking the rounds and the player and enemy scores. We also have placed several if statements in the bottom of the function to check if we have won, drew or lost the game against the CPU. All of the codes are commented in the function check carefully.
enemyPicksPositions event
private void enemyPicksPositions(object sender, EventArgs e) { // this function will allow the CPU to pick 3 positions on the MAP // we need to make sure that the enemy CPU will pick 3 positions on the map int index = rand.Next(enemyPosition.Count); // create a local variable called index and choose a random button from the enemy position list if (enemyPosition[index].Enabled == true && enemyPosition[index].Tag == null) { // when we find the buttons we need to check if they are enabled and they dont have a tag yet enemyPosition[index].Tag = "enemyship"; // now add a tag called enemy ship to the button totalenemy--; // and we will reduce the total enemy by 1 Debug.WriteLine("Enemy Position " + enemyPosition[index].Text); // the line above will show us inthe debug window which buttons the enemy chose // this can help us figure out if the game is working as intended } else { // if the top condition dont match then we will run it again to select the 3 positions and tags index = rand.Next(enemyPosition.Count); } if (totalenemy < 1) { // if the cpu has selected the 3 positions then we can stop the timer enemyPositionPicker.Stop(); } }
Above is the enemy picks position event. This event is responsible for the CPU to pick its three positions from the array of buttons. This event will be triggered when the form is loaded. In this event we will use the random number generator we included in the program earlier and we will pick a random button from the array of enemy position buttons when it will pick a button it will add a tag called enemyship to that specific button and it will reduce the total enemy by 1. This is the same logic we used when we wanted that player to pick three positions. Lastly if the total enemy is below one then we can stop the timer and we are ready to play.
Click on the start button to start debugging the game.
Enemy positions are showing up on the outputs window. These are positions the CPU chose randomly. This is made possible by the system diagnostics class we imported earlier. See how it works, you can use this class on any other project you might be working on. It helps save lots of time.
We picked three locations and we are able to select a position from the drop down menu.
Enemy is also attacking out locations, if we manage to hit one from the enemy it changes the icon to fire and if we miss it changes the icon to the cross.
First screen above is where we drew the game with the CPU and the second screen is showing we won.
This screen is showing the enemy won the game.
If you have successfully followed us to here then very well done. You should be proud of the accomplishment. It takes a lot of work to complete a project but have patience and learn from the errors along the way.
Full source code for the game is below. Enjoy and MOO out.
if (enemyPosition[index].Enabled && rounds > 0 ) system.argumentoutofrangeexception: ‘Index was out of range. Must be non-negative and less than the size of the collection
I have the same error as Charlie
Hi this error means that index has either gone over the number of buttons enabled on the enemy position or it has gone below it. The code from the attack enemy position event check the line before the if statement where we have declared the index variable it’s on page 11. I think it might have happened because it cannot find any buttons.
Does this still work?
Yes.
Hello, my code is now running and the game works well except for the fact that the enemy does not appear to be placing any ships – I miss every time because there are no ships to hit on the enemy board so the computer always wins. What could be causing this?
the code is working however I can only pick 3 buttons before cpu picks 3, if cpu picked 3 buttons before me then Im unable to pick all 3 and the game never starts… no idea whats wrong… i have also detected a typo in enemyPicksPosition => if (totalEnemyShips > 1) this should be if (totalEnemyShips < 1) otherwise enemy will only pick 1 button.
Does anyone know a way to do this without buttons? 😛