Why is this Mortar not working as expected?

I am trying to make a mortar for my game. Unlike most weapons that shoot projectiles, it does not shoot parts in a straight line. Since the projectiles are affected by gravity the mortar has to adjust its trajectory so that it will hit the player. For example, if a player is standing right by the Mortar it will have to aim almost straight up to hit the player. Here is how its intended to work:

When a player enters within Mortar’s range it rotates the base to face the player and then rotates the barrel to adjust for gravity and distance.

Here is a picture to help visualize the intended motion and the model of the Mortar.

Mortar - Roblox ,-- MODEL

1 Like

We need more context in order to help you out. What code is responsible for the mortar’s functionalities? I know it is embedded in the model, but adding it to the topic makes it easier for other people to understand the situation.

2 Likes

Ok, good idea! Here is all the code used for the Mortar:

--Made by Its4Realzies
local sP = script.Parent
--Attack settings
local projectileSpeed = 75
local attackDamage = script.AttackDamage.Value
local attackRate = script.AttackRate.Value
local attackRange = script.AttackRange.Value

--For rotating Mortar
local base = sP.RotateBase
local baseCFrame = base.CFrame
--For angleing Mortar
local heart = sP.Heart
local heartCFrame = heart.CFrame

--Aims the Mortar toward player
local function aimMortar(player)
	print("Aiming Mortar")
	local playerCharacter = player.Character
	
	--Faces mortar towards player
	local charTorso = player.Character.Torso
	base.Gyro.CFrame = CFrame.new(base.Position, charTorso.Position)
	
	--Angles mortar towards player
	local origin = heart.Position
	local targetPosition = charTorso.Position
	local gravity = Vector3.new(0, -game.Workspace.Gravity, 0)

	local velocity = (targetPosition - origin - 0.5 * gravity) 
	heart.Gyro.CFrame = CFrame.new(heart.Position, heart.Orientation + velocity)
end

--Finds nearest Player
local function findNearestPlayer()
	local lastDistance = math.huge
	local toReturn
	for _,plr in ipairs(game.Players:GetChildren()) do
		local plrCharacter = plr.Character
		if (plrCharacter ~= nil) and plrCharacter.PrimaryPart ~= nil then
			local currentDistance = (plrCharacter:GetPrimaryPartCFrame().p - heart.Position).Magnitude
			if  currentDistance < lastDistance then
				toReturn = plr
				lastDistance = currentDistance
			end
		end
	end
	return toReturn, lastDistance
end

--Shoots projectile where the barrel is facing
local function fireProjectile()
	local newProjectile = Instance.new('Part', sP)
	newProjectile.Name = "Projectile"
	newProjectile.TopSurface = 0
	newProjectile.BottomSurface = 0
	newProjectile.Color = Color3.fromRGB(140, 255, 148)
	newProjectile.CanCollide = false
	newProjectile.Reflectance = -0.5
	newProjectile.Shape = 'Ball'
	newProjectile.Size = Vector3.new(3, 3, 3)
	newProjectile.Position = heart.Position
	newProjectile.Orientation = heart.Orientation
	newProjectile.Velocity = newProjectile.CFrame.lookVector * projectileSpeed
	
	newProjectile.Touched:Connect(function(part)
		if part ~= heart and part ~= sP.Stripe and part ~= sP.Barrel and part ~= sP.HingedSupport then
			local particleHolder = Instance.new("Attachment", sP.AnchorBase)
			particleHolder.Name = "ParticleHolder"
			particleHolder.Position = newProjectile.Position
			particleHolder.WorldPosition = newProjectile.Position
			local newParticle = script.SplashEffect:Clone()
			newParticle.Parent = particleHolder
			newProjectile:Destroy()

			wait(1)

			newParticle.Enabled = false

			wait(3)

			newParticle:Destroy()
			particleHolder:Destroy()
		end
	end)
end


local RunService = game:GetService("RunService")
RunService.Heartbeat:Connect(function()
	
	local targetingPlayer, playerDistance = findNearestPlayer()
	if targetingPlayer ~= nil and playerDistance < attackRange then
		aimMortar(targetingPlayer)
	else
		base.Gyro.CFrame = baseCFrame
		heart.Gyro.CFrame = heartCFrame
	end
	
end)

while wait(1) do
	local targetingPlayer, playerDistance = findNearestPlayer()
	if targetingPlayer ~= nil and playerDistance < attackRange then
		fireProjectile()
	end
end





Edit for some reason the mortar lags a bit, but that could just be my low-end computer. There are no errors, It just the mortar is not angling the barrel in the way demonstrated in the photo above.

2 Likes

I would try to avoid BasePart.Orientation, cause it sometimes acts weird. You can set both the position and rotation by setting the CFrame.

This is the problem, cause you are firing it in the wrong direction. The rest of the code looks fine to me.

Ok, how do I fix this? I cant do heart.Oreintation because of its body gyro

I think this should work:

-- Setting it to heart.Gyro.CFrame would look unrealistic
newProjectile.CFrame = heart.CFrame

Now it should fire the projectile perpendicular to ‘heart’. If it does not solve your problem, then local function aimMortar(player) is responsible for that.

1 Like

Hearts.CFrame is always the same due to gyro though right?

Its still missing the player in most cases why is that??? Edit: what’s wrong with my math?

Well, not exactly, cause this is what your mortar is actually aiming at, rather than its aiming goal.

1 Like

Your mortar is not aiming correctly.

1 Like

Do you know how I could get the effect I intended?

It involves quite a bit of maths, so will just give you an article on this topic: Projectile Motion Tutorial for Arrows and Missiles in Unity3D – Volkan Ilbeyli – Graphics Programmer.

The idea is that you need to find the initial velocity vector for your projectile. You can then aim your cannon in the direction of that vector and finally fire the projectile with the magnitude of that vector.

1 Like

How do I get the launch angle???

I am having problems applying just about everything. Here is what I have so far:
–Start and end of projectiles path
local startPosition = sP.Position
local targetPosition = target.Position
–We don’t care about the y-component of the initial and target position.
local projectileXZPos = Vector3.new(heart.Position.X, nil, heart.Position.Z)
local targetXZPos = Vector3.new(targetPosition.Position.X, nil, targetPosition.Position.Z)

--Shorthands for the formula
local R = Vector3.Distance(projectileXZPos, targetXZPos)
local G = workspace.Gravity
local tanAlpha = math.Tan(LaunchAngle * math.Deg2Rad)
local H = targetPosition.Y - startPosition.Y

--Calculate the local space components of the velocity 
--Required to land the projectile on the target object 
local Vz = Mathf.Sqrt(G * R * R / (2.0f * (H - R * tanAlpha)) );
local Vy = tanAlpha * Vz;

Can you help me get the velocity?

Alright, so what you are trying to solve is a parabolic arc. @EgoMoose made a great article covering parabolic arcs and it should help you. You should check it out, as it’s a great tutorial.

A less complicated way of doing this is rotating the motor so it’a facing the player, calculating the magnitude of the motor and the player. (For example if the player X and Z axis is negative - then rotate the motor a little backwards by * the magnitude. You will have to play around to see which orientation is most accurate) Then add a VectorForce straight up into the sky and destroying the VectorForce a second later.

I am aware. In fact, I have posted that exact article as a source I was trying to use in my previous post trying to fix this Mortar. How do I angle a projectile to hit a player I don’t how to apply that logic to the angle of the Mortar.

You don’t. You apply it to the projectile and then get the angle using the projectile position and direction.

1 Like

Could you give me example of code (I suck at math)

Yes. I am currently working on it but I am having trouble setting up the parabolic equation. However, I should have something done by today.

1 Like