A video of what I did is below, but in this post I’ll talk more generally about how you might build an app like this.

(The red icons are births, the pairs of figures are marriages, and crosses are deaths).

You can use mapping for all sorts of things, but I suggest that Codea is best for showing animations, such as changes over time, because you are redrawing the screen all the time.

This part is easy. I just took a screen cap of a Google maps page, and used a picture editor to cut out the part I wanted.

Now suppose I have a lot of data I want to show on my map. In my example at the top, it would be a list of dates and places, like this

Births 1710 Sheepdip (yes, this is a place!) 1711 Plymouth .. Marriages 1719 Black Torrington ..

So my first problem (after collecting all my data) is to figure out the pixel positions of all these places, on my map image.

The first time I did it exactly the wrong way. I opened my map image in a picture editor, made a list of all the places I needed, and then looked for each one in the map image, and made a note of the x,y pixel position.

This is not only slow, but what happens if I decide later that I want to use a different map image? I have to start all over again.

A better approach is to use the longitude and latitude of each place, and find a database with all of these listed. So I looked online and found a big file with all the English towns listed. I checked it against my list of places, and there were a couple that weren’t there (in real life, there are **always** some that aren’t there, no matter what you’re trying to do), so I either changed my place name to one that was in the file, or else added my place name to the file after looking up its latitude and longitude on Google.

Just one more problem. I need to know the latitude and longitude of each corner of my map image as well – actually, just two opposite corners will do – so I carefully measured the bottom left and top right corner, on the original Google map page (if you click on a Google map, it will show the latitude and longitude at the bottom of the page). Now it’s a math calculation to figure out where each place is, on my image map.

The advantage of this method is that it’s not only quicker, but if I want to change my map image, all I have to do is measure the longitude and latitude of the corners of the new map.

Now I can program my Codea app to draw the map image. I probably have to size it to fit the screen, and it won’t be the exact shape of the iPad screen, so I need to do something like this. It will fill the screen as much as possible, keeping the map in proportion to its original size.

local w,h --the actual size we will draw the map if map.width/WIDTH > map.height/HEIGHT then --map is flatter than our screen w=WIDTH --use the full width h=map.height*WIDTH/map.width --restrict height else --map is taller than our screen h=HEIGHT --use the full height w=map.width*HEIGHT/map.height --restrict width end

Then I loop through my data – in my case, I am showing a set of years, one by one, using a simple timer to tell Codea when to go to the next year, every few seconds.

My code decides which items to show – in my example, I show births in the year they happened, and they stay on the map for the next 5 years, so the viewer gets a chance to see them properly, and the icons I show on the map shrink in size during that 5 year period.

The math to calculate where to draw each icon is a little tricky, because we know the longitude and latitude of the place where we want it drawn, and we know the longitude and latitude of the corners of our map, and we can calculate the pixel corners of our map image (they won’t all be at the corners of the Codea screen, because our map isn’t the same shape as the Codea screen – see the video at top, where the map is taller, so we have to resize it, leaving a black border at the left and right) – and then we have to put all this together to calculate the screen position. It’s not difficult math, though.

There is one problem with drawing icons on a map, which is that if you have 5 of them in one place, you will only see one, because they are drawn on top of each other. This is especially important for my deaths, because I leave the little crosses on the screen forever. I got around this by adding a small random amount to the x,y position, so the icons were drawn slightly separately.

And that’s all there is to it. You can do some great animations with Codea, pretty simply like this.

]]>

If you are fairly new to Codea, this may help you understand things like tables a little better.

You use a joystick (up/down/left/right) to steer a snake (green squares) around the screen, trying to touch the food (red square). Each time you touch the food, your length increases by 1, and new food appears. If you go off the edge of the screen, you lose.

This used to be a popular game on old “feature” phones, you know, those really old ones with about 10 pixels on the screen, because this game didn’t use many pixels. In fact, the snake was made up of 1 pixel squares.

If you did that with your iPad, you wouldn’t see the snake, it would be so tiny, so we have to make it bigger. We’ll pretend the iPad screen is made up of little squares, each 20 pixels wide. The snake and food will also be squares which are 20 pixels wide.

Now, when you touch the joystick controls, the head of the snake needs to turn left, right, up or down, and the rest of the snake will follow.

So suppose the snake is 4 squares long, and is going from right to left. Then we turn down.

The picture shows how the first square goes down and is followed by the other squares. But the other squares don’t all start going down immediately – they follow exactly the same path as the first square, so the last square (number 4 in the picture) goes left 3 squares before it starts going down.

How do we do this? How do we get the squares to remember the path that the first square took?

The answer is amazingly easy, and it’s probably why this game was made originally, because it is so simple.

If you want to test yourself, have a think about it before reading the answer below.

Our program will have a list of the positions for the squares of the snake. So suppose the list for the picture above looks like this, before we turn down the screen:

(200,100), (220,100), (240,100), (260,100)

You can see the x values of the squares are 20 pixels apart, so they are in a row next to each other (as you see above).

Now we turn down. The list for the second picture above will look like this. The first square has gone down 20 pixels and the other squares have moved left

(200,80), (200,100), (220,100), (240,100)

and the list for the third picture will look like this (the second square has now started going down)

(200,60), (200,80), (200,100), (220,100)

Can you see the pattern?

The first item in each list is the new position of the head of the snake.

To get the positions of the rest of the snake, you take the list for the previous move, and move them all one to the left, because the second square is moving to where the first square was, the third square moves to where the second square was, and so on.

So if you have a four-square snake with a list of positions (a,b,c,d), and the snake moves to e, then the list of positions will become (e,a,b,c). This means you add the new position at the front of the list, and delete the last item on the list.

This does exactly what we need.

And it gets better, because there is an easy solution for our next problem. How do we add a square to the tail of the snake if it eats some food? Should it go above, below, left or right of the end of the snake? The obvious answer is that it should go where the tail of the snake has just been, ie the empty square where it was one move ago. And this is incredibly easy to do.

When we are adding a new position to our list, we look at whether we ate food, and if we did, we don’t delete the last item on the list (but if we didn’t eat food, we do delete it). This effectively adds a new item on the end, exactly where the tail of the snake was one move ago. Perfect.

If we moved the snake by one square (20 pixels) every frame, it would get across the whole screen in about a second, which is way too fast. We need to slow it down.

We can do this by using a counter that increases by 1 at each frame. Then, every (say) 10th frame, we move the snake.

Adjusting every 10th frame means the picture only changes 6 times a second, and it will look jumpy. I’ll fix this below, but first, let’s program it like this.

Here is my setup function

function setup() speed=20 --a bigger number makes it slower and easier size=20 --size of each squares (pixels) --start our snake in the middle of the screen -- // is integer division, ie WIDTH/2//size=math.floor(WIDTH/2) --we do this because the snake must be exactly on a square start=vec2(WIDTH/2//size,HEIGHT/2//size)*size S={start} --table holding snake squares --JOYSTICK SETUP CODE GOES HERE (we'll look at this later) AddFood() --add a food item --set the direction we are moving (nothing at first) counter=0 end

And here is my draw function. When you start out programming, you will probably have most of your code in the draw function, and it starts getting very messy. It’s a good idea to put the code into separate functions, making it easier to debug.

function draw() background(150, 192, 209, 255) DrawSnake() DrawFood() DrawJoyStick() end

Here is the DrawSnake code – this is the important function. See the numbered notes underneath

function DrawSnake() --update snake position counter=counter+1 if counter%speed==0 then --[1] local p=S[1]+direction*size --[2] if p.x<0 or p.x>WIDTH or p.y<0 or p.y>HEIGHT then --[3] --LOSE GAME - NOT PROGRAMMED end table.insert(S,1,p) --[4] --eat food if S[1]:dist(food)<0.1 then --[5] --UPDATE SCORE -- NOT PROGRAMMED AddFood() --create new food somewhere else else table.remove(S,#S) --[6] end end pushStyle() --[7] fill(75, 140, 72, 255) for i=1,#S do --[8] rect(S[i].x,S[i].y,size) end popStyle() end

[1] counter%speed = remainder of counter/speed. It will be 0 if counter is an exact multiple of speed, so if speed=10, then this will give an answer of 0 when counter =10, 20, 30,…. So this is an easy way of doing something every 10 frames.

[2] I haven’t shown you this yet, but touching the joystick gives you a direction vector, Left is (-1,0), right is (1,0), up is (0,1) and down is (0,-1). We multiply this by the size of our squares, and add it to the position of the first item in our snake list.

[3] if we go off the edge off the screen, we lose. I didn’t program what happens if you lose.

[4] if we are still on the screen, we insert our new position p into the first place in our snake table.

[5] if we are very close to the position of the food (the dist function calculates distance in pixels between two points), then we ate the food. I didn’t program the score, but I add new food somewhere else.

[6] I delete the last item in the snake table, but only if we didn’t eat food (as explained above).

[7] whenever I change colours, I put pushStyle() first, and popStyle() when I’ve finished. This makes Codea save the previous settings and put them back afterwards.

[8] now I draw the squares of the snake using a for loop (#S is the number of items in the table S)

Below is my function for adding food. I’ve included it to show how I can make sure it is not too close to the player, and it is not too close to the joystick controls. As with the snake, the food position needs to be a multiple of the square size (ie 20), so I start by calculating how many squares there are in the width and height, choosing one at random, and doing my checks. If it’s too close to the snake or the joystick, I do it again, and break (ie exit) if everything is OK.

function AddFood() while true do --keep looping until we get a position we want --positions must be a multiple of the size, so --first figure out how many we can fit into the screen local w,h=WIDTH//size,HEIGHT//size --choose one that isn't on the edge food=vec2(math.random(2,w-1),math.random(2,h-1))*size --make sure it is further than 50 pixels from our snake head --and also that it is not inside our joystick if food:dist(S[1])>50 and food:dist(joyCentre)>joyLength then break end end end

Finally, the joystick code. This code goes in setup. What is all this? I set up a table with 4 items, one for each of the paddle arms. Each of these 4 items is a table, made up of the centre position of that joystick arm, its length and width, and the direction to go in, if that arm is touched.

joyCentre=vec2(WIDTH-140,140) --centre of joystick paddle joyWidth,joyLength=40,80 --length and width of paddle arms joyArms={ {joyCentre-vec2(joyLength,joyWidth)/2,joyLength,joyWidth,vec2(-1,0)}, {joyCentre+vec2(joyLength,joyWidth)/2,joyLength,joyWidth,vec2(1,0)}, {joyCentre-vec2(joyWidth,joyLength)/2,joyWidth,joyLength,vec2(0,-1)}, {joyCentre+vec2(joyWidth,joyLength)/2,joyWidth,joyLength,vec2(0,1)} }

The reason I do this here, is that it makes the touch code much simpler.

function touched(t) if t.state==ENDED then --if we have finished touching for i=1,4 do --check if we touched each arm local j=joyArms[i] --just to make the next line shorter --check if we touched inside this arm if math.abs(t.x-j[1].x)<j[2] and math.abs(t.y-j[1].y)<j[3] then direction=j[4] break end end end end

Drawing the joystick is also simple. I’m lazy, and I draw the 4 arms as two thick lines instead of 4 rectangles.

function DrawJoyStick() pushStyle() stroke(86, 133, 199, 255) strokeWidth(joyWidth) line(joyCentre.x-joyLength,joyCentre.y, joyCentre.x+joyLength,joyCentre.y) line(joyCentre.x,joyCentre.y-joyLength, joyCentre.x,joyCentre.y+joyLength) popStyle() end

So far, our program looks like this. Can we make it less jumpy?

The answer is yes.

Instead of just jumping from one square to the next, we can interpolate, so the movement is smooth. We will still only eat food or change direction every S frames, where S is the speed we have chosen, but we will move the squares in between.

This is easy to do. Imagine we have a 4-square snake with positions (a,b,c,d), and it is going to move to e (it doesn’t matter whether this is in the same direction or not).

In the program above, we would wait for S frames (where S is the speed), and then change the list to (e,a,b,c).

What we will do now, is to add e to the front of the list, and delete the last item. Then when we draw, we use the counter to interpolate. So if the speed is 10 (ie it takes 10 frames to move one square, or 20 pixels), the position of the first snake square will be as follows for the next 3 frames

frame 1 = e*1/10 + a*9/10 frame 2 = e*2/10 + a*8/10 frame 3 = e*3/10 + a*7/10 .... frame 10 = e*10/10 + a*0/10

So we calculate each square’s position by interpolating between its previous position and its next position. It means one small change to our initial table, so instead of S={start}, we have S={start,start}, so we have two positions to interpolate between, even if they are the same at the beginning.

This is the code for drawing the snake

local f=(counter%speed)/speed --fraction for i=1,#S-1 do local x,y=S[i].x*f+S[i+1].x*(1-f),S[i].y*f+S[i+1].y*(1-f) rect(x,y,size) end

Note how my loop stops one before the end of the table S. That’s because I included an extra item in it at the beginning for interpolation. The first snake square interpolates between the first and second item, the second square interpolates between the second and third list items, and the last snake square interpolates between the second to last and last list items.

And when you do all this, you get the result shown in the video at the top of this post.

Here is the final code. I’m not sure the joystick code is working perfectly, but I mainly did this post to show how to program snake, so I’m not going to go back and look at it.

I hope you enjoy it, and maybe try changing it.

]]>

It turned out harder than I expected, but that’s good. I learned something from doing it, and I hope you will find it interesting, especially if you haven’t used Codea for very long, because there are some useful techniques.

This is quite a long post, but that’s because I am explaining everything. If you don’t have time to read it all, or it’s too basic for you, skip to the headings marked with an asterisk *, which have the most interesting stuff.

The first problem is swiping – well, not the actual swiping, but figuring out whether our swipe hit an object, and where it hit it, so we know how to slice the object. Fruit Ninja just seems to break all the fruit exactly in half every time, but we’re going to slice more accurately, so if you just want to break a corner off, you can.

Drawing the actual swipe

Let’s start with the simplest problem – drawing the swipe on the screen as we are doing it. I’m going to draw a yellow line that follows my finger as it swipes.

Obviously, we’re going to use the touched function for this, and I’ll keep a little table that stores all the finger positions as I move my finger. Something like this.

- when the touched state==BEGAN, put the current touch position in the table
- when the touched state==MOVING, add the current touch position to the table
- when the touched state==ENDED, clear all the items out of the table

And this is how the code looks

```
function touched(t)
if t.state==BEGAN then
tp={vec2(t.x,t.y)} --tp is table of touch positions
elseif t.state==MOVING then
local v=vec2(t.x,t.y)
table.insert(tp,v)
elseif t.state==ENDED then
tp={}
end
end
```

Then, in my draw function, I’ll draw a line between each pair of points in this table, creating a line, when the finger is swiping. (If the finger is not swiping, then table tp is empty, so nothing is drawn).

--in the draw function stroke(234, 229, 31) strokeWidth(5) for j=1,#tp-1 do line(tp[j].x,tp[j].y,tp[j+1].x,tp[j+1].y) end

How do we know if we cut a fruit?

We could decide that if our finger just touches an object, then it breaks in half, but I want to be able to slice accurately, so I decided that I have to swipe right through an object before it splits.

This means I need to know the point where the swipe enters the object, and the point where it exits the object.

The line between them is where I’ll cut the object, as shown at left, where the green line is the swipe, and the red line is how I want to cut the object.

So I need to detect

- when the swipe enters an object, and
- when the swipe exits an object

We can’t figure out if we are entering an object until we know what our object looks like. Well, they are just rectangular pictures, which are added using the function below.

First, I choose a random image for my object, from a little table I read in beforehand. These are just some pictures from the libraries provided with Codea. I store this image in img, and I store its width and height in w and h. Then I choose a random position above the top of the screen, and store that in pos.

The next 5 lines create a mesh, which in this case is just a set of six vertex positions that make two triangles in the shape of our rectangle. If you haven’t used meshes, don’t be put off, they are not difficult, and they are very important if you want to program any interesting graphics.

Finally, we add all this information to the fruit table, putting it in a little table of its own, with all the items named. Note that I have a speed item, which we will use to adjust the position, and a rot number which will rotate the object as it falls.

```
function AddFruit()
local i=math.random(1,#images)
local img=images[i]
local w,h=img.width,img.height
local pos=vec2((0.2+0.6*math.random())*WIDTH,HEIGHT+100)
local m=mesh() --create mesh
m.vertices={vec2(-w/2,-h/2),vec2(w/2,-h/2),vec2(w/2,h/2),vec2(w/2,h/2),
vec2(-w/2,h/2),vec2(-w/2,-h/2)}
m.texCoords={vec2(0,0),vec2(1,0),vec2(1,1),vec2(1,1),vec2(0,1),vec2(0,0)}
m.texture=img
m:setColors(color(255))
table.insert(fruit,{m=m,pos=pos,w=w,h=h,img=img,angle=0,rot=1,
speed=vec2(0,-2),replace=true})
end
```

When I draw, I update the position and rotation, and draw each object. If it falls off the bottom, I delete it, and add a new item at the top again.

```
for i,f in pairs(fruit) do
f.angle=f.angle+f.rot --rotate
f.pos=f.pos+f.speed --move
if f.pos.y<-50 then --off the screen, so..
AddFruit() --add a new item at the top
table.remove(fruit,i) --delete this one
else --draw it
pushMatrix()
translate(f.pos.x,f.pos.y)
rotate(f.angle)
f.m:draw()
popMatrix()
end
end
```

Note that when you are drawing rotating objects, you first need to translate to the object position, then rotate it. This is because Codea always rotates around (0,0), and we want to rotate the object round its centre, so we have to make that centre (0,0), which we do by translating there. (The pushMatrix function stores the settings before I translate and rotate, and popMatrix puts them back the way they were afterwards).

The function below tells me whether a touch point p is inside a rectangular object f (which is one of our falling objects, with a position pos, width w and height h). Nothing clever here.

```
function IsInsideFruit(f,p)
if math.abs(p.x-f.pos.x)<f.w/2 and math.abs(p.y-f.pos.y)<f.h/2 then
return true
end
end
```

In my touched function, I loop through all the falling objects, checking whether the current touch position is inside them (using the function above). The code below sets an entry item to the current touch position, when the swipe first enters an object, and it sets an exit item when the swipe leaves the object again.

--f is the object --tv is the touch point (x,y) if IsInsideFruit(f,tv) then f.enter = f.enter or tv elseif f.enter and not f.exit then f.exit=tv BreakFruit(i) --split the object end

The IsInsideFruit function assumes our object is the right way up. But it isn’t. We’re rotating it. So how can we tell if our touch point is inside a rectangle which is at an angle?

We could figure out where the four corners of the rectangle are, and then use some fancy math to decide if our touch is inside it. But there is a much simpler way, if you can get your head around it.

The image on the left shows a rotated rectangle, plus a blue point that is outside and a red point that is inside it. If we rotate the rectangle and the two points back to an upright position, then we can use our IsInsideFruit function to tell us if the two points are inside the rectangle or not. This is easy, because we know the angle of the rectangle, so we need to rotate by the negative of that angle.

We don’t need to rotate the rectangle, because we already know we’re going to have an upright rectangle. All we need to do is rotate the touch point, by the negative of the rectangle angle, using the code below. To rotate a point needs the sin and cos of the angle, and because we are rotating around the centre of the rectangle, we need to

- subtract that centre position from the point we are rotating (so that (0,0) is now at the centre of the rectangle),
- rotate the point, and then
- add back the rectangle centre again

```
function Transform(f,p)
local r=math.rad(-f.angle)
local s,c=math.sin(r),math.cos(r)
local pp=p-f.pos --translate to centre of rectangle
return vec2(pp.x*c-pp.y*s,pp.x*s+pp.y*c)+f.pos
end
```

So before I test if my touch point is inside an object, I first have to rotate it by the negative of the angle of that object.

This is the most interesting part of the project.

It is quite tricky, because the cut can be made in various ways, leaving us with two odd shaped objects. After thinking of different methods, I eventually decided this was the best.

I calculate the average of the entry and exit points, simply because I know this is inside the rectangle, and it is on the line joining the two parts I want to split, so I can use it as a central point for both parts of the split object.

In this diagram, we have the rectangle, and a red line showing the split. I calculate the centre of the red line, shown by the red dot, and connect it to all the corners to make triangles. The corners which are above the red line go into one part, and those below go into the other, making two separate objects, made up of triangles in a mesh.

When I create the triangles, I need to ideally go anti-clockwise around the points in each of the two meshes, which means I need to sort them somehow. I decided to do this by calculating the rotation angle of the line from the red dot (alongside) to all the points, which I can do with the atan function.

So if my red dot is at (-5,25), and I want the angle to a corner at (-100,100), I subtract the red dot position to get (-105,75), and calculate the angle as atan2(75,-105). If the angle is between the angles for the two cut points (the left and right of the red line), that point goes into the first object, otherwise it goes into the second object.

So this is my process

- create two lists, one for each of the two parts of the object
- calculate the average of the entry and exit points
- calculate the angle of the entry and exit points, measured from the point in 2
- add the entry and exit points (and their angles) to both lists
- loop through the 4 corners, and calculate their angle. If it falls between the entry and exit points, add the point to the first list, otherwise add it to the second
- sort each list by angle, from smallest to largest
- now loop through all the points in each list, creating a series of triangles
- create two meshes from these triangles
- add them as falling objects, giving them opposite rotation and direction, so they spin away from each other
- delete the object that was split

And all of that gives you the video at the top.

The full code is here.

]]>

So

- when I’ve done all the fun design and problem solving and learning, and now I have to make it all work, and
- when I know I’m making it only for myself, because the code is becoming too complex for anyone else to use, if I shared it, and
- when it gets harder and harder to add features because they interact with other parts of the program, making them difficult to test and debug, and
- it’s feeling more and like a chore

that’s when I usually stop.

For me, the challenges in this project were the initial model design, calculating damage based on the angle at which a shot hit the target tank, and calculating how visible a tank was, when hidden behind trees or buildings. And I’ve done them all now.

I could carry on, and have tanks chasing me round the map and shooting at me, but that’s a lot of work for something nobody is going to play. Also, it’s been two months on one project, and I want a change. I think what I’ll do instead is pull the things I learned out of this project, and put them in one of my ebooks, so they can be used by anyone who needs them.

That may include a little program that includes my tank model with simple controls to operate it, so it can be dropped into a 3D scene. If that isn’t too hard to do.

]]>

In my last post, I found a way of getting a list of tiles that are in the line of sight between two tanks. Now I need to create some “trees”, enter their positions in the tile table, and modify my code so it doesn’t just get a list of tiles, but looks at which trees are included in each tile.

So in the video below, I have my two tanks, and all the trees are drawn in light green. I run my code to get a list of the tiles I need to inspect, get a list of the trees in those tiles, and shade them in darker green. So as I move one of the tanks around the screen, the line between the tanks will move too. Any tree that is even partly in a square that is crossed by the line, should be shaded darker. And that is what you see. So that seems to work.

Now I’ve put the “list of tiles” code into my tank project, and used it to get a list of trees that need to be tested for blocking the view.

At first, it seemed to give me a random list of trees all over the map, and I spent a couple of hours puzzling before I realised that I sort the trees by distance from the player at each frame, and that messed up the order of the trees. I stored an ID number for each tree, which never changed, to fix this. I did it wrong, and spent some more unhappy hours wondering why my tank wasn’t hidden when it should be.

Anyway. Here is a video of another tank looking at my tank, and all the trees that need to be checked are shaded orange. For this test, I spaced all the trees evenly over the map, and made them small so I could always see the other tank. You can see the tree colours changing as the other tank moves around.

This is tricky because the other tank could be at any angle, and it is not square, so if it is looking straight at us, it will be narrower than if we are looking at it from the side.

I didn’t try to solve this in the last post, but had an idea for doing it. And I think it works, like this. Suppose tank A is looking at tank B, and we want to know how wide B looks.

- When building our tank model, create 4 “corner” positions, which are the outer edges of the body.
- When we want to figure out the width of B, first rotate all four positions by the current angle of the (body of the) tank, so we know where they are, for B
- Subtract the position of A from each of them (which gives us the direction from A to each corner of B)
- Calculate the angle (in radians) of each direction (x,0,z)
- Calculate the smallest and largest angle, which gives us the width as an angle, in radians

So if tank T is looking at the two sides of tank A in this drawing, the angle made by the line between T and each side can be converted into an angle. We can then calculate the angles from tank A to each side of all the trees we need to look at, to see if the angles overlap.

But there was a problem with this method, because the atan function I am using to calculate angles, flips from positive to negative at 180 degrees, and this makes a mess of my comparison of angles. So in the end I found a better alternative, and a nice trick.

The built in angleBetween function will calculate the angle between two 2D direction vectors, which don’t have to be normalised first. We are effectively working in 2D, because our tanks can only look around (and move) on a flat surface, so I can use this function.

First, I calculate the direction from tank A to tank T. This will be our baseline direction against which everything else will be measured. Then I calculate the directions from tank A to the two sides of tank B, and use angleBetween to calculate the difference in angle from the baseline.

T = position of tank that is looking A = position of other tank A1 = position of left side of tank A A2 = position of right side of tank A baseline = A - T angle1 = baseline:angleBetween(A1 - T,baseline) --eg -0.05 angle2 = baseline:angleBetween(A2 - T,baseline) --eg 0.10

So tank A is at an angle between -0.05 and +0.10 radians of the line from T to A.

Now suppose I have a tree, and I calculate the positions of its left and right edges, then use angleBetween again to get the angles in the same way as I did for tank A. I can now test whether the range of angles overlaps, eg if the tree angles are -0.14 to -0.02, then the tree blocks the view of tank A between -0.05 and -0.02.

As discussed in the last post, I need an easy way of deleting the overlapping part, and the best I could think of was to split the range for tank A into (say) 20 pieces, so for the range above, it might be -2.15, -2.1475,-2.145,…-2.10. Then I test each of these points against the range of the tree, and if any of them fall inside the tree range, I delete that point from the table.

And finally – below is the result. The trees that are being tested are shaded orange, and the visibility percentage is shown at upper left. Note this shows how much of our tank can be seen by the other tank.

The trick

Oh, and I did say I found a nice trick. Or rather, yojimbo2000 did. Here is a version of angleBetween that is 2-3 times faster, as long as you don’t mind passing through the x,y position values separately instead of in a vector. It’s very neat when you think it is calculating the difference between two angles with just one trig function.

--angle between (x1,y1) and (x2,y2) in radians function AngleBetween(x1,y1,x2,y2) return atan2( x1*y2 - y1*x2, x1*x2 + y1*y2 ) end

ss

]]>

This means you rarely have to worry about speed, in most projects. But if you push Codea so hard that performance falls below 60 frames per second, you may need to improve it. And because some things run faster than others, there are some tricks you can use, as shown below.

This surprising advice comes from someone who should know – the main developer of Lua, the language behind Codea – and many other experts agree.

What they mean, is that rather than fiddling about with optimising your code itself, you can usually get much better improvements by changing what your code is doing.

For example, if I am drawing 1,000 spaceships on the screen at once, and I am using readImage in the draw loop to get them from disk, I will get much better performance by reading the image into memory in the setup function, and spriting it in draw. If I want even better performance, I can use put the spaceships into individual meshes, and to improve it yet again, I can put them in a single mesh. Obviously, you need greater skills to do these things, so you should try to learn as much as you can, especially from other people and their code.

Similarly, if you are working in 3D, you may have hundreds of trees or other objects in a scene. Normally, you can leave it to OpenGL to culling (ie ignore) objects that are out of view of the camera, but if performance is suffering, there may be something you can do. Although OpenGL is quicker than Codea, it has to look at every vertex one at a time, whereas we can look at the whole object to decide whether to draw it or not. So if we have an object with 100 vertices, we can save OpenGL 100 vertex checks by testing if it is in the field of view or not. Even billboard objects, with only 6 vertices, are worth checking for visibility in this way.

So optimising what work you do is probably going to give you much greater improvements than optimising how you do it.

But if you’ve done all that, and your program still isn’t fast enough, read on.

Codea’s built in functions are held in a table named _G. Looking them up takes time and “localising” them can improve performance dramatically.

Suppose we have some code that uses math.sin and math.rad frequently.

s = math.sin(math.rad(angle))

We can make it run much faster (about 3x faster on my iPad!) by putting this “localisation” code first

local sin,rad=math.sin,math.rad

and then using this code for our calculation

s = return sin(rad(angle))

This is because of the way Lua stores references. Local variables can be looked up much more quickly than global variables.

Important note – there is no point “localising” every time we calculate sin, because Codea has to look up the math.sin function in _G before it can localise it. *So you don’t save any time by just using it once*. The trick is to localise once, and then use the local value many times.

So the best place to put the localisation code is often at the very top of the code tab where you are going to do the calculations (or maybe just before the function that is going to use the localisation). Then the localisation lookup will only be done once, when Codea starts.

Does the same applies to the functions we write ourselves, since they are also stored in the global table? I tested a function that used sin and rad, to see if it made a difference if it was put into a different tab to the code that used it, and whether it made a difference if it was localised, and the result was that it didn’t seem to make any difference at all. Go figure.

Lua has several shortcuts which are faster than math functions. These shortcuts date back to the early days when there were no such things as math libraries, and one character function codes saved space.

Each of these shortcuts is up to 2x faster

13.5//3 = 4 instead of math.floor(13.5/3) 3^4 = 81 instead of math.pow(3,4) 13%3 = 1 (remainder) instead of math.modf or math.fmod

But this one is about the same speed

4^0.5 = 2 instead of math.sqrt(3)

These results vary for different iPads, as you will see below.

If you have a table T with items T[1], T[2], etc, then for i=1, #T do is more than twice as fast as for i, j in pairs(T) do.

If you are inserting items in a table T, then T[#T+1]=a seems to be about the same speed as table.insert(T,a). However, other testers have reported that the first method is faster.

The usual advice that multiplying is faster than dividing doesn’t seem to apply. There isn’t a significant difference.

I carried out tests on many built in functions, using an iPad Mini 1, and an iPad Air 2. The results are given below, as the number of times each function can run in one frame, which is 1/60 of a second. The bigger the number, the better.

Please note the speeds are only approximate, so ignore small differences.

Shortcut functions

First, the shortcuts I talked about above. This table compares them with the built in functions, for the two iPads, and then I compare the two iPads.

Trigonometry functions

This time, I’ve compared the two iPads.

Other math functions

Vector functions

All figures are for vec3, ie (x,y,z)

Can you see how slow the vector functions are, compared to the other functions? (of course, they are still very fast, considering these numbers are for just 1/60 of a second).

The relative slowness is because of the overhead of creating the vector, as Codea’s developer, Simeon, explains

The problem is that vec2 (and friends) are Lua “user data” objects, which are basically Lua-managed interfaces to C code and data. The problem is that performing operations on them needs to return a result, which needs to ask Lua to create a new user data. This causes Lua to allocate memory with its memory allocator. And that’s the source of the performance difference.

So I tried doing the calculations without vectors by “decomposing” (ie splitting) the vectors into three numbers and doing the math separately on each of them. So v1+v2 becomes x1+x2,y1+y2,z1+z2.

And this is the result, which is quite dramatic.

It means we can improve basic vector calculation speed by 5-6 times by decomposing the vectors. There isn’t much improvement for the length/distance functions.

The problem, of course, is that the main reason we use vectors is to make our code tidy, use the special vector functions, and avoid having to do calculations for x, y and z separately. We can possibly reduce the clutter by writing functions for our decomposed functions, like this

--normal vector code v1 = vec3(x1,y1,z1) v2 = vec3(x2,y2,z2) v3 = v1 + v2 --decomposed addition x3,y3,z3 = x1+x2, y1+y2, z1+z2 --using a function for the decomposed addition --to make the code a little more readable x3,y3,z3 = VecAdd(x1,y1,z1,x2,y2,z2) function VecAdd(x1,y1,z1,x2,y2,z2) return x1+x2, y1+y2, z1+z2 end

However, you still have to manage 3 times as many variables, and writing functions can halve the speed.

It seems that unless you abandon vectors completely and just work with x,y and z values (which could make debugging much more difficult), the best way to take advantage of the greater speed of decomposed vectors might be where you are doing a lot of calculations with the same vectors, in which case you can decompose them into x,y,z to start with, and then do all the calculations very quickly. This is similar to the suggestion for localising functions earlier, ie it’s not worth doing unless you have a lot of calculations to do.

I’ll end by repeating the main points

- Codea is extremely fast, so you will rarely need to worry about performance
- Start by trying to reduce the work done by Codea, before you look at the code you are using
- Localise, localise, localise
- Use shortcut functions
- Think about the options for vector functions,

and, of course, save up for a faster iPad!

]]>

Suppose I have two teams of tanks, with maybe 3-5 tanks in each team, and only one is a human player, all the rest being bots. So I have 5-7 bot tanks running around trying to see and shoot each other.

How do bot tanks **see** enemy tanks? It’s not easy. But this challenge is the main reason I took on this project, and it hasn’t disappointed me. It’s really interesting.

As I think I’ve already said, you can send out “rays” in all directions and see what they hit, but that takes a lot of processing power. So you need to cheat as much as possible.

If you think about it, we can split the work we need to do into different “layers”, like this.

- for each tank, find the enemy tanks which are in its field of view (usually 45 degrees in front of it)
- for each enemy tank, find the trees and buildings that might be blocking the view
- for each tree or building, figure out which part of the enemy tank it is blocking
- calculate how much of the enemy tank is visible
- decide if our tank can see it or not

This is fast and easy, using the culling method I explained in the last post.

We could have hundreds of objects in our scene, and if each of 7 tanks has to check all of them a couple of times (once for each enemy tank in its field of view), the work adds up quickly, even if it is fast. It helps if we can ignore objects that are nowhere near the field of view, so we don’t even bother testing them.

Grid / Tilemap

A simple way to do it is to split the scene up into a grid of squares (or “tilemap”, if you prefer), and have a table that tells us which objects are in each square. So when I am looking from tank A to tank B, I only have to worry about objects in the tiles between A and B, and I can ignore all the others in the scene.

Suppose I have a scene that is 2000 x 1800 pixels. I can split it into squares of 40 pixels square, which will give me 50 x 45 squares. I create a 2D table, to hold all the objects in each square. This table is only used to make quick decisions about which objects might be blocking the view from one tank to another.

So when I create my trees at the start, I also need to figure out which square they belong to, and add them to the table, something like this.

--create the grid treeGrid={} --to hold lists of trees in each square trees={} --the actual trees for i=1,200 do --add 200 trees --create tree in random position, choose one of the tree images randomly local pos=vec3(math.random(1,2000),0,math.random(1,1800)) local img=math.random(1,5) trees[i]={pos=pos,img=img} --store tree data AddToGrid(i,pos) --see below end

What does AddToGrid do? I haven’t included the code, but it figures out which square the tree will be drawn in, and adds it to the list for that square. So if tree number 83 is being drawn at (1321, 0, 1512), I can calculate the table position, using integer division, so

1321//40 = 33

which is the same as, but faster than, math.modf(1321/40)

1512//40 = 37

and set treeGrid[33][37]={83}

If tree 147 is also drawn in that square, then treeGrid[33][37]={83,147}, and so on

So treeGrid has a list of all the trees drawn in each square.

There is just one complication. Trees can be drawn anywhere, so they may overlap two or even four different squares. However, it is easy to test which squares a tree overlaps, because it is a circle (remembering that it rotates to face the camera). You simply take four points on the edge of the circle (north, south, east and west), and calculate which square each point belongs to. Then you include the tree in each square (in treeGrid) that it overlaps.

Which squares need to be checked?

Suppose tank A is looking at tank B. Which tiles need to be checked?

The problem is that if you draw a line between the positions of A and B, it probably will look something like this, cutting through the corners of some squares.

How can you get a list of the squares that the line passes through?

Well, as usual, some very clever people have spent a lot of time developing methods for this, and I borrowed one that seemed to work quickly (it still took me two days to find it, though). It starts at one end of the line, then figures out where the line first reaches the edge of a square, and adds that square to the list. Then it figures out where the line hits the next square, and so on. It is here if you want it.

So I get a list of squares like this

And after all of this work, I can look at tank B from tank A, and quickly get a list of squares I need to check, and look in treeGrid to find out which trees are in those squares. I will end up with a list like this: {23,27,35,57,132,162,13}, which means I have to look at tree #23, tree #27, and so on.

I still have to decide whether I need to do the list twice, because if tank B is side on, it may be quite wide, and just basing my list of squares on its centre position may not be accurate. I may need to do the list twice, once for each side of the tank, and combine the lists. I need to test this.

All of this has been difficult enough, but when we look at whether a tree is hiding the other tank, it gets really tough.

Here we have tank A looking at tank B, around a tree which is partly blocking the view.

- How much of tank B can be seen?
- And what if there is another tree blocking even more of the view?
- And the width of tank B (as seen by A) will depend on its rotation angle.

Like I said, this is tough.

And even if we solve it, we need to make it fast as well.

I’m going to avoid complicating things, by assuming that tanks can’t see through, or over or under trees, so the full width of the tree will block the view.

Tangents

First, I need to draw a line from A to the edge of the tree on each side. The area in between is blocked by the tree.

The line from a point to the two sides of a circle is a tangent, and where it touches the edge of the circle, it forms a right angle with the centre of the circle, like this.

It takes a bit of work to find tangent points, so I wondered if there was a faster approach,

and there is,

If I take the direction from our tank to the centre of the circle, and find the points on the sides of the circle at 90 degrees to that direction, as shown alongside, it’s much quicker, and in most cases, is accurate enough for me.

It’s quicker because of a very neat geometry trick. If the direction from my tank to the tree is (x,0,z), the circle centre is at c, and the circle radius is r, then the two points are at

- left hand = c + r * (-z,0,x)
- right hand = c + r * (z,0,-x)

And the result looks like this. Note how it’s pretty accurate (the red lines don’t go inside the circle much unless you very close, when it probably doesn’t matter much.

Width of the other tank

I’m going to leave this for now, but for example, I could calculate it by taking the four corner positions of the target tank, and testing which was furthest to the left or right, from the viewpoint of our tank, to get the left and right positions and therefore the width.

Yes, but how does this help?

I’ve thought about all the different ways I could use the information above to figure out how much of the other tank is hidden.

And I think this is what I’m going to do.

I’m going to calculate angles. Yes, angles.

The direction from my tank to the two circle tangents, and to the two sides of the target tank, can be expressed as an angle, which I can calculate using atan (which is very fast).

I’ll calculate the angle for the left and right of the target tank, and split it into (say) 20 parts, and put them into a table. So if the two angles are 0.5 and 0.7 (in radians), then my table will look like this: {0.5, 0.51, 0.52,… ..0.69, 0.7}.

Then for each tree, I’ll calculate the angles for the two radians, eg 0.45 and 0.55, and test my table of points against this range. Any point that falls into the tree range will get removed from the table, so in this case, the points 0.5 to 0.55 would get removed.

Then I test the next tree the same way, and when I’m done, I count the remaining points in my table and divide by 20, and that is the % of the tank which is visible.

I don’t really like having to do 20 tests per tree, but if I’m clever and make sure my list of trees is in order from closest to furthest, then the trees that are most likely to block the other tank will be looked at first, and my table may shrink very quickly. I also have the option of using my fast culling approach from my last post, to decide if trees even need to be tested at all.

Once I have this working, I can use the % visible statistic to decide if a tank can be seen. For example, I might say that it can be seen is visibility exceeds 20%, unless the other tank is moving, in which case it is only 10%. Or if I want to be clever, I could say that if we are looking straight at the other tank, it only needs to be 20%, but if it is at the edge of our vision, it needs to be 40%.

But first I have to make all this work. That’s the boring part, programming and debugging…

]]>

We are in a 3D scene, but everything is on a flat surface, so we move around in two dimensions (this is sometimes referred to as 2.5D).

Our camera is looking along the surface, as shown by the blue arrow. The dotted blue lines show the “field of view”, ie what the camera can see.

At the left, there is a tree, which is a 2D image that rotates to face the camera.

We need to know if the camera can see it. If you rotate the tree through 360 degrees (on the vertical y axis), it forms a cylinder with a circle at its base, with a radius of half the width of the image. If that circle is inside the “field of view”, then our tree can be seen. So we are going to work with this circle and the field of view, to find an efficient test for whether they overlap or not.

At the point where the circle first touches the left hand side of field of view, the dotted line is a tangent, ie the line is at right angles to the centre of the circle, as shown on the left.

If, given camera position and a circle, we can calculate the tangent point where the line from the camera just touches the circle, at right angles to the centre, we can then test if this point is inside the field of view or not.

However, there are two tangent points, one on each side of the circle. We want to use the one that is closest to the field of view.

Figuring all this out could cost us quite a few lines of code.

But would you believe everything can be done with just three (3) lines of code?

And here is the first line.

p = circlePos+cameraDirection*(radius/math.sin(math.rad(FOV/2)))

It looks like Greek, I know, so I’ll use a picture to explain. Stay with me, though, it’s not hard when you “get” it, and the result is magical.

We’ll start with the circle just touching the field of view, at a right angle to the circle centre.

We draw a line (marked d alongside) from the circle centre, in the same direction as the camera (straight up in this case), up to where it crosses the dotted line marking the edge of the field of view.

We’ll call the angle between the blue lines a (which is half the field of view). Using geometry, the angle between the green and (dotted) blue line at top left, must also be a, (because the dotted line touches the circle at right angles).

How long is the line d? Well, if the circle radius is r, then sin(a) = r / d, so

d = r / sin(a)

And that’s where the formula above comes from. It gives the position of the point p at the end of line d. And the line running through the tangent from the camera to the edge of the circle will always pass through p as well.

But what happens when the circle is not touching the edge of the field of view? We still calculate p exactly the same way, but now it won’t be touching the field of view – it will be inside or outside. So now we need to test this, which can be done quite easily using a dot product.

v=(p-camPos):normalize() if v:dot(camDir)>cosFOV then visible=true end

We normalize the direction from the camera to p, then calculate the dot product, which gives us the cos of the angle between them. We can compare this with cos(a), and if it is greater, it means p is inside the field of view, and visible.

There are two tangents where the camera touches the circle. Which one do we use?

Amazingly, we don’t need any code for this.

When we calculated the point p, we used the camera direction, and *this will always point us toward the tangent that is closest to the field of view*, so we don’t need any extra calculations.And that is a major reason for calculating p in this way.

My thanks to forum user yojimbo2000 for this awesome shortcut, which I would never have thought of in a million years.

We’re nowhere near done. This function needs to be very fast, and even if it’s only three lines, there’s a lot we can do to speed it up. And I guarantee you will be surprised by what works best.

So here is our visibility function.

```
--cameraDirection must be normalised
--FOV is field of view (default is 45)
function IsVisible2(circlePos,cameraPos,cameraDirection,radius,FOV)
local p=circlePos+cameraDirection*(radius/math.sin(math.rad(FOV/2)))
local v=(p-cameraPos):normalize()
return v:dot(cameraDirection)>math.cos(math.rad(FOV/2))
end
```

If you have a lot of objects to test for visibility, like all the trees in my tank scene, then you’ll quickly realise that there is no point calculating the sin and cos of FOV/2 for each object, because it’s going to be the same for all the objects. So we should “pre-calculate” all the items that won’t be different between objects, giving us something like this.

```
--precalculated global variables are
--radiusAdjust = 1/math.sin(math.rad(FOV/2)))
--cosFOV = math.cos(math.rad(FOV/2))
function IsVisible2(circlePos,cameraPos,cameraDirection,radius,FOV)
local p = circlePos + cameraDirection*radiusAdjust
local v=(p-cameraPos):normalize()
return v:dot(cameraDirection)>cosFOV
end
```

That makes a big difference to speed, but would you guess I can still speed it up by a factor of three (3) ?

One reason is that the normalize function involves a square root. We could make the function faster by not taking the square root, and comparing the squares of the results.

So our code will change

--instead of local v=(p-cameraPos):normalize() --which is the same as local v=(p-cameraPos) v = v / v:len() --which is the same as v = v / (v.x*v.x + v.y*v.y) ^ 0.5 --we can use v = v * v / (v.x*v.x + v.y*v.y) --ie everything is squared --and compare with the square of cosFOV return v:dot(cameraDirection)>cosFOV*cosFOV

(and we can pre-calculate cosFOV*cosFOV to make it a little faster still).

Now the surprising thing is that we aren’t done, even though our code looks like bare bones, using optimised built in functions.

It seems vector math is slow.

Really quite slow.

If we decompose the vectors into ordinary variables as shown below, the speed improves dramatically. Note that

- I’m breaking the vectors into x,y values. Normally, a 3D surface will have y as height, so the surface will use the x and z axes. But I was using 2D code for testing, and it is easy to change.
- When you only compare squared values, you get a second (and incorrect) “visible” result when the circle is directly behind the camera. So I’ve included an initial test for this (it makes things a little slower, but for any trees behind the camera, it will save us calculating tangents etc, so it may even be faster on average)

--these global variables are pre-calculated --tangentAdjust = 1 / math.sin(math.rad(FOV/2)) --cosFOV2 = square of math.cos(math.rad(FOV/2)) --camposX, camposY = x,y values of camera position --camdirX,camdirY = x,y values of camera direction function IsVisible(pos,radius) local px,py=pos.x,pos.y --if circle is behind the camera, it's invisible local dx,dy=px-camposX,py-camposY if dx*camdirX+dy*camdirY<0 then return end --calculate the tangent point & subtract camera position local u=radius*tangentAdjust local ptx,pty=px+camdirX*u-camposX,py+camdirY*u-camposY --(squared) length of this direction local sq=ptx*ptx+pty*pty --numerator of dot function local a=ptx*camdirX+pty*camdirY --compare squares of the results --we multiply by sq on the right instead of dividing by it on --the left, as multiplying is faster return a*a>cosFOV2*sq end

The result is blazingly fast. On my iPad Air 2, I can do 18,500 visibility tests per frame, for objects in front of the camera, and if they are behind the camera, I can do 30,000 tests. By comparison, the original 3 line function I started with above, could only manage 4,500 tests per frame! That’s a 4x speed improvement.

And the video below shows that it works. The yellow circle brightens when IsVisible is true. The green line shows the tangent.

Normally, vectors perform pretty well. Why is it possible to improve on them in this case?

Simeon, developer of Codea, explains it like this.

The performance difference appears to be due to allocations, every vector mult / sub / add has to allocate a new vector object as Lua user data to return its results. The overhead in the allocations accounts for all the difference in performance.

This problem exhibits itself because the vectors are short-lived and created / deleted constantly. Using vectors in a more long-term scenario should be totally fine (the overhead will not really be noticeable without lots of operations).

]]>

Initially, I’m going to have the enemy tanks drive around in random directions. Obviously, I don’t want all the tanks to drive through each other, or to drive off the edge of the map, so I need to program some behaviour for them.

Like this (a test with many tanks so there are lots of collisions).

(I realise the other tanks don’t avoid my tank, and drive straight through it, but that’s because I still have to program special behaviour for that, like shooting at me!).

Keeping them on the map is pretty easy. I just check if they are getting close to the edge, and if they are, I make a large random turn left or right.

I had to think hard about avoiding collisions, however, and I looked up some articles on “steering behaviour”.In the end, however, I created my own collision test. Normally, I wouldn’t do this, because I am never going to write a better algorithm than the best of the internet, but in this case, I wanted to minimise the work involved, since I am going to have to test every tank against every other tank, 60 times a second.

And I think the result is quite interesting.

First, although the tanks are rectangular, I am going to treat them as circles (as suggested by several sites) because that makes testing much simpler. For any given tank, I’m going to start by calculating the distance to every other tank. If a tank is within a certain minimum distance, then I need to check if they are going to collide soon. But I will only do this check for one tank – the closest one.

This approach means I only need to do a single distance calculation for each pair of tanks, and if I have any tanks close by, I’ll just look at the closest one in detail.

Assuming I do have a nearby tank I need to check for a collision, and there are many alternatives, including

- checking whether the line made by the direction our tank is going in, will pass inside the circle of the other tank
- calculating where our tank’s direction comes closest to the other tank’s position (which means calculating the shortest distance from a point to a line) and whether this is less than the tank’s circle radius
- calculating the direction required to miss the other tank and checking that our direction is outside it

But they all require quite a few calculations, and I want to minimise the work.

I remembered something I had done a while back when figuring out whether a shot fired from a laser would hit a 3D target. I simply calculated the distance to the target, and then multiplied that distance by the direction of my shot and added the result to my current position. This tells me exactly where my shot will be when it gets to the same distance as the target. Then it’s easy to calculate the distance between that “hit point” and the target, to see if it is close enough to hit.

I can do much the same thing here. I already know the distance d to the other tank, and I know the direction I am going in, so I can multiply them (and add my current position) to get the position of my tank when it has travelled a distance of d. If this “hit point” is inside the circle of the other tank, then I need to turn to avoid a collision.

Before we do that, I should answer an obvious question. The other tank is moving too, so shouldn’t I allow for that in my calculations? Possibly, but since we can adjust our position 60 times a second, and the other tank will take avoiding action too, there is no real need.

How much should I turn? Clearly, it should be in a direction away from the other tank, and how much I turn should depend on how big the collision is likely to be. But because I am doing this test many times per second, I have many chances to turn, so I only need to turn a small amount each time, and I chose 0.5 degrees.

I just need to know one more thing – whether to turn left or right, and I found a formula on the net that tells me whether my “hit point” is to the left or right of the other tank’s position, so I can then turn in the same direction (away from the other tank).

All of this means that checking for collisions mainly requires one distance calculation for each pair of tanks, and if a collision is likely, I only need a few extra arithmetic calculations.

And the video at the top, filled with colliding tanks, seems to show it works quite well.

The full collision code is given below.

function Tank:CheckForCollisions() --look for tanks within collision distance (collisionRange) --initialise variables to hold details of the closest tank --minDist holds shortest distance so far, we aren't interested --unless it is within collision range, so start with that local minDist,minP,minT=collisionRange,nil,nil for _,t in pairs(Tanks) do if t~=self then local d=self.pos:dist(t.pos) if d<minDist then --this tank is closest so far local p=self.pos+d*dir --calc "hit point" if p:dist(t.pos)<Tank.radius then --we will hit! minDist=d --store distance to tank minP=p --and the "hit point" minT=t.pos --and the other tank position end end end end if minT then --we need to turn to avoid a tank --this formula is positive if our "hit point" is --on the left of the other tank if (minP.x-self.pos.x)*(minT.z-self.pos.z) -(minP.z-self.pos.z)*(minT.x-self.pos.x)>0 then self:rotate(0.5) else self:rotate(-0.5) end return true end end

]]>

There’s a video at the end of the explanation.

The simplest type of game would simply record a hit and give each tank (say) three lives before it dies.

World of Tanks is much more complex. Hits can damage tracks, cause fires, disable the engine or gun, or knock out the driver, gun loader, etc. The damage also allows for the angle at which shots hit the armour, and the thickness of the armour, which varies.

This part is easy. Typically, the armour is much the same thickness all the way round except for the front (and maybe also the whole turret), which tends to be about twice as thick. So I’m simply making the forward facing triangles, and the turret, twice as thick as the others.

The greatest damage is caused by hitting armour head on, at right angles. The least damage is caused by hitting it a big angle – in fact, in World of Tanks, I understand that shots start bouncing off when the angle exceeds 70 degrees.

So how does the angle affect damage? It’s actually very simple.

The picture below shows a sheet of (grey) armour with thickness **t**, and on the left, a red shot is hitting it vertically, at right angles to the armour.

How much armour does it travel through before it reaches the inside (the line marked **e**)? The answer is obviously **t**.

Now look at the shot on the right, which hits the armour at an angle a from the vertical. Clearly, the shot has to travel further before reaching the inside. We can calculate the distance **e**, knowing that cos(a) = t / e, so e = t / cos(a). I’m calling **e** the “effective” thickness of the armour.

Here is the best part. When I test my triangles to see which one was hit, the first thing I do is use the dot function to test if they are facing toward or away from the shot (and ignore them if they are facing away). And do you know what the dot function is, assuming you have normalised the directions you are comparing? It is the cos of the angle between the directions, which is the angle a in the picture above. So I can calculate the effective thickness very simply, as the actual thickness, divided by the result of the dot function.

Both in World of Tanks and in real life, you’ll see advice to angle your armour, often at 30 degrees to the enemy (that means, from the enemy viewpoint, your front armour is angled at 30 degrees, and your side armour at 60 degrees). If you apply the thickness formula above, this means the effective thickness of your front armour is 15% bigger, and that of your side armour is twice as thick. So, you can “increase” the armour thickness of the front, and although you are partly exposing your weaker side armour, the big angle doubles its effective thickness to be as large as the front armour, and there is also a good probability any shot at the side armour will bounce.

I answered this a couple of posts ago, using some fancy math I found.

I can calculate

- what distance the shot travelled
- which part of the tank was hit (body, turret, or tracks)
- which triangle was hit
- the angle at which it hit

The amount of damage is calculated using distance, angle, and actual armour thickness.

I did some reading, which is a fun part of this kind of project, and learned all kinds of interesting but irrelevant things, but when it came to damage, the answer always seemed to be “it depends on [a big list of things]”. Shots that penetrate may spray fragments or bounce around inside and kill everyone, or hit the ammo and explode the tank, or hit the engine but not hurt anyone, or go straight through the tank without damaging anything, or ….[many other things].

I’m going to keep things simple, and borrow some ideas from World of Tanks. Something like this.

```
Part of tank hit Damage type Effect
Front plate Visibility Worse
Left side plate Accuracy Worse
Right side plate Ammunition Slow reloading > Explosion
Back plate Engine Speed reduced > Can't move
Tracks Speed Speed reduced > Can't move
Turret Turret Rotation Slow > Can't rotate
```

I didn’t explain that for penetration testing, I made a table of vertices for each of the body, turret and (the bounding box for) the tracks. I can easily add a table which tells me which damage category each triangle falls into, so when I know where a shot hits, I can just look up the damage category, adjust it randomly, and apply the damage.

So the damage system looks something like this:

- Has the shot hit a tank?
- If it has, find the triangle that was hit, the distance travelled, and the angle
- Adjust the penetration power by the angle and distance travelled
- If it penetrated the armour of that triangle, look up the damage category
- Apply damage based on that category

So now I’ve programmed all that, and below is a video where I shoot at all the sides and different parts of a tank, showing the results on screen. These results include

- distance to the tank,
- the angle at which the shot hit,
- the combined effect of those two factors on the power of the hit,
- the actual armor thickness
- the damage category

Now I can use this to set up a health system, and adjust speed, accuracy, etc based on hits.

But the really hard part is coming up. Getting the enemy tanks to drive around and then “see” the player and shoot at him.

]]>