Guilherme Oliveira

Guilherme de Oliveira

Gameplay Programmer

Your Human is Sick

Project Type: Capstone (University)
Engine: Unreal
Languages: C++, Blueprints

Your Human is Sick is the most interesting project that I've taken part of. I'm taking a Master's in Game Design, and in Capstone we, the students, can come up with our own games and make them, as long as we go through the process of pitching the game and creating the documentation for it, of course the pipeline and team should be able to do it (and the team has to agree and be motivated to do it too) - With all that being said, the idea of making a top-down adventure game inspired in The Legend of Zelda that was about the Immune System was too good for me to let pass. Time went by and with the documentation provided I was able to put the game oficially in pipeline!

The ones who created the documentation are tasked as the Creative Director of the game, which its main job is to supervise the team and guide the team to ensure the game is always on track from a creative point of view. Besides that, it was also agreed with the team I would be able to do some development tasks, and I worked on some basic C++ behaviors and classes.

But after that, an interesting story started to happen, because of the nature of the game and the team, the game moved so fast and iterated so fast that Blueprints were the best approach, most of the things would slow the process if they were tackled in C++ - So I stepped down and removed my developer hat and focused on Creative Director endeavours. More often than not I will still perform reviews and tweak and add small things in Blueprints, mainly adding polish tasks (i.e. spawning particles and playing sounds) or tweaking or removing some mechanics that I, as a Creative Director, don't really see adding much to the game anymore.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "FloorSwitch.generated.h"

UCLASS()
class YOURHUMANISSICK_API AFloorSwitch : public AActor {
  GENERATED_BODY()

public:
  AFloorSwitch();

  UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Floor Switch")
  class UBoxComponent* pTriggerBox;

  UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Floor Switch")
  class UStaticMeshComponent* pFloorSwitchMesh;

  UPROPERTY(BlueprintReadWrite, Category = "Floor Switch")
  FVector InitialSwitchLocation;

  /** Time to raise the Floor Switch */
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Floor Switch")
  float SwitchTime;

  /** One time Switches does not raise up again */
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Floor Switch")
  bool bIsOneTimeSwitch;

  /** Timer Handle so the box does not immediately deactivate */
  FTimerHandle SwitchTimerHandle;

  int NumberOfCharactersOnSwitch;

  /** Was activated? Used to not activate one-time switches more than once */
  bool bWasSwitchActivated;

  UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Floor Switch")
  bool bIsFloorSwitchPressed;

  FORCEINLINE bool GetFloorSwitchPressed() const { return bIsFloorSwitchPressed; }

protected:
  virtual void BeginPlay() override;

public:	
  virtual void Tick(float DeltaTime) override;

  UFUNCTION()
  void OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

  UFUNCTION()
  void OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

  /** Actions when the player steps on the floor switch */
  UFUNCTION(BlueprintImplementableEvent, Category = "Floor Switch")
  void SteppedOnFloorSwitch();

  UFUNCTION(BlueprintImplementableEvent, Category = "Floor Switch")
  void LeftFloorSwitch();

  /** Floor Switch Position Update*/
  UFUNCTION(BlueprintImplementableEvent, Category = "Floor Switch")
  void LowerFloorSwitch();

  UFUNCTION(BlueprintImplementableEvent, Category = "Floor Switch")
  void RaiseFloorSwitch();

  UFUNCTION(BlueprintCallable, Category = "Floor Switch")
  void UpdateSwitchLocation(float ZIncrement);

  void LeftFloorSwitchProcedure();
};                        
                        

void AFloorSwitch::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) {
  UE_LOG(LogTemp, Warning, TEXT("Floor Switch - Player Stepped"))
  if (!bWasSwitchActivated && (NumberOfCharactersOnSwitch == 0) ) {
    bWasSwitchActivated = true;
    LowerFloorSwitch();
    SteppedOnFloorSwitch();
    bIsFloorSwitchPressed = true;
  }

  NumberOfCharactersOnSwitch++;
}

void AFloorSwitch::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex) {
  UE_LOG(LogTemp, Warning, TEXT("Floor Switch - Player Left"))

  // We only call the left floor switch if it is not a one time only switch
  if (!bIsOneTimeSwitch && (--NumberOfCharactersOnSwitch == 0) ) {
    GetWorldTimerManager().SetTimer(SwitchTimerHandle, this, &AFloorSwitch::LeftFloorSwitchProcedure, SwitchTime);
    bWasSwitchActivated = false;
    bIsFloorSwitchPressed = false;
  }
}                      
                        

When the project started to reach its end, a lot of small holes remain unfilled, and designers and developers in the game already had many tasks and a lot of small fixes to do, so for that I had to put my developer hat back on! Two big things that I made was integrating player and enemy animations and making functionality for the user interface, we didn't have people versatile with any those two aspects in the team, so it went to the bottom of priorities, but I believe it was important to do those things, and do them right, so I took some time to get back into the game and do those things.

Enemy Animations

Creating animations in Unreal usually involves a lot moving pieces, changes have to be made on the character blueprint, an animation blueprint has to be created, a state machine for the animation transition has to be set up and if you want to get a little bit fancy you have to use Animation Montages.

I implemented animations on the player character and in four different enemy types, on the video above and on the images below you can see how the Charging Enemy works, it's listed the animation graph, the locomotion state machine and the event graph, there is also the blueprint for the Charging Enemy Character, but it just sets bools with information given by the Behavior Tree.

User Interface Functionality

An interesting point that I could make in the game was the User Interface functionality, such as scene transitions, save/load game screens, options screens, controls screens, credits screen, etc... Previously I had only very basic experience with Unreal Widgets and it was the first making an UI that supported controller in Unreal.

A point worth mentioning is the "Confirmation Screen" - It appears when you select if you want to go to the main menu, retry the level, etc... And it prompts a screen saying "Are you sure you want to do this?" - The thing is that I wanted to reuse the Widget, so the solution I came up with is having Event Dispatchers for the "Yes" and "No" options, so wichever part of the game calls this widget is responsible for saying what "Yes" and "No" means, an example is illustrated on the images below.