Gnome.Codes website logo

Tower Defense

Year:

2021

Skill:

3/5

Episodes:

22

Duration:

12hrs

Demo Game

Tower Defense

Help

Download files are available at the bottom of the page.

Solutions to common issues are listed below:

This is one of the most common complaints I hear in regards to the series, I know it can be very frustrating if this is your first time scripting.

First off, I recommend everyone has already watched my beginner guide before starting this series.

This will save you so much time in the long run! Watching this will ensure you are familiar with many of the basic concepts of coding which this series assumes you already know.


Locating the problem

In order for us to know what the problem actually is, we need to open our output window. This displays any errors or typos in big red text that are preventing the code from running properly.

Select the "View" panel from the top of your screen and select "Output"


You should now see this window at the bottom of your screen



INDEX NIL ERROR

 

It's important to note, that we have 2 different variables, one named waypoints and the other named waypoint singular these should not be mixed up!

If you've got them confused, your code may look something like this with a red underline:


waypoint is just used as a counter, which will have a number value.

waypoints refers to the folder in the workspace that our parts are contained within.

In this example, the problem is because you're trying to get the contents of the waypoint which is just a number,  when you need to get the contents of the waypoints folder.

This is the correct code:

local zombie = script.Parent
local waypoints = workspace.Waypoints

for waypoint=1, #waypoints:GetChildren() do
    zombie.Humanoid:MoveTo(waypoints[waypoint].Position)
    zombie.Humanoid.MoveToFinished:Wait()
end

Waypoints is not a valid member of Workspace "Workspace" ERROR

In the script above, we have stated that there is a folder named "Waypoints", located in the workspace

Therefore, this must be the exact name of the folder.

For example, if you've named your folder waypoints with a lowercase w  this is not the same as Waypoints

Everything in coding is case sensitive




NO ERROR?

Your output window is blank, and there are no error messages but your zombie is still not moving?

Is your model anchored?

Every part inside the mob needs to have the anchored property unticked

Otherwise your monster will be rooted to the spot, regardless of what code you write!


The humanoid MoveTo method automatically times out if the destination has not been reached after 8 seconds, therefore it will skip ahead to the next waypoint if the mob is too slow or the waypoints too far apart.

Solution #1

The easiest way to fix this is to simply place down more waypoints, so that even the slowest mob can always reach each waypoint within 8 seconds. (Remember to re-number the names of your waypoints if doing this)


Solution #2

Alternatively, for a more comprehensive solution you can overwrite this behaviour by re-calling the function. This code sample is from the roblox docs

local function moveTo(humanoid, targetPoint, andThen)
    local targetReached = false

    -- listen for the humanoid reaching its target
    local connection
    connection = humanoid.MoveToFinished:Connect(function(reached)
        targetReached = true
        connection:Disconnect()
        connection = nil
        if andThen then
            andThen(reached)
        end
    end)

    -- start walking
    humanoid:MoveTo(targetPoint)

    -- execute on a new thread so as to not yield function
    task.spawn(function()
        while not targetReached do
            -- does the humanoid still exist?
            if not (humanoid and humanoid.Parent) then
                break
            end
            -- has the target changed?
            if humanoid.WalkToPoint ~= targetPoint then
                break
            end
            -- refresh the timeout
            humanoid:MoveTo(targetPoint)
            task.wait(6)
        end

        -- disconnect the connection if it is still connected
        if connection then
            connection:Disconnect()
            connection = nil
        end
    end)
end

local function andThen(reached)
    print((reached and "Destination reached!") or "Failed to reach destination!")
end

moveTo(script.Parent:WaitForChild("Humanoid"), Vector3.new(50, 0, 50), andThen)

SOLUTION #3 (Not recommended)

Many scripters will suggest a quick modification by adding a repeat...until loop. 

repeat 
  humanoid:MoveTo(waypoints[waypoint].Position)
until humanoid.MoveToFinished:Wait()

Personally I'm not a fan of this approach as I think you should avoid adding loops in your code where they are not needed.

Combining loops with events can easily introduce recursive problems if you don't know what you're doing.


SOLUTION #4 (advanced)

Ultimately, the best option is to forego the humanoid entirely. The humanoid datamodel can be expensive on resources if you want to have lots of enemies and it's methods and functionality can often be clunky.


An alternative way to move models is using Lerp, to move the model a small amount each frame.

The basics of this I covered here: https://www.youtube.com/watch?v=R-xurNJtx7M

You can see the principles of this integrated into a more advanced project such as my doors monster: https://www.youtube.com/watch?v=fDIIv-XZbVY

Animations make your games more lively, without them something just doesn't feel right. Here are the most common issues.

1: Client-sided Animations

In the TD tutorial, GnomeCode implemented the Animations so that it is played on the client via a LocalScript. These scripts run codes which are only visible to the client, not the server. We don't want the server to handle the Animations because they take up performance.

The obvious fix is pressing Play!


2: Animation ownership

You can only use Animations owned by you or on the Marketplace Catalog. You cannot use Animations from others.

As of now, there are no practical methods of sharing Animations between users to users. However, you could upload a model containing the Animation and let the other user publish it to Roblox themselves. 


3: AnimationPriority

An obvious fix would be to republish the animation with the right AnimationPriority. However, you can set them in the script as well by doing animationTrack.AnimationPriority = Enum.AnimationPriority.PriorityName

Here are the AnimationPriority for every animations in the TD tutorial:

Walk.AnimationPriority = Enum.AnimationPriority.Movement

Idle.AnimationPriority = Enum.AnimationPriority.Idle

Attack.AnimationPriority = Enum.AnimationPriority.Action

4: Legacy Animation Blending

An alternative to 3. AnimationPriority, you can add an attribute called RbxLegacyAnimationBlending as a boolean and set it to true.

However, according to this DevForum Reply, you will lose some new animation engine features for it


5: First few mobs aren't animated

Ep1-10: For players who have slow loading times. Sometimes, at the start of a wave, the first few mobs won't play animations.

This happens because the animation script only runs when it detects when a child is added in the mobs folder and because the

main script spawns in some mobs before that, the first few aren't animated.

Solution: You can loop through the mobs folder again and call the animate function when the local script is fully loaded in.

for _, mob in ipairs(workspace.Mobs:GetChildren()) do
    animateMob(mob)
end

SOLUTION:

If you're having trouble with your player collisions I'd suggest changing  CharacterAdded  to  CharacterAppearanceLoaded  in your OnPlayerAdded script. 

This should give the character a bit more time to load in before it tries to assign the collision groups.

CharacterAdded  gets called the very instant the character begins loading. The only reason I get away with it in my video is because my character has very few components.


NOTE:

Since the tutorial, Roblox has deprecated 

PhysicsService:SetPartCollisionGroup()

To fix this warning replace  SetPartCollisionGroup  with  object.CollisionGroup = "CollisionGroupName" 

After you are done with that you can remove PhysicsService from your script because it is no longer being used

Make sure to do this in  Mob, Tower, onPlayerAdded and gameController scripts

When using :SetNetworkOwner(), you might come across the error:

Network Ownership API cannot be called on Anchored parts or parts welded to Anchored parts error

To solve this, check if any of the model's BasePart descendants have their Anchored property set to false. You could either select every descendants of the model in the Explorer or run some code in the Command Bar to loop through every descendants and set every BasePart.Anchored to false. I would recommend going for the latter. 

Here's the code to run in the Command Bar (formatted to be readable), replace model with whatever way you wanna reference the model:

for _, instance in model:GetDescendants() do
  if instance:IsA("BasePart") then
    instance.Anchored = false
  end
end

For convenience, you can also put this into your Tower script, just put it before you do :SetNetworkOwner(). However, it might slow down the game every time you place a new tower, especially if there are many children inside of the model.

An alternative solution would be to ditch :SetNetworkOwner() entirely and set every BasePart.Anchored to true. If you want to do this, you can no longer use BodyGyro as shown in the tutorial.

(GnomeCode fixed this problem in Ep7 at 14:00 Merging tower scripts, he moved the test script over to a module script and make it generalized)

Explanation

In this Episode, GnomeCode uses a Union for his test tower, it's a BasePart, not a Model.

Many people use a Model as their test tower (which is not a BasePart!) There is no Model.Position, but you can use a Part as a replacement.


Solution

Check if your Model has a HumanoidRootPart as its child. If not, add a HumanoidRootPart, they are usually a Part with Part.Transparency = 1 at the center of the Model. You could set the Model.PrimaryPart to this Part.

After checking, you make a slight change the TowerTesting Script. Instead of tower.Position, change to tower.HumanoidRootPart.Position (similar to in Ep7) or tower.PrimaryPart.Position (I'd prefer this)

The towers I showed in my video were all R15 characters, however if you're using the classic R6 character shape, then hipheight is calculated slightly differently and cannot be used.

Instead you can calculate the height of torso from the floor, using their leg height.

Update your game controller local script with this new value for R6 humanoids only (You will still need the old formula for R15 models)

local y = result.Position.Y + towerToSpawn["Left Leg"].Size.Y + (towerToSpawn.PrimaryPart.Size.Y / 2)

(see below image for difference between the two character types)


Explanation

This error occurs when you attempt to enter a number with decimal points into  IntValue.Value

(e.g 0.3, 1.2, 4367.125812353289)

IntValue.Value only accepts integers, they are numbers without decimal points

(e.g 1, 3, 29, 62)


Solution

You can easily fix this by replacing IntValue with a NumberValue. After that, rename it to Cooldown or whatever name you called it and problem solved! Now you can use any numbers with decimal points


An example of what your Cooldown NumberValue would look like:


This one is actually a pretty simple fix

in Health (the module in ReplicatedStorage.Modules)

simply parent the newHealthBar to the model instead

so your old script:

local newHealthBar = script.HealthGui:Clone()
newHealthBar.Adornee = model:WaitForChild("Head")
newHealthBar.Parent = Players.LocalPlayer.PlayerGui:WaitForChild("Billboards")

becomes:

local newHealthBar = script.HealthGui:Clone()
newHealthBar.Adornee = model:WaitForChild("Head")
newHealthBar.Parent = model

That way it will automatically be deleted whenever the mob is destroyed

Sometimes you may find your first and last modes selecting the wrong target.

The targeting has a case where the incorrect target is selected lets imagine two mobs "MobA" and "MobB"

If MobA is 3 studs from the 5th waypoint and MobB is 4 studs away from the 6th waypoint

If MobA is considered first in the loop it will set best distance to 3 meaning that when it considers MobB it updates the bestWaypoint but fails to then update as it is a lower distance than the best distance.

This can be solved by switching the "First" and "Last" targeting modes to:

elseif mode == "First" then
    if not bestWaypoint or mob.MovingTo.Value > bestWaypoint then
        bestWaypoint = mob.MovingTo.Value
        bestDistance = nil
    end
    if mob.MovingTo.Value == bestWaypoint and (not bestDistance or distanceToWaypoint < bestDistance) then
        bestDistance = distanceToWaypoint
        bestTarget = mob
    end
elseif mode == "Last" then
    if not bestWaypoint or mob.MovingTo.Value < bestWaypoint then
        bestWaypoint = mob.MovingTo.Value
        bestDistance = nil
    end
    if mob.MovingTo.Value == bestWaypoint and (not bestDistance or distanceToWaypoint > bestDistance) then
        bestDistance = distanceToWaypoint
        bestTarget = mob
    end

that way the best distance is reset whenever a new best waypoint is set.

This one is pretty simple.

First go to your elevators ElevatorServer script and find

elevatorEvent.OnServerEvent:Connect(function(player)

After that add elevator near the player so it becomes

elevatorEvent.OnServerEvent:Connect(function(player, elevator)

 (you need to do this for every ElevatorServer script)

After that go to your StarterGui then find Client script inside of Elevator ScreenGui and add 

local elevatorTo

 On top of your local script 


Now go to

elevatorEvent.OnClientEvent:Connect(function(elevator) 

and add a new line, on that line type 

elevatorTo = elevator

Then add elevatorTo in

elevatorEvent:FireServer() 

 so it becomes 

elevatorEvent:FireServer(elevatorTo) 


Small typo made during this video.

All you need to do is replace this line from line 45 in Datastore script

data[player.UserId].Stars = stars

with this

data[player.UserId].Stars += stars

This has already been fixed in the download file for this series.

Downloads

Login

Sign up