Vertical Velocity By Camera Issue (soccer game)

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!

I am making a soccer game, and I want the shots to be completely aimable. I managed to add a basic straight shot, but it doesn’t take in consideration where your camera is looking vertically.
Unlike in games like Azure Latch where there’s a slight amount of vertical aiming, I want to make it huge. Like for example if you look straight up the ball shoot straight up.

  1. What is the issue? Include screenshots / videos if possible!

I have read about UpVector and can’t seem to get it to work. Either the ball isn’t shot at all, it is shot straight up, or it is shot backwards and it glitches depending on how I change the scripts.

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

I have searched for solutions and searched up how LookVector works. I tried swapping from multiplying look vector by up vector, multiplying vector 3 y by up vector and all of that by look vector and so on. All my solutions do not work.

CURRENT LOCAL SCRIPT:

local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local remoteFunctions = ReplicatedStorage:WaitForChild("RemoteFunctions")
local shootFunction = remoteFunctions:WaitForChild("ShootFunction")


local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local remoteFunctions = ReplicatedStorage:WaitForChild("RemoteFunctions")
local shootFunction = remoteFunctions:WaitForChild("ShootFunction")

UserInputService.InputBegan:Connect(function(input, gameProcessed)
	
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		if not player.Character:FindFirstChild("Ball") then
			return
		end
		local chargeTime = 0
		repeat
			task.wait(0.1)
			chargeTime += 1
			
		until chargeTime >= 15 or UserInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton1) == false
		local lookVector = workspace.CurrentCamera.CFrame.LookVector
		local upVector = workspace.CurrentCamera.CFrame.UpVector
		shootFunction:InvokeServer(chargeTime, lookVector, upVector)
	end
end)

CURRENT SERVER SCRIPT:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunctions = ReplicatedStorage:WaitForChild("RemoteFunctions")
local shootFunction = remoteFunctions:WaitForChild("ShootFunction")
local ball = workspace:WaitForChild("Ball")
ball.Touched:Connect(function(hit)
	if ball.InPossession.Value == false and hit.Parent:FindFirstChildOfClass("Humanoid") then
		ball.CanCollide = false
		ball.InPossession.Value = true
		ball.PossessorName.Value = hit.Parent.Name
		local motor6D = Instance.new("Motor6D")
		motor6D.Part0 = hit.Parent:WaitForChild("HumanoidRootPart")
		motor6D.Part1 = workspace.Ball
		motor6D.Parent = hit.Parent.HumanoidRootPart
		motor6D.Name = "Ball"
		ball.Parent = hit.Parent
	end
end)

	

shootFunction.OnServerInvoke = function(player, chargeTime, lookVector, upVector)
	print(upVector)
	local character = workspace[ball.PossessorName.Value]
	character:WaitForChild("HumanoidRootPart").Ball:Destroy()
	local bodyVelocity = Instance.new("BodyVelocity")
	bodyVelocity.Parent = ball
	bodyVelocity.P = math.huge
	bodyVelocity.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
	bodyVelocity.Velocity = lookVector * Vector3.new(0, 5 * upVector, 75)
	ball.CanCollide = false
	
	--[[for i = 1, chargeTime do
		if i <= chargeTime / 2 then
			bodyVelocity.Velocity = bodyVelocity.Velocity + Vector3.new(0, 5, 0)
		else
			bodyVelocity.Velocity = bodyVelocity.Velocity - Vector3.new(0, 5, 0)
		end
		if i == 2 then
			ball.CanCollide = true
		end
		task.wait(0.1)
	end]]
	task.wait(0.1)
	ball.CanCollide = true
	task.wait(3)

bodyVelocity:Destroy()
ball.InPossession.Value = false
ball.Parent = workspace
end
while task.wait() do
	ball.AssemblyLinearVelocity *= 0.99
	ball.AssemblyAngularVelocity *= 0.99
end

(part of scripts removed, these aren’t final as I’m trying to figure this out.)

After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!

-- This is an example Lua code block

Please do not ask people to write entire scripts or design entire systems for you. If you can’t answer the three questions above, you should probably pick a different category.

Hello @ParadoxumDoge

Why the ball keeps going the wrong way

  • The CFrame.LookVector already contains the whole aim direction, including its Y component. If your camera is tilted at 40 degrees up, LookVector not equal to (0, 0.64, 0.77) if you look straight up it comes (0, 1, 0)

  • CFrame.UpVector is not “how much I’m aiming up” - it’s the axis that points out of the top of the camera, so mixing it with LookVector will always break the direction

  • BodyVelocity.Velocity = lookVector * Vector3.new(0, 5 * upVector, 75) multiplies a vector by another vector (and tries to multiply a vector by a number in the middle) that gives nonsense values or a runtime error.

Client

local UIS = game:GetService("UserInputService")
local RemoteShoot = game.ReplicatedStorage.RemoteFunctions:WaitForChild("ShootFunction")
local player = game.Players.LocalPlayer
local camera = workspace.CurrentCamera

local CHARGE_LIMIT = 1.5       -- seconds until max charge
local CHARGE_STRENGTH = 60        -- studs/s added per second held

UIS.InputBegan:Connect(function(inp,gp)
	if gp or inp.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
	if not player.Character or not player.Character:FindFirstChild("Ball") then return end

	local start = tick()

	-- fire when the button is released
	local endedConn
	endedConn = UIS.InputEnded:Connect(function(endInp)
		if endInp.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
		endedConn:Disconnect()

		local charge = math.clamp(tick() - start, 0, CHARGE_LIMIT)          -- 0-1.5 s
		local dir = camera.CFrame.LookVector                             -- already includes pitch
		RemoteShoot:InvokeServer(charge, dir)
	end)
end)

Server

local RemoteShoot = game.ReplicatedStorage.RemoteFunctions:WaitForChild("ShootFunction")
local BALL = workspace:WaitForChild("Ball")

local VERT_BOOST = 2         -- multiply Y component (bigger = “huge” vertical aiming)
local BASE_SPEED = 80        -- studs/s at zero charge
local CHARGE_SPEED = 40        -- extra studs/s per second held

RemoteShoot.OnServerInvoke = function(player, charge, camDir)
	-- camDir is already a Vector3 the client gave us
	local possessorName = BALL:FindFirstChild("PossessorName") and BALL.PossessorName.Value
	if not possessorName or player.Name ~= possessorName then return end   -- basic security

	-- detach the ball
	local char = player.Character
	if char and char:FindFirstChild("HumanoidRootPart") and char.HumanoidRootPart:FindFirstChild("Ball") then
		char.HumanoidRootPart.Ball:Destroy()
	end
	BALL.Parent = workspace
	BALL.CanCollide = false      -- re-enable a bit later

	-- exaggerate the vertical component, then renormalise so speed stays consistent
	local dir = Vector3.new(camDir.X, camDir.Y * VERT_BOOST, camDir.Z).Unit
	local speed = BASE_SPEED + charge * CHARGE_SPEED

	BALL.AssemblyLinearVelocity = dir * speed    -- modern, lightweight way
	task.delay(0.1, function() BALL.CanCollide = true end)
	BALL.InPossession.Value = false
end

Hope this helps with your problem!

bout to try it out ty and yeah my coding is very efficient ik fr fr ik the reason in this case it goes the wrong direction but prior it would go in the floor and clip backwards

yo tysm it works, ty
edit: collision groups bugged it time to lock in

1 Like

yeah this is weird all i did was added player/ball collision groups and now the ball shoots straight backwards a preset amount