220. World of Warships #3
There is a time when you have to stop adding lots of cool stuff, and tidy up, and include game rules. Also, you always find that you built something the wrong way, and have to tear it down and rewrite it. None of that is much fun.
That’s what I have mainly been doing, but I do also have some fun new stuff to share with you, such as auto-aiming and different camera views. This video shows the result, and I’ll explain it all below
The video shows me exchanging fire with another ship, both of us using automatic aiming.
The guns can only rotate within certain limits, (otherwise they might shoot part of their own ship) – I set a maximum rotation of 140 degrees left or right, and maximum elevation (up) of 85 degrees. You’ll see that in the video, I have to turn the ship quite a lot, until the back guns can aim at the target.
I’ve also imposed a 10 second reloading time (plus a small random time) on all the guns, and included little colour indicators at the bottom right of the screen, which are green if the gun can fire, and yellow if they are reloading.
So when I touch the screen to fire, the program calculates the (left/right/up) angles to the target, and shoots all the guns which are fully reloaded and which can rotate enough to aim at the target.
Exactly the same happens for the target ship, except I have programmed it to automatically fire at me every 11 seconds.
Automatic aiming
To aim at the target, I need two angles
- y axis – the left/right angle to the target
- z axis – the upward angle of the gun turrets
We can use simple trigonometry to calculate the y axis angle, like this
local d=(target-self.pos) --direction vector to target local a=math.deg(math.atan2(-d.z,d.x))-self.angle
If you draw a simple picture, you should be able to figure this out. Note
- we use -d.z because under OpenGL, we face forward into -z, and
- we subtract the rotation angle of the ship at the end
Once I have this angle, I can calculate the rotation angle for each gun. For the guns at the front, this is just the angle without any adjustment, but the guns at the back need 180 degrees added because they are facing the back of the ship. Then I test whether the guns are able to turn this far.
The z axis angle requires one of those fancy trajectory formulae, which I found in Wikipedia. It seems to work quite well, although it seems to overshoot a little. I add a bit of randomness, so the aim varies a bit.
I note that because all the guns use the same y axis rotation, the shells from the front guns land further forward than the back guns. Ideally, the angle should be slightly different for the front and back guns, so they all land on the same spot. I may fix this in future.
Camera views
Different camera views make a huge difference to a game, so I’ve included quite a few, which are listed along the bottom of the screen. Touching one of them changes the view, as shown in the video.
- Behind1 – directly behind the ship
- Behind2 – behind and to the left of the ship
- Bridge – view from the bridge of the ship
- Top – top down view from a long way up
- Scan – continuously rotating view, good for checking for enemies all around
- Target – locks the view onto the current target
- Zoom – zooms in and out
Here is my function that sets up the camera
function SetCamera() if zooming then zoom=zoom+1 if zoom>#viewZoom then zoom=1 end zooming=false end if camView=="Behind1" or camView=="Behind2" or camView=="Bridge" then camPos=S.pos+RotateVector(viewPos[camView],S.angle,0) camLook=camPos+RotateVector(vec3(40,0,0),S.angle,0) viewAngle=0 elseif camView=="Top" then camPos=S.pos+viewPos[camView] camLook=S.pos+RotateVector(vec3(1,0,0),S.angle,0) viewAngle=0 elseif camView=="Scan" then camPos=S.pos+RotateVector(vec3(-40,10,0),viewAngle,0) camLook=S.pos viewAngle=viewAngle+0.25 elseif camView=="Target" then camLook=E[target].pos camPos=S.pos+(S.pos-E[target].pos):normalize()*30+vec3(0,10,0) viewAngle=0 end camera(camPos.x,camPos.y,camPos.z,camLook.x,camLook.y,camLook.z) end
Zooming
First, we test if are zooming. I should explain that you zoom by simply providing a number in the perspective() function, so zooming is as simple as writing perspective(20). The normal default is 45 (the number of degrees covered by the width of the screen), and if you reduce this, you zoom in. I’ve set 4 zoom levels – 45,20,5,2, and touching “Zoom” rotates through these options.
If we zoom, we don’t change our view, we simply zoom in or out.
Ship-based positions
The two “behind” and the bridge options are very simple. I provide a vector which is the position of the camera relative to the ship, and for the bridge it is vec3(12,6,0), which I calculated by trial and error. So I rotate this vector by the angle of the ship, add it to the ship’s position, and that is my camera position.
I don’t want to look directly at the ship, but ahead, so for the “look” part of the camera setting, I add a vector vec3(40,0,0), again rotated by the angle of the ship. The reason this vector only has a number for x, is that the starting angle of the ship is left to right along the x axis (remember the initial picture), pointing towards +x. So the vector vec3(40,0,0) is 40 pixels in front of the ship, before any rotation.
The “Top” option is very similar. The camera position doesn’t need to be rotated left or right, because it is directly above.
Scanning
The “Scan” option is interesting, because we want the camera to swing around the ship, looking over the the top of it to scan the horizon for enemy ships. The camera position is simply the ship position plus a vector rotated by an angle, which increases slightly each frame. Note the camera looks directly at the ship because we don’t need to look ahead, since we are rotating continuously.
Targeting
The “Target” option is also interesting. It’s easy enough to set the camera to look at the target, but I want the camera to be positioned behind our ship, looking over the top of it, looking at the target – this is so I can see the guns firing, and any damage cause by the enemy. So my calculation goes like this
- start with our ship position
- calculate the direction from the enemy ship to us
- normalize it to give it a length of 1
- multiply by 30, which takes the camera 30 pixels past our ship, and then
- add 10 to the y value so we can see over the top of our ship
So if you draw a line from the target to our ship and kept going for 30 pixels, that’s where the camera would be.
And you can be sure that this code didn’t just write itself. It took me quite a while, and I had quite a few puzzling moments before finally getting it to work. I don’t think I have any natural talent for geometry.
Next is a really difficult challenge – deciding when shells hit the target, what damage they cause, and how to show the damage. I have absolutely no idea how to do it, right now.