142 lines
5.4 KiB
C++
142 lines
5.4 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
|
|
#include "HeadBobComponent.h"
|
|
|
|
#include "GameFramework/Character.h"
|
|
#include "GameFramework/CharacterMovementComponent.h"
|
|
|
|
UHeadBobComponent::UHeadBobComponent()
|
|
{
|
|
// C'est le pawn qui appelle UpdateBob depuis son propre Tick, juste avant de
|
|
// composer la transform de la camera. Un tick a nous ne servirait qu'a
|
|
// introduire une incertitude d'ordre.
|
|
PrimaryComponentTick.bCanEverTick = false;
|
|
|
|
// Valeurs de depart, a affiner a l'oeil dans BP_FpsPlayer. Elles sont ici et
|
|
// non en initialiseurs de membre pour que le constructeur reste le seul
|
|
// endroit a lire quand on cherche "combien ca bouge par defaut".
|
|
IdleProfile = FHeadBobProfile(0.15f, 0.55f, 0.05f, 0.03f, 0.f);
|
|
WalkProfile = FHeadBobProfile(1.1f, 1.6f, 1.2f, 0.4f, 0.f);
|
|
SprintProfile = FHeadBobProfile(1.55f, 2.1f, 1.7f, 0.8f, 0.f);
|
|
CrouchProfile = FHeadBobProfile(0.7f, 1.f, 0.9f, 0.3f, 0.f);
|
|
AirProfile = FHeadBobProfile(0.f, 0.f, 0.f, 0.f, 0.f);
|
|
|
|
// On demarre deja sur le profil d'immobilite : partir de zeros ferait monter
|
|
// la respiration pendant la premiere seconde de jeu, ce qui se voit.
|
|
BlendedProfile = IdleProfile;
|
|
}
|
|
|
|
void UHeadBobComponent::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
|
|
OwnerCharacter = Cast<ACharacter>(GetOwner());
|
|
if (!OwnerCharacter)
|
|
{
|
|
UE_LOG(LogTemp, Warning, TEXT("UHeadBobComponent : le proprietaire n'est pas un ACharacter, le balancement de tete est desactive."));
|
|
return;
|
|
}
|
|
|
|
// GetCharacterMovement() est un simple accesseur sur un membre du Character,
|
|
// mais le stocker evite un appel indirect par frame pour rien.
|
|
OwnerMovement = OwnerCharacter->GetCharacterMovement();
|
|
}
|
|
|
|
void UHeadBobComponent::UpdateBob(float DeltaTime)
|
|
{
|
|
// IsLocallyControlled : chez l'hote, le pawn de chaque ami porte AUSSI ce
|
|
// composant. Sans cette garde, le serveur calculerait trois balancements pour
|
|
// des cameras que personne ne regarde.
|
|
if (!bEnableHeadBob || !OwnerCharacter || !OwnerMovement || !OwnerCharacter->IsLocallyControlled())
|
|
{
|
|
// Remise a zero et pas simple sortie : couper l'effet en pleine partie
|
|
// laisserait sinon la camera figee sur le dernier decalage calcule.
|
|
LocationOffset = FVector::ZeroVector;
|
|
RotationOffset = FRotator::ZeroRotator;
|
|
return;
|
|
}
|
|
|
|
// Meme garde-fou que les ressorts de camera : un hoquet de framerate ferait
|
|
// sauter la phase de plusieurs pas d'un coup.
|
|
const float Step = FMath::Min(DeltaTime, 1.f / 30.f);
|
|
|
|
BlendTowards(SelectTargetProfile(), Step);
|
|
|
|
// La phase avance a la frequence FONDUE, pas a celle du profil vise : c'est
|
|
// ce qui fait que le rythme des pas accelere progressivement au demarrage du
|
|
// sprint au lieu de doubler d'une frame a l'autre.
|
|
StridePhase = FMath::Fmod(StridePhase + BlendedProfile.StrideFrequency * 2.f * PI * Step, 2.f * PI);
|
|
|
|
const float Intensity = GlobalIntensity * IntensityScale;
|
|
|
|
// Une foulee = deux pas. Le balancement lateral suit la foulee entiere (on
|
|
// bascule sur un pied puis sur l'autre), la montee-descente bat deux fois plus
|
|
// vite, une bosse par pied qui touche le sol. C'est ce rapport 1:2 qui dessine
|
|
// le fameux 8 couche et qui fait qu'on lit une marche plutot qu'un flottement.
|
|
const float LateralWave = FMath::Sin(StridePhase);
|
|
const float VerticalWave = FMath::Sin(StridePhase * 2.f);
|
|
|
|
// X reste nul : une composante avant-arriere donnerait un effet de zoom qui,
|
|
// en vue premiere personne, se lit comme du mal de mer plus que comme un pas.
|
|
LocationOffset = FVector(
|
|
0.f,
|
|
LateralWave * BlendedProfile.LateralAmount * Intensity,
|
|
VerticalWave * BlendedProfile.VerticalAmount * Intensity);
|
|
|
|
// Le roulis suit le balancement lateral : la tete penche du cote ou le corps
|
|
// se porte. Le tangage suit la montee-descente, et vaut 0 par defaut.
|
|
RotationOffset = FRotator(
|
|
VerticalWave * BlendedProfile.PitchAmount * Intensity,
|
|
0.f,
|
|
LateralWave * BlendedProfile.RollAmount * Intensity);
|
|
}
|
|
|
|
const FHeadBobProfile& UHeadBobComponent::SelectTargetProfile() const
|
|
{
|
|
if (OwnerMovement->IsFalling())
|
|
{
|
|
return AirProfile;
|
|
}
|
|
|
|
// Vitesse a plat : la composante verticale d'une pente ou d'un ascenseur n'a
|
|
// rien a voir avec la cadence des pas.
|
|
const float GroundSpeed = OwnerMovement->Velocity.Size2D();
|
|
|
|
// L'immobilite passe AVANT l'accroupissement : accroupi sans bouger, on
|
|
// respire, on ne marche pas sur place.
|
|
if (GroundSpeed < IdleSpeedThreshold)
|
|
{
|
|
return IdleProfile;
|
|
}
|
|
|
|
if (OwnerCharacter->bIsCrouched)
|
|
{
|
|
return CrouchProfile;
|
|
}
|
|
|
|
return (GroundSpeed > SprintSpeedThreshold) ? SprintProfile : WalkProfile;
|
|
}
|
|
|
|
void UHeadBobComponent::BlendTowards(const FHeadBobProfile& Target, float Step)
|
|
{
|
|
BlendedProfile.StrideFrequency = FMath::FInterpTo(BlendedProfile.StrideFrequency, Target.StrideFrequency, Step, ProfileBlendSpeed);
|
|
BlendedProfile.VerticalAmount = FMath::FInterpTo(BlendedProfile.VerticalAmount, Target.VerticalAmount, Step, ProfileBlendSpeed);
|
|
BlendedProfile.LateralAmount = FMath::FInterpTo(BlendedProfile.LateralAmount, Target.LateralAmount, Step, ProfileBlendSpeed);
|
|
BlendedProfile.RollAmount = FMath::FInterpTo(BlendedProfile.RollAmount, Target.RollAmount, Step, ProfileBlendSpeed);
|
|
BlendedProfile.PitchAmount = FMath::FInterpTo(BlendedProfile.PitchAmount, Target.PitchAmount, Step, ProfileBlendSpeed);
|
|
}
|
|
|
|
void UHeadBobComponent::ResetBob()
|
|
{
|
|
StridePhase = 0.f;
|
|
BlendedProfile = IdleProfile;
|
|
LocationOffset = FVector::ZeroVector;
|
|
RotationOffset = FRotator::ZeroRotator;
|
|
}
|
|
|
|
void UHeadBobComponent::SetIntensityScale(float NewScale)
|
|
{
|
|
IntensityScale = FMath::Max(NewScale, 0.f);
|
|
}
|