Player Attacks - UQdeco2800/2021-studio-6 GitHub Wiki
Descriptions
Player can inflict damage to NPCs either by melee or ranged attack. Melee is implemented in such a way that enemies must only be within a certain range from the player with designated key (SPACE BAR) and long range attacks are projectiles which players can produce when clicking with the mouse (LMB) tapping another designated key (Enter).
Melee Attack
In summary the process of player's melee attack can be summarized into the following steps:
- Enemy approaches player but it is not within the vicinity of player to melee attack the NPC. So, if player decides to attack NPC (via space bar) - nothing will happen.
- However, if enemy is in range, the player is facing the right direction and they attack, damage will be inflicted to enemy.
- Once enemy's health is reduced to 0, enemy entity will then be registered in a queue with the use of a method called
toBeDisposed()
in a component calledDisposeComponent.java
.
Implementation of Melee Attack
There are a few conditions for melee attack to be successful - enemies will need to be close enough for player to melee, player needs to be facing the right direction and then has to press space bar. If any of these conditions are not fulfilled or at least timed properly (since player taping space bar before collision should not register damage to enemies when enemies come into range) during the game, enemies will not be taking damage. These are tracked with the use of a method called onEnemyClose()
which activates on collision and meleeAttackClicked
variable which updates based on collision ending events and attack key pressed by user. Image shown below is the hitbox of the player, the fixture of the body of the player and the melee range range fixture (the four seperate boxes corresponding to the four directions they can attack in)
- Modularity of Component: At the moment, the player's melee attack's properties are affected by 5 variables; damage for how much damage is dealt in a single hit, knockback for npc knockback effect, attackLength for quickly the player can attack and both length and height that set the attack range of the player. These are passed in through a weapon config file, which is given to the PlayerMeleeAttackComponent on construction. This allows different types of weapons to be implemented by just changing the given weapon config file (see BaseWeaponConfig in entities/configs for an example).
- Additional Component: An additional component attached to player entity - called
PlayerMeleeAttackComponent.java
is a component used to create an additional sensory like Fixture on player as seen in the image above. This forms the melee attack range of players and is used by the system to detect whether enemies are close enough to players (when bounding boxes of fixtures are colliding with one another). The component also checks for whether player has met the conditions specified above (did player click space bar to attack enemy and is enemy close enough to receive damage) - On Collision Detection: With the use of the component added to the player entity, whenever there is a collision (or at least an intersection) between the 2 fixtures - player's melee range fixture and enemy's fixture. A method called
onEnemyClose()
will be launched and it checks for whether conditions are met as mentioned previously for NPC to receive damage from player. - After Collision Detection: It is also worth mentioning that, once the collision between player and NPC is over, the player's attack will be reset
- Physics Time Step: At the moment, the game is refreshing every second (I may be wrong) and the 2 conditions will constantly be checked to ensure that player will be able to execute melee attack to enemy when timing is right.
Different types of Weapons
PlayerMeleeAttackComponent is designed make use of modifiable variables for most of its functions. Specifically, these are set using a json weapon config file. Each config file should specify the weapons attackLength (i.e. the time it takes to swing the weapon in ms), the attackDamage, the knockback of the weapon, the length and height of the collison box, the animation length (Using the formula (attackLength/number of animation frames)/1000), the path to the atlas file for the animations, the animation coordinates array for each direction (up/down/left/right) (currently, these are determined by manual testing and checking) and the scale of the weapon animation. This collection of variables are all that define a weapon. As such, making a new weapon is as simple as creating and filling out a new config file and creating the relevant atlas animation files for the attack. At the moment, the only weapons in game are the sword (medium range), dagger (short range) and axe (long range). The weapon should also be made an item that can be picked up in PlayerPickupComponent.
Long Ranged Attack
This action can be launched in only 4 different directions (south, north, west and east). These directions are dictated by the last key movement pressed down by the user when moving in the game world. Upon clicking a key to decide on a direction to launch the projectiles, user would then need to press the L key button which launches the projectile in the designated direction. Projectiles launched will then be able to collide with anything in the game world as long as these are given defined layers (obstacle, NPC, etc.). Upon colliding, the projectiles would then 'react' differently, whether it may be reused when colliding with obstacles (which is similar to dispose but the bullet is not destroyed for performance purposes) or deal damage (if it collides with NPCs). User can shoot as many as 5 projectiles in a row (as long as these projectiles are still moving in the game world, user will not be able to launch more than 5 but the moment a bullet collides with anything else, it can be reused to be launched again). Additional implementation will now prevent player from launching more projectiles after bullet collides with objects in game world (if player can shoot without the need to reload or keep track of ammo - game would be too easy), player can only continue executing long ranged attack only if these 2 conditions are met - there are sufficient ammo in player's current inventory for reloading and player has successfully reloaded (by clicking the R key). If any of the condition listed is not met, player will not be able to execute any long ranged attack.
Implementation of Long Range Attack
This was implemented with the use of 2 different entities - player and a new entity called bullet. In short, player entity has a component called PlayerRangeAttackComponent.java
that keeps track of the number entity bullets created and stores the bullets as an array of bullet entities. This component is also responsible for registering the current direction that player is facing with the use of a method called registerDirection(Vector2 directionCheck)
. Finally, the same component is also responsible for launching the bullet when a key (L) is pressed by setting the starting and ending coordinates for the bullet to be launched from and its' destination in the game world. At the same time, the launchStatus
of the bullet would be true for conditions to be checked when bullet is travelling in the game world.
Currently, the bullet entity is created the moment the game area is created and visible to the player (if player manages to reach a certain part of the game screen but in most cases - player would not be able to see bullets before it is launched). The bullet is positioned in the most left corner of the game world - that is not visible by the user. Continuing from the moment bullet is launched from the player's position, the BulletCollisionComponent.java
would then play its' role of checking for collisions that have occurred between the bullet and any other objects in the game world. The method responsible for this behavior is bulletHit(Fixture me, Fixture other)
. The method is responsible for letting the system know that the bullet has collided with a variable called collideStatus
which will be explained in the notes. Finally, it gives the bullet the behavior of knowing what needs to be dealt with damage (NPC) and what it needs to collide and return to the player's array of stored bullets (obstacles).
- Bullet Entity: This represents the bullets created in the game world. Initially, an attempt was made to create bullets with the use of a component attached to the player's entity but this did not work as there wasn't a clean and good way of accomplishing such task (without changing a big portion of the code base)
- Bullet Component: With the creation of an entity, there are components that are attached to the bullet and the most important component is the
BulletCollisionComponent.java
which gives the bullet entity the ability to react to collision. - Purpose of
launchStatus
: Knowing the launch status of a bullet is essential to the collision detection method (bulletHit
). As multiple bullet entities are already created in the game world, it can still collide with NPCs moving around the game world - which is where the launch status comes into play and if it isfalse
(which it is by default when the entity is created along with its' component), the collision detection method will ignore the bullet if it comes into contact with any other object in the game world. - Purpose of
collideStatus
: Similar tolaunchStatus
, it acts as conditions that needs to be checked within the collision detection method but it is more so related to the number of frames that are being refreshed in a second. A bug was found when the feature was implemented where bullet colliding with another object is registered twice - causing the list of bullet entities in the player to store duplicates of bullets which causes the feature to be buggy which can cause the bullets to behave in a way that is not meant to when it is being launched. The solution is to keep track of that and the moment the bullet collides with an object in the game world, it is registered in the system and it prevents this problem from occurring. - Player Direction Registration: Player's direction is currently being registered in the
PlayerRangedAttackComponent.java
, a future implementation is to create an entire new component calledPlayerMovementComponent.java
perhaps. This is done by monitoring which key (W, A, S or D) is pressed last. The moment a key is pressed, a local variable calledlongAttackDir
is updated and registers that input. - Magazine count: As mentioned, player can only shoot 5 times at maximum in one go. After that, user will need to reload by pressing 'R', image below will visibly showcase the number bullets left for player to shoot. This enable player to be more conscious of the number bullets left. Player will not be able to shoot anymore when magazine count is empty. These are implemented via the use of
PlayerInterfaceDisplay.java
which controls the number of rock images that will be displayed on player's HUD. Event triggering methods that updates the player's HUD are located in the java file mentioned previously. Events are then triggered whenever player's gun magazine is updated (whether it may be decreasing when player shoots or reloading) inPlayerRangeAttackComponent.java
which controls and tracks number of bullets left in gun magazine. - Reloading: Player will need to reload after shooting 5 times. This is done by clicking the 'R' key - this event is triggered in
KeyboardPlayerInputComponent.java
which triggers the text 'Reloading ...' on the game screen for user to receive feedback on their action and it callsreload()
method inPlayerActions.java
which starts the entire reloading process where it takes an approximate 2 seconds for reloading to actually occur. This point of doing this is to simulate the process of reloading in real life. This reloading process involves updating the time (game time) to reload via the use ofreload()
if conditions are met and is checked viacanReload()
.canReload()
will continuously be checked in theupdate()
method which is called during every time step. - HUD related to long range attack: This is related to how data related to bullet left in magazine and player's inventory is currently being displayed in game screen. Currently, all HUD related variables are being displayed via the use of
PlayerInterfaceDisplay.java
. These labels or images are constantly being updated via triggering events byPlayerRangeAttackComponent.java
and whenever ammo is taken out from inventory after reloading, the event to update the ammo number via the use ofInventoryComponent.java
.
Design Justification
- Melee and range attacks are implemented to give player the ability to diversify their way of attacking NPCs. These will give players the ability to have the experience of either attacking NPCs from far or not at all (melee).
- Initially, melee attack was inflicted to all NPC in all direction within the vicinity. However, with feedback, it is deemed that it would be too easy for player to melee attack NPCs without the need to adjust their movement while attacking NPCs in game. Currently, player will now need to move in the direction that they want to inflict damage and melee attack will only occur in the last movement key pressed by user. This fulfills the goal of making it more challenging for player to play when trying to inflict melee damage to NPC in game.
- Long range attack initially can be executed by player without limit. This would be too overpowered because player can shoot non stop and this could make the game too easy. This then led to an extension to range attack by including ammo count to it by limiting player's ability to shoot a maximum number of 5 times and require reloading if player wants to shoot more. This reloading will take 2 more seconds to complete which heightens player's ability find a safe location while reloading and try to evade NPCs throughout the game before being able to launch long range attack.
Design Pattern
- The design pattern adopted for the player attacks (both melee and range) is the observer design pattern. This is a design pattern that was already heavily adopted in base of the game like player's movement - a prime example is when the game engine waits for the player to press a key (an observer that waits for an event to occur) related to movement like W,A,S or D - when any the key listed is pressed, an event is triggered and the relevant methods are called to move the player in the corresponding direction.
- This design pattern is similarly done with range and melee attack. For range attack, bullets are created when game areas are created. These bullets would not change in position and is not involved in the game's mechanic (yet). However, there are observers which are coded to continuously wait and check if any keys were pressed by the player. In this case, each bullet object (or subjects) will be launched sequentially (as these are added onto a list of bullets in the player's component) when spacebar is clicked. Each bullets are created the same way and have the ability to be launched based on their public methods which are called when the observers notify them to do so.
- For the melee attack, the observers are in charged in always updating to ensure that it is receiving the most recent player position. This is essential as the observer is in charged of not only creating fixture like objects to attack enemies that are in ranged but are also responsible for creating fixtures in the player's current position. It makes sense that the observer design pattern is the ideal pattern to go with as it is able to keep track of all relevant variables involved in inflicting damage from player to enemy. In addition to creating such behavior (melee attack), a sequential event will also be triggered by the observer when it senses a collision with any object in the game world, depending on the type of object that has been in collision, relevant public methods would then be called based on the fixtures in collision.
- This design pattern allows for the particular object to be updated which allows the rest of the objects (entities) that are already in the game world to also be affected if the object has been set be in an active state. This works best for this particular game engine which works by stepping through time and would require multiple relevant objects to be updated and tracked which explains why such a design pattern is most suited for this particular implementation.
Sequence Diagram
Sequence diagram to show the process of hitting an enemy through the melee attack. Note that certain elements have been abstracted out (Physics calls, animations, events and other unrelated calls).