Skip to content

Obstacles Enemies Code

Shanshan edited this page Oct 5, 2021 · 7 revisions

Table of Contents

Obstacles

Obstacle creation
Randomly generated obstacles on ground
Meteorite generation
Throns effect: characters slow down

Enemies

Flying monkey & Face worm
  Create flying monkey
  Obstacle Attack Task
  Create face worm
  Trigger the Face Worm on the game area

Shared function

Event Handler
  Collision event
  Animation and disappearance

Obstacle creation

In ObstacleFactory.java, There are three feature for the creation of obstacles (static, aggressive, can trigger animations and disappear). createBaseObstacle() is a common attribute for creating such obstacles. The difference between createPlantsObstacle() and createThornsObstacle() is the component or task parameter.

createMeteorite() is separated from the basic obstacle. This obstacle needs to be generated many at a time, and the layer needs to be set to a separate PhysicsLayer.METEORITE to specify them specifically.Stones are divided into three categories according to their size, and each category corresponds to a different basic attack.

    public enum MeteoriteType {
        SmallMeteorite, MiddleMeteorite, BigMeteorite;
    }

Basic attacks for all obstacles can be set in obstacles.json.

Randomly generated obstacles on ground

Randomly generate obstacles function is in ForestGameArea.java. There is a mechanism to prevent obstacles from being randomly generated at the same coordinate. Each time obstacles within a range are randomly generated, the coordinates of these obstacles are gathered in randomPoints. Each time a new random coordinate is generated, it will be checked whether the coordinate already exists, and if it is, it will be regenerated.

Since the interval between the first loading of the game and subsequent map updates is different, the function uses firstGenerate to distinguish these two situations.

    /* The number of each type of obstacle. Note: total obstacles cannot be greater than 20 (range of loading map)*/
    private static final int NUM_OBSTACLES = 2;

    private boolean firstGenerate = true;

    /**
     * Obstacles are randomly generated according to the position of the character. The first time it
     * is called, NUM_OBSTACLES obstacles are generated in the range of (0-30) units after the
     * player's position. For each subsequent call, the position of generating obstacles is twenty
     * units behind the player, and the generating range is 20 units.
     * <p>
     * For example, the first call to the player x position is 0, and the x range for generating
     * obstacles is 0-30.  The second call to the player's x position is 10, and the x range for
     * generating obstacles is 31-50.
     */
    public void spawnObstacles() {
        GridPoint2 minPos, maxPos;
        // Record the coordinates of all obstacles, Record the coordinates of all obstacles to prevent obstacles
        // from being generated at the same location
        ArrayList<GridPoint2> randomPoints = new ArrayList<GridPoint2>();

        int playerX = (int) player.getPosition().x;

        if (firstGenerate) {
            minPos = new GridPoint2(playerX, 0);
            maxPos = new GridPoint2(playerX + 30, 0);
            firstGenerate = false;
        } else {
            minPos = new GridPoint2(playerX + 21, 0);
            maxPos = new GridPoint2(playerX + 40, 0);
        }

        for (int i = 0; i < NUM_OBSTACLES; i++) {
            do {
                randomPos = RandomUtils.randomX(3, minPos, maxPos);
            } while (randomPoints.contains(randomPos));
            randomPoints.add(randomPos);

            //...
            //create an entity and spawn it
            //...
        }
    }

Meteorite generation

This function spawnMeteorites() in ForestGameArea can control the number of meteorites spawned. Here, meteorites are divided into three categories according to their size. The size of each type of meteorite fluctuates randomly within a certain range. You can control the generation of different numbers of meteorites and the number of random generations of various types of meteorites by changing the parameters of the function when calling the function.

    /**
     * Generate a certain number of meteorites, called by render() in MainGameScreen.java. The final number of meteorites
     * is the sum of all parameters.
     * <p>
     * Big size meteorites: 1.5 - 2 times of the multiples of meteorites texture,
     * total number is bigNum(+bigRandomRange), the values in parentheses are random.
     * Midden size meteorites: 1 - 1.5 times of the multiples of meteorites texture,
     * total number is middleNum(+midRandomRange)
     * Small size meteorites: 0.5 + randomSize: 0.5 - 1 times of the multiples of meteorites texture,
     * total number is smallNum(+smallRandomRange)
     * <p>
     * e.g. 1(+2) means that the number of generations is at least 1, and the final possible range is 1-3.
     *
     * @param bigNum           At least the number of large meteorites generated.
     * @param middleNum        At least the number of middle meteorites generated.
     * @param smallNum         At least the number of small meteorites generated.
     * @param bigRandomRange   The number of large meteorites that may be randomly generated.
     * @param midRandomRange   The number of middle meteorites that may be randomly generated.
     * @param smallRandomRange The number of small meteorites that may be randomly generated.
     */
    public void spawnMeteorites(int bigNum, int middleNum, int smallNum, int bigRandomRange, int midRandomRange,
                                int smallRandomRange)

Throns effect: characters slow down

The deceleration effect is triggered by the collision event in ObstacleDisappear.java.

MainGameScreen.setSlowPlayer(5f);

The key to slowing down is in MainGameScreen.java. The deceleration state is global, and every time a deceleration is set, the slowPlayer will be judged. If slowPlayer is false, set the time (slowPlayerTime) required for slowPlayer and the player to decelerate, otherwise extend the deceleration time. slowPlayer() is called by render(), and every time the game is rendered, DeltaTime is subtracted and the remaining slowPlayerTime is judged. If slowPlayerTime is not less than zero, the character will move backward (the same as the idea of the character automatically forward). Otherwise, the deceleration state is false.

    private static boolean slowPlayer = false; // Character deceleration state
    private static float slowPlayerTime; // Remaining deceleration time
    /**
     * Set the player deceleration time, the value set in the function is used by slowPlayer(), and the function is
     * implemented in render(). This function called by thornsDisappear() in ObstacleDisappear.java
     *
     * @param slowPlayerTime How many seconds the player slows down.
     */
    public static void setSlowPlayer(float slowPlayerTime) {
        if (!slowPlayer) { // if the player already slow, don't set double times
            slowPlayer = true;
            MainGameScreen.slowPlayerTime = slowPlayerTime;
        } else { // double set
            MainGameScreen.slowPlayerTime += slowPlayerTime;
        }
    }


    /**
     * Slow down the player, called by render().
     */
    private void slowPlayer() {
        if (slowPlayer) {
            slowPlayerTime -= ServiceLocator.getTimeSource().getDeltaTime();
            if (slowPlayerTime > 0) {
                player.setPosition((float) (player.getPosition().x - 0.06), player.getPosition().y);
            } else {
                slowPlayer = false;
            }
        }
    }

Create Flying Monkey and Face Worm

Create flying monkey

Define the attributes of fly monkey in NPCFactory.javaAdd physics, animation, AI and other components, and generate fly monkey in ForestGameArea.javaby using spawnFlyingMonkey().

      public static Entity createFlyingMonkey(Entity target) {

       Entity Monkey = new Entity();

          AITaskComponent aiComponent =
                    new AITaskComponent()
                    .addTask(new ObstacleAttackTask(target,10,6f));

         AnimationRenderComponent animator =
                    new AnimationRenderComponent(
                    ServiceLocator.getResourceService()
                            .getAsset("images/monkey.atlas", TextureAtlas.class));

        animator.addAnimation("1m", 0.2f, Animation.PlayMode.LOOP);

        Monkey
            .addComponent(animator)
            .addComponent(aiComponent);

       animator.startAnimation("1m");
       Monkey.setScale(2.3f, 2.3f);
       logger.debug("Create a Flying Monkey");
       return Monkey;
  }

Obstacle Attack Task

ObstacleAttackTask.java is sets the trigger function according to the distance between the character and the obstacle.When the distance is close enough, getPriority() will return the set priority parameter. enemyCreatePosition() will execute and return the position of the fly monkey, Later, it will be used to spawn a face worm at this location.

    @Override
    public void start() {
        super.start();
        MainGameScreen.setSpownFacehugger(enemyCreatePosition());
    }

    /**
     * @return priority
     */
    @Override
    public int getPriority() {
        float dst = getDistanceToTarget();
        if (dst < viewDistance) {
            return priority;
        }

        return -1;
    }
     // return the position of the entity
    public Vector2 enemyCreatePosition(){
        
        return owner.getEntity().getPosition();
    }

Create face worm

Define the properties of the face bug in NPCFactory.java, and add various components to it, such as AI (Call the chase task), animation, physics, texture.

    public static Entity createFaceWorm(Entity target) {
    Entity FaceWorm = createBaseNPC(target);
    BaseEntityConfig config = configs.faceWorm;

    AnimationRenderComponent animator =
        new AnimationRenderComponent(
            ServiceLocator.getResourceService().getAsset("images/Facehugger.atlas", TextureAtlas.class));
    animator.addAnimation("baolian1", 0.1f, Animation.PlayMode.LOOP);

    FaceWorm
        .addComponent(new CombatStatsComponent(config.health, config.baseAttack))
        .addComponent(animator)
        .addComponent(new EnemyAnimationController())
        .addComponent(new ObstacleDisappear(ObstacleDisappear.ObstacleType.FaceWorm));

    FaceWorm.setScale(2.4f,2.4f);
    logger.debug("Create a Face Worm");
    return FaceWorm;
  }
  }

Trigger the Face Worm on the game area

In ForestGameArea.java Generate Face Worm at current Flying Monkeys location. Called by render() in MainGameScreen.java and position the location of flying monkeys

     public void spawnFaceWorm(Vector2 position) {
        Entity ghost = NPCFactory.createFaceWorm(player);
        spawnEntityAt(ghost, position, false, false);
   }

In MainGameScreen.java Set the location where the monster is spawned, and called by start() in ObstacleAttackTask.java, the variable is used by the spokenFacehugger(). spokenFacehugger() generate face worm based on the position of the flying monkey, which is called by render().

     public static void setSpownFacehugger(Vector2 position) {
        facehuggerPosition = position;
        spownFacehugger = true;
    }
    
    
    private void spownFacehugger() {
        if (spownFacehugger) {
            forestGameArea.spawnFaceWorm(facehuggerPosition);
            spownFacehugger = false;
        }
    }

Event Handler

Collision event

All collision events are handled in ObstacleEventHandler.java.The constructor of ObstacleDisappear requires the ObstacleType variable. When ObstacleDisappear is created, the collision event adds different processing functions according to the type of obstacles (including enemies). One of the most important things in the collision handling function is to determine the layer of itself and other entities when the event is triggered.

    /**
     * The types of obstacles and enemies are used to determine the type of entity that triggers the event.
     */
    public enum ObstacleType {
        PlantsObstacle, ThornsObstacle, Meteorite, FaceWorm;
    }

    public void create() {
        hitboxComponent = this.entity.getComponent(HitboxComponent.class);
        animator = this.entity.getComponent(AnimationRenderComponent.class);

        switch (obstacleType) {
            case PlantsObstacle:
                entity.getEvents().addListener("collisionStart", this::plantsDisappear);
                break;
            case ThornsObstacle:
                entity.getEvents().addListener("collisionStart", this::thornsDisappear);
                break;
            case Meteorite:
                entity.getEvents().addListener("collisionStart", this::meteoriteDisappear);
                break;
            case FaceWorm:
                entity.getEvents().addListener("collisionStart", this::faceWormDisappear);
                break;
            default:
                logger.error("No corresponding event.");
        }
    }

Animation and disappearance

There are three corresponding problem:

  1. The texture needs to disappear when the animation is playing, otherwise the texture and the animation will overlap.
  2. The disappearance of obstacles has to wait for the animation to play for a while before it works.
  3. Due to the animation components involved, simply using the dispose() function cannot achieve the purpose of the individual obstacle disappearing (for entities that use the same animation, one animation is disposed, and all other entities cannot load the corresponding animation).

For problem 1

In Entity.java setRemoveTexture() and update() solved the problem of removing texture. The original code of updata() is modified. Every time the component is updated, it is now determined whether the texture needs to be removed.

    /**
     * Perform an update on all components. This is called by the entity service and should not be
     * called manually.
     */
    public void update() {
        if (!enabled) {
            return;
        }

        if (dispose) {
            this.dispose();
            return;
        }

        for (Component component : createdComponents) {
            // When texture and animation are given an entity at the same time, the texture needs to disappear when the
            // animation is played to avoid the conflict between the texture and the animation.
            if (removeTexture) {
                if (component.getClass().equals(TextureRenderComponent.class)) {
                    component.dispose();
                }
            }
            component.triggerUpdate();
        }
        if (disappear) {
            this.removeAfterAnimation();
            return;
        }
    }

For problems 2 and 3

setDisappear(), update() and removeAfterAnimation() used to solve these two problems. setDisappear() can set how long the animation will disappear after playing, and set the disappear variable of the current entity to true. Every time the component is updated, determine whether disappear is set to true. If so, call removeAfterAnimation(). In removeAfterAnimation(), get the duration of the animation that has been played. When the time length is greater than animationTime, pause the animation and dispose of all components except the animation component.

    /**
     * Set disappear to true. These variables play a role in removeAfterAnimation() and update().
     * @param animationTime Set how long the animation will disappear after playing
     */
    public void setDisappearAfterAnimation(float animationTime) {
        this.disappear = true;
        this.animationTime = animationTime;
    }

    /**
     * Let the obstacles disappear after playing the animation for animationTime second. Is called by update().
     *
     * The purpose of setting this method: When dispose() is used for animation components, all entities that use the
     * same animation become black boxes. Therefore, this method is currently used to make obstacles disappear.
     */
    public void removeAfterAnimation() {
        if (this.getComponent(AnimationRenderComponent.class).getAnimationPlayTime() > animationTime) {
            for (Component component : createdComponents) {
                if (component.getClass().equals(AnimationRenderComponent.class)) {
                    ((AnimationRenderComponent) component).stopAnimation();
                } else {
                    component.dispose();
                }
            }
            ServiceLocator.getEntityService().unregister(this);
        }
    }

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