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();
}
}
}
