–
Prototype for a jet ski game
Prototyp för ett vattenskoterspel
Authors:
Joakim Glysing Andreas Hedin Niklas Myrberg Resolution Interactive AB, Umeå Matti Larsson, Resolution Interactive AB Harry Aalto, KTH STH XXXX-XX-XX
Commissioner: Supervisors:
Date of approval:
Final thesis of 10 points in computer engineering
KTH STH
Abstract
The objective of this thesis has been to develop a playable prototype of a jet ski game. This has been requested by the Swedish game developer Resolution Interactive AB. One of Resolution Interactive’s other current projects, Clusterball® 2, was used as the base and together with those responsible at Resolution Interactive, three tasks were decided to be the main objectives for the prototype. The first task was how the jet ski interacts with the water and that it feels realistic to the user. Several physical formulas were implemented and tried in order to make the jet ski float realistically. In the end, Archimedes’ principle of buoyancy was decided to be the best. The second task was to add more details to the water from Clusterball® 2. The biggest improvement came from adding smaller waves to the surface. These waves do not affect the waters physics, only its visual quality. The third main objective was to create a few game modes the finished game could include. Two different modes were chosen; a racing and a stunt mode. These modes were chosen since they will be the most important in the finished game. These three objectives were those planned to be included in this thesis, but during the project, a fourth objective was added. Up until then, an old version of Gamebryo, the graphics engine, had been used. To get support for the latest gaming consoles, this was updated to a newer version.
Sammanfattning
Målet med detta arbete har varit att utveckla en spelbar prototyp för ett vattenskoterspel åt företaget Resolution Interactive AB. Arbetet började med en grund tagen från ett av Resolution Interactives andra pågående projekt; Clusterball® 2. Tillsammans med dem ansvariga på företaget bestämdes tre huvudsakliga mål för prototypen. Det första målet var att få vattenskoterns integration med vattnet och dess körkänsla att kännas realistisk. Flera fysikaliska formler implementerades för att hitta den som gav bäst resultat. Den slutliga versionen använder sig av Arkimedes princip. Det andra målet var att få vattnet, som utvecklades för Clusterball® 2, mer detaljerat eftersom vattnet spelar en så pass stor roll i ett vattenskoterspel. Den största förbättringen kom av de små vågor som lades till på ytan. Dessa påverkar inte fysiken utan är enbart för syns skull. Tredje huvudmålet var att skapa några av de spelsätt som kommer att finnas i det färdiga spelet. För detta examensarbete valdes Racing och Stunts. Dessa valdes eftersom de kommer att vara de viktigaste i det färdiga spelet. Dessa var de tre mål som var planerade att ingå i detta arbete, men under utvecklingstiden lades ytterligare ett mål till. Fram tills dess hade en gammal version av Gamebryo, grafikmotorn, använts men för att få stöd för de senaste spelkonsolerna så byttes denna ut mot en nyare version.
Contents
1 Introduction ............................................................................................................................. 7 1.1 Background ....................................................................................................................... 7 1.2 Objectives ......................................................................................................................... 7 1.2.1 Jet ski physics ............................................................................................................ 7 1.2.2 Water adjustments ...................................................................................................... 7 1.2.3 Game modes ............................................................................................................... 7 1.2.4 Updating the graphics engine ..................................................................................... 7 1.4 Delimitations .................................................................................................................... 8 2 Application environment ......................................................................................................... 9 3 Implementation...................................................................................................................... 10 3.1 Structure and design ....................................................................................................... 10 3.2 Jet ski implementation .................................................................................................... 11 3.2.1 Floating .................................................................................................................... 12 3.2.3 Movement ................................................................................................................ 13 3.2.3 Effects ...................................................................................................................... 14 3.3 Water implementation .................................................................................................... 14 3.3.1 Refraction ................................................................................................................. 15 3.3.2 Reflection ................................................................................................................. 15 3.3.3 Wind driven waves .................................................................................................. 15 3.3.3.1 What is perlin noise? ........................................................................................ 16 3.3.4 Water properties ....................................................................................................... 16 3.4 Game mode implementations ......................................................................................... 16 3.4.1 Racing ...................................................................................................................... 16 3.4.1.1 Mini map .......................................................................................................... 17 3.4.2 Stunts........................................................................................................................ 17 3.5 Updating the graphics engine ......................................................................................... 17 3.6 Camera system ................................................................................................................ 18 4 Conclusions ........................................................................................................................... 19 4.1 Jet ski Physics ................................................................................................................. 19 4.2 Water adjustments .......................................................................................................... 19
4.3 Game modes ................................................................................................................... 19 4.4 Updating the graphics engine ......................................................................................... 19 5 Recommendations ................................................................................................................. 21 5.1 Water adjustment ............................................................................................................ 21 References ................................................................................................................................ 22 Appendix A ............................................................................................................................. A1 Appendix B ............................................................................................................................. B1 Appendix C ............................................................................................................................. C1
1 Introduction
1.1 Background
Resolution Interactive AB has requested a playable prototype for a jet ski game. The finished game will be included in the game series Powersport Challenge (work title). Resolution Interactive is a Swedish game developer with its office in Umeå. Currently the company has 16 employees working on three different games.
1.2 Objectives
The objective of this thesis has been to develop a playable prototype for a jet ski game for the Swedish game developer Resolution Interactive AB. Three main objectives have been requested by Resolution Interactive for the prototype. These are the jet ski physics, adjustments to their current water (used in Clusterball® 2) and the implementation of a few game modes. During the project, a fourth objective was added to the list: replacing the graphics engine used at that time with a newer version. 1.2.1 Jet ski physics The behavior of the jet ski should resemble that of a real jet ski as much as possible. This includes both how the jet ski interacts with the water and how it reacts to input from the user. 1.2.2 Water adjustments The water in one of Resolution Interactive’s other current projects; Clusterball® 2 was used as the base for the water in this prototype. In Clusterball® 2, the water is mostly seen from far above the surface and does not require a lot of details. In this prototype though, the player will be positioned right on the surface and therefore a lot more visual details will have to be added for the water to look good. A higher resolution will also have to be added to the water physics to achieve the realistic result wanted by Resolution Interactive. 1.2.3 Game modes The game modes chosen to be implemented were Stunts and Racing. When racing, the player has to follow a track marked with buoys. Each buoy has a color and an arrow that indicates on which side the buoy should be passed. If a player passes a buoy on the wrong side, they will get punished, for example with a time penalty. The player that finishes a predefined number of laps on the shortest possible time is the winner. The goal in stunt mode is to collect points by doing stunts and tricks. The player with the most points when the time limit has been reached is the winner. 1.2.4 Updating the graphics engine The graphics engine used when the project started was an old version of Gamebryo with no support for the latest video game consoles, like Xbox 360 and PS3. Since Resolution Interactive is aiming at a release for both these consoles; Gamebryo will be updated to version 2.2. 7
1.4 Delimitations
There is a lot that can be added into a prototype like this. Anything that could be in the finished game could possibly be added to the prototype as well. Since there is no time to do all this, a lot of delimitations had to be done. The focus will be on the main tasks described earlier, but since this prototype will be built almost from scratch, a lot more work has to be done to run the prototype at all. The first thing to be delimitated was sound and music. Since this does not contribute to the main features of this prototype, plus the fact that there are no sound or music files to work with; this was an easy delimitation. Other delimitations made right from the start were the possibility for several players to play over a network or Internet. The small waves that would be created when the jet ski is in contact with the water surface were put aside due to the fact that the whole class handling the water surface will be changed later, when the physics engine is replaced. When the trees were added to the prototype the number of frames per second (fps) sank dramatically. This was thought to fix itself when Gamebryo was updated to the latest version, but the speed gained from this change was not as much as needed. As it is now, trees cannot be used and still keep a playable fps. Since it is not a major feature in this prototype, the trees will not be used.
8
2 Application environment
This prototype is written for Windows in C++ and is single threaded. It is built using a code base from Clusterball® 2, another game from Resolution Interactive currently being developed. It uses Gamebryo, an industry standard graphics engine by Emergent Game Technologies, for its rendering. For physics; Meqon’s physics engine is being used. It handles tasks like collision detection and rigid body movement. During the first half of this project, an older version of Gamebryo was used, but has then been updated to a newer version. Since another physics engine; PhysX by Ageia, is included with the newer Gamebryo, it will most likely replace the one currently used. This is not included in this thesis though.
9
3 Implementation
3.1 Structure and design
This prototype is built using a code base from Clusterball® 2 and much of the structure and design from it are left unchanged. Figure 3.1 shows a simplified version of the prototype’s structure where only the most important classes are shown. Application and Game has almost the same functionality as in Clusterball® 2 and much of the code is even the same. Application is the entry point of the application and inherits from NiApplication in Gamebryo. NiApplication has functions for updating and rendering the game world; these methods are overloaded by Application and will be called by the main loop in Gamebryo. This makes Application responsible for the base of the render code and the base of the update code. When rendering Application simply tells Gamebryo a frame has been started and then calls the render function at Game. However since Application is the owner of the Gamebryo renderer it has a function (PartialRender) which renders a node in the scene graph with a specific camera, this function Figure 3.1 is later used by the different components to render themselves. Game is a singleton and the main class of this prototype. It is called from Application to both update and render the game world. When its method for updating is called from Application, the delta time is passed in as a parameter. Delta time is the time it takes to iterate the game loop once. This time value is needed when animating objects. For example; when moving the jet ski. If the jet ski would just move a single unit each frame; then the jet ski would move faster on a computer that can run the game with a higher number of frames per seconds. To prevent this, the delta time is multiplied with the number of units the jet ski should move per second to get the number of units to move the current frame. This means that if the jet ski should move 100 units per second and the delta time is one tenth of a second; the jet ski will move 10 units. If delta time would be half a second instead; it would move 50 units. This way the jet ski always moves 100 units per second and is not dependent on the number of frames per seconds achieved on the current hardware. The delta time is also passed on to all other game objects each frame so that they can be updated correctly. Game is also managing the different game states and the transitions between them. These states define what should be done each frame. If, for example, the state is LoadLevel; the render function will draw the 10
splash screen and then call the functions to load the level. If instead the state is LevelRun; then Game will call Gameplay which in turn handles input from the user and renders and updates the world. The input Gameplay receives from Game is fetched from Gamebryo. Game polls the input handler from its update function and the input handler returns a list with all currently pressed keys. This list is traversed and all keys the application is interested in (like keys for moving the jet ski) is handled while unused keys will be discarded. The input handler is only polled once each frame and only the keys pressed down at that moment are registered. This could lead to a pressed down key getting missed if the user should press a key fast enough to happen between two calls to the input handlers polling function. This is not very likely to happen though, since the polling occurs with an interval of just a few milliseconds. Gameplay is an abstract class and therefore never initialized. Instead, each game mode is its own Gameplay object. Should another game mode be needed; a new class inheriting Gameplay would be created. The Gameplay object has the Level object, a list with the players and each player’s HUD. It also handles all logic and variables concerning the specific game mode. Level has cameras, the water and also all game objects not belonging to a certain game mode. When playing split screen, all players has its own camera and Level will loop through these and render the scene graph, the water surface and the sky box once every frame for each camera. This is done by sending the camera and the object to PartialRender in Application. Since a projection grid is used for the water rendering, and this type of grid is dependent of the camera; the projection grid has to be recalculated between each render pass to fit the current camera’s frustum. The Actor class is the super class for both AI and human players. In this prototype, no AI has been implemented though. This class handles the jet ski object which in turn handles all physics and input concerning the jet ski. Each player has his own HUD which will draw and update 2D elements, like a speedometer and lap times. Every object in the world is placed in a container which contains the graphical representation of an object; these are attached to the screnegraph in Level. It also contains a physics body which is an approximate representation of the object, much less detailed than the graphical representation so that physics simulations can be run in real time; these are put into the physics engine. Game has a list of all these containers and calls update on them. Many objects do nothing in their update function, while others, like the ramps and buoys, do their floating calculations there.
3.2 Jet ski implementation
The biggest task when implementing the jet ski is to make it interact with the water in a natural way. The first thing is to get it to float, but it should also affect the water surface by creating ripples and waves.
11
The jet ski has two important member objects. The first is the physics object which is a part of the Meqon physics engine. This object is used whenever a force is added to the jet ski by Meqon. Since all movement and collision detection is made by Meqon in this prototype; this is a very important object. The second object is a graphical object which is only used to render the visual representation of the jet ski. This object doesn’t know if the jet ski is moving or turning or where in the world it is currently positioned. Therefore it collects all this information from the physics object before being rendered and can be rendered in the right position and with the correct rotation in the world. 3.2.1 Floating If no extra force is added to the jet ski; it is only affected by gravity. In this case the jet ski would fall and to prevent this; an equally strong force has to be added upwards. This will stop the jet ski from falling, but since it should be able to fall when in the air; the upward force is only added when it is on or below the surface. The strength of this force is based on Archimedes’ principle of buoyancy. To create the ability to dive this force is removed when the angle of the jet ski is such that it points downwards in a 15 degree angle. The jet ski will then float back up when it straightens and the force is added again. This solution might not be the best though; at least if used exactly as it is now. At the moment, the jet ski will dive when leaning forward, even if it has no speed. Another problem is that the jet ski dives too deep due to it not straightening out fast enough. Calculating the floating force is done each frame. This means that as many optimizations as possible should be done to these calculations. Often this means that the results are only estimations, but as long as it looks and feels right to the user, this does not need to be a problem. It is important to find the right balance between speed and physical correctness. To optimize in this case, the calculation of the volume does not include the angular direction of the jet ski since the cylinder lays approximately parallel with the water the whole time. One disadvantage to this method is that when the jet ski leans and the cylinder does not, the result will not be entirely correct. The advantage is a much faster calculation of the floating force, which is considered to be more important in this case. The formula for the floating force used is:
Where
is the fluid’s density,
is the volume of the submerged object and
is the gravity
constant (9.82 N/kg) The buoyancy of an object depends on two factors; one is the volume of the object and the other is the density of the surrounding fluid. If the object has a small volume and the density of the fluid is small, the buoyancy experienced will be less, and if the objects density is greater than the fluids, the object tends to sink.
12
Other models for calculating the floating force were also tried. The second best was based on Hooke’s Law. Hooke’s Law is used to approximate the force when a material is deformed.
Where k is the spring constant and x is the distance by which the spring is elongated. Hooke’s law by itself is not good in this case though. A damping must be added or the jet ski will keep bouncing on the surface. The resulting formula after adding damping is:
The new variables are b; which is a damping constant and v; which is the velocity of the object. In this prototype, a spring constant of 200 was used for k, x was the distance between the jet ski and the water surface and v was the velocity of the jet ski. For b; several different values were tried to get a good result. This was one of the problems with this method. The only way to find a good value was to try different ones until a good result was achieved. Even though several values were tried, a really good result was never found; the jet ski always bounced more than desired. When Hooke’s law was exchanged for Archimedes’ principle of buoyancy; the bouncing stopped entirely. 3.2.3 Movement All movement of the jet ski is done by adding a force to its physics object. When the user presses the key associated with acceleration; a call to the jet ski’s method Accelerate is made. This method is only calling Meqon’s function for adding a force to the jet ski’s physics object in the direction it is currently facing. Since Meqon handles all physics for the prototype, no more have to be done to make the jet ski move forward. The jet ski’s method for decelerating (Decelerate) is exactly the same except from one difference: instead of adding the force in the facing direction of the jet ski, it is added in the exact opposite direction. Making the jet ski turn is no more difficult, but a little more work is done anyway. When the jet ski’s method for turning is called; a call to Meqon’s function applyTorque is called to apply a torque to the jet ski around its y-axis. This is enough for making the jet ski turn, but a better result is achieved if the jet ski also decelerates when turning and a call to Decelerate is therefore made. Apart from these forces that are added depending on user input, a few other forces are also applied to the jet ski. These forces are applied each frame during the update phase, whether the application is getting any input or not.
13
The first one is simulating friction and this is made by using an inverted and scaled down copy of the jet ski’s velocity vector is applied to it. This will make the jet ski slow down and eventually stop when no acceleration is applied to it. Making the jet ski stop rotating is made in the same way, but instead of using the velocity vector, the angular velocity vector is used. If the jet ski is only stopped from rotating, this would lead to the jet ski staying turned upside down in most cases. To prevent this, one last force is added to the jet ski. First, the jet ski’s amount of rotation about its z- and x-axis is calculated. This is calculated using the following formula:
α is the rotation angle around the specific axis and v in this example is either the jet ski’s left vector or its direction vector. These are the vectors pointing either down the jet ski’s negative x-axis or down its positive z-axis. u is the jet ski’s up vector and is pointing down positive yaxis of the jet ski, which is always in this prototype. Since the length of u will always be 1; it can be disregarded in the divider. The difference between this angle and the default one is calculated and the inverse of this difference is then used to determine how much torque should be added to the jet ski. This torque is added the same way as when turning the jet ski, except when turning; torque is only added to the y-axis and now it is only applied to x- and z-axis. The y-axis should not be used since this would mean the user would not be able to steer the jet ski since it would always turn back to its default angle again. 3.2.3 Effects A particle system is attached to the front of the jet ski to simulate foam. If the jet ski stops, less foam will be created. Another particle system generates particles when the jet ski hits the water surface to simulate splashes. When the camera is close enough to the jet ski when one of these splashes happens, a texture looking like water on a camera lens is added to create immersion.
3.3 Water implementation
The water in the prototype is only a surface, and not a volume as real water. This means that all physics calculations are only made on the shape and movement of the surface and its waves. Several different techniques are used to create a realistic water simulation. To achieve a good visual result, the most important techniques are reflection and refraction. In this kind of game though; good water physics is more important than high visual quality. The water surface cannot be static and flat if it is to feel realistic. Therefore waves, that simulate the wind driven waves of a real ocean, have been added to the surface. These improve the visual quality of the water, but much more important are the improved physics of the water. Objects floating on the surface will now follow the movement of the waves.
14
Finally, a last type of wave was added to the water. These are the waves that are created from objects interacting with the surface, for example; the waves created from a moving jet ski or the splash when a jet ski falls onto the surface. These waves will also affect all floating objects, just like the wind driven waves. However, these waves were removed again from the game since they were not good enough and since the physics engine will be replaced after the end of this thesis, resulting in all the code having to be replaced as well, this problem was not highly prioritized and finally put aside completely. 3.3.1 Refraction Some problems were encountered with the water early in the development. Visual artifacts were visible when the camera’s position was too low compared to the wave’s height. In Clusterball® 2, this was never a problem since the camera was rarely positioned low enough. The first thing done to fix this was changing the water shader to a newer one written by the team making Clusterball® 2. This improved the water, but the distortion of objects in the water was still too strong which led to artifacts and the refraction strength were therefore reduced. These fixes do not solve the problem completely, but it has improved the water significantly. There is a downside to having a lesser refraction strength though. Apart from decreasing the amount of artifacts on the water, the lesser refraction strength also leads to the water being clearer. This means that when looking down at the water; the bottom can be seen more clearly than what feels realistic. The same goes when the camera is placed below the surface and looking up. With greater refraction strength, everything above the surface would be distorted, but when a lower value is used instead; there is less distortion which gives the feeling of looking through glass rather than water. There is no good solution to this as the water is made right now. The only thing that can be done is tweaking the refraction strength to get a result that is good enough in all cases. A better solution would be to create two refraction passes; one with a low refraction value on all dynamic objects, like the jet ski or the buoys and one with a higher value for the terrain and sky. 3.3.2 Reflection The reflection of the jet ski did not look good when the camera was close to it. This happened because the angle between the camera and the water was too low and therefore produced a reflection of the jet ski at the horizon of the water. Because of this, the reflection of the jet ski objects has been removed. 3.3.3 Wind driven waves The wind driven waves are the larger waves that are used to change the surface of the water. These waves are generated in real time using a perlin noise function and are one of the parts that use up the most time each frame. To increase the speed of this, another perlin noise function was tested. The result was not as expected though; it was not much faster and the visual result was worse. Therefore, the old perlin noise function is still being used.
15
3.3.3.1 What is perlin noise? Perlin noise is a method often used to create natural looking patterns. The idea is to use several different noise functions with different scales and adding them together to create both large and small features. 3.3.4 Water properties One of the first additions to the water was the ability to change the properties of the water in real time; like wave softness, wave height and toggling wireframe on and off. This made it easier to work with the water and adjust its properties. The water generated by the properties from Clusterball® 2 was more like jelly than water and this has been tweaked to a more natural feeling. A good value has been hard to find though since other parts are also affected by it; like the detonations. A higher stiffness to the water gave it a more natural feeling, but at the same time; more power would be needed to make a good looking visual detonation. Since the code for the water has been decided to be remade later, due to changing the physics engine, not much time was put into finding good values.
Figure 3.2
3.4 Game mode implementations
Each game mode inherits from a super class. This class contains some basic features such as changing the play states between Countdown, Game Over and Playing. It also handles input and creation of the players. The subclasses contain all game play logic and rendering of the user interface. 3.4.1 Racing Racing was the first game mode to be implemented. Rules are fairly simple; the player has to finish a number of laps around a track marked with buoys as fast as possible. Each buoy has to be passed at its correct side. If a buoy should be passed on the wrong side, a penalty could be given to the player. To decide whether a buoy is passed on the correct side or not; two planes are created. The first plane is positioned so that it passes through the center of the last buoy and the center of the current buoy. This plane determines on which side of the buoy the jet ski is Figure 3.3 located. The second plane is created perpendicular to the first plane (and passes through the center of the current buoy) and determines when the buoy is passed by the 16
player. First, these planes were created using the buoy after the current one instead of the buoy before. This led to some problems when the there were sharp corners on the track which led to the player sometimes “passing” a buoy while still being far ahead of it. When using the buoy before the active one instead, this problem is eliminated. 3.4.1.1 Mini map The purpose of the mini map is to give the player an overview of the track. The mini map can either be loaded as an image or generated when loading the level. When generating the mini map, each buoys position is scaled down from its world position to fit the size of the mini map. This is done in multiple steps. First, the track’s width and height is calculated. This is the difference between the max and min positions of the buoys in the xand z-axis. Then each buoy’s position is scaled to a value between 0 and 1 by taking its xcoordinate through the track’s width and the z-coordinate through the track’s height. These values are then multiplied with the width respective the height of the mini map. A texture representing the buoy is drawn to the mini map for each buoy at the calculated position. The jet ski texture is not rendered to the mini map at start up as the buoys. This is so because the jet ski icon’s position has to be updated each frame to represent the jet ski’s position. This is done the same way as with the buoys, but with one addition; the jet ski icon is also rotated to match the jet ski’s direction. 3.4.2 Stunts In this prototype, Stunts is far from being completed. Some basic tricks are implemented and can be used by key combinations. These tricks can only be performed while airborne and therefore several ramps have been placed on the level which the player can jump off. In the finished game, Stunts will likely have a track where the ramps are placed, and the player will have to follow this track and do tricks as he go.
3.5 Updating the graphics engine
The change to the newer graphic engine resulted in several problems. A lot of these problems were small and did not take more than an hour or two to fix, but there were also larger problems that required several days of work to be fixed. One of the bigger changes in the new version compared to the old one is the way rendering is done. This meant that all rendering had to be rewritten to work with the new version. This problem was not as difficult to fix as many others, but it took a lot of time to get it working correctly. The probably biggest problem was with the water. After the update, the water grid was full of errors. Since the water was something that had been worked on a lot before the update (and it is such an important part of the prototype) this error was considered very important to fix. Several days of work went into finding the cause of this. In the end, the error was found out to be in one of the functions used to update the grid. This function is built into Gamebryo and is meant to update the old mesh information with new one. For some reason, this did not work, and the function was replaced with a similar one instead. 17
Some classes were completely removed in the new version of Gamebryo. One of these classes was used to render 2D objects to the screen and was a big part of this prototype’s 2D rendering. This together with the fact that the rendering process had been completely changed meant that the whole class in charge of rendering the 2D elements in this prototype had to be rewritten. Apart from these larger problems, a lot of smaller ones came with the update. Many of these could easily be fixed in a few minutes, but some are yet to be fixed. The number of memory leaks increased dramatically after the change, and though some have been fixed, most of them have not. Why these memory leaks suddenly appeared is not known and the reason has not been found even after several hours of searching.
3.6 Camera system
A new camera class was created instead of using the one from Clusterball® 2. The camera inherits from the Gamebryo camera class and uses the Strategy design pattern. Strategy is a good pattern when different behaviors, and an easy way of switching between them, are wanted. Three different types of cameras are used in this prototype. The first is a chase camera, which is the main Figure 3.4 camera used when playing. It is placed behind the jet ski and follows it at all times. The second type is a locked camera, which means it is always looking at the player, but does not follow him. This camera type is good when the player isn’t in control of the jet ski, for example during a replay sequence, since it resembles a TV camera. The third and last camera type will probably never be used in the final game. It is a free camera which can be moved around freely in the world by the user. This camera is only added for use during the development. In this prototype, the player has the ability to change between these different camera types freely while playing. In the finished game, the user will likely be restricted to different chase cameras.
18
4 Conclusions
4.1 Jet ski Physics
The physics model decided to be best suited for the jet ski was Archimedes’ principle of buoyancy. The biggest improvement when using this model instead of the others tried were that the jet ski stopped bouncing completely. It is very possible that the same result could have been achieved with Hooke’s Law for example, but since the constants for this method had to be tested manually; it would probably have taken much longer. Even though the current jet ski physics is good, it is not finished. More work could have been put into this, but during the project this objective was put aside since Resolution Interactive will have two persons working mainly on the physics when this project is over.
4.2 Water adjustments
The adjustments to the water are mostly an increase in detail. To get more visual details on the water; smaller, animated waves, or ripples, were added to the surface. The grid used to render the water surface has also been improved to increase the detail level. At first, each square in the grid was too large, which meant no small waves could be added since the grid could not render them anyway. These squares were made smaller closer to the jet ski to make it possible to add smaller waves, like those made by the jet ski. Several features will still have to be added to the water. This is mainly features connected to how the jet ski affects the water surface. Waves should be created when the jet ski slides through the water surface. Support for this exists, but it will have to be improved a lot before it works as it should. Due to the fact that the physics engine will be replaced when this project is over, and therefore leading to all code related to the water physics having to be remade, this task was put aside.
4.3 Game modes
Two basic game modes have been implemented. Racing works well and is only missing a few minor features like selecting the amount of laps to use before the game ends. Stunts needs a lot more work before completion, but the current version does have the most basic elements such as jumping on ramps and doing tricks.
4.4 Updating the graphics engine
Many new errors were created when changing from Gamebryo 1.2 to Gamebryo 2.2. Most of them are fixed and there are almost no visible differences between the current version of the prototype and the one before updating the graphics engine. The biggest difference between these two versions is the number of memory leaks created when running the prototype. The source of these memory leaks has not been found and since the prototype can be run anyway; other errors have been prioritized higher. Running the game in multiplayer split screen also 19
stopped working due to changes in the rendering system in the update. This is not a big issue since split screen was never requested for the prototype. Another problem since the update is a lower fps. If the fps gets too low, the precision of the physics engine is heavily decreased. Why the fps is decreased is not known.
20
5 Recommendations
5.1 Water adjustment
The current version of this prototype uses a low refraction strength to avoid some of the graphical artifacts that is the result when higher refraction strength is used. Low refraction strength is not only positive though. For best result, two refraction passes should be used, one for the terrain and sky box, and one for objects on the surface, like jet skis and buoys.
21
References
Elias, H. (2000, 2 4). Perlin Noise. Retrieved 5 11, 2007, from The good-looking textured light-sourced bouncy fun smart and stretchy page: http://freespace.virgin.net/hugo.elias/models/m_perlin.htm Emergent Game Technologies. (2006, December 7). Gamebryo 2.2.2 Documentation. Calabasas, California, USA: Emergent Game Techonologies. Winter, E. (2006). Real-Time Water Simulation, An Interactive Ocean Surface. Uppsala: Uppsala Universitet.
Gamebryo documentation, July 2006 (Elias, 2000) (Winter, 2006) // Ta in (Emergent Game Technologies, 2006)te bort!
22
23
Appendix A
Sequence diagram of a render pass when playing the game.
:Application RenderFrame() Render() Render() Render() Render() Render(Camera *pCamera) PartialRender(NiCamera *pCamera, NiAvObject *pObj) :Game :Gameplay :Level :Skybox
PartialRender(NiCamera *pCamera, NiAvObject *pObj)
When Application::RenderFrame() is called from the main loop it starts a new frame and then calls Game::Render(). Game’s rendering method will decides what to render dependent on which state the game is currently in. If the state would be LoadLevel, which means the level is being loaded, nothing would be rendered in Game. In this case though when LevelRun is active, which indicates the game is running, Game will call Gameplay’s render method. Gameplay::Render() will call Level’s render method ones for each player. This is necessary since each player should have the world rendered from his in-game position. Level::Render() will first render the objects not in the scene graph; the water and sky box. Skybox::Render(Camera *pCamera) will receive a camera which it will just pass on to the PartialRender method together with its graphics object. PartialRender activates the camera and renders the object and all of its child and sibling objects. When the water, which is rendered the same way, and sky box are rendered, PartialRender will be called again but this time with the scene graph’s top node. PartialRender will traverse this graph and render all objects in it. When PartialRender returns, the game world will have been rendered. Sequence diagram of how input is handled.
:Gameplay Input from device GetPlayer(0) :Player :ItemJetSki :Meqon
Lean(fDeltaTime, leanVec) Lean(fDeltaTime, leanVec) applyTorque(const &Vector)
The class Gameplay inherits from InputReciever which means it can receive input packages from the input handler. These packages contain information about which keys are being pressed. Gameplay will receive these packages each frame that input is available. The A1
diagram above describes what happens if a user presses one of the arrow keys. Gameplay fetches the Player object and tells it how much to lean in each direction. The Player then calls its method that handles leaning (Lean) which in turn calls the JetSki’s method that handles leaning. It is here that the actual work is done by calling applyTorque in the physics engine.
A2
Appendix B
The screenshots below are taken in racing mode in the latest version of the prototype.
Screenshots published courtesy of Resolution Interactive AB. http://www.resolutioninteractive.com/
B1
Appendix C
An overview of the relevant classes in the prototype.
Application is inherited from Gamebryo’s NiApplication and has overloaded all its rendering functions. It is also the entry point to the application and has the main loop. Game is a singleton and the main class of the prototype and its rendering and update functions is called from the main loop each frame. Game handles the different states the game can be in. These states tell the game if it is currently loading a level, if a level is running or if it is game over, for example. Game owns the Gameplay object.
C1
Gameplay is an abstract class and therefore never initialized directly. Instead, each different game mode inherits Gameplay and implements the necessary functions. All input goes via Gameplay to the correct object. Keys controlling the jet ski gets passed on to the jet ski object and keys for camera control gets passed on to the camera for example. Gameplay is the owner of all the players, and their HUDs, and the level. Stunts is a subclass of Gameplay and only overloads its functions for rendering and updating. Racing is a subclass of Gameplay. Apart from the overloaded rendering and update functions from Gameplay, it also has the mini map and all the buoy objects plus functions for controlling if a player has finished a whole lap or if a player is passing a buoy on the wrong or right side. Level is the class owning the top node of the scene graph. It also has the water, the sky box and the cameras and calls their rendering and updating functions. Actor is the super class for both AI and human players. It is the owner of the jet ski and has functions for updating the position and score for the player. Player inherits from Actor and apart from the inherited functions; it also has functions for handling user input to the jet ski. GameItem is the super class for all game items like trees, jet skis and buoys. It can be implemented on its own but is mostly used as a super class. It owns both the visual and the physics objects for the item. ItemJetSki is a subclass to GameItem. It is the only GameItem object that has functions for adding force and torque to it and by doing so making it move and rotate. ItemBuoy is a subclass of GameItem. The buoys are used to mark the track when racing and apart from the variables inherited from GameItem, it also stores information about which side the user should pass it on. ItemRamp is a subclass of GameItem. Unlike both ItemJetSki and ItemBuoy, it has no extra information that is not in GameItem. The ramps are only used by the jet ski to jump from, and the physics calculations for this are done in the jet ski’s member methods and therefore the ItemRamp needn’t any other information apart from its position and rotation. Camera owns a CameraType object which is the actual camera. Camera is only a higher layer that allows the application to use any CameraType object without knowing anything about it. CameraType is the actual camera. The different types holds information about how the camera should react to player input or jet ski movement. ChaseCamera for example will get the current position and rotation of the jet ski and position itself behind it. The FreeCamera will instead only move on input from the user.
D1
2DStage is the class handling all 2D rendering. It has a list of NiScreenElements, Gamebryo’s class for rendering objects in 2D, and functions to add, position, rotate and scale these objects.
C2
2DStage can be initialized on its own but is normally used as a super class. HUD inherits 2DStage and is used to render 2D text and images to the screen. This is used for speedometers and lap times for example. The different game modes uses different subclasses of HUD. The racing mode’s HUD has information about lap times while stunt mode’s HUD has displays information about the tricks performed instead. Minimap is only used when racing and is a subclass to 2DStage. It shows all buoys’ positions and the jet ski’s position on the track. OceanSurface is the main water class and handles the projection grid, the physics and updating the waves according to the perlin noise. PerlinWaves is the class used to generate the perlin noise used for creating the waves. WaterMirror handles rendering of reflection and refraction.
C3