I’m making a medival battle simulator. one of the units in this game are archers, and more often than not, they have to be hitting some moving enemies.
I have already figured out most of the math for this, however It is still problematic.
here’s the code:
# closest enemy
var e : CharacterBody3D = findClosestEnemy()
# arrow speed
var s : float = 0.5
# target global Position relative to the archer
var P : Vector3 = e.global_position - global_position
# the velocity of the enemy
var V : Vector3 = e.velocity
# solving some quadratics
var a : float = V.dot(V) - (s * s)
var b : float = 2.0 * V.dot(P)
var c : float = P.dot(P)
var discriminant : float = b * b - 4.0 * a * c
var expectedPos : Vector3 = global_position
# if the targert is moving away faster than the bullet can reach it
if discriminant < 0:
expectedPos = e.global_position
else:
var t : float = (-b - sqrt(discriminant))/(2*a)
expectedPos = e.global_position + V * t
When i try to run this code though, the archer shoots the arrow only 1/3 of the way to the enemy.
note: the white cylinder in front of the archer is the expected position
please help. I have been trying to fix this problem for the past month.
The cylinder’s position is only a visualization of where the arrow will land after being shot. I need the arrow to hit the enemy. So if it worked correctly, the cylinder would be somewhere close to the enemy’s position
The archer is trying to shoot an arrow at the enemy, however, the arrow is slow and takes some time to reach a target that is far away. If we where to simply shoot at the enemy’s current position, by the time the arrow reaches that spot the enemy would have already moved away.
Here, I’m trying to solve for the position the enemy would be at by the time the arrow hits him, and the time the arrow takes to reach that position.
The expected position is where the enemy would be when the arrow reaches it. The formula for that being:
Expected position = enemy.current position + enemy.velocity* time
We can only use this formula when we have found the time it takes the arrow to reach the enemy. This however requires some mathematics, and I got the code above.
If expectedPos is correctly calculated and you assign it to cylinder’s global_position, the cylinder should show up exactly at the calculated position. So either you don’t position the cylinder correctly or expectedPos is not correctly calculated. Since the spear also goes to the position of the cylinder, the only reasonable conclusion is that expectedPos is not correctly calculated.
I have changed it to this, but I’m getting the same results, so I don’t think that’s the problem.
var discriminant : float = (b * b) - (4.0 * a * c)
I have been thinking about it and it probably has something to do with how I’m getting the enemy’s velocity.
In this example, the enemies velocity is -2 m/s along the x-axis and the distance between the archer and the knight being 100 m. if this were true, it would only take the knight 50 frames to get to the archer, but in the game, the knight moves much slower than that.
This is how the knight moves:
const WalkSpeed : float = 2
# targetPos is the archer's position
func move() -> void:
var t : Vector3 = (-global_position+targertPos).normalized()
velocity = t * WalkSpeed
move_and_slide()
maybe in the move_and_slide() function, the knight isn’t actually moved -2 m/s along the x-axis. would there be a reason for this?
sorry, I meant -2 metres/frame not -2 metres/second.
Also, I figured it out. after doing some research, i realised the move_and_slide() function automaticly multiplies velocity by delta, so when i set the velocity to -2 metres/frame, it actually moves -2*delta metres/frame.
to counter act this, I changed the knights move function so it divides the walkspeed by delta before being multiplied by delta in move_and_slide():
func move(delta) -> void:
var t : Vector3 = (-global_position+targertPos).normalized()
velocity = t * WalkSpeed / delta
move_and_slide()
A quick question, isn’t that targetPos (or e.global_position in the original post) grounded to the floor? I just wonder whether you also would want to add a little bit of Z for a clean headshot.
All the objects in this game are grounded to the floor. there is no difference in height. This is to improve on performance, since there will be hundreds of archers firing arrows at the same time.
And for headshots, I won’t bother.
For this type of game, adding extra damage for headshots doesn’t really make that much sense since the player isn’t the one firing the arrow.