Handy Scripts - A Collection of Handy Scripts

A Resource for Lazy Programmers

Hello there. I am just open sourcing this. The scripts here I created because I found myself re-writing a lot of code similar to this. So, I thought it would be useful for me to open source this. Each script comes with a description on where when and how to use it. Most of these scripts aren’t really essential but I found if you find yourself re-writing a lot of these then you can simply copy-paste and use it in game jams or to reach deadlines etc.

Types

Love autocomplete? So, do I . As such I typed all the “untyped” but documented objects. Making autocomplete do the rest of the work for you. I found the most useful is the ChatService types which make scripting ChatModules a cinch.

Type
 local Types = {} export type dictionary<I,V> = {[I]: V} export type array<V> = {[number]: V} export type TerrainMaterial = {	Size: Vector3,	[number]: {{Enum.Material}} } export type TerrainOccupancy = {	Size: Vector3,	[number]: {{number}} } export type OnlineFriend = {	VisitorId: number,	UserName :string,	DisplayName :string,	LastOnline :string,	IsOnline :boolean,	LastLocation: string,	PlaceId: number,	GameId:	string,	LocationType: number } export type HttpRequestOptions = {	Url: string	,	Method: string?,	Headers: dictionary<string,string>?,	Body: string? } export type HttpResponse = {	Success: boolean,	StatusCode:	number,	StatusMessage: string,	Headers: dictionary<string,string>,	Body: any } export type ChatMessageConfig = {	Text: string,	Color: Color3?,	Font: Enum.Font?,	TextSize: number?, } export type NotificationConfig = {	Title: string,	Text: string,	Icon: string?,	Duration: number?,	Callback: BindableFunction?,	Button1: string?,	Button2: string? } export type AgentParameters = {	AgentRadius: number?,	AgentHeight: number?,	AgentCanJump: boolean?,	WaypointSpacing: number?,	Costs: dictionary<string,number>? } export type BoundActionInfo = {	stackOrder:number,	priorityLevel:number,	createTouchButton:boolean,	inputTypes:{Enum.UserInputType | Enum.KeyCode},	description:string?,	title:string?,	image:string?, } export type ExtraData = {	ChatColor: Color3,	NameColor: string,	Font: Enum.Font,	TextSize: number,	Tags: array<string>, } export type CustomRBXScriptSignal<Arguments> = {	Wait: (CustomRBXScriptSignal<Arguments>) -> (),	Connect: (CustomRBXScriptSignal<Arguments>,(Arguments: any) -> ()) -> (RBXScriptConnection),	ConnectParallel: (CustomRBXScriptSignal<Arguments>,(Arguments: any) -> ()) -> (RBXScriptConnection) } export type ChatService = {	AddChannel: (ChatService: ChatService, channelName: string) -> (ChatChannel),	RemoveChannel: (ChatService: ChatService, channelName: string) -> (),	GetChannel: (ChatService: ChatService, channelName: string) -> (ChatChannel),	AddSpeaker: (ChatService: ChatService, speakerName: string) -> (ChatSpeaker),	RemoveSpeaker: (ChatService: ChatService, speakerName: string) -> (),	GetSpeaker: (ChatService: ChatService, speakerName: string) -> (ChatSpeaker),	GetChannelList: (ChatService: ChatService) -> (array<string>),	GetAutoJoinChannelList: (ChatService: ChatService) -> (array<string>),	RegisterFilterMessageFunction: (ChatService: ChatService, functionId: string, func: (speaker: string, messageObject: ChatMessage, channelName: string) -> ()) -> (),	UnregisterFilterMessageFunction: (ChatService: ChatService, functionId: string) -> (),	RegisterProcessCommandsFunction: (ChatService: ChatService, functionId: string, func: (speakerName: string, message: string, channelName: string) -> ()) -> (),	UnregisterProcessCommandsFunction: (ChatService: ChatService, functionId: string) -> (),	ChannelAdded: RBXScriptSignal,	ChannelRemoved: RBXScriptSignal,	SpeakerAdded: RBXScriptSignal,	SpeakerRemoved: RBXScriptSignal, } export type ChatMessage = {	ID: number,	FromSpeaker: string,	OriginalChannel: string,	IsFiltered: boolean,	MessageLength: number,	Message: string,	MessageType: string,--"Message", "System", "MeCommand", "Welcome", "SetCore", "Whisper	Time: number,	ExtraData: ExtraData } export type ChatChannel = {	Name: string,	WelcomeMessage: string,	Joinable: boolean,	Leavable: boolean,	AutoJoin: boolean,	Private: boolean,	KickSpeaker: (speakerName: string, reason: string?) -> (),	MuteSpeaker: (speakerName: string, reason: string?, duration: number?) -> (),	UnmuteSpeaker: (speakerName: string) -> (),	IsSpeakerMuted: (speakerName: string) -> (boolean),	GetSpeakerList: () -> (array<string>),	SendSystemMessage: (message: string),	RegisterFilterMessageFunction: (functionId: string, func: (speaker: string, messageObject: ChatMessage, channelName: string) -> ()) ->(),	UnregisterProcessCommandsFunction: (functionId: string) -> (),	MessagePosted: RBXScriptSignal,	SpeakerJoined: RBXScriptSignal,	SpeakerLeft: RBXScriptSignal,	SpeakerMuted: RBXScriptSignal,	SpeakerUnmuted: RBXScriptSignal, } export type ChatSpeaker = {	Name: string,	JoinChannel: (ChatSpeaker, channelName: string) -> (),	LeaveChannel: (ChatSpeaker, channelName: string) -> (),	GetChannelList: (ChatSpeaker) -> ({channelName: string}),	IsInChannel: (ChatSpeaker, channelName: string) -> (boolean),	SayMessage: (ChatSpeaker, message: string,channelName: string,extraData: ExtraData?) -> (ChatMessage),	SendMessage: (ChatSpeaker, mesage: string,channelName: string,fromSpeaker: string) -> (),	SendSystemMessage: (ChatSpeaker, messsage: string,channelName: string) -> (),	GetPlayer: (ChatSpeaker) -> (Player?),	SetExtraData: (ChatSpeaker, key: string,data: ExtraData) -> (),	GetExtraData: (ChatSpeaker) -> (),	SetMainChannel: (ChatSpeaker, channelName: string) -> (nil),	SaidMessage: RBXScriptSignal,	ReceivedMessage: RBXScriptSignal,	ReceivedSystemMessage: RBXScriptSignal,	ChannelJoined: RBXScriptSignal,	ChannelLeft: RBXScriptSignal,	Muted: RBXScriptSignal,	Unmuted: RBXScriptSignal,	ExtraDataUpdated: RBXScriptSignal,	MainChannelSet: RBXScriptSignal, } return Types 

Example of use in action

Scripting Templates

This is probably helpful for people who already know what they want to do. I have only created a couple of templates. Most of the template are Classes. I am out of ideas for new templates. If you have any leave your idea down below.

ServerToolScript
--!strict -- Services local Players = game:GetService("Players") -- References local Tool: Tool = script.Parent -- Variable local ACTIVATED_Connection: RBXScriptConnection = nil -- Events Tool.Equipped:Connect(function()-- Connect events after equip	local Character = Tool.Parent	local Player: Player? = Players:GetPlayerFromCharacter(Character)	if Player then	ACTIVATED_Connection = Tool.Activated:Connect(function()	Tool.Deactivated:Wait()	end)	Tool.Unequipped:Wait() -- Disconnect events after unequip	ACTIVATED_Connection:Disconnect()	end end) 
ClientToolScript
--!strict -- Services local UserInputService = game:GetService("UserInputService") local ContextActionService = game:GetService("ContextActionService") local Players = game:GetService("Players") -- References local Tool: Tool = script.Parent local Player = Players.LocalPlayer -- Variable local LMB_Connection: RBXScriptConnection = nil local RMB_Connection: RBXScriptConnection = nil -- Events Tool.Equipped:Connect(function(mouse: Mouse)-- Connect events after equip	LMB_Connection = mouse.Button1Down:Connect(function()	local Origin: CFrame = mouse.Origin	mouse.Button1Up:Wait()	end)	RMB_Connection = mouse.Button2Down:Connect(function()	mouse.Button2Up:Wait()	end)	Tool.Unequipped:Wait() -- Disconnect events after unequip	LMB_Connection:Disconnect()	RMB_Connection:Disconnect() end) 
Object-Oriented-Programming Classes

Here is a list of different OOP methods I found on the devforum whilst making this imply copy and paste the templatess

Class
local Class = {} Class._index = Class function Class.new()	local self = setmetatable(	{	},	Class	)	return self end Class.Do = function(self) end return Class 
ClosureClass
local ClosureClass = {} function ClosureClass.new()	local self = {	}	local function Do()	end	return {	Do = Do	} end return ClosureClass 
ProxyClass
local ClassMain = {} local ClassProxy = {} local ClassMeta = {} local ChangedEvent = Instance.new("BindableEvent") ClassProxy.Changed = ChangedEvent.Event ClassMeta.__index = ClassProxy ClassMeta.__newindex = function(t, i, v)	if t[i] ~= v then	ClassProxy[i] = v	ChangedEvent:Fire(i, v)	end end function ClassMain.new(self)	self = setmetatable(ClassMain,ClassMeta)	return self end return ClassProxy 
NewConstructorClass
local Class = {} Class.__index = Class local function new()	local self = {	}	return setmetatable(self, Class) end setmetatable(Class, {__call = new}) function Class:Do() end return Class 
ChatModule
-- Type needed local Types = require(script.Parent.Types) type ChatService = Types.ChatService local function Run(ChatService: ChatService) end return Run 

Module

Other misc modules. Planning to add a simple data handler module.

MaidModule
local Maid = {} function Maid.new()	local self = {	_tasks = {},	}	setmetatable(self, Maid)	return self end function Maid:__index(key)	return Maid[key] or self._tasks[key] end function Maid:__newindex(key, newTask)	if Maid[key] then	error(string.format("Cannot use %q as a Maid key", tostring(key)))	end	local tasks = self._tasks	local oldTask = tasks[key]	tasks[key] = newTask	if oldTask then	Maid.cleanupTask(oldTask)	end end function Maid:give(task)	local tasks = self._tasks	tasks[#tasks+1] = task end function Maid.cleanupTask(task)	local taskTy = typeof(task)	if taskTy == 'function' then	task()	elseif taskTy == 'RBXScriptConnection' then	task:Disconnect()	elseif taskTy == 'Instance' then	task:Destroy()	elseif task.Destroy then	task:Destroy()	elseif task.destroy then	task:destroy()	elseif task.disconnect then	task:disconnect()	else	error("Unable to cleanup unknown task")	end end function Maid:clean()	local tasks = self._tasks	for key,task in pairs(tasks) do	if typeof(task) == 'RBXScriptConnection' then	tasks[key] = nil	task:Disconnect()	end	end	local index, task = next(tasks)	while task ~= nil do	tasks[index] = nil	Maid.cleanupTask(task)	index, task = next(tasks)	end end Maid.destroy = Maid.clean Maid.Destroy = Maid.clean return Maid 
Stack
Stack = {} Stack.__index = Stack function Stack.new() return setmetatable({}, Stack) end -- put a new object onto a stack function Stack:push(input)	self[#self+1] = input end -- take an object off a stack function Stack:pop()	assert(#self > 0, "Stack underflow")	local output = self[#self]	self[#self] = nil	return output end 
Ragdoll
RagdollModule
local CollectionService = game:GetService("CollectionService") local Players = game:GetService("Players") local RigTypes = require(script.RigTypes) local RAGDOLLED_TAG = "__Ragdoll_Active" local function ragdoll(model, humanoid)	assert(humanoid:IsDescendantOf(model))	if CollectionService:HasTag(model, RAGDOLLED_TAG) then	return	end	CollectionService:AddTag(model, RAGDOLLED_TAG)	-- Turn into loose body:	humanoid:ChangeState(Enum.HumanoidStateType.Physics)	-- Instantiate BallSocketConstraints:	local attachments = RigTypes.getAttachments(model, humanoid.RigType)	for name, objects in pairs(attachments) do	local parent = model:FindFirstChild(name)	if parent then	local constraint = Instance.new("BallSocketConstraint")	constraint.Name = "RagdollBallSocketConstraint"	constraint.Attachment0 = objects.attachment0	constraint.Attachment1 = objects.attachment1	constraint.LimitsEnabled = true	constraint.UpperAngle = objects.limits.UpperAngle	constraint.TwistLimitsEnabled = true	constraint.TwistLowerAngle = objects.limits.TwistLowerAngle	constraint.TwistUpperAngle = objects.limits.TwistUpperAngle	constraint.Parent = parent	end	end	-- Instantiate NoCollisionConstraints:	local parts = RigTypes.getNoCollisions(model, humanoid.RigType)	for _, objects in pairs(parts) do	local constraint = Instance.new("NoCollisionConstraint")	constraint.Name = "RagdollNoCollisionConstraint"	constraint.Part0 = objects[1]	constraint.Part1 = objects[2]	constraint.Parent = objects[1]	end	-- Destroy all regular joints:	for _, motor in pairs(model:GetDescendants()) do	if motor:IsA("Motor6D") then	motor:Destroy()	end	end end return ragdoll 
RigTypes
local RigTypes = {} local HEAD_LIMITS = {	UpperAngle = 60;	TwistLowerAngle = -60;	TwistUpperAngle = 60; } local LOWER_TORSO_LIMITS = {	UpperAngle = 20;	TwistLowerAngle = -30;	TwistUpperAngle = 60; } local HAND_FOOT_LIMITS = {	UpperAngle = 10;	TwistLowerAngle = -10;	TwistUpperAngle = 10; } local ELBOW_LIMITS = {	UpperAngle = 30;	TwistLowerAngle = 0;	TwistUpperAngle = 120; } local KNEE_LIMITS = {	UpperAngle = 30;	TwistLowerAngle = -120;	TwistUpperAngle = 0; } local SHOULDER_LIMITS = {	UpperAngle = 60;	TwistLowerAngle = -60;	TwistUpperAngle = 175; } local HIP_LIMITS = {	UpperAngle = 40;	TwistLowerAngle = -5;	TwistUpperAngle = 150; } local R6_HEAD_LIMITS = {	UpperAngle = 30;	TwistLowerAngle = -60;	TwistUpperAngle = 60; } local R6_SHOULDER_LIMITS = {	UpperAngle = 90;	TwistLowerAngle = -30;	TwistUpperAngle = 175; } local R6_HIP_LIMITS = {	UpperAngle = 60;	TwistLowerAngle = -5;	TwistUpperAngle = 120; } local function createJointData(attach0, attach1, limits)	assert(attach0)	assert(attach1)	assert(limits)	assert(limits.UpperAngle >= 0)	assert(limits.TwistLowerAngle <= limits.TwistUpperAngle)	return {	attachment0 = attach0,	attachment1 = attach1,	limits = limits	} end local function find(model)	return function(first, second, limits)	local part0 = model:FindFirstChild(first[1])	local part1 = model:FindFirstChild(second[1])	if part0 and part1 then	local attach0 = part0:FindFirstChild(first[2])	local attach1 = part1:FindFirstChild(second[2])	if attach0 and attach1 and attach0:IsA("Attachment") and attach1:IsA("Attachment") then	return createJointData(attach0, attach1, limits)	end	end	end end function RigTypes.getNoCollisions(model, rigType)	if rigType == Enum.HumanoidRigType.R6 then	return RigTypes.getR6NoCollisions(model)	elseif rigType == Enum.HumanoidRigType.R15 then	return RigTypes.getR15NoCollisions(model)	else	return {}	end end -- Get list of attachments to make ballsocketconstraints between: function RigTypes.getAttachments(model, rigType)	if rigType == Enum.HumanoidRigType.R6 then	return RigTypes.getR6Attachments(model)	elseif rigType == Enum.HumanoidRigType.R15 then	return RigTypes.getR15Attachments(model)	else	return {}	end end function RigTypes.getR6Attachments(model)	local rightLegAttachment = Instance.new("Attachment")	rightLegAttachment.Name = "RagdollRightLegAttachment"	rightLegAttachment.Position = Vector3.new(0, 1, 0)	rightLegAttachment.Parent = model:FindFirstChild("Right Leg")	local leftLegAttachment = Instance.new("Attachment")	leftLegAttachment.Name = "RagdollLeftLegAttachment"	leftLegAttachment.Position = Vector3.new(0, 1, 0)	leftLegAttachment.Parent = model:FindFirstChild("Left Leg")	local torsoLeftAttachment = Instance.new("Attachment")	torsoLeftAttachment.Name = "RagdollTorsoLeftAttachment"	torsoLeftAttachment.Position = Vector3.new(-0.5, -1, 0)	torsoLeftAttachment.Parent = model:FindFirstChild("Torso")	local torsoRightAttachment = Instance.new("Attachment")	torsoRightAttachment.Name = "RagdollTorsoRightAttachment"	torsoRightAttachment.Position = Vector3.new(0.5, -1, 0)	torsoRightAttachment.Parent = model:FindFirstChild("Torso")	local headAttachment = Instance.new("Attachment")	headAttachment.Name = "RagdollHeadAttachment"	headAttachment.Position = Vector3.new(0, -0.5, 0)	headAttachment.Parent = model:FindFirstChild("Head")	local leftArmAttachment = Instance.new("Attachment")	leftArmAttachment.Name = "RagdollLeftArmAttachment"	leftArmAttachment.Position = Vector3.new(0.5, 1, 0)	leftArmAttachment.Parent = model:FindFirstChild("Left Arm")	local ragdollRightArmAttachment = Instance.new("Attachment")	ragdollRightArmAttachment.Name = "RagdollRightArmAttachment"	ragdollRightArmAttachment.Position = Vector3.new(-0.5, 1, 0)	ragdollRightArmAttachment.Parent = model:FindFirstChild("Right Arm")	local query = find(model)	return {	Head = query(	{"Torso", "NeckAttachment"},	{"Head", "RagdollHeadAttachment"},	R6_HEAD_LIMITS),	["Left Arm"] = query(	{"Torso", "LeftCollarAttachment"},	{"Left Arm", "RagdollLeftArmAttachment"},	R6_SHOULDER_LIMITS),	["Right Arm"] = query(	{"Torso", "RightCollarAttachment"},	{"Right Arm", "RagdollRightArmAttachment"},	R6_SHOULDER_LIMITS),	["Left Leg"] = createJointData(torsoLeftAttachment, leftLegAttachment, R6_HIP_LIMITS),	["Right Leg"] = createJointData(torsoRightAttachment, rightLegAttachment, R6_HIP_LIMITS),	} end function RigTypes.getR15Attachments(model)	local query = find(model)	return {	Head = query(	{"UpperTorso", "NeckRigAttachment"},	{"Head", "NeckRigAttachment"},	HEAD_LIMITS),	LowerTorso = query(	{"UpperTorso", "WaistRigAttachment"},	{"LowerTorso", "WaistRigAttachment"},	LOWER_TORSO_LIMITS),	LeftUpperArm = query(	{"UpperTorso", "LeftShoulderRigAttachment"},	{"LeftUpperArm", "LeftShoulderRigAttachment"},	SHOULDER_LIMITS),	LeftLowerArm = query(	{"LeftUpperArm", "LeftElbowRigAttachment"},	{"LeftLowerArm", "LeftElbowRigAttachment"},	ELBOW_LIMITS),	LeftHand = query(	{"LeftLowerArm", "LeftWristRigAttachment"},	{"LeftHand", "LeftWristRigAttachment"},	HAND_FOOT_LIMITS),	RightUpperArm = query(	{"UpperTorso", "RightShoulderRigAttachment"},	{"RightUpperArm", "RightShoulderRigAttachment"},	SHOULDER_LIMITS),	RightLowerArm = query(	{"RightUpperArm", "RightElbowRigAttachment"},	{"RightLowerArm", "RightElbowRigAttachment"},	ELBOW_LIMITS),	RightHand = query(	{"RightLowerArm", "RightWristRigAttachment"},	{"RightHand", "RightWristRigAttachment"},	HAND_FOOT_LIMITS),	LeftUpperLeg = query(	{"LowerTorso", "LeftHipRigAttachment"},	{"LeftUpperLeg", "LeftHipRigAttachment"},	HIP_LIMITS),	LeftLowerLeg = query(	{"LeftUpperLeg", "LeftKneeRigAttachment"},	{"LeftLowerLeg", "LeftKneeRigAttachment"},	KNEE_LIMITS),	LeftFoot = query(	{"LeftLowerLeg", "LeftAnkleRigAttachment"},	{"LeftFoot", "LeftAnkleRigAttachment"},	HAND_FOOT_LIMITS),	RightUpperLeg = query(	{"LowerTorso", "RightHipRigAttachment"},	{"RightUpperLeg", "RightHipRigAttachment"},	HIP_LIMITS),	RightLowerLeg = query(	{"RightUpperLeg", "RightKneeRigAttachment"},	{"RightLowerLeg", "RightKneeRigAttachment"},	KNEE_LIMITS),	RightFoot = query(	{"RightLowerLeg", "RightAnkleRigAttachment"},	{"RightFoot", "RightAnkleRigAttachment"},	HAND_FOOT_LIMITS),	} end function RigTypes.getR6NoCollisions(model)	local list = {}	local function addPair(pair)	local part0 = model:FindFirstChild(pair[1])	local part1 = model:FindFirstChild(pair[2])	if part0 and part1 then	table.insert(list, {part0, part1})	end	end	addPair({"Head", "Torso"})	addPair({"Left Arm", "Torso"})	addPair({"Right Arm", "Torso"})	addPair({"Left Leg", "Torso"})	addPair({"Right Leg", "Torso"})	addPair({"Left Leg", "Right Leg"})	return list end function RigTypes.getR15NoCollisions(model)	local list = {}	local function addPair(pair)	local part0 = model:FindFirstChild(pair[1])	local part1 = model:FindFirstChild(pair[2])	if part0 and part1 then	table.insert(list, {part0, part1})	end	end	addPair({"Head", "UpperTorso"})	addPair({"UpperTorso", "LowerTorso"})	addPair({"UpperTorso", "LeftUpperArm"})	addPair({"LowerTorso", "LeftUpperArm"})	addPair({"LeftUpperArm", "LeftLowerArm"})	addPair({"LeftLowerArm", "LeftHand"})	addPair({"LeftUpperArm", "LeftHand"})	addPair({"UpperTorso", "RightUpperArm"})	addPair({"LowerTorso", "RightUpperArm"})	addPair({"RightUpperArm", "RightLowerArm"})	addPair({"RightLowerArm", "RightHand"})	addPair({"RightUpperArm", "RightHand"})	addPair({"LeftUpperLeg", "RightUpperLeg"})	addPair({"UpperTorso", "RightUpperLeg"})	addPair({"LowerTorso", "RightUpperLeg"})	addPair({"RightUpperLeg", "RightLowerLeg"})	addPair({"RightLowerLeg", "RightFoot"})	addPair({"RightUpperLeg", "RightFoot"})	addPair({"UpperTorso", "LeftUpperLeg"})	addPair({"LowerTorso", "LeftUpperLeg"})	addPair({"LeftUpperLeg", "LeftLowerLeg"})	addPair({"LeftLowerLeg", "LeftFoot"})	addPair({"LeftUpperLeg", "LeftFoot"})	-- Support weird R15 rigs	addPair({"UpperTorso", "LeftLowerLeg"})	addPair({"UpperTorso", "RightLowerLeg"})	addPair({"LowerTorso", "LeftLowerLeg"})	addPair({"LowerTorso", "RightLowerLeg"})	addPair({"UpperTorso", "LeftLowerArm"})	addPair({"UpperTorso", "RightLowerArm"})	local upperTorso = model:FindFirstChild("UpperTorso")	if upperTorso and upperTorso.Size.x <= 1.5 then	addPair({"Head", "LeftUpperArm"})	addPair({"Head", "RightUpperArm"})	end	return list end return RigTypes 
Dataset
local Set = {} Set.__index = Set -- Function to construct a set from an optional list of items function Set.new(items)	local newSet = {}	for key, value in ipairs(items or {}) do	newSet[value] = true	end	return setmetatable(newSet, Set) end -- Function to add an item to a set function Set:add(item)	self[item] = true end -- Function to remove an item from a set function Set:remove(item)	self[item] = nil end -- Function to check if a set contains an item function Set:contains(item)	return self[item] == true end -- Function to output set as a comma-delimited list for debugging function Set:output()	local elems = {}	for key, value in pairs(self) do	table.insert(elems, tostring(key))	end	print(table.concat(elems, ", ")) end function Set.getIntersection(set1, set2)	local result = Set.new()	for key, value in pairs(set1) do	if set2:contains(key) then	result:add(key)	end	end	return result end function Set:__add(otherSet)	local result = Set.new()	for entry in pairs(self) do	result[entry] = true	end	for entry in pairs(otherSet) do	result[entry] = true	end	return result end function Set:__sub(otherSet)	local result = Set.new()	for entry in pairs(self) do	result[entry] = true	end	for entry in pairs(otherSet) do	result[entry] = nil	end	return result end return Set 
Smooth Camera
-- Services local ContextActionService = game:GetService("ContextActionService") local RunService = game:GetService("RunService") local UserInputService = game:GetService("UserInputService") local GameSettings = UserSettings().GameSettings local RenderSettings = game:GetService("RenderSettings") -- References local Players = game:GetService("Players") local Camera = workspace.CurrentCamera local Player = Players.LocalPlayer -- Objects local SmoothCamera = {	CameraSubject = nil;	CameraRotation = {	X = 0,	Y = 45	};	CameraMaxZoomDistance = 50;	CameraMinZoomDistance = 0.1;	CameraDistance = 25;	LockToCharacter = true,	SmoothSpeed = 3 } -- Constants local sin, cos, rad = math.sin, math.cos, math.rad local clamp, abs = math.clamp, math.abs local Vector2new = Vector2.new local Vector3new = Vector3.new local min, max = math.min, math.max local RandomShake = Random.new() -- Variables local SubjectPosition = Vector3new() local PointerEvent: RBXScriptConnection = nil local CacheCFrame: CFrame = nil local Fullscreen: boolean = GameSettings:InFullScreen() local QualityLevel: number = GameSettings.SavedQualityLevel.Value local CameraRotationMatrix = CFrame.new() local qW = 30 -- Quaternion rotation speed local S = 1 local ShakeType = nil -- Functions local function UpdateCamera(DeltaTime)	local MouseDelta = UserInputService:GetMouseDelta() * -GameSettings.MouseSensitivity	SmoothCamera.CameraRotation.X += MouseDelta.X	SmoothCamera.CameraRotation.Y += MouseDelta.Y	-- UpValue S prevents camera from moving past clamp	--[[if abs(SmoothCamera.CameraRotation.Y) > 1000 then	S = 0	SmoothCamera.CameraRotation.Y = clamp(SmoothCamera.CameraRotation.Y,-1000,1000)	else	S = 1	end]]	-- Rotate Camera on Y Axis	CameraRotationMatrix *= (CFrame.new(0,0,0,rad(MouseDelta.Y * S),0,0,qW))	-- Rotate Camera on Z Axis	--CameraRotationMatrix *= (CFrame.Angles(0,rad(MouseDelta.X / 16),0))	CameraRotationMatrix = (CFrame.Angles(0,rad(MouseDelta.X / 16),0) * CameraRotationMatrix)	if SmoothCamera.LockToCharacter then	CacheCFrame = SmoothCamera.CameraSubject.CFrame:ToWorldSpace(CameraRotationMatrix)	else	CacheCFrame = CameraRotationMatrix + SmoothCamera.CameraSubject.Position	end	-- Lerp Camera Target	if SmoothCamera.CameraSubject.Position == SmoothCamera.CameraSubject.Position then	SubjectPosition = SubjectPosition:Lerp(	SmoothCamera.CameraSubject.Position,	min(0.5,DeltaTime * SmoothCamera.SmoothSpeed*3)--30	)	end	-- Lerp Camera Position and UpVector	Camera.CFrame = CFrame.lookAt(	Camera.CFrame.Position:Lerp(	CacheCFrame.Position+(CacheCFrame.LookVector * -SmoothCamera.CameraDistance),	min(0.5,DeltaTime * SmoothCamera.SmoothSpeed*2)--20	),	SubjectPosition,	Camera.CFrame.UpVector:Lerp(	CacheCFrame.UpVector,	min(0.5,DeltaTime * SmoothCamera.SmoothSpeed)--10	)	) end function ShakeCamera(DeltaTime)	Camera.CFrame += Vector3.new(RandomShake:NextNumber(-2,2),RandomShake:NextNumber(-2,2),0) end function SmoothCamera:Init(LockToCharacter: boolean?)	SmoothCamera.LockToCharacter = LockToCharacter or false	RunService:BindToRenderStep("SmoothCameraUpdate",Enum.RenderPriority.Camera.Value,UpdateCamera)	PointerEvent = UserInputService.PointerAction:Connect(function(Wheel,Pan,Pinch,GameProcessedEvent)	SmoothCamera.CameraDistance = clamp(	SmoothCamera.CameraDistance - (Wheel + Pinch) * GameSettings.MouseSensitivity * 5,	SmoothCamera.CameraMinZoomDistance,	SmoothCamera.CameraMaxZoomDistance	)	end) end function SmoothCamera:Term()	RunService:UnbindFromRenderStep("SmoothCameraUpdate")	PointerEvent:Disconnect() end function SmoothCamera:Shake(Duration: number)--,Type: number?)	--[[ Types: -- Not implemented yet	* UpDown	* LeftRight	* All	* Rotate	* InOut	]]	RunService:BindToRenderStep("Shake",Enum.RenderPriority.Camera.Value + 1,ShakeCamera)	task.delay(Duration,RunService.UnbindFromRenderStep,RunService,"Shake") end -- Events -- Init return SmoothCamera 

Method

This module is a collection of methods and functions that I found useful to me whilst scripting. If you have any thing more to add to this please leve it in the bottom or dm me on discord.

Method
 local newVector3 = Vector3.new local floor = math.floor local Methods = {	import = function(self, Table: {any})	local Environment = getfenv(0)	for Key, Value in ipairs(Table) do	Environment[tostring(Key)] = Value	end	return	end,	roundToNearest = function(self, number: number, roundTo: number)	local x = number + (roundTo / 2)	return x - (number % roundTo)	end,	nestedToFlat3 = function(self, position: Vector3,size: Vector3)	return (position.X) + ((position.Y - 1) * size.X) + ((position.Z - 1) * size.X * size.Y)	end,	flatToNested3 = function(self, i: number, size: Vector3)	return newVector3(	i % size.X,	floor((i / size.X) % size.Y + 1),	floor((i / size.Z + size.Z)/size.Y )	)	end,	lerp = function(self, x: number, y: number, alpha: number)	return alpha * (y - x) + x	end,	factorial = function(self, n)	if n <= 1 then -- base case	return 1	else	return n * self.factorial(n-1) -- recursive step	end	end,	createNestedArray = function(self, dimensions: number, defaultValue: any?,...:number)	local sizes = {...}	local array = {}	if dimensions < 2 then	error("Nested arrays require 2 or more dimensions.")	end	for x = 1, sizes[1] do	if dimensions == 2 then	array[x] = {}	else	table.remove(sizes,1)	array[x] = self.createNestedArray(dimensions - 1,table.unpack(sizes))	end	end	return array	end } return Methods 

server link: TuVzJ9xuzJ

34 Likes

To help, NewConstructorClass code block has a method with PascalCase. That’s inconsistent with the rest of the code blocks.

4 Likes

It should be noted that modifying the Luau function environment table will disable Luau optimizations

5 Likes

What’s that for? The shorthand {string} can be used in place of {[number]:string}.

1 Like

It’s possible he wrote this a while ago back when this wasn’t available, this is slightly recent.

1 Like

I see. Most of the code is actually sourced from random places I found on the developer hub and forum. So, there probably are some inconsistencies.

Also, @Expertcoderz yes. You are right. However I typed the array<> like that because the documentation on the DevHub does. Since I don’t really want to go through the code and change every array to {V}. I could use replace but I guess I am just too lazy

2 Likes

Hey there!
This post seems like it could be useful for a lot of newer developers, but generally it just seems way too inconsistent and complicated. A lot of the people who can fully understand the concepts used in these would already have enough experience to make their own versions of scripts like this. Also, to be quite honest, there really isn’t any functionality for the modules that you have supplied, and the modules that could be useful seem very confusing to customize and modify. It’s quite confusing to look at, even though I’ve personally used OOP on ROBLOX for most of my projects.

Modules/Utilities
For example, the “ChatModule” doesn’t have any functionality to it, so it really doesn’t have a usage. It’s not really clear what it would be used for either since its not documented.

There are, for sure, some very useful modules and scripts provided here, but they don’t really have any documentation or explanation. This is prominent in the utility modules provided at the end of the post, such as “Maid” and “Ragdoll”. There is no explanation for the two modules/scripts, so only the people who have actually used them before will be able to understand what they are used for. Also, if I’m not wrong, haven’t those modules been posted already? I remember seeing a Maid module somewhere, and if so, you should really credit the original creator because it seems as though you are taking credit for this clearly incredibly useful module.

Server-Sided Tool Script
The server-sided tool management script just seems overly complicated. The main script connection for the tool being equipped doesn’t need to yield for the unequipped event to be called, as you can simply connect another callback function to the “Tool.Unequipped” event to do the exact same logic.

Class Module
Overall, a lot of the examples just seem to be incredibly complicated for seemingly no reason, and aren’t easily usable and understandable by newer developers. An example of this can be seen in the “Class” module, which uses two different formats to define functions for really no reason.

-- Format #1 function Class.new()	local self = setmetatable(	{	},	Class	)	return self end -- Format #2 Class.Do = function(self) end 

It makes it very confusing to read, and it doesn’t provide the most efficient and understandable example for newer developers who don’t necessarily fully understand OOP. The function has an argument named “self” (which people who have used OOP with lua before can immediately recognize), but it doesn’t provide an explanation for what this argument is and what it can be used for. Also, the “setmetatable” function being called is using multiple lines for really no reason, and can be easily simplified to the following:

local self = setmetatable({}, Class) return self 

Conclusion
Overall, this post does seem helpful in many ways, but I just personally feel as though the inconsistencies and lack of explanations could do more harm than good for newer developers. Please do correct me if you see any issues in my post, as I may not have completely understood the topic or parts of it. :sweat_smile:

Regardless, have a wonderful day! :wave:

3 Likes

the module is so over-complicated that it’s not even worth crediting. Maid (non-roblox-developer) edition:

local function CleanupTrash(Trash) local Type = typeof(Trash) if Type == "RBXScriptConnection" do task:Disconnect() elseif Type == "Instance" do task:Destroy() end end return function(Table) for _, Trash in pairs(Table) do Cleanup(Trash) end end 

The whole idea of making it a class seems pointless.

Actually the maid module is really useful in terms of object-oriented programming. When you create a new class, you can define a maid in the “constructor” (definition) of your class and then use that to store all of the RBXScriptConnections. Also, it makes it very intuitive to use and you wouldn’t have to keep retyping the same code over and over every time you have to use it, since maids are primarily used to prevent memory leaks. Overall, it has a very commonly applicable concept and creating it as a class just makes it easier to understand and actually put into usage.

Also, as a side-note, even if it’s over-complicated, it doesn’t mean that it doesn’t deserve to be credited. That seems to be created by someone else, who does not get any form of credit for a module that really is useful and handles a common issue quite efficiently.

Have a great day! :wave:

2 Likes

Maid and Ragdoll modules are both undocumented so I will put some documentation.

They are also both not possible to credit as the author is not known.

The scripts I put here are simply if you don’t want to write it out yourself and/or if you can’t actually script the things. It’s really just a large collection of scripts that do random but common tasks.

The template scripts are not supposed to have functionality. The idea is that you don’t have to remember the exact for making a Chatmodule or a Class. You can simply copy and paste these scripts.

Yielding the thread allows us to think procedurally which is a lot easier to program in contrast to event based. You are welcome to add those events but it’s much better, easier and readable imo to have the thread yielded.

For example
Object.Start:Connect(function(arg) -- Code to do something Object.End:Wait() -- Code to stop doing something end) 

I prefer instead of:

local Arguments = {} Object.Start:Connect(function(...) -- Code to do something Arguments = {...} end) Object.End:Connect(function() -- Sometimes arguments are not always passed in the "secondary" event and as such, needs to be stored in a higher scope -- Code to stop doing something Arguments = {} end) 

Okay then. I’ll see to it that everything here gets documented in the coming days so that people understand what these scripts are for and how they can be used.

Thanks for your feedback :slightly_smiling_face:

I like the ragdoll script as it is hard to find some working right now.
Thanks for this.

Update ! :nerd_face:

Added SmoothCamera Module as requested by @IamACoolGuy_1230

If you need full rotation simply comment the lines 57 to 63

I have also added a line 67 and 68 the option of switching between local rotation and global rotation in the horizontal axis. Note that global rotation is opposite when you are upside down. Also note that roblox’s default walk does not work as intended when camera is upside down. However it guaruantees that the camera will not end up tilted when set level with character.

I bumped this topic because I don’t think this smooth camera module is worth a topic.