Most of the posts that I make tend to be very mathematical and aimed at experienced developers. Today's post is instead going to be aimed at those of you who are newer to the platform, and are looking for a project to practice your skills on. We will be going over how we can make our very own game of Simon says!
Before you start reading this post I recommend you brush up on a few things if you aren't familiar with them already.
Setup
The only thing I'll be going into this with is the model I made for Simon. You are by no means forced to use the exact model hierarchy that I use, but it's important that you have an idea of where my script is in relation to what.
2 | local model = script.Parent |
3 | local centerClick = model:WaitForChild( "Center" ):WaitForChild( "ClickDetector" ) |
5 | local sound = model:WaitForChild( "BasePart" ):WaitForChild( "Sound" ) |
7 | local colours = model:WaitForChild( "Colours" ):GetChildren() |
8 | local numColours = #colours |
The rules of the game
For those of you who have never played Simon says the rules are as follows:
- Round n starts
- A pattern of n-length is showed to the player
- The player must repeat back the pattern in order
- If the player completes the pattern, they move onto round n+1
- If the player fails the pattern, they restart at round 1
Thus, functions we will need are as follows:
- Something to show we selected a colour
- Something to generate a pattern
- Something to play the pattern
- Something to show we won the game
- Something to show we lost the game
Selecting a colour
To start off we'll work on our function that shows a colour has been selected. It will be used for when the player selects a colour and when Simon is playing back a pattern or showing that we won or lost.
We'll define the function with three parameters. The first will be an index which represents which colour part we are using in the colours array. The second will be the time that the colour part lights up for, and the third will be the playback speed of the sound which we will use to control pitch.
01 | function playColour(index, t, speed) |
03 | local part = colours [ index ] |
05 | part.Material = Enum.Material.Neon |
06 | sound.PlaybackSpeed = speed |
11 | part.Material = Enum.Material.Plastic |
17 | for i = 1 , #colours do |
Generating a pattern
Our next step will be to write a function that generates a pattern of n-length. We can create this pattern with the Random
object and its method :NextInteger(min, max)
which gives a random integer between min and max inclusive.
1 | function genPattern(length, seed) |
3 | local r = Random.new(seed) |
5 | pattern [ i ] = r:NextInteger( 1 , #colours) |
We can note that for each element in the pattern we pick a random number between 1
and #colours
which means our pattern will be made up of indexes that correspond to a part in the colours array.
In other words, if our colour array and pattern looked like this for example:
1 | colours = { blue, red, green, yellow } |
2 | pattern = { 1 , 3 , 3 , 2 , 4 , 1 , 1 } |
5 | { blue, green, green, red, yellow, blue, blue } |
Playing the pattern
Now that we have a pattern, playing it is as simple as iterating through it and playing each colour that corresponds to the index.
1 | function playPattern(pattern) |
3 | playColour(pattern [ i ] , 0.2 , pattern [ i ] ) |
9 | playPattern(genPattern( 20 , tick())) |
Starting a round
We can now start to write the function that will start a round of Simon says.
03 | local currentPattern = { } |
06 | roundNum = roundNum + 1 |
08 | local pattern = genPattern(roundNum, tick()) |
09 | currentPattern = pattern |
This covers steps one and two of the initial rules process we defined at the beginning. All that's left to do is have the player repeat the pattern back.
Player input
For the player to repeat the pattern they need some way to interact with Simon. They way we will do this is with ClickDetectors
and the MouseClick
event. Our first step will be to create a click detector under each colour part in the colours array and connect a function that will fire when it's clicked.
02 | local clickDetectors = { } |
04 | function onClicked(player, index) |
08 | function initClickDetectors() |
09 | for i = 1 , numColours do |
10 | local part = colours [ i ] |
11 | local cdetect = Instance.new( "ClickDetector" ) |
13 | cdetect.MouseClick:Connect( function (player) onClicked(player, i) end ) |
16 | clickDetectors [ part ] = cdetect |
Sometimes throughout the game of Simon says we don't want the player to be able to click any buttons. Thus, we'll create a function that can enable or disable player input by setting the parent of the click detectors.
01 | function setInputEnabled(bool) |
03 | for part, cdetect in pairs (clickDetectors) do |
Now that we have this function we'll likely want to go back and adjust out pattern playing function so that the player can't click anything when the pattern is playing.
1 | function playPattern(pattern) |
4 | playColour(pattern [ i ] , 0.2 , pattern [ i ] ) |
Verifying the pattern
In order to complete step three, we will have to verify that when a player clicks a colour part it was the part that was next in the pattern. As such we will need a counter that tells us how far along the player is into the pattern. We will simply reset this back to zero every time a round begins.
06 | roundNum = roundNum + 1 |
08 | local pattern = genPattern(roundNum, tick()) |
09 | currentPattern = pattern |
13 | function onClicked(player, index) |
15 | setInputEnabled( false ) |
16 | playColour(index, 0.2 , index) |
20 | inputIndex = inputIndex + 1 |
22 | if (index ~ = currentPattern [ inputIndex ] ) then |
25 | elseif (inputIndex = = #currentPattern) then |
Win or lose the round
Our last two steps require we show the player they either won or lost and move them to the next round. Both these functions are quite easy to write by this point as we have the rest of the game logic in place.
02 | local timeBetweenRounds = 0.5 |
05 | setInputEnabled( false ) |
08 | for j = 1 , numColours do |
09 | playColour(j, 0.05 , j) |
12 | wait(timeBetweenRounds) |
19 | setInputEnabled( false ) |
22 | for j = numColours, 1 , - 1 do |
23 | playColour(j, 0.05 , j/numColours) |
26 | wait(timeBetweenRounds) |
34 | function onClicked(player, index) |
36 | setInputEnabled( false ) |
37 | playColour(index, 0.2 , index) |
41 | inputIndex = inputIndex + 1 |
43 | if (index ~ = currentPattern [ inputIndex ] ) then |
45 | elseif (inputIndex = = #currentPattern) then |
Initiating the game
Congratulations! You now have all the functions needed to have your simon says working! The absolute last thing we need to do is make sure the game starts running. We'll do this by having the player click the button in the middle.
4 | centerClick.MouseClick:Wait() |
You can find an un-copylocked version of the game here.
Conclusion
So that's it for now folks. Hope you enjoyed the post! As always, I'm looking for feedback, especially for this type of article as it's quite different than what I normally post.
Until next time!
Commentary
Leave a Comment