Creating your own Simon says

This guide was originally written for scriptinghelpers. The original can be found here.

Forgot to post this one to the devforum so here ya go!

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.

img1

-- defined at the top of the script
local model = script.Parent
local centerClick = model:WaitForChild("Center"):WaitForChild("ClickDetector")
-- looped = true on this sound
local sound = model:WaitForChild("BasePart"):WaitForChild("Sound") 
-- store the colours as an array
local colours = model:WaitForChild("Colours"):GetChildren()
local numColours = #colours

The rules of the game

Edit: I actually learned these are not the rules of Simon says after I posted this on scripting helpers :scream:. It doesn’t matter much though from the standpoint of a learning tool so I haven’t bothered to change it.

For those of you who have never played Simon says the rules are as follows:

  1. Round n starts
  2. A pattern of n-length is showed to the player
  3. The player must repeat back the pattern in order
  4. If the player completes the pattern, they move onto round n+1
  5. 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.

function playColour(index, t, speed)
	-- access the part from the array
	local part = colours[index] 
	-- set the material to neon and play the sound
	part.Material = Enum.Material.Neon
	sound.PlaybackSpeed = speed
	sound:Play()
	-- wait t seconds
	wait(t)
	-- set the part back to plastic and stop the sound
	part.Material = Enum.Material.Plastic
	sound:Stop()
end

-- a quick test
while (true) do
	for i = 1, #colours do
		playColour(i, 0.2, i)
		wait()
	end
end

img2

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.

function genPattern(length, seed)
	local pattern = {}
	local r = Random.new(seed)
	for i = 1, length do
		pattern[i] = r:NextInteger(1, #colours)
	end
	return pattern
end

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:

colours = {blue, red, green, yellow}
pattern = {1, 3, 3, 2, 4, 1, 1}

-- then the pattern corresponds to
{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.

function playPattern(pattern)
	for i = 1, #pattern do
		playColour(pattern[i], 0.2, pattern[i])
		wait()
	end
end

-- a simple test
playPattern(genPattern(20, tick()))

img3

Starting a round

We can now start to write the function that will start a round of Simon says.

-- define these near the top
local roundNum = 0
local currentPattern = {}

function startRound()
	roundNum = roundNum + 1
	
	local pattern = genPattern(roundNum, tick())
	currentPattern = pattern
	playPattern(pattern)
end

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.

-- define this near the top
local clickDetectors = {}

function onClicked(player, index)
	-- We'll write the code for this later
end

function initClickDetectors()
	for i = 1, numColours do
		local part = colours[i]
		local cdetect = Instance.new("ClickDetector")
		-- connect the function that defines what happens when the part is clicked
		cdetect.MouseClick:Connect(function(player) onClicked(player, i) end)
		cdetect.Parent = part
		-- dictionary where the part is the key and the click detector is the value
		clickDetectors[part] = cdetect
	end
end

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.

function setInputEnabled(bool)
	-- iterate over the key and value
	for part, cdetect in pairs(clickDetectors) do
		if (bool) then
			cdetect.Parent = part
		else
			cdetect.Parent = nil
		end
	end
end

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.

function playPattern(pattern)
	setInputEnabled(false)
	for i = 1, #pattern do
		playColour(pattern[i], 0.2, pattern[i])
		wait()
	end
	setInputEnabled(true)
end

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.

-- define near the top
local inputIndex = 0

function startRound()
	inputIndex = 0 -- reset every round
	roundNum = roundNum + 1
	
	local pattern = genPattern(roundNum, tick())
	currentPattern = pattern
	playPattern(pattern)
end

function onClicked(player, index)
	-- play the colour that was just selected
	setInputEnabled(false)
	playColour(index, 0.2, index)
	setInputEnabled(true)
	
	-- comparing our input to the next index in the pattern
	inputIndex = inputIndex + 1
	
	if (index ~= currentPattern[inputIndex]) then
		-- what the player selected was not what was next in the pattern
		-- lose the round!
	elseif (inputIndex == #currentPattern) then
		-- from our previous check we know the player selected correctly
		-- we also know that the last input made was the total length of the pattern
		-- win the round!
	end
end

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.

-- define near the top
local timeBetweenRounds = 0.5

function winRound()
	setInputEnabled(false)
	-- play an animation that tells us we won!
	for i = 1, 3 do
		for j = 1, numColours do
			playColour(j, 0.05, j)
		end
	end
	wait(timeBetweenRounds)
	setInputEnabled(true)
	-- next round!
	startRound()
end

function loseRound()
	setInputEnabled(false)
	-- play an animations that tells us we lost
	for i = 1, 3 do
		for j =  numColours, 1, -1 do
			playColour(j, 0.05, j/numColours)
		end
	end
	wait(timeBetweenRounds)
	setInputEnabled(true)
	-- reset back to first round
	roundNum = 0
	startRound()
end

-- finally, plugging these functions into our onClicked function
function onClicked(player, index)
	-- play the colour that was just selected
	setInputEnabled(false)
	playColour(index, 0.2, index)
	setInputEnabled(true)
	
	-- comparing our input to the next index in the pattern
	inputIndex = inputIndex + 1
	
	if (index ~= currentPattern[inputIndex]) then
		loseRound()
	elseif (inputIndex == #currentPattern) then
		winRound()
	end
end

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.

initClickDetectors()
setInputEnabled(false)
-- wait for the player to click the center button
centerClick.MouseClick:Wait()
setInputEnabled(true)
-- start Simon says
startRound()

img4

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!

68 Likes

Nice tutorial! Easy to understand.

2 Likes

Clear and comprehensible! Really nice tutorial!

1 Like

A very nice and simple project for starters (or just anyone) who want to apply what they learned about scripting.

1 Like

Wow i never seen this before great tutorial.

2 Likes

Good Tutorial :grin:

1 Like

Nice tutorial!

I remember using that device as a child. (Apart from when all the lights appear after each successful attempt, it’s just at the end of each section of 5)

3 Likes

I remember reading this on scripting helpers. I fail to see the “simon says” theme?

Same here unless this is the new Simon Says.

1 Like

Very cool tutorial

3 Likes

Amazing tutorial, useful for those wishing to create a game like this! :+1:

1 Like

Thanks for the tutorial, curious to try it out.

1 Like

Looks Neat, I have seen some people put this to good use.

3 Likes

Amazing work. I’m excited to see what the community can do to repurpose this. Perhaps even a Bop It or even pattern difficulty progression?

2 Likes

This looks very, very good. Thanks for the tutorial!

1 Like

The amount of effort putting all of this into a thread- it’s amazing. Easy to read and easy to learn off of. Much appreciated my friend.

2 Likes

There was a (hasbro?) game you could buy at stores that was basically what is shown in the tutorial. So its not Simon says in the sense of the classic playground children’s game, but it is a version of it.

4 Likes

Very nice, This be useful for developers.

1 Like