0% found this document useful (0 votes)
169 views

C++ UAnimInstance Examples

The document provides an example of C++ code for a UAnimInstance class. It includes a character class with methods for movement, firing, and playing animations. When firing, it gets the animation instance and calls Montage_Play to play the firing animation.

Uploaded by

Nenad
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
169 views

C++ UAnimInstance Examples

The document provides an example of C++ code for a UAnimInstance class. It includes a character class with methods for movement, firing, and playing animations. When firing, it gets the animation instance and calls Montage_Play to play the firing animation.

Uploaded by

Nenad
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 101

C++ UAnimInstance

UAnimInstance::Montage_Play Examples

These are the top rated real world C++ (Cpp) examples of UAnimInstance::Montage_Play extracted from open source projects. You can rate examples to help us
improve the quality of examples.

Link: https://cpp.hotexamples.com/examples/-/UAnimInstance/Montage_Play/cpp-uaniminstance-montage_play-method-examples.html

EXAMPLE #1

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#include "MobileOpenCV.h"
#include "MobileOpenCVCharacter.h"
#include "MobileOpenCVProjectile.h"
#include "Animation/AnimInstance.h"
#include "GameFramework/InputSettings.h"

DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);

//////////////////////////////////////////////////////////////////////////
// AMobileOpenCVCharacter

AMobileOpenCVCharacter::AMobileOpenCVCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

// set our turn rates for input


BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;

// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
FirstPersonCameraComponent->RelativeLocation = FVector(0, 0, 64.f); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;

// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true);
Mesh1P->AttachParent = FirstPersonCameraComponent;
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;

// Create a gun mesh component


FP_Gun = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FP_Gun"));
FP_Gun->SetOnlyOwnerSee(true); // only the owning player will see this mesh
FP_Gun->bCastDynamicShadow = false;
FP_Gun->CastShadow = false;
FP_Gun->AttachTo(Mesh1P, TEXT("GripPoint"), EAttachLocation::SnapToTargetIncludingScale, true);

// Default offset from the character location for projectiles to spawn


GunOffset = FVector(100.0f, 30.0f, 10.0f);

// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}

//////////////////////////////////////////////////////////////////////////
// Input

void AMobileOpenCVCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)


{
// set up gameplay key bindings
check(InputComponent);

InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);


InputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);

//InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AMobileOpenCVCharacter::TouchStarted);


if( EnableTouchscreenMovement(InputComponent) == false )
{
InputComponent->BindAction("Fire", IE_Pressed, this, &AMobileOpenCVCharacter::OnFire);
}

InputComponent->BindAxis("MoveForward", this, &AMobileOpenCVCharacter::MoveForward);


InputComponent->BindAxis("MoveRight", this, &AMobileOpenCVCharacter::MoveRight);

// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
InputComponent->BindAxis("TurnRate", this, &AMobileOpenCVCharacter::TurnAtRate);
InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
InputComponent->BindAxis("LookUpRate", this, &AMobileOpenCVCharacter::LookUpAtRate);
}

void AMobileOpenCVCharacter::OnFire()
{
// try and fire a projectile
if (ProjectileClass != NULL)
{
const FRotator SpawnRotation = GetControlRotation();
// MuzzleOffset is in camera space, so transform it to world space before offsetting from the character location to find the final muzzle pos ition
const FVector SpawnLocation = GetActorLocation() + SpawnRotation.RotateVector(GunOffset);

UWorld* const World = GetWorld();


if (World != NULL)
{
// spawn the projectile at the muzzle
World->SpawnActor<AMobileOpenCVProjectile>(ProjectileClass, SpawnLocation, SpawnRotation);
}
}

// try and play the sound if specified


if (FireSound != NULL)
{
UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
}

// try and play a firing animation if specified


if(FireAnimation != NULL)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if(AnimInstance != NULL)
{
AnimInstance->Montage_Play(FireAnimation, 1.f);
}
}

void AMobileOpenCVCharacter::BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if( TouchItem.bIsPressed == true )
{
return;
}
TouchItem.bIsPressed = true;
TouchItem.FingerIndex = FingerIndex;
TouchItem.Location = Location;
TouchItem.bMoved = false;
}

void AMobileOpenCVCharacter::EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if (TouchItem.bIsPressed == false)
{
return;
}
if( ( FingerIndex == TouchItem.FingerIndex ) && (TouchItem.bMoved == false) )
{
OnFire();
}
TouchItem.bIsPressed = false;
}

void AMobileOpenCVCharacter::TouchUpdate(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if ((TouchItem.bIsPressed == true) && ( TouchItem.FingerIndex==FingerIndex))
{
if (TouchItem.bIsPressed)
{
if (GetWorld() != nullptr)
{
UGameViewportClient* ViewportClient = GetWorld()->GetGameViewport();
if (ViewportClient != nullptr)
{
FVector MoveDelta = Location - TouchItem.Location;
FVector2D ScreenSize;
ViewportClient->GetViewportSize(ScreenSize);
FVector2D ScaledDelta = FVector2D( MoveDelta.X, MoveDelta.Y) / ScreenSize;

if (ScaledDelta.X != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.X * BaseTurnRate;
AddControllerYawInput(Value);
}
if (ScaledDelta.Y != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.Y* BaseTurnRate;
AddControllerPitchInput(Value);
}
TouchItem.Location = Location;
}
TouchItem.Location = Location;
}
}
}
}

void AMobileOpenCVCharacter::MoveForward(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorForwardVector(), Value);
}
}

void AMobileOpenCVCharacter::MoveRight(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
}
}

void AMobileOpenCVCharacter::TurnAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void AMobileOpenCVCharacter::LookUpAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

bool AMobileOpenCVCharacter::EnableTouchscreenMovement(class UInputComponent* InputComponent)


{
bool bResult = false;
if(FPlatformMisc::GetUseVirtualJoysticks() || GetDefault<UInputSettings>()->bUseMouseForTouch )
{
bResult = true;
InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AMobileOpenCVCharacter::BeginTouch);
InputComponent->BindTouch(EInputEvent::IE_Released, this, &AMobileOpenCVCharacter::EndTouch);
InputComponent->BindTouch(EInputEvent::IE_Repeat, this, &AMobileOpenCVCharacter::TouchUpdate);
}
return bResult;
}
EXAMPLE #2

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#include "MurphysLaw.h"
#include "MurphysLawCharacter.h"
#include "../Network/MurphysLawPlayerState.h"
#include "../Weapon/MurphysLawBaseWeapon.h"
#include "../HUD/MurphysLawHUDWidget.h"
#include "../Components/MurphysLawInventoryComponent.h"
#include "../Menu/MurphysLawInGameMenu.h"
#include "../Network/MurphysLawPlayerController.h"
#include "../AI/MurphysLawAIController.h"
#include "WidgetComponent.h"
#include "Blueprint/UserWidget.h"
#include "Animation/AnimInstance.h"
#include "UnrealNetwork.h"
#include "../DamageZone/MurphysLawDamageZone.h"
#include "../Environnement/ExplosiveBarrel/MurphysLawExplosiveBarrel.h"
#include "MurphysLawNameplateWidget.h"
#include "Kismet/KismetMathLibrary.h"
#include "../Network/MurphysLawGameMode.h"
#include "../Network/MurphysLawGameState.h"

#include <MurphysLaw/Interface/MurphysLawIController.h>
#include <MurphysLaw/Utils/MurphysLawUtils.h>

DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);

//////////////////////////////////////////////////////////////////////////
// AMurphysLawCharacter

const float AMurphysLawCharacter::DefaultAimFactor = 90.0f;


const FName AMurphysLawCharacter::MATERIAL_PARAM_TEAM_COLOR_CLOTHES("TeamClothesColor");
const FName AMurphysLawCharacter::MATERIAL_PARAM_TEAM_COLOR_MASK("TeamMaskColor");
const float AMurphysLawCharacter::ROTATION_RATE_HUMAN(360.f);
const float AMurphysLawCharacter::ROTATION_RATE_BOT(160.f);
const float AMurphysLawCharacter::FACTOR_HEADSHOT(3.f);
const float AMurphysLawCharacter::FACTOR_CHESTSHOT(1.5f);
const FString AMurphysLawCharacter::SOCKET_HEAD = "Head";
const FString AMurphysLawCharacter::SOCKET_SPINE = "Spine1";

AMurphysLawCharacter::AMurphysLawCharacter()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;

// Set size for collision capsule


GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

// set our turn rates for input


BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;

// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
FirstPersonCameraComponent->RelativeLocation = FVector(0, 0, 64.f); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;
// Create a SceneComponent
SceneComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
SceneComponent->AttachParent = GetCapsuleComponent();
SceneComponent->RelativeLocation = FVector(-20, 0, 120);

// Create a CharacterNameplate
CharacterNameplate = CreateDefaultSubobject<UWidgetComponent>(TEXT("CharacterNameplate"));
CharacterNameplate->AttachParent = SceneComponent;

// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true);
Mesh1P->AttachParent = FirstPersonCameraComponent;
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;

GetMesh()->SetCollisionObjectType(ECC_PhysicsBody);
GetMesh()->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
GetMesh()->SetCollisionResponseToChannel(ECC_Visibility, ECR_Ignore);
GetMesh()->SetCollisionResponseToAllChannels(ECR_Block);

// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)

// Set base health level for the character


MaxHealth = 100.f;
Dead = false;
IsRunning = false;

// Set the Index so the character starts with no weapon


CurrentWeaponIndex = NO_WEAPON_VALUE;

// Creates the inventory component to store the weapons of the character


Inventory = CreateDefaultSubobject<UMurphysLawInventoryComponent>(TEXT("InventoryComponent"));
checkf(Inventory != nullptr, TEXT("Inventory has not been initialized correctly"));

// Movement related
IsCrouched = false;
GetCharacterMovement()->GetNavAgentPropertiesRef().bCanCrouch = true;
ConfigureMovement(false); // Configure as human to see human related values in editor

// AI and possessing settings from APawm


AIControllerClass = nullptr; // Should be a subclass of AMurphysLawAIController
AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;
AutoPossessPlayer = EAutoReceiveInput::Disabled;

// Spawning settings
SpawnCollisionHandlingMethod = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;

// Try to load the sound of switching weapon


static ConstructorHelpers::FObjectFinder<USoundBase> SwitchWeaponSound(TEXT("SoundWave'/Game/MurphysLawRessources/Weapons/S_Switch_Weapon.S_Switch_Weapon'"));
SwitchingWeaponSound = nullptr;
if (SwitchWeaponSound.Succeeded())
{
SwitchingWeaponSound = SwitchWeaponSound.Object;
}

// Sets up the stamina info


MaxStamina = 100.f;
RunningStaminaDecayRate = 0.4f;
JumpStaminaDecayAmount = 7.5f;
StaminaRegenerationRate = 0.07f;

// Team color flags


ValidTeamBodyMeshColor = false;
ValidTeamMaskMeshColor = false;
}

// Indicates to the server what properties of the object to replicate on the clients
void AMurphysLawCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty> &OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);

DOREPLIFETIME(AMurphysLawCharacter, CurrentWeaponIndex);
DOREPLIFETIME(AMurphysLawCharacter, CurrentHealth);
DOREPLIFETIME(AMurphysLawCharacter, Dead);
DOREPLIFETIME(AMurphysLawCharacter, TeamBodyMeshColor);
DOREPLIFETIME(AMurphysLawCharacter, TeamMaskMeshColor);
}

void AMurphysLawCharacter::BeginPlay()
{
Super::BeginPlay();

// AI settings ressources
if (AIControllerClass == nullptr) ShowWarning("No AIController class assigned to MurphysLawCharacter");

if (SwitchingWeaponSound == nullptr) ShowWarning("MurphysLawCharacter - Unable to load the SwitchingWeaponSound");

// If the InventoryComponent's BeginPlay has not been called yet, we call it


if (!Inventory->HasBegunPlay())
{
Inventory->BeginPlay();
}

EquipFirstWeapon();

// Set the current health of the character here in case the value has been overriden in a subclass
CurrentHealth = MaxHealth;

auto NameplateWidget = Cast<UMurphysLawNameplateWidget>(CharacterNameplate->GetUserWidgetObject());


if (NameplateWidget)
{
NameplateWidget->SetCharacter(this);
}

// Sets the current stamina level to the maximum


CurrentStamina = MaxStamina;
}

// Called when game ends


void AMurphysLawCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);

// Clear the inventory


if (Inventory->HasBegunPlay()) Inventory->EndPlay(EndPlayReason);
}

// Called when the character is possessed by a new controller


void AMurphysLawCharacter::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);

if (NewController != nullptr)
{
// Update settings of movement component to allow "natural" movement for bots
const bool ConfigureAsBot = NewController->IsA<AAIController>();
ConfigureMovement(ConfigureAsBot);
}
}

/** Configure character movement with human or bot specific settings */


void AMurphysLawCharacter::ConfigureMovement(const bool ConfigureForBot)
{
if (ConfigureForBot)
{ // Bot movement configuration

// Allow looking in different direction than movement


bUseControllerRotationYaw = true;
GetCharacterMovement()->bOrientRotationToMovement = false;

// Change left/right rotation speed


GetCharacterMovement()->RotationRate.Yaw = ROTATION_RATE_BOT;
}
else
{ // Human movement configuration

// Allow looking in different direction than movement


bUseControllerRotationYaw = true;
GetCharacterMovement()->bOrientRotationToMovement = false;

// Change left/right rotation speed


GetCharacterMovement()->RotationRate.Yaw = ROTATION_RATE_HUMAN;
}
}

/** Defines a world-space point where an ai should look


Since the gun is located in the center of the body, it is a good focal point */
AActor* AMurphysLawCharacter::GetFocalPoint() const { return GetEquippedWeapon(); }

void AMurphysLawCharacter::Tick(float DeltaSeconds)


{
Super::Tick(DeltaSeconds);

if (FirstPersonCameraComponent != nullptr)
{
// Gets the camera angle for the mini-map (to rotate it as the player rotates the player)
Bearing = FirstPersonCameraComponent->GetComponentRotation().Yaw;
}

// Calculate the current regeneration rate according on character's movement


const bool IsMoving = GetVelocity().Size() != 0.f;
const float CurrentRegenerationRate = IsMoving ? StaminaRegenerationRate : StaminaRegenerationRate * 2;

// Raise the level of stamina overtime


UpdateStaminaLevel(CurrentRegenerationRate * DeltaSeconds * GetMaxStaminaLevel());

// If the character is not moving, we deactivate his running so he doesn't lose stamina
if (!IsMoving)
{
SetIsRunning(false);
}

// But lower the stamina if the player is running


if (IsRunning)
{
UpdateStaminaLevel(-RunningStaminaDecayRate * DeltaSeconds * GetMaxStaminaLevel());

// If the character is out of breath, it stops running


if (GetCurrentStaminaLevel() <= 0.f)
{
SetIsRunning(false);
}
}
if (this->GetMovementComponent()->IsFalling())
{
SetIsInAir(true);
HighestZ = FMath::Max(HighestZ, GetActorLocation().Z);
}
else
{
float DeltaZ = (HighestZ - GetActorLocation().Z) / 10.f;
//On ne veut pas de dégât pour les petits sauts...
//Pour calculer le dégât fait en sautant de la tour
if (DeltaZ > 100)
{
FHitResult HitResult;
float Damage = DeltaZ * 0.35f;
FVector HurtDirection = GetActorLocation();
FPointDamageEvent CollisionDamageEvent(Damage, HitResult, HurtDirection, UDamageType::StaticClass());

if (Role == ROLE_Authority)
{
TakeDamage(Damage, CollisionDamageEvent, GetController(), this);
}
}

//Pour calculer le dégât fait en sautant du balcon


else if (DeltaZ > 30)
{
FHitResult HitResult;
float Damage = DeltaZ * 0.5f;
FVector HurtDirection = GetActorLocation();
FPointDamageEvent CollisionDamageEvent(Damage, HitResult, HurtDirection, UDamageType::StaticClass());

if (Role == ROLE_Authority)
{
TakeDamage(Damage, CollisionDamageEvent, GetController(), this);
}
}

HighestZ = 0;
SetIsInAir(false);
}
/** [PS] DO NOT REMOVE - Trying to make it work */

//ACharacter* myCharacter = UGameplayStatics::GetPlayerCharacter(GetWorld(), 0);


//if (myCharacter) //Maybe check character
//{
// FVector charLocation = myCharacter->GetActorLocation();
// FVector sceneLocation = SceneComponent->GetComponentLocation();
//
// FRotator PlayerRot = UKismetMathLibrary::FindLookAtRotation(sceneLocation, charLocation);
// float X, Y, Z;

// UKismetMathLibrary::BreakRotator(PlayerRot, X, Y, Z);
// FRotator Result = UKismetMathLibrary::MakeRotator(0.f, 0.f, Z);
// //UKismetMathLibrary::BreakRotIntoAxes(PlayerRot, X, Y, Z);
//
// SceneComponent->SetWorldLocationAndRotation(sceneLocation, Result);
// /*SceneComponent->SetWorldRotation(FRotationMatrix::MakeFromXY(X, Y).ToQuat());*/
//}
}

//////////////////////////////////////////////////////////////////////////
// Input

bool AMurphysLawCharacter::IsInAir() const


{
return InAir;
}

void AMurphysLawCharacter::SetIsInAir(bool isInAir)


{
InAir = isInAir;
}

void AMurphysLawCharacter::Aim()
{
if (HasWeaponEquipped())
{
FirstPersonCameraComponent->FieldOfView = GetEquippedWeapon()->AimFactor;
IsCharacterAiming = true;
}
}

void AMurphysLawCharacter::StopAiming()
{
if (HasWeaponEquipped())
{
FirstPersonCameraComponent->FieldOfView = DefaultAimFactor;
IsCharacterAiming = false;
}
}

void AMurphysLawCharacter::MoveForward(float Value)


{
if (Value != 0.0f && CanPlayerMove())
{
// If the character is going backward, he can't be running
if (Value < 0.f)
{
SetIsRunning(false);
}

// add movement in that direction


AddMovementInput(GetActorForwardVector(), Value);
}
}

void AMurphysLawCharacter::MoveRight(float Value)


{
if (Value != 0.0f && CanPlayerMove())
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
}
}

void AMurphysLawCharacter::Run()
{
SetIsRunning(true);
}

void AMurphysLawCharacter::StopRunning()
{
SetIsRunning(false);
}
///////////////////////////////////////////////////////////////////////////

#pragma region Health functions

// Reports current health level of the character


float AMurphysLawCharacter::GetCurrentHealthLevel() const { return CurrentHealth; }

// Reports maximum health level of the character


float AMurphysLawCharacter::GetMaxHealthLevel() const { return MaxHealth; }

// Increase the health of the character


void AMurphysLawCharacter::ReceiveHealAmount(const float HealingAmount)
{
checkf(HealingAmount >= 0, TEXT("HealingAmount needs to be higher than zero"));
CurrentHealth = FMath::Min(CurrentHealth + HealingAmount, MaxHealth);
}

#pragma endregion

// Refills ammos for the current equipped weapon


void AMurphysLawCharacter::ReceiveAmmo(const int32 NumberOfAmmo)
{
GetEquippedWeapon()->AddAmmoInInventory(NumberOfAmmo);

// If the equipped weapon is empty when picking up ammos, it auto-reloads


if (ShouldReload())
{
Reload();
}
}

// Refills ammos for the collected weapon or collects it if character didn't have it yet
void AMurphysLawCharacter::CollectWeapon(class AMurphysLawBaseWeapon* Weapon)
{
Inventory->CollectWeapon(Weapon);

// If the equipped weapon is empty when picking up the weapon and it's the same, it auto-reloads
if (HasWeaponEquipped()
&& GetEquippedWeapon()->IsOfSameType(Weapon)
&& ShouldReload())
{
Reload();
}
}

// Makes the character die


void AMurphysLawCharacter::Die()
{
Dead = true;

IMurphysLawIController* PlayerController = Cast<IMurphysLawIController>(Controller);


if (PlayerController)
{
PlayerController->OnKilled(TimeToRespawn);
}

// Disable collisions for the actor as he's dead


SetActorEnableCollision(false);

// Hide the current weapon of the player before destroying it


if (HasWeaponEquipped())
{
GetEquippedWeapon()->SetActorHiddenInGame(true);
Inventory->GetFullMeshWeapon(CurrentWeaponIndex)->SetActorHiddenInGame(true);
}
}

bool AMurphysLawCharacter::CanPlayerMove()
{
bool CanMove = true;
AMurphysLawBaseWeapon* CurrentWeapon = GetEquippedWeapon();
if (CurrentWeapon)
CanMove = !(IsCrouched && CurrentWeapon->IsReloading);

return CanMove;
}

// Makes the character live again


void AMurphysLawCharacter::Relive()
{
Dead = false;
CurrentHealth = MaxHealth;
Inventory->Reinitialize();
EquipFirstWeapon();

// Enable collisions for the actor as he's alive


SetActorEnableCollision(true);
}

// Executed when the Dead variable is replicated


void AMurphysLawCharacter::OnRep_Dead()
{
// When the character is revived by the server, we reinitialize his inventory
if (!Dead)
{
Inventory->Reinitialize();
EquipFirstWeapon();

// Show HUD
auto PlayerController = Cast<AMurphysLawPlayerController>(GetController());
if(PlayerController != nullptr)
PlayerController->ChangeHUDVisibility(ESlateVisibility::Visible);
}
}

void AMurphysLawCharacter::EquipFirstWeapon()
{
CurrentWeaponIndex = NO_WEAPON_VALUE;
// Determines the current weapon of the character
for (int32 i = 0; i < Inventory->NumberOfWeaponInInventory && CurrentWeaponIndex == NO_WEAPON_VALUE; ++i)
{
AMurphysLawBaseWeapon* Weapon = Inventory->GetWeapon(i);
if (Weapon != nullptr)
{
// The first available weapon in the inventory become the current weapon
CurrentWeaponIndex = i;
Weapon->SetActorHiddenInGame(false);
Inventory->GetFullMeshWeapon(i)->SetActorHiddenInGame(false);
}
}
}

// Reports if the character is dead


bool AMurphysLawCharacter::IsDead() const
{
return Dead;
}

#pragma region Shooting and bullet collision

void AMurphysLawCharacter::Fire()
{
// check if we have a weapon equipped
if (HasWeaponEquipped())
{
// if the weapon has been able to fire
if (GetEquippedWeapon()->Fire(this))
{
// Stop the character from running
SetIsRunning(false);

// try and play a firing animation if specified


if (FireAnimation != nullptr)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if (AnimInstance != nullptr)
{
AnimInstance->Montage_Play(FireAnimation, 1.f);
}
}

// check for bullet collisions


ComputeBulletCollisions();
}

// We reload the weapon if it is empty and we have bullets left in our inventory
if (ShouldReload())
{
Reload();
}
}
}

// Check for bullet collisions


void AMurphysLawCharacter::ComputeBulletCollisions()
{
// Only look for pawns
FCollisionObjectQueryParams CollisionObjectQueryParams;
CollisionObjectQueryParams.AddObjectTypesToQuery(ECollisionChannel::ECC_PhysicsBody);
CollisionObjectQueryParams.AddObjectTypesToQuery(ECollisionChannel::ECC_Destructible);
CollisionObjectQueryParams.AddObjectTypesToQuery(ECollisionChannel::ECC_WorldStatic);

// Remove self from query potential results since we are the first to collide with the ray
FCollisionQueryParams RayQueryParams;
RayQueryParams.AddIgnoredActor(this);

// Ray starting coordinates


const FVector CollisionRayStart = GetFirstPersonCameraComponent()->GetComponentLocation();
const FVector CollisionRayInitialDirection = GetFirstPersonCameraComponent()->GetComponentTransform().GetRotation().GetAxisX();

// Group damage of touched objects together


const float MaxFragmentDeviationRadian = FMath::DegreesToRadians(GetEquippedWeapon()->GetMaxFragmentDeviationAngle(IsCharacterAiming));

bool AtLeastOneHit = false;

// Trace lines to detect pawn


for (int32 i = 0; i < GetEquippedWeapon()->GetNumberOfEmittedFragments(); ++i)
{
// Ray ending coordinates
const FVector CollisionRayAngledDirection = FMath::VRandCone(CollisionRayInitialDirection, MaxFragmentDeviationRadian);
const FVector CollisionRayEnd = CollisionRayStart + (CollisionRayAngledDirection * GetEquippedWeapon()->GetMaxTravelDistanceOfBullet());

FHitResult CollisionResult;
bool HasHit = GetWorld()->LineTraceSingleByObjectType(CollisionResult, CollisionRayStart, CollisionRayEnd, CollisionObjectQueryParams, RayQueryParams);

if (HasHit)
{
// Simple damage amount considering the distance to the target depending on the bone hit
float DeliveredDamage = GetDeliveredDamage(CollisionResult);

FPointDamageEvent CollisionDamageEvent(DeliveredDamage, CollisionResult, CollisionRayAngledDirection, UDamageType::StaticClass());

// If the actor we hit is a hittable actor and an enemy, we have at least one hit so we'll show the Hit Marker
if (IsHittableActor(CollisionResult.GetActor()) && !MurphysLawUtils::IsInSameTeam(CollisionResult.GetActor(), this))
{
AtLeastOneHit = true;
}

if (Role == ROLE_Authority)
{
CollisionResult.GetActor()->TakeDamage(DeliveredDamage, CollisionDamageEvent, GetController(), this);
}
else
{
Server_TransferDamage(CollisionResult.GetActor(), DeliveredDamage, CollisionDamageEvent, GetController(), this);
}
}
}

// If there was at least one hit, we show the HitMarker


if (AtLeastOneHit)
{
auto MyController = Cast<AMurphysLawPlayerController>(GetController());
if (MyController != nullptr && MyController->GetHUDInstance() != nullptr)
{
MyController->GetHUDInstance()->ShowHitMarker();
}
}
}

float AMurphysLawCharacter::GetDeliveredDamage(const FHitResult& CollisionResult) const


{
float DeliveredDamage = GetEquippedWeapon()->ComputeCollisionDamage(CollisionResult.Distance);
FString BoneName = CollisionResult.BoneName.ToString();

if (BoneName == SOCKET_HEAD)
DeliveredDamage *= FACTOR_HEADSHOT;
else if (BoneName == SOCKET_SPINE)
DeliveredDamage *= FACTOR_CHESTSHOT;

return DeliveredDamage;
}

bool AMurphysLawCharacter::Server_TransferDamage_Validate(class AActor * DamagedActor, const float DamageAmount, struct FDamageEvent const & DamageEvent, class AController *
EventInstigator, class AActor * DamageCauser) { return true; }
void AMurphysLawCharacter::Server_TransferDamage_Implementation(class AActor * DamagedActor, const float DamageAmount, struct FDamageEvent const & DamageEvent, class AController *
EventInstigator, class AActor * DamageCauser)
{
DamagedActor->TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
}

float AMurphysLawCharacter::TakeDamage(float DamageAmount, struct FDamageEvent const & DamageEvent, class AController * EventInstigator, AActor * DamageCauser)
{
float ActualDamage = 0.f;

if (CurrentHealth > 0.f)


{
if (Role == ROLE_Authority)
{
ActualDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);

// If the player actually took damage


if (ActualDamage > 0.f)
{
// If the character has a HUD, we show the damages on it
auto MyController = Cast<AMurphysLawPlayerController>(GetController());
if (MyController != nullptr)
{
// We start by getting best info on the hit
FVector ImpulseDirection;
FHitResult Hit;
DamageEvent.GetBestHitInfo(this, DamageCauser, Hit, ImpulseDirection);

// We calculate the vector from the character to the damage causer


FVector2D HitVector = FVector2D(FRotationMatrix(GetControlRotation()).InverseTransformVector(-ImpulseDirection));
HitVector.Normalize();

// We compute the vector representing the ForwardVector


FVector2D StraightVector = FVector2D(1.f, 0.f);
StraightVector.Normalize();

// Finally, we calculate the angle where the hit came from


float Angle = UKismetMathLibrary::DegAcos(FVector2D::DotProduct(StraightVector, HitVector));

// The angle ranges from -180.f to 180.f


Angle = HitVector.Y < 0.f ? -Angle : Angle;

// Dispatch to the controller


MyController->ShowDamage(Angle);
}
}
}
else
{
// Let the server do it
Server_TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
}
}

return ActualDamage;
}

void AMurphysLawCharacter::OnReceiveAnyDamage(float Damage, const UDamageType* DamageType, AController* InstigatedBy, AActor* DamageCauser)
{
// If it was friendly fire, we do not damage our teammate, except if it is from an explosive
if (IsFriendlyFire(InstigatedBy) && !DamageCausedByExplosive(DamageCauser))
{
return;
}

CurrentHealth = FMath::Max(CurrentHealth - Damage, 0.f);

if (CurrentHealth <= 0.f)


{
UpdateStatsOnKill(InstigatedBy, DamageCauser);
Die();
}
}

bool AMurphysLawCharacter::Server_TakeDamage_Validate(float DamageAmount, struct FDamageEvent const & DamageEvent, class AController * EventInstigator, AActor * DamageCauser) { return
true; }
void AMurphysLawCharacter::Server_TakeDamage_Implementation(float DamageAmount, struct FDamageEvent const & DamageEvent, class AController * EventInstigator, AActor * DamageCauser)
{
TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
}

// Update the statistics of players involved in the death


void AMurphysLawCharacter::UpdateStatsOnKill(AController* InstigatedBy, AActor* DamageCauser)
{
AMurphysLawGameState* GameState = GetWorld()->GetGameState<AMurphysLawGameState>();
FString DeathMessage = "";
// Try to cast the DamageCauser to DamageZone to see if the player committed suicide
auto DamageZone = Cast<AMurphysLawDamageZone>(DamageCauser);

// Does the player committed suicide?


if (InstigatedBy == GetController() || DamageCausedByDamageZone(DamageCauser))
{
InstigatedBy = GetController();
DeathMessage = FString::Printf(TEXT("%s committed suicide."), *GetHumanReadableName());
if (GetPlayerState())
GetPlayerState()->IncrementNbDeaths();
if (GameState)
GameState->PlayerCommitedSuicide(GetPlayerState()->GetTeam() == AMurphysLawGameMode::TEAM_A);
}
else if (IsFriendlyFire(InstigatedBy) && DamageCausedByExplosive(DamageCauser))
{
// Or was he killed by a teammate because of explosion?
DeathMessage = FString::Printf(TEXT("%s was killed by a teammate."), *GetHumanReadableName());
if (GetPlayerState())
GetPlayerState()->IncrementNbDeaths();

if (GameState)
GameState->PlayerKilledTeammate(GetPlayerState()->GetTeam() == AMurphysLawGameMode::TEAM_A);
}
else
{
// If the player was killed by somebody else
if (InstigatedBy)
{
DeathMessage = FString::Printf(TEXT("%s was killed by %s"), *GetHumanReadableName(), *InstigatedBy->GetHumanReadableName());

// Increment the number of kills of the other player


auto OtherPlayerState = Cast<AMurphysLawPlayerState>(InstigatedBy->PlayerState);
if (OtherPlayerState)
OtherPlayerState->IncrementNbKills();

// And increment the number of deaths of the current player


if (GetPlayerState())
GetPlayerState()->IncrementNbDeaths();

if (GameState)
{
if (GetPlayerState())
{
GameState->PlayerWasKilled(OtherPlayerState->GetTeam() == AMurphysLawGameMode::TEAM_A);
}
else
ShowError("PlayerState is null");
}
else
ShowError("GameState is null");
}
}

AMurphysLawGameMode* GameMode = Cast<AMurphysLawGameMode>(GetWorld()->GetAuthGameMode());


if (GameMode && DeathMessage != "")
GameMode->SendDeathMessage(Cast<AMurphysLawPlayerController>(InstigatedBy), DeathMessage);
}

// Returns true if the other actor is an explosive barrel, otherwise false


bool AMurphysLawCharacter::DamageCausedByExplosive(class AActor* OtherActor) const
{
return Cast<AMurphysLawExplosiveBarrel>(OtherActor) != nullptr;
}

// Returns true if the other actor is an damage zone, false otherwise


bool AMurphysLawCharacter::DamageCausedByDamageZone(class AActor* OtherActor) const
{
return Cast<AMurphysLawDamageZone>(OtherActor) != nullptr;
}

// Returns true if the controller inflicting the damage is in the same team, false otherwise
bool AMurphysLawCharacter::IsFriendlyFire(class AController* OtherController) const
{
// If the controller of the actor that hit me is a player controller
auto OtherPlayerController = Cast<AMurphysLawPlayerController>(OtherController);
if (OtherPlayerController && OtherPlayerController != GetController())
{
// And if we both have a player state
auto OtherPlayerState = Cast<AMurphysLawPlayerState>(OtherPlayerController->PlayerState);
if (OtherPlayerState && GetPlayerState())
{
// We check if we are on the same team
return OtherPlayerState->GetTeam() == GetPlayerState()->GetTeam();
}
}

return false;
}

#pragma endregion

// Called when the player press the Reload key


void AMurphysLawCharacter::Reload()
{
// Checks if we have a weapon equipped
if (HasWeaponEquipped())
{
// If the weapon has been able to reload
if (GetEquippedWeapon()->Reload())
{
SetIsRunning(false);
}
}
}

// Reports the reference to the current weapon of the character


AMurphysLawBaseWeapon* AMurphysLawCharacter::GetEquippedWeapon() const
{
return Inventory->GetWeapon(CurrentWeaponIndex);
}

// Reports if the character has a weapon equipped


bool AMurphysLawCharacter::HasWeaponEquipped() const
{
return CurrentWeaponIndex != NO_WEAPON_VALUE && GetEquippedWeapon() != nullptr;
}

// Called when the player press a key to change his character's weapon
void AMurphysLawCharacter::EquipWeapon(int32 Index)
{
// Checks if the index passed in parameter is within the boundaries of our Inventory
if (Index >= Inventory->NumberOfWeaponInInventory) return;

// Don't try to re-equip the weapon we already have in hand


if (Index == CurrentWeaponIndex) return;
// Allows the character to change weapon even if the current weapon is reloading
GetEquippedWeapon()->IsReloading = false;

// Plays a sound when switching weapon if available


if (SwitchingWeaponSound != nullptr)
{
UGameplayStatics::PlaySoundAtLocation(this, SwitchingWeaponSound, GetActorLocation());
}

// If the server modifies the value of the property, it is sent to the clients automatically
if (Role == ROLE_Authority)
{
Server_ChangeCurrentWeapon_Implementation(Index);
}
else
{
// If a client wants to modify the value, it has to send a request to the server
Server_ChangeCurrentWeapon(Index);
}
}

// Replicates the current weapon index


bool AMurphysLawCharacter::Server_ChangeCurrentWeapon_Validate(int32 Index) { return true; }
void AMurphysLawCharacter::Server_ChangeCurrentWeapon_Implementation(int32 Index)
{
int32 OldIndex = CurrentWeaponIndex;
CurrentWeaponIndex = Index;
OnRep_CurrentWeaponIndex(OldIndex);
}

// Executed when CurrentWeaponIndex is replicated


void AMurphysLawCharacter::OnRep_CurrentWeaponIndex(int32 OldIndex)
{
auto OldWeapon = Inventory->GetWeapon(OldIndex);
auto OldFullMeshWeapon = Inventory->GetFullMeshWeapon(OldIndex);
auto NewFullMeshWeapon = Inventory->GetFullMeshWeapon(CurrentWeaponIndex);

// Hide the old weapons


if (OldWeapon != nullptr) OldWeapon->SetActorHiddenInGame(true);
if (OldFullMeshWeapon != nullptr) OldFullMeshWeapon->SetActorHiddenInGame(true);

// And show the new ones


if (HasWeaponEquipped()) GetEquippedWeapon()->SetActorHiddenInGame(false);
if (NewFullMeshWeapon != nullptr) NewFullMeshWeapon->SetActorHiddenInGame(false);
}

void AMurphysLawCharacter::ToggleCrouch()
{
// If the character is not crouched
if (CanCrouch())
{
IsCrouched = true;
Crouch();

// Stop the player from running


SetIsRunning(false);
}
else
{
IsCrouched = false;
UnCrouch();
}
}
// Gets the player state casted to MurphysLawPlayerState
AMurphysLawPlayerState* AMurphysLawCharacter::GetPlayerState() const
{
return Cast<AMurphysLawPlayerState>(PlayerState);
}

// Returns true if the weapon hold by the character should be reloaded, false otherwise
bool AMurphysLawCharacter::ShouldReload() const
{
return HasWeaponEquipped()
&& !GetEquippedWeapon()->IsReloading
&& GetEquippedWeapon()->GetNumberOfAmmoLeftInMagazine() <= 0
&& GetEquippedWeapon()->GetNumberOfAmmoLeftInInventory() > 0;
}

void AMurphysLawCharacter::SetMeshTeamColorTint(const MurphysLawTeamColor& TeamColor)


{
TeamBodyMeshColor = TeamColor.GetPrimary();
TeamMaskMeshColor = TeamColor.GetDarker();

if (Role == ROLE_Authority)
{
ValidTeamBodyMeshColor = true;
ValidTeamMaskMeshColor = true;
ApplyMeshTeamColor();
}
}

void AMurphysLawCharacter::OnRep_TeamBodyMeshColor()
{
ValidTeamBodyMeshColor = true;
ApplyMeshTeamColor();
}

void AMurphysLawCharacter::OnRep_TeamMaskMeshColor()
{
ValidTeamMaskMeshColor = true;
ApplyMeshTeamColor();
}

void AMurphysLawCharacter::ApplyMeshTeamColor()
{
// Put color on meshes
if (ValidTeamBodyMeshColor && ValidTeamMaskMeshColor)
{
/* Get references to mesh material to be able to color them. */
UMaterialInstanceDynamic* ShaderMeshBody = GetMesh()->CreateDynamicMaterialInstance(0);
UMaterialInstanceDynamic* ShaderMeshArms = GetMesh1P()->CreateDynamicMaterialInstance(0);
checkf(ShaderMeshBody != nullptr && ShaderMeshArms != nullptr, TEXT("Unable to find first material on character"));

ShaderMeshArms->SetVectorParameterValue(MATERIAL_PARAM_TEAM_COLOR_CLOTHES, TeamBodyMeshColor);
ShaderMeshArms->SetVectorParameterValue(MATERIAL_PARAM_TEAM_COLOR_MASK, TeamMaskMeshColor);
ShaderMeshBody->SetVectorParameterValue(MATERIAL_PARAM_TEAM_COLOR_CLOTHES, TeamBodyMeshColor);
ShaderMeshBody->SetVectorParameterValue(MATERIAL_PARAM_TEAM_COLOR_MASK, TeamMaskMeshColor);
}
}

// Called when the character jumps


void AMurphysLawCharacter::Jump()
{
// Only jump when the character has enough stamina or isn't already in the air
if (GetCurrentStaminaLevel() - JumpStaminaDecayAmount <= 0.f
|| IsInAir()) return;

// Stop the character from running first


SetIsRunning(false);

Super::Jump();

UpdateStaminaLevel(-JumpStaminaDecayAmount);
}

// Changes the IsRunning state


void AMurphysLawCharacter::SetIsRunning(bool NewValue)
{
IsRunning = NewValue;

// If the character starts running, we change its speed


if (NewValue)
{
if (!CanCrouch()) UnCrouch();
GetCharacterMovement()->MaxWalkSpeed = RUN_SPEED;
}
else
{
GetCharacterMovement()->MaxWalkSpeed = WALK_SPEED;
}
}

// Reports the current stamina level


float AMurphysLawCharacter::GetCurrentStaminaLevel() const { return CurrentStamina; }

// Reports the maximum stamina level


float AMurphysLawCharacter::GetMaxStaminaLevel() const { return MaxStamina; }

// Function to update the character's stamina


void AMurphysLawCharacter::UpdateStaminaLevel(float StaminaChange)
{
// Change the stamina level itself
CurrentStamina += StaminaChange;

// Make sure the value stays between 0 and its maximum value
CurrentStamina = UKismetMathLibrary::FClamp(GetCurrentStaminaLevel(), 0.f, GetMaxStaminaLevel());
}

// Determines if an actor is an hittable actor


bool AMurphysLawCharacter::IsHittableActor(AActor* OtherActor)
{
return OtherActor != nullptr
&& (OtherActor->IsA<AMurphysLawCharacter>()
|| OtherActor->IsA<AMurphysLawExplosiveBarrel>());
}

// Called from TeleportTo() when teleport succeeds


void AMurphysLawCharacter::TeleportSucceeded(bool bIsATest)
{
Super::TeleportSucceeded(bIsATest);

// To make sure all the weapons are teleported aswell as the character
Inventory->AttachAllWeaponsToOwner();
}
EXAMPLE #3

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#include "FP_FirstPerson.h"
#include "FP_FirstPersonCharacter.h"
#include "Animation/AnimInstance.h"

static FName WeaponFireTraceIdent = FName(TEXT("WeaponTrace"));


#define COLLISION_WEAPON ECC_GameTraceChannel1

DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);

//////////////////////////////////////////////////////////////////////////
// AFP_FirstPersonCharacter

AFP_FirstPersonCharacter::AFP_FirstPersonCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

// set our turn rates for input


BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;

// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
FirstPersonCameraComponent->RelativeLocation = FVector(0, 0, 64.f); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;

// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true);
Mesh1P->AttachParent = FirstPersonCameraComponent;
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;

// Create a gun mesh component


FP_Gun = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FP_Gun"));
FP_Gun->SetOnlyOwnerSee(true); // only the owning player will see this mesh
FP_Gun->bCastDynamicShadow = false;
FP_Gun->CastShadow = false;
FP_Gun->AttachTo(Mesh1P, TEXT("GripPoint"), EAttachLocation::SnapToTargetIncludingScale, true);

WeaponRange = 5000.0f;
WeaponDamage = 500000.0f;

// Default offset from the character location for projectiles to spawn


GunOffset = FVector(100.0f, 30.0f, 10.0f);

// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}

//////////////////////////////////////////////////////////////////////////
// Input

void AFP_FirstPersonCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)


{
// set up gameplay key bindings
check(InputComponent);

// Bind jump events


InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
InputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);

InputComponent->BindAction("Fire", IE_Pressed, this, &AFP_FirstPersonCharacter::OnFire);


TryEnableTouchscreenMovement(InputComponent);

// Bind movement events


InputComponent->BindAxis("MoveForward", this, &AFP_FirstPersonCharacter::MoveForward);
InputComponent->BindAxis("MoveRight", this, &AFP_FirstPersonCharacter::MoveRight);

// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
InputComponent->BindAxis("TurnRate", this, &AFP_FirstPersonCharacter::TurnAtRate);
InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
InputComponent->BindAxis("LookUpRate", this, &AFP_FirstPersonCharacter::LookUpAtRate);
}

void AFP_FirstPersonCharacter::OnFire()
{
// Play a sound if there is one
if (FireSound != NULL)
{
UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
}

// try and play a firing animation if specified


if(FireAnimation != NULL)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if(AnimInstance != NULL)
{
AnimInstance->Montage_Play(FireAnimation, 1.f);
}
}

// Now send a trace from the end of our gun to see if we should hit anything
APlayerController* PlayerController = Cast<APlayerController>(GetController());

// Calculate the direction of fire and the start location for trace
FVector CamLoc;
FRotator CamRot;
PlayerController->GetPlayerViewPoint(CamLoc, CamRot);
const FVector ShootDir = CamRot.Vector();

FVector StartTrace = FVector::ZeroVector;


if (PlayerController)
{
FRotator UnusedRot;
PlayerController->GetPlayerViewPoint(StartTrace, UnusedRot);

// Adjust trace so there is nothing blocking the ray between the camera and the pawn, and calculate distance from adjusted start
StartTrace = StartTrace + ShootDir * ((GetActorLocation() - StartTrace) | ShootDir);
}

// Calculate endpoint of trace


const FVector EndTrace = StartTrace + ShootDir * WeaponRange;
// Check for impact
const FHitResult Impact = WeaponTrace(StartTrace, EndTrace);

// Deal with impact


AActor* DamagedActor = Impact.GetActor();
UPrimitiveComponent* DamagedComponent = Impact.GetComponent();

// If we hit an actor, with a component that is simulating physics, apply an impulse


if ((DamagedActor != NULL) && (DamagedActor != this) && (DamagedComponent != NULL) && DamagedComponent->IsSimulatingPhysics())
{
DamagedComponent->AddImpulseAtLocation(ShootDir*WeaponDamage, Impact.Location);
}
}

void AFP_FirstPersonCharacter::BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
// If touch is already pressed check the index. If it is not the same as the current touch assume a second touch and thus we want to fire
if (TouchItem.bIsPressed == true)
{
if( TouchItem.FingerIndex != FingerIndex)
{
OnFire();
}
}
else
{
// Cache the finger index and touch location and flag we are processing a touch
TouchItem.bIsPressed = true;
TouchItem.FingerIndex = FingerIndex;
TouchItem.Location = Location;
TouchItem.bMoved = false;
}
}

void AFP_FirstPersonCharacter::EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
// If we didnt record the start event do nothing, or this is a different index
if((TouchItem.bIsPressed == false) || ( TouchItem.FingerIndex != FingerIndex) )
{
return;
}

// If the index matches the start index and we didn't process any movement we assume we want to fire
if ((FingerIndex == TouchItem.FingerIndex) && (TouchItem.bMoved == false))
{
OnFire();
}

// Flag we are no longer processing the touch event


TouchItem.bIsPressed = false;
}

void AFP_FirstPersonCharacter::TouchUpdate(const ETouchIndex::Type FingerIndex, const FVector Location)


{
// If we are processing a touch event and this index matches the initial touch event process movement
if ((TouchItem.bIsPressed == true) && (TouchItem.FingerIndex == FingerIndex))
{
if (GetWorld() != nullptr)
{
UGameViewportClient* ViewportClient = GetWorld()->GetGameViewport();
if (ViewportClient != nullptr)
{
FVector MoveDelta = Location - TouchItem.Location;
FVector2D ScreenSize;
ViewportClient->GetViewportSize(ScreenSize);
FVector2D ScaledDelta = FVector2D(MoveDelta.X, MoveDelta.Y) / ScreenSize;
if (ScaledDelta.X != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.X * BaseTurnRate;
AddControllerYawInput(Value);
}
if (ScaledDelta.Y != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.Y* BaseTurnRate;
AddControllerPitchInput(Value);
}
TouchItem.Location = Location;
}
TouchItem.Location = Location;
}
}
}

void AFP_FirstPersonCharacter::MoveForward(float Value)


{
if (Value != 0.0f)
{
// Add movement in that direction
AddMovementInput(GetActorForwardVector(), Value);
}
}

void AFP_FirstPersonCharacter::MoveRight(float Value)


{
if (Value != 0.0f)
{
// Add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
}
}

void AFP_FirstPersonCharacter::TurnAtRate(float Rate)


{
// Calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void AFP_FirstPersonCharacter::LookUpAtRate(float Rate)


{
// Calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

FHitResult AFP_FirstPersonCharacter::WeaponTrace(const FVector& StartTrace, const FVector& EndTrace) const


{
// Perform trace to retrieve hit info
FCollisionQueryParams TraceParams(WeaponFireTraceIdent, true, Instigator);
TraceParams.bTraceAsyncScene = true;
TraceParams.bReturnPhysicalMaterial = true;

FHitResult Hit(ForceInit);
GetWorld()->LineTraceSingleByChannel(Hit, StartTrace, EndTrace, COLLISION_WEAPON, TraceParams);

return Hit;
}

void AFP_FirstPersonCharacter::TryEnableTouchscreenMovement(UInputComponent* InputComponent)


{
InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AFP_FirstPersonCharacter::BeginTouch);
InputComponent->BindTouch(EInputEvent::IE_Released, this, &AFP_FirstPersonCharacter::EndTouch);
InputComponent->BindTouch(EInputEvent::IE_Repeat, this, &AFP_FirstPersonCharacter::TouchUpdate);
}

EXAMPLE #4

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.

#include "ShaderPluginDemo.h"
#include "ShaderPluginDemoCharacter.h"
#include "Animation/AnimInstance.h"
#include "GameFramework/InputSettings.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Engine/TextureRenderTarget2D.h"
#include "../Public/HighResScreenshot.h"

DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);

//////////////////////////////////////////////////////////////////////////
// AShaderPluginDemoCharacter

AShaderPluginDemoCharacter::AShaderPluginDemoCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(55.f, 96.0f);

// set our turn rates for input


BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;

// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
FirstPersonCameraComponent->RelativeLocation = FVector(-39.56f, 1.75f, 64.f); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;

static ConstructorHelpers::FObjectFinder<UTextureRenderTarget2D> TextureFinderL(


TEXT("TextureRenderTarget2D'/Game/FirstPerson/Textures/TargetL.TargetL'"));
RenderTargetL = TextureFinderL.Object;

static ConstructorHelpers::FObjectFinder<UTextureRenderTarget2D> TextureFinderR(


TEXT("TextureRenderTarget2D'/Game/FirstPerson/Textures/TargetR.TargetR'"));
RenderTargetR = TextureFinderR.Object;

/*RenderTargetL = CreateDefaultSubobject<UTextureRenderTarget2D>(TEXT("RenderTarget_L"));
RenderTargetL->InitAutoFormat(1024, 1024);
RenderTargetL->AddressX = TA_Wrap;
RenderTargetL->AddressY = TA_Wrap;*/

/*RenderTargetR = CreateDefaultSubobject<UTextureRenderTarget2D>(TEXT("RenderTarget_R"));
RenderTargetR->InitAutoFormat(1024, 1024);
RenderTargetR->AddressX = TA_Wrap;
RenderTargetR->AddressY = TA_Wrap;*/
StereoCameraComponentL = CreateDefaultSubobject<USceneCaptureComponent2D>(TEXT("StereoCameraComponent_L"));
//StereoCameraComponentL->FOVAngle = 90.f;
StereoCameraComponentL->AttachParent = GetCapsuleComponent();
StereoCameraComponentL->RelativeLocation = FVector(0.f, 0.f, 70.f); // Position the camera
StereoCameraComponentL->TextureTarget = RenderTargetL;
StereoCameraComponentL->bCaptureEveryFrame = false;

StereoCameraComponentR = CreateDefaultSubobject<USceneCaptureComponent2D>(TEXT("StereoCameraComponent_R"));
StereoCameraComponentR->AttachParent = GetCapsuleComponent();
StereoCameraComponentR->RelativeLocation = FVector(0.f, 30.f, 70.f); // Position the camera
StereoCameraComponentR->TextureTarget = RenderTargetR;
StereoCameraComponentR->bCaptureEveryFrame = false;

// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true);
Mesh1P->AttachParent = FirstPersonCameraComponent;
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;
Mesh1P->RelativeRotation = FRotator(1.9f, -19.19f, 5.2f);
Mesh1P->RelativeLocation = FVector(-0.5f, -4.4f, -155.7f);

// Create a gun mesh component


FP_Gun = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FP_Gun"));
FP_Gun->SetOnlyOwnerSee(true); // only the owning player will see this mesh
FP_Gun->bCastDynamicShadow = false;
FP_Gun->CastShadow = false;
//FP_Gun->AttachTo(Mesh1P, TEXT("GripPoint"), EAttachLocation::SnapToTargetIncludingScale, true);

FP_MuzzleLocation = CreateDefaultSubobject<USceneComponent>(TEXT("MuzzleLocation"));
FP_MuzzleLocation->AttachTo(FP_Gun);
FP_MuzzleLocation->SetRelativeLocation(FVector(0.2f, 48.4f, -10.6f));

// Default offset from the character location for projectiles to spawn


GunOffset = FVector(100.0f, 30.0f, 10.0f);

// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)

//ShaderPluginDemo variables
EndColorBuildup = 0;
EndColorBuildupDirection = 1;
PixelShaderTopLeftColor = FColor::Green;
ComputeShaderSimulationSpeed = 1.0;
ComputeShaderBlend = 0.5f;
ComputeShaderBlendScalar = 0;
TotalElapsedTime = 0;
}

//Since we need the featurelevel, we need to create the shaders from beginplay, and not in the ctor.
void AShaderPluginDemoCharacter::BeginPlay()
{
// Call the base class
Super::BeginPlay();
FP_Gun->AttachTo(Mesh1P, TEXT("GripPoint"), EAttachLocation::SnapToTargetIncludingScale, true); //Attach gun mesh component to Skeleton, doing it here because the skelton is
not yet created in the constructor

PixelShading = new FPixelShaderUsageExample(PixelShaderTopLeftColor, GetWorld()->Scene->GetFeatureLevel());


ComputeShading = new FComputeShaderUsageExample(ComputeShaderSimulationSpeed, 1024, 1024, GetWorld()->Scene->GetFeatureLevel());
}
//Do not forget cleanup :)
void AShaderPluginDemoCharacter::BeginDestroy()
{
Super::BeginDestroy();

if (PixelShading)
{
delete PixelShading;
}
if (ComputeShading)
{
delete ComputeShading;
}
}

//Saving functions
void AShaderPluginDemoCharacter::SavePixelShaderOutput()
{
PixelShading->Save();
}
void AShaderPluginDemoCharacter::SaveComputeShaderOutput()
{
ComputeShading->Save();
}

void AShaderPluginDemoCharacter::ModifyComputeShaderBlend(float NewScalar)


{
ComputeShaderBlendScalar = NewScalar;
}

void AShaderPluginDemoCharacter::Tick(float DeltaSeconds)


{
Super::Tick(DeltaSeconds);

TotalElapsedTime += DeltaSeconds;

if (PixelShading)
{
EndColorBuildup = FMath::Clamp(EndColorBuildup + DeltaSeconds * EndColorBuildupDirection, 0.0f, 1.0f);
if (EndColorBuildup >= 1.0 || EndColorBuildup <= 0)
{
EndColorBuildupDirection *= -1;
}

FTexture2DRHIRef InputTexture = NULL;

if (ComputeShading)
{
ComputeShading->ExecuteComputeShader(TotalElapsedTime);
InputTexture = ComputeShading->GetTexture(); //This is the output texture from the compute shader that we will pass to the pixel shader.
}

ComputeShaderBlend = FMath::Clamp(ComputeShaderBlend + ComputeShaderBlendScalar * DeltaSeconds, 0.0f, 1.0f);


PixelShading->ExecutePixelShader(RenderTarget, InputTexture, FColor(EndColorBuildup * 255, 0, 0, 255), ComputeShaderBlend);
}

//save screenshot to disk


SaveRenderTargetToDisk(RenderTargetL, "screenshotL1.png",false);
SaveRenderTargetToDisk(RenderTargetR, "screenshotR1.png",false);
}

void AShaderPluginDemoCharacter::SaveRenderTargetToDisk(UTextureRenderTarget2D* InRenderTarget, FString Filename, bool debug)


{
FTextureRenderTargetResource* RTResource = InRenderTarget->GameThread_GetRenderTargetResource();

FReadSurfaceDataFlags ReadPixelFlags(RCM_UNorm);
ReadPixelFlags.SetLinearToGamma(true);
TArray<FColor> OutBMP;
RTResource->ReadPixels(OutBMP, ReadPixelFlags);

FIntRect SourceRect;

FIntPoint DestSize(InRenderTarget->GetSurfaceWidth(), InRenderTarget->GetSurfaceHeight());

FString ResultPath;
FHighResScreenshotConfig& HighResScreenshotConfig = GetHighResScreenshotConfig();
if (HighResScreenshotConfig.SaveImage(FPaths::GameSavedDir() + TEXT("Screenshots/") + Filename, OutBMP, DestSize, &ResultPath) && debug) {
UE_LOG(LogFPChar, Warning, TEXT("Screenshot saved to: %s"), *ResultPath);
}

void AShaderPluginDemoCharacter::OnFire()
{
//Try to set a texture to the object we hit!
FHitResult HitResult;
FVector StartLocation = FirstPersonCameraComponent->GetComponentLocation();
FRotator Direction = FirstPersonCameraComponent->GetComponentRotation();
FVector EndLocation = StartLocation + Direction.Vector() * 10000;
FCollisionQueryParams QueryParams;
QueryParams.AddIgnoredActor(this);

if (GetWorld()->LineTraceSingleByChannel(HitResult, StartLocation, EndLocation, ECC_Visibility, QueryParams))


{
TArray<UStaticMeshComponent*> StaticMeshComponents = TArray<UStaticMeshComponent*>();
AActor* HitActor = HitResult.GetActor();

if (NULL != HitActor)
{
HitActor->GetComponents<UStaticMeshComponent>(StaticMeshComponents);
for (int32 i = 0; i < StaticMeshComponents.Num(); i++)
{
UStaticMeshComponent* CurrentStaticMeshPtr = StaticMeshComponents[i];
CurrentStaticMeshPtr->SetMaterial(0, MaterialToApplyToClickedObject);
UMaterialInstanceDynamic* MID = CurrentStaticMeshPtr->CreateAndSetMaterialInstanceDynamic(0);
UTexture* CastedRenderTarget = Cast<UTexture>(RenderTarget);
MID->SetTextureParameterValue("InputTexture", CastedRenderTarget);
}
}
}

// try and play the sound if specified


if (FireSound != NULL)
{
UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
}

// try and play a firing animation if specified


if (FireAnimation != NULL)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if (AnimInstance != NULL)
{
AnimInstance->Montage_Play(FireAnimation, 1.f);
}
}
}

void AShaderPluginDemoCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)


{
// set up gameplay key bindings
check(InputComponent);

//ShaderPluginDemo Specific input mappings


InputComponent->BindAction("SavePixelShaderOutput", IE_Pressed, this, &AShaderPluginDemoCharacter::SavePixelShaderOutput);
InputComponent->BindAction("SaveComputeShaderOutput", IE_Pressed, this, &AShaderPluginDemoCharacter::SaveComputeShaderOutput);
InputComponent->BindAxis("ComputeShaderBlend", this, &AShaderPluginDemoCharacter::ModifyComputeShaderBlend);
//ShaderPluginDemo Specific input mappings

InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);


InputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);

//InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AShaderPluginDemoCharacter::TouchStarted);


if (EnableTouchscreenMovement(InputComponent) == false)
{
InputComponent->BindAction("Fire", IE_Pressed, this, &AShaderPluginDemoCharacter::OnFire);
}

InputComponent->BindAxis("MoveForward", this, &AShaderPluginDemoCharacter::MoveForward);


InputComponent->BindAxis("MoveRight", this, &AShaderPluginDemoCharacter::MoveRight);

// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
InputComponent->BindAxis("TurnRate", this, &AShaderPluginDemoCharacter::TurnAtRate);
InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
InputComponent->BindAxis("LookUpRate", this, &AShaderPluginDemoCharacter::LookUpAtRate);
}

void AShaderPluginDemoCharacter::BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if (TouchItem.bIsPressed == true)
{
return;
}
TouchItem.bIsPressed = true;
TouchItem.FingerIndex = FingerIndex;
TouchItem.Location = Location;
TouchItem.bMoved = false;
}

void AShaderPluginDemoCharacter::EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if (TouchItem.bIsPressed == false)
{
return;
}
if ((FingerIndex == TouchItem.FingerIndex) && (TouchItem.bMoved == false))
{
OnFire();
}
TouchItem.bIsPressed = false;
}
void AShaderPluginDemoCharacter::TouchUpdate(const ETouchIndex::Type FingerIndex, const FVector Location)
{
if ((TouchItem.bIsPressed == true) && (TouchItem.FingerIndex == FingerIndex))
{
if (TouchItem.bIsPressed)
{
if (GetWorld() != nullptr)
{
UGameViewportClient* ViewportClient = GetWorld()->GetGameViewport();
if (ViewportClient != nullptr)
{
FVector MoveDelta = Location - TouchItem.Location;
FVector2D ScreenSize;
ViewportClient->GetViewportSize(ScreenSize);
FVector2D ScaledDelta = FVector2D(MoveDelta.X, MoveDelta.Y) / ScreenSize;
if (FMath::Abs(ScaledDelta.X) >= 4.0 / ScreenSize.X)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.X * BaseTurnRate;
AddControllerYawInput(Value);
}
if (FMath::Abs(ScaledDelta.Y) >= 4.0 / ScreenSize.Y)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.Y * BaseTurnRate;
AddControllerPitchInput(Value);
}
TouchItem.Location = Location;
}
TouchItem.Location = Location;
}
}
}
}

void AShaderPluginDemoCharacter::MoveForward(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorForwardVector(), Value);
}
}

void AShaderPluginDemoCharacter::MoveRight(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
}
}

void AShaderPluginDemoCharacter::TurnAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void AShaderPluginDemoCharacter::LookUpAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}
bool AShaderPluginDemoCharacter::EnableTouchscreenMovement(class UInputComponent* InputComponent)
{
bool bResult = false;
if (FPlatformMisc::GetUseVirtualJoysticks() || GetDefault<UInputSettings>()->bUseMouseForTouch)
{
bResult = true;
InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AShaderPluginDemoCharacter::BeginTouch);
InputComponent->BindTouch(EInputEvent::IE_Released, this, &AShaderPluginDemoCharacter::EndTouch);
InputComponent->BindTouch(EInputEvent::IE_Repeat, this, &AShaderPluginDemoCharacter::TouchUpdate);
}
return bResult;
}

EXAMPLE #5

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.

#include "Totem.h"
#include "TotemCharacter.h"
#include "TotemProjectile.h"
#include "Animation/AnimInstance.h"
#include "Engine.h"
#include "Cyclone.h"
#include "QuakeAttack.h"
#include "UnrealNetwork.h"
#include "TotemPlayerState.h"
#include "TotemPlayerController.h"
#include "TotemGameMode.h"
#include "TerrainManager.h"
#include "TotemGameState.h"

//////////////////////////////////////////////////////////////////////////
// ATotemCharacter

ATotemCharacter::ATotemCharacter(const FObjectInitializer& ObjectInitializer)


: Super(ObjectInitializer), BaseTurnRate(45.0f), BaseLookUpRate(45.0f), PlayerHealth(100.0f), ProjectileDamage(10.0f), TimeBetweenShots(0.5f), ShotPressed(false),
ShotFired(false), TimeSinceAttack(0.0f), CanRapidFire(false), MoveSpeedScale(1.0f), StrafeSpeedScale(1.0f), PreviousHit(), bInvincible(false),
CurrentAbility(nullptr), bCanMove(true), AttachCyclone(nullptr), NumAbilities(0), bFirstSpawn(true), bBeingKnockedBack(false), TimeRestoreFromKnockedBack(3.0f),
TimeSinceKnockedBack(-1.0f), DamageScale(1.0f), bAcceptMoveInput(true), DistanceFromPlayer(0.0f), FirstTick(true), ThirdPersonAbilityCamDistance(550.0f),
ThirdPersonDeathCamDistance(700.0f), bClickedThisFrame(false),
bDeathCam(false), bDeathEffectFired(false), DeathCamAngle(20), InstigKillTimer(5.0f), TimeSinceHit(0.0f)
{
Instigator = this; //seem instigator automagically be himself, but for safe, I assign again

// Set size for collision capsule


GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

//Create CameraBoom for Third person Camera


CameraBoom = ObjectInitializer.CreateDefaultSubobject<USpringArmComponent>(this, TEXT("CameraBoom"));
CameraBoom->AttachParent = GetCapsuleComponent();
CameraBoom->RelativeLocation = FVector(0, 0, 64.f); // Position the camera
CameraBoom->TargetArmLength = 0;
CameraBoom->bUsePawnControlRotation = true;
// Create a CameraComponent
FirstPersonCameraComponent = ObjectInitializer.CreateDefaultSubobject<UCameraComponent>(this, TEXT("FirstPersonCamera"));
//FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
FirstPersonCameraComponent->AttachParent = CameraBoom;
FirstPersonCameraComponent->RelativeLocation = FVector(20, 0, 0); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;

// Create a CameraComponent
PlayerBody = ObjectInitializer.CreateDefaultSubobject<UStaticMeshComponent>(this, TEXT("PlayerBody"));
PlayerBody->AttachParent = GetCapsuleComponent();
PlayerBody->RelativeLocation = FVector(0, 0, 0); // Position the body

// Default offset from the character location for projectiles to spawn


GunOffset = FVector(100.0f, 30.0f, 10.0f);

// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = ObjectInitializer.CreateDefaultSubobject<USkeletalMeshComponent>(this, TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true); // only the owning player will see this mesh
Mesh1P->AttachParent = FirstPersonCameraComponent;
Mesh1P->RelativeLocation = FVector(0.f, 0.f, -150.f);
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;

//bReplicates = true;

// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)

//Allow the actor to ( anywhere


PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.bStartWithTickEnabled = true;
PrimaryActorTick.bAllowTickOnDedicatedServer = true;
SetActorTickEnabled(true);

Metrics::CreateInstance();

//Death state
bIsDead = false;
TotalDeadBodyDuration = 5.0f;
CurrentDeadBodyDuration = -1.0f;

Team = ETeam::NO_TEAM;
bCanBasicAttack = false;
bCanUseAbility = false;
bAcceptMoveInput = false;
bReborn = true;
TotalRebornDuration = 0.1f;

bAcceptCameraChange = true;

bThirdPersonCamLerp = false;
bThirdPersonDeathCamLerp = false;

bRightButtonDownThisFrame = false;

TotemCharacterMoveState = ETotemCharacterMoveState::NoMove;

NormalWalkSpeed = 600.0f; //Just a random number


SprintSpeed = 1200.0f;
DeathDissolvePlayAfterTimer = 0.733f;
}

void ATotemCharacter::BeginPlay()
{
Super::BeginPlay();
NormalWalkSpeed = GetCharacterMovement()->MaxWalkSpeed;
}

void ATotemCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const


{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);//very important! Make sure not ignore parent stuff about replication
DOREPLIFETIME(ATotemCharacter, PlayerHealth);
DOREPLIFETIME(ATotemCharacter, bCanMove);
DOREPLIFETIME(ATotemCharacter, bBeingKnockedBack);
DOREPLIFETIME(ATotemCharacter, bIsDead);
DOREPLIFETIME(ATotemCharacter, TotalDeadBodyDuration);
DOREPLIFETIME(ATotemCharacter, Team);
DOREPLIFETIME(ATotemCharacter, bCanBasicAttack);
DOREPLIFETIME(ATotemCharacter, bCanUseAbility);
DOREPLIFETIME(ATotemCharacter, bReborn);
DOREPLIFETIME(ATotemCharacter, bThirdPersonCamLerp);
DOREPLIFETIME(ATotemCharacter, bThirdPersonDeathCamLerp);
DOREPLIFETIME(ATotemCharacter, bAcceptCameraChange);
DOREPLIFETIME(ATotemCharacter, TotemCharacterMoveState);
DOREPLIFETIME(ATotemCharacter, bDeathEffectFired);
DOREPLIFETIME(ATotemCharacter, DamageScale);
}

void ATotemCharacter::ServerSpawnProjectile_Implementation(const FVector& SpawnLocation, const FRotator& SpawnRotation, AActor* TheOwner, APawn* TheInstigator)
{
//if (GEngine)
//{
// GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("Should appear only on server!"));
//}
UWorld* const World = GetWorld();
if (World != NULL)
{
//to add owner and instigator information
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = TheOwner;
SpawnParams.Instigator = TheInstigator;
if (ProjectileClass)
{
FVector Offset = RootComponent->GetComponentRotation().Vector();
Offset.Z = 0.0f;
Offset.Normalize();
Offset *= DistanceFromPlayer;
World->SpawnActor<ATotemProjectile>(ProjectileClass, SpawnLocation + Offset, SpawnRotation, SpawnParams);
ATotemPlayerState *state = nullptr;
if (Cast<ATotemPlayerController>(Controller))
{
state = Cast<ATotemPlayerState>(Cast<ATotemPlayerController>(Controller)->PlayerState);
}

if (state)
{
#if WRITE_METRICS
std::string team;
switch (state->Team)
{
case ETeam::RED_TEAM:
team = "Red";
break;
case ETeam::BLUE_TEAM:
team = "Blue";
break;
default:
team = "None";
break;
}
std::string attack;
if (Cast<AEarthShaman>(this))
{
attack = "Rock fist";
}
else if (Cast<AWindShaman>(this))
{
attack = "Sonic bolt";
}
else
{
attack = "Nothing?";
}
Metrics::WriteToFile(std::string(TCHAR_TO_UTF8(*(state->MyName))), team, attack);

#endif
}
}
else if (FireProjectileClass)
{
AFireProjectile* shotProjectile = World->SpawnActor<AFireProjectile>(FireProjectileClass, SpawnLocation, SpawnRotation, SpawnParams);
}
}
}

bool ATotemCharacter::ServerSpawnProjectile_Validate(const FVector& SpawnLocation, const FRotator& SpawnRotation, AActor* TheOwner, APawn* TheInstigator)
{
return true;
}

//////////////////////////////////////////////////////////////////////////
// Input

void ATotemCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)


{
// set up gameplay key bindings
check(InputComponent);

//InputComponent->BindAction("Jump", IE_Pressed, this, &ATotemCharacter::Jump);


//InputComponent->BindAction("Jump", IE_Released, this, &ATotemCharacter::StopJumping);

//AEarthShaman* shaman = Cast<AEarthShaman>(this);


//if (shaman)
//{
// InputComponent->BindAction("Fire", IE_Pressed, shaman, &AEarthShaman::OnShotDown);
// InputComponent->BindAction("Fire", IE_Released, shaman, &AEarthShaman::OnShotUp);
//}
//else
//{
// InputComponent->BindAction("Fire", IE_Pressed, this, &ATotemCharacter::OnShotDown);
// InputComponent->BindAction("Fire", IE_Released, this, &ATotemCharacter::OnShotUp);
//}
//InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &ATotemCharacter::TouchStarted);

//InputComponent->BindAction("Ability", IE_Pressed, this, &ATotemCharacter::RightDown);


//InputComponent->BindAction("Ability", IE_Released, this, &ATotemCharacter::RightUp);
//InputComponent->BindAction("ScrollUp", IE_Pressed, this, &ATotemCharacter::ScrollUp);
//InputComponent->BindAction("ScrollDown", IE_Pressed, this, &ATotemCharacter::ScrollDown);
InputComponent->BindAction("ToggleDeathCam", IE_Pressed, this, &ATotemCharacter::ToggleDebugDeathCam);

//InputComponent->BindAxis("MoveForward", this, &ATotemCharacter::MoveForward);


//InputComponent->BindAxis("MoveRight", this, &ATotemCharacter::MoveRight);

//// We have 2 versions of the rotation bindings to handle different kinds of devices differently
//// "turn" handles devices that provide an absolute delta, such as a mouse.
//// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
//InputComponent->BindAxis("Turn", this, &ATotemCharacter::AddControllerYawInput);
//InputComponent->BindAxis("TurnRate", this, &ATotemCharacter::TurnAtRate);
//InputComponent->BindAxis("LookUp", this, &ATotemCharacter::AddControllerPitchInput);
//InputComponent->BindAxis("LookUpRate", this, &ATotemCharacter::LookUpAtRate);

void ATotemCharacter::ToggleDebugDeathCam()
{
bDeathCam = !bDeathCam;

if (bDeathCam)
{
bThirdPersonCamLerp = true;
ThirdPersonCamDistance = ThirdPersonDeathCamDistance;
}
else
{
bThirdPersonCamLerp = false;
ThirdPersonCamDistance = 0.0f;
}
}

void ATotemCharacter::ReduceHealth(float damage, ATotemPlayerController* player, AbilityType type, FName abilityName)


{
if (PlayerHealth > 0 && HasAuthority() && !bInvincible)
{
PlayerHealth -= damage * DamageScale;

ATotemCharacter* character = nullptr;

if (type != AbilityType::NONE && type != AbilityType::End && player)


{
character = Cast<ATotemCharacter>(player->AcknowledgedPawn);
PreviousHit.controller = player;
PreviousHit.name = abilityName;
PreviousHit.type = type;
TimeSinceHit = InstigKillTimer;
}
else if (PlayerHealth <= 0)
{
#if WRITE_METRICS
ATotemPlayerController* control = Cast<ATotemPlayerController>(Controller);
if (control)
{
ATotemPlayerState* state = Cast<ATotemPlayerState>(control->PlayerState);
std::string team;
switch (state->Team)
{
case ETeam::RED_TEAM:
team = "Red";
break;
case ETeam::BLUE_TEAM:
team = "Blue";
break;
default:
team = "None";
break;
}
Metrics::WriteToFile(std::string(TCHAR_TO_UTF8(*(state->MyName))), team, std::string("Died in lava"), std::string(), std::string(),
std::string("Probably"));
state->NumOfDie += 1;
}
#endif
}

if (PlayerHealth <= 0)
{
PlayerHealth = 0;
if (character)
{
#if WRITE_METRICS
if (type != AbilityType::NONE && type != AbilityType::End)
{
ATotemPlayerController* control = Cast<ATotemPlayerController>(Controller);
if (control)
{
ATotemPlayerState* state = Cast<ATotemPlayerState>(control->PlayerState);
ATotemPlayerState* attackerState = Cast<ATotemPlayerState>(player->PlayerState);
if (state && attackerState)
{
std::string team;
std::string attackerTeam;
switch (state->Team)
{
case ETeam::RED_TEAM:
team = "Red";
break;
case ETeam::BLUE_TEAM:
team = "Blue";
break;
default:
team = "None";
break;
}
switch (attackerState->Team)
{
case ETeam::RED_TEAM:
attackerTeam = "Red";
break;
case ETeam::BLUE_TEAM:
attackerTeam = "Blue";
break;
default:
attackerTeam = "None";
break;
}
Metrics::WriteToFile(std::string(TCHAR_TO_UTF8(*(attackerState->MyName))), attackerTeam,
std::string("Killed"), std::string(TCHAR_TO_UTF8(*(state->MyName))), team, std::string("Using: " + std::string(TCHAR_TO_UTF8(*(abilityName.ToString())))));
// Player statistics part
state->NumOfDie += 1;
// Prevent count friendly kill
if (team != attackerTeam)
{
attackerState->NumOfKill += 1;
}
}
}
}
#endif
ATotemPlayerController* controller = Cast<ATotemPlayerController>(GetController());
if (controller && controller != player)
{
character->DealtDamageTo(nullptr, controller, player, true);
}
}
else if (PreviousHit.controller)
{
////Tell controller to count the kill
//ATotemCharacter* character = Cast<ATotemCharacter>(PreviousHit.controller->GetPawn());
//if (character)
//{
// ATotemPlayerController* controller = Cast<ATotemPlayerController>(GetController());
// if (controller)
// {
// character->DealtDamageTo(nullptr, controller, PreviousHit.controller, true);
// }
//}
//else
//{
// //TODO: update score on the controller if previous hit player died in the meantime
//}
DeathToLava(PreviousHit.controller, PreviousHit.type);
// Add kill and dead statistics, hopefully it will work
ATotemPlayerState* AttackerState = Cast<ATotemPlayerState>(PreviousHit.controller->PlayerState);

if (Controller)
{
ATotemPlayerController* VictimController = Cast<ATotemPlayerController>(Controller);
if (VictimController)
{
ATotemPlayerState* VictimState = Cast<ATotemPlayerState>(Controller->PlayerState);
if (VictimState)
{
VictimState->NumOfDie += 1;
if (AttackerState)
{
if (AttackerState->Team != VictimState->Team)
{
AttackerState += 1;
}
}
}
}
}

#if WRITE_METRICS
ATotemPlayerController* control = Cast<ATotemPlayerController>(Controller);
if (control)
{
ATotemPlayerState* state = Cast<ATotemPlayerState>(control->PlayerState);
ATotemPlayerState* attackerState = Cast<ATotemPlayerState>(PreviousHit.controller->PlayerState);
if (state && attackerState)
{
std::string team;
std::string attackerTeam;
switch (state->Team)
{
case ETeam::RED_TEAM:
team = "Red";
break;
case ETeam::BLUE_TEAM:
team = "Blue";
break;
default:
team = "None";
break;
}
switch (attackerState->Team)
{
case ETeam::RED_TEAM:
attackerTeam = "Red";
break;
case ETeam::BLUE_TEAM:
attackerTeam = "Blue";
break;
default:
attackerTeam = "None";
break;
}
Metrics::WriteToFile(std::string(TCHAR_TO_UTF8(*(attackerState->MyName))), attackerTeam, std::string("Killed"),
std::string(TCHAR_TO_UTF8(*(state->MyName))), team, std::string("Using: " + std::string(TCHAR_TO_UTF8(*(abilityName.ToString())))));
}
}
#endif
}
KillPlayer();
}
else if (character)
{
ATotemPlayerController* controller = Cast<ATotemPlayerController>(GetController());
if (controller)
{
character->DealtDamageTo(nullptr, controller, player, false);
}
}
}

if (!bInvincible )
{
OnDamageTaken(player);

DamageTakenBy(type, abilityName);
}
}

void ATotemCharacter::Tick(float DeltaTime)


{
Super::Tick(DeltaTime);

if (TimeSinceHit > 0.0f)


{
TimeSinceHit -= DeltaTime;
}
else
{
PreviousHit.controller = nullptr;
PreviousHit.type = AbilityType::NONE;
PreviousHit.name = "";
}

if (bThirdPersonDeathCamLerp)
{
if (Controller)
{
Controller->SetControlRotation(FRotator(-1.0f * DeathCamAngle, 0, 0));
}
}
if (bThirdPersonCamLerp || bThirdPersonDeathCamLerp )
{
CameraBoom->TargetArmLength = FMath::Lerp<float>(CameraBoom->TargetArmLength, ThirdPersonCamDistance, DeltaTime*4.0f);
}
else
{
CameraBoom->TargetArmLength = FMath::Lerp<float>(CameraBoom->TargetArmLength, 0, DeltaTime*4.0f);
}

if (bIsDead && (ThirdPersonCamDistance!= ThirdPersonDeathCamDistance))


{
ThirdPersonCamDistance = ThirdPersonDeathCamDistance;
}

if ( ((FMath::Abs(CameraBoom->TargetArmLength - ThirdPersonCamDistance) < 0.5f) && bIsDead && bThirdPersonDeathCamLerp))


{
ATotemPlayerController *PC = Cast<ATotemPlayerController>(Controller);
if (PC)
{
if (HasAuthority())
{
PC->HandleDead(FVector(0, 0, 0), FRotator(0, 0, 0));
}
bThirdPersonDeathCamLerp = false;
}
}

if (bDeathEffectFired && RayCastOnGround() )


{
FireMaterialEffect();
bDeathEffectFired = false;
}

if ((CameraBoom->TargetArmLength > 0.5f) && (!bIsShamanVisible))


{
if (GEngine->GetGamePlayer(GetWorld(), 0)->PlayerController == Controller)
{
MakeSelfVisible();
bIsShamanVisible = true;
}
}

if ((CameraBoom->TargetArmLength <= 0.5f) && (bIsShamanVisible))


{
if (GEngine->GetGamePlayer(GetWorld(), 0)->PlayerController == Controller)
{
MakeSelfInvisible();
bIsShamanVisible = false;
}
}

if (Controller && FirstTick)


{
if (GEngine->GetGamePlayer(GetWorld(), 0)->PlayerController == Controller)
{
MakeSelfInvisible();
bIsShamanVisible = false;
}
FirstTick = false;
}

ATotemPlayerState *TotemPlayerState = Cast<ATotemPlayerState>(PlayerState);


if (TotemPlayerState && bFirstSpawn)
{
SetActorLocation(TotemPlayerState->StartingLocation);
SetActorRotation(TotemPlayerState->StartingRotation);
bFirstSpawn = false;
}

if (bReborn && HasAuthority())


{
TotalRebornDuration -= DeltaTime;
if (TotalRebornDuration < 0.0f)
{
bReborn = false;
ExitReborn();
}
}

if (HasAuthority())
{
if (bBeingKnockedBack)
{
TimeSinceKnockedBack -= DeltaTime;
if (TimeSinceKnockedBack < 0.0f)
{
bBeingKnockedBack = false;
}
}
#if (WRITE_METRICS > 1)
if (DeltaTime >= .025)
{
ATotemPlayerController* control = Cast<ATotemPlayerController>(Controller);
if(control)
{
ATotemPlayerState* state = Cast<ATotemPlayerState>(control->PlayerState);
if(state)
{
std::string team;
switch (state->Team)
{
case ETeam::RED_TEAM:
team = "Red";
break;
case ETeam::BLUE_TEAM:
team = "Blue";
break;
default:
team = "None";
break;
}
Metrics::WriteToFile(std::string(TCHAR_TO_UTF8(*(state->MyName))), team, std::string("Frame rate hit"),
std::string(std::to_string(DeltaTime) + "ms"), std::string(std::to_string(1.0f/DeltaTime) + "fps"));
}
}
}
#endif
}

//Keep track of how long has passed since the last attack
TimeSinceAttack += DeltaTime;
ShotPressedDuration += DeltaTime;

//If shot is pressed and enough time has been pressed


if (ShotPressed && TimeSinceAttack >= TimeBetweenShots)
{
//And you either can rapid fire or no shot has been fired since the shot button was pressed
if (CanRapidFire || !ShotFired)
{
OnFire();
ShotFired = true;
TimeSinceAttack = 0.0f;
}
}
else if (!ShotPressed)
{
ShotPressedDuration = 0;
}

//If get hit by cyclone, then the shaman cannnot move


if (HasAuthority())
{
TArray<AActor* > OverlappedActors;
TArray<ACyclone* > OverlappedCyclones;
GetOverlappingActors(OverlappedActors);
for (int32 i = 0; i < OverlappedActors.Num(); i++)
{
ACyclone* Cyclone = Cast<ACyclone>(OverlappedActors[i]);
if (Cyclone && !Cyclone->IsPendingKill())
{
//overlap myself doesn't count
if (Cyclone->GetOwner() != GetController())
{
OverlappedCyclones.Add(Cyclone);
}
}
}

if (OverlappedCyclones.Num() == 0)
{
//Do this to prevent bCanMove be replictated every frame
if (!bCanMove)
{
bCanMove = true;
}
AttachCyclone = nullptr;
}
else if (OverlappedCyclones.Num() > 0)
{
if (bCanMove)
{
bCanMove = false;
}
if (AttachCyclone)
{
FVector CycloneVelocity = AttachCyclone->ProjectileMovement->Velocity;
ATotemPlayerController* own = Cast<ATotemPlayerController>(AttachCyclone->GetOwner());
if (own)
{
ReduceHealth(AttachCyclone->Damage * DeltaTime, own, AbilityType::WIND, FName(TEXT("Cyclone"))); //every second reduce Damage
amount of health
}
LaunchCharacter(CycloneVelocity, true, true);
}
//CharacterMovement->Velocity = CycloneVelocity;
}
}

//temporary solution, maybe later we need more fancy stuff.


if (bIsDead && HasAuthority())
{
CurrentDeadBodyDuration -= DeltaTime;
if (CurrentDeadBodyDuration <= 0)
{
Destroy();
}
}

if (bClickedThisFrame)
{
bClickedThisFrame = false;
if (Controller)
{
APlayerController* control = Cast<APlayerController>(Controller);
if (control)
{
if (!control->IsInputKeyDown(EKeys::LeftMouseButton))
{
LeftUp();
}
}
}
}

if (bRightButtonDownThisFrame)
{
bRightButtonDownThisFrame = false;
if (Controller)
{
APlayerController* control = Cast<APlayerController>(Controller);
if (control)
{
if (!control->IsInputKeyDown(EKeys::RightMouseButton))
{
RightUp();
}
}
}
}

//CheckShiftKey();

//This function will be only execute on server side


void ATotemCharacter::KillPlayer()
{
UWorld* const World = GetWorld();
if (World)
{
ATotemGameState* TotemGameState = Cast<ATotemGameState>(World->GetGameState());
if (PlayerHealth <= 0 && HasAuthority() && TotemGameState->CurrentState == ETotemGameState::Playing)
{
//ATotemPlayerController *PC = Cast<ATotemPlayerController>(Controller);
//if (PC)
//{
// // later delete this
// //FVector loc = FirstPersonCameraComponent->GetComponentLocation();
// //FRotator rot = FirstPersonCameraComponent->GetComponentRotation();
// FVector loc;
// FRotator rot;
// //PC->HandleDead(loc, rot);

// bThirdPersonCamLerp = true;
// //PC->HandleDead(loc, rot)
//}
bThirdPersonDeathCamLerp = true;
ThirdPersonCamDistance = ThirdPersonDeathCamDistance;
ATotemPlayerController *PC = Cast<ATotemPlayerController>(Controller);

if (PC)
{

PC->DisableInput(PC);
}

CurrentDeadBodyDuration = TotalDeadBodyDuration;
bIsDead = true;
EnterDeath();
GetWorld()->GetTimerManager().SetTimer(mDeathDissolveEffectTimerHandle, this, &ATotemCharacter::CallBlueprintEventForMaterial,
DeathDissolvePlayAfterTimer, false);
//don't destory pawn himself, will cause weird
//Problem. But I don't know why...
//Destroy();
}

}
}

void ATotemCharacter::OnFire()
{
//only server can spawn projectile theoretically
// try and fire a projectile
if (ProjectileClass != NULL || FireProjectileClass != NULL)
{
const FRotator SpawnRotation = GetControlRotation();
// MuzzleOffset is in camera space, so transform it to world space before offsetting from the character location to find the final muzzle pos ition
const FVector SpawnLocation = GetActorLocation() + SpawnRotation.RotateVector(GunOffset);

UWorld* const World = GetWorld();


if (World != NULL)
{
//to add owner and instigator information
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = Cast<ATotemPlayerController>(Controller);
SpawnParams.Instigator = Instigator;
//When the server function is called by serve, it also execute.
ServerSpawnProjectile(SpawnLocation, SpawnRotation, SpawnParams.Owner, SpawnParams.Instigator);
// spawn the projectile at the muzzle single player version
//World->SpawnActor<ATotemProjectile>(ProjectileClass, SpawnLocation, SpawnRotation, SpawnParams);
}
}

// try and play the sound if specified


if (FireSound != NULL)
{
UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
}

// try and play a firing animation if specified


if (FireAnimation != NULL)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if (AnimInstance != NULL)
{
AnimInstance->Montage_Play(FireAnimation, 1.f);
}
}

}
void ATotemCharacter::OnShotDown()
{
ShotPressed = true;
ShotFired = false;
ShotDownMetrics();
}

void ATotemCharacter::ShotDownMetrics_Implementation()
{
#if WRITE_METRICS

ATotemPlayerController* control = Cast<ATotemPlayerController>(Controller);


if (control)
{
ATotemPlayerState* state = Cast<ATotemPlayerState>(control->PlayerState);
if (state)
{
std::string team;
switch (state->Team)
{
case ETeam::RED_TEAM:
team = "Red";
break;
case ETeam::BLUE_TEAM:
team = "Blue";
break;
default:
team = "None";
break;
}
std::string attack;
if (Cast<AFireShaman>(this))
{
attack = "Flamethrower start";
Metrics::WriteToFile(std::string(TCHAR_TO_UTF8(*(state->MyName))), team, attack);
}
}
}
#endif
}

bool ATotemCharacter::ShotDownMetrics_Validate()
{
return true;
}

void ATotemCharacter::OnShotUp()
{
ShotPressed = false;
ShotUpMetrics();
}

void ATotemCharacter::ShotUpMetrics_Implementation()
{
#if WRITE_METRICS
if (Cast<AFireShaman>(this))
{
ATotemPlayerController* control = Cast<ATotemPlayerController>(Controller);
if (control)
{
ATotemPlayerState* state = Cast<ATotemPlayerState>(control->PlayerState);
if (state)
{
std::string team;
switch (state->Team)
{
case ETeam::RED_TEAM:
team = "Red";
break;
case ETeam::BLUE_TEAM:
team = "Blue";
break;
default:
team = "None";
break;
}
Metrics::WriteToFile(std::string(TCHAR_TO_UTF8(*(state->MyName))), team, std::string("Flamethrower end"));
}
}
}
#endif
}

bool ATotemCharacter::ShotUpMetrics_Validate()
{
return true;
}

void ATotemCharacter::TouchStarted(const ETouchIndex::Type FingerIndex, const FVector Location)


{
// only fire for first finger down
if (FingerIndex == 0)
{
OnFire();
}
}

void ATotemCharacter::MoveForward(float Value)


{
if (Value != 0.0f && bCanMove && bAcceptMoveInput)
{
// add movement in that direction
AddMovementInput(GetActorForwardVector(), Value * MoveSpeedScale);
}
}

void ATotemCharacter::MoveRight(float Value)


{
if (Value != 0.0f && bCanMove && bAcceptMoveInput)
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value * StrafeSpeedScale);
}
}

void ATotemCharacter::TurnAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void ATotemCharacter::LookUpAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

void ATotemCharacter::SetCurrentAbility(class BaseAbility* ability)


{
CurrentAbility = ability;
}

void ATotemCharacter::RightUp()
{

void ATotemCharacter::RightDown()
{
bRightButtonDownThisFrame = true;
}

void ATotemCharacter::ScrollUp()
{
if (CurrentAbility != nullptr) CurrentAbility->ScrollUp();
}

void ATotemCharacter::ScrollDown()
{
if (CurrentAbility != nullptr) CurrentAbility->ScrollDown();
}

int ATotemCharacter::CurrentAbilityIndex()
{
if (CurrentAbility != nullptr)
{
return CurrentAbility->GetIndex();
}
else
{
return -1;
}
}

void ATotemCharacter::AddAbility(BaseAbility* newAbility)


{
newAbility->SetIndex(NumAbilities);
if (CurrentAbility == nullptr)
{
CurrentAbility = newAbility;
CurrentAbility->SetNextAbility(CurrentAbility);
CurrentAbility->SetPreviousAbility(CurrentAbility);
++NumAbilities;
}
else
{
newAbility->SetNextAbility(CurrentAbility);
newAbility->SetPreviousAbility(CurrentAbility->GetPreviousAbility());
CurrentAbility->GetPreviousAbility()->SetNextAbility(newAbility);
CurrentAbility->SetPreviousAbility(newAbility);
++NumAbilities;
}
}

void ATotemCharacter::BeginDestroy()
{
while (NumAbilities > 0)
{
BaseAbility* DestroyAbility = CurrentAbility;
CurrentAbility = CurrentAbility->GetNextAbility();
delete DestroyAbility;
--NumAbilities;
}
Super::BeginDestroy();
}

void ATotemCharacter::Jump()
{
//ActInAir();
Super::Jump();
}

//This only called on the server.


void ATotemCharacter::PossessedBy(AController * NewController)
{
Super::PossessedBy(NewController);
ATotemPlayerState* TotemPlayerState = Cast<ATotemPlayerState>(PlayerState);
if (TotemPlayerState)
{
StartingLocation = TotemPlayerState->StartingLocation;
StartingRotation = TotemPlayerState->StartingRotation;
ATotemPlayerState* TotemPlayerState = Cast<ATotemPlayerState>(PlayerState);
if (TotemPlayerState)
{
TotemPlayerState->SetCurrentState(ETotemPlayState::Playing);
}
Team = TotemPlayerState->Team;
}
}

void ATotemCharacter::SetAbility(int32 index)


{
int count = 0;
if (index < NumAbilities && !CurrentAbility->isRightClickDown())
{
while (index != CurrentAbilityIndex())
{
ScrollUp();
count++;
if (count > NumAbilities)
{
break;
}
}
}
}

float ATotemCharacter::CooldownLeftOnAbility(int32 AbilityIndex)


{
if (AbilityIndex >= NumAbilities)
{
return -1.0f;
}

BaseAbility* Ability = CurrentAbility;

while (AbilityIndex != Ability->GetIndex())


{
Ability = Ability->GetNextAbility();
}

return Ability->CooldownLeft();
}

float ATotemCharacter::CooldownPercentRemaining(int32 AbilityIndex)


{
if (AbilityIndex >= NumAbilities)
{
return -1.0f;
}

BaseAbility* Ability = CurrentAbility;

while (AbilityIndex != Ability->GetIndex())


{
Ability = Ability->GetNextAbility();
}

return (float)Ability->CooldownLeft() / (float)Ability->GetCooldown();


}

float ATotemCharacter::AbilityCooldown(int32 AbilityIndex)


{
if (AbilityIndex >= NumAbilities)
{
return -1.0f;
}

BaseAbility* Ability = CurrentAbility;

while (AbilityIndex != Ability->GetIndex())


{
Ability = Ability->GetNextAbility();
}

return Ability->GetCooldown();
}

void ATotemCharacter::ClientPlaySound_Implementation(USoundBase* Sound, float distToPlay, float VolumeMultiplier, float PitchMultiplier, float StartTime, USoundAttenuation*
AttenuationSettings)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("ClientPlayExplosionSound is triggered!"));
}
for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator)
{
if ((*Iterator)->IsLocalPlayerController())
{
ATotemCharacter* MyCharacter = Cast<ATotemCharacter>((*Iterator)->GetPawn());
if (MyCharacter)
{
//FVector use local variable store actor location
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("Local player is found!"));
}
if ((GetActorLocation() - MyCharacter->GetActorLocation()).Size() <= distToPlay)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("Dist check succeed!"));
}
UGameplayStatics::PlaySoundAtLocation(this, Sound, GetActorLocation(), VolumeMultiplier, PitchMultiplier, StartTime,
AttenuationSettings);
}

}
break;
}
}
}
void ATotemCharacter::ShrineUnlocked(FString name)
{
#if WRITE_METRICS
Metrics::WriteToFile(std::string(), std::string(), std::string(TCHAR_TO_UTF8(*name) + std::string(" shrine unlocked")));
#endif
}

void ATotemCharacter::ShrineCaptured(FString name, FString team)


{
#if WRITE_METRICS
Metrics::WriteToFile(std::string(), TCHAR_TO_UTF8(*team), std::string(std::string("captured ") + TCHAR_TO_UTF8(*name) + std::string(" shrine")));
#endif
}

//
void ATotemCharacter::EnterDeath()
{
ClientSetIfAcceptMoveInput(false);
bCanBasicAttack = false;
bCanUseAbility = false;
bAcceptCameraChange = false;
GetCharacterMovement()->Velocity = FVector(0.0f);
}

void ATotemCharacter::ExitReborn()
{
ClientSetIfAcceptMoveInput(true);
bCanBasicAttack = true;
bCanUseAbility = true;
}

void ATotemCharacter::ClientSetIfAcceptMoveInput_Implementation(bool flag)


{
bAcceptMoveInput = flag;
}

//
//void ATotemCharacter::ActInAir_Implementation()
//{
//
//}
//
//bool ATotemCharacter::ActInAir_Validate()
//{
// return true;
//}

void ATotemCharacter::DestroyTerrain(FVector location, float radius, UWorld* world)


{

TActorIterator<ATotemCharacter> characterIterator(world);

if (characterIterator && (*characterIterator))


{
(*characterIterator)->ClientDestroyTerrain(location, radius);
}
}

void ATotemCharacter::ClientDestroyTerrain_Implementation(FVector location, float radius)


{
//UE_LOG(LogTemp, Warning, TEXT("ClientDestroyTerrain_Implementation (FireBall): %s, radius : %f"), *location.ToString(), radius);

for (TActorIterator<ATerrainManager> ActorItr(GetWorld()); ActorItr; ++ActorItr)


{
ATerrainManager * voxelVolume = (ATerrainManager*)(*ActorItr);

voxelVolume->EditVolume(location, radius, false);


//voxelVolume->ExecAddMesh(SpawnLocation, 10.0f);
}
}

void ATotemCharacter::AddControllerYawInput(float Val)


{
if (bAcceptCameraChange)
{
Super::AddControllerYawInput(Val);
}

void ATotemCharacter::AddControllerPitchInput(float Val)


{
if (bAcceptCameraChange)
{
Super::AddControllerPitchInput(Val);
}
}

void ATotemCharacter::ToggleSpectator()
{
if (Controller)
{
ATotemPlayerController* TotemPlayerController = Cast<ATotemPlayerController>(Controller);
if (TotemPlayerController)
{
TotemPlayerController->ServerSetMyShaman(EPlayerType::SPECTATE_SHAMAN);
ATotemPlayerState* TotemPlayerState = Cast<ATotemPlayerState>(TotemPlayerController->PlayerState);
if (TotemPlayerState)
{
TotemPlayerState->bDisplayUI = false;
}
}
}
}

void ATotemCharacter::LeftDown()
{
bClickedThisFrame = true;
}

void ATotemCharacter::LeftUp()
{

bool ATotemCharacter::Invincible()
{
return bInvincible;
}

void ATotemCharacter::SetInvincible(bool invince)


{
bInvincible = invince;
bCanBasicAttack = !invince;
bCanUseAbility = !invince;
}
void ATotemCharacter::SetTotemCharacterMoveState(ETotemCharacterMoveState NewState)
{
TotemCharacterMoveExit(TotemCharacterMoveState);
TotemCharacterMoveState = NewState;
ServerSyncTotemCharacterMoveState(NewState);
TotemCharacterMoveEnter(NewState);
}

void ATotemCharacter::ServerSyncTotemCharacterMoveState_Implementation(ETotemCharacterMoveState NewState)


{
TotemCharacterMoveState = NewState;
//OnRep_TotemCharacterMoveState();
ClientsSwitchSpeed(NewState);
}

bool ATotemCharacter::ServerSyncTotemCharacterMoveState_Validate(ETotemCharacterMoveState NewState)


{
return true;
}

void ATotemCharacter::ClientsSwitchSpeed_Implementation(ETotemCharacterMoveState NewState)


{
switch (TotemCharacterMoveState)
{
case ETotemCharacterMoveState::NoMove:
{
GetCharacterMovement()->MaxWalkSpeed = NormalWalkSpeed;
break;
}
case ETotemCharacterMoveState::Sprint:
{
GetCharacterMovement()->MaxWalkSpeed = SprintSpeed;
break;
}
}
}

void ATotemCharacter::OnRep_TotemCharacterMoveState()
{
// change speed here GetCharacterMovement()->MaxWalkSpeed = NormalMaxSpeed;
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("Unreal API Shit!"));
}
switch (TotemCharacterMoveState)
{
case ETotemCharacterMoveState::NoMove:
{
GetCharacterMovement()->MaxWalkSpeed = NormalWalkSpeed;
break;
}
case ETotemCharacterMoveState::Sprint:
{
GetCharacterMovement()->MaxWalkSpeed = SprintSpeed;
break;
}
}

void ATotemCharacter::TotemCharacterMoveEnter(ETotemCharacterMoveState NewState)


{
switch (NewState)
{
case ETotemCharacterMoveState::NoMove:
{
#if PRINT_LOG
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("No Sprint!"));
}
break;
#endif
}
case ETotemCharacterMoveState::Sprint:
{
#if PRINT_LOG
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("Sprint!"));
}
break;
#endif
}
}
}

void ATotemCharacter::TotemCharacterMoveExit(ETotemCharacterMoveState OldState)


{
switch (OldState)
{
case ETotemCharacterMoveState::NoMove:
break;
case ETotemCharacterMoveState::Sprint:
break;
}
}

void ATotemCharacter::CheckShiftKey()
{
//For Sprint, designed only execute on owning client
if (Controller)
{
if (Controller->IsLocalPlayerController())
{
APlayerController* PlayerController = Cast<APlayerController>(Controller);
if (PlayerController)
{
if (PlayerController->IsInputKeyDown(EKeys::LeftShift))
{
// if no move state && on the ground
if (TotemCharacterMoveState == ETotemCharacterMoveState::NoMove && GetCharacterMovement()->IsWalking())
{
SetTotemCharacterMoveState(ETotemCharacterMoveState::Sprint);
}
}
else
{
if (TotemCharacterMoveState == ETotemCharacterMoveState::Sprint)
{
SetTotemCharacterMoveState(ETotemCharacterMoveState::NoMove);
}
}
}
}
}
}
bool ATotemCharacter::RayCastOnGround()
{
UWorld *world = GetWorld();

FVector StartTrace = GetActorLocation();


FVector EndTrace = StartTrace - FVector(0, 0, 150);

if (world)
{
FHitResult Hit(ForceInit);
FCollisionQueryParams collisionParams(FName(TEXT("EARTH_SHAMAN_TRACE")), true, this);
world->LineTraceSingle(Hit, StartTrace, EndTrace, ECC_WorldStatic, collisionParams);

if (Hit.bBlockingHit && Hit.GetActor())


{
//DrawDebugLine(world, StartTrace, Hit.ImpactPoint, FColor::Green, false, 5.0f);
return true;
}
}
//DrawDebugLine(world, StartTrace, EndTrace, FColor::Red, false, 5.0f);

return false;
}

void ATotemCharacter::CallBlueprintEventForMaterial()
{

bDeathEffectFired = true;
}

EXAMPLE #6

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.

#include "GameWeapons.h"
#include "States/GWWeaponState.h"
#include "Tracing/GWTraceBase.h"
#include "Tracing/GWTraceRangedWeapon.h"

#include "Ammo/GWAmmo.h"

#include "IGISkeletalMesh.h"

#include "Net/UnrealNetwork.h"

#include "GWWeaponRanged.h"

AGWWeaponRanged::AGWWeaponRanged(const FObjectInitializer& ObjectInitializer)


: Super(ObjectInitializer)
{
bReplicates = true;

PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.bStartWithTickEnabled = false;
PrimaryActorTick.bAllowTickOnDedicatedServer = true;
PrimaryActorTick.bRunOnAnyThread = true;
PrimaryActorTick.TickGroup = ETickingGroup::TG_DuringPhysics;
SetTickGroup(ETickingGroup::TG_DuringPhysics);

bIsWeaponFiring = false;

ReloadEndCount = 0;
}
void AGWWeaponRanged::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
if (bTraceEveryTick && bIsWeaponFiring && TargetingMethod)
{
TargetingMethod->SingleLineTraceSetHitLocation();
if (Role < ROLE_Authority || GetNetMode() == ENetMode::NM_Standalone)
OnWeaponFiring(HitInfo.Origin, HitInfo.HitLocation);
}
}
void AGWWeaponRanged::BeginPlay()
{
CurrentState = ActiveState;
RemaningAmmo = MaximumAmmo;
RemainingMagazineAmmo = MagazineSize;
CurrentSpread = BaseSpread;
CurrentHorizontalRecoil = RecoilConfig.HorizontalRecoilBase;
CurrentVerticalRecoil = RecoilConfig.VerticalRecoilBase;

if (TargetingMethod)
{
TargetingMethod->SetRange(Range);
TargetingMethod->SetCurrentSpread(CurrentSpread);
TargetingMethod->Initialize();
}

if (TypeOfAmmo)
{
CurrentAmmo = NewObject<UGWAmmo>(TypeOfAmmo);
}
}
void AGWWeaponRanged::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);

DOREPLIFETIME_CONDITION(AGWWeaponRanged, RemainingMagazineAmmo, COND_OwnerOnly);


DOREPLIFETIME_CONDITION(AGWWeaponRanged, RemaningAmmo, COND_OwnerOnly);
DOREPLIFETIME(AGWWeaponRanged, ReloadEndCount);
DOREPLIFETIME(AGWWeaponRanged, ReloadBeginCount);

void AGWWeaponRanged::InitializeWeapon()
{
Super::InitializeWeapon();
}
void AGWWeaponRanged::InputPressed()
{
BeginFire();
}
void AGWWeaponRanged::InputReleased()
{
EndFire();
}

void AGWWeaponRanged::InputReload()
{
BeginReload();
}
void AGWWeaponRanged::FireWeapon(const FHitResult& TargetIn, float DamageIn, APawn* InstigatorIn)
{
if (CurrentAmmo)
{
CurrentAmmo->ApplyDamage(TargetIn, DamageIn, InstigatorIn);
}
}
void AGWWeaponRanged::ShootWeapon()
{
if (!CheckIfHaveAmmo())
{
CurrentState->EndActionSequence();
return;
}
SubtractAmmo();
CalculateCurrentWeaponSpread();

//if (Role == ROLE_Authority)


//{
if (TargetingMethod)
TargetingMethod->Execute();

OnShoot();
//}
if (GetNetMode() == ENetMode::NM_Standalone || Role < ROLE_Authority)
OnRep_HitInfo();
}
void AGWWeaponRanged::ActionEnd()
{

//activate states
void AGWWeaponRanged::BeginFire()
{
//if (Role < ROLE_Authority)
//{
// if (!CheckIfHaveAmmo())
// return;

// ServerBeginFire();
// bIsWeaponFiring = true;
// //start shooting also on onwing client, to provide better feedback.
// CurrentState->BeginActionSequence();
// //OnRep_HitInfo();

// GetWorldTimerManager().ClearTimer(ReduceSpreadOverTimeTimerHandle);
// //if (TargetingMethod)
// // TargetingMethod->Execute();
//}
//else
//{
if (!CheckIfHaveAmmo())
{
CurrentState->EndActionSequence();
return;
}
GetWorldTimerManager().ClearTimer(ReduceSpreadOverTimeTimerHandle);
bIsWeaponFiring = true;
CurrentState->BeginActionSequence();
//}
}
void AGWWeaponRanged::ServerBeginFire_Implementation()
{
BeginFire();
}
bool AGWWeaponRanged::ServerBeginFire_Validate()
{
return true;
}

void AGWWeaponRanged::EndFire()
{
//if (Role < ROLE_Authority)
//{
// bIsWeaponFiring = false;
// CurrentState->EndActionSequence();

// GetWorldTimerManager().SetTimer(ReduceSpreadOverTimeTimerHandle, this, &AGWWeaponRanged::ReduceSpreadOverTime, 0.1, true);


// ServerEndFire();
//}
//else
//{
bIsWeaponFiring = false;
GetWorldTimerManager().SetTimer(ReduceSpreadOverTimeTimerHandle, this, &AGWWeaponRanged::ReduceSpreadOverTime, 0.1, true);
CurrentState->EndActionSequence();
OnFireEnd();
//}
}
void AGWWeaponRanged::ServerEndFire_Implementation()
{
EndFire();
}
bool AGWWeaponRanged::ServerEndFire_Validate()
{
return true;
}

void AGWWeaponRanged::BeginReload()
{
if (Role < ROLE_Authority)
{
OnRep_ReloadBeign();
ServerBeginReload();
}
else
{
ReloadBeginCount++;
GotoState(ReloadState);
if (GetNetMode() == ENetMode::NM_Standalone)
OnRep_ReloadBeign();
}
}
void AGWWeaponRanged::ServerBeginReload_Implementation()
{
BeginReload();
}
bool AGWWeaponRanged::ServerBeginReload_Validate()
{
return true;
}

void AGWWeaponRanged::CalculateReloadAmmo()
{
float NeededAmmo = MagazineSize - RemainingMagazineAmmo;
if (NeededAmmo <= RemainingMagazineAmmo)
return;
RemaningAmmo -= NeededAmmo;
RemainingMagazineAmmo += NeededAmmo;
}

void AGWWeaponRanged::EndReload()
{
if (Role < ROLE_Authority)
{
if (!CheckIfCanReload())
return;
CalculateReloadAmmo();
ServerEndReload();
}
else
{
/*
Impelemtnsome reload mechanic..
1. Where is ammo coming from ?
2. How it is setup ?
3. Does weapon have it's own magazine max ammo count, or is it stored externally ?
*/
if (!CheckIfCanReload())
return;
CalculateReloadAmmo();
ReloadEndCount++;
if (GetNetMode() == ENetMode::NM_Standalone)
OnRep_ReloadEnd();
}
}
void AGWWeaponRanged::ServerEndReload_Implementation()
{
EndReload();
}
bool AGWWeaponRanged::ServerEndReload_Validate()
{
return true;
}

void AGWWeaponRanged::OnRep_Shooting()
{
//OnWeaponFiring();
}

void AGWWeaponRanged::OnRep_ReloadBeign()
{
OnReloadBegin();
if (ReloadAnimation)
{
IIGISkeletalMesh* skelMeshInt = Cast<IIGISkeletalMesh>(Instigator);
if (!skelMeshInt)
return;

UAnimInstance* AnimInst = skelMeshInt->GetMasterSkeletalMesh()->GetAnimInstance();


if (!AnimInst)
return;
float montageLenght = ReloadAnimation->CalculateSequenceLength();

float multiplier = montageLenght / ReloadTime;

AnimInst->Montage_Play(ReloadAnimation, multiplier);
}
}

void AGWWeaponRanged::OnRep_ReloadEnd()
{
OnReloadEnd();
}

void AGWWeaponRanged::OnRep_HitInfo()
{
OnWeaponFiring(HitInfo.Origin, HitInfo.HitLocation);
}

bool AGWWeaponRanged::CheckIfHaveAmmo()
{
if (RemainingMagazineAmmo > 0)
return true;
else
return false;
}
void AGWWeaponRanged::SubtractAmmo()
{
RemainingMagazineAmmo -= AmmoCost;
}
bool AGWWeaponRanged::CheckIfCanReload()
{
if (RemainingMagazineAmmo >= MagazineSize)
return false;
else
return true;
}

void AGWWeaponRanged::CalculateCurrentWeaponSpread()
{
CurrentSpread = CurrentSpread * SpreadMultiplier;
CurrentHorizontalRecoil = CurrentHorizontalRecoil * RecoilConfig.HorizontalRecoilMultiplier;
CurrentVerticalRecoil = CurrentVerticalRecoil * RecoilConfig.VerticalRecoilMultiplier;
if (CurrentHorizontalRecoil > RecoilConfig.HorizontalRecoilMaximum)
{
CurrentHorizontalRecoil = RecoilConfig.HorizontalRecoilMaximum;
}
if (CurrentVerticalRecoil > RecoilConfig.VerticalRecoilMaximum)
{
CurrentVerticalRecoil = RecoilConfig.VerticalRecoilMaximum;
}
if (CurrentSpread > MaximumSpread)
{
CurrentSpread = MaximumSpread;
}
OnCurrentWeaponSpread.Broadcast(CurrentSpread);
if (TargetingMethod)
{
TargetingMethod->SetCurrentSpread(CurrentSpread);
}
}
void AGWWeaponRanged::ReduceSpreadOverTime()
{
CurrentSpread -= SpreadReduce;
CurrentHorizontalRecoil -= 1;
CurrentVerticalRecoil -= 1;
if (CurrentSpread <= BaseSpread)
{
CurrentSpread = BaseSpread;
GetWorldTimerManager().ClearTimer(ReduceSpreadOverTimeTimerHandle);
}
}
EXAMPLE #7

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#include "UDKPresentation.h"
#include "UDKPresentationCharacter.h"
#include "UDKPresentationProjectile.h"
#include "Animation/AnimInstance.h"
#include "GameFramework/InputSettings.h"

DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);

//////////////////////////////////////////////////////////////////////////
// AUDKPresentationCharacter

AUDKPresentationCharacter::AUDKPresentationCharacter()
{
maxJump = 1;
currentJump = 0;
maxAmmo = 0;
ammo = maxAmmo;
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

// set our turn rates for input


BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;

// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
FirstPersonCameraComponent->RelativeLocation = FVector(0, 0, 64.f); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;

// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true);
Mesh1P->AttachParent = FirstPersonCameraComponent;
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;

// Create a gun mesh component


FP_Gun = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FP_Gun"));
FP_Gun->SetOnlyOwnerSee(true); // only the owning player will see this mesh
FP_Gun->bCastDynamicShadow = false;
FP_Gun->CastShadow = false;
FP_Gun->AttachTo(Mesh1P, TEXT("GripPoint"), EAttachLocation::SnapToTargetIncludingScale, true);

// Default offset from the character location for projectiles to spawn


GunOffset = FVector(100.0f, 30.0f, 10.0f);

// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}

void AUDKPresentationCharacter::UpdateAmmo()
{
if (CharacterMovement->IsMovingOnGround()) {
ammo = maxAmmo;
}
}

void AUDKPresentationCharacter::MoreAmmo()
{
maxAmmo++;
}

int AUDKPresentationCharacter::GetMaxAmmo()
{
return maxAmmo;
}

int AUDKPresentationCharacter::GetAmmo()
{
if (CharacterMovement->IsMovingOnGround()) {
ammo = maxAmmo;
}
return ammo;
}

void AUDKPresentationCharacter::Jump()
{
if (CharacterMovement->IsMovingOnGround()) {
currentJump = 0;
bPressedJump = true;
JumpKeyHoldTime = 0.0f;
ammo = maxAmmo;
}
else {
DoubleJump();
}
}

void AUDKPresentationCharacter::DoubleJump() {
if (currentJump < maxJump) {
CharacterMovement->Velocity.Z = 0;
CharacterMovement->Velocity += FVector(0, 0, 500);
currentJump++;
}
}

void AUDKPresentationCharacter::StopJumping()
{
bPressedJump = false;
JumpKeyHoldTime = 0.0f;
}

//////////////////////////////////////////////////////////////////////////
// Input

void AUDKPresentationCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)


{
// set up gameplay key bindings
check(InputComponent);

InputComponent->BindAction("Jump", IE_Pressed, this, &AUDKPresentationCharacter::Jump);


InputComponent->BindAction("Jump", IE_Released, this, &AUDKPresentationCharacter::StopJumping);

//InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AUDKPresentationCharacter::TouchStarted);


if( EnableTouchscreenMovement(InputComponent) == false )
{
InputComponent->BindAction("Fire", IE_Pressed, this, &AUDKPresentationCharacter::OnFire);
}

InputComponent->BindAxis("MoveForward", this, &AUDKPresentationCharacter::MoveForward);


InputComponent->BindAxis("MoveRight", this, &AUDKPresentationCharacter::MoveRight);

// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
InputComponent->BindAxis("TurnRate", this, &AUDKPresentationCharacter::TurnAtRate);
InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
InputComponent->BindAxis("LookUpRate", this, &AUDKPresentationCharacter::LookUpAtRate);
}

void AUDKPresentationCharacter::OnFire()
{
if (CharacterMovement->IsMovingOnGround()) {
ammo = maxAmmo;
}
if (ammo <= 0) return;
// try and fire a projectile
if (ProjectileClass != NULL)
{
const FRotator SpawnRotation = GetControlRotation();
// MuzzleOffset is in camera space, so transform it to world space before offsetting from the character location to find the final muzzle pos ition
const FVector SpawnLocation = GetActorLocation() + SpawnRotation.RotateVector(GunOffset);

ammo--;
CharacterMovement->Velocity = GetControlRotation().Vector()*(-1000) + 0.5f * CharacterMovement->Velocity;

UWorld* const World = GetWorld();


if (World != NULL)
{
// spawn the projectile at the muzzle
World->SpawnActor<AUDKPresentationProjectile>(ProjectileClass, SpawnLocation, SpawnRotation);
}
}

// try and play the sound if specified


if (FireSound != NULL)
{
UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
}

// try and play a firing animation if specified


if(FireAnimation != NULL)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if(AnimInstance != NULL)
{
AnimInstance->Montage_Play(FireAnimation, 1.f);
}
}

void AUDKPresentationCharacter::BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if( TouchItem.bIsPressed == true )
{
return;
}
TouchItem.bIsPressed = true;
TouchItem.FingerIndex = FingerIndex;
TouchItem.Location = Location;
TouchItem.bMoved = false;
}

void AUDKPresentationCharacter::EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if (TouchItem.bIsPressed == false)
{
return;
}
if( ( FingerIndex == TouchItem.FingerIndex ) && (TouchItem.bMoved == false) )
{
OnFire();
}
TouchItem.bIsPressed = false;
}

void AUDKPresentationCharacter::TouchUpdate(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if ((TouchItem.bIsPressed == true) && ( TouchItem.FingerIndex==FingerIndex))
{
if (TouchItem.bIsPressed)
{
if (GetWorld() != nullptr)
{
UGameViewportClient* ViewportClient = GetWorld()->GetGameViewport();
if (ViewportClient != nullptr)
{
FVector MoveDelta = Location - TouchItem.Location;
FVector2D ScreenSize;
ViewportClient->GetViewportSize(ScreenSize);
FVector2D ScaledDelta = FVector2D( MoveDelta.X, MoveDelta.Y) / ScreenSize;

if (ScaledDelta.X != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.X * BaseTurnRate;
AddControllerYawInput(Value);
}
if (ScaledDelta.Y != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.Y* BaseTurnRate;
AddControllerPitchInput(Value);
}
TouchItem.Location = Location;
}
TouchItem.Location = Location;
}
}
}
}

void AUDKPresentationCharacter::MoveForward(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorForwardVector(), Value);
}
}

void AUDKPresentationCharacter::MoveRight(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
}
}

void AUDKPresentationCharacter::TurnAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void AUDKPresentationCharacter::LookUpAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

bool AUDKPresentationCharacter::EnableTouchscreenMovement(class UInputComponent* InputComponent)


{
bool bResult = false;
if(FPlatformMisc::GetUseVirtualJoysticks() || GetDefault<UInputSettings>()->bUseMouseForTouch )
{
bResult = true;
InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AUDKPresentationCharacter::BeginTouch);
InputComponent->BindTouch(EInputEvent::IE_Released, this, &AUDKPresentationCharacter::EndTouch);
InputComponent->BindTouch(EInputEvent::IE_Repeat, this, &AUDKPresentationCharacter::TouchUpdate);
}
return bResult;
}

EXAMPLES #8 and #9

// Fill out your copyright notice in the Description page of Project Settings.

#include "FPSCode.h"
#include "Gun.h"
#include "Animation/AnimInstance.h"
#include "FPSCodeCharacter.h"
#include "Kismet/KismetMathLibrary.h"

AGun::AGun()
{
Mesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Mesh"));

void AGun::BeginPlay()
{
Super::BeginPlay();

CurrentAmmo = MagSize;

//If Aiming FOV is not set, set it to 65


if (!AimingFOV)
{
AimingFOV = 65;
}
}

void AGun::Tick(float Seconds)


{
Super::Tick(Seconds);

//Applying negative recoil to compensate for recoil that was added during firing (occurs after stopping firing)
if (RecoilInterp)
{
if ((UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Pitch > 270 && InitialRecoilPitch > 270) || (UGameplayStatics::GetPlayerController(this,
0)->GetControlRotation().Pitch < 270 && InitialRecoilPitch < 270))
{
if (UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Pitch > InitialRecoilPitch)
{
float Pitch = UKismetMathLibrary::FInterpTo_Constant(UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Pitch,
InitialRecoilPitch, Seconds, 12);
float Yaw = UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Yaw;
float Roll = UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Roll;

UGameplayStatics::GetPlayerController(this, 0)->SetControlRotation(FRotator{ Pitch, Yaw, Roll });


}
}
else
{
if (UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Pitch < InitialRecoilPitch)
{
float Pitch = UKismetMathLibrary::FInterpTo_Constant(UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Pitch,
UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Pitch - 90, Seconds, 12);
float Yaw = UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Yaw;
float Roll = UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Roll;

UGameplayStatics::GetPlayerController(this, 0)->SetControlRotation(FRotator{ Pitch, Yaw, Roll });


}
else
{
float Pitch = UKismetMathLibrary::FInterpTo_Constant(UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Pitch,
InitialRecoilPitch, Seconds, 12);
float Yaw = UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Yaw;
float Roll = UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Roll;

UGameplayStatics::GetPlayerController(this, 0)->SetControlRotation(FRotator{ Pitch, Yaw, Roll });


}
}
}

void AGun::Fire()
{

void AGun::OnEquip()
{
Super::OnEquip();

if (WeaponEquipAnimation)
{
Mesh->GetAnimInstance()->Montage_Play(WeaponEquipAnimation, 1.f);
}

if (PlayerEquipAnimation)
{
UAnimInstance* AnimInstance = Cast<AFPSCodeCharacter>(UGameplayStatics::GetPlayerCharacter(this, 0))->GetMesh1P()->GetAnimInstance();

if (AnimInstance)
{
AnimInstance->Montage_Play(PlayerEquipAnimation, 1.f);
}
}

void AGun::Attack()
{
if (CurrentAmmo > 0)
{
if (bIsAuto)
{
if (bCanAttack)
{
bCanAttack = false;

CanStopShooting = true;

GetWorld()->GetTimerManager().SetTimer(BoolTimerHandle, this, &AGun::SetCanAttack, 1 / (RateOfFire / 60)); //Setting timer for being able
to attack again (to prevent spamming attack)

GetWorld()->GetTimerManager().SetTimer(AutoTimerHandle, this, &AGun::Fire, 1 / (RateOfFire / 60), true, 0.f); //Setting timer for
refiring for automatic weapons

InitialRecoilPitch = UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Pitch; //Setting initial pitch before


recoil is added

if (!MuzzleSocket.IsNone()) //Drawing muzzle flash


{
FVector Location = Mesh->GetSocketLocation(MuzzleSocket);
FRotator Rotation = Mesh->GetSocketRotation(MuzzleSocket);

Flash = UGameplayStatics::SpawnEmitterAttached(MuzzleFlash, Mesh, MuzzleSocket, Location, Rotation,


EAttachLocation::KeepWorldPosition);
}
}
}
else
{
if (bCanAttack)
{
bCanAttack = false;

GetWorld()->GetTimerManager().SetTimer(BoolTimerHandle, this, &AGun::SetCanAttack, 1 / (RateOfFire / 60));

Fire();
}
}
}
}

void AGun::SetCanAttack()
{
bCanAttack = true;
}

void AGun::StopFire()
{
if (bIsAuto)
{
if (CanStopShooting)
{
CanStopShooting = false;

if (GetWorld()->GetTimerManager().IsTimerActive(AutoTimerHandle)) //Clearing timers


{
GetWorld()->GetTimerManager().ClearTimer(AutoTimerHandle);
}

if (Flash)
{
Flash->DestroyComponent();
}

RecoilInterp = true;

if (GetWorld()->GetTimerManager().IsTimerActive(RecoilHandle)) //If recoil is active, disable it


{
GetWorld()->GetTimerManager().ClearTimer(RecoilHandle);
}

GetWorld()->GetTimerManager().SetTimer(RecoilHandle, this, &AGun::DisableRecoilInterp, .4f); //Set timer to disable recoil after short amount of
time
}
}
}

void AGun::Reload()
{
if (!IsReloading)
{
if (CurrentAmmo < MagSize && TotalAmmo > 0)
{
IsReloading = true;

//Playing Reload animation on weapon


if (WeaponReloadAnimation)
{
Mesh->GetAnimInstance()->Montage_Play(WeaponReloadAnimation, 1.f);
}

//Playing Reload animation montage on player


if (PlayerReloadAnimation)
{
UAnimInstance* AnimInstance = Cast<AFPSCodeCharacter>(UGameplayStatics::GetPlayerCharacter(this, 0))->GetMesh1P()->GetAnimInstance();

if (AnimInstance)
{
bCanAttack = false;

float AnimTime = AnimInstance->Montage_Play(PlayerReloadAnimation, 1.f);

FTimerHandle AnimHandle;

GetWorld()->GetTimerManager().SetTimer(AnimHandle, this, &AGun::SetAmmoValues, AnimTime); //Set timer to apply reloaded


ammo values when animation is done
}
else
{
SetAmmoValues();
}
}
else
{
SetAmmoValues();
}
}
}
}

//Setting various ammo counts based on current ones


void AGun::SetAmmoValues()
{
bCanAttack = true;
IsReloading = false;

if ((TotalAmmo + CurrentAmmo) < MagSize)


{
CurrentAmmo = CurrentAmmo + TotalAmmo;

TotalAmmo = 0;
}
else
{
TotalAmmo = TotalAmmo - (MagSize - CurrentAmmo);

CurrentAmmo = MagSize;
}
}

void AGun::DisableRecoilInterp()
{
RecoilInterp = false;

EXAMPLE #10

(see EXAMPLE # 3)
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

/*=============================================================================
Character.cpp: ACharacter implementation
=============================================================================*/

#include "EnginePrivate.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Components/PrimitiveComponent.h"
#include "Net/UnrealNetwork.h"
#include "DisplayDebugHelpers.h"
#include "Animation/AnimInstance.h"
#include "Animation/AnimMontage.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/DamageType.h"

DEFINE_LOG_CATEGORY_STATIC(LogCharacter, Log, All);


DEFINE_LOG_CATEGORY_STATIC(LogAvatar, Log, All);
FName ACharacter::MeshComponentName(TEXT("CharacterMesh0"));
FName ACharacter::CharacterMovementComponentName(TEXT("CharMoveComp"));
FName ACharacter::CapsuleComponentName(TEXT("CollisionCylinder"));

ACharacter::ACharacter(const FObjectInitializer& ObjectInitializer)


: Super(ObjectInitializer)
{
// Structure to hold one-time initialization
struct FConstructorStatics
{
FName ID_Characters;
FText NAME_Characters;
FConstructorStatics()
: ID_Characters(TEXT("Characters"))
, NAME_Characters(NSLOCTEXT("SpriteCategory", "Characters", "Characters"))
{
}
};
static FConstructorStatics ConstructorStatics;

// Character rotation only changes in Yaw, to prevent the capsule from changing orientation.
// Ask the Controller for the full rotation if desired (ie for aiming).
bUseControllerRotationPitch = false;
bUseControllerRotationRoll = false;
bUseControllerRotationYaw = true;

CapsuleComponent = CreateDefaultSubobject<UCapsuleComponent>(ACharacter::CapsuleComponentName);
CapsuleComponent->InitCapsuleSize(34.0f, 88.0f);

static FName CollisionProfileName(TEXT("Pawn"));


CapsuleComponent->SetCollisionProfileName(CollisionProfileName);

CapsuleComponent->CanCharacterStepUpOn = ECB_No;
CapsuleComponent->bShouldUpdatePhysicsVolume = true;
CapsuleComponent->bCheckAsyncSceneOnMove = false;
CapsuleComponent->bCanEverAffectNavigation = false;
CapsuleComponent->bDynamicObstacle = true;
RootComponent = CapsuleComponent;

JumpKeyHoldTime = 0.0f;
JumpMaxHoldTime = 0.0f;

#if WITH_EDITORONLY_DATA
ArrowComponent = CreateEditorOnlyDefaultSubobject<UArrowComponent>(TEXT("Arrow"));
if (ArrowComponent)
{
ArrowComponent->ArrowColor = FColor(150, 200, 255);
ArrowComponent->bTreatAsASprite = true;
ArrowComponent->SpriteInfo.Category = ConstructorStatics.ID_Characters;
ArrowComponent->SpriteInfo.DisplayName = ConstructorStatics.NAME_Characters;
ArrowComponent->AttachParent = CapsuleComponent;
ArrowComponent->bIsScreenSizeScaled = true;
}
#endif // WITH_EDITORONLY_DATA

CharacterMovement = CreateDefaultSubobject<UCharacterMovementComponent>(ACharacter::CharacterMovementComponentName);
if (CharacterMovement)
{
CharacterMovement->UpdatedComponent = CapsuleComponent;
CrouchedEyeHeight = CharacterMovement->CrouchedHalfHeight * 0.80f;
}

Mesh = CreateOptionalDefaultSubobject<USkeletalMeshComponent>(ACharacter::MeshComponentName);
if (Mesh)
{
Mesh->AlwaysLoadOnClient = true;
Mesh->AlwaysLoadOnServer = true;
Mesh->bOwnerNoSee = false;
Mesh->MeshComponentUpdateFlag = EMeshComponentUpdateFlag::AlwaysTickPose;
Mesh->bCastDynamicShadow = true;
Mesh->bAffectDynamicIndirectLighting = true;
Mesh->PrimaryComponentTick.TickGroup = TG_PrePhysics;
Mesh->bChartDistanceFactor = true;
Mesh->AttachParent = CapsuleComponent;
static FName MeshCollisionProfileName(TEXT("CharacterMesh"));
Mesh->SetCollisionProfileName(MeshCollisionProfileName);
Mesh->bGenerateOverlapEvents = false;
Mesh->bCanEverAffectNavigation = false;
}

BaseRotationOffset = FQuat::Identity;
}

void ACharacter::PostInitializeComponents()
{
Super::PostInitializeComponents();

if (!IsPendingKill())
{
if (Mesh)
{
BaseTranslationOffset = Mesh->RelativeLocation;
BaseRotationOffset = Mesh->RelativeRotation.Quaternion();

// force animation tick after movement component updates


if (Mesh->PrimaryComponentTick.bCanEverTick && CharacterMovement)
{
Mesh->PrimaryComponentTick.AddPrerequisite(CharacterMovement, CharacterMovement->PrimaryComponentTick);
}
}

if (CharacterMovement && CapsuleComponent)


{
CharacterMovement->UpdateNavAgent(*CapsuleComponent);
}

if (Controller == NULL && GetNetMode() != NM_Client)


{
if (CharacterMovement && CharacterMovement->bRunPhysicsWithNoController)
{
CharacterMovement->SetDefaultMovementMode();
}
}
}
}

UPawnMovementComponent* ACharacter::GetMovementComponent() const


{
return CharacterMovement;
}

void ACharacter::SetupPlayerInputComponent(UInputComponent* InputComponent)


{
check(InputComponent);
}

void ACharacter::GetSimpleCollisionCylinder(float& CollisionRadius, float& CollisionHalfHeight) const


{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if (IsTemplate())
{
UE_LOG(LogCharacter, Log, TEXT("WARNING ACharacter::GetSimpleCollisionCylinder : Called on default object '%s'. Will likely return zero size. Consider using GetDefaultHalfHeight()
instead."), *this->GetPathName());
}
#endif

if (RootComponent == CapsuleComponent && IsRootComponentCollisionRegistered())


{
// Note: we purposefully ignore the component transform here aside from scale, always treating it as vertically aligned.
// This improves performance and is also how we stated the CapsuleComponent would be used.
CapsuleComponent->GetScaledCapsuleSize(CollisionRadius, CollisionHalfHeight);
}
else
{
Super::GetSimpleCollisionCylinder(CollisionRadius, CollisionHalfHeight);
}
}

void ACharacter::UpdateNavigationRelevance()
{
if (CapsuleComponent)
{
CapsuleComponent->SetCanEverAffectNavigation(bCanAffectNavigationGeneration);
}
}

void ACharacter::ApplyWorldOffset(const FVector& InOffset, bool bWorldShift)


{
Super::ApplyWorldOffset(InOffset, bWorldShift);

// Update cached location values in movement component


if (CharacterMovement)
{
CharacterMovement->OldBaseLocation+= InOffset;
}
}

float ACharacter::GetDefaultHalfHeight() const


{
UCapsuleComponent* DefaultCapsule = GetClass()->GetDefaultObject<ACharacter>()->CapsuleComponent;
if (DefaultCapsule)
{
return DefaultCapsule->GetScaledCapsuleHalfHeight();
}
else
{
return Super::GetDefaultHalfHeight();
}
}

UActorComponent* ACharacter::FindComponentByClass(const TSubclassOf<UActorComponent> ComponentClass) const


{
// If the character has a Mesh, treat it as the first 'hit' when finding components
if (Mesh && Mesh->IsA(ComponentClass))
{
return Mesh;
}

return Super::FindComponentByClass(ComponentClass);
}
void ACharacter::OnWalkingOffLedge_Implementation(const FVector& PreviousFloorImpactNormal, const FVector& PreviousFloorContactNormal, const FVector& PreviousLocation, float TimeDelta)
{
}

void ACharacter::NotifyJumpApex()
{
// Call delegate callback
if (OnReachedJumpApex.IsBound())
{
OnReachedJumpApex.Broadcast();
}
}

void ACharacter::Landed(const FHitResult& Hit)


{
OnLanded(Hit);
}

bool ACharacter::CanJump() const


{
return CanJumpInternal();
}

bool ACharacter::CanJumpInternal_Implementation() const


{
const bool bCanHoldToJumpHigher = (GetJumpMaxHoldTime() > 0.0f) && IsJumpProvidingForce();

return !bIsCrouched && CharacterMovement && (CharacterMovement->IsMovingOnGround() || bCanHoldToJumpHigher) && CharacterMovement->IsJumpAllowed() && !CharacterMovement-
>bWantsToCrouch;
}

void ACharacter::OnJumped_Implementation()
{
}

bool ACharacter::IsJumpProvidingForce() const


{
return (bPressedJump && JumpKeyHoldTime > 0.0f && JumpKeyHoldTime < GetJumpMaxHoldTime());
}

// Deprecated
bool ACharacter::DoJump( bool bReplayingMoves )
{
return CanJump() && CharacterMovement->DoJump(bReplayingMoves);
}

void ACharacter::RecalculateBaseEyeHeight()
{
if (!bIsCrouched)
{
Super::RecalculateBaseEyeHeight();
}
else
{
BaseEyeHeight = CrouchedEyeHeight;
}
}

void ACharacter::OnRep_IsCrouched()
{
if (CharacterMovement)
{
if (bIsCrouched)
{
CharacterMovement->Crouch(true);
}
else
{
CharacterMovement->UnCrouch(true);
}
}
}

void ACharacter::SetReplicateMovement(bool bInReplicateMovement)


{
Super::SetReplicateMovement(bInReplicateMovement);

if (CharacterMovement != nullptr)
{
// Set prediction data time stamp to current time to stop extrapolating
// from time bReplicateMovement was turned off to when it was turned on again
FNetworkPredictionData_Server* NetworkPrediction = CharacterMovement->GetPredictionData_Server();

if (NetworkPrediction != nullptr)
{
NetworkPrediction->ServerTimeStamp = GetWorld()->GetTimeSeconds();
}
}
}

bool ACharacter::CanCrouch()
{
return !bIsCrouched && CharacterMovement && CharacterMovement->CanEverCrouch() && GetRootComponent() && !GetRootComponent()->IsSimulatingPhysics();
}

void ACharacter::Crouch(bool bClientSimulation)


{
if (CharacterMovement)
{
if (CanCrouch())
{
CharacterMovement->bWantsToCrouch = true;
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
else if (!CharacterMovement->CanEverCrouch())
{
UE_LOG(LogCharacter, Log, TEXT("%s is trying to crouch, but crouching is disabled on this character! (check CharacterMovement NavAgentSettings)"), *GetName());
}
#endif
}
}

void ACharacter::UnCrouch(bool bClientSimulation)


{
if (CharacterMovement)
{
CharacterMovement->bWantsToCrouch = false;
}
}

void ACharacter::OnEndCrouch( float HeightAdjust, float ScaledHeightAdjust )


{
RecalculateBaseEyeHeight();

if (Mesh)
{
Mesh->RelativeLocation.Z = GetDefault<ACharacter>(GetClass())->Mesh->RelativeLocation.Z;
BaseTranslationOffset.Z = Mesh->RelativeLocation.Z;
}
else
{
BaseTranslationOffset.Z = GetDefault<ACharacter>(GetClass())->BaseTranslationOffset.Z;
}

K2_OnEndCrouch(HeightAdjust, ScaledHeightAdjust);
}

void ACharacter::OnStartCrouch( float HeightAdjust, float ScaledHeightAdjust )


{
RecalculateBaseEyeHeight();

if (Mesh)
{
Mesh->RelativeLocation.Z = GetDefault<ACharacter>(GetClass())->Mesh->RelativeLocation.Z + HeightAdjust;
BaseTranslationOffset.Z = Mesh->RelativeLocation.Z;
}
else
{
BaseTranslationOffset.Z = GetDefault<ACharacter>(GetClass())->BaseTranslationOffset.Z + HeightAdjust;
}

K2_OnStartCrouch(HeightAdjust, ScaledHeightAdjust);
}

void ACharacter::ApplyDamageMomentum(float DamageTaken, FDamageEvent const& DamageEvent, APawn* PawnInstigator, AActor* DamageCauser)
{
UDamageType const* const DmgTypeCDO = DamageEvent.DamageTypeClass->GetDefaultObject<UDamageType>();
float const ImpulseScale = DmgTypeCDO->DamageImpulse;

if ( (ImpulseScale > 3.f) && (CharacterMovement != NULL) )


{
FHitResult HitInfo;
FVector ImpulseDir;
DamageEvent.GetBestHitInfo(this, PawnInstigator, HitInfo, ImpulseDir);

FVector Impulse = ImpulseDir * ImpulseScale;


bool const bMassIndependentImpulse = !DmgTypeCDO->bScaleMomentumByMass;

// limit Z momentum added if already going up faster than jump (to avoid blowing character way up into the sky)
{
FVector MassScaledImpulse = Impulse;
if(!bMassIndependentImpulse && CharacterMovement->Mass > SMALL_NUMBER)
{
MassScaledImpulse = MassScaledImpulse / CharacterMovement->Mass;
}

if ( (CharacterMovement->Velocity.Z > GetDefault<UCharacterMovementComponent>(CharacterMovement->GetClass())->JumpZVelocity) && (MassScaledImpulse.Z > 0.f) )


{
Impulse.Z *= 0.5f;
}
}

CharacterMovement->AddImpulse(Impulse, bMassIndependentImpulse);
}
}

void ACharacter::ClearCrossLevelReferences()
{
if( BasedMovement.MovementBase != NULL && GetOutermost() != BasedMovement.MovementBase->GetOutermost() )
{
SetBase( NULL );
}
Super::ClearCrossLevelReferences();
}

namespace MovementBaseUtility
{
bool IsDynamicBase(const UPrimitiveComponent* MovementBase)
{
return (MovementBase && MovementBase->Mobility == EComponentMobility::Movable);
}

void AddTickDependency(FTickFunction& BasedObjectTick, UPrimitiveComponent* NewBase)


{
if (NewBase && MovementBaseUtility::UseRelativeLocation(NewBase))
{
if (NewBase->PrimaryComponentTick.bCanEverTick)
{
BasedObjectTick.AddPrerequisite(NewBase, NewBase->PrimaryComponentTick);
}

AActor* NewBaseOwner = NewBase->GetOwner();


if (NewBaseOwner)
{
if (NewBaseOwner->PrimaryActorTick.bCanEverTick)
{
BasedObjectTick.AddPrerequisite(NewBaseOwner, NewBaseOwner->PrimaryActorTick);
}

// @TODO: We need to find a more efficient way of finding all ticking components in an actor.
for (UActorComponent* Component : NewBaseOwner->GetComponents())
{
if (Component && Component->PrimaryComponentTick.bCanEverTick)
{
BasedObjectTick.AddPrerequisite(Component, Component->PrimaryComponentTick);
}
}
}
}
}

void RemoveTickDependency(FTickFunction& BasedObjectTick, UPrimitiveComponent* OldBase)


{
if (OldBase && MovementBaseUtility::UseRelativeLocation(OldBase))
{
BasedObjectTick.RemovePrerequisite(OldBase, OldBase->PrimaryComponentTick);
AActor* OldBaseOwner = OldBase->GetOwner();
if (OldBaseOwner)
{
BasedObjectTick.RemovePrerequisite(OldBaseOwner, OldBaseOwner->PrimaryActorTick);

// @TODO: We need to find a more efficient way of finding all ticking components in an actor.
for (UActorComponent* Component : OldBaseOwner->GetComponents())
{
if (Component && Component->PrimaryComponentTick.bCanEverTick)
{
BasedObjectTick.RemovePrerequisite(Component, Component->PrimaryComponentTick);
}
}
}
}
}

FVector GetMovementBaseVelocity(const UPrimitiveComponent* MovementBase, const FName BoneName)


{
FVector BaseVelocity = FVector::ZeroVector;
if (MovementBaseUtility::IsDynamicBase(MovementBase))
{
if (BoneName != NAME_None)
{
const FBodyInstance* BodyInstance = MovementBase->GetBodyInstance(BoneName);
if (BodyInstance)
{
BaseVelocity = BodyInstance->GetUnrealWorldVelocity();
return BaseVelocity;
}
}

BaseVelocity = MovementBase->GetComponentVelocity();
if (BaseVelocity.IsZero())
{
// Fall back to actor's Root component
const AActor* Owner = MovementBase->GetOwner();
if (Owner)
{
// Component might be moved manually (not by simulated physics or a movement component), see if the root component of the actor has a velocity.
BaseVelocity = MovementBase->GetOwner()->GetVelocity();
}
}

// Fall back to physics velocity.


if (BaseVelocity.IsZero() && MovementBase->GetBodyInstance())
{
BaseVelocity = MovementBase->GetBodyInstance()->GetUnrealWorldVelocity();
}
}

return BaseVelocity;
}

FVector GetMovementBaseTangentialVelocity(const UPrimitiveComponent* MovementBase, const FName BoneName, const FVector& WorldLocation)
{
if (MovementBaseUtility::IsDynamicBase(MovementBase))
{
if (const FBodyInstance* BodyInstance = MovementBase->GetBodyInstance(BoneName))
{
const FVector BaseAngVel = BodyInstance->GetUnrealWorldAngularVelocity();
if (!BaseAngVel.IsNearlyZero())
{
FVector BaseLocation;
FQuat BaseRotation;
if (MovementBaseUtility::GetMovementBaseTransform(MovementBase, BoneName, BaseLocation, BaseRotation))
{
const FVector RadialDistanceToBase = WorldLocation - BaseLocation;
const FVector TangentialVel = FVector::DegreesToRadians(BaseAngVel) ^ RadialDistanceToBase;
return TangentialVel;
}
}
}
}

return FVector::ZeroVector;
}

bool GetMovementBaseTransform(const UPrimitiveComponent* MovementBase, const FName BoneName, FVector& OutLocation, FQuat& OutQuat)
{
if (MovementBase)
{
if (BoneName != NAME_None)
{
const USkeletalMeshComponent* SkeletalBase = Cast<USkeletalMeshComponent>(MovementBase);
if (SkeletalBase)
{
const int32 BoneIndex = SkeletalBase->GetBoneIndex(BoneName);
if (BoneIndex != INDEX_NONE)
{
const FTransform BoneTransform = SkeletalBase->GetBoneTransform(BoneIndex);
OutLocation = BoneTransform.GetLocation();
OutQuat = BoneTransform.GetRotation();
return true;
}

UE_LOG(LogCharacter, Warning, TEXT("GetMovementBaseTransform(): Invalid bone '%s' for SkeletalMeshComponent base %s"), *BoneName.ToString(),
*GetPathNameSafe(MovementBase));
return false;
}
// TODO: warn if not a skeletal mesh but providing bone index.
}

// No bone supplied
OutLocation = MovementBase->GetComponentLocation();
OutQuat = MovementBase->GetComponentQuat();
return true;
}

// NULL MovementBase
OutLocation = FVector::ZeroVector;
OutQuat = FQuat::Identity;
return false;
}
}

/** Change the Pawn's base. */


void ACharacter::SetBase( UPrimitiveComponent* NewBaseComponent, const FName InBoneName, bool bNotifyPawn )
{
// If NewBaseComponent is null, ignore bone name.
const FName BoneName = (NewBaseComponent ? InBoneName : NAME_None);

// See what changed.


const bool bBaseChanged = (NewBaseComponent != BasedMovement.MovementBase);
const bool bBoneChanged = (BoneName != BasedMovement.BoneName);

if (bBaseChanged || bBoneChanged)
{
// Verify no recursion.
APawn* Loop = (NewBaseComponent ? Cast<APawn>(NewBaseComponent->GetOwner()) : NULL);
for( ; Loop!=NULL; Loop=Cast<APawn>(Loop->GetMovementBase()) )
{
if( Loop == this )
{
UE_LOG(LogCharacter, Warning, TEXT(" SetBase failed! Recursion detected. Pawn %s already based on %s."), *GetName(), *NewBaseComponent->GetName());
return;
}
}

// Set base.
UPrimitiveComponent* OldBase = BasedMovement.MovementBase;
BasedMovement.MovementBase = NewBaseComponent;
BasedMovement.BoneName = BoneName;

if (CharacterMovement)
{
if (bBaseChanged)
{
MovementBaseUtility::RemoveTickDependency(CharacterMovement->PrimaryComponentTick, OldBase);
MovementBaseUtility::AddTickDependency(CharacterMovement->PrimaryComponentTick, NewBaseComponent);
}

if (NewBaseComponent)
{
// Update OldBaseLocation/Rotation as those were referring to a different base
// ... but not when handling replication for proxies (since they are going to copy this data from the replicated values anyway)
if (!bInBaseReplication)
{
CharacterMovement->SaveBaseLocation();
}

// Enable pre-cloth tick if we are standing on a physics object, as we need to to use post-physics transforms
CharacterMovement->PreClothComponentTick.SetTickFunctionEnable(NewBaseComponent->IsSimulatingPhysics());
}
else
{
BasedMovement.BoneName = NAME_None; // None, regardless of whether user tried to set a bone name, since we have no base component.
BasedMovement.bRelativeRotation = false;
CharacterMovement->CurrentFloor.Clear();
CharacterMovement->PreClothComponentTick.SetTickFunctionEnable(false);
}

if (Role == ROLE_Authority || Role == ROLE_AutonomousProxy)


{
BasedMovement.bServerHasBaseComponent = (BasedMovement.MovementBase != NULL); // Also set on proxies for nicer debugging.
UE_LOG(LogCharacter, Verbose, TEXT("Setting base on %s for '%s' to '%s'"), Role == ROLE_Authority ? TEXT("Server") : TEXT("AutoProxy"), *GetName(),
*GetFullNameSafe(NewBaseComponent));
}
else
{
UE_LOG(LogCharacter, Verbose, TEXT("Setting base on Client for '%s' to '%s'"), *GetName(), *GetFullNameSafe(NewBaseComponent));
}

// Notify this actor of his new floor.


if ( bNotifyPawn )
{
BaseChange();
}
}
}

void ACharacter::SaveRelativeBasedMovement(const FVector& NewRelativeLocation, const FRotator& NewRotation, bool bRelativeRotation)


{
checkSlow(BasedMovement.HasRelativeLocation());
BasedMovement.Location = NewRelativeLocation;
BasedMovement.Rotation = NewRotation;
BasedMovement.bRelativeRotation = bRelativeRotation;
}

FVector ACharacter::GetNavAgentLocation() const


{
FVector AgentLocation = FNavigationSystem::InvalidLocation;

if (GetCharacterMovement() != nullptr)
{
AgentLocation = GetCharacterMovement()->GetActorFeetLocation();
}

if (FNavigationSystem::IsValidLocation(AgentLocation) == false && CapsuleComponent != nullptr)


{
AgentLocation = GetActorLocation() - FVector(0, 0, CapsuleComponent->GetScaledCapsuleHalfHeight());
}

return AgentLocation;
}

void ACharacter::TurnOff()
{
if (CharacterMovement != NULL)
{
CharacterMovement->StopMovementImmediately();
CharacterMovement->DisableMovement();
}

if (GetNetMode() != NM_DedicatedServer && Mesh != NULL)


{
Mesh->bPauseAnims = true;
if (Mesh->IsSimulatingPhysics())
{
Mesh->bBlendPhysics = true;
Mesh->KinematicBonesUpdateType = EKinematicBonesUpdateToPhysics::SkipAllBones;
}
}

Super::TurnOff();
}

void ACharacter::Restart()
{
Super::Restart();

bPressedJump = false;
JumpKeyHoldTime = 0.0f;
ClearJumpInput();
UnCrouch(true);

if (CharacterMovement)
{
CharacterMovement->SetDefaultMovementMode();
}
}

void ACharacter::PawnClientRestart()
{
if (CharacterMovement != NULL)
{
CharacterMovement->StopMovementImmediately();
CharacterMovement->ResetPredictionData_Client();
}

Super::PawnClientRestart();
}

void ACharacter::PossessedBy(AController* NewController)


{
Super::PossessedBy(NewController);

// If we are controlled remotely, set animation timing to be driven by client's network updates. So timing and events remain in sync.
if (Mesh && (GetRemoteRole() == ROLE_AutonomousProxy && GetNetConnection() != nullptr))
{
Mesh->bAutonomousTickPose = true;
}
}

void ACharacter::UnPossessed()
{
Super::UnPossessed();

if (CharacterMovement)
{
CharacterMovement->ResetPredictionData_Client();
CharacterMovement->ResetPredictionData_Server();
}

// We're no longer controlled remotely, resume regular ticking of animations.


if (Mesh)
{
Mesh->bAutonomousTickPose = false;
}
}

void ACharacter::TornOff()
{
Super::TornOff();

if (CharacterMovement)
{
CharacterMovement->ResetPredictionData_Client();
CharacterMovement->ResetPredictionData_Server();
}

// We're no longer controlled remotely, resume regular ticking of animations.


if (Mesh)
{
Mesh->bAutonomousTickPose = false;
}
}

void ACharacter::BaseChange()
{
if (CharacterMovement && CharacterMovement->MovementMode != MOVE_None)
{
AActor* ActualMovementBase = GetMovementBaseActor(this);
if ((ActualMovementBase != NULL) && !ActualMovementBase->CanBeBaseForCharacter(this))
{
CharacterMovement->JumpOff(ActualMovementBase);
}
}
}

void ACharacter::DisplayDebug(UCanvas* Canvas, const FDebugDisplayInfo& DebugDisplay, float& YL, float& YPos)
{
Super::DisplayDebug(Canvas, DebugDisplay, YL, YPos);

float Indent = 0.f;

UFont* RenderFont = GEngine->GetSmallFont();

static FName NAME_Physics = FName(TEXT("Physics"));


if (DebugDisplay.IsDisplayOn(NAME_Physics) )
{
FIndenter PhysicsIndent(Indent);

FString BaseString;
if ( CharacterMovement == NULL || BasedMovement.MovementBase == NULL )
{
BaseString = "Not Based";
}
else
{
BaseString = BasedMovement.MovementBase->IsWorldGeometry() ? "World Geometry" : BasedMovement.MovementBase->GetName();
BaseString = FString::Printf(TEXT("Based On %s"), *BaseString);
}

YL = Canvas->DrawText(RenderFont, FString::Printf(TEXT("RelativeLoc: %s Rot: %s %s"), *BasedMovement.Location.ToString(), *BasedMovement.Rotation.ToString(), *BaseString), Indent,


YPos);
YPos += YL;

if ( CharacterMovement != NULL )
{
CharacterMovement->DisplayDebug(Canvas, DebugDisplay, YL, YPos);
}
const bool Crouched = CharacterMovement && CharacterMovement->IsCrouching();
FString T = FString::Printf(TEXT("Crouched %i"), Crouched);
YL = Canvas->DrawText(RenderFont, T, Indent, YPos );
YPos += YL;
}
}

void ACharacter::LaunchCharacter(FVector LaunchVelocity, bool bXYOverride, bool bZOverride)


{
UE_LOG(LogCharacter, Verbose, TEXT("ACharacter::LaunchCharacter '%s' (%f,%f,%f)"), *GetName(), LaunchVelocity.X, LaunchVelocity.Y, LaunchVelocity.Z);

if (CharacterMovement)
{
FVector FinalVel = LaunchVelocity;
const FVector Velocity = GetVelocity();

if (!bXYOverride)
{
FinalVel.X += Velocity.X;
FinalVel.Y += Velocity.Y;
}
if (!bZOverride)
{
FinalVel.Z += Velocity.Z;
}

CharacterMovement->Launch(FinalVel);

OnLaunched(LaunchVelocity, bXYOverride, bZOverride);


}
}

void ACharacter::OnMovementModeChanged(EMovementMode PrevMovementMode, uint8 PrevCustomMode)


{
K2_OnMovementModeChanged(PrevMovementMode, CharacterMovement->MovementMode, PrevCustomMode, CharacterMovement->CustomMovementMode);
MovementModeChangedDelegate.Broadcast(this, PrevMovementMode, PrevCustomMode);
}

/** Don't process landed notification if updating client position by replaying moves.
* Allow event to be called if Pawn was initially falling (before starting to replay moves),
* and this is going to cause him to land. . */
bool ACharacter::ShouldNotifyLanded(const FHitResult& Hit)
{
if (bClientUpdating && !bClientWasFalling)
{
return false;
}

// Just in case, only allow Landed() to be called once when replaying moves.
bClientWasFalling = false;
return true;
}

void ACharacter::Jump()
{
bPressedJump = true;
JumpKeyHoldTime = 0.0f;
}

void ACharacter::StopJumping()
{
bPressedJump = false;
JumpKeyHoldTime = 0.0f;
}

void ACharacter::CheckJumpInput(float DeltaTime)


{
const bool bWasJumping = bPressedJump && JumpKeyHoldTime > 0.0f;
if (bPressedJump)
{
// Increment our timer first so calls to IsJumpProvidingForce() will return true
JumpKeyHoldTime += DeltaTime;
const bool bDidJump = CanJump() && CharacterMovement && CharacterMovement->DoJump(bClientUpdating);
if (!bWasJumping && bDidJump)
{
OnJumped();
}
}
}

void ACharacter::ClearJumpInput()
{
// Don't disable bPressedJump right away if it's still held
if (bPressedJump && (JumpKeyHoldTime >= GetJumpMaxHoldTime()))
{
bPressedJump = false;
}
}

float ACharacter::GetJumpMaxHoldTime() const


{
return JumpMaxHoldTime;
}

//
// Static variables for networking.
//
static uint8 SavedMovementMode;

void ACharacter::PreNetReceive()
{
SavedMovementMode = ReplicatedMovementMode;
Super::PreNetReceive();
}

void ACharacter::PostNetReceive()
{
if (Role == ROLE_SimulatedProxy)
{
CharacterMovement->bNetworkUpdateReceived = true;
CharacterMovement->bNetworkMovementModeChanged = (CharacterMovement->bNetworkMovementModeChanged || (SavedMovementMode != ReplicatedMovementMode));
}

Super::PostNetReceive();
}

void ACharacter::OnRep_ReplicatedBasedMovement()
{
if (Role != ROLE_SimulatedProxy)
{
return;
}

// Skip base updates while playing root motion, it is handled inside of OnRep_RootMotion
if (IsPlayingNetworkedRootMotionMontage())
{
return;
}

TGuardValue<bool> bInBaseReplicationGuard(bInBaseReplication, true);

const bool bBaseChanged = (BasedMovement.MovementBase != ReplicatedBasedMovement.MovementBase || BasedMovement.BoneName != ReplicatedBasedMovement.BoneName);


if (bBaseChanged)
{
// Even though we will copy the replicated based movement info, we need to use SetBase() to set up tick dependencies and trigger notifications.
SetBase(ReplicatedBasedMovement.MovementBase, ReplicatedBasedMovement.BoneName);
}

// Make sure to use the values of relative location/rotation etc from the server.
BasedMovement = ReplicatedBasedMovement;

if (ReplicatedBasedMovement.HasRelativeLocation())
{
// Update transform relative to movement base
const FVector OldLocation = GetActorLocation();
const FQuat OldRotation = GetActorQuat();
MovementBaseUtility::GetMovementBaseTransform(ReplicatedBasedMovement.MovementBase, ReplicatedBasedMovement.BoneName, CharacterMovement->OldBaseLocation, CharacterMovement-
>OldBaseQuat);
const FVector NewLocation = CharacterMovement->OldBaseLocation + ReplicatedBasedMovement.Location;

if (ReplicatedBasedMovement.HasRelativeRotation())
{
// Relative location, relative rotation
FRotator NewRotation = (FRotationMatrix(ReplicatedBasedMovement.Rotation) * FQuatRotationMatrix(CharacterMovement->OldBaseQuat)).Rotator();

// TODO: need a better way to not assume we only use Yaw.


NewRotation.Pitch = 0.f;
NewRotation.Roll = 0.f;

SetActorLocationAndRotation(NewLocation, NewRotation);
}
else
{
// Relative location, absolute rotation
SetActorLocationAndRotation(NewLocation, ReplicatedBasedMovement.Rotation);
}

// When position or base changes, movement mode will need to be updated. This assumes rotation changes don't affect that.
CharacterMovement->bJustTeleported |= (bBaseChanged || GetActorLocation() != OldLocation);

INetworkPredictionInterface* PredictionInterface = Cast<INetworkPredictionInterface>(GetMovementComponent());


if (PredictionInterface)
{
PredictionInterface->SmoothCorrection(OldLocation, OldRotation);
}
}
}

void ACharacter::OnRep_ReplicatedMovement()
{
// Skip standard position correction if we are playing root motion, OnRep_RootMotion will handle it.
if (!IsPlayingNetworkedRootMotionMontage())
{
Super::OnRep_ReplicatedMovement();
}
}

/** Get FAnimMontageInstance playing RootMotion */


FAnimMontageInstance * ACharacter::GetRootMotionAnimMontageInstance() const
{
return (Mesh && Mesh->AnimScriptInstance) ? Mesh->AnimScriptInstance->GetRootMotionMontageInstance() : NULL;
}

void ACharacter::OnRep_RootMotion()
{
if( Role == ROLE_SimulatedProxy )
{
UE_LOG(LogRootMotion, Log, TEXT("ACharacter::OnRep_RootMotion"));

// Save received move in queue, we'll try to use it during Tick().


if( RepRootMotion.AnimMontage )
{
// Add new move
FSimulatedRootMotionReplicatedMove NewMove;
NewMove.RootMotion = RepRootMotion;
NewMove.Time = GetWorld()->GetTimeSeconds();
RootMotionRepMoves.Add(NewMove);
}
else
{
// Clear saved moves.
RootMotionRepMoves.Empty();
}
}
}

void ACharacter::SimulatedRootMotionPositionFixup(float DeltaSeconds)


{
const FAnimMontageInstance* ClientMontageInstance = GetRootMotionAnimMontageInstance();
if( ClientMontageInstance && CharacterMovement && Mesh )
{
// Find most recent buffered move that we can use.
const int32 MoveIndex = FindRootMotionRepMove(*ClientMontageInstance);
if( MoveIndex != INDEX_NONE )
{
const FVector OldLocation = GetActorLocation();
const FQuat OldRotation = GetActorQuat();
// Move Actor back to position of that buffered move. (server replicated position).
const FSimulatedRootMotionReplicatedMove& RootMotionRepMove = RootMotionRepMoves[MoveIndex];
if( RestoreReplicatedMove(RootMotionRepMove) )
{
const float ServerPosition = RootMotionRepMove.RootMotion.Position;
const float ClientPosition = ClientMontageInstance->GetPosition();
const float DeltaPosition = (ClientPosition - ServerPosition);
if( FMath::Abs(DeltaPosition) > KINDA_SMALL_NUMBER )
{
// Find Root Motion delta move to get back to where we were on the client.
const FTransform LocalRootMotionTransform = ClientMontageInstance->Montage->ExtractRootMotionFromTrackRange(ServerPosition, ClientPosition);

// Simulate Root Motion for delta move.


if( CharacterMovement )
{
const float MontagePlayRate = ClientMontageInstance->GetPlayRate();
// Guess time it takes for this delta track position, so we can get falling physics accurate.
if (!FMath::IsNearlyZero(MontagePlayRate))
{
const float DeltaTime = DeltaPosition / MontagePlayRate;

// Even with negative playrate deltatime should be positive.


check(DeltaTime > 0.f);
CharacterMovement->SimulateRootMotion(DeltaTime, LocalRootMotionTransform);

// After movement correction, smooth out error in position if any.


INetworkPredictionInterface* PredictionInterface = Cast<INetworkPredictionInterface>(GetMovementComponent());
if (PredictionInterface)
{
PredictionInterface->SmoothCorrection(OldLocation, OldRotation);
}
}
}
}
}

// Delete this move and any prior one, we don't need them anymore.
UE_LOG(LogRootMotion, Log, TEXT("\tClearing old moves (%d)"), MoveIndex+1);
RootMotionRepMoves.RemoveAt(0, MoveIndex+1);
}
}
}

int32 ACharacter::FindRootMotionRepMove(const FAnimMontageInstance& ClientMontageInstance) const


{
int32 FoundIndex = INDEX_NONE;

// Start with most recent move and go back in time to find a usable move.
for(int32 MoveIndex=RootMotionRepMoves.Num()-1; MoveIndex>=0; MoveIndex--)
{
if( CanUseRootMotionRepMove(RootMotionRepMoves[MoveIndex], ClientMontageInstance) )
{
FoundIndex = MoveIndex;
break;
}
}

UE_LOG(LogRootMotion, Log, TEXT("\tACharacter::FindRootMotionRepMove FoundIndex: %d, NumSavedMoves: %d"), FoundIndex, RootMotionRepMoves.Num());


return FoundIndex;
}

bool ACharacter::CanUseRootMotionRepMove(const FSimulatedRootMotionReplicatedMove& RootMotionRepMove, const FAnimMontageInstance& ClientMontageInstance) const


{
// Ignore outdated moves.
if( GetWorld()->TimeSince(RootMotionRepMove.Time) <= 0.5f )
{
// Make sure montage being played matched between client and server.
if( RootMotionRepMove.RootMotion.AnimMontage && (RootMotionRepMove.RootMotion.AnimMontage == ClientMontageInstance.Montage) )
{
UAnimMontage * AnimMontage = ClientMontageInstance.Montage;
const float ServerPosition = RootMotionRepMove.RootMotion.Position;
const float ClientPosition = ClientMontageInstance.GetPosition();
const float DeltaPosition = (ClientPosition - ServerPosition);
const int32 CurrentSectionIndex = AnimMontage->GetSectionIndexFromPosition(ClientPosition);
if( CurrentSectionIndex != INDEX_NONE )
{
const int32 NextSectionIndex = (CurrentSectionIndex < ClientMontageInstance.NextSections.Num()) ? ClientMontageInstance.NextSections[CurrentSectionIndex] : INDEX_NONE;

// We can only extract root motion if we are within the same section.
// It's not trivial to jump through sections in a deterministic manner, but that is luckily not frequent.
const bool bSameSections = (AnimMontage->GetSectionIndexFromPosition(ServerPosition) == CurrentSectionIndex);
// if we are looping and just wrapped over, skip. That's also not easy to handle and not frequent.
const bool bHasLooped = (NextSectionIndex == CurrentSectionIndex) && (FMath::Abs(DeltaPosition) > (AnimMontage->GetSectionLength(CurrentSectionIndex) / 2.f));
// Can only simulate forward in time, so we need to make sure server move is not ahead of the client.
const bool bServerAheadOfClient = ((DeltaPosition * ClientMontageInstance.GetPlayRate()) < 0.f);

UE_LOG(LogRootMotion, Log, TEXT("\t\tACharacter::CanUseRootMotionRepMove ServerPosition: %.3f, ClientPosition: %.3f, DeltaPosition: %.3f, bSameSections: %d, bHasLooped:
%d, bServerAheadOfClient: %d"),
ServerPosition, ClientPosition, DeltaPosition, bSameSections, bHasLooped, bServerAheadOfClient);

return bSameSections && !bHasLooped && !bServerAheadOfClient;


}
}
}
return false;
}

bool ACharacter::RestoreReplicatedMove(const FSimulatedRootMotionReplicatedMove& RootMotionRepMove)


{
UPrimitiveComponent* ServerBase = RootMotionRepMove.RootMotion.MovementBase;
const FName ServerBaseBoneName = RootMotionRepMove.RootMotion.MovementBaseBoneName;

// Relative Position
if( RootMotionRepMove.RootMotion.bRelativePosition )
{
bool bSuccess = false;
if( MovementBaseUtility::UseRelativeLocation(ServerBase) )
{
FVector BaseLocation;
FQuat BaseRotation;
MovementBaseUtility::GetMovementBaseTransform(ServerBase, ServerBaseBoneName, BaseLocation, BaseRotation);

const FVector ServerLocation = BaseLocation + RootMotionRepMove.RootMotion.Location;


FRotator ServerRotation;
if (RootMotionRepMove.RootMotion.bRelativeRotation)
{
// Relative rotation
ServerRotation = (FRotationMatrix(RootMotionRepMove.RootMotion.Rotation) * FQuatRotationTranslationMatrix(BaseRotation, FVector::ZeroVector)).Rotator();
}
else
{
// Absolute rotation
ServerRotation = RootMotionRepMove.RootMotion.Rotation;
}

UpdateSimulatedPosition(ServerLocation, ServerRotation);
bSuccess = true;
}
// If we received local space position, but can't resolve parent, then move can't be used. :(
if( !bSuccess )
{
return false;
}
}
// Absolute position
else
{
UpdateSimulatedPosition(RootMotionRepMove.RootMotion.Location, RootMotionRepMove.RootMotion.Rotation);
}

SetBase( ServerBase, ServerBaseBoneName );

return true;
}

void ACharacter::UpdateSimulatedPosition(const FVector& NewLocation, const FRotator& NewRotation)


{
// Always consider Location as changed if we were spawned this tick as in that case our replicated Location was set as part of spawning, before PreNetReceive()
if( (NewLocation != GetActorLocation()) || (CreationTime == GetWorld()->TimeSeconds) )
{
FVector FinalLocation = NewLocation;

// Only need to check for encroachment when teleported without any velocity.
// Normal movement pops the character out of geometry anyway, no use doing it before and after (with different rules).
bSimGravityDisabled = false;
if (CharacterMovement->Velocity.IsZero())
{
if (GetWorld()->EncroachingBlockingGeometry(this, NewLocation, NewRotation))
{
bSimGravityDisabled = true;
}
}

// Don't use TeleportTo(), that clears our base.


SetActorLocationAndRotation(FinalLocation, NewRotation, false);
CharacterMovement->bJustTeleported = true;
}
else if( NewRotation != GetActorRotation() )
{
GetRootComponent()->MoveComponent(FVector::ZeroVector, NewRotation, false);
}
}

void ACharacter::PostNetReceiveLocationAndRotation()
{
if( Role == ROLE_SimulatedProxy )
{
// Don't change transform if using relative position (it should be nearly the same anyway, or base may be slightly out of sync)
if (!ReplicatedBasedMovement.HasRelativeLocation())
{
const FVector OldLocation = GetActorLocation();
const FQuat OldRotation = GetActorQuat();
UpdateSimulatedPosition(ReplicatedMovement.Location, ReplicatedMovement.Rotation);

INetworkPredictionInterface* PredictionInterface = Cast<INetworkPredictionInterface>(GetMovementComponent());


if (PredictionInterface)
{
PredictionInterface->SmoothCorrection(OldLocation, OldRotation);
}
}
}
}

void ACharacter::PreReplication( IRepChangedPropertyTracker & ChangedPropertyTracker )


{
Super::PreReplication( ChangedPropertyTracker );

const FAnimMontageInstance* RootMotionMontageInstance = GetRootMotionAnimMontageInstance();

if ( RootMotionMontageInstance )
{
// Is position stored in local space?
RepRootMotion.bRelativePosition = BasedMovement.HasRelativeLocation();
RepRootMotion.bRelativeRotation = BasedMovement.HasRelativeRotation();
RepRootMotion.Location = RepRootMotion.bRelativePosition ? BasedMovement.Location : GetActorLocation();
RepRootMotion.Rotation = RepRootMotion.bRelativeRotation ? BasedMovement.Rotation : GetActorRotation();
RepRootMotion.MovementBase = BasedMovement.MovementBase;
RepRootMotion.MovementBaseBoneName = BasedMovement.BoneName;
RepRootMotion.AnimMontage = RootMotionMontageInstance->Montage;
RepRootMotion.Position = RootMotionMontageInstance->GetPosition();

DOREPLIFETIME_ACTIVE_OVERRIDE( ACharacter, RepRootMotion, true );


}
else
{
RepRootMotion.Clear();

DOREPLIFETIME_ACTIVE_OVERRIDE( ACharacter, RepRootMotion, false );


}

ReplicatedMovementMode = CharacterMovement->PackNetworkMovementMode();
ReplicatedBasedMovement = BasedMovement;

// Optimization: only update and replicate these values if they are actually going to be used.
if (BasedMovement.HasRelativeLocation())
{
// When velocity becomes zero, force replication so the position is updated to match the server (it may have moved due to simulation on the client).
ReplicatedBasedMovement.bServerHasVelocity = !CharacterMovement->Velocity.IsZero();

// Make sure absolute rotations are updated in case rotation occurred after the base info was saved.
if (!BasedMovement.HasRelativeRotation())
{
ReplicatedBasedMovement.Rotation = GetActorRotation();
}
}
}

void ACharacter::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const


{
Super::GetLifetimeReplicatedProps( OutLifetimeProps );

DOREPLIFETIME_CONDITION( ACharacter, RepRootMotion, COND_SimulatedOnly );


DOREPLIFETIME_CONDITION( ACharacter, ReplicatedBasedMovement, COND_SimulatedOnly );
DOREPLIFETIME_CONDITION( ACharacter, ReplicatedMovementMode, COND_SimulatedOnly );
DOREPLIFETIME_CONDITION( ACharacter, bIsCrouched, COND_SimulatedOnly );
}

bool ACharacter::IsPlayingRootMotion() const


{
if (Mesh && Mesh->AnimScriptInstance)
{
return (Mesh->AnimScriptInstance->RootMotionMode == ERootMotionMode::RootMotionFromEverything) ||
(Mesh->AnimScriptInstance->GetRootMotionMontageInstance() != NULL);
}
return false;
}

bool ACharacter::IsPlayingNetworkedRootMotionMontage() const


{
if (Mesh && Mesh->AnimScriptInstance)
{
return (Mesh->AnimScriptInstance->RootMotionMode == ERootMotionMode::RootMotionFromMontagesOnly) &&
(Mesh->AnimScriptInstance->GetRootMotionMontageInstance() != NULL);
}
return false;
}

float ACharacter::PlayAnimMontage(class UAnimMontage* AnimMontage, float InPlayRate, FName StartSectionName)


{
UAnimInstance * AnimInstance = (Mesh)? Mesh->GetAnimInstance() : NULL;
if( AnimMontage && AnimInstance )
{
float const Duration = AnimInstance->Montage_Play(AnimMontage, InPlayRate);

if (Duration > 0.f)


{
// Start at a given Section.
if( StartSectionName != NAME_None )
{
AnimInstance->Montage_JumpToSection(StartSectionName, AnimMontage);
}

return Duration;
}
}

return 0.f;
}

void ACharacter::StopAnimMontage(class UAnimMontage* AnimMontage)


{
UAnimInstance * AnimInstance = (Mesh)? Mesh->GetAnimInstance() : NULL;
UAnimMontage * MontageToStop = (AnimMontage)? AnimMontage : GetCurrentMontage();
bool bShouldStopMontage = AnimInstance && MontageToStop && !AnimInstance->Montage_GetIsStopped(MontageToStop);

if ( bShouldStopMontage )
{
AnimInstance->Montage_Stop(MontageToStop->BlendOutTime, MontageToStop);
}
}

class UAnimMontage * ACharacter::GetCurrentMontage()


{
UAnimInstance * AnimInstance = (Mesh)? Mesh->GetAnimInstance() : NULL;
if ( AnimInstance )
{
return AnimInstance->GetCurrentActiveMontage();
}

return NULL;
}

void ACharacter::ClientCheatWalk_Implementation()
{
SetActorEnableCollision(true);
if (CharacterMovement)
{
CharacterMovement->bCheatFlying = false;
CharacterMovement->SetMovementMode(MOVE_Falling);
}
}

void ACharacter::ClientCheatFly_Implementation()
{
SetActorEnableCollision(true);
if (CharacterMovement)
{
CharacterMovement->bCheatFlying = true;
CharacterMovement->SetMovementMode(MOVE_Flying);
}
}

void ACharacter::ClientCheatGhost_Implementation()
{
SetActorEnableCollision(false);
if (CharacterMovement)
{
CharacterMovement->bCheatFlying = true;
CharacterMovement->SetMovementMode(MOVE_Flying);
}
}
/** Returns Mesh subobject **/
USkeletalMeshComponent* ACharacter::GetMesh() const {
return Mesh;
}
#if WITH_EDITORONLY_DATA
/** Returns ArrowComponent subobject **/
UArrowComponent* ACharacter::GetArrowComponent() const {
return ArrowComponent;
}
#endif
/** Returns CharacterMovement subobject **/
UCharacterMovementComponent* ACharacter::GetCharacterMovement() const {
return CharacterMovement;
}
/** Returns CapsuleComponent subobject **/
UCapsuleComponent* ACharacter::GetCapsuleComponent() const {
return CapsuleComponent;
}

EXAMPLE #11

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#include "BET.h"
#include "BETCharacter.h"
#include "BETProjectile.h"
#include "Animation/AnimInstance.h"
#include "GameFramework/InputSettings.h"
#include "BETProjectileWeapon.h"
#include "BETWeapon.h"

DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);

//////////////////////////////////////////////////////////////////////////
// ABETCharacter

ABETCharacter::ABETCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

// set our turn rates for input


BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;

// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
FirstPersonCameraComponent->RelativeLocation = FVector(0, 0, 64.f); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;

// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true);
Mesh1P->AttachParent = FirstPersonCameraComponent;
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;
// Create a gun mesh component
FP_Gun = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FP_Gun"));
FP_Gun->SetOnlyOwnerSee(true); // only the owning player will see this mesh
FP_Gun->bCastDynamicShadow = false;
FP_Gun->CastShadow = false;
FP_Gun->AttachTo(Mesh1P, TEXT("GripPoint"), EAttachLocation::SnapToTargetIncludingScale, true);

// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}

void ABETCharacter::BeginPlay()
{
Super::BeginPlay();
if (WeaponClass)
{
FActorSpawnParameters SpawnParameters;
SpawnParameters.Instigator = this;
Weapon = GetWorld()->SpawnActor<ABETWeapon>(WeaponClass, SpawnParameters);
Weapon->AttachRootComponentToActor(this);
}
}
//////////////////////////////////////////////////////////////////////////
// Input

void ABETCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)


{
// set up gameplay key bindings
check(InputComponent);

//InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &ABETCharacter::TouchStarted);


if( EnableTouchscreenMovement(InputComponent) == false )
{
InputComponent->BindAction("Fire", IE_Pressed, this, &ABETCharacter::OnFire);
}

InputComponent->BindAxis("MoveForward", this, &ABETCharacter::MoveForward);


InputComponent->BindAxis("MoveRight", this, &ABETCharacter::MoveRight);

// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
InputComponent->BindAxis("TurnRate", this, &ABETCharacter::TurnAtRate);
InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
InputComponent->BindAxis("LookUpRate", this, &ABETCharacter::LookUpAtRate);
}

void ABETCharacter::OnFire()
{
if (Weapon){
Weapon->Fire();
}

// try and play the sound if specified


if (Weapon->FireSound != NULL)
{
UGameplayStatics::PlaySoundAtLocation(this, Weapon->FireSound, GetActorLocation());
}

// try and play a firing animation if specified


if (Weapon->FireAnimation != NULL)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if (AnimInstance != NULL)
{
AnimInstance->Montage_Play(Weapon->FireAnimation, 1.f);
}
}
}

void ABETCharacter::BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if( TouchItem.bIsPressed == true )
{
return;
}
TouchItem.bIsPressed = true;
TouchItem.FingerIndex = FingerIndex;
TouchItem.Location = Location;
TouchItem.bMoved = false;
}

void ABETCharacter::EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if (TouchItem.bIsPressed == false)
{
return;
}
if( ( FingerIndex == TouchItem.FingerIndex ) && (TouchItem.bMoved == false) )
{
OnFire();
}
TouchItem.bIsPressed = false;
}

void ABETCharacter::TouchUpdate(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if ((TouchItem.bIsPressed == true) && ( TouchItem.FingerIndex==FingerIndex))
{
if (TouchItem.bIsPressed)
{
if (GetWorld() != nullptr)
{
UGameViewportClient* ViewportClient = GetWorld()->GetGameViewport();
if (ViewportClient != nullptr)
{
FVector MoveDelta = Location - TouchItem.Location;
FVector2D ScreenSize;
ViewportClient->GetViewportSize(ScreenSize);
FVector2D ScaledDelta = FVector2D( MoveDelta.X, MoveDelta.Y) / ScreenSize;

if (ScaledDelta.X != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.X * BaseTurnRate;
AddControllerYawInput(Value);
}
if (ScaledDelta.Y != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.Y* BaseTurnRate;
AddControllerPitchInput(Value);
}
TouchItem.Location = Location;
}
TouchItem.Location = Location;
}
}
}
}

void ABETCharacter::MoveForward(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorForwardVector(), Value);
}
}

void ABETCharacter::MoveRight(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
}
}

void ABETCharacter::TurnAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void ABETCharacter::LookUpAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

bool ABETCharacter::EnableTouchscreenMovement(class UInputComponent* InputComponent)


{
bool bResult = false;
if(FPlatformMisc::GetUseVirtualJoysticks() || GetDefault<UInputSettings>()->bUseMouseForTouch )
{
bResult = true;
InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &ABETCharacter::BeginTouch);
InputComponent->BindTouch(EInputEvent::IE_Released, this, &ABETCharacter::EndTouch);
InputComponent->BindTouch(EInputEvent::IE_Repeat, this, &ABETCharacter::TouchUpdate);
}
return bResult;
}
EXAMPLE #12

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#include "Wizards.h"
#include "WizardsCharacter.h"
#include "WizardsProjectile.h"
#include "WizardsBlast.h"
#include "WizardsCone.h"
#include "WizardsSaveGame.h"
#include "UnrealNetwork.h"
#include "Animation/AnimInstance.h"
#include "GameFramework/InputSettings.h"

//DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);

//////////////////////////////////////////////////////////////////////////
// AWizardsCharacter

AWizardsCharacter::AWizardsCharacter()
{
bReplicates = true;
//Tick for mana regen
PrimaryActorTick.bCanEverTick = true;
//Set Health and Mana
Health = 100.0;
maxHealth = 100.0;
Mana = 100.0;
maxMana = 100.0;
//Spell Stuff for Testing
//SList.spellCost = 10.0;

ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName(TEXT("ParticleSystem'/Game/StarterContent/Particles/P_Sparks.P_Sparks'"));
//SList.myParticle = ArbitraryParticleName.Object;
//SList.test = &ArbitraryParticleName;
currSpell = 0;
//For the record, this probably isn't the best way to get particles for the spells but it works
//A better method, implemented at a later and unknown date, would be to hold this array in its own class
//that is called once and never destroyed
//Projectiles
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName0(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Fire_Projectile.P_Fire_Projectile'"));
particleList.Add(ArbitraryParticleName0.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName1(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Lightning_Projectile.P_Lightning_Projectile'"));
particleList.Add(ArbitraryParticleName1.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName2(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Water_Projectile.P_Water_Projectile'"));
particleList.Add(ArbitraryParticleName2.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName3(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Ice_Projectile.P_Ice_Projectile'"));
particleList.Add(ArbitraryParticleName3.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName4(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Earth_Projectile.P_Earth_Projectile'"));
particleList.Add(ArbitraryParticleName4.Object);
//Now for all of the EXPLOSIONS
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName5(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Fire_Blast.P_Fire_Blast'"));
particleList.Add(ArbitraryParticleName5.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName6(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Lightning_Blast.P_Lightning_Blast'"));
particleList.Add(ArbitraryParticleName6.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName7(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Water_Blast.P_Water_Blast'"));
particleList.Add(ArbitraryParticleName7.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName8(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Ice_Blast.P_Ice_Blast'"));
particleList.Add(ArbitraryParticleName8.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName9(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Earth_Blast.P_Earth_Blast'"));
particleList.Add(ArbitraryParticleName9.Object);
//Next up is cones
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName10(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Fire_Cone.P_Fire_Cone'"));
particleList.Add(ArbitraryParticleName10.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName11(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Lightning_Cone.P_Lightning_Cone'"));
particleList.Add(ArbitraryParticleName11.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName12(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Water_Cone.P_Water_Cone'"));
particleList.Add(ArbitraryParticleName12.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName13(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Ice_Cone.P_Ice_Cone'"));
particleList.Add(ArbitraryParticleName13.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName14(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Earth_Cone.P_Earth_Cone'"));
particleList.Add(ArbitraryParticleName14.Object);

//spell test;
//SList.Add(test);
//SList[currSpell].spellCost = 10.0;
//SList[currSpell].theWizard = this;
//SList[currSpell].canBounce = true;
//ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName8(TEXT("ParticleSystem'/Game/StarterContent/Particles/P_Sparks.P_Sparks'"));
//SList[currSpell].myParticle = ArbitraryParticleName8.Object;
//SList.test = &ArbitraryParticleName;
//SList.particleLocation = FName(TEXT("ParticleSystem'/Game/StarterContent/Particles/P_Sparks.P_Sparks'"));

// Set size for collision capsule


GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

// set our turn rates for input


BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;

// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
FirstPersonCameraComponent->RelativeLocation = FVector(0, 0, 64.f); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;

// Default offset from the character location for projectiles to spawn


GunOffset = FVector(100.0f, 30.0f, 10.0f);

// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true); // only the owning player will see this mesh
Mesh1P->AttachParent = FirstPersonCameraComponent;
Mesh1P->RelativeLocation = FVector(0.f, 0.f, -150.f);
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;

// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}

void AWizardsCharacter::newCharactersSpells()
{
UWizardsSaveGame* LoadGameInstance = NewObject<UWizardsSaveGame>();
// spellList = LoadGameInstance->spellList;
//if is empty
if (LoadGameInstance->LoadGameDataFromFile()) {

for (int i = 0; i < 5; i++) {


FtheSpell thisSpell;
mySpellBook.Add(thisSpell);
mySpellBook[i].spellEffect = LoadGameInstance->spellBook[i]->spellEffect;
mySpellBook[i].spellType = LoadGameInstance->spellBook[i]->spellType;
mySpellBook[i].spellCost = LoadGameInstance->spellBook[i]->spellCost;
mySpellBook[i].spellSpeed = LoadGameInstance->spellBook[i]->spellSpeed*9000.0 + 1000.0;//range from 1000 to 10000
mySpellBook[i].spellDamage = LoadGameInstance->spellBook[i]->spellDamage;//not in at the moment
mySpellBook[i].spellRange = LoadGameInstance->spellBook[i]->spellRange*4.0 + 1.0; //1 to 5 seconds
mySpellBook[i].spellSize = LoadGameInstance->spellBook[i]->spellSize*4.0 + 1.0;//range 1 to 5
mySpellBook[i].canBounce = LoadGameInstance->spellBook[i]->canBounce;//boolean, in
mySpellBook[i].hasGravity = LoadGameInstance->spellBook[i]->hasGravity;//totally in
mySpellBook[i].isHoming = LoadGameInstance->spellBook[i]->isHoming; //not currenttly implemented
mySpellBook[i].explodeOnCollision = LoadGameInstance->spellBook[i]->explodeOnCollision; //none of this shit down here is implemented
mySpellBook[i].explodeOnDeath = LoadGameInstance->spellBook[i]->explodeOnDeath;
mySpellBook[i].explosionHitDamage = LoadGameInstance->spellBook[i]->explosionHitDamage;
mySpellBook[i].explosionHitSize = LoadGameInstance->spellBook[i]->explosionHitSize*3.0 + 2.0;
mySpellBook[i].explosionDeathDamage = LoadGameInstance->spellBook[i]->explosionDeathDamage;
mySpellBook[i].explosionDeathSize = LoadGameInstance->spellBook[i]->explosionDeathSize*3.0 + 2.0;
//mySpellBook[i]->myParticle = particleList[mySpellBook[i]->spellEffect + mySpellBook[i]->spellType * 5];
//mySpellBook[i]->explParticle = particleList[mySpellBook[i]->spellEffect + 5];
UE_LOG(LogTemp, Warning, TEXT("Spell Gathering Succesful!"));
}
}
else {
UE_LOG(LogTemp, Warning, TEXT("Abort Mission!"));
}
}

/////////////
// On Tick
void AWizardsCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (Mana <= maxMana) {
Mana += DeltaTime;
}
}

//////////////////////////////////////////////////////////////////////////
// Input

void AWizardsCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)


{
// set up gameplay key bindings
check(InputComponent);

InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);


InputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
InputComponent->BindAction("ChangeSpell1", IE_Pressed, this, &AWizardsCharacter::spellSwitch<0>);
InputComponent->BindAction("ChangeSpell2", IE_Pressed, this, &AWizardsCharacter::spellSwitch<1>);
InputComponent->BindAction("ChangeSpell3", IE_Pressed, this, &AWizardsCharacter::spellSwitch<2>);
InputComponent->BindAction("ChangeSpell4", IE_Pressed, this, &AWizardsCharacter::spellSwitch<3>);
InputComponent->BindAction("ChangeSpell5", IE_Pressed, this, &AWizardsCharacter::spellSwitch<4>);

//InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AWizardsCharacter::TouchStarted);


if (EnableTouchscreenMovement(InputComponent) == false)
{
InputComponent->BindAction("Fire", IE_Pressed, this, &AWizardsCharacter::OnFire);
InputComponent->BindAction("Fire", IE_Released, this, &AWizardsCharacter::OffFire);

InputComponent->BindAxis("MoveForward", this, &AWizardsCharacter::MoveForward);


InputComponent->BindAxis("MoveRight", this, &AWizardsCharacter::MoveRight);
// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
InputComponent->BindAxis("TurnRate", this, &AWizardsCharacter::TurnAtRate);
InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
InputComponent->BindAxis("LookUpRate", this, &AWizardsCharacter::LookUpAtRate);
}

void AWizardsCharacter::OnFire()
{
if (!mySpellBook.IsValidIndex(0)) {
UE_LOG(LogTemp, Warning, TEXT("Spell Gathering Needed!"));
newCharactersSpells();
}
if (Mana > mySpellBook[currSpell].spellCost) {
Mana -= mySpellBook[currSpell].spellCost;
// try and fire a projectile

if (mySpellBook[currSpell].spellType == 0)
{
const FRotator SpawnRotation = GetControlRotation();
const FVector SpawnLocation = GetActorLocation() + SpawnRotation.RotateVector(GunOffset);

UWorld* const World = GetWorld();


if (World)
{
// spawn the projectile at the muzzle
/*UParticleSystem* projParticle = particleList[mySpellBook[currSpell].spellEffect + mySpellBook[currSpell].spellType * 5];
UParticleSystem* blastParticle = particleList[mySpellBook[currSpell].spellEffect + 5];
AWizardsProjectile* wizardsSpell = World->SpawnActor<AWizardsProjectile>(ProjectileClass, SpawnLocation, SpawnRotation);// , myparams);
wizardsSpell->SpellCreation(&mySpellBook[currSpell], projParticle, blastParticle, this);*/
if (Role < ROLE_Authority)
{
ServerFireProjectile(mySpellBook[currSpell], SpawnRotation, SpawnLocation);//mySpellBook[currSpell]);
}
else {
ClientFireProjectile(mySpellBook[currSpell], SpawnRotation, SpawnLocation);
}
}
}
else if (mySpellBook[currSpell].spellType == 1) {
const FRotator SpawnRotation = FRotator(0.0);//GetControlRotation();
// MuzzleOffset is
in camera space, so transform it to world space before offsetting from the character location to find the final muzzle position
const FVector SpawnLocation = FVector(0.0);//GetActorLocation() + SpawnRotation.RotateVector(GunOffset);

UWorld* const World = GetWorld();


if (World)
{
// spawn the projectile at the muzzle
/*UParticleSystem* blastParticle = particleList[mySpellBook[currSpell].spellEffect + mySpellBook[currSpell].spellType * 5];
AWizardsBlast* wizardsSpell = World->SpawnActor<AWizardsBlast>(BlastClass, SpawnLocation, SpawnRotation);// , myparams);
wizardsSpell->SpellCreation(blastParticle, mySpellBook[currSpell].spellSize, mySpellBook[currSpell].spellDamage, this);
wizardsSpell->AttachRootComponentTo(GetCapsuleComponent());//Probably useful for Blasts, Rays, and Conical attacks*/
if (Role < ROLE_Authority)
{
ServerFireProjectile(mySpellBook[currSpell], SpawnRotation, SpawnLocation);
}
else {
ClientFireProjectile(mySpellBook[currSpell], SpawnRotation, SpawnLocation);
}
}
}
else if (mySpellBook[currSpell].spellType == 2) {
const FRotator SpawnRotation = FRotator(0.0);//GetControlRotation();
// MuzzleOffset is
in camera space, so transform it to world space before offsetting from the character location to find the final muzzle position
const FVector SpawnLocation = FVector(0.0);//GetActorLocation() + SpawnRotation.RotateVector(GunOffset);

UWorld* const World = GetWorld();


if (World)
{
// spawn the projectile at the muzzle
/*UParticleSystem* coneParticle = particleList[mySpellBook[currSpell].spellEffect + mySpellBook[currSpell].spellType * 5];
AWizardsCone* wizardsCone = World->SpawnActor<AWizardsCone>(ConeClass, SpawnLocation, SpawnRotation);// , myparams);
wizardsCone->SpellCreation(coneParticle, mySpellBook[currSpell].spellSize, mySpellBook[currSpell].spellDamage, this);
wizardsCone->AttachRootComponentTo(GetCapsuleComponent());//Probably useful for Blasts, Rays, and Conical attacks
activeAttack = Cast<AActor>(wizardsCone);*/
if (Role < ROLE_Authority)
{
ServerFireProjectile(mySpellBook[currSpell], SpawnRotation, SpawnLocation);
}
else {
ClientFireProjectile(mySpellBook[currSpell], SpawnRotation, SpawnLocation);
}
}
}
// God this sound is so annoying
/*if (FireSound != NULL)
{
UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
}*/

// try and play a firing animation if specified


if (FireAnimation != NULL)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if (AnimInstance != NULL)
{
AnimInstance->Montage_Play(FireAnimation, 1.f);
}
}

}
}

void AWizardsCharacter::OffFire() {
if (activeAttack != NULL) {
activeAttack->Destroy();
ServerDestroyCone(activeAttack);
}
}

void AWizardsCharacter::BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if (TouchItem.bIsPressed == true)
{
return;
}
TouchItem.bIsPressed = true;
TouchItem.FingerIndex = FingerIndex;
TouchItem.Location = Location;
TouchItem.bMoved = false;
}

void AWizardsCharacter::EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if (TouchItem.bIsPressed == false)
{
return;
}
if ((FingerIndex == TouchItem.FingerIndex) && (TouchItem.bMoved == false))
{
OnFire();
}
TouchItem.bIsPressed = false;
}

void AWizardsCharacter::TouchUpdate(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if ((TouchItem.bIsPressed == true) && (TouchItem.FingerIndex == FingerIndex))
{
if (TouchItem.bIsPressed)
{
if (GetWorld() != nullptr)
{
UGameViewportClient* ViewportClient = GetWorld()->GetGameViewport();
if (ViewportClient != nullptr)
{
FVector MoveDelta = Location - TouchItem.Location;
FVector2D ScreenSize;
ViewportClient->GetViewportSize(ScreenSize);
FVector2D ScaledDelta = FVector2D(MoveDelta.X, MoveDelta.Y) / ScreenSize;
if (ScaledDelta.X != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.X * BaseTurnRate;
AddControllerYawInput(Value);
}
if (ScaledDelta.Y != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.Y* BaseTurnRate;
AddControllerPitchInput(Value);
}
TouchItem.Location = Location;
}
TouchItem.Location = Location;
}
}
}
}

void AWizardsCharacter::MoveForward(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorForwardVector(), Value);
}
}

void AWizardsCharacter::MoveRight(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
}
}
void AWizardsCharacter::TurnAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void AWizardsCharacter::LookUpAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

bool AWizardsCharacter::EnableTouchscreenMovement(class UInputComponent* InputComponent)


{
bool bResult = false;
if (FPlatformMisc::GetUseVirtualJoysticks() || GetDefault<UInputSettings>()->bUseMouseForTouch)
{
bResult = true;
InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AWizardsCharacter::BeginTouch);
InputComponent->BindTouch(EInputEvent::IE_Released, this, &AWizardsCharacter::EndTouch);
InputComponent->BindTouch(EInputEvent::IE_Repeat, this, &AWizardsCharacter::TouchUpdate);
}
return bResult;
}

template<int newspell>
void AWizardsCharacter::spellSwitch()
{
currSpell = newspell;
}

float AWizardsCharacter::GetHealth() {
return Health;
}
float AWizardsCharacter::GetMana() {
return Mana;
}

void AWizardsCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const {


Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AWizardsCharacter, mySpellBook);
DOREPLIFETIME(AWizardsCharacter, Mana);
DOREPLIFETIME(AWizardsCharacter, currSpell);
}

void AWizardsCharacter::ServerFireProjectile_Implementation(FtheSpell castSpell, FRotator rotation, FVector location) {


//UWorld* const World = GetWorld();
UE_LOG(LogTemp, Warning, TEXT("Server Side"));
//OnFire();
ClientFireProjectile(castSpell, rotation, location);
/*if (theSpell->spellType == 0) {
const FRotator SpawnRotation = GetControlRotation();
const FVector SpawnLocation = GetActorLocation() + SpawnRotation.RotateVector(GunOffset);
AWizardsProjectile* wizardsSpell = World->SpawnActor<AWizardsProjectile>(ProjectileClass, SpawnLocation, SpawnRotation);// , myparams);
wizardsSpell->SpellCreation(theSpell, this);

}
else if (theSpell->spellType == 1) {
const FRotator SpawnRotation = FRotator(0.0);//GetControlRotation();
const FVector SpawnLocation = FVector(0.0);//GetActorLocation() + SpawnRotation.RotateVector(GunOffset);
AWizardsBlast* wizardsSpell = World->SpawnActor<AWizardsBlast>(BlastClass, SpawnLocation, SpawnRotation);// , myparams);
wizardsSpell->SpellCreation(theSpell->myParticle, theSpell->spellSize, theSpell->spellDamage, this);
wizardsSpell->AttachRootComponentTo(GetCapsuleComponent());//Probably useful for Blasts, Rays, and Conical attacks
UE_LOG(LogTemp, Warning, TEXT("Boom!"));
}
else if (theSpell->spellType == 2) {
const FRotator SpawnRotation = FRotator(0.0);//GetControlRotation();
const FVector SpawnLocation = FVector(0.0);//GetActorLocation() + SpawnRotation.RotateVector(GunOffset);
AWizardsCone* wizardsCone = World->SpawnActor<AWizardsCone>(ConeClass, SpawnLocation, SpawnRotation);// , myparams);
wizardsCone->SpellCreation(theSpell->myParticle, theSpell->spellSize, theSpell->spellDamage, this);
wizardsCone->AttachRootComponentTo(GetCapsuleComponent());//Probably useful for Blasts, Rays, and Conical attacks
activeAttack = Cast<AActor>(wizardsCone);
UE_LOG(LogTemp, Warning, TEXT("Svoosh!"));
}*/
}
bool AWizardsCharacter::ServerFireProjectile_Validate(FtheSpell castSpell, FRotator rotation, FVector location) {
return true;
}

void AWizardsCharacter::ClientFireProjectile_Implementation(FtheSpell castSpell, FRotator rotation, FVector location){


/*if (!mySpellBook.IsValidIndex(0)) {
UE_LOG(LogTemp, Warning, TEXT("Spell Gathering Needed!"));
newCharactersSpells();
}*/

//UE_LOG(LogTemp, Warning, TEXT("Role %d has currSpell %d and SpawnRotation "), Role, currSpell);

if (castSpell.spellType == 0)
{
UWorld* const World = GetWorld();
if (World)
{
// spawn the projectile at the muzzle
UParticleSystem* projParticle = particleList[castSpell.spellEffect + castSpell.spellType * 5];
UParticleSystem* blastParticle = particleList[castSpell.spellEffect + 5];
AWizardsProjectile* wizardsSpell = World->SpawnActor<AWizardsProjectile>(ProjectileClass, location, rotation);// , myparams);
wizardsSpell->SpellCreation(&castSpell, projParticle, blastParticle, this);
}
}
else if (castSpell.spellType == 1) {
UWorld* const World = GetWorld();
if (World)
{
// spawn the projectile at the muzzle
UParticleSystem* blastParticle = particleList[castSpell.spellEffect + castSpell.spellType * 5];
AWizardsBlast* wizardsSpell = World->SpawnActor<AWizardsBlast>(BlastClass, location, rotation);// , myparams);
wizardsSpell->SpellCreation(blastParticle, castSpell.spellSize, castSpell.spellDamage, this);
wizardsSpell->AttachRootComponentTo(GetCapsuleComponent());//Probably useful for Blasts, Rays, and Conical attacks
}
}
else if (castSpell.spellType == 2) {
UWorld* const World = GetWorld();
if (World)
{
// spawn the projectile at the muzzle
UParticleSystem* coneParticle = particleList[castSpell.spellEffect + castSpell.spellType * 5];
AWizardsCone* wizardsCone = World->SpawnActor<AWizardsCone>(ConeClass, location, rotation);// , myparams);
wizardsCone->SpellCreation(coneParticle, castSpell.spellSize, castSpell.spellDamage, this);
wizardsCone->AttachRootComponentTo(GetCapsuleComponent());//Probably useful for Blasts, Rays, and Conical attacks
activeAttack = Cast<AActor>(wizardsCone);
}
}
}
bool AWizardsCharacter::ClientFireProjectile_Validate(FtheSpell castSpell, FRotator rotation, FVector location){
return true;
}

void AWizardsCharacter::ServerDestroyCone_Implementation(AActor* theActor) {


ClientDestroyCone(theActor);

}
bool AWizardsCharacter::ServerDestroyCone_Validate(AActor* theActor) {
return true;
}

void AWizardsCharacter::ClientDestroyCone_Implementation(AActor* theActor) {


if (theActor != NULL) {
theActor->Destroy();
}
if (activeAttack != NULL) {
activeAttack->Destroy();
}
}
bool AWizardsCharacter::ClientDestroyCone_Validate(AActor* theActor) {
return true;
}

You might also like