Hello! I made this gui for the sake of having a menu with a character creation sub-menu.
Other than the fact that most of the code looks sloppy and unorganized, and the color theory within the GUI is ugly and non-existent, is there anything better I could be doing? Both scripting AND ui design wise (Except for the color, I couldn’t think of anything better.)
Any and all feedback is appreciated as the assets are listed below. Sorry if it is hard to read, my eyes are weird </3
Video Preview:
Link to uncopylocked game:
Scripts Related
Town Hall Script
local Replicated_Storage = game:GetService("ReplicatedStorage")
local Server_Script_Service = game:GetService("ServerScriptService")
local Tween_Service = game:GetService("TweenService")
local CharacterCreationModule = require(Server_Script_Service.CharacterCreationModule)
local Events_Folder = Replicated_Storage:WaitForChild("Events")
local CorrectUserIdEvent = Events_Folder:WaitForChild("CorrectUserId")
local Town_Hall_Model = script.Parent
local Interaction_Part = Town_Hall_Model.Entrance.Interaction_Part
local Proximity_Prompt = Interaction_Part.ProximityPrompt
local Guis_Folder = Replicated_Storage:WaitForChild("Guis_Folder")
local Cutscene_Gui = nil
local Confirmation_Gui = Guis_Folder:WaitForChild("Confirmation_GUI")
local Town_Hall_Gui = Guis_Folder:WaitForChild("TownHallGUI")
local ti = TweenInfo.new(
1,
Enum.EasingStyle.Quad,
Enum.EasingDirection.InOut,
0,
false,
0
)
local Connections = {
["Confirmation"] = {},
["CreateNewCharacter"] = {}
}
local function Disconnect(Type: string)
for i, v in pairs(Connections[Type]) do
v:Disconnect()
end
end
local function ExitTownHallGui(Player:Player, GuiToDelete:Instance)
local PlayerGui = Player:FindFirstChild("PlayerGui")
Tween_Service:Create( --Show's the options to enter a UserId, or use current character.
GuiToDelete:FindFirstChild("Background"),
ti,
{ Position = UDim2.new(0.5, 0, 2, 0) }
):Play()
wait(1)
GuiToDelete:Destroy()
end
local function TownHall_Logic(Player:Player)
local PlayerGui = Player:FindFirstChild("PlayerGui")
local TownHall_GuiClone
if PlayerGui:FindFirstChild("TownHallGUI") ~= nil then
TownHall_GuiClone = PlayerGui:FindFirstChild("TownHallGUI")
TownHall_GuiClone:FindFirstChild("Background").Position = UDim2.new(0.5, 0, 2, 0)
Tween_Service:Create( --Show's the options to enter a UserId, or use current character.
TownHall_GuiClone:FindFirstChild("Background"),
ti,
{ Position = UDim2.new(0.5, 0, 0.5, 0) }
):Play()
elseif not PlayerGui:FindFirstChild("TownHallGUI") then
TownHall_GuiClone = Town_Hall_Gui:Clone()
TownHall_GuiClone:FindFirstChild("Background").Position = UDim2.new(0.5, 0, 2, 0)
Tween_Service:Create( --Show's the options to enter a UserId, or use current character.
TownHall_GuiClone:FindFirstChild("Background"),
ti,
{ Position = UDim2.new(0.5, 0, 0.5, 0) }
):Play()
end
local Background = TownHall_GuiClone:FindFirstChild("Background")
local ExitButton = Background:FindFirstChild("ExitButton")
local Menu = Background:FindFirstChild("Menu")
local Features_Folder = Menu:FindFirstChild("Features")
local Buttons_Folder = Menu:FindFirstChild("Buttons")
local CNC_Background = Features_Folder:FindFirstChild("CreateNewCharacter")
TownHall_GuiClone.Parent = PlayerGui
Buttons_Folder.CreateNewCharacter.Clickable.MouseButton1Click:Connect(function(...)
print("Clicked Create New Character")
--< Fade out all of the buttons.
for _, Button in pairs(Buttons_Folder:GetChildren()) do
if Button:IsA("CanvasGroup") then
print(Button)
Button.Visible = false
end
end
CNC_Background.Visible = true
wait(1)
Tween_Service:Create( --Show's the options to enter a UserId, or use current character.
CNC_Background,
ti,
{ Position = UDim2.new(0.5, 0, 0.5, 0) }
):Play()
end)
CNC_Background.EnterUserId.MouseButton1Click:Connect(function(...)
print(Player, "Clicked Enter UserId")
Tween_Service:Create(
CNC_Background,
ti,
{ Position = UDim2.new(0.5, 0, 2, 0) }
):Play()
wait(1)
CNC_Background.Visible = false
--< Disconnect("CreateNewCharacter")
--< Tween down the CNC_Background
--< Tween up the TextBox GUI
local UserIdTextBox = Features_Folder:WaitForChild("UserIdTextBox")
UserIdTextBox.Visible = true
Tween_Service:Create(
UserIdTextBox,
ti,
{ Position = UDim2.new(0.5, 0, 0.5, 0) }
):Play()
print(UserIdTextBox)
end)
CNC_Background.UseCurrentOutfit.MouseButton1Click:Connect(function(...)
if not Player then warn("No player detected"); return end
print(Player, "Clicked Use Current Outfit")
local Character = Player.Character
if not Character then warn("Character not found"); return end
print(Character)
local HumanoidDescription = Character:FindFirstChild("Humanoid"):FindFirstChild("HumanoidDescription")
print(HumanoidDescription)
if not HumanoidDescription then warn("Humanoid or Humanoid Description not found"); return end
local new = CharacterCreationModule:CreateRigFromHumanoidDescription(HumanoidDescription, workspace, CFrame.new(28.6, 5.5, -9))
if new then
Tween_Service:Create(
CNC_Background,
ti,
{ Position = UDim2.new(0.5, 0, 2, 0) }
):Play()
Player.PlayerGui:WaitForChild("TownHallGUI"):WaitForChild("Background"):WaitForChild("Menu"):WaitForChild("Buttons"):WaitForChild("CreateNewCharacter").Visible = true
wait(0.25)
Tween_Service:Create( --Show's the options to enter a UserId, or use current character.
Player.PlayerGui:WaitForChild("TownHallGUI"):WaitForChild("Background"):WaitForChild("Menu"):WaitForChild("Buttons"):WaitForChild("CreateNewCharacter"),
ti,
{ Position = UDim2.new(0.189, 0, 0.207, 0) }
):Play()
end
end)
ExitButton.MouseButton1Click:Connect(function(...)
print(Player, "Exit Click!")
ExitTownHallGui(Player, TownHall_GuiClone)
end)
end
local function Show_Confirmation_Popup(Player:Player)
local PlayerGui = Player:FindFirstChild("PlayerGui")
local Confirmation = Confirmation_Gui:Clone()
Confirmation.Background.Location_Text.Text = "Town Hall"
Confirmation.Background.Position = UDim2.new(0.5, 0, 2, 0) --< Bottom Center off of the screen
Tween_Service:Create(
Confirmation.Background, --< Animates the background
ti,
{ Position = UDim2.new(0.5, 0, 0.5, 0) }
):Play()
local yesConnection = table.insert(Connections.Confirmation, Confirmation.Background.Yes.MouseButton1Click:Connect(function()
Disconnect("Confirmation")
print("Yes")
Tween_Service:Create(
Confirmation.Background, --< Animates the background
ti,
{ Position = UDim2.new(0.5, 0, 2, 0) }
):Play()
wait(1)
Confirmation:Destroy() --< Destroys the ScreenGui Object
TownHall_Logic(Player)
end))
local noConnection = table.insert(Connections.Confirmation, Confirmation.Background.No.MouseButton1Click:Connect(function()
Disconnect("Confirmation")
print("No")
Tween_Service:Create(
Confirmation.Background, --< Animates the background
ti,
{ Position = UDim2.new(0.5, 0, 2, 0) }
):Play()
wait(1)
Confirmation:Destroy()
end))
Confirmation.Parent = PlayerGui
end
local function Test(Player:Player)
print(Player.Name .. " has initiated the Test")
Show_Confirmation_Popup(Player)
end
Proximity_Prompt.Triggered:Connect(Test)
--CharacterCreationModule:CreateRigFromUserId(128160667, workspace, CFrame.new(28.6, 5.5, -9))
--CharacterCreationModule:CreateRigFromHumanoidDescription(128160667, workspace, CFrame.new(14.6, 5.5, -9))
CorrectUserIdEvent.OnServerEvent:Connect(function(Player:Player, UserId:number, Element:Instance)
print(Player, UserId)
if not UserId then warn("No UserId Provided"); return end
Tween_Service:Create(
Element,
ti,
{Position = UDim2.new(0.5, 0, 2, 0)}
):Play()
wait(1)
Element.Visible = false
CharacterCreationModule:CreateRigFromUserId(UserId, workspace, CFrame.new(28.6, 5.5, -9))
Player.PlayerGui:WaitForChild("TownHallGUI"):WaitForChild("Background"):WaitForChild("Menu"):WaitForChild("Features"):WaitForChild("CreateNewCharacter").Visible = true
Player.PlayerGui:WaitForChild("TownHallGUI"):WaitForChild("Background"):WaitForChild("Menu"):WaitForChild("Buttons"):WaitForChild("CreateNewCharacter").Visible = true
wait(0.25)
Tween_Service:Create( --Show's the options to enter a UserId, or use current character.
Player.PlayerGui:WaitForChild("TownHallGUI"):WaitForChild("Background"):WaitForChild("Menu"):WaitForChild("Buttons"):WaitForChild("CreateNewCharacter"),
ti,
{ Position = UDim2.new(0.189, 0, 0.207, 0) }
):Play()
end)
Character Creation Module Script
Located in Server Script Service
--!nocheck
--< Services >--
local Players_Service = game:GetService("Players")
--< Modules >--
--< Variables >--
local CharacterCreationModule = {}
CharacterCreationModule.__index = CharacterCreationModule
--< Functions >--
--[[
Creates a new R6 only model rig with the given UserId, parents the rig to the "Parent" parameter, and then returns the rig as a variable for further use.
-----------------------------------------------------------------------------------------------------------------------------------------------
]]
function CharacterCreationModule:CreateRigFromUserId(
UserId : number,
Parent : Instance?,
CFramePosition : CFrame
): Model
if not UserId then warn("UserId is nil."); return end
if not Parent then warn("You did not set the parent parameter."); return end
if not CFramePosition then warn("No position has been set."); return end
local NewRig = Players_Service:CreateHumanoidModelFromUserId(UserId)
NewRig.PrimaryPart:PivotTo(CFramePosition)
NewRig.Parent = Parent
return NewRig
end
--[[
Creates a new R6 and/or R15 model rig with the given UserId and retrieves a HumanoidDescription, parents the rig to the "Parent" parameter, and then returns the rig as a variable for further use.
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
]]
function CharacterCreationModule:CreateRigFromUserIdToHumanoidDescription(
UserId : number,
Parent : Instance?,
CFramePosition : CFrame
):Model
if not UserId then warn("UserId is nil."); return end
if not Parent then warn("You did not set the parent parameter."); return end
if not CFramePosition then warn("No position has been set."); return end
local HumanoidDescriptionFromUserId = Players_Service:GetHumanoidDescriptionFromUserId(UserId)
local NewRig = Players_Service:CreateHumanoidModelFromDescription(HumanoidDescriptionFromUserId, Enum.HumanoidRigType.R15)
NewRig.PrimaryPart:PivotTo(CFramePosition)
NewRig.Parent = Parent
return NewRig
end
--[[
Creates a new R6 and/or R15 model rig with the given HumanoidDescription, parents the rig to the "Parent" parameter, and then returns the rig as a variable for further use.
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
]]
function CharacterCreationModule:CreateRigFromHumanoidDescription(
HumanoidDescription : HumanoidDescription,
Parent : Instance?,
CFramePosition : CFrame
):Model
if not HumanoidDescription then warn("HumanoidDescription is nil."); return end
if not Parent then warn("You did not set the parent parameter."); return end
if not CFramePosition then warn("No position has been set."); return end
local NewRig = Players_Service:CreateHumanoidModelFromDescription(HumanoidDescription, Enum.HumanoidRigType.R15)
NewRig.PrimaryPart:PivotTo(CFramePosition)
NewRig.Parent = Parent
return NewRig
end
--< Connections >--
return CharacterCreationModule
UIFX Module Script
I just want to give a quick thank you to @AltaccVedoxxx00 for creating the Module Script (UIFX - Animation Module) and @KingBlueDash for modifying it to function better (UIFX - Animation Module - #4 by KingBlueDash)
local SoundService = game:GetService("SoundService")
local TweenService = game:GetService("TweenService")
local HoveringSound = SoundService:FindFirstChild("HoveringSound")
local ClickingSound = SoundService:FindFirstChild("ClickingSound")
local UIFX = {}
UIFX.__index = UIFX
type tab = {[any]: any}
function UIFX:HoverBigger(
Element: GuiButton,
Size: UDim2,
SwapsColors: boolean,
Rotates: boolean,
Threshold: {any},
Duration: number,
Style: Enum.EasingStyle,
Direction: Enum.EasingDirection,
AddToAConnectionsTable: {boolean | tab}
)
local OriginalSize = Element.Size
local OriginalBGColor = Element.BackgroundColor3
local OriginalRotation = Element.Rotation
local OriginalTextColor, OriginalImageColor, OriginalIconImageColor, Icon
if Element:FindFirstChild("Icon") then
Icon = Element:FindFirstChild("Icon")
OriginalIconImageColor = Icon.ImageColor3
end
if Element:IsA("TextButton") or Element:IsA("TextBox") then
OriginalTextColor = Element.TextColor3
elseif Element:IsA("ImageButton") then
OriginalTextColor = nil
OriginalImageColor = Element.ImageColor3
end
local function OnMouseEnter()
task.wait(.0125)
if HoveringSound then
HoveringSound:Play()
end
local SwappedColorAmount
if Threshold then
if Threshold[3] then
SwappedColorAmount = Threshold[3]
else
SwappedColorAmount = 1
end
end
TweenService:Create(
Element,
TweenInfo.new(Duration, Style, Direction),
{Size =
UDim2.new(
OriginalSize.X.Scale + Size.X.Scale,
OriginalSize.X.Offset + Size.X.Offset,
OriginalSize.Y.Scale + Size.Y.Scale,
OriginalSize.Y.Offset + Size.Y.Offset
),
Rotation = (Rotates and math.random(Threshold[1], Threshold[2]) or OriginalRotation)
}
):Play()
if SwapsColors then
local InvertedBGColor3 = Color3.new(
SwappedColorAmount - Element.BackgroundColor3.R,
SwappedColorAmount - Element.BackgroundColor3.G,
SwappedColorAmount - Element.BackgroundColor3.B
)
local InvertedTextColor3, InvertedImageColor3
if Element:IsA("TextButton") or Element:IsA("TextBox") then
InvertedTextColor3 = Color3.new(
SwappedColorAmount - Element.TextColor3.R,
SwappedColorAmount - Element.TextColor3.G,
SwappedColorAmount - Element.TextColor3.B
)
TweenService:Create(
Element,
TweenInfo.new(Duration * 1.75, Style, Direction),
{TextColor3 = InvertedTextColor3, BackgroundColor3 = InvertedBGColor3}
):Play()
elseif Element:IsA("ImageButton") then
InvertedImageColor3 = Color3.new(
SwappedColorAmount - Element.ImageColor3.R,
SwappedColorAmount - Element.ImageColor3.G,
SwappedColorAmount - Element.ImageColor3.B
)
TweenService:Create(
Element,
TweenInfo.new(Duration * 1.75, Style, Direction),
{BackgroundColor3 = InvertedBGColor3, ImageColor3 = InvertedImageColor3}
):Play()
end
end
end
local function OnMouseLeave()
task.wait(.0125)
TweenService:Create(
Element,
TweenInfo.new(Duration, Style, Direction),
{Size = OriginalSize, Rotation = OriginalRotation}
):Play()
if SwapsColors then
if Element:IsA("TextButton") or Element:IsA("TextBox") then
TweenService:Create(
Element,
TweenInfo.new(Duration * 1.75, Style, Direction),
{BackgroundColor3 = OriginalBGColor, TextColor3 = OriginalTextColor}
):Play()
elseif Element:IsA("ImageButton") then
TweenService:Create(
Element,
TweenInfo.new(Duration * 1.75, Style, Direction),
{BackgroundColor3 = OriginalBGColor, ImageColor3 = OriginalImageColor}
):Play()
end
end
end
local EnterConnection
local LeaveConnection
EnterConnection = Element.MouseEnter:Connect(OnMouseEnter)
LeaveConnection = Element.MouseLeave:Connect(OnMouseLeave)
local function StopFunction()
OnMouseLeave()
EnterConnection:Disconnect()
LeaveConnection:Disconnect()
if Element:FindFirstChild("__uiFXMouseEntered") then
Element:FindFirstChild("__uiFXMouseEntered"):Destroy()
end
if Element:FindFirstChild("__uiFXMouseLeft") then
Element:FindFirstChild("__uiFXMouseLeft"):Destroy()
end
end
if AddToAConnectionsTable[1] and AddToAConnectionsTable[2] then
table.insert(AddToAConnectionsTable[2], StopFunction)
else
return StopFunction
end
end
function UIFX:ButtonPress(
Element: GuiButton,
Size: UDim2,
Rotates: boolean,
Threshold: {any},
Duration: number,
Style: Enum.EasingStyle,
Direction: Enum.EasingDirection,
AddToAConnectionsTable: {boolean | tab}
)
local OriginalSize = Element.Size
local OriginalRotation = Element.Rotation
local function OnMouseDown()
if ClickingSound then
ClickingSound:Play()
end
TweenService:Create(
Element,
TweenInfo.new(Duration, Style, Direction),
{
Size = UDim2.new(
OriginalSize.X.Scale + Size.X.Scale,
OriginalSize.X.Offset + Size.X.Offset,
OriginalSize.Y.Scale + Size.Y.Scale,
OriginalSize.Y.Offset + Size.Y.Offset
),
Rotation = (Rotates and math.random(Threshold[1], Threshold[2]) or OriginalRotation)
}
):Play()
end
local function OnMouseUp()
TweenService:Create(
Element,
TweenInfo.new(Duration, Style, Direction),
{Size = OriginalSize, Rotation = OriginalRotation}
):Play()
end
local EnterConnection
local LeaveConnection
if Element:IsA("CanvasGroup") then
--print(tostring(Element) .. " is a CanvasGroup")
EnterConnection = Element.Clickable.MouseButton1Down:Connect(function()
OnMouseDown()
end)
LeaveConnection = Element.Clickable.MouseButton1Up:Connect(function()
OnMouseUp()
end)
else
EnterConnection = Element.MouseButton1Down:Connect(function()
OnMouseDown()
end)
LeaveConnection = Element.MouseButton1Up:Connect(function()
OnMouseUp()
end)
end
local function StopFunction()
OnMouseUp()
EnterConnection:Disconnect()
LeaveConnection:Disconnect()
end
if AddToAConnectionsTable[1] and AddToAConnectionsTable[2] then
table.insert(AddToAConnectionsTable[2], StopFunction)
else
return StopFunction
end
end
return UIFX
Local Script located inside of the TextBox named UserIdTextBox
This script just makes sure that only a number (0-9) is entered, and wont return true if anything other than a number was entered.
local Replicated_Storage = game:GetService("ReplicatedStorage")
local Events_Folder = Replicated_Storage:WaitForChild("Events")
local CorrectUserIdEvent = Events_Folder:WaitForChild("CorrectUserId")
-- Get the Textbox object
local textBox = script.Parent
-- Function to validate user input
local function validateInput(input:string) :boolean
-- Check if the input is empty
if input == "" then
return false
end
-- Check if the input contains non-numeric or non-decimal characters
if string.match(input, "[^%d]") then
return false
end
return true
end
local colorNormal = Color3.new(1, 1, 1) -- white
local colorWrong = Color3.new(1, 0, 0) -- red
local colorCorrect = Color3.new(0, 1, 0) -- green
-- Function to handle user input
local function handleInput()
local input = textBox.Text
if not validateInput(input) then
textBox.Text = "" --Custom text for invalid entry
--Executes when there is not valid input
textBox.BackgroundColor3 = colorNormal
print("False")
else
--Executes when there is valid input
textBox.BackgroundColor3 = colorCorrect
textBox.Text = "Loading Character..."
print("True")
wait(1)
CorrectUserIdEvent:FireServer(input, textBox)
textBox.BackgroundColor3 = colorNormal
textBox.Text = "Done!"
wait(0.5)
textBox.Text = ""
end
end
-- Connect the handleInput function to the TextBox object's events
textBox.Focused:Connect(function()
-- Clear the Textbox when the user focuses on it
textBox.Text = ""
end)
textBox.FocusLost:Connect(function()
handleInput()
end)
UI_FX local script located throughout the GUI's
local Replicated_Storage = game:GetService("ReplicatedStorage")
local UIFX = require(Replicated_Storage.UIFX)
local Hover = UIFX.HoverBigger(
nil,
script.Parent,
UDim2.new(0.01, 0, 0.01, 0),
false,
true,
{-5, 5},
.125,
Enum.EasingStyle.Sine,
Enum.EasingDirection.InOut,
{false}
)
local Press = UIFX.ButtonPress(
nil,
script.Parent,
UDim2.new(0.02, 0, 0.02, 0),
true,
{-5, 5},
.125,
Enum.EasingStyle.Sine,
Enum.EasingDirection.InOut,
{false}
)
Local Scripts named BackgroundAnimation
Very sloppy code, I know.
while true do
game:GetService("TweenService"):Create(script.Parent, TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out), {Position = UDim2.new(-1, 90, -1, 0)}):Play()
wait(1)
script.Parent.Position = UDim2.new(-1, 0, -1, 0)
end