Recently, I made a boat spawning system for my friends game and only one problem has occurred so far and that is telling the distance from land to water. I’m not really sure how to approach this issue as I don’t know if it will take a raycasting approach or simply a mathematics equation I am missing when spawning the boat.
Any help will be appreciated thank you!
What it should spawn like:
How it sometimes spawn depending on character pos:
Spawning Script:
--> Services
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CollectionService = game:GetService("CollectionService")
--> Variables
local Remotes = ReplicatedStorage.Remotes
local Assets = ReplicatedStorage.Assets
local CurrentBoats = workspace:FindFirstChild("CurrentBoats")
local CurrentCamera = workspace.CurrentCamera
--> References
--> Dependancies
--> Constructor
local BoatServer = {}
---------------------
local function ReturnBoatInstance(a): boolean
if CurrentBoats:FindFirstChild(a) and a:IsDescendantOf(CurrentBoats) then
return true else return false
end
end
-- Returns when i get a chance to import to real game :D
function GetPartsInterferingWithBoat(BoatInstance: Model)
-- not finished lol
local PartsTouching = workspace:GetPartBoundsInBox(BoatInstance.PrimaryPart.CFrame, BoatInstance.PrimaryPart.Size)
-- doesnt return parts touching when boat is clearly on land.
-- prints -> {}
print(PartsTouching)
end
function BoatServer:TeleportBoat(BoatInstance: Model, Player: Player)
local Character = Player and Player.Character
if not Character then warn(`{Player.Name} character could not be found.`) end
-- Create a temporary part that will be designed to help guide the boat to a "correct" position.
local BoatGuidance = Instance.new("Part")
BoatGuidance.Anchored = true
BoatGuidance.Size = Vector3.new(1,1,1)
-- Equation for determing character postition.
local VectorPosition = (Character:GetPivot().Position - CurrentCamera.CFrame.LookVector) - Vector3.yAxis/Character:GetPivot().Y - Vector3.zAxis/Character:GetPivot().Z - Vector3.xAxis*7
BoatGuidance.Position = VectorPosition
BoatGuidance.Parent = workspace
if BoatGuidance and not ReturnBoatInstance(BoatInstance.Name) then
local RequestBoat = Assets.Boats:FindFirstChild(BoatInstance.Name):Clone()
RequestBoat:PivotTo(BoatGuidance.CFrame * CFrame.Angles(0, math.rad(180), 0))
BoatGuidance:Destroy()
RequestBoat.Parent = CurrentBoats
end
task.defer(function()
while true do
task.wait(1/3 * 3)
GetPartsInterferingWithBoat(BoatInstance)
end
end)
end
Remotes.Shared:FindFirstChild("SummonBoat").OnServerInvoke = function(Player, Boat)
if ReturnBoatInstance(Boat.Name) then
return BoatServer:TeleportBoat(Boat, Player)
else
return BoatServer:TeleportBoat(Boat, Player)
end
end
return BoatServer
You could use .magnitude from an invisible part or anything that would determine the coastline of the island. If it is close in any axis, then you could move it in an axis determined by the magnitude that’s already there. This would make sure your boat spawns on land.
This is a mile out there with untested scripting… Attempted raycasting approach.
ServerScript
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CollectionService = game:GetService("CollectionService")
local Remotes = ReplicatedStorage.Remotes
local Assets = ReplicatedStorage.Assets
local CurrentBoats = workspace:FindFirstChild("CurrentBoats")
local CurrentCamera = workspace.CurrentCamera
local BoatServer = {}
local function ReturnBoatInstance(a): boolean
if CurrentBoats:FindFirstChild(a) and a:IsDescendantOf(CurrentBoats) then
return true
else
return false
end
end
local function GetDistanceToWater(BoatInstance: Model)
local origin = BoatInstance.PrimaryPart.Position
local direction = Vector3.new(0, -500, 0)
local rayParams = RaycastParams.new()
rayParams.FilterDescendantsInstances = {BoatInstance}
rayParams.FilterType = Enum.RaycastFilterType.Blacklist
local result = workspace:Raycast(origin, direction, rayParams)
if result then
local hitPart = result.Instance
local distance = (origin - result.Position).Magnitude
if hitPart and hitPart:IsDescendantOf(workspace:FindFirstChild("Water")) then
print("Boat is over water, distance:", distance)
else
print("Boat is over land, distance:", distance)
end
return distance
end
print("No hit detected.")
return nil
end
function BoatServer:TeleportBoat(BoatInstance: Model, Player: Player)
local Character = Player and Player.Character
if not Character then
warn(`{Player.Name} character could not be found.`)
return
end
local BoatGuidance = Instance.new("Part")
BoatGuidance.Anchored = true
BoatGuidance.Size = Vector3.new(1,1,1)
local VectorPosition = (Character:GetPivot().Position - CurrentCamera.CFrame.LookVector) - Vector3.yAxis/Character:GetPivot().Y - Vector3.zAxis/Character:GetPivot().Z - Vector3.xAxis*7
BoatGuidance.Position = VectorPosition
BoatGuidance.Parent = workspace
if BoatGuidance and not ReturnBoatInstance(BoatInstance.Name) then
local RequestBoat = Assets.Boats:FindFirstChild(BoatInstance.Name):Clone()
RequestBoat:PivotTo(BoatGuidance.CFrame * CFrame.Angles(0, math.rad(180), 0))
BoatGuidance:Destroy()
RequestBoat.Parent = CurrentBoats
end
task.defer(function()
while true do
task.wait(1)
local distance = GetDistanceToWater(BoatInstance)
if distance then
print("Distance from boat to surface:", distance)
end
end
end)
end
Remotes.Shared:FindFirstChild("SummonBoat").OnServerInvoke = function(Player, Boat)
if ReturnBoatInstance(Boat.Name) then
return BoatServer:TeleportBoat(Boat, Player)
else
return BoatServer:TeleportBoat(Boat, Player)
end
end
return BoatServer
LocalScript
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Remotes = ReplicatedStorage.Remotes
local LocalPlayer = Players.LocalPlayer
local Mouse = LocalPlayer:GetMouse()
local BoatName = "BoatModel"
local function SummonBoat()
local Character = LocalPlayer.Character
if not Character then return end
local Boat = ReplicatedStorage.Assets.Boats:FindFirstChild(BoatName)
if not Boat then return end
local Response = Remotes.Shared:FindFirstChild("SummonBoat"):InvokeServer(Boat)
end
Mouse.Button1Down:Connect(SummonBoat)