-
What do you want to achieve?
Ok, this is going to seem odd but I am having 2 issues and I would prefer to combine the issues into one post rather than make 2 separate posts. Anyways, I am at the point where I am coding the main game of my tower defense game, and I have 2 inconveniences popping up in my game. The first one, the most annoying, comes with my movement of enemies through the map. Another issue has to do with my placement system. -
What is the issue?
So, issue #1:
How my game works is I have all the waves inside a module script for spawning. I will call the waves from a main server script then I execute code in the same module script to spawn enemies on the map and move them from the start of the map to the end. I made a demo a bit ago to test some things and the code for enemy movement I made worked without issue, so I thought it would be better to reuse it as there was no issue with it. However, the issue I am experiencing can be seen in the following video:
In the video I highlight the only turn causing an issue. Zombies will only spasm and delete like the first, or jitter like the other zombies on only the second turn. I have no idea why this occurs as in the demo I made which uses the exact same code doesn’t have any issue at all. Here is the code:
function wave(enemyType, enemyCount)
for i = 1, enemyCount do
spawn(function()
local enemyClone = enemyType:Clone()
enemyClone.HumanoidRootPart.Position = spawnPoint.Position
enemyClone.Parent = map.Enemies
enemyClone.Name = enemyType.Name
local walkAnim = enemyClone.Humanoid:FindFirstChild("Walk")
local walkAnimation = enemyClone.Humanoid:LoadAnimation(walkAnim)
walkAnimation:Play()
table.insert(enemiesModule.enemies, enemyClone.Name)
enemiesRemaining.Value = enemiesRemaining.Value + 1
for i = 1, (#trackParts:GetChildren() - 1), 1 do
local trackPart = trackParts[i]
local previousValue = i - 1
local previousTrackPart = trackParts:FindFirstChild(previousValue)
enemyClone:SetPrimaryPartCFrame(previousTrackPart.CFrame)
local endPoint = {
Position = trackPart.CFrame.Position
}
local tweenInfo = TweenInfo.new(((trackParts:FindFirstChild(i).Position - trackParts:FindFirstChild(previousValue).Position).Magnitude) / (1 + enemyClone.Humanoid.WalkSpeed), Enum.EasingStyle.Linear, Enum.EasingDirection.Out)
local tween = tweenService:Create(enemyClone.PrimaryPart, tweenInfo, endPoint)
tween:Play()
tween.Completed:Wait(0.1)
end
local damage = enemyClone:FindFirstChild("Configuration"):WaitForChild("Damage")
if healthValue.Value > 0 then
healthValue.Value = healthValue.Value - damage.Value
elseif healthValue.Value < 0 then
healthValue.Value = 0
end
local animation = enemyClone.Humanoid.DeathAnim
local anim = enemyClone.Humanoid:LoadAnimation(animation)
anim:Play()
anim.Stopped:Wait(0.1)
enemyClone.Humanoid:TakeDamage(100)
wait(0.25)
enemyClone:Destroy()
end)
wait(0.5)
end
end
Then there is issue #2:
This lies with my placement system. I have another video which displays my issue here:
In the video the first tower is placed fine then the second tower is placed way high up off the ground. Now while I did intersect the towers in placement which can cause the issue, the issue has also happened sometimes when placing a tower regularly.
Here is the whole placement system, as I am unsure what part is causing the issues:
--Client
local replicatedStorage = game:GetService("ReplicatedStorage")
local placeEvents = replicatedStorage.Events.PlaceEvents:FindFirstChild("Place")
local equippedTowers = script.Parent.EquippedTowers
local towerFolder = workspace:FindFirstChild("Map").Towers
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local posX = nil
local posY = nil
local posZ = nil
local debounce = false
local placing = false
local gridSize = 0.25
local confirmed = false
local function snap()
posX = math.floor(mouse.Hit.X / gridSize + 0.5) * gridSize
posY = mouse.Hit.Y
posZ = math.floor(mouse.Hit.Z / gridSize + 0.5) * gridSize
end
local function movement(tower)
mouse.TargetFilter = tower
snap()
if tower ~= nil then
tower:SetPrimaryPartCFrame(CFrame.new(posX, posY, posZ))
end
local target = mouse.Target
local config = tower.Configuration
local base = tower.Tower.Base
local hitBox = tower.Tower.HitBox
local min = base.Position - (base.Size / 2)
local max = base.Position + (base.Size / 2)
local hitBoxMin = hitBox.Position - (hitBox.Size / 2)
local hitBoxMax = hitBox.Position + (hitBox.Size / 2)
local towerRegion = Region3.new(min, max)
local hitBoxRegion = Region3.new(hitBoxMin, hitBoxMax)
local intersectingParts = workspace:FindPartsInRegion3(towerRegion, base) --Get any intersecting parts with the tower base
local touchingHitBox = workspace:FindPartsInRegion3(hitBoxRegion, hitBox) --Get any intersecting parts with the tower hitbox
local intersectingHitBox = nil
for _, hitboxPart in pairs(touchingHitBox) do --Loop through the list intersecting the hit box to check for another tower hitbox (Basically if there is an intersecting hitbox you are placing inside another tower)
if hitboxPart.Name == "HitBox" then
intersectingHitBox = hitboxPart
end
end
local touchingPath = nil
for _, pathPart in pairs(intersectingParts) do --Loop through the list intersecting the tower base to check for paths that way you can't place the tower on the path
if pathPart.Name == "Path" then
touchingPath = pathPart
end
end
if placing == true and target.Name == config.Placement.Value and intersectingHitBox ~= "HitBox" and touchingPath ~= "Path" and player.GamePlayData.TowersPlaced.Value < player.GamePlayData.MaxTowers.Value and player.GamePlayData.Cash.Value >= config.PlaceCost.Value and player.PlayerData.Wood.Value >= config.CraftingCost.Wood.Value and player.PlayerData.Stone.Value >= config.CraftingCost.Stone.Value and player.PlayerData.Metal.Value >= config.CraftingCost.Metal.Value and player.PlayerData.Crystal.Value >= config.CraftingCost.Crystal.Value and player.PlayerData.Plasma.Value >= config.CraftingCost.Plasma.Value then
confirmed = true
tower.Tower.HitBox.Color = Color3.fromRGB(85, 255, 127)
else
confirmed = false
tower.Tower.HitBox.Color = Color3.fromRGB(255, 0, 0)
end
end
local function startPlacement(tower)
for i, placedTower in pairs(towerFolder:GetChildren()) do
placedTower.Tower.HitBox.Transparency = 0
placedTower.Tower.HitBox.BrickColor = BrickColor.new("Medium stone grey")
end
tower.Parent = workspace.Map.Towers
tower.Tower.HitBox.Transparency = 0.5
tower.Tower.HitBox.Color = Color3.fromRGB(85, 255, 127)
tower.Tower.Range.Size = Vector3.new(0.25, tower.Configuration.Range.Value, tower.Configuration.Range.Value)
mouse.Move:Connect(function()
if placing == true then
movement(tower)
end
end)
end
for i, towerSlot in pairs(equippedTowers:GetChildren()) do
if towerSlot:IsA("ImageButton") then
towerSlot.MouseButton1Click:Connect(function()
if placing == false then
placing = true
local towerModel = replicatedStorage.Towers:FindFirstChild(player.TowerSlots["1"].Value)["1"]:FindFirstChild(player.TowerSlots["1"].Value)
local cloneTower = towerModel:Clone()
startPlacement(cloneTower)
mouse.Button1Down:Connect(function()
if confirmed == true and debounce == false then
debounce = true
placeEvents:FireServer(Vector3.new(posX, posY, posZ), cloneTower.Name, cloneTower.Configuration.PlaceCost.Value, cloneTower.Configuration.CraftingCost.Wood.Value, cloneTower.Configuration.CraftingCost.Stone.Value, cloneTower.Configuration.CraftingCost.Metal.Value, cloneTower.Configuration.CraftingCost.Crystal.Value, cloneTower.Configuration.CraftingCost.Plasma.Value)
cloneTower:Destroy()
confirmed = false
placing = false
for i, placedTower in pairs(towerFolder:GetChildren()) do
placedTower.Tower.HitBox.Transparency = 1
end
wait(1)
debounce = false
else
print("Can't place")
end
end)
end
end)
end
end
--Server
local replicatedStorage = game:GetService("ReplicatedStorage")
local placeEvent = replicatedStorage.Events.PlaceEvents:FindFirstChild("Place")
placeEvent.OnServerEvent:Connect(function(player, placePos, towerName, placeCost, wood, stone, metal, crystal, plasma)
local placedTowers = player:WaitForChild("GamePlayData").TowersPlaced
local towerLimit = player:WaitForChild("GamePlayData").MaxTowers
if placedTowers.Value < towerLimit.Value and player.GamePlayData.Cash.Value >= placeCost and player.PlayerData.Wood.Value >= wood and player.PlayerData.Stone.Value >= stone and player.PlayerData.Metal.Value >= metal and player.PlayerData.Crystal.Value >= crystal and player.PlayerData.Plasma.Value >= plasma then
local towerToPlace = replicatedStorage.Towers:FindFirstChild(towerName)["1"]:FindFirstChild(towerName)
local towerClone = towerToPlace:Clone()
towerClone:SetPrimaryPartCFrame(CFrame.new(placePos))
towerClone.Configuration.Owner.Value = player.Name
towerClone.Parent = workspace.Map.Towers
player.GamePlayData.Cash.Value -= placeCost
player.PlayerData.Wood.Value -= wood
player.PlayerData.Stone.Value -= stone
player.PlayerData.Metal.Value -= metal
player.PlayerData.Crystal.Value -= crystal
player.PlayerData.Plasma.Value -= plasma
player.GamePlayData.TowersPlaced.Value += 1
end
end)
-
What solutions have you tried so far? Did you look for solutions on the Developer Hub?
Issue #1:
I have not attempted any solutions as I am left scratching my head. It could be my fault for reusing code but it had no issues in a demo I made, and that was how I wanted my enemy spawning to work on for my actual game. So I have no idea why issues are arising when I had no prior issue.
Issue #2:
I am unsure about what to do here mainly because I don’t know what is causing the issue. I think I could go about checking for intersecting Tower Hitboxes better to help reduce any occurrence of the issue but otherwise I am unsure what to do.
I am deeply sorry to make such a lengthy post, these issues have put development on hold for me as I don’t want to progress with development until everything works properly.
If you have any questions regarding the code or other stuff ask away as it can likely help narrow a solution.