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.)
“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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.)
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.
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.
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.
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.
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.
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.