MyKarcio
(MyKarcio)
February 9, 2025, 9:21pm
1
Hi I have a problem with the GetSocketLocation function. When I use it in a player class that has a mesh the position agrees. When I use it in a PlayerController that takes a player instance the position is one frame behind. The same is true when I invoke GameplayAbiltiy in GAS. Is there any way to make the sockets position update first? Here is the code I used for debugging.
void AGameCharacter::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
FVector test = Weapon->GetSocketLocation(WeaponTipSocketName);
DrawDebugSphere(GetWorld(), test, 10, 10, FColor::White);
}
void AGamePlayerController::PlayerTick(float DeltaTime)
{
APawn* TPawn = GetPawn();
if (!TPawn)
return;
AGameCharacter* ControlledPawn = Cast<AGameCharacter>(TPawn);
if (!ControlledPawn)
return;
const FVector position = ControlledPawn->WeaponMesh->GetSocketLocation(ControlledPawn->WeaponTipSocketName);
DrawDebugSphere(GetWorld(), position, 10, 10, FColor::Green);
}
As you can see Green sphere did not overlap with White sphere. What I can change to make spheres overlap?
PREDALIEN
(PREDALIEN)
February 11, 2025, 9:19am
2
It seems your problem will be solved here
MyKarcio
(MyKarcio)
February 12, 2025, 6:15pm
4
Hey, @PREDALIEN thank you very much for your help. It turns out that the PlayerController is naturally supposed to be executed before the Player because it handles the player’s inputs, which then need to be interpreted by the player, their meshes, animations, etc.
Using the link you sent, I decided to create a PostPlayerComponent, where I will handle the appropriate inputs that need to be processed after the player’s tick. I’m leaving the code here for future reference in case anyone else wants to do the same.
I don’t know if this is the way it should be done, or if I have accidentally created an antipattern, but it serves its purpose.
Once again, thank you very much!
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "PostPlayerPlayerController.generated.h"
class GameCharacter;
class GamePlayerController;
UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class Game_API UPostPlayerPlayerController : public UActorComponent
{
GENERATED_BODY()
public:
UPostPlayerPlayerController();
protected:
virtual void BeginPlay() override;
public:
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
private:
AGamePlayerController* PlayerController;
AGameCharacter* OwnerPawn;
};
#include "Component/PostPlayerPlayerController.h"
#include "Player/GamePlayerController.h"
UPostPlayerPlayerController::UPostPlayerPlayerController()
{
PrimaryComponentTick.bCanEverTick = true;
}
void UPostPlayerPlayerController::BeginPlay()
{
Super::BeginPlay();
OwnerPawn = Cast<AGameCharacter>(GetOwner());
check(OwnerPawn);
PlayerController = Cast<AGamePlayerController>(OwnerPawn->GetController());
check(PlayerController);
if (OwnerPawn->PrimaryActorTick.bCanEverTick)
{
PrimaryComponentTick.AddPrerequisite(OwnerPawn, OwnerPawn->PrimaryActorTick);
}
}
void UPostPlayerPlayerController::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
const USkeletalMeshComponent* WeaponMesh = OwnerPawn->Weapon;
const FVector Position = WeaponMesh->GetSocketLocation(OwnerPawn->WeaponTipSocketName);
DrawDebugSphere(GetWorld(), Position, 10, 10, FColor::Green);
}
MyKarcio
(MyKarcio)
February 12, 2025, 10:08pm
5
Inside this PostPlayerPlayerController, I created a simple input handler because, by default, inputs are processed before the tick. To ensure that the bullet spawns at the correct position relative to the barrel, I had to implement a system that handles input last. IDK if this effort is worth 1 frame delay but I did it
.h
#pragma once
#include "CoreMinimal.h"
#include "GameplayTagContainer.h"
#include "Components/ActorComponent.h"
#include "PostPlayerPlayerController.generated.h"
struct FGameplayTag;
class UGameAbilitySystemComponent;
class UGameInputConfig;
class AGameCharacter;
class AGamePlayerController;
UENUM(BlueprintType)
enum class EInputType : uint8
{
Pressed,
Released,
Held
};
USTRUCT(BlueprintType)
struct FInputActionData
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite, Category = "Input")
EInputType InputType;
UPROPERTY(BlueprintReadWrite, Category = "Input")
FGameplayTag InputTag;
};
UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class Game_API UPostPlayerPlayerController : public UActorComponent
{
GENERATED_BODY()
public:
UPostPlayerPlayerController();
UFUNCTION(BlueprintCallable, Category = "Player|Location")
FVector GetSocketLocationAfter() const;
protected:
virtual void BeginPlay() override;
public:
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
private:
void ProcessInputStack();
UPROPERTY()
TArray<FInputActionData> InputActionStack;
UPROPERTY(EditDefaultsOnly, Category = "Input")
TObjectPtr<UGameInputConfig> InputConfig;
UPROPERTY()
TObjectPtr<UGameAbilitySystemComponent> GameAbilitySystemComponent;
void AbilityInputTagPressed(FGameplayTag InputTag);
void AbilityInputTagReleased(FGameplayTag InputTag);
void AbilityInputTagHeld(FGameplayTag InputTag);
UGameAbilitySystemComponent* GetASC();
UPROPERTY()
AGamePlayerController* PlayerController;
UPROPERTY()
AGameCharacter* OwnerPawn;
FVector Position;
};
and .cpp
// Copyright (c) 2024, Angelus Games. All rights reserved.
#include "Component/PostPlayerPlayerController.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "AbilitySystem/GameAbilitySystemComponent.h"
#include "Input/GameInputComponent.h"
#include "Player/GamePlayerController.h"
UPostPlayerPlayerController::UPostPlayerPlayerController()
{
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.TickGroup = TG_PrePhysics;
}
FVector UPostPlayerPlayerController::GetSocketLocationAfter() const
{
return Position;
}
void UPostPlayerPlayerController::BeginPlay()
{
Super::BeginPlay();
OwnerPawn = Cast<AGameCharacter>(GetOwner());
check(OwnerPawn);
PlayerController = Cast<AGamePlayerController>(OwnerPawn->GetController());
check(PlayerController);
if (OwnerPawn->PrimaryActorTick.bCanEverTick)
{
PrimaryComponentTick.AddPrerequisite(OwnerPawn, OwnerPawn->PrimaryActorTick);
}
UGameInputComponent* GameInputComponent = CastChecked<UGameInputComponent>(PlayerController->InputComponent);
GameInputComponent->BindAbilityActions(InputConfig, this,
&ThisClass::AbilityInputTagPressed,
&ThisClass::AbilityInputTagReleased,
&ThisClass::AbilityInputTagHeld);
}
void UPostPlayerPlayerController::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
const USkeletalMeshComponent* WeaponMesh = OwnerPawn->Weapon;
Position = WeaponMesh->GetSocketLocation(OwnerPawn->WeaponTipSocketName);
DrawDebugSphere(GetWorld(), Position, 10, 10, FColor::Green);
PlayerController->UpdateTargetPosition();
ProcessInputStack();
}
void UPostPlayerPlayerController::ProcessInputStack()
{
if (InputActionStack.Num() == 0)
return;
if (GetASC())
{
while (InputActionStack.Num() > 0)
{
const FInputActionData ActionData = InputActionStack.Pop();
switch (ActionData.InputType)
{
case EInputType::Pressed:
GameAbilitySystemComponent->AbilityInputTagPressed(ActionData.InputTag);
break;
case EInputType::Released:
GameAbilitySystemComponent->AbilityInputTagReleased(ActionData.InputTag);
break;
case EInputType::Held:
GameAbilitySystemComponent->AbilityInputTagHeld(ActionData.InputTag);
break;
}
}
}
}
void UPostPlayerPlayerController::AbilityInputTagPressed(const FGameplayTag InputTag)
{
InputActionStack.Push({ EInputType::Pressed, InputTag });
}
void UPostPlayerPlayerController::AbilityInputTagReleased(const FGameplayTag InputTag)
{
InputActionStack.Push({ EInputType::Released, InputTag });
}
void UPostPlayerPlayerController::AbilityInputTagHeld(const FGameplayTag InputTag)
{
InputActionStack.Push({ EInputType::Held, InputTag });
}
UGameAbilitySystemComponent* UPostPlayerPlayerController::GetASC()
{
if (GameAbilitySystemComponent == nullptr)
{
GameAbilitySystemComponent = Cast<UGameAbilitySystemComponent>
(UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(OwnerPawn));
}
return GameAbilitySystemComponent;
}