Make a reflecting mirror

Learn to make a working mirror reflecting you and others.

by Mark_GoodMan

Author Avatar

Introduction

Hello, my name is Mark_GoodMan, today i'll show you how to make a REALISTIC MIRROR, thanks to sStillWater for this script, like a VERY BIG THANKS to him lol. Right, now let's get into making the MIRROR!

Note!

Before you say anything like i stole sStillWater's script or anything, i made this tutorial because there are many people having trouble with making a mirror, the video sStillWater made was not fully detailed and 70% people saying it doesn't work for them, so before saying this script wasn't made by me and i stole it from him, i made this to help people how to make a mirror, 100% fully detailed and accuracy, the video on youtube is a bit too fast and slow reader may have trouble making the mirror, so i made this to help people learn how to completely make a working mirror, and to make your game success, thank you for reading!

1. Making parts

I know this may be easy just by adding parts, but no, you need to change it's details correctly.

Now insert a Model, then rename it to "Mirror model", then insert "Part" in it (as shown in the image)

img|165x45

Now after you add the Part, scale the part to full size of map (as shown in the image)

img|135x80

After it, now select it and go to properties and then change the following appearance (as shown in the images below).

img|85x45

2. Scripting mirrors

After you change the properties of the part, now go to StarterGui and insert "LocalScript"

img|90x45

After putting LocalScript in StarterGui, rename it to mirrors.

Now click on LocalScript (mirrors) and enter the following script:

-- StarterGUI:
-- LocalScript:
-- Name The LocalScript: mirrors

local player = game:GetService("Players").LocalPlayer
local character; repeat wait() character = player.Character until character
local camera = game:GetService("Workspace").CurrentCamera

local rs = game:GetService("RunService").RenderStepped
local reflections = require(script:WaitForChild("reflections"))

local mirrors = game:GetService("Workspace"):WaitForChild("Mirror model")

rs:connect(function()
    reflections:clearCharacters()
    local characters = {}
    for _, player in pairs(game:GetService("Players"):GetPlayers()) do
        table.insert(characters, player.Character)
    end
    for _, mirror in pairs(mirrors:GetChildren()) do
        if mirror:IsA("BasePart") then
            reflections:drawWorld(mirror, game:GetService("Workspace"), {mirrors, unpack(characters)})
            reflections:drawCharacters(characters, mirror)
        end
    end
end)

Now after you done putting the script into the LocalScript (mirrors), now right click on LocalScript and insert ModuleScript

img|85x45

After inserting ModuleScript, rename it to reflections.

Now click on ModuleScript (reflections) and enter the following script:

--// Declarations

local class = {}
local camera = game:GetService("Workspace").CurrentCamera

local renderPacket = {}

local reflections = Instance.new("Model", camera)
local users = Instance.new("Model", reflections)
local environment = Instance.new("Model", reflections)
reflections.Name = "reflections"
users.Name = "users"
environment.Name = "environment"

--// Functions

--[[
    Collects all items recursively in a certain class range.
    @param children: A table full of instances that the function will search
    @param class: The type of instance (IsA method used) that is required
    @param tab: If adding to another table this parameter may be used.
    @return A table containing all the instances that met the requirements.
--]]

function gather(children, class, tab)
    local tab = tab and tab or {}
    for _, child in pairs(children) do
        if child:IsA(class) then
            table.insert(tab, child)
        end
        tab = gather(type(child) == "table" and child or child:GetChildren(), class, tab)
    end
    return tab
end

--[[
    Collects all items recursively in a certain class range with a given instance ignore list (including decendants).
    @param children: A table full of instances that the function will search
    @param class: The type of instance (IsA method used) that is required
    @param ignores: Table of instances that can be ignored
    @return A table containing all the instances that met the requirements.
--]]

function gather_Ignores(children, class, ignores)
    local tab = {}
    for _, part in pairs(gather(children, class)) do
        local pass = true
        for _, thing in pairs(ignores) do
            if part == thing or part:IsDescendantOf(thing) then
                pass = false
            end
        end
        if pass then
            table.insert(tab, part)
        end
    end
    return tab
end

--// Methods

--[[
    Reflects item on local x axis.
    @param item: The instance (model or basepart) that will be reflected.
    @param against: The instance (basepart) that will be used as the reflection origin (mirror).
--]]

function class:reflect(item, against)
    local cf = item:IsA("BasePart") and item.CFrame or item.PrimaryPart.CFrame
    local x, y, z, r00, r01, r02, r10, r11, r12, r20, r21,r22 = against.CFrame:toObjectSpace(cf):components()
    local newCf = against.CFrame:toWorldSpace(CFrame.new(-x ,y ,z , r00, -r01, -r02, -r10, r11, r12, -r20, r21, r22))
    if item:IsA("BasePart") then
        item.CFrame = newCf
        if item:IsA("CornerWedgePart") then
            item.Size = Vector3.new(item.Size.z, item.Size.y, item.Size.x)
            item.CFrame = item.CFrame * CFrame.Angles(0,math.rad(90),0)
        end
    elseif item:IsA("Model") then
        item:SetPrimaryPartCFrame(newCf)
    end
end

--[[
    Reflects item on local x axis.
    @param item: The instance (model or basepart) that will be reflected.
    @param against: The instance (basepart) that will be used as the reflection origin (mirror).
--]]

function class:drawWorld(against, parent, ignores)
    table.insert(ignores, camera)
    local renders = gather_Ignores({parent}, "BasePart", ignores)
    renderPacket[against] = renderPacket[against] or {}
    for _, part in pairs(renders) do
        if (not part.Locked or part.Parent:IsA("Hat")) and part.Transparency < 1 then
            if not renderPacket[against][part] or renderPacket[against][part].cf ~= part.CFrame then
                if renderPacket[against][part] then
                    renderPacket[against][part].focus:Destroy()
                end
                local focus = part:Clone()
                local joints = gather(focus:GetChildren(), "JointInstance")
                for _, joint in pairs(joints) do joint:Destroy() end
                focus.Anchored = true
                self:reflect(focus, against)
                focus.Parent = environment
                renderPacket[against][part] = {
                    cf = part.CFrame;
                    origin = part;
                    focus = focus;
                    event = part.AncestryChanged:connect(function() focus:Destroy() end);
                }
            end
        end
    end
end

function class:drawCharacters(characters, against)
    for _, character in pairs(characters) do
        character.Archivable = true
        local character = character:Clone()
        local hum = character:FindFirstChild("Humanoid")
        local scripts = gather(character:GetChildren(), "BaseScript")
        local parts = gather(character:GetChildren(), "BasePart")
        local joints = gather(character:GetChildren(), "JointInstance")
        if hum then
            hum.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None
        end
        for _, src in pairs(scripts) do src:Destroy() end
        for _, joint in pairs(joints) do joint:Destroy() end
        for _, part in pairs(parts) do
            part.Anchored = true
            part.CanCollide = false
            self:reflect(part, against)
        end
        character.Parent = users
    end
end

function class:clearCharacters()
    users:ClearAllChildren()
end

function class:cleanup()
    environment:ClearAllChildren()
    users:ClearAllChildren()
    renderPacket = {}
end

return class

And after you pasted the script in the ModuleScript (reflections), your done, now try to run the game in studio, you should see yourself on the otherside of the mirrors as it follows all of your movements correctly, just like i did see myself, the script is still working btw. It also reflect model and NPC and props, let's just say for short it will definitely reflect everything it sees.

img|60x45

BUG FIXING!

So recently i did some mirror test and they all worked, but the problem here is that they reflects the wrong way (as shown in the image)

img|55x45

The reason for this is because the scale is at the wrong position, if you rotate it's still the same, to fix this we just need to rescale it to the other side of the map (not rotating)

img|80x45

img|80x45

img|80x45

After scaling it to the other side of the map, now it should reflect you normally and fine, that's all for the bugs fixing!

yet this is script made by sStillWater, shoutout to him. Also i'll be checking on this tutorial, so if the script is outdated or not working now, then i'll try to find a way to fix it.

Last time viewed this tutorial was 3/27/2020, thank you for reading :D