I'm trying to get a enemy ship to shoot several bullets out at once in a spread pattern. The bullets aren't being targeted at any specific object or point (not a homing missile)...I just want them to shoot out at various angles, like 180 45 and 225*
Ideally, I'd like to have a function that lets you pass in a origin x/y, an angle and speed...
--shootBullet(x, y, angle, speed) shootBullet(25,25,45,2) |
...then I could put that as part of the a larger collection of functions for my shooting routines. I just
I felt that I had some of the answer in the bullet trajectory math here, but after trying to change different variables and stuff, I just couldn't get it right. This is what I get for not going beyond Algebra I in school ;P
If anyone has a method or can share the math, I'd appreciate it. Thanks!



Sounds like you should google up some bullet hell patterns.
For getting an angle into x and y, you want something like
dx=cos(angle)speed
dy=sin(angle)speed
edit: pico-8 doesn't work in degrees or radians; it uses rotations. (360deg==2pi==1rot)



That's the math I'm using but the Y never changes...X does though. I read through the P8 manual and see that sin() is inverted, so it sounds like I have to convert that? I dunno. I'll keep digging...believe me, I've been Googling...
I don't think it helps that the Lua math library in P8 is limited or missing or whatever. I can't do radians.
speed=2 angle=45 x += speed * cos(angle) y += speed * sin(angle) |



Disclaimer - I still haven't made anything pico yet. (just bought yesterday) This might compile, but no promises
You'll probably want to keep a table just for dumb bullets like these, and use a single update function that runs through them to update since all they need is x,y and dx,dy. (Another point - front loading the sin and cos at creation time means less math on every update, which is usually a good thing)
function makedumbbullet(x,y,a,s)
local newb={}
newb.x=x
newb.y=y
newb.dx=cos(a)s
newb.dy=-sin(a)s
return newb
end
which you call with a quick "add(dumbbullettable, makedumbbullet(x,y,angle,speed))"
function updatedumbbullets()
for b in all dumbbullettable
b.x+=b.dx
b.y+=b.dy
end
end



function bulletring(x,y,number,speed)
for i=1,number do
add(dumbbullettable, makedumbbullet(x,y,i/number,speed))
end
edited for proper angle value



You can still use degrees, just be sure to plug in
degrees * 3.14159 / 180 = radians
someplace
edit: and the inverted sin is because of the direction y pixels are numbered (top to bottom, I assume like most video functions)
edit edit: Not guaranteeing pi is available, so I'll check... and it isn't. So punch in those digits! (I guess)



Cool, I never thought about having a pre-loaded table...good idea.
Here's how I took your snippets and applied it, it's still not moving the object in the right direction, but I feel I'm missing something. Do I need to convert the angle into something? Is that where the radians come in?
The result here is X changes but Y stays constant, so the ball just moves to the right.
dumbbullettable={} -- start with ball in the bottom-left corner, try to move it to top-right x=10 y=120 s=1 angle=45 function makedumbbullet(x,y,a,s) local newb={} newb.x= x newb.y= y newb.dx= cos(a)*s newb.dy= -sin(a)*s return newb end -- Shoot at 45* angle (if north is up, this should go northeast) add(dumbbullettable, makedumbbullet(x,y,angle,s)) --LOOPS function _update() foreach(dumbbullettable, function(obj) obj.x+=obj.dx obj.y+=obj.dy end) end function _draw() cls() foreach(dumbbullettable, function(obj) print(obj.x.." x "..obj.y,80,5,7) circfill(obj.x,obj.y,3,7) end) end |



exactly right. Any built in function that expects an angle expects it to be in radians. So just convert it before you get to the sin and cos.
function makedumbbullet(x,y,a,s) [b]a=a/360[/b] local newb={} newb.x= x newb.y= y newb.dx= cos(a)*s newb.dy= sin(a)*s return newb end |
should do the trick
edit: and totally change that table and function name. All those repeated letters are just begging for typos... (I keep having to fix it just writing posts)
edit edit - fixed the worst typo on the first line.
final edit - fixed angle value



Gotcha...so I added the radians math and now the X and Y change (yay) but the ball certainly isn't moving at 45 angle. It's more like a 10 angle looks like, very narrow. It's weird.
I'm assuming 0 is straight up, 90 is right, 180 down and 270 is left.
-- start with ball in the middle, try to move it to top-right x=60 y=60 s=1 angle=45 pi=3.14159265359 function makedumbbullet(x,y,a,s) rad=a*pi/180 local newb={} newb.x= x newb.y= y newb.dx= cos(rad)*s newb.dy= -sin(rad)*s return newb end -- Shoot at 45* angle (if north is up, this should go northeast) add(dumbbullettable, makedumbbullet(x,y,angle,s)) |



0 degrees is straight right, and goes ccw as the angle increases.
edit: pico-8 uses "turns": 1 turn == 360 deg



Don't forget that Pico-8 is weird and takes the range [0,1] instead of [0,2PI]. If you have radians, divide by 2PI first.
print(sin(0.25)) -1 print(sin(0.75)) 1 for t=0,1,0.1 do print(sin(t)) end 0 -0.5877 -0.9511 -0.9511 -0.588 -0.0003738 0.5877 0.951 0.9512 0.5883 0.0003891 |



As seen in dddaaannn's example, note that sin() returns an already-inverted value to match screen space, so you don't need to re-invert the y.
The quirks with pico-8's trig functions are documented here: https://www.lexaloffle.com/pico-8.php?page=manual (search for "Quirks")



Instead of thinking of pico-8's sin and cos as being in radians, think of them as being in "turns".



So if I want to provide a function with a "human" angle value, like 45 or 270, I need to convert that into radians before I give it to sin() and cos()...right?
But if I give sin() and cos() a decimal value between 0 and 1, I can skip the extra calculation?
So in decimal world, 0 is right, so .25 is straight up, .50 is left and .75 is down?
I'm at work so I can't test it anything here but tonight I'll try all of these snippets out.



So in decimal world, 0 is right, so .25 is straight up, .50 is left and .75 is down? |
Totally.
When you code with pico8 you dont have to bother with degrees or radians.
1 is equal to a full turn.



Here's what I mashed together that got things working in a way I understood and was easy to implement. In short, instead of providing "human" angle numbers, I just give it decimals and everything works fine.
So worst case, I have a little cheat sheet that reminds me which decimals go with which angles. Easy enough.
Thanks to everyone that chimed in. I'm a little smarter and my game will be a little better thanks to everyone's help.
bullets={} -- settings, start in the center and create an X shot x=60 y=60 s=2 function createBullet(x,y,a,s) local newb={} newb.x=x newb.y=y newb.dx=cos(a)*s newb.dy=sin(a)*s return newb end --LOOPS function _update() foreach(bullets, function(obj) obj.x+=obj.dx obj.y+=obj.dy end) end function _draw() cls() circfill(x,y,3,1) foreach(bullets, function(obj) circfill(obj.x,obj.y,3,7) end) end -- Create bullets -- This will shoot 4 bullets in a X pattern from the center point -- Angles: 0=right/0*, .25=up/90*, .50=left/180*, .75=down/270*, .125=45* add(bullets, createBullet(x,y,.125,s)) --45* add(bullets, createBullet(x,y,.375,s)) --135* add(bullets, createBullet(x,y,.625,s)) --225* add(bullets, createBullet(x,y,.875,s)) --315* |
[Please log in to post a comment]