Guilherme Oliveira

Guilherme de Oliveira

Gameplay Programmer

Unreal Gameplay

Project Type: Gameplay Practice Projects
Engine: Unreal
Languages: C++, Blueprints

During my free time I enjoy practicing and prototyping Gameplay Mechanics, this way I can always be improving and seeing different challenges that can come up in this job. I enjoy taking high quality assets and putting things together, adding the design and code part of the equation to it - in these examples, I'm using one Paragon character (Sparrow) and a free model from the Mixamo website.

Unreal Sparrow

The Challenge I'm imposing to myself with this one is getting something that was made for a game, in this case, Paragon, and how I can adapt it to another type of game and putting all these things together. I always wanted to prototype a shooting mechanic with a bow, so I decided to use Sparrow to try this.

The Sparrow Gameplay Character has 4 main states: BowDown, BowAiming, FastShot and HeavyShot, with a simple difference between those.

  • Bow Down: High velocity and no aiming capabilities
  • Bow Aiming: Medium Velocity, can aim to prepare to shoot
  • Fast Shot: Locks movements for a little while, shots for low damage
  • Heavy Shot: Locks movement while charging bow, player has to release key to shot for higher damage

UENUM(BlueprintType)
enum class EBowStatus : uint8 {
  EBS_BowDown       UMETA(DisplayName = "BowDown"),
  EBS_BowAiming     UMETA(DisplayName = "BowAiming"),
  EBS_FastShot      UMETA(DisplayName = "FastShot"),
  EBS_HeavyShot     UMETA(DisplayName = "HeavyShot"),
  EBS_MAX           UMETA(DisplayName = "DefautMAX")
};
                        

void AThirdPersonShooterCharacter::MoveForward(float Value) {
  LastFrameUpValue = Value;
  MoveCharacterWithAxis(EAxis::X, Value);
}

void AThirdPersonShooterCharacter::MoveRight(float Value) {
  LastFrameRightValue = Value;
  MoveCharacterWithAxis(EAxis::Y, Value);
}

void AThirdPersonShooterCharacter::MoveCharacterWithAxis(EAxis::Type AxisType, float Value) {
  if (Controller != nullptr && Value != 0.0f) {
    const FRotator Rotation = Controller->GetControlRotation();
    const FRotator YawRotation = FRotator(0.0f, Rotation.Yaw, 0.0f);
    const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(AxisType);
    AddMovementInput(Direction, Value);
  }
}

/*
	Set the Current Bow Status for the Player Character
	- The only reason this function is not FORCEINLINE is that we might want to execute some behavior when going to some states
*/
void AThirdPersonShooterCharacter::SetBowStatus(EBowStatus BowStatus) {
	if (BowStatus == EBowStatus::EBS_BowDown) {
		GetCharacterMovement()->MaxWalkSpeed = BowAimingPlayerVelocity;
		GetCharacterMovement()->bOrientRotationToMovement = true;
		GetCharacterMovement()->bUseControllerDesiredRotation = false;
	}
	else if (BowStatus == EBowStatus::EBS_BowAiming) {
		GetCharacterMovement()->MaxWalkSpeed = BowDownPlayerVelocity;
		GetCharacterMovement()->bOrientRotationToMovement = false;
		GetCharacterMovement()->bUseControllerDesiredRotation = true;
	}

	CurrentBowStatus = BowStatus;
}
                        

ARPG Demo

The point of this Demo is to challenge myself into making an Action RPG combat mechanic, Dark Souls and Bloodborne are games that I take as a reference for everything as a designer and developer, so trying and understanding what goes into that type of combat is something that takes me interest!

void AMainCharacter::Attack() {
  if (!bIsAttacking) {
    bIsAttacking = true;

    UAnimInstance* pAnimInstance = GetMesh()->GetAnimInstance();
    if (pAnimInstance != nullptr && pCombatMontage != nullptr) {
      int32 Section = FMath::RandRange(0, 1);

      switch (Section) {
      case 0:
        pAnimInstance->Montage_Play(pCombatMontage, 1.5f);
        pAnimInstance->Montage_JumpToSection(FName("Attack_2"), pCombatMontage);
        break;
      case 1:
        pAnimInstance->Montage_Play(pCombatMontage, 2.0f);
        pAnimInstance->Montage_JumpToSection(FName("Attack_1"), pCombatMontage);
        break;
      default:
        break;
      }
    }
  }
}

void AMainCharacter::FinishedAttack() {
  bIsAttacking = false;

  if (bLeftMouseButtonDown) {
    Attack();
  }
}
                        

void AWeapon::EquipWeaponIntoCharacter(AMainCharacter* CharacterToEquip) {
  if (CharacterToEquip != nullptr) {
    /** Ignoring the collision with the camera, so it doesn't affect the spring arm */
    pWeaponSkeletalMesh->SetCollisionResponseToChannel(ECollisionChannel::ECC_Camera, ECollisionResponse::ECR_Ignore);
    pWeaponSkeletalMesh->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Ignore);
    pWeaponSkeletalMesh->SetSimulatePhysics(false);

    const USkeletalMeshSocket* pRightHandSocket = CharacterToEquip->GetMesh()->GetSocketByName("RightHandSocket");

    if (pRightHandSocket != nullptr) {
      pRightHandSocket->AttachActor(this, CharacterToEquip->GetMesh());
      bRotate = false;
      
      AWeapon* CurrentlyEquippedWeapon = CharacterToEquip->GetEquippedWeapon();
      
      if (CurrentlyEquippedWeapon != nullptr) {
        CurrentlyEquippedWeapon->Destroy();
      }

      CharacterToEquip->SetEquippedWeapon(this);
      CharacterToEquip->SetActiveOverlappingItem(nullptr);
    }

    if (pOnEquipSound != nullptr) {
      UGameplayStatics::PlaySound2D(this, pOnEquipSound);
    }

    if (!bWeaponParticle) {
      pIdleParticlesComponent->Deactivate();
    }
  }
}