PcoWSkbVqDnWTu_dm2ix
We use cookies on this site to enhance your user experience

Top Down Action: PiBot Launcher

Top Down Action: PiBot Launcher

Jul 30 2018, 2:23 PM PST 10 min

Now that our PiBots can move and identify the player, let’s give them the ability to fight back. All of the PiBots have a tool already added that is identical to the one the player uses. We need to insert a script into each of these to tell them what to do when they are activated. Unlike the player tool though, the robot’s code all runs on the server, so we will not need to make any LocalScript|LocalScripts.

TDS_PiBot_Launcher.png

Let’s first add a Script called PieLauncher to RobotScripts in ServerStorage|ServerStorage. For now, let’s just put in the basic functionality for the tool into this script:

local tool = script.Parent
local robot = tool.Parent
local animationTrack = robot.Humanoid:LoadAnimation(tool.Animation)

local function onActivate()
	animationTrack:Play()
end

tool.Activated:connect(onActivate)

We first load the Animation in the tool into the robot’s Humanoid. We then play the resulting AnimationTrack when the Tool/Activated|Activated event fires.

Now let’s modify the AIScriptGiver Script inside of ServerScriptService|ServerScriptService to insert our new script into the PiBot’s launchers:

local robotScripts = game.ServerStorage.RobotScripts

local function insertAIScript(robot)
	local aiScript = robotScripts:FindFirstChild(robot.Name):Clone()
	aiScript.Parent = robot
	if robot.Name == "PiBot" then
		local launcherScript = robotScripts.PieLauncher:Clone()
		launcherScript.Parent = robot.Tool
	end
end

local function onRobotAdded(robot)
	insertAIScript(robot)
end

for _, robot in pairs(game.Workspace.Robots:GetChildren()) do
	insertAIScript(robot)
end

game.Workspace.Robots.ChildAdded:connect(onRobotAdded)

When inserting a script into a robot, we check if that robot is a PiBot. If it is, then we insert a copy of the PieLauncher script into that robot’s tool. Now when we run the game we can see the PiBots activating their launchers when the player gets close. Right now though, there is nothing limiting how often they can activate, so the robots try to use their tools every update tick.

Let’s implement a cooldown in the PiBots so they don’t activate their tools as often. In PiBot, we have already defined the variables ActionCooldown and LastAction. We can use these in RobotController to put a delay between launches. Open the RobotController Script in RobotScripts inside of ServerStorage|ServerStorage. Edit the code like so:

local RobotController = {}

local updateDelta = .1

local function distanceToCharacter(robot, character)
	if not character or character.Humanoid.Health <= 0 then return nil end
	local toPlayer = character.Head.Position - robot.Head.Position
	local toPlayerRay = Ray.new(robot.Head.Position, toPlayer)
	local part = game.Workspace:FindPartOnRay(toPlayerRay, robot, false, false)
	if part and part:IsDescendantOf(character) then
		return toPlayer.magnitude
	end
	return nil
end

local function getClosestVisibleCharacter(robot)
	local closestDistance = math.huge
	local closestCharacter = nil
	for _, player in pairs(game.Players:GetPlayers()) do
		local distance = distanceToCharacter(robot, player.Character)
		if distance and distance < closestDistance then
			closestDistance = distance
			closestCharacter = player.Character
		end
	end
	return closestCharacter, closestDistance
end

local function orientRobot(robot, target)
	local torso = robot.Torso
	local targetTorso = target.Torso
	torso.BodyGyro.CFrame = CFrame.new(torso.Position, targetTorso.Position)
end

function RobotController:RunAI(robot, configurations)
	while wait(updateDelta) and robot.Humanoid.Health > 0 do
		local target, targetDistance = getClosestVisibleCharacter(robot)
		if target then
			orientRobot(robot, target)
			if targetDistance < configurations.ActionDistance then
				local now = tick()
				if now - configurations.LastAction > configurations.ActionCooldown then
					spawn(function()
						configurations.Action(target)
					end)
					configurations.LastAction = now
				end
			elseif targetDistance < configurations.AggroDistance then
				configurations.Aggro(target)
			end
		end
	end
end

return RobotController

When a robot tries to activate its ability, the current time is stored into the variable now using the tick function. The amount of time since the last action can be calculated by subtracting the value in LastAction from the current time in now. That can then be compared to the value stored in ActionCooldown. If now minus the LastAction is longer than the ActionCooldown, that means the action is ready to be activated again. We can then set LastAction to the value stored in now.

We also surround the line configurations.Action(target) by a function called Spawn. In some of our Action functions, we will have code that includes a wait(). If we don’t use Spawn, our whole update loop will have to wait until that code finishes. Using spawn allows the enclosed code to run at the same time as code after the spawn, allowing your script to do multiple things at the same time.

Launch Pies

Launching the pies from the PiBots’ launchers will be very similar to how we launched them from the player’s tool. In the PieLauncher script add the following code:

local tool = script.Parent
local robot = tool.Parent
local animationTrack = robot.Humanoid:LoadAnimation(tool.Animation)
local launchTime = .15
local launchSpeed = 20
local reloadSpeed = 1
local pieLifetime = 2

local function onActivate()
	local pie = game.ServerStorage.Models.Pie:Clone()
	
	animationTrack:Play()
	
	wait(launchTime)
	
	tool.Pie.Transparency = 1
	pie.CFrame = tool.Pie.CFrame
	local direction = tool.Handle.CFrame.lookVector
	pie.Velocity = launchSpeed * direction
	pie.Parent = tool
	pie:SetNetworkOwner(nil)
	wait(reloadSpeed)
	tool.Pie.Transparency = 0 

	wait(pieLifetime)
	
	pie:Destroy()
end

tool.Activated:connect(onActivate)

In the onActivate function we first make a new pie from the model inside ServerStorage|ServerStorage. Then, after waiting for the time set in launchTime, we put the pie in the tool so that it shows up in the game, make sure the server owns the physics, and then we place the pie over the pie that exists in the tool. We also hide the pie built into the tool to make it look like it is the one being flung.

We then get the direction to launch the pie from the lookVector of the tool’s handle, and then set the Velocity of our new pie in that direction. We make the built-in pie visible again to show the launcher is reloaded. After waiting some time we then destroy the pie we made at the beginning of the function.

Pie collision

The last thing we need to do is cause the pies to damage on contact. We can borrow code more or less directly from PieLauncherLocalScript. Enter the following code into PieLauncher:

local tool = script.Parent
local robot = tool.Parent
local animationTrack = robot.Humanoid:LoadAnimation(tool.Animation)
local launchTime = .15
local launchSpeed = 20
local reloadSpeed = 1
local pieLifetime = 2
local pieDamage = 20

local function findCharacter(object)
	if object == game.Workspace then return nil end
	if object:FindFirstChild('Humanoid') then
		return object
	end
	return findCharacter(object.Parent)
end

local function bindOnHit(pie)
	pie.Touched:connect(function(otherPart)
		if not otherPart or not otherPart.Parent then return end
		local character = findCharacter(otherPart)
		if character and game.Players:GetPlayerFromCharacter(character) then
			character.Humanoid:TakeDamage(pieDamage)
			pie:Destroy()
		end
	end)
end

local function onActivate()
	local pie = game.ServerStorage.Models.Pie:Clone()
	bindOnHit(pie)	
	
	animationTrack:Play()
	
	wait(launchTime)
	
	tool.Pie.Transparency = 1
	pie.CFrame = tool.Pie.CFrame
	local direction = tool.Handle.CFrame.lookVector
	pie.Velocity = launchSpeed * direction
	pie.Parent = tool
	pie:SetNetworkOwner(nil)
	wait(reloadSpeed)
	tool.Pie.Transparency = 0
	
	wait(pieLifetime)
	
	pie:Destroy()
end

tool.Activated:connect(onActivate)

Like the player’s launcher, the PiBot’s launcher will bind a function to the BasePart/Touched|Touched event of their launched pies. When the pies hit something, the function checks if the hit object is part of a player character. If so, that player will take damage and the pie will be removed.