How should I structure my soccer ball system so that I can keep it clean (not a lot of lines, variables, etc) How can I make it modular, etc? My current system is pretty messy and has a lot of lines so I want to know how I can make it modular and how to structure my code/system.
My current system works this way: There are several tools: Pass tool, shoot tool, dribble tool, Goalkeeper tool, etc. Inside each of these tools is different scripts for different “methods” like for pass tool you can do a high pass or a low pass etc.
Hm well you could structure it around the player (what I’d personally do) or you could structure it around the ball.
I dunno I’ll just give ya an example
local Ball = {}
Ball.__index = Ball
function Ball.new(model)
local self = setmetatable({}, Ball)
self.__index = Ball
self.Model = model
return self
end
function Ball:Clone()
if not self.Model then return end
local Clone = setmetatable(getmetatable(self), Ball)
Clone.__index = Ball
Clone.Model = self.Model:Clone()
Clone.Model.Parent = workspace
return Clone
end
function Ball:Destroy()
if not self.Model then return end
self.Model:Destroy()
self.__index:Destroy()
self = nil
end
function Ball:KickTo(cframe: CFrame)
if not self.Model then return end
self.Model.CFrame = cframe
end
return Ball
I don’t have much context so uh yeah. Mb if this isn’t actually any help.
I just created a basic OOP module really. feel free to add some extensions
I can give you a little more context, So lets say I have a pass tool for the ball. Inside the pass tool there is multiple scripts like “high pass” “low pass” “bouncy pass” etc. so basically there is tools with different methods inside them
I guess you could pass a player object and check what tool is equipped. With that you could add some code to y’know do with that info
And by the way I’m writing this on mobile (no it’s not ChatGPT lol I just type fast) so I’m not gonna write a whole bunch; just a outline
local PassMethods = {
["Bouncy"] = true,
["Normal"] = true
}
function Ball:Pass(passMethod: string)
if not PassMethods[passMethod] then return end
-- rest of ur code
end
I will actually provide the other method I was mentioning in my first reply, the player method. Also that’s how I’d do this:
local PlayerMod = {}
PlayerMod.__index = PlayerMod
function PlayerMod.new(Player: Player)
local self = setmetatable({}, PlayerMod)
self.__index = PlayerMod
self.Player = Player
self.Position = "Viewer"
return self
end
function PlayerMod:Kick(kickType: string)
if not self.Player then return end
--[[ do stuff depending
on the context
like player look angle, etc
maybe do some raycasting
]]
end
function PlayerMod:AssignPosition(position: string)
if not self.Player then return end
self.Position = position
end
return PlayerMod
What I meant was like adding methods in the player instead of inside the ball. So you would call a :Kick method with access to the player instead of using a :Kick method without access to the player if that makes sense
There probably is. Maybe the methods you’re using right now. Although I’d suggest trying to use this approach if you can comprehend it.
You could go all functional maybe like this:
local function Kick()
-- code
end
local function Pass()
-- code
end
etc, etc
Now you could add a script in each ball, or you could tag them all and apply this to all of the, with CollectionService
local CollectionService = game:GetService("CollectionService")
local Tagged = CollectionService:GetTagged("SoccerBalls")
for i, v in Tagged do
-- add code
end
local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local Debris = game:GetService("Debris")
local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart")
local RightArm = Character:WaitForChild("Right Arm")
local LeftArm = Character:WaitForChild("Left Arm")
local RightLeg = Character:WaitForChild("Right Leg")
local LeftLeg = Character:WaitForChild("Left Leg")
local Client = ReplicatedStorage:WaitForChild("Client")
local Modules = Client:WaitForChild("Modules")
local ToolController = require(Modules:WaitForChild("ToolController"))
local Tool = script.Parent
local ExpectingInput = false
local IsMouseDown = false
local IsKicking = false
local Power = 0
local Angle = 0
local Max_Power = 100
local Max_Angle = 10
function ResetPower()
Power = 0
Angle = 0
ToolController:UpdatePowerBar(Power, Max_Power)
end
Tool.Equipped:Connect(function()
ExpectingInput = true
ToolController:UpdateTitle("Pass")
ToolController:UpdateGui(true)
end)
Tool.Unequipped:Connect(function()
ExpectingInput = false
ResetPower()
ToolController:UpdateGui(false)
end)
UserInputService.InputBegan:Connect(function(input, gameProcessedEvent)
if gameProcessedEvent or not ExpectingInput then
return
end
if input.UserInputType == Enum.UserInputType.MouseButton1 then
IsMouseDown = true
ResetPower()
while IsMouseDown and ExpectingInput do
Power = math.min(Power + (Max_Power * .005), Max_Power)
Angle = math.min(Angle + (Max_Angle * .005), Max_Angle)
ToolController:UpdatePowerBar(Power, Max_Power)
task.wait()
end
end
end)
UserInputService.InputEnded:Connect(function(input, gameProcessedEvent)
if gameProcessedEvent or not ExpectingInput then
return
end
if input.UserInputType == Enum.UserInputType.MouseButton1 then
IsMouseDown = false
IsKicking = true
task.wait(.6)
IsKicking = false
ResetPower()
end
end)
local function OnTouched(hit: BasePart)
if hit.Name ~= "Ball" or not IsKicking then
return
end
ToolController:UpdateNetworkOwner(hit)
local KickVelocity = Instance.new("BodyVelocity", hit)
KickVelocity.MaxForce = Vector3.new(math.huge, 50, math.huge)
KickVelocity.Velocity = HumanoidRootPart.CFrame.LookVector * Power + HumanoidRootPart.CFrame.UpVector
Debris:AddItem(KickVelocity, .25)
local BallPosition = hit.Position + Vector3.new(0, HumanoidRootPart.Position.Y - hit.Position.Y, 0)
local DotProduct = HumanoidRootPart.CFrame.LookVector:Dot(BallPosition - HumanoidRootPart.Position)
local TorsoOrientation = math.acos(DotProduct / (BallPosition - HumanoidRootPart.Position).Magnitude)
local LeftArmDistance = (hit.Position - LeftArm.Position).Magnitude
local RightArmDistance = (hit.Position - RightArm.Position).Magnitude
if TorsoOrientation > math.rad(18) and LeftArmDistance < RightArmDistance then
local CurveForce = Instance.new("BodyForce", hit)
CurveForce.Force = HumanoidRootPart.CFrame.RightVector * -200
local CurveAngularVelocity = Instance.new("BodyAngularVelocity", hit)
CurveAngularVelocity.MaxTorque = Vector3.new(0, 1000, 0)
CurveAngularVelocity.AngularVelocity = Vector3.new(0, 1000, 0)
Debris:AddItem(CurveForce, Power/Max_Power)
Debris:AddItem(CurveAngularVelocity, .1)
elseif TorsoOrientation > math.rad(30) and RightArmDistance < LeftArmDistance then
local CurveForce = Instance.new("BodyForce", hit)
CurveForce.Force = HumanoidRootPart.CFrame.RightVector * 200
local CurveAngularVelocity = Instance.new("BodyAngularVelocity", hit)
CurveAngularVelocity.MaxTorque = Vector3.new(0, 1000, 0)
CurveAngularVelocity.AngularVelocity = Vector3.new(0, -1000, 0)
Debris:AddItem(CurveForce, Power/Max_Power)
Debris:AddItem(CurveAngularVelocity, .1)
end
IsKicking = false
ResetPower()
end
RightLeg.Touched:Connect(OnTouched)
heres how I’m doing it right now. Its just a script inside a tool that handles all the physics and stuff but I definitely know theres a way to atleast clean it up