Resource Collection Functionality - UQdeco2800/2022-studio-3 GitHub Wiki

Involved Components

  • WorkerInventoryComponent: a component placed on worker units to store collected wood, stone, and metal
  • CollectStatsComponent: a component placed on worker units to specify how much of a resource they can collect
  • ResourceStatsComponent: a component placed on resource entities to specify how much of each resource they contain, and to allow harvest by worker units
  • ResourceCollectComponent: a component placed on worker units that are used to collect resources from valid entities on collision.

Involved Config

  • ResourceConfig: Defines the properties stored in player config files to be loaded by the Player Factory.

Involved Factories

  • WorkerFactory: default spawn method for a worker unit
  • ForagerFactory: spawns a forager unit, which collects wood from Trees generated in TreeFactory
  • MinerFactory: spawns a miner unit, which collects stone/metal from Stones generated in StoneFactory
  • WorkerBaseFactory: spawns a worker base which will be used by workers to drop off resources for the player
  • StoneFactory: spawns a stone entity, from which a Miner unit can collect stone/metal
  • TreeFactory: spawns a tree entity, from which a Forager unit can collect wood

Current Implementation

To demonstrate the collection of resources functionality, the UI on the screen displays how many resources the worker collects from the resource entity on collision. The onCollisionStart function is added as an event listener in the ResourceCollectComponent (a component of the worker) and is triggered when the worker comes into a collision with a resource entity such as a tree or a stone. If the worker is a forager unit they can only collect wood and if they are a miner they can only collect stone. What they collect is then added to their WorkerInventoryComponent and the resources that the resource entity has is deducted from their ResourceStatsComponent. How many they can collect in the elapsed time is defined in the CollectStatsComponent. The log to console also displays when the worker loads to the base, listing how many of each resource the Base has in its current state.

private void onCollisionStart(Fixture me, Fixture other) {
        if (hitboxComponent.getFixture() != me) {
            // Not triggered by hitbox, ignore
            return;
        }

        if (!PhysicsLayer.contains(targetLayer, other.getFilterData().categoryBits)) {
            // Doesn't match our target layer, ignore
            return;
        }

        // Try to collect resources from target
        Entity target = ((BodyUserData) other.getBody().getUserData()).entity;
        ResourceStatsComponent targetStats = target.getComponent(ResourceStatsComponent.class);

        if (targetStats == null) {
            logger.info("Resource Stats not found");
            return;
        }

        BaseComponent isBase = target.getComponent(BaseComponent.class);
        if (isBase != null) {
            // checks if collided entity is a base
            loadToBase(targetStats);
            return;
        }

        Entity collector = ((BodyUserData) me.getBody().getUserData()).entity;
        MinerComponent collectorIsMiner = collector.getComponent(MinerComponent.class);
        ForagerComponent collectorIsForager = collector.getComponent(ForagerComponent.class);

        if (this.lastTimeMined == 0 || this.gameTime.getTimeSince(this.lastTimeMined) >= COLLECTION_TIME) {
            // if enough time has elapsed for the collector to collect the resource
            if (collectorIsMiner != null) {
                // If the worker type is Miner
                collectStone(targetStats);
            } else if (collectorIsForager != null){
                // If the worker type is Forager
                collectWood(targetStats);
            }
            this.lastTimeMined = this.gameTime.getTime();                    
        }
    }

Resource Collection Animation

In order to achieve a greater level of user experience, and to indicate to the user that his actions were having an effect on the game entities in real time, an animation was implemented. The animation is played when the user decides to use a forager to collect a wood resource from a tree entity.

(Image Credits: Wishnu)

When working on the class TreeFactory, it was necessary to use a single png file for all images of the tree, whether it be of the tree entity being idle, or being destroyed. The use of separate images along with atlas files for each resulted in subsequent errors being generated. To make this convenient, different loops in the same atlas file were used.


public static Entity createTree(){

        AnimationRenderComponent animator =
                new AnimationRenderComponent(
                        ServiceLocator.getResourceService()
                                .getAsset("images/tree_.atlas", TextureAtlas.class));
        animator.addAnimation("tree_damaged", 0.5f, Animation.PlayMode.NORMAL);
        animator.addAnimation("tree_idle", 1f, Animation.PlayMode.LOOP);
        animator.addAnimation("tree_destroyed", 1f, Animation.PlayMode.LOOP);



        Entity tree = new Entity()
            .addComponent(new PhysicsComponent())
            .addComponent(new ColliderComponent())
            .addComponent(new HitboxComponent().setLayer(PhysicsLayer.RESOURCE_NODE))
            .addComponent(new ResourceStatsComponent(stats.wood, stats.stone, stats.metal))
            .addComponent(new TreeComponent())
            .addComponent(animator);
        tree.getComponent(PhysicsComponent.class).setBodyType(BodyDef.BodyType.StaticBody);
        tree.getComponent(AnimationRenderComponent.class).startAnimation("tree_idle");

        return tree;
    }

Previously, when the tree entity spawned, a texture render component was used for the png. However, this was changed to an idle animation.

Subsequently, in the ResourceCollectComponent class, the tree_damaged animation was triggered on the entity target, which would be the tree in this instance, when the method startCollecting was called.


 startCollecting(me, other);
        if (targetStats.isDead()) {
            target.getComponent(AnimationRenderComponent.class).startAnimation("tree_damaged");
            stopCollecting();
            collector.getEvents().trigger("workerIdleAnimate");
            ServiceLocator.getEntityService().unregister(target);
            returnToBase(collector);