Any ideas on how to script a cannon that moves a player to a specific direction?

What do you want to achieve?
Hi! so its my first time making topic here, and im trying to make a cannon that can
fling players to a specific location.

What is the issue?
I really dont know how to achieve this effect, i tried researching but theres not really much
results…

What solutions have you tried so far?
I tried using Tween Service to tween the player’s Humanoid Root Part to the location, but it just doesnt feel like being shot out of a cannon, and the camera seems to lag out.

Heres a prototype i tried using Tween Service:

–//Variables//—

–Objects
local cannonPart = script.Parent

local explosionAttachment = cannonPart.ExplosionAttachment

local selfPrompt = cannonPart.SelfPrompt
local cubePrompt = cannonPart.CubePrompt

local cubeSpawn = workspace.CubeSpawn

–Audio
local cannonSound = cannonPart.CannonSound

–Tween Service
local tweenService = game:GetService(“TweenService”)

local tweenInfo = TweenInfo.new(
0.3,
Enum.EasingStyle.Linear,
Enum.EasingDirection.Out,
0,
false,
0
)

–Misc
local debounce = false

–//Functions//-
function tweenObject(object, tweenInfo, goal)

local tween = tweenService:Create(object, tweenInfo, goal)

tween:Play()
return tween

end

–//Main//–
selfPrompt.Triggered:Connect(function(player)

local character = player.Character
local explosion = Instance.new("Explosion")

explosion.DestroyJointRadiusPercent = 0

character.PrimaryPart.Anchored = true
character:SetPrimaryPartCFrame(cannonPart.CFrame)

wait(1)

explosion.Position = explosionAttachment.WorldPosition
explosion.Parent = workspace

cannonSound:Play()

local tween = tweenObject(character.PrimaryPart, tweenInfo, {CFrame = cubeSpawn.SpawnPart.CFrame * CFrame.new(0, 5, 0)})

tween.Completed:Wait()

character.PrimaryPart.Anchored = false

end)

You would have to calculate the velocity needed to launch the player from the cannon’s position to the other position, I suggest reading this post about projectile motion by EgoMoose, the first part explains just that: Modeling a projectile's motion

2 Likes

Ok so i looked into it, but it seems a bit complicated and im still in middle school… Can you explain
it in an easier way for me to understand or are there other alternatives?

This is mostly calculus, you will have to first understand differential calculus and then you can move on to integral calculus, which is used in the article. You have 2 choices, either you learn differential and integral calculus or you can just copy-paste the code he wrote in the article that calculates the velocity needed to launch an object from one position to another. Learning it is possible in middle school (possible, not easy), I am in 9th grade and I was capable of learning both topics, so therefore I think others are capable too, keep in mind you will have to have sufficient algebraic knowledge before you can get a hold of either topics, otherwise this is going to be extremely difficult.

2 Likes

Alright, ill try both of the methods

1 Like

So i finally made it work

–//Variables//–

–Objects
local cannonPart = script.Parent

local selfPrompt = cannonPart.SelfPrompt
local cubePrompt = cannonPart.CubePrompt

local explosionAttachment = cannonPart.ExplosionAttachment

local cubeSpawn = workspace.Spawns.CubeSpawn

–Audio
local cannonSound = cannonPart.CannonSound

–RunService
local runService = game:GetService(“RunService”)

–Misc
local t = 1

–//Main//–
selfPrompt.Triggered:Connect(function(player)

local character = player.Character

local humanoid = character.Humanoid
local rootPart = character.HumanoidRootPart

local gravity = Vector3.new(0, -workspace.Gravity, 0)
local x0 = cannonPart.Position

local v0 = (cubeSpawn.SpawnPart.Position - x0 - 0.5 * gravity * t * t) / t

local nt = 0

while (nt < t * 1) do
	rootPart.CFrame = CFrame.new(0.5 * gravity * nt * nt + v0 * nt + x0)
	nt = nt + runService.Heartbeat:Wait()
	
end

end)

But another problem arised when i tried it

The player midair moves kinda jittery, What can i do to fix it?

You are updating the CFrame of the HumanoidRootPart, which causes this type of jittery movement.
You have to set HumanoidRootPart.Velocity to the calculated v0, that would also not require using any while loop and therefore be more optimized. This replacement should work:

local character = player.Character

local humanoid = character.Humanoid
local rootPart = character.HumanoidRootPart

local gravity = Vector3.new(0, -workspace.Gravity, 0)
local x0 = cannonPart.Position

local v0 = (cubeSpawn.SpawnPart.Position - x0 - 0.5 * gravity * t * t) / t
rootPart.Velocity = v0
-->> Everything afterwards is not necessary.

I tried this before, but it just doesn’t work for some reason…

You can maybe try the CFrame method and just Lerp the character’s CFrame to smooth it out?

1 Like

image
I dont know what the number should be

You should put the product of the elapsed time with some speed, for example dt*3, the dt (elapsed time) is just so it runs on the same speed for all frame rates.

1 Like

So i should do


?

Well i did it but nothing happened, like the last gif

Is it in a while loop? Lerp just positions it a percentage from point A to point B, so if let’s say you Lerp from 3 to 5 for 0.5 (50%), you would be at 4, because you are not immediately updating the position it will make the movement smoother.

Yeah it is

while (nt < t * 1) do
	rootPart.CFrame:Lerp(CFrame.new(0.5 * gravity * nt * nt + v0 * nt + x0), nt * 3)
	nt = nt + runService.Heartbeat:Wait()

end

CFrame:Lerp() returns an interpolated CFrame, it doesn’t assign the CFrame by itself, you would have to set the CFrame to the interpolated CFrame, like this:

rootPart.CFrame = rootPart.CFrame:Lerp(CFrame.new(0.5 * gravity * nt * nt + v0 * nt + x0), nt * 3)

Ohh i see, that was pretty stupid of me…

It did work but it still has that jittery movement

Well I see, and I can’t really think of a solution, one thing that might work is to maybe clone a sphere and set the velocity to that sphere (like a cannonball), focus the player’s camera on the sphere while it’s flying in the air, and when the sphere collides with the ground you will just destroy the sphere, teleport the player to where the sphere landed and focus the camera back on the character, it could also be a cool effect.

1 Like

Hmm that could work, what if i weld the character to that object?

That could also work, this is the only possible solution I can currently think of.

1 Like

So i found a better solution pretty fast, I just had to anchor the HumanoidRootPart because i thought
physics was in the way, I took my time to polish my cannon script and found out that i can make updating the models CFrame smoother by launching it to all clients and using RunService.RenderStepped. Here is a portion of my code:

cannonLaunchEvent.OnClientEvent:Connect(function(player, model)

local humanoid = model:FindFirstChild("Humanoid")
local isACube = model:FindFirstChild("IsACube")

local rootPart = model.PrimaryPart

local gravity = Vector3.new(0, -workspace.Gravity, 0)
local x0 = rootPart.Position

local v0 = (cubeSpawnPart.Position - x0 - 0.5 * gravity * t * t) / t

local nt = 0

while (nt < t * 1) do
	
	rootPart.CFrame = rootPart.CFrame:Lerp(
		CFrame.new(0.5 * gravity * nt * nt + v0 * nt + x0), nt * 0.5
	)
	
	if humanoid then
		rootPart.CFrame = CFrame.lookAt(rootPart.Position, cubeSpawnPart.Position) * CFrame.Angles(math.rad(270), math.rad(360), 0)
		
	elseif isACube then
		rootPart.CFrame = CFrame.lookAt(rootPart.Position, cubeSpawnPart.Position)
		
	end
	
	nt = nt + runService.RenderStepped:Wait()

end

cannonLaunchEvent:FireServer(model)

end)

And heres my cannon in action:

Thank you @ComplexMetatable for your help, your the best!

3 Likes