Feedback on Interaction System Code

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!

1 Like

You should take the distance from the extremities of the hitbox instead of simply the center, would be more reliable especially on the smiley.

1 Like

oh yeah that would be a great idea, thank you!

1 Like