第一次提交
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Mover/Zipline/GMS_ZiplineInterface.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_ZiplineInterface)
|
||||
|
||||
// Add default functionality here for any IGMS_ZiplineInterface functions that are not pure virtual.
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Mover/Zipline/GMS_ZiplineModeTransition.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "DefaultMovementSet/CharacterMoverComponent.h"
|
||||
#include "Kismet/KismetSystemLibrary.h"
|
||||
#include "Mover/GMS_MoverStructLibrary.h"
|
||||
#include "Mover/Zipline/GMS_ZiplineInterface.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_ZiplineModeTransition)
|
||||
|
||||
// UGMS_ZiplineStartTransition //////////////////////////////
|
||||
|
||||
UGMS_ZiplineStartTransition::UGMS_ZiplineStartTransition(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
#if ENGINE_MINOR_VERSION >=6
|
||||
FTransitionEvalResult UGMS_ZiplineStartTransition::Evaluate_Implementation(const FSimulationTickParams& Params) const
|
||||
#else
|
||||
FTransitionEvalResult UGMS_ZiplineStartTransition::OnEvaluate(const FSimulationTickParams& Params) const
|
||||
#endif
|
||||
{
|
||||
FTransitionEvalResult EvalResult = FTransitionEvalResult::NoTransition;
|
||||
|
||||
UCharacterMoverComponent* MoverComp = Cast<UCharacterMoverComponent>(Params.MovingComps.MoverComponent.Get());
|
||||
|
||||
const FMoverSyncState& SyncState = Params.StartState.SyncState;
|
||||
|
||||
if (MoverComp && MoverComp->IsAirborne() && SyncState.MovementMode != ZipliningModeName)
|
||||
{
|
||||
if (const FGMS_MoverTagInputs* AbilityInputs = Params.StartState.InputCmd.InputCollection.FindDataByType<FGMS_MoverTagInputs>())
|
||||
{
|
||||
if (ZipliningInputTag.IsValid() && AbilityInputs->Tags.HasTagExact(ZipliningInputTag))
|
||||
{
|
||||
TArray<AActor*> OverlappingActors;
|
||||
MoverComp->GetOwner()->GetOverlappingActors(OUT OverlappingActors);
|
||||
|
||||
for (AActor* CandidateActor : OverlappingActors)
|
||||
{
|
||||
bool bIsZipline = UKismetSystemLibrary::DoesImplementInterface(CandidateActor, UGMS_ZiplineInterface::StaticClass());
|
||||
|
||||
if (bIsZipline)
|
||||
{
|
||||
EvalResult.NextMode = ZipliningModeName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return EvalResult;
|
||||
}
|
||||
|
||||
|
||||
// UGMS_ZiplineEndTransition //////////////////////////////
|
||||
|
||||
UGMS_ZiplineEndTransition::UGMS_ZiplineEndTransition(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#if ENGINE_MINOR_VERSION >=6
|
||||
FTransitionEvalResult UGMS_ZiplineEndTransition::Evaluate_Implementation(const FSimulationTickParams& Params) const
|
||||
#else
|
||||
FTransitionEvalResult UGMS_ZiplineEndTransition::OnEvaluate(const FSimulationTickParams& Params) const
|
||||
#endif
|
||||
{
|
||||
FTransitionEvalResult EvalResult = FTransitionEvalResult::NoTransition;
|
||||
|
||||
if (const FCharacterDefaultInputs* DefaultInputs = Params.StartState.InputCmd.InputCollection.FindDataByType<FCharacterDefaultInputs>())
|
||||
{
|
||||
if (DefaultInputs->bIsJumpJustPressed)
|
||||
{
|
||||
EvalResult.NextMode = AutoExitToMode;
|
||||
}
|
||||
}
|
||||
|
||||
return EvalResult;
|
||||
}
|
||||
|
||||
#if ENGINE_MINOR_VERSION >=6
|
||||
void UGMS_ZiplineEndTransition::Trigger_Implementation(const FSimulationTickParams& Params)
|
||||
#else
|
||||
void UGMS_ZiplineEndTransition::OnTrigger(const FSimulationTickParams& Params)
|
||||
#endif
|
||||
{
|
||||
//TODO: create a small jump, using current directionality
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Mover/Zipline/GMS_ZipliningMode.h"
|
||||
|
||||
#include "MoverComponent.h"
|
||||
#include "DefaultMovementSet/Settings/CommonLegacyMovementSettings.h"
|
||||
#include "Kismet/KismetSystemLibrary.h"
|
||||
#include "MoveLibrary/MovementUtils.h"
|
||||
#include "Mover/Zipline/GMS_ZiplineInterface.h"
|
||||
#include "Mover/Zipline/GMS_ZiplineModeTransition.h"
|
||||
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_ZipliningMode)
|
||||
|
||||
|
||||
// FGMS_ZipliningState //////////////////////////////
|
||||
|
||||
FMoverDataStructBase* FGMS_ZipliningState::Clone() const
|
||||
{
|
||||
FGMS_ZipliningState* CopyPtr = new FGMS_ZipliningState(*this);
|
||||
return CopyPtr;
|
||||
}
|
||||
|
||||
bool FGMS_ZipliningState::NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess)
|
||||
{
|
||||
Super::NetSerialize(Ar, Map, bOutSuccess);
|
||||
|
||||
Ar << ZiplineActor;
|
||||
Ar.SerializeBits(&bIsMovingAtoB, 1);
|
||||
|
||||
bOutSuccess = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FGMS_ZipliningState::ToString(FAnsiStringBuilderBase& Out) const
|
||||
{
|
||||
Super::ToString(Out);
|
||||
|
||||
Out.Appendf("ZiplineActor: %s\n", *GetNameSafe(ZiplineActor));
|
||||
Out.Appendf("IsMovingAtoB: %d\n", bIsMovingAtoB);
|
||||
}
|
||||
|
||||
bool FGMS_ZipliningState::ShouldReconcile(const FMoverDataStructBase& AuthorityState) const
|
||||
{
|
||||
const FGMS_ZipliningState* AuthorityZiplineState = static_cast<const FGMS_ZipliningState*>(&AuthorityState);
|
||||
|
||||
return (ZiplineActor != AuthorityZiplineState->ZiplineActor) ||
|
||||
(bIsMovingAtoB != AuthorityZiplineState->bIsMovingAtoB);
|
||||
}
|
||||
|
||||
void FGMS_ZipliningState::Interpolate(const FMoverDataStructBase& From, const FMoverDataStructBase& To, float Pct)
|
||||
{
|
||||
const FGMS_ZipliningState* FromState = static_cast<const FGMS_ZipliningState*>(&From);
|
||||
const FGMS_ZipliningState* ToState = static_cast<const FGMS_ZipliningState*>(&To);
|
||||
|
||||
ZiplineActor = ToState->ZiplineActor;
|
||||
bIsMovingAtoB = ToState->bIsMovingAtoB;
|
||||
}
|
||||
|
||||
|
||||
// UGMS_ZipliningMode //////////////////////////////
|
||||
|
||||
UGMS_ZipliningMode::UGMS_ZipliningMode(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
Transitions.Add(CreateDefaultSubobject<UGMS_ZiplineEndTransition>(TEXT("ZiplineEndTransition")));
|
||||
}
|
||||
|
||||
#if ENGINE_MINOR_VERSION >=6
|
||||
void UGMS_ZipliningMode::GenerateMove_Implementation(const FMoverTickStartData& StartState, const FMoverTimeStep& TimeStep, FProposedMove& OutProposedMove) const
|
||||
#else
|
||||
void UGMS_ZipliningMode::OnGenerateMove(const FMoverTickStartData& StartState, const FMoverTimeStep& TimeStep, FProposedMove& OutProposedMove) const
|
||||
#endif
|
||||
{
|
||||
UMoverComponent* MoverComp = GetMoverComponent();
|
||||
|
||||
// Ziplining is just following a path from A to B, so all movement is handled in OnSimulationTick
|
||||
OutProposedMove = FProposedMove();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if ENGINE_MINOR_VERSION >=6
|
||||
void UGMS_ZipliningMode::SimulationTick_Implementation(const FSimulationTickParams& Params, FMoverTickEndData& OutputState)
|
||||
#else
|
||||
void UGMS_ZipliningMode::OnSimulationTick(const FSimulationTickParams& Params, FMoverTickEndData& OutputState)
|
||||
#endif
|
||||
{
|
||||
// Are we continuing a move or starting fresh?
|
||||
const FGMS_ZipliningState* StartingZipState = Params.StartState.SyncState.SyncStateCollection.FindDataByType<FGMS_ZipliningState>();
|
||||
|
||||
FMoverDefaultSyncState& OutputSyncState = OutputState.SyncState.SyncStateCollection.FindOrAddMutableDataByType<FMoverDefaultSyncState>();
|
||||
FGMS_ZipliningState& OutZipState = OutputState.SyncState.SyncStateCollection.FindOrAddMutableDataByType<FGMS_ZipliningState>();
|
||||
|
||||
USceneComponent* UpdatedComponent = Params.MovingComps.UpdatedComponent.Get();
|
||||
UMoverComponent* MoverComp = Params.MovingComps.MoverComponent.Get();
|
||||
AActor* MoverActor = MoverComp->GetOwner();
|
||||
|
||||
USceneComponent* StartPoint = nullptr;
|
||||
USceneComponent* EndPoint = nullptr;
|
||||
FVector ZipDirection;
|
||||
FVector FlatFacingDir;
|
||||
|
||||
const float DeltaSeconds = Params.TimeStep.StepMs * 0.001f;
|
||||
|
||||
FVector ActorOrigin;
|
||||
FVector BoxExtent;
|
||||
MoverActor->GetActorBounds(true, OUT ActorOrigin, OUT BoxExtent);
|
||||
const FVector ActorToZiplineOffset = MoverComp->GetUpDirection() * BoxExtent.Z;
|
||||
|
||||
if (!StartingZipState)
|
||||
{
|
||||
// There is no existing zipline state... so let's find the target
|
||||
// A) teleport to the closest starting point, set the zip direction
|
||||
// B) choose the appropriate facing direction
|
||||
// C) choose the appropriate initial velocity
|
||||
TArray<AActor*> OverlappingActors;
|
||||
MoverComp->GetOwner()->GetOverlappingActors(OUT OverlappingActors);
|
||||
|
||||
for (AActor* CandidateActor : OverlappingActors)
|
||||
{
|
||||
bool bIsZipline = UKismetSystemLibrary::DoesImplementInterface(CandidateActor, UGMS_ZiplineInterface::StaticClass());
|
||||
|
||||
if (bIsZipline)
|
||||
{
|
||||
const FVector MoverLoc = UpdatedComponent->GetComponentLocation();
|
||||
USceneComponent* ZipPointA = IGMS_ZiplineInterface::Execute_GetStartComponent(CandidateActor);
|
||||
USceneComponent* ZipPointB = IGMS_ZiplineInterface::Execute_GetEndComponent(CandidateActor);
|
||||
|
||||
if (FVector::DistSquared(ZipPointA->GetComponentLocation(), MoverLoc) < FVector::DistSquared(ZipPointB->GetComponentLocation(), MoverLoc))
|
||||
{
|
||||
OutZipState.bIsMovingAtoB = true;
|
||||
StartPoint = ZipPointA;
|
||||
EndPoint = ZipPointB;
|
||||
}
|
||||
else
|
||||
{
|
||||
OutZipState.bIsMovingAtoB = false;
|
||||
StartPoint = ZipPointB;
|
||||
EndPoint = ZipPointA;
|
||||
}
|
||||
|
||||
ZipDirection = (EndPoint->GetComponentLocation() - StartPoint->GetComponentLocation()).GetSafeNormal();
|
||||
|
||||
const FVector WarpLocation = StartPoint->GetComponentLocation() - ActorToZiplineOffset;
|
||||
|
||||
FlatFacingDir = FVector::VectorPlaneProject(ZipDirection, MoverComp->GetUpDirection()).GetSafeNormal();
|
||||
|
||||
OutZipState.ZiplineActor = CandidateActor;
|
||||
|
||||
UpdatedComponent->GetOwner()->TeleportTo(WarpLocation, FlatFacingDir.ToOrientationRotator());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we were unable to find a valid target zipline, refund all the time and let the actor fall
|
||||
if (!StartPoint || !EndPoint)
|
||||
{
|
||||
FName DefaultAirMode = DefaultModeNames::Falling;
|
||||
if (UCommonLegacyMovementSettings* LegacySettings = MoverComp->FindSharedSettings_Mutable<UCommonLegacyMovementSettings>())
|
||||
{
|
||||
DefaultAirMode = LegacySettings->AirMovementModeName;
|
||||
}
|
||||
|
||||
OutputState.MovementEndState.NextModeName = DefaultModeNames::Falling;
|
||||
OutputState.MovementEndState.RemainingMs = Params.TimeStep.StepMs;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
check(StartingZipState->ZiplineActor);
|
||||
OutZipState = *StartingZipState;
|
||||
|
||||
USceneComponent* ZipPointA = IGMS_ZiplineInterface::Execute_GetStartComponent(StartingZipState->ZiplineActor);
|
||||
USceneComponent* ZipPointB = IGMS_ZiplineInterface::Execute_GetEndComponent(StartingZipState->ZiplineActor);
|
||||
|
||||
if (StartingZipState->bIsMovingAtoB)
|
||||
{
|
||||
StartPoint = ZipPointA;
|
||||
EndPoint = ZipPointB;
|
||||
}
|
||||
else
|
||||
{
|
||||
StartPoint = ZipPointB;
|
||||
EndPoint = ZipPointA;
|
||||
}
|
||||
|
||||
ZipDirection = (EndPoint->GetComponentLocation() - StartPoint->GetComponentLocation()).GetSafeNormal();
|
||||
FlatFacingDir = FVector::VectorPlaneProject(ZipDirection, MoverComp->GetUpDirection()).GetSafeNormal();
|
||||
}
|
||||
|
||||
|
||||
// Now let's slide along the zipline
|
||||
const FVector StepStartPos = UpdatedComponent->GetComponentLocation() + ActorToZiplineOffset;
|
||||
const FVector DesiredEndPos = StepStartPos + (ZipDirection * MaxSpeed * DeltaSeconds); // TODO: Make speed more dynamic
|
||||
|
||||
FVector ActualEndPos = FMath::ClosestPointOnSegment(DesiredEndPos,
|
||||
StartPoint->GetComponentLocation(),
|
||||
EndPoint->GetComponentLocation());
|
||||
|
||||
bool bWillReachEndPosition = (ActualEndPos - EndPoint->GetComponentLocation()).IsNearlyZero();
|
||||
|
||||
FVector MoveDelta = ActualEndPos - StepStartPos;
|
||||
|
||||
|
||||
FMovementRecord MoveRecord;
|
||||
MoveRecord.SetDeltaSeconds(DeltaSeconds);
|
||||
|
||||
|
||||
if (!MoveDelta.IsNearlyZero())
|
||||
{
|
||||
FHitResult Hit(1.f);
|
||||
|
||||
UMovementUtils::TrySafeMoveUpdatedComponent(Params.MovingComps, MoveDelta, FlatFacingDir.ToOrientationQuat(), true, Hit, ETeleportType::None, MoveRecord);
|
||||
}
|
||||
|
||||
|
||||
const FVector FinalLocation = UpdatedComponent->GetComponentLocation();
|
||||
const FVector FinalVelocity = MoveRecord.GetRelevantVelocity();
|
||||
|
||||
OutputSyncState.SetTransforms_WorldSpace(FinalLocation,
|
||||
UpdatedComponent->GetComponentRotation(),
|
||||
FinalVelocity,
|
||||
nullptr); // no movement base
|
||||
|
||||
UpdatedComponent->ComponentVelocity = FinalVelocity;
|
||||
|
||||
|
||||
if (bWillReachEndPosition)
|
||||
{
|
||||
FName DefaultAirMode = DefaultModeNames::Falling;
|
||||
if (UCommonLegacyMovementSettings* LegacySettings = MoverComp->FindSharedSettings_Mutable<UCommonLegacyMovementSettings>())
|
||||
{
|
||||
DefaultAirMode = LegacySettings->AirMovementModeName;
|
||||
}
|
||||
|
||||
OutputState.MovementEndState.NextModeName = DefaultAirMode;
|
||||
// TODO: If we reach the end position early, we should refund the remaining time
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user