Hello, I am using oop to create swords for my game, but whenever I set one of the SwordUtils.new({}) constructors/things to variables, then it always returns nil.
Here is an example of what I mean:
I don’t know what I am doing wrong, I tried making the Commander variable/thing to be local, I tried removing it and adding :SpawnUnits directly onto the end of SwordUtils.new({}) but nothing works, maybe this is a game engine bug? If you need my full module script, then I will gladly give it.
function SwordUtils:SpawnUnits(UnitsWithChance)
self.LaunchesEnemies = true
self.LaunchEnemyInfo = UnitsWithChance
local Percentages = {}
local Units = {}
local cmdPosition = -1
for count=1,#UnitsWithChance/2 do
cmdPosition += 2
table.insert(Percentages,100/UnitsWithChance[cmdPosition + 1])
table.insert(Units,UnitsWithChance[cmdPosition])
end
local clones = 0
if spawnCheck then
while clones < 2 do task.wait()
for i,unit : BodyForce in pairs(Units) do
local random = math.random(1,Percentages[i])
if random == Percentages[i] then
clones += 1
local clone = unit:Clone()
local bodyVelocity = Instance.new('BodyVelocity')
clone.Parent = workspace.Allies
clone:SetPrimaryPartCFrame(self.Sword.Handle.CFrame)
bodyVelocity.Parent = clone.HumanoidRootPart
bodyVelocity.MaxForce = Vector3.new(10000,10000,10000)
bodyVelocity.Velocity = Vector3.new(25,15,25) * self.Sword.Parent.HumanoidRootPart.CFrame.LookVector
Debris:AddItem(bodyVelocity,0.5)
end
end
end
else
spawnCheck = true
end
end
This is how a typical OOP module script is set up. You will need to set the index of the module to itself, then create a new table with the metatable as the module INSIDE a .new() function, and finally return that new table that you created:
local SwordUtils= {}
SwordUtils.__index = SwordUtils
function SwordUtils.new(UnitsWithChance)
local self = setmetatable({}, SwordUtils)
self.Sword = UnitsWithChance.Sword --or use wahtever other way to add data to the self table you like
return self
end
return Booth
You will still need to return something back after calling the .new(SwordInfo) constructor, otherwise when you run SwordUtils.new(SwordInfo) it just runs as a normal function and returns nothing. If you want it to return an object, you must create a table in it and return the table at the end by using the example I gave above. Those items are the BARE MINIMUM for using OOP in roblox
I can’t remove SetPoints though, it is an essential part of my script which sets RaycastHitbox points on the sword, and I don’t know why I would want to error something that already errors.
ah I think I figured out the issue, end line return newSword is inside of CollectionService:GetInstanceAddedSignal and it might be returning the metatable for collectionservice instead of returning it in the actual function, how would I solve this? add a double return? I’m not really sure, i’ve tried making a variable called returnMetatable and setting it to the metatable I have and then returning that variable, but no matter what I do, it returns nil, is there any way to return a value from one function to the previous?
function SwordUtils.new(SwordInfo)
CollectionService:AddTag(SwordInfo.Sword,SwordInfo.Sword.Name)
SwordInfo.Sword:Clone().Parent = game:GetService('StarterPack')
CollectionService:GetInstanceAddedSignal(SwordInfo.Sword.Name):Connect(function(Sword)
local animChildren = Sword.Animations:GetChildren()
local toolActivated = false
local newHitbox
local newSword = setmetatable({},SwordUtils)
local projectileInfo = SwordInfo.ProjectileInfo or {} --if first value is nil instead use an empty table so it doesn't error
local debuffInfo = SwordInfo.DebuffInfo or {}
newSword.Sword = Sword
newSword.Damage = SwordInfo.Damage
newSword.ProjectileType = projectileInfo.projectileType
newSword.ProjectileVelocity = projectileInfo.projectileVelocity
newSword.LaunchEnemyInfo = nil
newSword.LaunchesEnemies = false
if not game:GetService('StarterGui').ShopUi.Main.Swords:FindFirstChild(Sword.Name) then
local GuiSwordTemplate = ReplicatedStorage.UI.SwordTemplate:Clone()
GuiSwordTemplate.Name = Sword.Name
GuiSwordTemplate.Parent = game:GetService('StarterGui').ShopUi.Main.Swords
GuiSwordTemplate.SwordViewport.Stats.Text = 'Damage: '..tostring(SwordInfo.Damage)
..' Cooldown: '..tostring(SwordInfo.cooldown)
..' Debuff: '..tostring(debuffInfo.Debuff)
..' Projectile: '..tostring(projectileInfo.projectileType)
..' Projectile Cooldown: '..tostring(projectileInfo.projectileCooldown)
..' Projectile Velocity: '..tostring(projectileInfo.projectileVelocity)
GuiSwordTemplate.SwordViewport.Price.Text = SwordInfo.price
end
local ActiveAbility
if SwordInfo.Ability then
Sword.Equipped:Connect(function()
ActiveAbility = Sword.Parent[SwordInfo.Ability]
ActiveAbility.Sound.SoundId = Sword.Blade[SwordInfo.Ability].SoundId
ActiveAbility.Enabled = true
end)
Sword.Unequipped:Connect(function()
ActiveAbility.Enabled = false
end)
end
if projectileInfo and projectileInfo ~= {} then
local Attacking = false
local swordEquipped = false
local findAmbient = Sword:FindFirstChild('Ambient',true)
Sword.Equipped:Connect(function()
swordEquipped = true
if findAmbient then
findAmbient:Resume()
end
end)
Sword.Unequipped:Connect(function()
swordEquipped = false
if findAmbient then
findAmbient:Pause()
end
end)
ReplicatedStorage.Events.PressedE.OnServerEvent:Connect(function(plr)
if Attacking == false and swordEquipped == true and projectileInfo ~= {} then
local Anim = plr.Character.Humanoid:LoadAnimation(Sword.ProjectileAnim)
Attacking = true
Anim:Play()
Anim:AdjustSpeed(projectileInfo.AnimSpeed)
SwordUtils.FireProjectile(newSword)
delay(1,function()
Anim:Stop()
end)
delay(projectileInfo.projectileCooldown,function()
Attacking = false
end)
end
end)
end
Sword.Activated:Connect(function()
if toolActivated == false then
local randomAnim = animChildren[math.random(1,#animChildren)]
local char = Sword.Parent
local anim = char.Humanoid:LoadAnimation(randomAnim)
toolActivated = true
if newSword.LaunchesEnemies == true then
SwordUtils.SpawnUnits(newSword,newSword.LaunchEnemyInfo)
end
if Sword.Handle:FindFirstChildWhichIsA('Sound') then
Sword.Handle.Attack:Play()
if newHitbox == nil then
newHitbox = RaycastHitbox.new(Sword.Handle)
newHitbox:SetPoints(Sword.Handle,SwordInfo.SetPoints)
end
else
if Sword.Blade:FindFirstChild('Attack1') then
local attackSounds = {}
for number,sound in pairs(Sword.Blade:GetChildren()) do
if string.match(sound.Name,'Attack') then
table.insert(attackSounds,sound)
end
end
attackSounds[math.random(1,#attackSounds)]:Play()
else
Sword.Blade.Attack:Play()
end
if newHitbox == nil then
newHitbox = RaycastHitbox.new(Sword.Blade)
newHitbox:SetPoints(Sword.Blade,SwordInfo.SetPoints)
end
end
local Params = RaycastParams.new()
local playerTable = {char}
for _,player in pairs(Players:GetPlayers()) do
table.insert(playerTable,player.Character)
end
Params.FilterDescendantsInstances = playerTable
Params.FilterType = Enum.RaycastFilterType.Blacklist
newHitbox.RaycastParams = Params
newHitbox:HitStart()
newHitbox.OnHit:Connect(function(hit,humanoid)
if humanoid.Parent.Parent.Name == 'Enemies' then
if humanoid:FindFirstChild('damageMultiplier') then
humanoid:TakeDamage(SwordInfo.Damage * humanoid.damageMultiplier.Value)
else
humanoid:TakeDamage(SwordInfo.Damage)
end
if debuffInfo.Debuff ~= nil then
Debuffs[debuffInfo.Debuff](humanoid,debuffInfo.duration)
end
end
end)
anim:Play()
anim:AdjustSpeed(SwordInfo.AnimSpeed)
anim.Stopped:Wait()
task.wait(0.1)
newHitbox:HitStop()
task.wait(SwordInfo.cooldown)
toolActivated = false
end
end)
return newSword -- here is the problem I think, its probably returning newSword on the CollectionService function instead of SwordUtils.new()
end)
end