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

Ship Blasters

Ship Blasters

Remember the blasters on the ship? Now that the ship can move around, it’s time to give players the ability to defend themselves from the enemies around them by making the blasters operational.

Create the Blaster Projectiles

The blasters will need something to use as a projectile. You can use a single basic part of any shape like a cube or a sphere.

Examples of projectiles with different shapes, colors, and materials

  1. Create a new part named BlasterProjectile. Customize the part to fit your game. This example uses a bubble.
Using Different Parts

The scripts used in this tutorial might not work when using combinations of parts, unioned parts, or anything from the Toolbox.


  1. In the Properties > Behavior, uncheck CanCollide. This will prevent players from bouncing off their own projectiles.
  1. Add a VectorForce and Attachment to BlasterProjectile. Later on, you’ll add in scripts that will use these two objects.
  1. Now that the projectile is setup, move it to ReplicatedStorage so the server can find and create projectiles during the game.
Why ReplicatedStorage?

Only the server can get objects from ServerStorage while ReplicateStorage lets the client and server access objects there. Since this projectile will be used on both the client and server, it needs to use ReplicatedStorage.


Scripting the Blaster Projectiles

It’s important that players experience the least amount of delay possible between the time they press the firing key and when they see the blaster actually fire. Even slight delays can frustrate players. Since you want to eliminate lag for players, a LocalScript would make sense here.

However, remember that LocalScripts only run on the player’s device. If you use just local scripts for the blasters, blaster shots won’t be visible on the server for everyone else. A second problem with LocalScripts is that the enemies are running on the server. The blaster shots will have to interact with the server scripts so that other players in the game know what enemies have been destroyed.

Both a Local Script and a Server Script

The solution is to have the blaster fire twice. Once with a local script, and again with a server script. The local script projectile will be visible only to the player. The server script will be visible to everyone else. Since the server script is also more secure, it’ll also be used to award points.

Using Remote Events and Remote Functions

The local script and server script will need to communicate with each other to create the projectiles and trigger the destruction of enemies. Normally, local scripts and server scripts don’t have a way to talk to each other.

To do so, they have to use special objects called RemoteEvents and RemoteFunctions. RemoteEvents can send one way messages. RemoteFunctions can send and receive messages.

Set Up the Script

  1. Create a new script in ServerScriptService named BlasterRemotes.
  2. Create a variable for getting the players, and a variable for getting ReplicatedStorage, where the projectiles are stored.
--Place in ServerScriptService. Sets up remote events used by BlasterHandler (client) and BlasterLauncher(server)

local ReplicatedStorage = game:GetService(“ReplicatedStorage”)

Create the Remote Functions

RemoteFunctions will be used for sending messages to the server to create projectiles as well as to measure the ping for each player. Like other object types, RemoteFunctions can be created as new instances. The new instances should be parented to Replicated Storage since both client and server scripts can access objects stored there.

  1. Create a new RemoteFunction instance. Name it LaunchProjectile, and parent it to ReplicatedStorage.
--Place in ServerScriptService. Sets up remote events used by BlasterHandler (client) and BlasterLauncher(server)

local ReplicatedStorage = game:GetService("ReplicatedStorage")

-- RemoteFunction for when a projectile is launched
local launchProjectile = Instance.new("RemoteFunction")
launchProjectile.Name = "LaunchProjectile"
launchProjectile.Parent = ReplicatedStorage
  1. Create a new RemoteFunction named PingChecker. Parent it to Replicated Storage.
--RemoteFunction to measure the ping for all clients
local pingChecker = Instance.new("RemoteFunction")
pingChecker.Name = "Ping"
pingChecker.Parent = ReplicatedStorage

Create the Remote Events

You’ll also need two RemoteEvents. One for detecting when an enemy ship should be destroyed, and one for detecting when projectiles should be destroyed once they’ve come into contact with an enemy ship.

  1. After pingChecker, create a new RemoteEvent named DestroyEnemy and parent it to ReplicatedStorage.
-- RemoteEvent for when the client detects an enemy should be destroyed
local destroyEnemy = Instance.new("RemoteEvent")
destroyEnemy.Name = "DestroyEnemy"
destroyEnemy.Parent = ReplicatedStorage
  1. On the next line, create a new RemoteEvent named DestroyProjectile and parent it to ReplicatedStorage.
-- RemoteEvent for when the client detects a projectile should be destroyed
local destroyProjectile = Instance.new("RemoteEvent")
destroyProjectile.Name = "DestroyProjectile"
destroyProjectile.Parent = ReplicatedStorage

Creating the Local Script

The local script will create projectiles on the client-side (player device). The first variables are directly related to gameplay, controlling how fast the projectile moves and the blaster’s cooldown period before it can shoot again.

Create and Setup BlasterLaunch

  1. In StarterPlayerScripts, add a new LocalScript named BlasterLauncher.
  2. To set up the variables, copy and paste the code below into BlasterLauncher.
-- This script handles blaster events on the client-side of the game

-- How fast the projectile moves
local PROJECTILE_SPEED = 40
-- How often a projectile can be made on mouse clicks (in seconds)
local LAUNCH_COOLDOWN = 1
-- How far away the projectile is created from the front of the player
local PROJECTILE_OFFSET = 4
  1. The next set of variables gets the RemoteEvents and RemoteFunctions set up in BlasterRemotes as well as the projectile template. Copy and paste the below:
-- Variables for RemoteEvents and Functions (see the BlasterRemotes Script)
local launchProjectile = ReplicatedStorage:WaitForChild("LaunchProjectile")
local destroyProjectile = ReplicatedStorage:WaitForChild("DestroyProjectile")
local destroyEnemy = ReplicatedStorage:WaitForChild("DestroyEnemy")
local pingChecker = ReplicatedStorage:WaitForChild("Ping")

-- Variable to store the basic projectile object
local projectileTemplate = ReplicatedStorage:WaitForChild("BlasterProjectile")
-- Variable for the player object
local player = Players.LocalPlayer

local canLaunch = true

Create Projectiles on the Client

The code below will copy a projectile from ReplicatedStorage on the client and then send a message to the server to create a projectile on the server side and hide it from the player so they don’t see two projectiles. It also sets up a touched event that destroys the projectile when it hits either an enemy or a wall.

  1. In BlasterHandler, copy and paste the code below:
-- Fires when the player clicks the mouse
local function onLaunch()
	-- Only launch if the player's character exists and the blaster isn't on cooldown
	if player.Character and canLaunch then
		-- Prevents the player from launching again until the cooldown is done
		canLaunch = false
		spawn(function()
			wait(LAUNCH_COOLDOWN)
			canLaunch = true
		end)
		
		-- Create a new projectile
		local projectile = projectileTemplate:Clone()
		local playerCFrame = player.Character.PrimaryPart.CFrame
		local direction = playerCFrame.LookVector
		projectile.Position = playerCFrame.Position + direction * PROJECTILE_OFFSET
		projectile.Velocity = direction * PROJECTILE_SPEED
		
		-- Zero out gravity on the projectile so it doesn't fall through the ground
		local mass = projectile:GetMass()
		projectile.VectorForce.Force = Vector3.new(0, 1, 0) * mass * game.Workspace.Gravity
		
		-- Puts the projectile in the workspace
		projectile.Parent = game.Workspace
		
		-- RemoteFunction that tell the server to create a new projectile and send it back to us
		local serverProjectile = launchProjectile:InvokeServer(projectile.Position, projectile.Velocity)
		-- Hide the server copy of the projectile
		serverProjectile.LocalTransparencyModifier = 1
		
		-- Remote Event for the projectile
		projectile.Touched:Connect(function(other)
			-- The only collisions we care about are those with enemies and with walls
			if other.Name == "EnemyBall" or other.Name == "Wall" then
				-- Hit an enemy or wall, destroy the projectile and tell the server to destroy its copy
				projectile:Destroy()
				destroyProjectile:FireServer(serverProjectile)
				
				-- Hit an enemy, destroy it and tell the server to destroy it as well
				if other.Name == "EnemyBall" then
					destroyEnemy:FireServer(other)
					other:Destroy()
				end
			end
		end)
	end
end
  1. At the bottom of BlasterLauncher, under onLaunch, connect the launch function to the player’s control using ContextActionService:
-- Connect a function to the ping RemoteFunction. This can be empty because all
-- the server needs to hear is a response
ping.OnClientInvoke = function() end

ContextActionService:BindAction("Launch", onLaunch, false, Enum.UserInputType.MouseButton1)
Don't Test Yet

If you test now, the game won’t fire any projectiles. To fire projectiles, the BlasterLauncher script needs some more functions from the BlasterHandler script you’ll create next.



Creating Projectiles on the Server

The next script will run on the server. This script will be listening for whenever a player creates a projectile so that it can create a server-side projectile for all players to see.

  1. In ServerScriptService, create a new Script named BlasterHandler.
  2. To create the variables for the remote events, copy and paste the below code into BlasterHandler.
-- This script handles blaster projectiles on the server-side of the game

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

-- Variables for RemoteEvents and Functions (see the BlasterHandler Script)
local launchProjectile = ReplicatedStorage:WaitForChild("LaunchProjectile")
local destroyProjectile = ReplicatedStorage:WaitForChild("DestroyProjectile")
local destroyEnemy = ReplicatedStorage:WaitForChild("DestroyEnemy")
local pingChecker = ReplicatedStorage:WaitForChild("Ping")
  1. To keep track of the player’s ping, copy and paste the code below:
-- Variable to store the basic projectile object
local projectileTemplate = ReplicatedStorage.BlasterProjectile
-- Table to keep track of the ping of all connected clients
local currentPing = {}
Since players are all on different devices, they might have different pings. So all the projectiles are shown consistently to players, pingChecker will check a player's ping to figure out when to create projectiles for that player.

  1. To tell the server that a projectile launched, copy and paste the code below. This function will be used in the client script, BlasterLauncher, to make sure both scripts talk to each other when a projectile is created.
-- This is fired every time as enemy touches another part
local function onTouched(other)

end
  1. To destroy the projectile and enemies, copy and paste the functions below.
-- Called when the client detects a projectile should be destroyed
local function onDestroyProjectile(player, projectile)
	projectile:Destroy()
end

-- Called when the client detects an enemy should be destroyed
local function onDestroyEnemy(player, enemy)
	enemy:Destroy()
end
While the script just destroys an enemy now, you'll eventually add onto the onDestroyEnemy function so players can get points for destroying enemies.

  1. Copy and paste the code below to turn on the pingChecker for any players that join the game. These functions will keep track of the player’s ping so the server can create projectiles with minimal delay.
-- Called when a player joins the game. This function sets up a loop that
-- measures the ping of the player
local function onPlayerAdded(player)
	while player and wait(2) do
		local start = tick()
		pingChecker:InvokeClient(player)
		local ping = tick() - start
		currentPing[player] = ping
	end
end

-- Called when a player leaves the game. Removes their entry from the
-- ping table
local function onPlayerRemoving(player)
	currentPing[player] = nil
end
  1. To connect the functions to server events and find all the players in the game, copy and paste this code at the bottom of the script:
-- Set up event bindings
destroyProjectile.OnServerEvent:Connect(onDestroyProjectile)
destroyEnemy.OnServerEvent:Connect(onDestroyEnemy)
Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(onPlayerRemoving)
  1. Test the game and check that the player can fire projectiles.


These documents are licensed by Roblox Corporation under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Roblox, Powering Imagination, and Robux are trademarks of Roblox Corporation, registered in the United States and other countries.