Yokho's Adventure

3D OpenGl game with complex game mechanics and graphics

Project Background

This game was developed as part of the Advanced games technology module at City University London The project focused on the game design elements rather than on graphics (mechanics, logic, AI etc.)

Story & Gameplay

“Yokho’s adventure” is a classic “third person”, Role-Playing Game (RPG). The main objective of the game is to find the bunny by exploring the cursed forest. Unfortunately, the bunny was captured by an evil monster (Revenant) and his followers (Imps). To lure out the Revenant, our hero (Yokho) must slay his imps, by using her mighty sword and spells. Luckily, other heroes left several potions and invisibility gems in the forest which aid Yokho.

The player starts in the middle of a dark forest, surrounded by Imps and several power-ups. Yokho has three different attacks that aid her in the fight, a melee sword attack, an area-of-effect (AOE) attack that freezes the enemies, and a ranged, fire ball attack that explodes when hits the target damaging the enemies nearby.
In order to attack an enemy, the player has to target them first (Tab), except when the frost nova is used. The targeting system always chooses the closest enemy. The power-ups grant extra mana or health for Yokho, and the invisibility gem makes the player invisible for the monsters for a short period of time.
The objective for the game is to free the bunny from the boss enemy (Revenant) that appears after Yokho kills 10 imps. Once the player kills the boss, the bunny appears on its dead body.

Implementation

Player attack mechanics

Sword attack (melee): If the targeted enemy is close enough, deals 15 damage / swing
Fireball (ranged): Ranged attack, always hits the targeted enemy as the direction of the fireball is updated at every frame. When the fireball reaches its target, it explodes and damages other enemies nearby. If any of the damaged enemies were frozen by a nova, the debuff will fell off.
Frost nova (ranged): A very low damage AOE attack. Its purpose is not to kill the enemies but to freeze them. When the enemies are frozen, they take extra damage from other attacks (fireball, melee), however fireball will remove this debuff.

Enemy attack mechanics

Imps: Imps only have a single melee attack, however if the player gets too close (20 metres) they will chase him till they die, or the player gets further than 25 metres.
Revenant: The revenant also has a melee attack which deals 25% more damage than the imps’. It also chases the player in 20 metres, however the player must be at least 35 metres from it to switch back to patrol state.
Enraged: The imps at 30% and the Revenant at 50% health switch to Enrage state. In this stage, they will chase the player until one of them dies. The enemies also gain extra damage to their attacks (+25%) and to illustrate this change, the sizes of the model and the animations are also changed (increased size, and attack animation instead of walk).
The Revenant also starts to use his other attack, the felball. This spell has a 5 second cooldown, and it always targets the player. If the ball hits the player directly, it deals 50 damage, but since the direction of the felball is not updated, the player can avoid the direct hit. However, once the ball’s velocity gets close to zero, it explodes, and If the player is too close to the explosion, a burn debuff is applied on him that deals damage over four seconds.

Power-up mechanics

Mana potions grant 20 mana for the player, health potions add the same amount to the player’s health. Since there is no mana or health regeneration in the game, these are essential for beating the boss.
The invisibility gem makes the enemies ignore the player for 5 seconds. During this time, the player can freely move between the enemies, and if any enemy was already chasing him, they will return to their patrol states. However, if an enemy was in enrage state earlier, it will return to that, instead of staying in patrol state.

Modelling

Basic models

There are five different primitive based objects (plus the particles of the particle emitter). The health and mana potion are using the same primitive based shape and normal vectors, however different textures are applied on them. They are created by a square based pyramid on the bottom, and another, smaller one upside down on the top. The textures are loaded only once for each potion types, and at rendering the correct texture is used to the potion.
The invisibility pick-up is also created by square based pyramids, however with different size, normal vectors, and textures. The current target marker for the player is also a square based pyramid, pointing down to the current target.
The grass in the game was created by two crossed rectangles, and on each rectangle, on both side a grass texture is rendered with transparency. Finally, there is one more object, a tetrahedron (random pick-up). The original idea was to have a pick-up that can be health / mana / invisibility, but later by design decision this was excluded from the level. It is, however still in the source code, and for testing purpose there is one rendered above the scene.

Mesh based objects

There are three animated and seven static meshes in the game: All meshes have a wrapper class to render, update and apply logic on them. The trees and grass are generated by using the Vegetation class. The textures of the five different types of trees and two types of grass patches are only loaded once at the beginning of the Vegetation class, to increase performance and reduce the necessary memory for loading the textures. The type of the tree object is randomly generated at creation and in the rendering the type is used to choose which texture to render.

Heads-up display

The game has two types of negative effects, (one for the player and one for the enemies), plus a positive effect for the player, these are all displayed as small icons with their remaining time. The textures were downloaded from the internet from other games, such as Dota, and World of Warcraft, and a red / green stroke was added in photoshop for negative / positive effects.

The heads up display (implemented in the HUD class) includes two unit frames, one for the player (always visible) and one for the current target (if enemy selected). The textures for all unit frames were created in Adobe Photoshop, by making screenshots of the particular model, and adding it to the unit frame. The mana and health bars are rendered as quads, and their current length is calculated by looking at the player’s and enemies’ maximum and current mana or health. The lengths is then used for the vertex coordinates of the bars.

Camera, Lighting, FX

Camera technique

The target was to achieve a modern RPG game style control, where the player controls the character with the keyboard and the mouse together. The player is always rendered at the view point of the camera, and the mouse movement rotates the camera position around the view point. By using the scroll, the player can zoom out / in, between a minimum and maximum distance. In the same time with the ‘W’, ’A’, ’S’, ’D’ keys the player can move the character to different directions. The most challenging part was to avoid the camera position going behind the ground texture (face vertex mesh), and to keep the same distance between the position and the view point, unless the player uses the zoom. This required a ‘Y’ coordinate checking in every function where any movement was calculated. The view point’s (player’s position) ‘Y’ coordinate is also adjusted by the face vertex mesh, to keep the player on the top of the mesh.

Lighting

There are six different lights (two static, 4 dynamic) in the level, all controlled by the Lightinghandler class. The lighting class has pointers for the player and the enemies and the position of the dynamic lights are updated by calling the Lighting class’ Update() method.

Special effects

Cross-fading: Cross-fading effect was used when the player takes damage. The CrossFade class sits inside the Player class, and when the Player/TakeHit() function is called, it activates the effect.
Fog: When the boss appears in the level, fog effect is applied on the scene by using OpenGL’s GL_FOG with an exponential function and grey colour.
Transparency & Blending: Transparency and blending were used to achieve realistic grass textures for the grass meshes. The textures are rendered to quads however OpenGL’s blending function was enabled to only render the greenish part of the textures. The leaves of the trees were rendered with the same technique however, an alpha test was also required for the correct result. Furthermore, in case in case of the grass, both sides of the quads are rendered.

                glCullFace(GL_FRONT_AND_BACK);
                glEnable(GL_TEXTURE_2D);
                glBindTexture(GL_TEXTURE_2D, texture->m_textureID);
                glEnable(GL_BLEND);
                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                glAlphaFunc(GL_GREATER, 0.1f);
                glEnable(GL_ALPHA_TEST);
                glBegin(GL_QUADS);
            
Finally, transparency and blending were used at the frost nova spell of the player that renders a sphere with increased radius.
Explosions: There are two types of explosions used in the game, one for the fireball and one for the felball explosions. Both use the SpriteExplosion class, however the fireball keeps looping through the textures until it reaches its target (or the ground), making the explosion look like a moving fireball. To achieve this, a new parameter was added to the Initialise function (loop), which is set to true for the fireball but false to the felball explosions.

Particle effects: Particle effects are added to the game, leaving a smoke trail after the player’s fireball. The SmokeParticleEmitter class had to be modified, so the positions of the new particles are following the fireballs path (which was also changing since the targets are moving, and the fireball follows the targets). To achieve this result, at the initialisation of the emitter, a pointer to the target is sent to the class, and in the update method the direction of the trail is updated by calculating the vector towards the target’s current position.

Physics, AI, Gameplay

Physics

Realistic physics was applied on the felball to achieve a bouncing effect. When the boss fires the felball, an instantaneous angular acceleration is applied on the body, which creates a rotating effect around the x axis. The balls initial velocity is decreasing by time, and once the ball stops moving, it explodes. In order to shot the ball in a curve rather than in a straight line towards the player, 0.3 is added to the direction.

                camera = cam;
                CVector3f targetPos = camera->GetViewPoint();
                //playerPosition = playerPos;
                targetPos.y = targetPos.y + 1.0f;
                origin.y = origin.y + 3.0f;
                m_position = origin;
                active = true;
                
                CVector3f direction = targetPos - m_position;	// Direction camera is facing
                direction.Normalise();
                direction.y += 0.3f;
                
                // Initialise all physical variables
                m_acceleration = CVector3f(0.0f, -19.8f, 0.0f);
                m_instantaneousAcceleration = CVector3f(0.0f, 0.0f, 0.0f);
                m_velocity = (m_acceleration + m_instantaneousAcceleration) * 0.00001f;
                m_angle = CVector3f(0.0f, 0.0f, 0.0f);
                m_angularVelocity = CVector3f(0.0f, 0.0f, 0.0f);
                m_angularAcceleration = CVector3f(0.0f, 0.0f, 0.0f);
                m_instantaneousAngularAcceleration = CVector3f(150.0f, 0.0f, 0.0f) / m_rotationalInertia;
                m_contactTime = 0.0f;
                // Set the ball to the current camera position
                m_instantaneousAcceleration = (250 * direction) / m_mass;
            
The power-ups, trees, enemies, player and the fel- and fireballs all have their own bounding boxes, which are checked against each other in the CollisionHandler class. In case of collisions certain effects are applied to the base objects, depending on the type of the objects (Player can’t move through trees, fireballs explode when they reach an enemy etc.)

Artificial Intelligence

All together there are 20 imp NPCs on the level plus the boss once it is summoned. All enemies are controlled by state machines with 6 different states.
The states are the same for all enemies, however the behaviour and the state’s leaving and entering conditions are sometimes different. IMAGE On the top of the above state changes, when the player picks up an invisibility gem, all enemies switch to patrol stage, regardless of their original state. The effect however only lasts for 5 seconds, and any enraged enemies will target the player again once the effect wears off.

Gameplay elements

Power-ups
Three types of power-ups were implemented in the game, mana -and health potions and the invisibility gems.
The potions are using the same class and primitive based shape with different textures and effects. The invisibility gem has its own class, and all power-ups initialisation, update and rendering are controlled by the PickupHandler class.
Combos
There are two different combos in the game, both related to the frost nova spell. Once the enemy is frozen, the player’s attacks do extra damage to them. This is checked in the player’s Attack() function, attacks against frozen targets deal 2x damage, and in the CollisionHandler, enemies hit by fireball while they are frozen take extra damage too. However, if the player attacks a frozen target with the fireball it will break the effect instantly break the effect.
Easy-hard modes
Implemented by the different type of enemies, the easier imps and the more challenging revenant. The trigger is killing ten imps which will automatically summon the boss. Dealing with the enemies needs different strategy: the imps can be gathered together and killed by fireballs and its AOE effect, but with the revenant the player should be careful because its felballs direct hit deal a lot of damage.

Conclusion

Strengths

The camera movement and the control of the player is probably the biggest strength of the game. I achieved a control that is very close to what we see in a modern RPG games. The camera cannot fall behind the ground mesh, rotating the camera position around the player while it’s moving doesn’t change the distance between the camera and the player, and zooming in or out is also implemented.
The atmosphere is also a strength of the game, the monsters, spells and meshes and the special effects (explosions, fog, smoke trail) fit together in a “dark cursed forest” scene.
The spell system is complex enough (given the available time), there is single and multiple target attacks, positive and negative over time effects and interesting combos to deal with multiple enemies in the same time.
The balance between the damage values and the health / mana values are also work nicely, and the power-up items add to this balance.
The heads-up display’s functionality is in pair with the modern RPG games, it displays a portrait, health and mana bars, buffs and debuffs, however the design could be prettier.

Weaknesses

There should be more types of enemies and more unique behaviours. More enemies could have ranged attacks, and attacks could have other debuffs (for example bleeding effect on the player). The boss could be more complex too with different stages, and in each stage, it could use different attacks. For example, between 50-100% the enemy would use only melee attacks, between 25-50% melee attacks plus a ranged, and in the last 25% melee, ranged and a charge ability that stuns the player.
The variety of the environment could be bigger, with other tree or bush models, and possibly some buildings. The size of the level could be increased however, this would only make sense if there were more types of enemies or different objectives for the game.
Physics is probably the weakest part of the game, however it was difficult to include forces in the game, given the genre and the combat system.

Expanding the project

Plenty of work is needed to make this a complete game but the most important one is the content. More levels, monsters and spells are required, and a levelling system could also increase the quality of the product. This would require significant development time, however the structure of the code is designed to be easily extendable. The levels are already separated (intro & forest), adding a new level would be quite straight forward. Adding more gameobjects with the correct lighting, collision detection and sound would be also easy since all those features are handled in the specific handler (CollisionHandler, PickUpHandler, LightingHandler, AudioHandler).
The enemies derived from a base class which makes it easy to add new types of enemies and use collision, audio and lighting on them.
Although, the biggest challenge would be implementing modern animations for the player and the monsters. Currently the game uses the old MD2 models, which are suitable for quick development, but outdated for modern games. Skeleton based animations should be added, however given the short time this was not possible for this project.

Technologies Used

The game was developed in C++ by using OpenGL 4.0 and the Windows API. The textures were designed in Photoshop and the meshes were edited with Blender.

Interested in hiring me for your project?

Looking for an experienced developer who is interested in games development and keen to learn more about the technology and the industry?