Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- // CLIENT-SIDE //
- --[[
- ==========================================================
- DRONE CONTROL & SHOOTING SYSTEM - CLIENT SIDE
- ==========================================================
- PLACE THIS LOCALSCRIPT HERE:
- StarterPlayer > StarterPlayerScripts
- ==========================================================
- PRE SETUP GUIDE:
- ==========================================================
- 1. BUILDING THE DRONE (in Workspace):
- - Create a Model called "Drone".
- - Inside it, add the following Parts:
- 1. "Main" → Main body of drone. (Set PrimaryPart to this!)
- 2. "Camera" → Part for player camera positioning.
- 3. "Propeller" → Will spin when drone is active.
- 4. "CanonTip" → Bullets spawn from here.
- 2. REPLICATEDSTORAGE SETUP:
- Inside ReplicatedStorage:
- - Folder "Remotes":
- RemoteEvent "StartDrone"
- RemoteEvent "Shoot"
- - Folder "Sounds":
- Sound "Shoot"
- Sound "Reload"
- - Folder "Clones":
- Part "Bullet" (Small, CanCollide=true, Anchored=false, Neon works well)
- 3. UI SETUP (StarterGui):
- - ScreenGui "DroneUI"
- Inside it:
- TextLabel "TextLabel" (crosshair display - optional)
- TextLabel "Ammo" (ammo counter)
- 4. CONTROLS:
- W/S → Forward / Backward
- A/D → Strafe left / right
- Space → Ascend
- LeftShift → Descend
- Left Mouse Button → Shoot
- ==========================================================
- ]]
- --// SERVICES
- local Players = game:GetService("Players")
- local ReplicatedStorage = game:GetService("ReplicatedStorage")
- local UserInputService = game:GetService("UserInputService")
- local RunService = game:GetService("RunService")
- local TweenService = game:GetService("TweenService")
- local Debris = game:GetService("Debris")
- local Workspace = game:GetService("Workspace")
- --// PLAYER REFERENCES
- local player = Players.LocalPlayer -- Get the current player running this script
- local character = player.Character or player.CharacterAdded:Wait() -- Player's character model in the world
- local humanoid = character:WaitForChild("Humanoid") -- Controls character movement and properties
- --// REMOTE EVENTS
- local remotes = ReplicatedStorage:WaitForChild("Remotes") -- Folder containing all remote events
- local startDrone = remotes:WaitForChild("StartDrone") -- Event to begin drone control mode
- local shootRemote = remotes:WaitForChild("Shoot") -- Event to sync shooting with server
- --// AUDIO ASSETS
- local sounds = ReplicatedStorage:WaitForChild("Sounds") -- Folder containing all sound effects
- local shootSFX = sounds:WaitForChild("Shoot") -- Sound played when firing bullets
- local reloadSFX = sounds:WaitForChild("Reload") -- Sound played during ammo reload
- --// OBJECT TEMPLATES
- local clones = ReplicatedStorage:WaitForChild("Clones") -- Folder containing template objects
- local bulletTemplate = clones:WaitForChild("Bullet") -- Template bullet part to clone when shooting
- --// DRONE MODEL PARTS
- local drone = Workspace:WaitForChild("Drone") -- Main drone model in the world
- local cameraPart = drone:WaitForChild("Camera") -- Part that camera follows for first-person view
- local mainPart = drone:WaitForChild("Main") -- Main body part (should be set as PrimaryPart)
- local propeller = drone:WaitForChild("Propeller") -- Part that spins to show drone activity
- local canonPart = drone:WaitForChild("CanonTip") -- Point where bullets spawn from
- --// USER INTERFACE
- local playerGui = player:WaitForChild("PlayerGui") -- Player's GUI container
- local droneUi = playerGui:WaitForChild("DroneUI") -- Main drone control interface
- local crosshair = droneUi:WaitForChild("TextLabel") -- Crosshair display (optional visual aid)
- local ammoLabel = droneUi:WaitForChild("Ammo") -- Shows current ammo count and reload status
- --// CAMERA SYSTEM
- local camera = Workspace.CurrentCamera -- Main game camera
- local originalFOV = camera.FieldOfView -- Store original field of view to restore later
- local originalMouseBehavior = UserInputService.MouseBehavior -- Store original mouse behavior for restoration
- --// CONFIGURATION TABLE - Easily adjustable settings
- local CONFIG = {
- -- Movement physics and feel
- Movement = {
- MinAcceleration = 1, -- Base acceleration when no input
- MaxForwardAcceleration = 5, -- Maximum forward speed multiplier
- MaxSidewardAcceleration = 2.5, -- Maximum side movement speed
- AccelerationIncrease = 1.025, -- How quickly acceleration builds up (per frame)
- AccelerationDecrease = 0.965, -- How quickly acceleration decays when not accelerating
- VerticalSpeed = 0.25, -- Up/down movement speed (Space/Shift keys)
- HorizontalSpeed = 0.5, -- Left/right strafe speed (A/D keys)
- BackwardSpeed = 0.25 -- Backward movement speed (S key)
- },
- -- Shooting mechanics and bullet behavior
- Shooting = {
- BulletSpeed = 500, -- How fast bullets travel (studs per second)
- BulletLifetime = 5, -- How long bullets exist before auto-destruction
- MaxAmmo = 30, -- Maximum ammunition capacity
- ReloadTime = 2 -- Time in seconds to reload (handled server-side)
- },
- -- Visual effects and feedback
- Visuals = {
- PropellerSpinSpeed = 5, -- Degrees per frame the propeller rotates
- FOVIncreaseRate = 0.2, -- How fast FOV increases when accelerating
- FOVReturnSpeed = 0.15 -- How fast FOV returns to normal when decelerating
- }
- }
- --// INTERNAL STATE VARIABLES - Runtime tracking
- local acceleration = CONFIG.Movement.MinAcceleration -- Current forward acceleration multiplier
- local isReloading = false -- Track if player is currently reloading
- local connections = {} -- Store all event connections for cleanup
- --// UTILITY FUNCTIONS
- --// Safely disconnect all active event connections to prevent memory leaks
- local function cleanupConnections()
- -- Loop through all stored connections
- for _, conn in ipairs(connections) do
- -- Check if connection exists and has Disconnect method
- if conn and conn.Disconnect then
- conn:Disconnect() -- Safely disconnect the event
- end
- end
- -- Clear the connections table for reuse
- table.clear(connections)
- end
- --// Update the ammo counter display based on current state
- local function updateAmmoLabel()
- if isReloading then
- -- Show reload status instead of numbers
- ammoLabel.Text = "Reloading..."
- else
- -- Show current ammo / maximum ammo format
- ammoLabel.Text = string.format("%d/%d", player:GetAttribute("Ammo"), CONFIG.Shooting.MaxAmmo)
- end
- end
- --// BULLET SYSTEM - Projectile creation and physics
- --// Create a physical bullet and launch it toward target
- local function createBullet(origin, target)
- -- Clone the bullet template to create a new projectile
- local bullet = bulletTemplate:Clone()
- bullet.Parent = Workspace -- Place bullet in the world
- bullet.Position = origin -- Start bullet at firing position
- -- Create physics body to control bullet movement
- local bodyVelocity = Instance.new("BodyVelocity")
- bodyVelocity.MaxForce = Vector3.new(4000, 4000, 4000) -- Maximum force in all directions
- -- Calculate direction and apply speed
- bodyVelocity.Velocity = (target - origin).Unit * CONFIG.Shooting.BulletSpeed
- bodyVelocity.Parent = bullet -- Attach physics to bullet
- -- Track if bullet has been destroyed to prevent double-destruction
- local destroyed = false
- local touchConn
- -- Handle bullet collision with objects
- touchConn = bullet.Touched:Connect(function(hit)
- -- Ignore if already destroyed or hitting the drone itself
- if destroyed or hit:IsDescendantOf(drone) then return end
- -- Only collide with actual parts (not accessories, etc.)
- if hit:IsA("BasePart") then
- destroyed = true -- Mark as destroyed
- bullet:Destroy() -- Remove from world
- end
- end)
- -- Clean up connection if bullet is removed from world
- bullet.AncestryChanged:Connect(function(_, parent)
- if not parent then -- Bullet was removed from workspace
- destroyed = true
- if touchConn then touchConn:Disconnect() end
- end
- end)
- -- Auto-destroy bullet after specified lifetime to prevent lag
- Debris:AddItem(bullet, CONFIG.Shooting.BulletLifetime)
- end
- --// MOVEMENT SYSTEM - Drone position control
- --// Handle all drone movement based on current key presses
- local function moveDrone()
- -- VERTICAL MOVEMENT - Up and down controls
- if UserInputService:IsKeyDown(Enum.KeyCode.Space) then
- -- Move drone upward by vertical speed amount
- drone:SetPrimaryPartCFrame(drone.PrimaryPart.CFrame * CFrame.new(0, CONFIG.Movement.VerticalSpeed, 0))
- end
- if UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) then
- -- Move drone downward by vertical speed amount
- drone:SetPrimaryPartCFrame(drone.PrimaryPart.CFrame * CFrame.new(0, -CONFIG.Movement.VerticalSpeed, 0))
- end
- -- HORIZONTAL MOVEMENT - Left and right strafe
- if UserInputService:IsKeyDown(Enum.KeyCode.A) then
- -- Strafe left relative to drone's current facing direction
- drone:SetPrimaryPartCFrame(drone.PrimaryPart.CFrame * CFrame.new(-CONFIG.Movement.HorizontalSpeed, 0, 0))
- end
- if UserInputService:IsKeyDown(Enum.KeyCode.D) then
- -- Strafe right relative to drone's current facing direction
- drone:SetPrimaryPartCFrame(drone.PrimaryPart.CFrame * CFrame.new(CONFIG.Movement.HorizontalSpeed, 0, 0))
- end
- -- FORWARD MOVEMENT - Accelerating forward motion with visual feedback
- if UserInputService:IsKeyDown(Enum.KeyCode.W) then
- -- Build up acceleration if not at maximum
- if acceleration < CONFIG.Movement.MaxForwardAcceleration then
- acceleration *= CONFIG.Movement.AccelerationIncrease -- Increase acceleration
- camera.FieldOfView += CONFIG.Visuals.FOVIncreaseRate -- Widen FOV for speed effect
- end
- -- Move forward with current acceleration multiplier
- drone:SetPrimaryPartCFrame(drone.PrimaryPart.CFrame * CFrame.new(0, 0, -CONFIG.Movement.BackwardSpeed * acceleration))
- else
- -- DECELERATION - Slow down when not pressing forward
- if acceleration > CONFIG.Movement.MinAcceleration then
- acceleration *= CONFIG.Movement.AccelerationDecrease -- Reduce acceleration
- -- Return FOV to normal, but don't go below original
- camera.FieldOfView = math.max(originalFOV, camera.FieldOfView - CONFIG.Visuals.FOVReturnSpeed)
- end
- end
- -- BACKWARD MOVEMENT - Simple reverse motion
- if UserInputService:IsKeyDown(Enum.KeyCode.S) then
- -- Move backward at constant speed (no acceleration)
- drone:SetPrimaryPartCFrame(drone.PrimaryPart.CFrame * CFrame.new(0, 0, CONFIG.Movement.BackwardSpeed))
- end
- end
- --// SHOOTING SYSTEM - Weapon firing mechanics
- --// Handle shooting logic with raycasting for accuracy
- local function handleShooting()
- -- Prevent shooting if reloading or out of ammo
- if isReloading or player:GetAttribute("Ammo") <= 0 then
- return -- Exit early, no shooting allowed
- end
- -- RAYCASTING SETUP - Determine where player is aiming
- local cameraRayOrigin = cameraPart.CFrame.Position -- Start from camera position
- local rayOrigin = canonPart.Position -- Bullets spawn from canon tip
- local rayDirection = camera.CFrame.LookVector * 1000 -- Ray direction based on camera look
- -- FIRST RAYCAST - Where is the crosshair pointing?
- local crosshairHit = Workspace:Raycast(cameraRayOrigin, rayDirection)
- if crosshairHit then
- -- SECOND RAYCAST - Can we actually hit that point from the gun?
- local targetPosition = crosshairHit.Position
- local bulletDirection = (targetPosition - rayOrigin).Unit * 1000 -- Direction from gun to target
- local aimedHit = Workspace:Raycast(rayOrigin, bulletDirection)
- if aimedHit then
- -- SUCCESSFUL SHOT SETUP
- local finalTarget = aimedHit.Position
- createBullet(rayOrigin, finalTarget) -- Create the visual bullet
- shootSFX:Play() -- Play shooting sound effect
- -- Notify server about the shot for other players to see
- shootRemote:FireServer(rayOrigin, finalTarget)
- end
- end
- end
- --// MAIN UPDATE LOOP - Per-frame processing
- --// Called every frame while drone is active - handles real-time updates
- local function onRenderStep()
- -- DRONE ORIENTATION - Make drone face the camera's look direction
- local currentPos = drone.PrimaryPart.Position
- local lookCF = CFrame.new(currentPos, currentPos + camera.CFrame.LookVector)
- drone:SetPrimaryPartCFrame(lookCF)
- -- PROPELLER ANIMATION - Spin the propeller for visual effect
- propeller.CFrame = propeller.CFrame * CFrame.Angles(0, math.rad(CONFIG.Visuals.PropellerSpinSpeed), 0)
- -- MOUSE CONTROL - Keep mouse locked to center for FPS-style control
- UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
- -- MOVEMENT UPDATE - Process all movement input
- moveDrone()
- end
- --// DRONE CONTROL INITIALIZATION
- --// Set up drone control mode - called when drone starts
- local function startDroneControl()
- -- UI SETUP - Show drone interface
- droneUi.Enabled = true -- Make drone UI visible
- UserInputService.MouseIconEnabled = false -- Hide mouse cursor for immersion
- -- CAMERA SETUP - Switch to drone's perspective
- camera.CameraSubject = cameraPart -- Camera follows drone's camera part
- -- CHARACTER LOCKDOWN - Prevent normal character movement
- humanoid.WalkSpeed = 0 -- Can't walk while controlling drone
- humanoid.JumpHeight = 0 -- Can't jump while controlling drone
- -- EVENT CONNECTIONS - Set up all input handlers
- -- Connect the main update loop to run every frame
- table.insert(connections, RunService.RenderStepped:Connect(onRenderStep))
- -- Connect input handling for shooting
- table.insert(connections, UserInputService.InputBegan:Connect(function(input, gpe)
- if gpe then return end -- Ignore if GUI processed the input
- -- Handle left mouse button for shooting
- if input.UserInputType == Enum.UserInputType.MouseButton1 then
- handleShooting()
- end
- end))
- end
- --// EVENT LISTENERS - React to game state changes
- -- Listen for changes to player's ammo attribute and update display
- table.insert(connections, player:GetAttributeChangedSignal("Ammo"):Connect(updateAmmoLabel))
- -- Listen for changes to player's reloading status
- table.insert(connections, player:GetAttributeChangedSignal("Reloading"):Connect(function()
- isReloading = player:GetAttribute("Reloading") -- Update local reload state
- if isReloading then
- reloadSFX:Play() -- Play reload sound when starting to reload
- end
- updateAmmoLabel() -- Update the ammo display
- end))
- --// MAIN ENTRY POINT - Start the system
- -- Listen for the server's signal to start drone control
- table.insert(connections, startDrone.OnClientEvent:Connect(startDroneControl))
- -- // SERVER-SIDE //
- --[[
- ==========================================================
- DRONE SHOOTING SYSTEM - SERVER SIDE
- ==========================================================
- PLACE THIS SCRIPT HERE:
- ServerScriptService
- PURPOSE:
- - Keeps ammo count secure on the server
- - Handles reloads
- - Detects hits & applies damage
- ==========================================================
- ]]
- local Players = game:GetService("Players")
- local ReplicatedStorage = game:GetService("ReplicatedStorage")
- --// Remote events
- local remotes = ReplicatedStorage:WaitForChild("Remotes")
- local shoot = remotes:WaitForChild("Shoot")
- --// Assign ammo attributes to new players
- Players.PlayerAdded:Connect(function(player)
- player:SetAttribute("Ammo", 30)
- player:SetAttribute("Reloading", false)
- end)
- --// Reload function
- local function reload(player)
- player:SetAttribute("Reloading", true)
- task.spawn(function()
- task.wait(2) -- Reload duration
- player:SetAttribute("Ammo", 30)
- player:SetAttribute("Reloading", false)
- end)
- end
- --// Handle shooting from client
- shoot.OnServerEvent:Connect(function(player, rayOrigin, targetPosition)
- -- Ignore if reloading
- if player:GetAttribute("Reloading") then return end
- local ammo = player:GetAttribute("Ammo")
- -- Deduct ammo
- if ammo > 0 then
- player:SetAttribute("Ammo", ammo - 1)
- if ammo - 1 <= 0 then
- -- Reload when empty
- reload(player)
- end
- else
- reload(player)
- return
- end
- -- Raycast for hit detection
- local bulletDirection = (targetPosition - rayOrigin).Unit * 10000
- local result = workspace:Raycast(rayOrigin, bulletDirection)
- if result then
- local hitPart = result.Instance
- local character = hitPart.Parent
- local humanoid = character:FindFirstChild("Humanoid")
- -- Check if hit is a humanoid
- if humanoid and character ~= player.Character then
- humanoid.Health -= 10 -- Damage per bullet
- end
- end
- end)
Advertisement
Add Comment
Please, Sign In to add comment