Advanced Dance Floor [mad city]

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve?

I’m interested in the key functions surrounding this specific dance floor in Mad City I’ve never seen anything like it and one would assume it would be a lag machine, it seems to pause when the player leaves the building and resume when the player returns.

  1. What is the issue?

I have replicated this dance floor somewhat, different changing lights and colours patterns was easy but I am neglecting some features for instance it has a sine wave like effect and or some form of tween it also features a galaxy effect to which I have seen and have access to similar particle effects.

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

I have figured out the galaxy and particle effect and have applied different colours to my dance floor, as well as different colour patterns I am now looking to optimise it and add some form of flare I am completely inspired by this dance floor now that I’ve seen it and would love to have some light shed on how it works further or parts I may have missed


I have uploaded two videos show-casing the effect I am attempting to achieve and I am mind boggled at the complexity of this small thing, thank you in regards to any responses in advanced.

I’m also very interested in how they achieved the lights

2 Likes

This looks like something fun to make!

I am neglecting some features for instance it has a sine wave like effect

For the sine wave, I threw something together to attempt to mimic it, and I think this might help with that part you are missing.

local RunService = game:GetService("RunService")

local DanceFloor = game:GetService("Workspace"):WaitForChild("DanceFloor") -- Folder in workspace.

local FloorTiles = {} -- 2D array containing each tile

-- What the table looks like:
--[[ 4x4 dance floor example
	{
		[1] = {[1] = Part; [2] = Part; [3] = Part; [4] = Part;};
		[2] = {[1] = Part; [2] = Part; [3] = Part; [4] = Part;};
		[3] = {[1] = Part; [2] = Part; [3] = Part; [4] = Part;};
		[4] = {[1] = Part; [2] = Part; [3] = Part; [4] = Part;};
	}
]]

-- Note: It is probably best to simulate this on the client and dynamically load and unload it
-- to avoid massive server lag.
-- This is server sided just for testing purposes.

local function CreateFloor(position:Vector3, tile_size:Vector3, padding:Vector2, grid_size:Vector2)
	FloorTiles = {}
	
	local current_position = position
	
	-- Y column
	for y = 1, grid_size.X, 1 do
		FloorTiles[y] = {}
		
		-- X column
		for x = 1, grid_size.Y, 1 do
			local new_tile = Instance.new("Part")
			new_tile.Position = current_position
			new_tile.Size = tile_size
			new_tile.Anchored = true
			new_tile.CanCollide = false
			new_tile.Parent = DanceFloor
			
			-- Setup position for next tile
			current_position += Vector3.new(tile_size.X, 0, 0) + Vector3.new(padding.X, 0, 0)
			
			FloorTiles[y][x] = new_tile
		end 
		
		-- Reset position X at the end of each X column
		local old_z, old_y = current_position.Z, current_position.Y
		current_position = Vector3.new(position.X, old_y, old_z)
		
		-- Shift position over for next X column
		current_position += Vector3.new(0, 0, tile_size.Z) + Vector3.new(0, 0, padding.Y)
	end
end


-- Create dance floor
CreateFloor(
	Vector3.new(0, 1, 0), -- Grid position
	Vector3.new(4, 1, 4), -- Tile size
	Vector2.new(1, 1), -- Padding
	Vector2.new(10, 10) -- Grid size
)

-- Sine wave example
RunService.Heartbeat:Connect(function(DeltaTime)
	for y = 1, #FloorTiles, 1 do
		for x = 1, #FloorTiles[y] do
			local part = FloorTiles[y][x]
			
			local old_x, old_z = part.Position.X, part.Position.Z
			local wave = math.sin((tick() * 2) + (y + x)) * 0.5
			
			-- for the wave, just add the y and x indexes together to get the unique index for each part.
			-- then add that to the tick. this makes it like a 'wave'.
			part.Position = Vector3.new(old_x, wave + 0.5, old_z)
		end
	end
end)

I’ll drop a quick explanation here,
I store all the parts in a 2D array, which is like a grid that tells me where each tile is, and importantly, I can index them using X and Y coordinates.
Then, I loop through each part from top left, to bottom right. For every part, I add a sine wave to its Y position, which makes the wave effect. (I am not very knowledgeable about trigonometry or those functions in general, so if I say something wrong, please correct me.). But, I also have to add something to the sine wave in order to make each part offset from the wave. So, I add the X and Y values of each part together to get the part number of the part. (This is sort of hard for me to articulate, as I am not very good at explaining some things, so I’ll just say that the part number is like the number of the part, ex: part 1, part 2, part 3, etc.) Then, I add this value to the sine wave to offset the part from the wave. which makes it further along the wave than the parts before it. (Again, I apologize if this is unclear, I am not the best explainer.)

[EDIT] Whoops, I realized that the dance floor that I made was slower than the video you provided, So, just change the *2 on line 70 to make it faster or slower. Larger numbers are faster.

local wave = math.sin((tick() * 2) + (y + x)) * 0.5 -- Change the tick() * 2 to whatever you want.

I’m not saying this is how Mad City does it, but it could be a possibility.

Video of it in action:

Hope this helps with that aspect of it!
Good luck with your project!

5 Likes

Thank you so much for this resource and advanced response.

It will act as a fountain of knowledge to a learner, Complexitify has just added a huge aspect of simplicity to my complex project.

I’m definitely going to tinker and play around with this whilst learning scripting.

I understand what you’re saying about the indexing order.

The rest is slightly harder to digest.

2 Likes

If you would like to continue further, I’m interested in how we could alter individual colour patterns?

Would it be a node based system as a best approach?

Anyone can do a random brick colour.

I’m talking more giving inputs directly to the table, that dynamically give changes.

Then we can input data such as changes to properties nested into functions with different colour patterns that will change our tables part properties.

For example this would be a node representation of all of the parts with their colours values:

[B],[R],[G],[G],[R],[B]
[R],[R],[R],[R],[R],[R]
[R],[R],[P],[P],[R],[R]
[R],[R],[P],[P],[R],[R]
[R],[R],[R],[R],[R],[R]
[B],[R],[G],[G],[R],[B]

R,G,B,Y are representative of keys for Red, Green, Blue, Yellow and say we wanted to individually have control over what colours to change or nodes to adapt?

Or to even make it somewhat toggle-able?

I have represented this as a node based system let’s say I would like to have functions representing different colour to the parts and have some form of control over the grid and what patterns appear.

Is there a way I could input to the table directly and change these values?

Sorry for going so in-depth but I believe this post will help other users and it’s important for me myself to learn and write correctly now after years of tutorial hell and learning others mistakes and replicating them.

This has greatly helped me with my dance floor project, it’ll probably come up in other projects also, I couldn’t thank you enough regardless!

Here is an example similar to some more effects I’m attempting to achieve. watch all or 0:40 secs to 1 min. It would seem that he is directly pulsing some form of gradient through them and has direct control over each part as he’s not just tweeting them but he is also dynamically controlling an entire line of colours.

If anyone else is interested, there is a model for this in the comments of the video and I’ll be dissecting it and attempting the advanced dance floor, the journey begins.

2 Likes

Thank you!

The rest is slightly harder to digest.

I’ll try to explain further on my current limited knowledge of sine functions. (If anyone else reads this and knows more than me, or notices something I said wrong, please correct me.)

When using math.sin(x), to create a wave, you input a value that constantly goes up. So I use tick(), which returns the amount of seconds that has passed since January 1, 1970. Which leaves me with math.sin(tick()). This will now return a value between -1 and 1. It will progress between those two values every time tick() changes to make a wave. Now, If I just applied this to the dance floor as is, I would get something like this:


Not as exciting right?

So, to make the dance floor more like a wave, I offset the tick value by the tile number. This puts each tile further along the wave than the others.
I made a quick graph to show what I mean:


By adding a number to tick(), it offsets the wave returned.

Hope this helps you digest it!

Yes, I would think that using another 2D array, or even the same one for color maps would probably fit this project best.

Yes! We can modify the script to contain a new table for the ColorMap, then initialize the ColorMap along with the grid in the CreateGrid() function, then add another function to apply the ColorMap to the grid like so:

local FloorTiles = {} -- 2D array containing each tile
local ColorMap = {} -- 2D array containing each tile's color.

-- Note: It is probably best to simulate this on the client and dynamically load and unload it
-- to avoid massive server lag.
-- This is server sided just for testing purposes.

local function ApplyColorMap()
	for y = 1, #FloorTiles, 1 do
		for x = 1, #FloorTiles[y] do
			local part = FloorTiles[y][x]

			if ColorMap[y][x] then
				part.Color = ColorMap[y][x]
			end
		end
	end
end

local function CreateFloor(position:Vector3, tile_size:Vector3, padding:Vector2, grid_size:Vector2)
	FloorTiles = {}
	
	local current_position = position
	
	-- Y column
	for y = 1, grid_size.X, 1 do
		FloorTiles[y] = {}
		ColorMap[y] = {}
		
		-- X column
		for x = 1, grid_size.Y, 1 do
			local new_tile = Instance.new("Part")
			new_tile.Position = current_position
			new_tile.Size = tile_size
			new_tile.Anchored = true
			new_tile.CanCollide = false
			new_tile.Parent = DanceFloor
			
			-- Setup position for next tile
			current_position += Vector3.new(tile_size.X, 0, 0) + Vector3.new(padding.X, 0, 0)
			
			FloorTiles[y][x] = new_tile
			ColorMap[y][x] = Color3.new(1, 1, 1) -- Initialize to white, or whatever color you like.
		end 
		
		-- Reset position X at the end of each X column
		local old_z, old_y = current_position.Z, current_position.Y
		current_position = Vector3.new(position.X, old_y, old_z)
		
		-- Shift position over for next X column
		current_position += Vector3.new(0, 0, tile_size.Z) + Vector3.new(0, 0, padding.Y)
	end
	
	ApplyColorMap()
end

So, I also got the model, and dug into it. It appears that the person in the video is using perlin noise to create the waving gradient effects.

I modified the script a little to include a demo of possible perlin noise effects.

local RunService = game:GetService("RunService")

local DanceFloor = game:GetService("Workspace"):WaitForChild("DanceFloor")

local FloorTiles = {} -- 2D array containing each tile
local ColorMap = {} -- 2D array containing each tile's color.

-- What the table looks like:
--[[ 4x4 dance floor example
	{
		[1] = {[1] = Part; [2] = Part; [3] = Part; [4] = Part;};
		[2] = {[1] = Part; [2] = Part; [3] = Part; [4] = Part;};
		[3] = {[1] = Part; [2] = Part; [3] = Part; [4] = Part;};
		[4] = {[1] = Part; [2] = Part; [3] = Part; [4] = Part;};
	}
]]

-- Note: It is probably best to simulate this on the client and dynamically load and unload it
-- to avoid massive server lag.
-- This is server sided just for testing purposes.

local function ApplyColorMap()
	for y = 1, #FloorTiles, 1 do
		for x = 1, #FloorTiles[y] do
			local part = FloorTiles[y][x]

			if ColorMap[y][x] then
				part.Color = ColorMap[y][x]
			end
		end
	end
end

local function CreateFloor(position:Vector3, tile_size:Vector3, padding:Vector2, grid_size:Vector2)
	FloorTiles = {}
	
	local current_position = position
	
	-- Y column
	for y = 1, grid_size.X, 1 do
		FloorTiles[y] = {}
		ColorMap[y] = {}
		
		-- X column
		for x = 1, grid_size.Y, 1 do
			local new_tile = Instance.new("Part")
			new_tile.Position = current_position
			new_tile.Size = tile_size
			new_tile.Anchored = true
			new_tile.CanCollide = false
			new_tile.CastShadow = false -- Optimizations
			new_tile.Parent = DanceFloor
			
			-- Setup position for next tile
			current_position += Vector3.new(tile_size.X, 0, 0) + Vector3.new(padding.X, 0, 0)
			
			FloorTiles[y][x] = new_tile
			ColorMap[y][x] = Color3.new(1, 1, 1) -- Initialize to white, or whatever color you like.
		end 
		
		-- Reset position X at the end of each X column
		local old_z, old_y = current_position.Z, current_position.Y
		current_position = Vector3.new(position.X, old_y, old_z)
		
		-- Shift position over for next X column
		current_position += Vector3.new(0, 0, tile_size.Z) + Vector3.new(0, 0, padding.Y)
	end
	
	ApplyColorMap()
end

local function PerlinNoiseTest(color, offset)
	for y = 1, #ColorMap, 1 do
		for x = 1, #ColorMap[y] do
			local noise = math.noise((x / 10) + offset, (y / 10) + offset) * 0.5
			-- noise returns a value between -0.5 and 0.5. The values are
			-- procedurally generated.
			ColorMap[y][x] = Color3.new(color.R + noise, color.G + noise, color.B + noise)
		end
	end
end


-- Create dance floor
CreateFloor(
	Vector3.new(0, 1, 0), -- Grid position
	Vector3.new(4, 1, 4), -- Tile size
	Vector2.new(1, 1), -- Padding
	Vector2.new(10, 10) -- Grid size
)

-- Update tiles
RunService.Heartbeat:Connect(function(DeltaTime)
	for y = 1, #FloorTiles, 1 do
		for x = 1, #FloorTiles[y] do
			local part = FloorTiles[y][x]
			
			-- Sine wave example
			local old_x, old_z = part.Position.X, part.Position.Z
			local wave = math.sin((tick() * 2) + (y + x)) * 0.5
			
			-- for the wave, just add the y and x indexes together to get the unique index for each part.
			-- then add that to the tick. this makes it like a 'wave'.
			part.Position = Vector3.new(old_x, wave + 0.5, old_z)
		end
	end
end)


while true do
	local color = BrickColor.Random().Color
	
	for i = 1, 300 do
		PerlinNoiseTest(color, i / 300)
		ApplyColorMap()
		task.wait(0.03)
	end
	task.wait(1)
end

This was made using some code from the person in the video.

Also, a few colors might be a little distorted when applying noise. I believe this is because the colors are like (0.94, 0.32, 0.45) and the noise adds it so it goes over 1 on some values. It can probably be fixed by clamping the individual color values to 1 and 0.

To be clear, I am new to perlin noise, so I recommend reading this if you want more info:

Hope this helps with the colors!

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.