I am trying to make a working pvz game on roblox but i’m having some trouble
Here’s the test i did with the peashooter and my zombie
As you can see, it shoots the peas like it’s supposed to.
So then i used @Chickenhouse07’s pvz kit and started making modifications to it and here’s how the game plays out currently:
But i have a problem now, i put the scripts for the plant inside the model to make it animated and do things but when i planted it and sent the zombie after it, here’s what happened:
That’s not supposed to happen i want the scripts inside the plant model to work the way they did in the test but i don’t know how to fix it so here’s the scripts i have:
Peashooter’s shoot script:
local TweenService = game:GetService(“TweenService”)
local debris = game:GetService(“Debris”)
local Peashooter = script.Parent
local Pea = workspace.Pea
local PeasContainer = Instance.new(“Folder”)
PeasContainer.Name = “PeasContainer”
PeasContainer.Parent = Peashooter
local FirePart = Peashooter:FindFirstChild(“FirePart”)
local Head = Peashooter:FindFirstChild(“FakeHead”)
local Hole = Peashooter:FindFirstChild(“Hole”)
local Sidewalk = workspace:FindFirstChild(“SIdewalk”)
local MAX_SPEED = 55
local MAX_LIFE = 1
local DAMAGE = 20
local SHOOT_INTERVAL = 1.5
local function isWithinSidewalk(targetPosition)
if Sidewalk then
local sidewalkCFrame = Sidewalk.CFrame
local sidewalkSize = Sidewalk.Size
local minBound = sidewalkCFrame.Position - sidewalkSize / 2
local maxBound = sidewalkCFrame.Position + sidewalkSize / 2
-- Ensure the target is within the sidewalk's bounds along the X-axis
return targetPosition.X >= minBound.X and targetPosition.X <= maxBound.X
end
return false
end
local function findTarget()
local closestTarget = nil
local closestDistance = math.huge
local peashooterPosition = Peashooter:FindFirstChild(“HumanoidRootPart”).Position
for _, object in workspace:GetChildren() do
if object:IsA("Model") and object:FindFirstChild("Zombie") then
local targetHumanoid = object:FindFirstChild("Zombie")
local targetPosition = object:FindFirstChild("HumanoidRootPart").Position
local distance = (peashooterPosition - targetPosition).Magnitude
if distance < closestDistance and isWithinSidewalk(targetPosition) then
closestTarget = object
closestDistance = distance
end
end
end
return closestTarget
end
local function createTween(part, targetSize, duration)
local tweenInfo = TweenInfo.new(duration, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut)
local tween = TweenService:Create(part, tweenInfo, {Size = targetSize})
return tween
end
local function shootPea(targetPosition)
local pea_clone = Pea:Clone()
pea_clone.Parent = PeasContainer
pea_clone.CFrame = FirePart.CFrame
debris:AddItem(pea_clone, MAX_LIFE)
local targetDirection = (targetPosition - FirePart.Position).Unit
local targetCFrame = CFrame.new(FirePart.Position, targetPosition)
local targetPosition = targetCFrame.Position
pea_clone.Touched:Connect(function(hit)
if hit.Parent:FindFirstChild("Zombie") then
local targetHumanoid = hit.Parent:FindFirstChild("Zombie")
targetHumanoid:TakeDamage(DAMAGE)
pea_clone:Destroy()
end
end)
-- Trigger blink when a pea is spawned
local blinkScript = Peashooter:FindFirstChild("PeashooterBlink")
if blinkScript and blinkScript:FindFirstChild("triggerBlink") then
blinkScript.triggerBlink()
end
-- Tweening effect
local originalHeadSize = Head.Size
local targetHeadSize = originalHeadSize + Vector3.new(0, 0, originalHeadSize.Z * 0.19)
local originalHoleSize = Hole.Size
local targetHoleSize = originalHoleSize + Vector3.new(0, 0, originalHoleSize.Z * 0.2) -- Expand along Z-axis
local shrinkHeadSize = originalHeadSize - Vector3.new(0, 0, originalHeadSize.Z * 0.083)
local shrinkHoleSize = originalHoleSize - Vector3.new(0, 0, originalHoleSize.Z * 0.09)
local headTweenShrink = createTween(Head, shrinkHeadSize, 0.1)
local headTweenExpand = createTween(Head, targetHeadSize, 0.3)
local headTweenBackToNormal = createTween(Head, originalHeadSize, 0.25)
local holeTweenShrink = createTween(Hole, shrinkHoleSize, 0.095)
local holeTweenExpand = createTween(Hole, targetHoleSize, 0.3)
local holeTweenBackToNormal = createTween(Hole, originalHoleSize, 0.25)
headTweenShrink:Play()
holeTweenShrink:Play()
headTweenShrink.Completed:Connect(function()
headTweenExpand:Play()
holeTweenExpand:Play()
end)
headTweenExpand.Completed:Connect(function()
headTweenBackToNormal:Play()
holeTweenBackToNormal:Play()
end)
while (pea_clone.Position - targetPosition).Magnitude > 1 do
pea_clone.CFrame = pea_clone.CFrame + targetDirection * MAX_SPEED * task.wait()
end
end
task.spawn(function()
while true do
local target = findTarget()
if target then
shootPea(target:FindFirstChild(“HumanoidRootPart”).Position)
end
task.wait(SHOOT_INTERVAL)
end
end)
game:GetService(“RunService”).Heartbeat:Connect(function(deltaTime)
for _, pea in ipairs(PeasContainer:GetChildren()) do
pea.CFrame = pea.CFrame + pea.CFrame.LookVector * MAX_SPEED * deltaTime
end
end)
The Zombie’s Behavior script:
local zombie = script.Parent
local humanoid = zombie:FindFirstChildOfClass(“Humanoid”)
local animator = humanoid:FindFirstChildOfClass(“Animator”)
local house = workspace:FindFirstChild(“House”)
local walkAnimations = { – Add your walk animation IDs here
“rbxassetid://137545334964529”,
“rbxassetid://140723213087927”
}
local eatAnimationId = “rbxassetid://83489481475373” – Add your eat animation ID here
local killedAnimations = { – Add your killed animation IDs here
“rbxassetid://138427626345650”,
“rbxassetid://98721502213748”
}
local currentWalkTrack
local function playAnimation(animationId)
local animation = Instance.new(“Animation”)
animation.AnimationId = animationId
local track = animator:LoadAnimation(animation)
track:Play()
return track
end
local function playWalkAnimation()
if currentWalkTrack then
currentWalkTrack:Stop()
end
currentWalkTrack = playAnimation(walkAnimations[math.random(#walkAnimations)])
end
local function moveToHouse()
if house then
local direction = (house.Position - zombie.HumanoidRootPart.Position).Unit
local targetPosition = zombie.HumanoidRootPart.Position
-- Move in a straight line along one axis at a time
if math.abs(direction.X) > math.abs(direction.Z) then
targetPosition = Vector3.new(house.Position.X, zombie.HumanoidRootPart.Position.Y, zombie.HumanoidRootPart.Position.Z)
else
targetPosition = Vector3.new(zombie.HumanoidRootPart.Position.X, zombie.HumanoidRootPart.Position.Y, house.Position.Z)
end
-- Rotate to face the house
zombie.HumanoidRootPart.CFrame = CFrame.lookAt(zombie.HumanoidRootPart.Position, house.Position)
humanoid:MoveTo(targetPosition)
end
end
local function onTouch(other)
local otherHumanoid = other.Parent:FindFirstChildOfClass(“Humanoid”)
if otherHumanoid and otherHumanoid ~= humanoid and not other.Parent.Name:find(“Zombie”) then
if currentWalkTrack then
currentWalkTrack:Stop()
end
local eatTrack = playAnimation(eatAnimationId)
humanoid:MoveTo(humanoid.RootPart.Position) – Stop moving
-- Inflict 5 or 10 damage per second
while otherHumanoid.Health > 0 and eatTrack.IsPlaying do
local damage = math.random(20)
otherHumanoid:TakeDamage(damage)
task.wait(1) -- Inflict damage every second
end
eatTrack:Stop() -- Stop the eat animation
task.wait(1) -- Wait one second before resuming walk animation
playWalkAnimation() -- Resume walk animation
moveToHouse() -- Resume moving to the house
end
end
local function onHealthChanged(health)
if health <= humanoid.MaxHealth / 2 then
local leftArm = zombie:FindFirstChild(“LeftArm”)
if leftArm then
leftArm:Destroy() – Detach left arm
local bone = zombie:FindFirstChild(“Bone”)
if bone then
bone.Transparency = 0 – Set Bone transparency to 0
end
end
end
if health <= 0 then
local fakeHead = zombie:FindFirstChild(“FakeHead”)
if fakeHead then
fakeHead:BreakJoints() – Detach head
fakeHead.Parent = nil – Remove head from the zombie model
end
local killedTrack = playAnimation(killedAnimations[math.random(#killedAnimations)])
killedTrack.Stopped:Wait() – Wait for the killed animation to finish
zombie:Destroy() – Despawn the zombie
end
end
humanoid.HealthChanged:Connect(onHealthChanged)
zombie.HumanoidRootPart.Touched:Connect(onTouch)
humanoid.MoveToFinished:Connect(function(reached)
if not reached then
moveToHouse() – Retry moving to the house if not reached
end
end)
playWalkAnimation() – Start with a walk animation
print(“Multiple zombies can now walk towards the house simultaneously.”) – New line added
while true do
moveToHouse()
task.wait(1) – Adjust the wait time as needed
end
And these are some of the errors i read in the output
You must publish this place to the web to access DataStore. - Server - Saving:2
14:32:33.625 Stack Begin - Studio
14:32:33.625 Script ‘ServerScriptService.Saving’, Line 2 - Studio - Saving:2
14:32:33.625 Stack End - Studio
14:32:34.013 triggerBlink is not a valid member of Script “ReplicatedStorage.Plants.PeaShooter.PeashooterBlink” - Client - PeashooterBlink:40
14:32:34.013 Stack Begin - Studio
14:32:34.013 Script ‘ReplicatedStorage.Plants.PeaShooter.PeashooterBlink’, Line 40 - Studio - PeashooterBlink:40
14:32:34.013 Stack End - Studio
14:32:34.323 Plugin “[SEMI-BROKEN] Character Creator” was denied script injection permission. You can grant this permission in the Plugin Manager. - Server
14:32:34.323 Stack Begin - Studio
14:32:34.323 Script ‘cloud_171922549.CustomCharGui.PluginScript’, Line 10 - Studio
14:32:34.324 Stack End - Studio
14:32:34.326 cloud_3104443226.S^X | v1.4.1b:26: attempt to index nil with ‘WaitForChild’ - Server
14:32:34.326 Stack Begin - Studio
14:32:34.326 Script ‘cloud_3104443226.S^X | v1.4.1b’, Line 26 - Studio
14:32:34.327 Stack End - Studio
14:32:34.506 Plugin “Module Cloud” was denied script injection permission. You can grant this permission in the Plugin Manager. - Server
14:32:34.506 Stack Begin - Studio
14:32:34.506 Script ‘cloud_11779568144.ModulePluginGui.PluginScript’, Line 15 - Studio
14:32:34.507 Stack End - Studio
14:32:34.809 Plugin “[SEMI-BROKEN] Character Creator” was denied script injection permission. You can grant this permission in the Plugin Manager. - Client
14:32:34.809 Stack Begin - Studio
14:32:34.809 Script ‘cloud_171922549.CustomCharGui.PluginScript’, Line 10 - Studio
14:32:34.809 Stack End - Studio
14:32:34.826 Plugin “Module Cloud” was denied script injection permission. You can grant this permission in the Plugin Manager. - Client
14:32:34.826 Stack Begin - Studio
14:32:34.826 Script ‘cloud_11779568144.ModulePluginGui.PluginScript’, Line 15 - Studio
14:32:34.826 Stack End - Studio
14:32:38.634 Infinite yield possible on ‘ReplicatedStorage.Projectiles:WaitForChild(“Pea”)’ - Studio
14:32:38.634 Stack Begin - Studio
14:32:38.634 Script ‘ReplicatedStorage.Plants.PeaShooter.PeashootersShoot’, Line 8 - Studio - PeashootersShoot:8
14:32:38.635 Stack End - Studio
14:32:50.080 The game has started for the client - Client - ClientManager:160
14:32:55.096 Players.greenfavoritecolor.PlayerGui.Sun:15: invalid argument #2 to ‘random’ (interval is empty) - Client - Sun:15
14:32:55.096 Stack Begin - Studio
14:32:55.097 Script ‘Players.greenfavoritecolor.PlayerGui.Sun’, Line 15 - function GetRandomPos - Studio - Sun:15
14:32:55.097 Script ‘Players.greenfavoritecolor.PlayerGui.Sun’, Line 24 - Studio - Sun:24
14:32:55.097 Stack End - Studio
14:32:56.223 The current thread cannot read ‘Source’ (lacking capability PluginOrOpenCloud) - Client - ScriptFixer:9
14:32:56.224 Stack Begin - Studio
14:32:56.224 Script ‘ReplicatedStorage.Modules.ScriptFixer’, Line 9 - function ForceActivateScripts - Studio - ScriptFixer:9
14:32:56.224 Script ‘ReplicatedStorage.Modules.SpawnPlant.PlantDetail’, Line 9 - Studio - PlantDetail:9
14:32:56.224 Script ‘ReplicatedStorage.Modules.SpawnPlant’, Line 20 - function Planting - Studio - SpawnPlant:20
14:32:56.224 Script ‘ReplicatedStorage.Modules.SpawnPlant’, Line 43 - Studio - SpawnPlant:43
14:32:56.224 Script ‘Players.greenfavoritecolor.PlayerGui.ClientManager’, Line 531 - Studio - ClientManager:531
14:32:56.224 Stack End - Studio
14:32:56.224 triggerBlink is not a valid member of Script “Workspace.Plants.PeaShooter.PeashooterBlink” - Client - PeashooterBlink:40
14:32:56.224 Stack Begin - Studio
14:32:56.225 Script ‘Workspace.Plants.PeaShooter.PeashooterBlink’, Line 40 - Studio - PeashooterBlink:40
14:32:56.225 Stack End - Studio
14:33:01.597 The current thread cannot read ‘Source’ (lacking capability PluginOrOpenCloud) - Client - ScriptFixer:9
14:33:01.598 Stack Begin - Studio
14:33:01.598 Script ‘ReplicatedStorage.Modules.ScriptFixer’, Line 9 - function ForceActivateScripts - Studio - ScriptFixer:9
14:33:01.598 Script ‘ReplicatedStorage.Modules.SpawnPlant.PlantDetail’, Line 9 - Studio - PlantDetail:9
14:33:01.598 Script ‘ReplicatedStorage.Modules.SpawnPlant’, Line 20 - function Planting - Studio - SpawnPlant:20
14:33:01.598 Script ‘ReplicatedStorage.Modules.SpawnPlant’, Line 43 - Studio - SpawnPlant:43
14:33:01.598 Script ‘Players.greenfavoritecolor.PlayerGui.ClientManager’, Line 531 - Studio - ClientManager:531
14:33:01.598 Stack End - Studio
14:33:01.598 triggerBlink is not a valid member of Script “Workspace.Plants.PeaShooter.PeashooterBlink” - Client - PeashooterBlink:40
14:33:01.599 Stack Begin - Studio
14:33:01.599 Script ‘Workspace.Plants.PeaShooter.PeashooterBlink’, Line 40 - Studio - PeashooterBlink:40
14:33:01.599 Stack End - Studio
14:33:15.143 Humanoid is not a valid member of UnionOperation “LeftShoulder” - Client - SpawnZombo:171
14:33:15.143 Stack Begin - Studio
14:33:15.143 Script ‘ReplicatedStorage.Modules.SpawnZombo’, Line 171 - Studio - SpawnZombo:171
14:33:15.143 Script ‘Players.greenfavoritecolor.PlayerGui.ClientManager’, Line 190 - Studio - ClientManager:190
14:33:15.144 Stack End - Studio
here’s the peashooter blink:
local Peashooter = script.Parent
local Eyes = Peashooter:FindFirstChild(“Eyes”)
local Blink1 = Peashooter:FindFirstChild(“Blink 1”)
local Blink2 = Peashooter:FindFirstChild(“Blink 2”)
local function blinkSequence()
Eyes.Transparency = 1
Blink1.Transparency = 0
task.wait(0.1)
Blink1.Transparency = 1
Blink2.Transparency = 0
task.wait(0.1)
Blink2.Transparency = 1
Blink1.Transparency = 0
task.wait(0.1)
Blink1.Transparency = 1
Eyes.Transparency = 0
end
local function triggerBlink()
blinkSequence()
end
– Random blinking
task.spawn(function()
while true do
task.wait(math.random(5, 10)) – Random interval between blinks
blinkSequence()
end
end)
– Blink every time a pea appears in PeasContainer
local PeasContainer = Peashooter:FindFirstChild(“PeasContainer”)
if PeasContainer then
PeasContainer.ChildAdded:Connect(function(child)
triggerBlink()
end)
end
script.Parent:FindFirstChild(“PeashooterBlink”).triggerBlink = triggerBlink
here’s the zombie spawn:
local SpawnZombo = {}
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local PhysicsService = game:GetService(“PhysicsService”)
local Debris = game:GetService(“Debris”)
local ZombieFolder = script.Parent.Parent:WaitForChild(“Zombies”)
local MoveZomb = require(script.MoveZomb)
local function Freeze(Zombie)
local NewParticleEmitter = nil
if not Zombie:FindFirstChild("Frozen") then
local NewParticleEmitter = Instance.new("ParticleEmitter")
NewParticleEmitter.Name = "Frozen"
NewParticleEmitter.Size = NumberSequence.new(.4)
NewParticleEmitter.SpreadAngle = Vector2.new(-180, 180)
NewParticleEmitter.Rate = 30
NewParticleEmitter.Lifetime = NumberRange.new(.6)
NewParticleEmitter.Color = ColorSequence.new(Color3.new(0.0980392, 0.654902, 1))
NewParticleEmitter.Parent = Zombie:FindFirstChild("HumanoidRootPart")
end
if Zombie:FindFirstChild("Zombie") then
Zombie.Humanoid.WalkSpeed = Zombie.Config.Walk.Value /2
end
wait(8)
if Zombie:FindFirstChild("Zombie") then
Zombie.Humanoid.WalkSpeed = Zombie.Config.Walk.Value
end
end
local function Eat(Plant, Zombie)
local Tile = Plant.Config.Tile.Value
local Hp = Plant.Config.EatHp
local EatAnimation = Zombie.Humanoid:LoadAnimation(Zombie.Eat) -- I disabled this, but if you can figure out how get it to work go ahead XD
local WalkAnim = Zombie.Humanoid:LoadAnimation(Zombie.Walk)
local OldWalk = Zombie.Humanoid.WalkSpeed
if Plant ~= nil and Zombie ~= nil then
Zombie.Humanoid.WalkSpeed = 0
WalkAnim:Stop()
EatAnimation:Play()
repeat
wait(1)
Hp.Value -= 1
until Hp.Value <= 0 or Zombie == nil
if Zombie.Parent ~= nil and Zombie.Humanoid.Health ~= 0 then
EatAnimation:Stop()
WalkAnim:Stop()
Zombie.Humanoid.WalkSpeed = OldWalk
Tile.Config.InUse.Value = nil
Tile.Config.Type.Value = ""
Plant:Destroy()
end
end
EatAnimation:Stop()
WalkAnim:Stop()
end
local function HpCheck(Zombie, HealthGui)
Zombie.Config.Hp.Changed:Connect(function()
if Zombie:FindFirstChild("Zombie") then
local SizeToChnge = Zombie.Humanoid.Health / 100
local HealthGui = Zombie:WaitForChild("HealthGui")
HealthGui.CurrentHealth.Size = UDim2.new(SizeToChnge, 0, HealthGui.CurrentHealth.Size.Y.Scale, 0)
end
end)
end
local function CollisionCheck(Zombie)
local Eating = false
Zombie.HumanoidRootPart.Touched:Connect(function(Part)
if Part:FindFirstChild("DmgZombie") then
local Dmg = Part:FindFirstChild("DmgZombie").Value
if Part:FindFirstChild("FreezerTag") then
if Zombie == nil then return end
print("RanBefore")
workspace.Audio.Hit:Play()
Zombie.Humanoid.Health -= Dmg
Zombie.Config.Hp.Value -= Dmg
Part:Destroy()
if Zombie.Humanoid.Health <= 0 then
wait(1)
Zombie:Destroy()
else
spawn(function() Freeze(Zombie) end)
end
else
workspace.Audio.Hit:Play()
Zombie.Humanoid.Health -= Dmg
Zombie.Config.Hp.Value -= Dmg
if not Part:FindFirstChild("Mower") then
Part:Destroy()
end
if Zombie.Humanoid.Health <= 0 then
wait(1)
Zombie:Destroy()
end
end
elseif Part.Parent:FindFirstChild("CertifiedPlant") then
local Plant = Part.Parent
if Plant ~= nil and Zombie ~= nil and Eating == false then
Eating = true
Eat(Plant, Zombie)
Eating = false
end
end
end)
end
SpawnZombo.Remove = function(Zombie, Time)
local World1Folder = workspace.World:WaitForChild("Map").DontChange
local humanoid = Zombie:FindFirstChild("Zombie")
local animation = humanoid:LoadAnimation(Zombie:FindFirstChild("Eat"))
local animation2 = humanoid:LoadAnimation(Zombie:FindFirstChild("Walk"))
animation:Stop()
animation2:Stop()
wait(Time)
if Zombie ~= nil then
Zombie:Destroy()
end
end
SpawnZombo.SpawnType = function(Zombie, row, plr)
local World1Folder = workspace.World:WaitForChild("Map").DontChange
local ZombieClone = Zombie:Clone()
ZombieClone.Humanoid.Health = Zombie.Config.MaxHp.Value
ZombieClone.Parent = workspace.Zombies
for i, object in ipairs(ZombieClone:GetDescendants()) do
if object:IsA("BasePart") then
object.CollisionGroup = "Zombie"
end
end
local HealthGuiClone = script.Parent.HealthGui:Clone()
HealthGuiClone.Parent = ZombieClone
HealthGuiClone.Adornee = ZombieClone:FindFirstChild("Head")
HpCheck(ZombieClone, HealthGuiClone)
CollisionCheck(ZombieClone)
local ZombSpawn = World1Folder.Points.Start:FindFirstChild("Start" .. row)
ZombieClone.PrimaryPart.CFrame = ZombSpawn.CFrame
MoveZomb.Move(ZombieClone, row, plr)
end
return SpawnZombo
Here’s the plant spawn script:
local Spawner = {}
local PhysicsService = game:GetService(“PhysicsService”)
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local PlantModule = require(script.PlantDetail)
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local PlantDetail = require(ReplicatedStorage.Modules.SpawnPlant.PlantDetail)
local function Planting(plr, Tile, Plant)
if Plant == nil then return end
local NewPlant = Plant:Clone()
NewPlant.Parent = workspace.Plants
NewPlant:MoveTo(Tile.Position)
NewPlant.PrimaryPart.Position = NewPlant.PrimaryPart.Position + Vector3.new(0, -3.8, 0)
-- 🛠️ FIX: Auto activate scripts for this Plant
PlantDetail.FixScripts(NewPlant)
Tile.Config.InUse.Value = NewPlant
NewPlant:WaitForChild("Config").Tile.Value = Tile
-- Now run plant behavior
PlantDetail.Duplicate(NewPlant, plr)
end
Spawner.Plant = function(Plant, Tile, plr)
if Plant == nil then return end
local PlayerAmountOfSun = plr.PlayerGui.PlayerStatsAndValues.Sun
local PlantToClone = Plant
local config = PlantToClone:WaitForChild("Config")
local cost = config.Cost
if PlayerAmountOfSun.Value >= cost.Value then
PlayerAmountOfSun.Value -= cost.Value
Planting(plr, Tile, PlantToClone)
end
end
return Spawner
I tried asking ai for help but nothing worked yet
So i’d really appreciate help from you