ID:1972438
 
(See the best response by Ter13.)
Basically, I would like insight (pseudo-code would be ideal) on how to go about doing the following:
  • Shoot a projectile that gradually curves one direction or another, similar to what's seen here. (This is probably simple.)
  • Shoot a projectile that intercepts (or passes through) a given location, curving based on a strength or gravity input. Picture something similar to this where you may have a flurry of projectiles shooting out at once to barrage a certain location from various angles. The higher the strength/gravity input, the farther out the projectiles "balloon" the angle.


If nothing else, I'd prefer help with the second bullet; the first one is likely something I can manage on my own through trial and error.

Any help is appreciated.
Best response
Situation 1:

Shoot a projectile that gradually curves one direction or another, similar to what's seen here. (This is probably simple.)

Situation 2:

Shoot a projectile that intercepts (or passes through) a given location, curving based on a strength or gravity input. Picture something similar to this where you may have a flurry of projectiles shooting out at once to barrage a certain location from various angles. The higher the strength/gravity input, the farther out the projectiles "balloon" the angle.



Situation 1 is easy enough. How my projectile library handles this in rough pseudocode:

        Fire()
set waitfor = 0
if(active) return
active = 1
fire_time = world.time

var/matrix/M

while(active)
switch(facing)
if(FACING_DIRECTION)
dir = ang2dir(angle)
if(FACING_ANGLE)
M = matrix()
M.Turn(-angle)
transform = M
Project(angle,step_speed)
sleep(TICK_LAG)
if(turn_rate)
angle += turn_rate
if(angle>=360) angle = angle%360
else if(angle<0) angle += floor(angle/-360)*360
if(duration&&fire_time+duration<world.time)
Expire(REASON_TIME)
else if(distance&&cur_distance>distance)
Expire(REASON_DISTANCE)


For situation 2, I'd actually not recommend at all creating a parabola to move along, but rather tracking to a particular target every step during the fire. Set a turn rate limitation, then turn the projectile toward the target based on the minimum turn rate. Cock out the initial firing angle by a particular angle, and then allow the target to home towards to the target location with a very limited turn rate.

angle += clamp(get_angle(src,target),-turn_rate,turn_rate)


Figuring out the turn rate is determined by:

twice the absolute value of the offset angle in degrees divided by the Length of the parabola segment divided by the speed of the projectile

or:

OA / PL / S

Figuring out the length of the parabola involves integral calculus. Kaiochao needs to take over from here, because he's asian.
An alternate way to approach #2 involves linear algebra/matrices and parametric equations.

To start off, the parametric function C'(t) = <cost, sint> will draw out a circle centered at the origin, and its starting point will be at (1,0). If we shift it up slightly, rotate a bit, and squash it, we get C(t) = <½ sin(π t), -½ cos(π t) + ½>, which is more amenable to what you want. Now, it draws out a circle that starts at the origin and ends at (1, 0) on the y-axis, sweeping out counter-clockwise as t moves from 0 to 1. The rate it which t goes from 0 to 1 will change the speed at which your projectile moves.

So, now to figure out how to get to our final result. This approach is very amenable to using matrices.

To simplify things, let's say that for now, we always start at the origin and the enemy will always be in front of us. I.e., we start at (0, 0) and end at (0, y) for some y ≥ 1. Then we need to stretch the function out to fill up the vertical space between the start and end point. This can be accomplished with a matrix that scales on the y axis, multiplied by C(t).

/*

[1 0] [ 1/2 sin(pi t) ]
[0 y] [-1/2 cos(pi t) + 1/2]

*/


Next, you would like to stretch it out horizontally as a function of strength S. This can be accomplished by stretching it in the x axis, using another matrix.

/*

[S 0] [1 0] [ 1/2 sin(pi t) ]
[0 1] [0 y] [-1/2 cos(pi t) + 1/2]

*/


Lastly, we can relax a restriction and assume that the target is still y away from us, but at an angle r instead of just directly in front. Then we can apply a rotation matrix:

/*

[cos(r) -sin(r)] [S 0] [1 0] [ 1/2 sin(pi t) ]
[sin(r) cos(r)] [0 1] [0 y] [-1/2 cos(pi t) + 1/2]

*/


Performing the matrix multiplication, we then get C(t) = < -½ y sin(r) + ½ y cos(π t) sin(r) + ½ S cos(r) sin(π t), ½ y cos(r) - ½ y cos(r) cos(π t) + ½ S sin(r) sin(π t) >.

If you wanted to implement this in an object, then you could have:

projectile
parent_type = /obj

energy_ball
var
// Controls the 'smoothness' of the animation.
const/POINT_SPACING = 15

delay // Delay between jumps in animation.

turf
start // Starting location.
end // Ending location.

distance
angle

stretch // Amount to stretch by.

New(turf/_start, turf/_end, _delay = 2, _stretch = 0)
start = _star
end = _end

distance = sqrt((start.x - end.y)**2 + (start.y - end.y)**2)
angle = atan2(start.x - end.x, start.y - end.y)

delay = _delay
stretch = _stretch

proc
PointAtStep(t)
// Gives the point to move to at time t. From the
// definition of the function, PointAtStep(0) = start
// and PointAtStep(1) = end.

return list(
-1/2 * distance * sin(angle) + 1/2 * distance * cos(3.14159 * t),
1/2 * distance * cos(angle) - 1/2 * y * cos(angle) * cos(3.14159 * t) + 1/2 * stretch * sin(angle) * sin * sin(3.14159 * t)
)

Start()
for(var/i = 0, i <= POINT_SPACING, i ++)
var/list/location = PointAtStep(i / POINT_SPACING)
loc = locate(location[1] + start.x, location[2] + start.y, start.z)

sleep(delay)


Obviously, there are some things you could do to simplify the above. sin(angle) and cos(angle) can be cached, as they do not change once r has been fixed (and you could exploit the fact that sin(atan2(x,y)) = y / sqrt(x**2 + y**2) and cos(atan2(x,y)) = x / sqrt(x**2 + y**2) to never even have to directly compute the sine and cosine of the angle, as an aside). But regardless, that's the general idea.
Appreciate the responses.

While Ter13's suggestion for situation #2 is easier and entirely do-able for me at this point, I am very interested in your method, Popisfizzy.

Admittedly though, I'm not familiar with matrix multiplication. It's been on my learning to-do list for a little while now, so I will take this opportunity to use your method as a reference of study and dive into that subject.

Again, my thanks for the assistance. One less hurdle to jump over and one step closer to a finished project.