Hello, I made this interaction system a while back and wanted to revamp it but I am not sure what I can improve on or if there is something I should do differently. If you spot out something I should fix let me know!
Video Example:
https://gyazo.com/1db85a730dd9f64dc4ef791d3d8fe5cb
Client script:
local InteractionModule = require(game.ReplicatedStorage.Interaction).new()
local Mouse = InteractionModule.Player:GetMouse()
InteractionModule.RS.RenderStepped:Connect(function()
InteractionModule:Update()
end)
local function InteractObj(AcN, Input)
if Input == Enum.UserInputState.Begin then
InteractionModule:Interact()
end
end
InteractionModule.CAS:BindAction(InteractionModule.Action, InteractObj, true, Enum.KeyCode[InteractionModule.Keybind])
local function UIInteractObj()
InteractionModule:Interact()
end
InteractionModule.InteractUI.InteractButton.MouseButton1Down:Connect(UIInteractObj)
ClientSided Interaction Module:
local TW = game:GetService("TweenService")
local Interaction = {}
Interaction.__index = Interaction
function Interaction.new()
return setmetatable({
InteractRe = game:GetService("ReplicatedStorage").Remotes.Interact;
Player = game:GetService("Players").LocalPlayer;
InteractUI = game:GetService("Players").LocalPlayer.PlayerGui.InteractGUI;
CAS = game:GetService("ContextActionService");
CS = game:GetService("CollectionService");
RS = game:GetService("RunService");
Camera = workspace.CurrentCamera;
Keybind = "E";
lt = tick();
MinDist = math.min(7);
Action = "Interact";
Object = nil;
Cooldown = os.clock();
CanInteract = true;
Interacting = false;
Interacted = false;
Interactions = {};
}, Interaction)
end
function Interaction:UpdateUI()
self.InteractUI.InteractButton.Text = self.Keybind
self.InteractUI.InteractLabel.Text = self.Object.Parent.Name
local Pos, IsScreen = self.Camera:WorldToScreenPoint(self.Object.Position)
if not IsScreen then
self.InteractUI.InteractButton.Visible = false
return
end
local Letters = string.gsub(self.Object.Parent.Name, "[^%a]+", "")
local FrameTween = TW:Create(self.InteractUI.InteractButton,TweenInfo.new(0.1),{Position = UDim2.new(0,Pos.X,0,Pos.Y)})
local LabelAnimTween = TW:Create(self.InteractUI.InteractLabel,TweenInfo.new(.15),{Position = UDim2.new(0,Pos.X + #Letters*12 ,0,Pos.Y)})
local LabelColorPress = TW:Create(self.InteractUI.InteractLabel,TweenInfo.new(.15),{TextColor3 = Color3.fromRGB(255, 247, 0)})
local LabelColorNorm = TW:Create(self.InteractUI.InteractLabel,TweenInfo.new(.1),{TextColor3 = Color3.fromRGB(255, 255, 255)})
FrameTween:Play()
if self.CanInteract then
LabelAnimTween:Play()
self.InteractUI.InteractButton.Visible = true
LabelAnimTween.Completed:Wait()
self.InteractUI.InteractLabel.Visible = true
end
end
function Interaction:Clean()-- clean up fucntions and connections and UI
self.InteractUI.InteractButton.Visible = false
self.InteractUI.InteractLabel.Visible = false
self.InteractUI.InteractLabel.Text = ""
self.InteractUI.InteractButton.Text = ""
end
function Interaction:Interact()-- when the object is found then you can interact
local LabelColorPress = TW:Create(self.InteractUI.InteractLabel,TweenInfo.new(.3),{TextColor3 = Color3.fromRGB(72, 72, 72)})
local LabelColorNorm = TW:Create(self.InteractUI.InteractLabel,TweenInfo.new(.2),{TextColor3 = Color3.fromRGB(255, 255, 255)})
if self.Object ~= nil and self.CanInteract and (os.clock() - self.Cooldown >= 1) then
self.Interacted = true
self.Cooldown = os.clock()
self.InteractRe:FireServer(self.Object.Parent.Name, self.Object, self.Action)
elseif (os.clock() - self.Cooldown < 1) then
self.Interacted = false
end
-- check if object can have extra interact options
-- check if object can have extra interact options
-- check if object can have extra interact options
if (self.Interacted) then
LabelColorPress:Play()
LabelColorPress.Completed:Wait()
LabelColorNorm:Play()
end
end
function Interaction:Add(Object)
if (not table.find(self.Interactions, Object)) then
table.insert(self.Interactions, Object)
end
end
function Interaction:Remove(Index)
if self.Interactions[Index] then
for i, Items in next, self.Interactions do
Items = nil
table.remove(self.Interactions, Index)
end
end
end
function Interaction:Check()
for i, InteractionObjs in next, self.CS:GetTagged("Interactables") do
local Character = self.Player.Character or self.Player.CharcterAdded:Wait()
local HRP = Character:WaitForChild("HumanoidRootPart")
local X, Y, Z, RX, RY, RZ = InteractionObjs.Position.X, InteractionObjs.Position.Y, InteractionObjs.Position.Z, HRP.Position.X, HRP.Position.Y, HRP.Position.Z
local Distance = ((X - RX) ^ 2 + (Y - RY) ^ 2 + (Z - RZ) ^ 2)^.5
--print(Distance)
if Distance >= self.MinDist then
self:Remove(i)
self.CanInteract = false
elseif Distance < self.MinDist then
self:Add(InteractionObjs)
self.CanInteract = true
--updateUI
return
end
end
end
function Interaction:Update() -- Update distance, make sure to disconnect few times
if (tick() - self.lt) >= .5 then
self.lt = tick()
self:Check()
end
for _, Interactions in next, self.Interactions do
if (self.Player:DistanceFromCharacter(Interactions.Position) <= self.MinDist) then
self.Object = Interactions
self:UpdateUI()
--updateUI
return
end
end
self.CanInteract = false
self:Clean()
self.Object = nil
end
return Interaction
Server:
local InteractRe = game:GetService("ReplicatedStorage").Remotes.Interact
local InteractModule = require(game.ServerStorage.Classes.InteractClass)
local function Interact(Player, ObjectName, Object, Action)
if ObjectName == Object.Parent.Name and Action == "Interact" then
local Obj = ObjectName
local name = string.sub(Obj, 1, 10)
InteractModule[name](Obj, Object)
end
end
InteractRe.OnServerEvent:Connect(Interact)
Server Interaction Module:
local InteractClass = {}
function InteractClass.Banana(Obj, Object)
print(Obj)
end
function InteractClass.Apple(Obj, Object)
print(Obj)
end
function InteractClass.Yes(Obj, Object)
print(Obj)
local Explos = Instance.new("Explosion")
Explos.Position = Object.Position
Explos.BlastRadius = 4000033333333333333333333333333333333333333333
Explos.BlastPressure = 3333333
Explos.Parent = Object
end
function InteractClass.Door(Obj, Object)
local Explos = Instance.new("Explosion")
Explos.Position = Object.Position
Explos.Parent = Object
local Open = CFrame.Angles(0,math.rad(90),0)
local Closed = CFrame.Angles(0,math.rad(-90),0)
if Object.Parent.OpenClose.Value == "Closed" then
local Opened = Object.Parent.Hinge.CFrame:ToWorldSpace(CFrame.Angles(0, - math.rad(120), 0))
Object.Parent:SetPrimaryPartCFrame(Opened * CFrame.Angles(0,40,0))
end
end
return InteractClass
Let me know if there are things I can improve on or fix up, thank you!