Auto Aiming/Targeting System Tutorial

Hello ppl , I’d like to introduce myself—I’m a Roblox Studio programmer who has been working on this as a hobby for just over two years and exploring the DevForum for less than a year. I’ve decided to finally create my own topic in the Resources category where I plan to publish weekly with this type of tutorials if you found it helpful. My goal is to share the scripts I’ve developed over time, with the hope of providing valuable insights and learning opportunities for you all.

I will strive to explain each script in a detailed and easy-to-understand manner, ensuring compatibility with other scripts and avoiding bugs. My aim is to make these scripts useful for both experienced developers and those new to scripting. I am aware that this forum includes both highly advanced programmers and beginners, so I will aim to cover a range of tutorials to benefit everyone.

Lastly, feel free to use these scripts freely, but please avoid publishing them under your own name without making modifications. Any credit given is greatly appreciated.

Tutorial 01

Auto Aiming/Targeting System Tutorial

Preview:

Difficulty: MEDIUM
(I will do the difficulty section based on what I think it can be for the average person. It may vary or not be precise, but I will do my best for it to be accurate.)

This script creates a lock-on targeting system. It allows the player to target and focus on the nearest enemy, highlighting the target and adjusting the player’s orientation accordingly. The system also supports cycling through multiple enemies.

In this tutorial I define “Lock on” as the player’s automatic aiming towards a model (with a humanoid) with the boolean named “Lockable” set to True

Setup:

  • Create a BoolValue named “Lockable” inside the Enemy/NPC model and set it to True.

  • Create a LoalScript inside StarterPlayerScripts.

Pretty easy to be honest now let’s head to the actual content.

(All these steps will be for the coding section in this case inside the localscript I previously told you to create, so you guys can understand all the code and how those it works.)

Step 1:
Service Variables

The script uses several Roblox services to handle players, user inputs, and frame updates. These services are crucial for managing interactions and ensuring smooth operation within the game.

-- Service Variables
local player = game.Players.LocalPlayer -- Reference to the local player.
local userInputService = game:GetService("UserInputService") -- Service to handle user inputs.
local runService = game:GetService("RunService") -- Service for frame updates.
local players = game:GetService("Players") -- Service to manage all players.

Step 2:
System Variables
These variables are the core of the lock-on system. They manage the lock-on state, track the current target, and store a list of potential enemies.

-- Lock-On System Variables
local lockOnEnabled = false -- Is lock-on currently enabled?
local targetEnemy = nil -- The enemy currently targeted.
local enemiesList = {} -- List of enemies sorted by distance.
local minimumDistance = 5 -- Minimum distance to maintain when locked on.
local maxDistance = 50 -- Maximum distance to activate lock-on.
-- Highlight Variables
local highlightColor = Color3.fromRGB(255, 0, 0) -- Red color for highlighting the target.

Step 3:
Highlight Functions
These functions manage the visual highlighting of the target enemy, using Roblox’s Highlight instance to provide a visual cue to the player.

-- Function to add a highlight effect to an enemy.
local function addHighlight(enemy)
	if not enemy:FindFirstChild("Highlight") then -- Check if already highlighted.
		local highlight = Instance.new("Highlight") -- Create a new highlight.
		highlight.Name = "Highlight" -- Name the highlight for identification.
		highlight.Adornee = enemy -- Attach the highlight to the enemy.
		highlight.FillColor = Color3.new(0, 0, 0) -- Transparent fill color.
		highlight.OutlineColor = highlightColor -- Red outline color.
		highlight.Parent = enemy -- Parent the highlight to the enemy.
	end
end

-- Function to remove the highlight effect from an enemy.
local function removeHighlight(enemy)
	if enemy and enemy:FindFirstChild("Highlight") then -- Ensure highlight exists.
		local highlight = enemy:FindFirstChild("Highlight") -- Find the highlight.
		if highlight then
			highlight:Destroy() -- Remove the highlight.
		end
	end
end

Step 4:
Update and Sort Enemies
This function scans the game world to find enemies and sorts them based on their proximity to the player. Only enemies within a certain distance and that meet specific criteria are considered lockable.

-- Function to find and sort enemies by distance from the player.
local function updateEnemiesList()
	local character = player.Character -- Get the player's character.
	if not character then
		return -- Exit if character doesn't exist.
	end

	enemiesList = {} -- Reset the enemies list.
	for _, obj in pairs(workspace:GetChildren()) do -- Loop through all objects.
		if obj:IsA("Model") and obj ~= character and obj:FindFirstChildOfClass("Humanoid") then -- Check if object is a valid enemy.
			local enemyHumanoid = obj:FindFirstChildOfClass("Humanoid") -- Get humanoid part.
			local enemyPrimaryPart = obj.PrimaryPart or obj:FindFirstChild("HumanoidRootPart") -- Get primary part.
			local lockableBool = obj:FindFirstChild("Lockable") -- Check if lockable.
			if enemyHumanoid and enemyPrimaryPart and enemyHumanoid.Health > 0 and lockableBool and lockableBool:IsA("BoolValue") and lockableBool.Value == true then
				local distance = (character.PrimaryPart.Position - enemyPrimaryPart.Position).magnitude -- Calculate distance.
				if distance <= maxDistance then
					table.insert(enemiesList, {enemy = obj, distance = distance}) -- Add to list.
				end
			end
		end
	end

	table.sort(enemiesList, function(a, b) -- Sort enemies by distance.
		return a.distance < b.distance
	end)
end

Step 5:
Target Cycling
This function allows the player to cycle through available targets. It updates the highlight to reflect the current target.

-- Function to switch to the next enemy in the sorted list.
local function cycleToNextEnemy()
	if lockOnEnabled and targetEnemy then
		updateEnemiesList() -- Refresh enemies list.
		if #enemiesList > 0 then
			local currentIndex = nil
			for i, entry in ipairs(enemiesList) do -- Find current target index.
				if entry.enemy == targetEnemy then
					currentIndex = i
					break
				end
			end

			if currentIndex then
				local nextIndex = (currentIndex % #enemiesList) + 1 -- Get next enemy index.
				removeHighlight(targetEnemy) -- Remove current highlight.
				targetEnemy = enemiesList[nextIndex].enemy -- Set next enemy as target.
				addHighlight(targetEnemy) -- Highlight new target.
			end
		end
	end
end

Step 6:
Lock-On Update
This function continuously updates the player’s orientation towards the target and checks if the target is still valid. If the target is lost, it selects a new target if available.

-- Function to update the lock-on status and player orientation.
local function updateLockOn()
	local character = player.Character -- Get player's character.
	if not character then
		lockOnEnabled = false -- Disable lock-on if character doesn't exist.
		removeHighlight(targetEnemy)
		targetEnemy = nil
		return
	end

	local primaryPart = character:FindFirstChild("HumanoidRootPart") -- Get primary part.
	if primaryPart then
		character.PrimaryPart = primaryPart -- Ensure primary part is set.
	end

	if lockOnEnabled and targetEnemy then
		local enemyPrimaryPart = targetEnemy.PrimaryPart or targetEnemy:FindFirstChild("HumanoidRootPart")
		if enemyPrimaryPart and targetEnemy:FindFirstChildOfClass("Humanoid") and targetEnemy.Humanoid.Health > 0 then
			local distance = (character.PrimaryPart.Position - enemyPrimaryPart.Position).magnitude
			if distance > maxDistance then
				lockOnEnabled = false -- Disable lock-on if target is too far.
				removeHighlight(targetEnemy)
				targetEnemy = nil
			else
				local direction = (enemyPrimaryPart.Position - character.PrimaryPart.Position)
				if direction.magnitude > minimumDistance then
					direction = Vector3.new(direction.X, 0, direction.Z).unit -- Ignore Y-axis difference.
					local newCFrame = CFrame.new(character.PrimaryPart.Position, character.PrimaryPart.Position + direction)
					character:SetPrimaryPartCFrame(newCFrame) -- Face character towards target.
				end
			end
		else
			lockOnEnabled = false -- Disable if target is invalid.
			removeHighlight(targetEnemy)
			targetEnemy = nil
			updateEnemiesList() -- Refresh enemies list.
			if #enemiesList > 0 then
				targetEnemy = enemiesList[1].enemy -- Select closest enemy.
				addHighlight(targetEnemy) -- Highlight new target.
				lockOnEnabled = true
			end
		end
	end
end

Step 7:
Toggle Lock-On
This function toggles the lock-on mode. It enables lock-on for the nearest enemy or cycles through enemies if already locked on.

-- Function to toggle lock-on state and switch targets.
local function toggleLockOn()
	if lockOnEnabled then
		if #enemiesList > 0 and targetEnemy == enemiesList[#enemiesList].enemy then
			lockOnEnabled = false -- Disable lock-on if last enemy is targeted.
			removeHighlight(targetEnemy)
			targetEnemy = nil
		else
			cycleToNextEnemy() -- Switch to next enemy.
		end
	else
		lockOnEnabled = true -- Enable lock-on.
		updateEnemiesList() -- Update enemies list.
		if #enemiesList > 0 then
			targetEnemy = enemiesList[1].enemy -- Set closest enemy as target.
			addHighlight(targetEnemy) -- Highlight target.
		else
			lockOnEnabled = false -- Disable if no enemies available.
		end
	end
end

Step 8:
User Input Handling
The script listens for user inputs to toggle the lock-on system. It supports both keyboard and touch inputs.

-- Connect user inputs to toggle the lock-on functionality.
userInputService.InputBegan:Connect(function(input, gameProcessed)
	if not gameProcessed then -- Ensure input is not processed by game UI.
		if input.KeyCode == Enum.KeyCode.LeftAlt then -- Toggle lock-on with Left Alt.
			toggleLockOn()
		elseif input.UserInputType == Enum.UserInputType.Touch then -- Toggle lock-on with touch.
			local gui = player:WaitForChild("PlayerGui"):WaitForChild("ScreenGui")
			local button = gui:FindFirstChild("LockOnButton")
			if button and button:IsA("ImageButton") and button.InputBegan:Connect(function(touch)
				toggleLockOn()
			end) then
				toggleLockOn()
			end
		end
	end
end)

Step 9:
Frame Update
The script ensures the lock-on system updates each frame, keeping the player facing the target and maintaining lock-on state.

-- Continuously update the lock-on system every frame.
runService.RenderStepped:Connect(updateLockOn)

Step 10:
Character Handling
This part ensures the lock-on system resets correctly when the player’s character changes, such as during respawn.

-- Reset lock-on state when the player's character changes.
player.CharacterAdded:Connect(function()
	lockOnEnabled = false -- Disable lock-on on character change.
	targetEnemy = nil
	enemiesList = {}
end)

Step 11:
Click Play

For this to work you need to press the Left Alt key that is set by default but you can modify it.

As you have already seen this script is local and has no influence on the server since it is merely something visual and the player’s rotation and movement is something local, I tried to use remote events and different functions to make it a server script but it went from 0 to countless bugs

If you have a better idea or implementations I am free to listen for feedback and hear what you think about this type of tutorials and the way how I explain. Any comment or suggestion is accepted. I will be attentive and if I see that this really helps people to learn and improve their knowledge I will continue doing this. If it has served you and you liked it, do not hesitate to leave your <3

6 Likes