Arcing Projectiles

Ask and answer questions about making games and related topics. Unrelated topics go in that other forum.

Moderators: marionline, SDHawk

User avatar
sheamkennedy
Liquid Metal Slime
Posts: 1110
Joined: Mon Sep 16, 2013 9:29 pm
Location: Tama-shi, Tokyo, Japan
Contact:

Post by sheamkennedy »

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
User avatar
Soule X
Red Slime
Posts: 86
Joined: Wed Sep 19, 2012 4:18 pm
Location: Indianapolis

Post by Soule X »

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.
TMC
Metal King Slime
Posts: 4308
Joined: Sun Apr 10, 2011 9:19 am

Post by TMC »

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:

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 &#40;t <= d/v&#41; then &#40;
        set timer&#40;timer id, 0&#41;  # Tip&#58; Restarts the timer with a count of 0 &#40;next tick&#41;
    &#41; else &#40;
        # Do cleanup
    &#41;
end
If you used gravity instead it would look like:

Code: Select all

define constant&#40;3, gravity&#41;

global variable&#40;1, vz&#41;  # vertical velocity
global variable&#40;2, vx&#41;  # horizontal velocity
global variable&#40;3, y&#41;   # Y position of the shadow of the arrow
global variable&#40;4, z&#41;   # pixels above the shadow where the arrow is
global variable&#40;5, slice&#41;

script, arrow eachtick, timer id, begin
    variable &#40;x, old z, y&#41;
    x &#58;= slice x&#40;slice&#41;

    x += vx
    vz += gravity
    z += vz
    #put slice&#40;shadow slice, x, y&#41;  # a shadow on the ground below the arrow
    put slice&#40;slice, x, y -- z&#41;

    #check whether or not the arrow has struck any obstacle...
    ...
    #... or the ground
    if &#40;z <= 0&#41; then &#40;
        ...
    &#41;

    set timer&#40;timer id, 0&#41;
end
(Edited to fix mistake pointed out)
Last edited by TMC on Wed May 13, 2015 11:09 am, edited 1 time in total.
User avatar
Soule X
Red Slime
Posts: 86
Joined: Wed Sep 19, 2012 4:18 pm
Location: Indianapolis

Post by Soule X »

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.

Code: Select all

script, Shoot Arrow, m, HEIGHT, DISTANCE, begin

Variable &#40;i&#41;

## FINDS AN EMPTY ARROW GLOBAL TO USE FOR THIS PARTICULAR ARROW
	for &#40;i,756,819,7&#41;
	do,begin
		if &#40;Read Global &#40;i&#41; == 0&#41;
		then,begin
			## PLACE ARROW &#40;m is the actual slice number of the npc firing the arrow&#41;
			Write Global &#40;i, Load small Enemy Sprite &#40;10&#41;&#41;
			Set Parent &#40;Read Global &#40;i&#41;, Lookup Slice &#40;sl&#58; map layer 2&#41;&#41;
			Put slice &#40;Read Global &#40;i&#41;, Slice X &#40;m&#41;, Slice Y &#40;m&#41;&#41;

			## SAVE HEIGHT AND DISTANCE
			Write Global &#40;i + 1, HEIGHT&#41;
			Write Global &#40;i + 2, DISTANCE&#41;

			## SAVE VELOCITY
			Write Global &#40;i + 3, Arrow Velocity * 100&#41;

			## SAVE TIME
			Write Global &#40;i + 4, &#40;abs &#40;DISTANCE&#41; + HEIGHT * 2&#41; / Arrow Speed&#41;

			## SETS THE FIRST HALF OF THE ARROW ARC IN MOTION
			Move Slice To &#40;Read Global &#40;i&#41;, SLice X &#40;Read global &#40;i&#41;&#41; -- DISTANCE / 2, SLice Y &#40;Read global &#40;i&#41;&#41; -- HEIGHT, &#40;abs &#40;DISTANCE&#41; + HEIGHT * 2&#41; / Arrow Speed / 2&#41;
			Exit Script
		end
	end
end


### ARROW SCRIPT WITHIN THE RUNNING LOOP
## MOVE ARROW PROJECTILES

	variable &#40;HEIGHT&#41;
	variable &#40;DISTANCE&#41;
	variable &#40;VELOCITY&#41;
	variable &#40;TIME&#41;
	variable &#40;xi&#41;
	Variable &#40;yi&#41;
	variable &#40;x&#41;
	variable &#40;y&#41;

## THIS CYCLES THROUGH ALL AVAILABLE ARROW PROJECTILE GLOBALS THAT MAY BE ACTIVE
	for &#40;i,756,819,7&#41;
	do,begin
		if &#40;Read Global &#40;i&#41; >> 0&#41;
		then,begin
			HEIGHT &#58;= Read Global &#40;i + 1&#41;
			DISTANCE &#58;= Read Global &#40;i + 2&#41;
			VELOCITY &#58;= Read Global &#40;i + 3&#41;
			TIME &#58;= Read Global &#40;i + 4&#41;
			
			## THESE ARE USED IN HIT DETECTION
			Write Global &#40;i + 5, Slice X &#40;Read Global &#40;i&#41;&#41;&#41;
			Write Global &#40;i + 6, Slice Y &#40;Read Global &#40;i&#41;&#41;&#41;

			## THIS SETS UP THE FALLING MOTION
			if &#40;Slice Is Moving &#40;Read Global &#40;i&#41;&#41; == FALSE && TIME >> &#40;abs &#40;DISTANCE&#41; + HEIGHT * 2&#41; / Arrow Speed / 4&#41;
			then, begin
				Write Global &#40;i + 4, &#40;abs &#40;DISTANCE&#41; + HEIGHT * 2&#41; / Arrow Speed / 2&#41;
				TIME &#58;= Read Global &#40;i + 4&#41;
				Set Slice Velocity X &#40;Read Global &#40;i&#41;, DISTANCE * -1 / 2 / TIME, TIME * 2&#41;
				if &#40;Get Slice Velocity X &#40;Read Global &#40;i&#41;&#41; << 1 && DISTANCE << 0&#41;
				then &#40;Set Slice Velocity X &#40;Read Global &#40;i&#41;, 1&#41;&#41;
				if &#40;Get Slice Velocity X &#40;Read Global &#40;i&#41;&#41; >> 1 && DISTANCE >> 0&#41;
				then &#40;Set Slice Velocity X &#40;Read Global &#40;i&#41;, 1&#41;&#41;
				Write Global &#40;i + 3, Arrow Velocity * 100 / 2&#41;
				VELOCITY &#58;= Arrow Velocity * 100 / 2
			end

			## THIS BOOSTS THE ARROW WHILE IT'S RISING TO MAKE IT MORE OF AN ARC
			if &#40; TIME >> &#40;abs &#40;DISTANCE&#41; + HEIGHT * 2&#41; / Arrow Speed / 2&#41;
			then,begin
				if &#40;VELOCITY << 0&#41;
				then &#40;VELOCITY &#58;= 0&#41;
				Set Slice Y &#40;Read Global &#40;i&#41;, Slice Y &#40;Read Global &#40;i&#41;&#41; -- VELOCITY / 100&#41;
				Write Global &#40;i + 3, Read Global &#40;i + 3&#41; -- GRAVITY&#41;

				## ARROW GRAPHIC CHANGE
				If &#40;HEIGHT * 100 / abs &#40;DISTANCE&#41; >= 133&#41;
				then &#40;Replace Small Enemy Sprite &#40;Read Global &#40;i&#41;, 9&#41;&#41;
			end

			## ANIMATE ARROWS
			if &#40;TIME <= &#40;&#40;abs &#40;DISTANCE&#41; + HEIGHT * 2&#41; * 100 / Arrow Speed * 65 / 10000&#41; && Get Sprite Set Number &#40;Read global &#40;i&#41;&#41; <> 11&#41;
			then &#40;Replace Small Enemy Sprite &#40;Read Global &#40;i&#41;, 10&#41;&#41;
			if &#40;TIME <= &#40;&#40;abs &#40;DISTANCE&#41; + HEIGHT * 2&#41; * 100 / Arrow Speed * 55 / 10000&#41; && TIME >= &#40;&#40;abs &#40;DISTANCE&#41; + HEIGHT * 2&#41; * 100 / Arrow Speed * 45 / 10000&#41;&#41;
			then &#40;Replace Small Enemy Sprite &#40;Read Global &#40;i&#41;, 11&#41;&#41;
			if &#40;TIME <= &#40;&#40;DISTANCE + HEIGHT * 2&#41; * 100 / Arrow Speed * 45 / 10000&#41;&#41;
			then &#40;Replace Small Enemy Sprite &#40;Read Global &#40;i&#41;, 12&#41;&#41;
			if &#40;TIME <= &#40;&#40;abs &#40;DISTANCE&#41; + HEIGHT * 2&#41; * 100 / Arrow Speed * 35 / 10000&#41; && HEIGHT * 100 / abs &#40;DISTANCE&#41; >= 133&#41;
			then &#40;Replace Small Enemy Sprite &#40;Read Global &#40;i&#41;, 13&#41;&#41;
			If &#40;abs &#40;DISTANCE&#41; * 100 / HEIGHT >= 133 && TIME >= &#40;&#40;abs &#40;DISTANCE&#41; + HEIGHT * 2&#41; * 100 / Arrow Speed * 40 / 10000&#41;&#41;
			then &#40;Replace Small Enemy Sprite &#40;Read Global &#40;i&#41;, 11&#41;&#41;
			If &#40;abs &#40;DISTANCE&#41; * 100 / HEIGHT >= 133 && TIME << &#40;&#40;abs &#40;DISTANCE&#41; + HEIGHT * 2&#41; * 100 / Arrow Speed * 40 / 10000&#41;&#41;
			then &#40;Replace Small Enemy Sprite &#40;Read Global &#40;i&#41;, 12&#41;&#41;
			if &#40;DISTANCE << 0&#41;
			then &#40;Horiz Flip Sprite &#40;Read Global &#40;i&#41;&#41;&#41;

			## THIS ADDS GRAVITY TO THE FALLING ARROW &#40;Which may cause it to fall short of full distance&#41;
			if &#40; TIME <= &#40;abs &#40;DISTANCE&#41; + HEIGHT * 2&#41; / Arrow Speed / 2&#41;
			then,begin
				Set Slice Y &#40;Read Global &#40;i&#41;, Slice Y &#40;Read Global &#40;i&#41;&#41; + VELOCITY / 100&#41;
				Write Global &#40;i + 3, Read Global &#40;i + 3&#41; + GRAVITY * 3&#41;
			end

			Write Global &#40;i + 4, TIME -- 1&#41;

			## MAKES THE ARROW FALL WELL PAST THE VISIBLE AREA BEFORE DESTROYING IT &#40;could also use a loop for conciseness&#41;
			if &#40;TIME << -20&#41;
			then,begin
				Free Slice &#40;i&#41;
				Write Global &#40;i, 0&#41;
				Write Global &#40;i + 1, 0&#41;
				Write Global &#40;i + 2, 0&#41;
				Write Global &#40;i + 3, 0&#41;
				Write Global &#40;i + 4, 0&#41;
				Write Global &#40;i + 5, 0&#41;
				Write Global &#40;i + 6, 0&#41;
			end
		end
	end

Set Timer &#40;2,0,1,@EnemyLoop&#41;
TMC
Metal King Slime
Posts: 4308
Joined: Sun Apr 10, 2011 9:19 am

Post by TMC »

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?
User avatar
Soule X
Red Slime
Posts: 86
Joined: Wed Sep 19, 2012 4:18 pm
Location: Indianapolis

Post by Soule X »

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.
TMC
Metal King Slime
Posts: 4308
Joined: Sun Apr 10, 2011 9:19 am

Post by TMC »

Ignoring air resistance, all projectiles travel in parabolas. So I think you've drastically overcomplicated things, but if it's already good enough then there's no need to spend time on it.
User avatar
msw188
Metal Slime
Posts: 787
Joined: Tue Oct 16, 2007 1:43 am
Location: Los Angeles, CA

Post by msw188 »

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 am Srime
User avatar
Soule X
Red Slime
Posts: 86
Joined: Wed Sep 19, 2012 4:18 pm
Location: Indianapolis

Post by Soule X »

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.
TMC
Metal King Slime
Posts: 4308
Joined: Sun Apr 10, 2011 9:19 am

Post by TMC »

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.
Last edited by TMC on Thu May 14, 2015 10:37 pm, edited 1 time in total.
Post Reply