Arcing Projectiles
Moderators: marionline, SDHawk
- sheamkennedy
- Liquid Metal Slime
- Posts: 1110
- Joined: Mon Sep 16, 2013 9:29 pm
- Location: Tama-shi, Tokyo, Japan
- Contact:
Cool, it'll be good to see the script. Also you should post a video or gif to show off the visuals if you have time to.
⊕ P E R S O N A L M U S I C: https://open.spotify.com/album/6fEo3fCm5C3XhtFRflfANr
â� C O L L A B M U S I C: https://dustpuppets.bandcamp.com/releases
â� C O L L A B M U S I C: https://dustpuppets.bandcamp.com/releases
Okay I finally had time to sit down and mess with this again.
I guess I was a little premature in thinking I had it perfected. After playing with the variables the code broke down at certain heights and distances.
I've tried various methods, which usually ends up working for the first half of the arc and completely failing on the falling part.
TMC's version of MSW's script seems like my best bet, but rather than a for loop I need to be able to do it with a timer running each tick. I can pass it any amount of global variables necessary, but I'm not 100% sure how to alter it to work with globals. Also I'm a little confused on what velocity should be. Is a fixed number okay for that or do I need it to also be a variable? Does it need to change? And also what is a reasonable range the number should be in?
The arrow generally will travel at max the length of the screen or possibly a little more.
I guess I was a little premature in thinking I had it perfected. After playing with the variables the code broke down at certain heights and distances.
I've tried various methods, which usually ends up working for the first half of the arc and completely failing on the falling part.
TMC's version of MSW's script seems like my best bet, but rather than a for loop I need to be able to do it with a timer running each tick. I can pass it any amount of global variables necessary, but I'm not 100% sure how to alter it to work with globals. Also I'm a little confused on what velocity should be. Is a fixed number okay for that or do I need it to also be a variable? Does it need to change? And also what is a reasonable range the number should be in?
The arrow generally will travel at max the length of the screen or possibly a little more.
v is horizontal velocity. Default walking speed is 4 pixels/tick, which is quite slow, so I would use a value of 10-15 for an arrow. There's no need to make it variable unless you're going to have some arrows fired at high angles.
The xi doesn't need to be globals if we instead move the slice relative to its previous position, but yi is needed. (We could instead rewrite the parabola equation in terms of acceleration due to gravity, which is how 99% of games will do it, and thereby get rid of several global variables.) The 't' variable could also be remove if you add a check for collision with the ground (which requires tracking z value)
Here's a refactored version:
If you used gravity instead it would look like:
(Edited to fix mistake pointed out)
The xi doesn't need to be globals if we instead move the slice relative to its previous position, but yi is needed. (We could instead rewrite the parabola equation in terms of acceleration due to gravity, which is how 99% of games will do it, and thereby get rid of several global variables.) The 't' variable could also be remove if you add a check for collision with the ground (which requires tracking z value)
Here's a refactored version:
Code: Select all
define constant(15, v)
global variable(1, h)
global variable(2, d)
global variable(3, t)
global variable(4, yi)
global variable(5, slice)
...
set timer(15, 0, 1, @arrow eachtick)
script, arrow eachtick, timer id, begin
variable (x, y)
x := slice x(slice)
#y := slice y(slice)
x += v
y := yi -- (h -- (v*t--d/2)^2 * 4*h/(d^2))
#command to put your graphic at coordinates (x,y)
put slice(slice, x, y)
#command to check whether or not the arrow has struck any obstacle, and if so, turn on a tag to stop the for loop
....
# Next tick
t += 1
if (t <= d/v) then (
set timer(timer id, 0) # Tip: Restarts the timer with a count of 0 (next tick)
) else (
# Do cleanup
)
end
Code: Select all
define constant(3, gravity)
global variable(1, vz) # vertical velocity
global variable(2, vx) # horizontal velocity
global variable(3, y) # Y position of the shadow of the arrow
global variable(4, z) # pixels above the shadow where the arrow is
global variable(5, slice)
script, arrow eachtick, timer id, begin
variable (x, old z, y)
x := slice x(slice)
x += vx
vz += gravity
z += vz
#put slice(shadow slice, x, y) # a shadow on the ground below the arrow
put slice(slice, x, y -- z)
#check whether or not the arrow has struck any obstacle...
...
#... or the ground
if (z <= 0) then (
...
)
set timer(timer id, 0)
end
Last edited by TMC on Wed May 13, 2015 11:09 am, edited 1 time in total.
Alright, I think I have it about as close to where I want it to be. I tried it the first way and you're right, it definitely looks silly without gravity involved. Btw I had to adjust the y line because it was firing under the map. y := yi -- (h -- (v*t--d/2)^2 * 4*h/(d^2)) is what I changed it to
The second script is probably more of what I need aesthetically but I'm pretty dead set on wanting to control the height and distance specifically. So last night I wrote a script that incorporated more of a gravity and velocity controlled arrow but still gets somewhat close to where I want it to go. I had to sacrifice on the distance part mostly because I really couldn't figure out a good way to keep it accurate when it's falling. It's a little sloppy and the animation needs some more work probably, but it's the best looking and most functional I've gotten to work.
I know it's pretty specific and no one will probably ever use it but I'll post it anyway.
The second script is probably more of what I need aesthetically but I'm pretty dead set on wanting to control the height and distance specifically. So last night I wrote a script that incorporated more of a gravity and velocity controlled arrow but still gets somewhat close to where I want it to go. I had to sacrifice on the distance part mostly because I really couldn't figure out a good way to keep it accurate when it's falling. It's a little sloppy and the animation needs some more work probably, but it's the best looking and most functional I've gotten to work.
I know it's pretty specific and no one will probably ever use it but I'll post it anyway.
Code: Select all
script, Shoot Arrow, m, HEIGHT, DISTANCE, begin
Variable (i)
## FINDS AN EMPTY ARROW GLOBAL TO USE FOR THIS PARTICULAR ARROW
for (i,756,819,7)
do,begin
if (Read Global (i) == 0)
then,begin
## PLACE ARROW (m is the actual slice number of the npc firing the arrow)
Write Global (i, Load small Enemy Sprite (10))
Set Parent (Read Global (i), Lookup Slice (sl: map layer 2))
Put slice (Read Global (i), Slice X (m), Slice Y (m))
## SAVE HEIGHT AND DISTANCE
Write Global (i + 1, HEIGHT)
Write Global (i + 2, DISTANCE)
## SAVE VELOCITY
Write Global (i + 3, Arrow Velocity * 100)
## SAVE TIME
Write Global (i + 4, (abs (DISTANCE) + HEIGHT * 2) / Arrow Speed)
## SETS THE FIRST HALF OF THE ARROW ARC IN MOTION
Move Slice To (Read Global (i), SLice X (Read global (i)) -- DISTANCE / 2, SLice Y (Read global (i)) -- HEIGHT, (abs (DISTANCE) + HEIGHT * 2) / Arrow Speed / 2)
Exit Script
end
end
end
### ARROW SCRIPT WITHIN THE RUNNING LOOP
## MOVE ARROW PROJECTILES
variable (HEIGHT)
variable (DISTANCE)
variable (VELOCITY)
variable (TIME)
variable (xi)
Variable (yi)
variable (x)
variable (y)
## THIS CYCLES THROUGH ALL AVAILABLE ARROW PROJECTILE GLOBALS THAT MAY BE ACTIVE
for (i,756,819,7)
do,begin
if (Read Global (i) >> 0)
then,begin
HEIGHT := Read Global (i + 1)
DISTANCE := Read Global (i + 2)
VELOCITY := Read Global (i + 3)
TIME := Read Global (i + 4)
## THESE ARE USED IN HIT DETECTION
Write Global (i + 5, Slice X (Read Global (i)))
Write Global (i + 6, Slice Y (Read Global (i)))
## THIS SETS UP THE FALLING MOTION
if (Slice Is Moving (Read Global (i)) == FALSE && TIME >> (abs (DISTANCE) + HEIGHT * 2) / Arrow Speed / 4)
then, begin
Write Global (i + 4, (abs (DISTANCE) + HEIGHT * 2) / Arrow Speed / 2)
TIME := Read Global (i + 4)
Set Slice Velocity X (Read Global (i), DISTANCE * -1 / 2 / TIME, TIME * 2)
if (Get Slice Velocity X (Read Global (i)) << 1 && DISTANCE << 0)
then (Set Slice Velocity X (Read Global (i), 1))
if (Get Slice Velocity X (Read Global (i)) >> 1 && DISTANCE >> 0)
then (Set Slice Velocity X (Read Global (i), 1))
Write Global (i + 3, Arrow Velocity * 100 / 2)
VELOCITY := Arrow Velocity * 100 / 2
end
## THIS BOOSTS THE ARROW WHILE IT'S RISING TO MAKE IT MORE OF AN ARC
if ( TIME >> (abs (DISTANCE) + HEIGHT * 2) / Arrow Speed / 2)
then,begin
if (VELOCITY << 0)
then (VELOCITY := 0)
Set Slice Y (Read Global (i), Slice Y (Read Global (i)) -- VELOCITY / 100)
Write Global (i + 3, Read Global (i + 3) -- GRAVITY)
## ARROW GRAPHIC CHANGE
If (HEIGHT * 100 / abs (DISTANCE) >= 133)
then (Replace Small Enemy Sprite (Read Global (i), 9))
end
## ANIMATE ARROWS
if (TIME <= ((abs (DISTANCE) + HEIGHT * 2) * 100 / Arrow Speed * 65 / 10000) && Get Sprite Set Number (Read global (i)) <> 11)
then (Replace Small Enemy Sprite (Read Global (i), 10))
if (TIME <= ((abs (DISTANCE) + HEIGHT * 2) * 100 / Arrow Speed * 55 / 10000) && TIME >= ((abs (DISTANCE) + HEIGHT * 2) * 100 / Arrow Speed * 45 / 10000))
then (Replace Small Enemy Sprite (Read Global (i), 11))
if (TIME <= ((DISTANCE + HEIGHT * 2) * 100 / Arrow Speed * 45 / 10000))
then (Replace Small Enemy Sprite (Read Global (i), 12))
if (TIME <= ((abs (DISTANCE) + HEIGHT * 2) * 100 / Arrow Speed * 35 / 10000) && HEIGHT * 100 / abs (DISTANCE) >= 133)
then (Replace Small Enemy Sprite (Read Global (i), 13))
If (abs (DISTANCE) * 100 / HEIGHT >= 133 && TIME >= ((abs (DISTANCE) + HEIGHT * 2) * 100 / Arrow Speed * 40 / 10000))
then (Replace Small Enemy Sprite (Read Global (i), 11))
If (abs (DISTANCE) * 100 / HEIGHT >= 133 && TIME << ((abs (DISTANCE) + HEIGHT * 2) * 100 / Arrow Speed * 40 / 10000))
then (Replace Small Enemy Sprite (Read Global (i), 12))
if (DISTANCE << 0)
then (Horiz Flip Sprite (Read Global (i)))
## THIS ADDS GRAVITY TO THE FALLING ARROW (Which may cause it to fall short of full distance)
if ( TIME <= (abs (DISTANCE) + HEIGHT * 2) / Arrow Speed / 2)
then,begin
Set Slice Y (Read Global (i), Slice Y (Read Global (i)) + VELOCITY / 100)
Write Global (i + 3, Read Global (i + 3) + GRAVITY * 3)
end
Write Global (i + 4, TIME -- 1)
## MAKES THE ARROW FALL WELL PAST THE VISIBLE AREA BEFORE DESTROYING IT (could also use a loop for conciseness)
if (TIME << -20)
then,begin
Free Slice (i)
Write Global (i, 0)
Write Global (i + 1, 0)
Write Global (i + 2, 0)
Write Global (i + 3, 0)
Write Global (i + 4, 0)
Write Global (i + 5, 0)
Write Global (i + 6, 0)
end
end
end
Set Timer (2,0,1,@EnemyLoop)
Actually whether you explicitly calculate acceleration due to gravity or not, the curve will be a parabola, so there should be no difference between the two scripts given the right set of parameters. So if you want to specify the height there's no reason you can't use the first script.
I'm really surprised to see you're still using movesliceto, as well as slice velocity. I didn't read the whole script, but it looks like it moves in something like half a sigmoid up to the top of the arc, then in a parabola down for the second half. So you didn't want a simple arc?
I'm really surprised to see you're still using movesliceto, as well as slice velocity. I didn't read the whole script, but it looks like it moves in something like half a sigmoid up to the top of the arc, then in a parabola down for the second half. So you didn't want a simple arc?
I want it to travel like an actual arrow would. Meaning higher velocity at the initial release, slowing at the peak, and gaining speed as it falls. The actual shape of the arc doesn't concern me as long as it looks somewhat realistic, but I want it to reach the general vicinity of the height and distance input.
Although the 'actual' velocity does indeed decrease to the top, then increase on the way down, it is worth pointing out (maybe) that the horizontal velocity remains constant (ignoring air resistance). What this means is that, as long as the horiz velocity is kept constant, and the parabola shape is forced by my equations, that the 'actual' velocity will decrease and increase automatically. ie, in my version, the SHAPE (and its parametrization) is enforcing the acceleration/velocity, rather than the acceleration enforcing the shape.
I haven't gone through your script, but I can't imagine it needs anywhere near that amount of code. I'm also pretty certain that if you use my version, you can keep your own original plans for inputs. My script take the desired target (based on given distance input) and magically makes the shooter super smart, so that whatever height he/she inputs, his/her arrow traverses that height and hits the target. It will never fall short of the input distance, unless it is interrupted by an obstacle.
I haven't gone through your script, but I can't imagine it needs anywhere near that amount of code. I'm also pretty certain that if you use my version, you can keep your own original plans for inputs. My script take the desired target (based on given distance input) and magically makes the shooter super smart, so that whatever height he/she inputs, his/her arrow traverses that height and hits the target. It will never fall short of the input distance, unless it is interrupted by an obstacle.
I am Srime
Yeah like I said, it's pretty sloppy. Lol
Your code did work, but at high velocity short distances would happen so fast you couldn't see enough of the arrow and at lower velocity it moved way too slow. The motion seemed like more floating than an object being launched.
I want to get a video up when I have some free time. This is a busy week though.
Your code did work, but at high velocity short distances would happen so fast you couldn't see enough of the arrow and at lower velocity it moved way too slow. The motion seemed like more floating than an object being launched.
I want to get a video up when I have some free time. This is a busy week though.
What I would do to solve that is to display the arrow on the target after it hits. So even if the distance is so short that it's only in air for 0 or 1 tick, you can still see it hit.
The other option is to increase the frame rate, but that will mean all existing scripts as well as hero and NPC movement will speed up.
The other option is to increase the frame rate, but that will mean all existing scripts as well as hero and NPC movement will speed up.
Last edited by TMC on Thu May 14, 2015 10:37 pm, edited 1 time in total.