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