This post is about creating a shower of arrows that are shot into the air and fall back to the earth. Useful for your next elves vs orcs game.

I’ve created a new physics class to handle this, because it needs a couple of special features.

In the setup, I’ve set gravity to the same value as in Codea, ie 133 pixels per second.

The arrows have a number of properties, as you can see below. I’ve separated direction from speed just to make it easier for a user to set them, and I’ll need to multiply them together to get velocity.

The damping factor just adds a little drag to the arrows, although with gravity as well, it may not be necessary. I have read that it’s a good idea to include damping because computers aren’t perfectly accurate, and if you set a constant speed, it may not stay that way because of rounding errors. Damping just makes sure the speed never spins out on control.

Ballistics = class()

--constants which apply to all physics objects
Ballistics.gravityStrength=vec2(0,-133) --not accurate

function Ballistics:init(position,direction,speed,mass,
colour,diameter,length)
self.position=position
self.velocity=direction*speed
self.mass=mass
self.color=colour or color(0)
self.width,self.length=diameter,length
--initialise
self.damping= 0.99
self.acceleration = vec2(0,0) --w.accel
Ballistics.gravityOn=true
end

The AdjustPosition function is exactly the same as for previous posts, but the draw function has some changes.

Arrows need to rotate as they shot up, then fall to earth, so they are always facing in the direction they are going. How do we do this? (So I got sick of trying to draw simple diagrams in iPad drawing apps. It’s just quicker to draw them and photograph them!)

Above is our arrow, heading upwards to the right. Its velocity is (x,y), so for every x pixels it moves to the right, it moves y pixels upwards.

We want to rotate the arrow so it sits on the dotted line I’ve drawn from the bottom line. That means rotating the arrow by a degrees to the right.

We know that tan(a) = x / y, so a = atan(x/y)

The code below calculates this and turns the result into degrees. Then it translates to the position of the arrow (remember, you always translate before you rotate), and rotates by -a. (Why negative a? – read here).

function Ballistics:draw()
pushMatrix()
fill(self.color)
pushMatrix()
local a=math.deg(math.atan(self.velocity.x/self.velocity.y))
translate(self.position.x, self.position.y)
rotate(-a)
ellipse( 0,0, self.width ,self.length)
popMatrix()
popMatrix()
end

--update speed
if Ballistics.gravityOn then
self.acceleration = self.acceleration + Ballistics.gravityStrength
end
self.velocity = self.velocity + self.acceleration * t
--set (x,y) position
self.position = self.position + self.velocity * t
--reset acceleration to 0
self.acceleration=vec2(0,0)
end

Then all that’s left is to create some arrows. I have a function that creates a single arrow, using random numbers to vary things slightly. THe arrows get put into a table called arrows.

The draw function adds a new arrow each time, draws the arrows, and if they go off the edge of the screen, it deletes them from the arrows table.

function setup()
arrows={}
end

table.insert(arrows,Ballistics(vec2(50,10+math.random(30,100)),
vec2(.95+.1*math.random(),.95+.1*math.random()),
math.random(225,275),
.01,color(255,255,0),2,30))
end

function draw()
background(0)
for i,p in pairs(arrows) do
p:draw()
if p.position.x>WIDTH or p.position.y<0 then
table.remove(arrows,i)
end
end
end

The code is here