-
Notifications
You must be signed in to change notification settings - Fork 0
Melee Enemies
Jump to a specific section
The melee enemies of Atlantis Sinks are integral to the player experience as they present an immediate threat to the city.
Within sprint 1, Team 9 created a Pirate Crab enemy to provide the player character with a close range combat challenge.
During these two weeks, several issues were faced with putting the enemy into the game from rendering to spawning the entity into the game world.
One of the issues we ran into during development was with rendering in pixels. It didn't occur that rendering was being done in units described to be equivalent to metres. When pixmaps for the healthbar were rendered onto the screen, they appeared to be really big and so Google was used to investigate the issue. An article was stumbled upon that described the solution. The solution was to temporarily swap the projection to render in pixels and then swap it back to its original.
This obviously looked like a feature that was going to get used several times so a class called RenderUtil
was created and placed in the util
package. This created an easy way to make draw
calls.
While unit testing, mocking and spying methods were quite difficult, but a couple of rounds of Googling and searching stackoverflow for answers helped. The difficulty was that some methods couldn't be easily mocked or stub as they lived in a static class and Mockito does not interact well with static classes. So the solution was to convert RenderUtil
to a singleton so that only one of it will always be available in the entire game. Once this had been implemented, mocking and stubbing was then possible. The existing tests also really helped in figuring out how to write tests for the engine.
The provided engine had the enemies spawning randomly across a map which spanned the entire screen, but in Atlantis Sinks the map layout was changed to a confined area which can be expanded as the player progresses through the game. Due to this, the initial approach of the engine failed in spawning the enemy within the map confines. Hence to overcome the issue, it was decided to spawn the enemy with the code in respect to the environmental objects for now, with the intention of converting this to a function in a later sprint for a better implementation.
More information regarding the objectives and tasks associated with the development of the melee enemy: Enemies Task Ticket and Melee Character Design
The design for the Pirate Crab melee enemy follows the design principles set out in the Base Enemy Entities page to keep enemies consistent across the design of the game.
More information on the design of melee enemies for Atlantis Sinks can be found here!
To test the effectiveness of the design of melee enemies, some form of testing has to be conducted to ensure it is recognisable and fits the theme of the game.
More information on the user testing for the Pirate Crab design can be found here.
Writing tests for the Pirate Crab successfully spawning in and chasing/attacking the player proved to be very difficult in JUnit; however, it is something that was found to be very easy to verify visually. The video below shows the enemy to be working as expected, but highlights the need for better obstacle pathing as the entity got stuck quite easily.
Like all enemies, the Pirate Crab is configured within the NPCs.json
file with stats based off of the EnemyConfig
class. As a standard enemy, the Pirate Crab has relatively low health and therefore drops a low amount of gold when defeated (as can be seen below), but these values can easily be balanced from within the json file.
"pirateCrab": {
"health": 50,
"baseAttack": 10,
"gold": 10
}
The Pirate Crab is spawned using the NPCFactory
class with the createPirateCrab()
method. This method takes a single argument for the target that the Pirate Crab should chase when in range of it. The method then returns a single Entity configured with the combat stats and textures for the Pirate Crab.
public static Entity createPirateCrabEnemy(Entity target) {
Entity pirateCrabEnemy = createBaseNPC(target);
EnemyConfig config = configs.pirateCrab;
TextureRenderComponent textureRenderComponent = new TextureRenderComponent("images/pirate_crab_SW.png");
pirateCrabEnemy
.addComponent(new CombatStatsComponent(config.health, config.baseAttack))
.addComponent(new HealthBarComponent(100, 10))
.addComponent(textureRenderComponent);
pirateCrabEnemy.getComponent(TextureRenderComponent.class).scaleEntity();
return pirateCrabEnemy;
}
The current implementation passes through the Player
entity as the enemies' target (with the intention of this being replaced with the Crystal
entity in future sprints) inside the spawnPirateCrabEnemy()
method from the ForestGameArea
class. The method contains a while
loop that attempts to find a position within the game world that it is able to spawn the Pirate Crab enemy at - if it is not able to find an available position after 1000 attempts, it will break the loop and not spawn the enemy in.
private void spawnPirateCrabEnemy(){
Entity pirateCrabEnemy = NPCFactory.createPirateCrabEnemy(player);
GridPoint2 minPos = new GridPoint2(0,0);
GridPoint2 maxPos = terrain.getMapBounds(0);
GridPoint2 randomPos = RandomUtils.random(minPos,maxPos);
int counter = 0;
while (this.entityMapping.wouldCollide(pirateCrabEnemy, randomPos.x, randomPos.y)
||entityMapping.isNearWater(randomPos.x, randomPos.y)){
randomPos = RandomUtils.random(minPos,maxPos);
if (counter > 1000){
return;
}
counter++;
}
spawnEntityAt(pirateCrabEnemy,randomPos,true,true);
}
This is then called within the create()
method of the ForestGameArea
class to actually spawn the enemy within the game world.