Ok, I made some progress, but still not getting the desired behaviour.
The clients can see the other players aligned to the surface planet while idle , but it gets the Zaxis up vector and not calculating the surface planet vector aligned…
I think i am about to get it, but still need to fully understand how it works. Do you know why I am no replicating the rotation of the characters? I read that replicating the rotation of the controller would replicate automatically, I tried but it didn’t worked…
I post the code again:
#include "PlanetUniversMediepolysCharacter.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "EnhancedInputComponent.h"
#include "Net/UnrealNetwork.h"
#include "Engine/Engine.h"
APlanetUniversMediepolysCharacter::APlanetUniversMediepolysCharacter()
{
Super::InitializeParamaters();
PrimaryActorTick.bCanEverTick = true;
CameraRoot = CreateDefaultSubobject<USceneComponent>(TEXT("CameraRoot"));
CameraRoot->SetupAttachment(RootComponent);
// Create a camera boom (pulls in towards the player if there is a collision)
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(CameraRoot);
CameraBoom->TargetArmLength = 400.0f; // The camera follows at this distance behind the character
CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller
// Create a follow camera
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation
FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm
SetReplicates(true);
SetReplicateMovement(true);
moveComp = GetCharacterMovement();
moveComp->SetIsReplicated(true);
}
void APlanetUniversMediepolysCharacter::BeginPlay()
{
Super::BeginPlay();
if (!planetGravity)
{
planetGravity = GetComponentByClass<UPlanetGravity>();
}
if (planetGravity)
{
planetGravity->SetIsReplicated(true);
}
else
{
UE_LOG(LogTemp, Error, TEXT("planetGravity is NULL when trying to replicate or register it."));
}
switch (GetLocalRole())
{
case ROLE_Authority:
RoleString = "Server (Authority)";
break;
case ROLE_AutonomousProxy:
RoleString = "Client (Owning Player)";
break;
case ROLE_SimulatedProxy:
RoleString = "Client (Simulated)";
break;
default:
RoleString = "Unknown Role";
}
UE_LOG(LogTemp, Warning, TEXT("%s: Role = %s"), *GetName(), *RoleString);
}
void APlanetUniversMediepolysCharacter::UpdateCameraLook()
{
FRotator PlayerRotation = CameraBoom->GetRelativeRotation();
//clamp PlayerRotation.Yaw value to 40 degrees
PlayerRotation.Pitch += LookAxisVector.Y;
PlayerRotation.Pitch = FMath::Clamp(PlayerRotation.Pitch, -40.0f, 40.0f);
PlayerRotation.Yaw += LookAxisVector.X;
// Apply the rotation to the CameraBoom
CameraBoom->SetRelativeRotation(PlayerRotation);
CameraBoom->SetWorldLocation(GetActorLocation());
AlignedToActor();
}
void APlanetUniversMediepolysCharacter::AlignedToActor()
{
if (planetGravity == nullptr) return;
if (planetGravity->GetGravityEnabled())
{
FVector CharacterLocation = GetActorLocation();
FVector UpDirection = (planetGravity->GetPlanetCenter() - CharacterLocation).GetSafeNormal();
// Step 2: Adjust the camera boom's rotation
// Get the forward direction of the character (local X-axis)
FVector CamForward = CameraRoot->GetForwardVector();
// Create a rotation with the forward vector and the up direction
FRotator DesiredRotation = FRotationMatrix::MakeFromZX(-UpDirection, CamForward).Rotator();
// Smoothly interpolate to the desired rotation for a natural effect
FRotator SmoothedRotation = FMath::RInterpTo(
CameraRoot->GetComponentRotation(), // Current rotation
DesiredRotation, // Target rotation
GetWorld()->GetDeltaSeconds(), // Delta time
2.0f // Interpolation speed
);
CameraRoot->SetWorldRotation(SmoothedRotation);
}
}
void APlanetUniversMediepolysCharacter::Move(const FInputActionValue& Value)
{
FVector2D MovementVector = Value.Get<FVector2D>();
FVector CalculateUpVector;
if (!planetGravity->GetGravityEnabled()) {
CalculateUpVector = GetActorUpVector();
}
else {
CalculateUpVector = (GetActorLocation() - planetGravity->GetPlanetCenter()).GetSafeNormal();
}
// Get the camera's forward vector
FVector CameraForward = FollowCamera->GetForwardVector();
FVector ForwardDirection = FVector::VectorPlaneProject(CameraForward, CalculateUpVector).GetSafeNormal();
// Move the character forward direction relative to the camera
AddMovementInput(ForwardDirection, MovementVector.Y);
// Get the camera's right vector
FVector CameraRight = FollowCamera->GetRightVector();
FVector RightDirection = FVector::VectorPlaneProject(CameraRight, CalculateUpVector).GetSafeNormal();
// Move the character relative to the camera
AddMovementInput(RightDirection, MovementVector.X);
}
void APlanetUniversMediepolysCharacter::AddAdditionalInputs()
{
}
void APlanetUniversMediepolysCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (!planetGravity) return;
// All clients and server calculate gravity continuously
CalculateGravity();
if (HasAuthority())
{
// Server maintains authoritative rotation
FRotator TargetRotation = CalculateRotationBySurface();
SetCharacterGravityRotation(TargetRotation);
}
else
{
// Clients use local prediction when server isn't available
SmoothAlignToGravity(DeltaTime);
// Apply smoothing to replicated rotations when available
if (!ReplicatedCharacterRotation.IsZero())
{
SmoothRotationApplied();
}
}
}
// Enable replication for GravityDirection
void APlanetUniversMediepolysCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
//DOREPLIFETIME(APlanetUniversMediepolysCharacter, medielerGravityDirection);
DOREPLIFETIME(APlanetUniversMediepolysCharacter, planetGravity);
DOREPLIFETIME(APlanetUniversMediepolysCharacter, ReplicatedCharacterRotation);
}
void APlanetUniversMediepolysCharacter::CalculateGravity()
{
if (!planetGravity) return;
if (planetGravity->GetGravityEnabled())
{
if (planetGravity->ApplyGravity(GetActorLocation()))
{
medielerGravityDirection = planetGravity->GetNewGravityDirection();
moveComp->SetGravityDirection(planetGravity->GetSurfaceNormal());
}
}
}
/** Called when entering a planet's gravity field */
void APlanetUniversMediepolysCharacter::OnEnterGravityField(UPlanetGravity* PlanetComponent)
{
if (planetGravity != PlanetComponent)
{
planetGravity = PlanetComponent;
OnRep_GravityChange();
}
}
/** Called when exiting a planet's gravity field */
void APlanetUniversMediepolysCharacter::OnExitGravityField(UPlanetGravity* PlanetComponent)
{
if (HasAuthority() && planetGravity == PlanetComponent)
{
planetGravity = nullptr;
OnRep_GravityChange();
}
}
void APlanetUniversMediepolysCharacter::OnRep_GravityChange()
{
moveComp = GetCharacterMovement();
CalculateGravity();
//log the Surface Direction
UE_LOG(LogTemp, Warning, TEXT("Client=>{%s} Role=>(%s), nova gravetat canviada: [%s] "), *GetName(), *RoleString, *medielerGravityDirection.ToString());
}
/** Smoothly aligns the character to the gravity direction */
FRotator APlanetUniversMediepolysCharacter::CalculateRotationBySurface()
{
if (!planetGravity->CurrentPlanetAttached)
{
return FRotator::ZeroRotator;
}
FVector PlanetCenter = planetGravity->CurrentPlanetAttached->GetPlanetCenter();
FVector GravityDirection = (GetActorLocation() - PlanetCenter).GetSafeNormal();
return FRotationMatrix::MakeFromZX(GravityDirection, GetActorForwardVector()).Rotator();
}
/** Smoothly aligns the character to the gravity direction */
void APlanetUniversMediepolysCharacter::SmoothAlignToGravity(float DeltaTime)
{
if (!planetGravity->CurrentPlanetAttached) return;
FRotator TargetRotation = CalculateRotationBySurface();
if (TargetRotation != FRotator::ZeroRotator)
{
SetCharacterGravityRotation(TargetRotation);
}
}
void APlanetUniversMediepolysCharacter::SetCharacterGravityRotation(FRotator NewRotation)
{
if (!HasAuthority()) return;
ReplicatedCharacterRotation = NewRotation;
SmoothRotationApplied();
OnRep_CharacterRotation(); // Call manually on server
}
void APlanetUniversMediepolysCharacter::OnRep_CharacterRotation()
{
// Apply to all remote clients (including the owning client when not authoritative)
if (HasAuthority()) return;
UE_LOG(LogTemp, Warning, TEXT("[%s] OnRep Rotation: %s"), *GetName(), *ReplicatedCharacterRotation.ToString());
SmoothRotationApplied();
}
void APlanetUniversMediepolysCharacter::SmoothRotationApplied()
{
FRotator CurrentRot = GetActorRotation();
FRotator Smoothed = FMath::RInterpTo(CurrentRot, ReplicatedCharacterRotation, GetWorld()->DeltaTimeSeconds, 1.0f);
SetActorRotation(Smoothed);
}
and the header file:
#pragma once
#include "CoreMinimal.h"
#include "TimerManager.h"
#include "SphericalMovementComponent.h"
#include "UniversMediepolysCharacter.h"
#include "PlanetUniversMediepolysCharacter.generated.h"
UCLASS()
class UNIVERSMEDIEPOLYS_API APlanetUniversMediepolysCharacter : public AUniversMediepolysCharacter
{
GENERATED_BODY()
public:
APlanetUniversMediepolysCharacter();
protected:
// To add mapping context
virtual void BeginPlay();
/** Camera boom positioning the camera behind the character */
UPROPERTY(VisibleAnywhere, Category = Camera, meta = (AllowPrivateAccess = "true"))
USpringArmComponent* CameraBoom;
/** Follow camera */
UPROPERTY(VisibleAnywhere, Category = Camera, meta = (AllowPrivateAccess = "true"))
UCameraComponent* FollowCamera;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
USceneComponent* CameraRoot;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "PlanetController")
APlayerController* PController;
UCharacterMovementComponent* moveComp;
UFUNCTION(BlueprintCallable)
void UpdateCameraLook();
UFUNCTION(BlueprintCallable)
void AlignedToActor();
UPROPERTY(ReplicatedUsing = OnRep_GravityChange)
UPlanetGravity* planetGravity;
UPROPERTY(ReplicatedUsing = OnRep_CharacterRotation)
FRotator ReplicatedCharacterRotation;
void AddAdditionalInputs() override;
public:
UFUNCTION(BlueprintCallable)
void SetPlanetGravity(UPlanetGravity* _planetGravity) { planetGravity = _planetGravity; }
UFUNCTION(BlueprintCallable)
UPlanetGravity* GetPlanetGravity() { return planetGravity; }
UPROPERTY()
FString RoleString;
void Move(const FInputActionValue& Value) override;
/** Returns CameraBoom subobject **/
FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
/** Returns FollowCamera subobject **/
FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
virtual void Tick(float DeltaTime) override;
#pragma region ReplicationFunctions
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const;
UPROPERTY(EditDefaultsOnly)
FVector medielerGravityDirection;
/** RepNotify function - Called when GravityDirection changes */
UFUNCTION()
void OnRep_GravityChange();
UFUNCTION()
FRotator CalculateRotationBySurface();
UFUNCTION()
void OnRep_CharacterRotation();
void SmoothRotationApplied();
UFUNCTION()
void SmoothAlignToGravity(float DeltaTime);
UFUNCTION()
void SetCharacterGravityRotation(FRotator NewRotation);
/** Function to calculate gravity locally */
UFUNCTION()
void CalculateGravity();
UFUNCTION(BlueprintCallable)
void OnEnterGravityField(UPlanetGravity* PlanetComponent);
UFUNCTION(BlueprintCallable)
void OnExitGravityField(UPlanetGravity* PlanetComponent);
#pragma endregion
};