221. World of Warships #4
This post is about making islands. And about performance.
Making islands
A warship game needs things to hide behind, otherwise it’s boring. So I need some realistic looking islands of various sizes, in random places. I want to keep performance good, so I’ll settle for a cartoony effect.
There are at least two ways to do this, that I can think of.
- create a rectangular mesh for each island, and use noise to create lumps and bumps
- start with a mesh that is the top half of a sphere, and stretch the vertices up and down to make it bumpy
I used the second approach when I was making asteroids, so I started with that, creating a sphere and then discarding the bottom half. Well, that was a mistake, not just because it was messy, but because I had so many vertices that performance suffered. So let’s talk briefly about performance.
Performance
When developing a potentially large app, it is important to keep track of performance, and make sure the frames per second don’t drop below about 30.
When developing, I measure performance with a simple measure
--in setup FPS=60 --in draw FPS=FPS*f+(1-f)/DeltaTime
There are some who prefer to take the average of 1/DeltaTime over several frames, but both methods give similar answers.
I set f according to what I am trying to measure. If I am measuring the effect of things that don’t change a lot, like drawing scenery, I set f=0.95 (say), so FPS changes very slowly, and it will reach a very stable level and not bounce around. If, however, I am measuring the effect of, say, shooting, I set f much lower, so it reacts quickly.
In this case, I have f set at 0.9 while I am developing, and after the latest work, I noticed FPS was down to about 32, which is not good, as I’m not finished adding features. I want it up at about 45-50, at least (on my iPad3).
So I did some testing. I disabled the scenery, because I had just increased the number of islands from 10 to 30, and that might have a big effect. And it turned out to be the problem.
You might imagine that even if you have a lot of islands, that there shouldn’t be a problem if none of them are even showing on the screen. But there is. Performance is affected by the total number of vertices, even if they are currently off screen.
And the issue was that each island had several thousand vertices – I was modelling them in too much detail. I could have reduced the number of vertices to about 900, but I didn’t like the method anyway, so I decided to change to method 1 – using noise to create islands. A big advantage of this approach is that because the island meshes are rectangular, it’s easy to calculate if you’re colliding with them – something I need to manage.
Noise based modelling
I’ve explained how to use noise before, so I won’t do it again.
I vary the size of the island meshes, but they average about 10 vertices across and 10 down, for 100 in total, which requires 600 triangles. I keep all the islands in a single mesh, to improve performance, and I have a table listing all their positions, which I use to prevent my ship colliding with them.
The main design issue is that islands should be tallest in the middle, and slope down toward the edge – I want the slope to be fairly steep, to hide the ship, but not vertical. So I applied a factor to the noise height, that starts at 0 at the edge, and increases to 1 when you get 30% of the way to the middle. This means that the noise function applies normally to the middle of each island, and is scaled down toward the edge.
Another little thing is how you avoid the islands looking rectangular. I do this by making the bottom of the island mesh slightly underwater, say at -1. Then the sea will be drawn around it, covering the bottom of the mesh, and the island will seem to have a curved edge, as seen in the picture at the top.
I want to avoid the islands looking too blocky, given that I haven’t used many vertices, so I used lighting and normals to give a rounded effect. For each vertex, I calculated the normal of all the triangles to which it belongs, and averaged them. Even though the lighting is done in the vertex shader and not the higher resolution fragment shader, it looks pretty good.
All of this means I can run at 60 FPS with 50 islands, which is great.
Collisions
I said above that I have a table of island positions which I use to prevent my ship colliding with them (the ship just stops, and you have to back away).
Seeing the other ship(s) behind islands
I haven’t solved how to decide when one ship can see the other, when an island is between them. This is not a problem for a human player, of course, but if this is a single player game, I have to program an AI ship to be the enemy, and it needs some way of knowing when it can “see” my ship.
The simplest may be to “draw a line” from [some part of] my ship to [some part of] the other ship, and test if the line intersects the island rectangle. This won’t be exactly accurate, but is probably good enough.
A more “realistic” approach is to get the other ship to draw what it sees to an image in memory, and then look for my ship in the image, aka ray tracing. This would be too slow with a full screen image, so ideally you want to restrict the drawing to just the part of the screen where my ship would be drawn (which can be pre-calculated), and use a special colour for the ship so it’s easy to pick out. You might only do this test every 2-3 frames, to reduce the performance hit.
I’ll probably tackle this next, and write about the result after that.
Shooting over islands
Now this is hard. The islands are all sorts of shapes, so if I try to shoot over one of them, how do I decide if it hits the island on the way, or not? I may have up to 8 shots in the air at a time, and I don’t want to be doing lots of testing in every frame.
Hmmm.