Need help with projectiles/bullets

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

Moderators: marionline, SDHawk

Post Reply
User avatar
Foxley
Metal Slime
Posts: 832
Joined: Sat Nov 09, 2013 5:54 pm

Need help with projectiles/bullets

Post by Foxley »

I'm trying to write a script to make bullets shoot out of enemies and move in a straight line, based off of the hero's current X/Y position.

Here's what I got working so far (which looks ridiculous):
Image

The code for this:

Code: Select all

testbox := create rect (10, 10, 1)
set parent (testbox, get npc slice (25))
rise := hero y (me) -- npc y (npc reference(25))
run := hero x (me) -- npc x (npc reference(25))
set slice velocity (testbox, run, rise, 30)
As you can see, the speed of the bullets (i.e. tiny green rectangles) are proportional to the player's distance from the NPC shooting them. I'm wondering if there's a good way to make the speed consistent while moving the bullet with the proper slope/trajectory.
Last edited by Foxley on Sat Mar 11, 2017 9:59 pm, edited 2 times in total.
TMC
Metal King Slime
Posts: 4101
Joined: Sun Apr 10, 2011 9:19 am

Post by TMC »

You calculate the distance as "dist := sqrt(rise^2 + run^2)". Then use "move slice by(testbox, run, rise, dist / 10)" where 10 is the pixels per second.

But I thought you wanted the projectiles to move in a straight line rather than stop at the heroes' former position. To do that, you could use either move slice by or set slice velocity, but I suggest using move slice by because it lets you avoid rounding error, even though it requires more calculations. You can only set the velocity to integer amounts. But suppose the projects have a velocity of 10, and the player is on the other side of the screen, about 300 pixels away. Then the difference between moving 2.5 pixels/frame vs 2 pixels/frame is 0.5 * 300/10 = 15 pixels, so the projectile will end up at the wrong tile.

Code: Select all

speed := 10  # Pixels per tick
move distance := 350  # How many pixels the projectile moves
xdir := hero x (me) -- npc x (npc reference(25))
ydir := hero y (me) -- npc y (npc reference(25))
dist := sqrt(xdir^2 + ydir^2)
# Scale the (xdir, ydir) vector to length movedistance
xdir := move distance * xdir / dist
ydir := move distance * ydir / dist
move slice by(testbox, xdir, ydir, move distance / speed)
User avatar
Foxley
Metal Slime
Posts: 832
Joined: Sat Nov 09, 2013 5:54 pm

Post by Foxley »

I didn't even know HS had a built in square root function!

Thank you for continually saving my butt TMC. This works perfect:
Image

I'll also have a per-tick check to see if these projectile slices end up in a screen edge zone, at which point it'll destroy the slice.
TMC
Metal King Slime
Posts: 4101
Joined: Sun Apr 10, 2011 9:19 am

Post by TMC »

I finally got the idea to add a sqrt function after using a sqrt script I wrote for 8 years...

Looking at that recording, I see that the positions where the boxes stop don't form a totally continuous arc, due to rounding error. Being a perfectionist, here's a version with less rounding error:

Code: Select all

dist := sqrt(100 * xdir^2 + 100 * ydir^2)
# Scale the (xdir, ydir) vector to length movedistance
xdir := move distance * xdir * 10 / dist
ydir := move distance * ydir * 10 / dist 
However, this is completely irrelevant to you, since you'll stop the projectile early, so I'm not suggesting changing your script.
User avatar
Foxley
Metal Slime
Posts: 832
Joined: Sat Nov 09, 2013 5:54 pm

Post by Foxley »

Thank you, it looks even better now.

Code: Select all

script, shoot projectile, ref, type, speed=5, begin
	variable (proj, move distance, xdir, ydir, dist)
	proj := type
	set parent (proj, get npc slice (ref))
	center slice (proj)
	
	move distance := 300
	xdir := hero x (me) -- npc x (ref)
	ydir := hero y (me) -- npc y (ref)
	dist := sqrt(100 * xdir^2 + 100 * ydir^2)
	
	# Scale the (xdir, ydir) vector to length movedistance
	xdir := move distance * xdir * 10 / dist
	ydir := move distance * ydir * 10 / dist
	move slice by (proj, xdir, ydir, move distance / speed)
	set slice extra (get npc slice(ref), extra 0, 100)
end
The 1st argument is of course the NPC who's shooting the projectile and the 2nd argument would be the kind of slice that's being made for the projectile, like so:

Code: Select all

shoot projectile (25, create ellipse (13, 13, 41, 0))
Last edited by Foxley on Sun Mar 12, 2017 11:27 pm, edited 4 times in total.
User avatar
Foxley
Metal Slime
Posts: 832
Joined: Sat Nov 09, 2013 5:54 pm

Post by Foxley »

Unfortunately, now I'm realizing that this script's main weakness is that it parents to the NPC, rather than something neutral like a map layer. If the NPC moves, the positioning of the projectile moves too, which would make no sense.

So I'm doing this instead:

Code: Select all

	set parent (proj, lookup slice (sl:obsolete overhead))
	set slice x (proj, npc pixel x(ref) + (slice width(proj) --20) / -2)
	set slice y (proj, npc pixel y(ref) + (slice height(proj) --20) / -2)
It took me a damn long time to suss out the proper formula to do faux centering of a 20x20 pixel NPC. Math is not my strong point.

So here's (I hope) the final form of this script:

Code: Select all

script, shoot projectile, ref, type, speed=5, begin
	variable (proj, move distance, xdir, ydir, dist)
	proj := type
	set parent (proj, lookup slice (sl:obsolete overhead))
	set slice x (proj, npc pixel x(ref) + (slice width(proj) --20) / -2)
	set slice y (proj, npc pixel y(ref) + (slice height(proj) --20) / -2)
	
	move distance := 300
	xdir := hero x (me) -- npc x (ref)
	ydir := hero y (me) -- npc y (ref)
	dist := sqrt(100 * xdir^2 + 100 * ydir^2)
	
	# Scale the (xdir, ydir) vector to length movedistance
	xdir := move distance * xdir * 10 / dist
	ydir := move distance * ydir * 10 / dist
	move slice by (proj, xdir, ydir, move distance / speed)
	set slice extra (get npc slice(ref), extra 0, 100)
end
Last edited by Foxley on Mon Mar 13, 2017 12:27 am, edited 1 time in total.
Post Reply