Skip to content

Sprint 3 Main Character New Unlockable Attires and Functionality improvement

Rohith Palakirti edited this page Oct 5, 2021 · 19 revisions

Contents

Automatic Item PickUp Implementation

When the main player - Jason collides with an item, Jason will automatically pick up the item. With this feature, the user has no need to press a key to initiate item pickup.

PlayerFactory.java

    switch (attire) {

            ...
            case "OG":
            default:
                mpcAnimator = createAnimationComponent("images/mpc/finalAtlas/OG/mpcAnimation.atlas");
                mpcTexture = new TextureRenderComponent("images/mpc/finalAtlas/OG/mpc_right.png");
                break;
        }
    ...
    mpcAnimator.addAnimation("main_player_pickup",0.125f,Animation.PlayMode.LOOP);

Automatic Item Pick Up animation is triggered from ItemComponent.java

After 1s the animation is stopped.

    private void onCollisionStart(Fixture me, Fixture other){

        if (PhysicsLayer.contains(PhysicsLayer.PLAYER, other.getFilterData().categoryBits)) {
           // checking if the collision is done with the player
           callback.accept(target);
           target.getEvents().trigger("itemPickUp");
           entity.getEvents().trigger("itemPickedUp");
           ...
           try {
               ...
               // after 1s stop the item pickUp animation
               Timer timer=new Timer();
               timer.scheduleTask(new Timer.Task() {
                   @Override
                   public void run() {
                       target.getEvents().trigger("stopPickUp");
                       timer.stop();
                   }
               },1);
           }
           catch (Exception e){
               System.out.print(e);
           }
        }
    }

Buff Debuffs Animation Integration

As the player Jason interacts progress in the game, it will experience buffs and debuffs from interacting with the obstacles and items.

Buffs And Debuffs Map

Buff/ Debuff Type
Hungry Debuff
Poisoned Debuff
Dizzy Debuff
Health Down Debuff
Health Limit UP Debuff
Speed Down Debuff
Thirsty Debuff
Recovered Buff
Health Up Buff

PlayerFactory.java adds the animation for those buffs and debuffs

These buffs/Debuffs are added for each movement animation.

           ...
            mpcAnimator.addAnimation("main_player_run_dizzy", 0.1f, Animation.PlayMode.LOOP);
            mpcAnimator.addAnimation("main_player_run_health-down", 0.1f, Animation.PlayMode.LOOP);
            mpcAnimator.addAnimation("main_player_run_health-limit-up", 0.1f, Animation.PlayMode.LOOP);
            mpcAnimator.addAnimation("main_player_run_health-up", 0.1f, Animation.PlayMode.LOOP);
            mpcAnimator.addAnimation("main_player_run_hungry", 0.1f, Animation.PlayMode.LOOP);
            mpcAnimator.addAnimation("main_player_run_poisoned", 0.1f, Animation.PlayMode.LOOP);
            mpcAnimator.addAnimation("main_player_run_recovered", 0.1f, Animation.PlayMode.LOOP);
            mpcAnimator.addAnimation("main_player_run_speed-down", 0.1f, Animation.PlayMode.LOOP);
            mpcAnimator.addAnimation("main_player_run_thirsty", 0.1f, Animation.PlayMode.LOOP);
           ...

The listeners implemented here lookout for trigger events and run the corresponding functions in PlayerAnimationController.java. These functions are discussed in detail in the following sections of the wiki.

     ...
     public void create() {
        super.create();
        animator = this.entity.getComponent(AnimationRenderComponent.class);
        ...
        entity.getEvents().addListener("hungry", this::animateHungry);
        entity.getEvents().addListener("poisoned", this::animatePoison);
        entity.getEvents().addListener("healthDown", this::animateHealthDown);
        entity.getEvents().addListener("dizzy", this::animateDizzy);
        entity.getEvents().addListener("health_limit_up", this::animateHealthLimit);
        entity.getEvents().addListener("healthUp", this::animateHealthUP);
        entity.getEvents().addListener("recovered", this::animateRecovered);
        entity.getEvents().addListener("speedDown", this::animateSpeedDown);
        entity.getEvents().addListener("thirsty", this::animateThirsty);
        entity.getEvents().addListener("stopBuffDebuff", this::animateWalk);
    }
    ...

Each time an animation event is triggered, a function is used to start the animation for the buff/debuff while taking into account the previous animation that was running and runs an animation for buff/debuff corresponding to that animation.

    
   ...
   
   /**
     *  Activate the hungry animation debuff
     */
    private void animateHungry() {
        animationName = animator.getCurrentAnimation();
        preAnimationCleanUp();
        switch (animationName) {
            case "main_player_run":
                animator.startAnimation("main_player_run_hungry");
                break;
            case "main_player_pickup":
                animator.startAnimation("main_player_pickup_hungry");
                break;
            case "main_player_jump":
                animator.startAnimation("main_player_jump_hungry");
                break;
            case "main_player_attack":
                animator.startAnimation("main_player_attack_hungry");
                break;
            case "main_player_crouch":
                animator.startAnimation("main_player_crouch_hungry");
                break;
            case "main_player_right":
                animator.startAnimation("main_player_right_hungry");
                break;
            case "main_player_walk":
            default:
                animator.startAnimation("main_player_walk_hungry");
                break;
        }
    }
  ...

Unlockable Attires

Select Attires Screen

A new screen for selecting unlocked attires was created and the Select Unlocked Attires Button was included in the game main menu.

States of Attires Screen

Zero Gold Achievements

One Gold Achievement

Two Gold Achievements

Three Gold Achievements

Four Gold Achievements

Five Gold Achievements

Six Gold Achievements or higher

Getting and Storing number of gold achievements

 int goldAchievements = GameRecords.getGoldAchievementsCount();
public UnlockedAttiresDisplay(GdxGame game,int goldAchievements) {
        this.goldAchievements = goldAchievements;
        this.game = game;
        this.stats = FileLoader.readClass(PlayerConfig.class, "configs/player.json");
        this.attire = stats.attire;
        ServiceLocator.registerResourceService(new ResourceService());
        loadAssets();

    }

Achievements needed to unlock attires

Number of Gold Achievements Attire Unlocked
0 Original
2 gold_2
4 gold_4
6 gold_6

Attires Unlocking Mechanism

public UnlockedAttiresDisplay(GdxGame game,int goldAchievements) {
        this.goldAchievements = goldAchievements;
        this.game = game;
        this.stats = FileLoader.readClass(PlayerConfig.class, "configs/player.json");
        this.attire = stats.attire;
        ServiceLocator.registerResourceService(new ResourceService());
        loadAssets();

    }
 if (goldAchievements == 0) {
            renderZeroUnlockedAttiresTable();
        } else {
            renderUnlockedAttiresTable();
        }
/**
     * Renders all unlocked attires
     */

 private void renderUnlockedAttiresTable() {
        renderUnlockedAttires(goldAchievements, 1);
    }
/**
     * Renders screens to show zero unlocked attires
     */
    private void renderZeroUnlockedAttiresTable() {
        Label message1 = new Label("YOU HAVEN'T UNLOCKED ANY NEW ATTIRES YET!",
                new Label.LabelStyle(new BitmapFont(), Color.RED));
        message1.setFontScale(3f);
        table.add(message1).padTop(20f).center();
        table.row();
        Label message2 = new Label("UNLOCK MORE GOLD ACHIEVEMENTS TO ACCESS NEW ATTIRES!",
                new Label.LabelStyle(new BitmapFont(), Color.YELLOW));
        message2.setFontScale(2f);
        table.add(message2).padTop(20f).center();
        table.row();

        Image gold_2 = new Image(ServiceLocator.getResourceService()
                .getAsset("images/mpc/attires/gold_2.png", Texture.class));
        table.add(gold_2).padLeft(10f).padRight(10f).padTop(20f).size(220, 150);
        table.row();
        Image gold_4 = new Image(ServiceLocator.getResourceService()
                .getAsset("images/mpc/attires/gold_4.png", Texture.class));
        table.add(gold_4).padLeft(10f).padRight(10f).padTop(20f).size(220, 150);
        table.row();
        Image gold_6 = new Image(ServiceLocator.getResourceService()
                .getAsset("images/mpc/attires/gold_6.png", Texture.class));
        table.add(gold_6).padLeft(10f).padRight(10f).padTop(20f).size(220, 150);
        table.row();
    }
/**
     * Utility function to render the given list of achievements and corresponding unlocked attires
     * @param goldAchievements number of gold achievements
     * @param alpha the opacity of each image (low for the ones which are locked)
     */
    private void renderUnlockedAttires(int goldAchievements, float alpha) {
        Label label2 = new Label("SELECT AN ATTIRE", new Label.LabelStyle(new BitmapFont(), Color.YELLOW));
        label2.setFontScale(2);
        table.center();
        table.add(label2).padTop(10f).padBottom(10f);
        table.row();

        if(goldAchievements < 2) {
            table.removeActor(label2);
            Label message1 = new Label("UNLOCK 1 MORE GOLD ACHIEVEMENT TO ACCESS NEW ATTIRES!",
                    new Label.LabelStyle(new BitmapFont(), Color.RED));
            message1.setFontScale(1.5f);
            table.add(message1).padTop(20f).center();
            table.row();

        }
        // Unlock 1 new attire
        if(goldAchievements == 2 || goldAchievements == 3) {
            lessThanFour(alpha);
        }
        // Unlock 2 new attires
        if(goldAchievements == 4 || goldAchievements == 5) {
            lessThanFour(alpha);
            lessThanSix(alpha);
        }
        // Unlock 3 new attires
        if(goldAchievements >= 6) {
            moreThanSix(alpha);

        }

    }
 private void renderOriginalAttire() {

        ImageButton attireImg = getImageButton("images/mpc/attires/original.png");
        attireImg.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                attireType = "OG";
                MPCConfig.updateAttire(attireType);
                confirmSelection("ORIGINAL", "original");

            }
        });
        unlockedAttiresTable.add(attireImg).left().padLeft(10f).padRight(10f).size(220, 150);
        unlockedAttiresTable.center();
        unlockedAttiresTable.row();
        
    }
private void lessThanFour(float alpha) {
        renderOriginalAttire();
        Image achievementImg = new Image(ServiceLocator.getResourceService()
                .getAsset("images/mpc/attires/trophies_2x.png", Texture.class));
        achievementImg.setScaling(Scaling.fit);
        achievementImg.setColor(255, 255, 255, alpha);

        ImageButton attireImg = getImageButton("images/mpc/attires/gold_2.png");
        attireImg.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                attireType = "gold_2";
                MPCConfig.updateAttire(attireType);
                confirmSelection("GOLD_2", "gold_2");

            }
        });
        unlockedAttiresTable.add(attireImg).left().padTop(10f).padLeft(10f).padRight(10f).size(220, 150);
        unlockedAttiresTable.add(achievementImg).right().padTop(10f).padLeft(10f).padRight(10f).size(220, 150);

        if(goldAchievements == 3) {
            Label message1 = new Label("UNLOCK 1 MORE GOLD ACHIEVEMENT TO ACCESS A NEW ATTIRE!",
                    new Label.LabelStyle(new BitmapFont(), Color.RED));
            message1.setFontScale(1.5f);
            unlockedAttiresTable.row();
            unlockedAttiresTable.add(message1).padLeft(10f).padRight(10f).size(120, 50);
            unlockedAttiresTable.row();
        }
        if (i % 3 == 0) {
            unlockedAttiresTable.row();
        }

    }

    private void lessThanSix(float alpha) {
        Image achievementImg = new Image(ServiceLocator.getResourceService()
                .getAsset("images/mpc/attires/trophies_4x.png", Texture.class));
        achievementImg.setScaling(Scaling.fit);
        achievementImg.setColor(255, 255, 255, alpha);

        ImageButton attireImg = getImageButton("images/mpc/attires/gold_4.png");
        attireImg.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                attireType = "gold_4";
                MPCConfig.updateAttire(attireType);
                confirmSelection("GOLD_4", "gold_4");

            }
        });


        unlockedAttiresTable.add(attireImg).left().padLeft(10f).padTop(10f).padRight(10f).size(220, 150);
        unlockedAttiresTable.add(achievementImg).right().padLeft(10f).padTop(10f).padRight(10f).size(220, 150);

        if(goldAchievements == 5) {
            Label message1 = new Label("UNLOCK 1 MORE GOLD ACHIEVEMENT TO ACCESS A NEW ATTIRE!",
                    new Label.LabelStyle(new BitmapFont(), Color.RED));
            message1.setFontScale(1.5f);
            unlockedAttiresTable.row();
            unlockedAttiresTable.add(message1).padLeft(10f).padRight(10f).size(120, 50).center();
            unlockedAttiresTable.row();
        }
        if (i % 3 == 0) {
            unlockedAttiresTable.row();
        }
    }

    private void moreThanSix(float alpha) {
        Label message1 = new Label("YOU HAVE UNLOCKED ALL ATTIRES FOR NOW!",
                new Label.LabelStyle(new BitmapFont(), Color.YELLOW));
        message1.setFontScale(2f);
        unlockedAttiresTable.add(message1).padTop(20f).padBottom(20f).center();
        unlockedAttiresTable.row();
        unlockedAttiresTable.center();
        lessThanFour(alpha);
        lessThanSix(alpha);

        Image achievementImg = new Image(ServiceLocator.getResourceService()
                .getAsset("images/mpc/attires/trophies_6x.png", Texture.class));
        achievementImg.setScaling(Scaling.fit);
        achievementImg.setColor(255, 255, 255, alpha);

        ImageButton attireImg = getImageButton("images/mpc/attires/gold_6.png");
        attireImg.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                attireType = "gold_6";
                MPCConfig.updateAttire(attireType);
                confirmSelection("GOLD_6", "gold_6");

            }
        });

User selection of attires

private void confirmSelection (String attireType, String attirePath) {

        dialog = new Dialog("YOU HAVE SELECTED THE " + attireType + " ATTIRE!", skin);
        dialog.setModal(true);
        dialog.setMovable(false);
        dialog.setResizable(true);

        dialog.pad(50).padTop(120);
        Image attire = new Image(new Texture("images/mpc/attires/" + attirePath + ".png"));
        Label heading = new Label("ATTIRE SELECTED!", new Label.LabelStyle(new BitmapFont(), Color.BLACK));
        heading.setFontScale(2f);
        dialog.getContentTable().add(heading).expandX().row();
        dialog.getContentTable().add(attire);
        dialog.getButtonTable().add(renderCloseButton()).size(50, 50).row();

        dialog.show(stage);


    }

Persisting user attire selection data in JSON file

The user's selected attire is stored in the mpc.json file locally in the home directory of the user.

{
attire: gold_6
}

The format of the JSON file and the properties are defined in the PlayerConfig.java file

/**
 * Defines the properties stored in player config files to be loaded by the Player Factory.
 */
public class PlayerConfig extends BaseEntityConfig  {
  public int gold = 1;
  public String favouriteColour = "none";
  public String attire = "OG";
}
public class MPCConfig {

    private static final String ROOT_DIR = "DECO2800Game";
    private static final String CONFIG_FILE = "mpc.json";
    private static final String path = ROOT_DIR + File.separator + CONFIG_FILE;

    /**
     * Stores the current values into a JSON file
     */
    public void readValues() {
        PlayerConfig values = getValues();
    }

    public static void updateAttire(String attireType) {
        PlayerConfig values = getValues();
        values.attire = attireType;

        updateValues(values);
    }
    public static void updateValues(PlayerConfig values) {

        FileLoader.writeClass(values, path, EXTERNAL);
    }
    public static void updateValues() {
        PlayerConfig values = getValues();
        if(values.attire == null){
            values.attire = "OG";
        }
        updateValues(values);

    }
    public static PlayerConfig getValues() {
        PlayerConfig values =  FileLoader.readClass(PlayerConfig.class, path, EXTERNAL);
        return values != null ? values : new PlayerConfig();

    }
}

Dynamically Loading New Attire Atlases

private static final PlayerConfig stats =
            FileLoader.readClass(PlayerConfig.class, "configs/player.json");
String attire = updateAttireConfig();
AnimationRenderComponent mpcAnimator;
        TextureRenderComponent mpcTexture;
        System.out.println("Loading attire: "+ attire);
        switch (attire) {

            case "gold_2":
                mpcAnimator = createAnimationComponent("images/mpc/finalAtlas/gold_2/mpcAnimation_2.atlas");
                mpcTexture = new TextureRenderComponent("images/mpc/finalAtlas/gold_2/mpc_right.png");
                break;
            case "gold_4":
                mpcAnimator = createAnimationComponent("images/mpc/finalAtlas/gold_4_buff_to_be_test/mpcAnimation_4.atlas");
                mpcTexture = new TextureRenderComponent("images/mpc/finalAtlas/gold_4_buff_to_be_test/mpc_right.png");
                break;
            case "gold_6":
                mpcAnimator = createAnimationComponent("images/mpc/finalAtlas/gold_6_buff_to_be_tested/mpcAnimation_6.atlas");
                mpcTexture = new TextureRenderComponent("images/mpc/finalAtlas/gold_6_buff_to_be_tested/mpc_right.png");
                break;
            case "OG":
            default:
                mpcAnimator = createAnimationComponent("images/mpc/finalAtlas/OG_buff_to_be_tested/mpcAnimation.atlas");
                mpcTexture = new TextureRenderComponent("images/mpc/finalAtlas/OG_buff_to_be_tested/mpc_right.png");
                break;
        }

Texture Atlas

Buff/Debuff Atlas

For the different attires, we have the same atlas in terms of creating buffs/ debuffs animation and was generated using a texture packer. The atlas has player buff representations for all the movements (explained in Sprint 2 Documentation )in the following states:

  1. Dizzy (facing right, animated)
  2. Hungry (facing right, animated)
  3. Poisoned (facing right, animated)
  4. Health Down (facing right, animated)
  5. Speed Down (facing right, animated)
  6. Thirsty (facing right, animated)
  7. Recovered (facing right, animated)
  8. Health UP (facing right, animated)
  9. Health Limit Up (facing right, animated)

Atlas Files

For OG Attire : mpcAnimation.atlas

For Gold_4 Attire : mpcAnimation_4.atlas

For Gold_6 Attire : mpcAnimation_6.atlas

Generated Texture pack

The bellow image file shows the atlas image for Gold_6 attire.

mpcAnimation.png mpcAnimation.png

Testing

Considering it is hard to test whether an animation plays because of its visual nature, unit tests were written to verify and validate that when an animation event is triggered the corresponding movement animation is triggered. The tests check that all the expected animations actually are rendered when their respective events are triggered.

PlayerAnimationRenderTest.java

@ExtendWith(GameExtension.class)
class PlayerAnimationRenderTest {

    private Entity player;
    private AnimationRenderComponent animator;
    @BeforeEach
    void beforeEach() {
        ServiceLocator.registerPhysicsService(new PhysicsService());
        player = new Entity()
                .addComponent(new PhysicsComponent())
                .addComponent(new PlayerAnimationController());

        animator = mock(AnimationRenderComponent.class);
        player.addComponent(animator);

        PlayerAnimationController animationController =
                player.getComponent(PlayerAnimationController.class);
        animationController.setTexturePresent(false);
        player.create();
    }


    @Test
    void shouldTriggerRightMovement() {
        player.getEvents().trigger("walkRight");
        verify(animator).startAnimation("main_player_run");
    }

    @Test
    void shouldTriggerWalkMovement() {
        player.getEvents().trigger("startMPCAnimation");
        verify(animator).startAnimation("main_player_walk");
    }

    @Test
    void shouldTriggerJumpMovement() {
        player.getEvents().trigger("jump");
        verify(animator).startAnimation("main_player_jump");
    }

    @Test
    void shouldTriggerCrouchMovement() {
        player.getEvents().trigger("crouch");
        verify(animator).startAnimation("main_player_crouch");
    }

    @Test
    void shouldTriggerItemPickUpMovement() {
        player.getEvents().trigger("itemPickUp");
        verify(animator).startAnimation("main_player_pickup");
    }

    @Test
    void shouldTriggerAttackMovement() {
        player.getEvents().trigger("attack");
        verify(animator).startAnimation("main_player_attack");
    }
}

UML Diagrams

Class Diagram

UnlockedAttiresDisplay

UnlockedAttiresScreen

PlayerAnimationController

AnimatorComponent

ItemComponent

MPCConfig

Sequence Diagrams

UnlockedAttiresScreen_createUI()

renderZeroUnlockedAttiresTable()

renderUnlockedAttires()

renderOriginalAttire()

UnlockedAttiresDisplay

lessThanFour()

lessThanSix()

moreThanSix()

confirmSelection()

MPCConfig_updateValues()

ItemComponent_onCollisionStart()

Relevant Files

UnlockedAttiresScreen.java

UnlockedAttiresDisplay.java

PlayerConfig.java

PlayerFactory.java

PlayerAnimationController.java

PlayerAnimationRenderTest.java

ItemComponent.java

mpcAnimation.atlas

mpcAnimation_4.atlas

mpcAnimation_6.atlas

mpcAnimation.png

mpcAnimation_4.png

mpcAnimation_6.png

Gameplay

Home

Main Character

πŸ‘Ύ Obstacle/Enemy

Food & Water

Pickable Items

Game achievements

Game Design

Emotional Goals

Game Story

Influences

Style

Pixel Grid Resolution

Camera Angle and The Player's Perspective

Features Design

Achievements Screen

Achievements Trophies and Cards

Music Selection GUI

πŸ‘Ύ Obstacle/Enemy

 Monster Manual
 Obstacles/Enemies
  - Alien Plants
  - Variation thorns
  - Falling Meteorites
  - FaceHugger
  - AlienMonkey
 Spaceship & Map Entry
 Particle effect

Buffs

Debuffs

Buffs and Debuffs manual

Game Instruction

[code for debuff animations](code for debuff animations)

Infinite loop game system

Main Menu Screen

New Setting Screen

Hunger and Thirst

Goals and Objectives

HUD User Interface

Inventory System

Item Bar System

Scoring System

Props store

BGM design

Sound Effect design

Main game interface

Invisible ceiling

New terrain sprint 4

New game over screen sprint 4

Code Guidelines

Main Character Movement, Interactions and Animations - Code Guidelines

Item Pickup

ItemBar & Recycle system

Main Menu Button Code

Game Instructions Code

πŸ‘Ύ Obstacle/Enemy

 Obstacle/Enemy
 Monster Manual
 Spaceship Boss
 Particle effects
 Other Related Code
 UML & Sequence diagram of enemies/obstacles

Scoring System Implementation Explanation

Music Implementation

Buff and Debuff Implementation

Score History Display

code improvement explanation

Infinite generating terrains Implementation Explanation

Game Over Screen and functions explaination

Buffer timer before game start

Scrolling background

Multiple Maps

Invisible ceiling

Rocks and woods layout optimization

Magma and nails code implementation

Background Music Selection

Chooser GUI Implementation

Chooser GUI Logic Persistence

Guide: Adding Background music for a particular screen

Achievements Ecosystem - Code Guidelines

Achievements System

Achievements Screen

Adding Achievements (Guide)

Game Records

DateTimeUtils

History Scoreboard - Score Details

Listening for important events in the Achievements ecosystem

Food and Water System

Food System Water System

Hunger and Thirst icon code guidelines

Asset Creation

In Game Background Music

User Testing

Hunger and Thirst User Testing

Main Character Testing

Buffs and Debuffs Testing

Buff and Debuff Manual User Testing

Game Instruction User Testing

The Main Menu User Test

The New Button User Test in Setting Page

The Main Menu Buttons User Testing

Hunger and Thirst User Test

Infinite loop game and Terrain Testing

Item Bar System Testing

Randomised Item Drops

Recycle System Testing

Scoring System Testing

Music User test

https://github.com/UQdeco2800/2021-ext-studio-2.wiki.git

πŸ‘Ύ Obstacle/Enemy

 Obstacle testing
  - Alien Plants & Variation Thorns
  - Falling Meteorites
 Enemy testing
  - Alien Monkeys & Facehugger
  - Spaceship Boss
 Monster Manual
 Particle-effect
 Player attack testing
  - Player Attack

Inventory system UI layout

Props store user testing

Achievements User Testing

Sprint 1

Sprint 2

Sprint 3

Sprint 4

Items testing

Player Status testing

Changeable background & Buffer time testing

Main game interface test

Invisible ceiling test

Game over screen test sprint 4

New terrain textures on bonus map test sprint 4

Buying Props User Testing

Testing

Hunger and Thirst Testing

Main Character Player

Achievements System, Game Records and Unlockable Chapters

DateTimeUtils Testing

Scoring System Testing Plan

Distance Display Testing Plan

Musics Implementation Testing plan

History Board Testing plan

Rocks and woods testing plan

Sprint 4 terrain tests

Items

Item Bar System Testing Plan

Recycle System Testing Plan

Game Engine

Game Engine Help

Getting Started

Entities and Components

Service Locator

Loading Resources

Logging

Unit Testing

Debug Terminal

Input Handling

UI

Animations

Audio

AI

Physics

Game Screens and Areas

Terrain

Concurrency & Threading

Settings

Troubleshooting

MacOS Setup Guide

Clone this wiki locally