My enemy NPC cannot aim well

You can make that your thrower predicts where the player will go to able to throw the ball right.

You can use Humanoid.MoveDirection.Magnitude * Humanoid.RootPart.Velocity to get position to throw.

2 Likes

So Humanoid.MoveDirection/HumanoidRootPart.Velocity can automatically predict the player position?

3 Likes

You should have your NPC consider the movement of the target player and aim where the player would be based on how they are moving and the speed of your projectile.

Something like this function that takes startPos(position of your NPC), velocity (how fast the superball moves), and targetPlayer (Player they should aim at) and gives you the vector the NPC should aim to hit the player if they keep moving in the same direction. It’s still dodgeable but it should make it so the player has to change directions to dodge.
*Edit, this would give the initial vector for a flat firing projectile.

function getAimingVectorForTarget(startPos, velocity, targetPlayer)
	local enemySpeed = targetPlayer.Humanoid.WalkSpeed
	local enemyDir = targetPlayer.Humanmoid.CFrame.MoveDirection
	
	local enemyPos = targetPlayer.HumanoidRootPart.Position
	local enemyX = enemyPos.X
	local enemyZ = enemyPos.Z
	local enemyY = enemyPos.Y
	
	local a = (enemySpeed * enemyDir.X * 2) + 
		(enemySpeed * enemyDir.Z * 2) -
		(velocity^2)
	local b = 2 * ((enemyX * enemySpeed * enemyDir.X) + (enemyY * enemySpeed * enemyDir.Y) +(enemyZ * enemySpeed * enemyDir.Z) -
		(startPos.X * enemySpeed * enemyDir.X) - (startPos.Y * enemySpeed * enemyDir.Y) - (startPos.Z * enemySpeed * enemyDir.Z))
	local c = (enemyX^2) + (enemyY^2) + (enemyZ^2) + (startPos.X^2) + (startPos.Y^2) + (startPos.Z^2) - 
		(2 * startPos.X * enemyX) - (2 * startPos.Y * enemyY) - (2 * startPos.Z * enemyZ)
	
	local t1 = (-b + math.sqrt((b^2) - (4 * a * c))) / (2 * a)
	local t2 = (-b - math.sqrt((b^2) - (4 * a * c))) / (2 * a)
	
	local t = t1 > 0 and t1 < t2 and t1 or t2
	
	local v = Vector3.new(
		(enemyX - startPos.X + (t * enemySpeed * enemyDir.X)) / (t * velocity),
		(enemyY - startPos.Y + (t * enemySpeed * enemyDir.Y)) / (t * velocity),
		(enemyZ - startPos.Z + (t * enemySpeed * enemyDir.Z)) / (t * velocity)
	)
	
	return v
	
end
1 Like

What do I need to add to Velocity on the function? It gives me this error:
Workspace.Npc.CannonScript:14: attempt to perform arithmetic (pow) on Vector3 and number

When I add EnemyPos.Character.HumanoidRootPart.Velocity to Velocity section.

2 Likes

Velocity is would just be a number, the Units per second the superball moves.

1 Like

Works good, I put the number as 43, only issue is, when I move right and left very quickly it just misses the shot, and it does not shoot from up like the Y axis of the SuperBall was pretty normal until after this code the Y axis suddenly dropped.

1 Like

Ping will affect accuracy…

1 Like

Yeah, if the player is moving rapidly in different directions it’s still going to aim at where they were going when he fires. If you want to have them try and predict where the player will be if they change direction that is a different problem.

Hmm, the Y axis should be working, is it not aiming up/down?

1 Like

You might want to check this:
https://devforum.roblox.com/t/predict-projectile-ballistics-including-gravity-and-motion

1 Like

No I mean, watch the video, it throws the SuperBall slightly from up and SuperBall goes down by the Gravity. But after this script it does not goes up at first that much.

1 Like

Looks promising, how can I use it for my script? I am not that good at scripting. I do not know how to use module script in general.

1 Like

Check out the place file it already has demo

1 Like

Yes I see but, I don’t understand what to do? I copied ModuleScript, and then just stuck, how do I need to edit my script so I can just use the module script as it is?

1 Like

If you want to use that module script then I would start with reading through that Post. It does give a good example usage towards the bottom.
In general to use module scripts you have to require them. In this case it would be something

-- Usually you put your requires at the top of your scripts.
local AimModule = require(wherever.You.put.the.module.script)
...
-- and that post also has good descriptions of what the parameters mean.
local aimVector = AimModule.SolveTrajectory(startPos, velocity, targetPos, targetVelocity)

Thank you for help, but I rather script myself than using Module script. I research for many hours recently, can my NPC miss the shots might because of some delay, or can it be fixed by making SuperBall to be thrown at the direction that NPC is facing at?

Still should dissect that Module until you understand it fully.

I’ve been researching whole hours so right, I cannot read the module script. I am very tired. I am still working on making the SuperBall to shoot very accurately at the player. Only information I got is to divide distance between the ball speed:

FlightTime = distance / BallSpeed

And then:
Pedict = Enemy.Position + (Enemy.Velocity * FlightTime)

Then change thrown to:

Thrown = Predict - StartPos

But only thing I don’t like about this code is it’s very easily dodable. Player can quickly click A + D by quickly moving right-left to trick NPC to think that they straight up going to right or left and miss the shot completely.

I think you’re going about this the wrong way. Though it would be sweet to have them actually aim, implementing that isn’t going to be easy or very good. Most commercial games that I know of actually never miss. The code they write is more of a high percentage chance of a random miss. In your case you have a huge bullet that will give away the fact it’s guided. In this case I would have the NPC unload … 5 shells then a reload time. Use your math to “guess” a line of shell shots.

“I cannot read the module script” – This is why I said dissect it. Even if I didn’t use this, understanding what it’s doing would become a priority. Generic module scripts are an amazing way to program – embrace it.

Thanks man, I will try my best. I ain’t going to give up until I finish this. I am trying to create a game where you can spawn these types of NPC against players, inspired by Dummies vs Noobs game.

2 Likes

So little bit dinging on Module script, I finally did it!
Of course, it’s still can be dodged by jumping around, but it became much harder to dodge from.

Thank you for help and other people to!
If any other people has the same issue as mine with the aim accuracy of a NPC, here is my final script to check how it works using this module script → https://devforum.roblox.com/t/predict-projectile-ballistics-including-gravity-and-motion

My Final Script:

local animator = npc.Humanoid.Animator
local animation = animator.BALLTHROW
local loadAnim = animator:LoadAnimation(animation)
local isAnimPlay = false
local npcHum = npc:WaitForChild("Humanoid")
local CalTrejectory = require(script.CalculateTrajectory)

function EnemyNear()
	local nearestPlr = nil
	local shortestDistance = 125

	for _, plr in pairs(game.Players:GetPlayers()) do
		if plr.Character and plr.Character:FindFirstChild("HumanoidRootPart") then
			local distance = (npc.HumanoidRootPart.Position - plr.Character.HumanoidRootPart.Position).magnitude

			if distance <= shortestDistance then
				nearestPlr = plr

				shortestDistance = distance
			end
		end
	end

	return nearestPlr
end

function throw()

	local EnemyPos = EnemyNear()

	if EnemyPos then

		local StartPos = npc.Torso.BallSpawn.Position
		local Root = EnemyPos.Character.HumanoidRootPart
		local BallSpeed = 178.00707
		local pickLongest = false
		local G = workspace.Gravity
		
		local aimAt = CalTrejectory.SolveTrajectory(
			StartPos,
			BallSpeed,
			Root.Position,
			Root.AssemblyLinearVelocity,
			pickLongest,
			G
		) or (StartPos + npc.Torso.BallSpawn.CFrame.LookVector * 10)
		
		if aimAt then
			local SuperBallclone = game.ServerStorage["BALL!"]:Clone()
			--local Force = Thrown / Duration + Vector3.new(0, workspace.Gravity * Duration * 0.5, 0)
			SuperBallclone.Parent = workspace
			SuperBallclone.Position = StartPos
			SuperBallclone:ApplyImpulse((aimAt - StartPos).Unit * BallSpeed * SuperBallclone.AssemblyMass)
			SuperBallclone:SetNetworkOwner(nil)

			local onceHit = false

			SuperBallclone.Touched:Connect(function(hit, hum)
				local hitHum = hit.Parent:FindFirstChild("Humanoid")

				if hitHum and hitHum ~= npcHum and not onceHit then
					hitHum:TakeDamage(11.00707)
					onceHit = true
					task.wait(1)
					onceHit = false
				end

			end)
		end
	end
end

while task.wait(math.random(0.1, 2.00707)) do
	
	if EnemyNear() then
		loadAnim:Play()
	end
	
	wait(0.4700707)
	throw()
	
	wait(1)
end

I spend more than 20 days just to research and script to make my NPC throw projectile as accurate as possible. Some laggy players may struggle on this NPC.:sob:

Explanation:

First you need to put the Module script on the your main Projectile script and write
local CalTrejectory = require(script.CalculateTrajectory)
in your main projectile script to get Module script work.

Then just create a variable and insert the function from module script like this

local aimAt = CalTrejectory.SolveTrajectory()

SolveTrajectory is the function from module script.

And then, the only thing you need to put inside of this function:
The start position which I wrote first
StartPos,
And then the speed of your projectile, it can be any number by your choice.
BallSpeed
Then the position you want your projectile to go which I put:
Root.Position → player’s HumanoidRootPart position
Then the velocity of the target
Root.AssemblyLinearVelocity
Then the pickLongest, I actually do not know what does it do, I just read the module script and saw it’s a boolean, I just write it as false
and then put the gravity which I put as:
G
Which is local G = workspace.Gravity

After these steps, I wrote ApplyImpluse
calculation aim with start pos, multiplying to speed of ball, and it’s mass which is the law from physics, I don’t remember the name :joy:

SuperBallclone:ApplyImpulse((aimAt - StartPos).Unit * BallSpeed * SuperBallclone.AssemblyMass)

Now you’re good to go!

1 Like