// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "MWCharacterMovementComponent.generated.h"
/**
*
*/
UCLASS()
class MONSTERWARS_API UMWCharacterMovementComponent : public UCharacterMovementComponent {
GENERATED_BODY()
bool safe_wants_to_sprint;
UPROPERTY(EditDefaultsOnly)
float sprint_max_walk_speed;
UPROPERTY(EditDefaultsOnly)
float walk_max_walk_speed;
public:
UMWCharacterMovementComponent();
virtual FNetworkPredictionData_Client* GetPredictionData_Client() const override;
UFUNCTION(BlueprintCallable)
void SprintPressed();
UFUNCTION(BlueprintCallable)
void SprintReleased();
protected:
virtual void UpdateFromCompressedFlags(uint8 Flags) override;
virtual void OnMovementUpdated(float DeltaSeconds, const FVector& OldLocation, const FVector& OldVelocity) override;
class FSavedMove_MonsterWars_Character : public FSavedMove_Character {
typedef FSavedMove_Character Super;
uint8 saved_wants_to_sprint:1;
virtual bool CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* InCharacter, float MaxDelta) const override;
virtual void Clear() override;
virtual uint8 GetCompressedFlags() const override;
virtual void SetMoveFor(ACharacter* C, float InDeltaTime, FVector const& NewAccel, FNetworkPredictionData_Client_Character& ClientData) override;
virtual void PrepMoveFor(ACharacter* C) override;
};
class FNetworkPredictionData_Client_MWCharacter : public FNetworkPredictionData_Client_Character {
public:
typedef FNetworkPredictionData_Client_Character Super;
FNetworkPredictionData_Client_MWCharacter(const UMWCharacterMovementComponent& ClientMovement);
virtual FSavedMovePtr AllocateNewMove() override;
};
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "MWCharacterMovementComponent.h"
#include "GameFramework/Character.h"
UMWCharacterMovementComponent::UMWCharacterMovementComponent() {
}
FNetworkPredictionData_Client* UMWCharacterMovementComponent::GetPredictionData_Client() const {
check(PawnOwner != nullptr);
if (ClientPredictionData == nullptr) {
UMWCharacterMovementComponent* MutableCMC = const_cast<UMWCharacterMovementComponent*>(this);
MutableCMC->ClientPredictionData = new FNetworkPredictionData_Client_MWCharacter(*this);
MutableCMC->ClientPredictionData->MaxSmoothNetUpdateDist = 92.f;
MutableCMC->ClientPredictionData->NoSmoothNetUpdateDist = 140.f;
}
return ClientPredictionData;
}
void UMWCharacterMovementComponent::UpdateFromCompressedFlags(uint8 Flags) {
Super::UpdateFromCompressedFlags(Flags);
safe_wants_to_sprint = (Flags & FSavedMove_Character::FLAG_Custom_0) != 0;
}
void UMWCharacterMovementComponent::OnMovementUpdated(float DeltaSeconds, const FVector& OldLocation, const FVector& OldVelocity) {
Super::OnMovementUpdated(DeltaSeconds, OldLocation, OldVelocity);
if (MovementMode == MOVE_Walking) {
if (safe_wants_to_sprint) {
MaxWalkSpeed = sprint_max_walk_speed;
} else {
MaxWalkSpeed = walk_max_walk_speed;
}
}
}
void UMWCharacterMovementComponent::SprintPressed() {
safe_wants_to_sprint = true;
}
void UMWCharacterMovementComponent::SprintReleased() {
safe_wants_to_sprint = false;
}
bool UMWCharacterMovementComponent::FSavedMove_MonsterWars_Character::CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* InCharacter, float MaxDelta) const {
FSavedMove_MonsterWars_Character* NewMWSavedMove = static_cast<FSavedMove_MonsterWars_Character*>(NewMove.Get());
if (saved_wants_to_sprint != NewMWSavedMove->saved_wants_to_sprint) {
return false;
}
return FSavedMove_Character::CanCombineWith(NewMove, InCharacter, MaxDelta);
}
void UMWCharacterMovementComponent::FSavedMove_MonsterWars_Character::Clear() {
FSavedMove_Character::Clear();
saved_wants_to_sprint = 0;
}
uint8 UMWCharacterMovementComponent::FSavedMove_MonsterWars_Character::GetCompressedFlags() const {
uint8 Result = Super::GetCompressedFlags();
if (saved_wants_to_sprint) {
Result |= FLAG_Custom_0;
}
return Result;
}
void UMWCharacterMovementComponent::FSavedMove_MonsterWars_Character::SetMoveFor(ACharacter* C, float InDeltaTime, FVector const& NewAccel, FNetworkPredictionData_Client_Character& ClientData) {
FSavedMove_Character::SetMoveFor(C, InDeltaTime, NewAccel, ClientData);
UMWCharacterMovementComponent* CMC = Cast<UMWCharacterMovementComponent>(C->GetCharacterMovement());
saved_wants_to_sprint = CMC->safe_wants_to_sprint;
}
void UMWCharacterMovementComponent::FSavedMove_MonsterWars_Character::PrepMoveFor(ACharacter* C) {
Super::PrepMoveFor(C);
UMWCharacterMovementComponent* CMC = Cast<UMWCharacterMovementComponent>(C->GetCharacterMovement());
CMC->safe_wants_to_sprint = saved_wants_to_sprint;
}
UMWCharacterMovementComponent::FNetworkPredictionData_Client_MWCharacter::FNetworkPredictionData_Client_MWCharacter(const UMWCharacterMovementComponent& ClientMovement) : Super(ClientMovement) {
}
FSavedMovePtr UMWCharacterMovementComponent::FNetworkPredictionData_Client_MWCharacter::AllocateNewMove() {
return FSavedMovePtr(new FSavedMove_MonsterWars_Character);
}
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "InputActionValue.h"
#include "../Components/Movement/MWCharacterMovementComponent.h"
#include "MonsterWarsCharacter.generated.h"
UCLASS(config=Game)
class AMonsterWarsCharacter : public ACharacter
{
GENERATED_BODY()
/** Camera boom positioning the camera behind the character */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class USpringArmComponent* CameraBoom;
/** Follow camera */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class UCameraComponent* FollowCamera;
/** Jump Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* JumpAction;
/** Move Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* MoveAction;
/** Look Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* LookAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* RunAction;
protected:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = Movement)
class UMWCharacterMovementComponent* CustomMovementComponent;
public:
AMonsterWarsCharacter(const FObjectInitializer& ObjectInitializer);
virtual void Jump() override;
virtual void Landed(const FHitResult& Hit) override;
/** Called for movement input */
void Move(const FInputActionValue& Value);
void OnMovementModeChanged(EMovementMode PrevMovementMode, uint8 PreviousCustomMode);
void StopMove(const FInputActionValue& Value);
/** Called for looking input */
void Look(const FInputActionValue& Value);
void Run(const FInputActionValue& Value);
void StopRun(const FInputActionValue& Value);
void AdjustJumpingRotation();
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty> & OutLifetimeProps) const override;
protected:
// APawn interface
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
virtual void Tick(float DeltaTime) override;
// To add mapping context
virtual void BeginPlay();
public:
/** Returns CameraBoom subobject **/
FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
/** Returns FollowCamera subobject **/
FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
};
#include "MonsterWarsCharacter.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "GameFramework/Controller.h"
#include "GameFramework/SpringArmComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
//#include "Kismet/GameplayStatics.h"
#include "Net/UnrealNetwork.h"
//////////////////////////////////////////////////////////////////////////
// AMonsterWarsCharacter
AMonsterWarsCharacter::AMonsterWarsCharacter(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer.SetDefaultSubobjectClass<UMWCharacterMovementComponent>(ACharacter::CharacterMovementComponentName)){
// Set size for collision capsule
CustomMovementComponent = Cast<UMWCharacterMovementComponent>(GetCharacterMovement());
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
// Don't rotate when the controller rotates. Let that just affect the camera.
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
// Configure character movement
//GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...
GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f); // ...at this rotation rate
// Note: For faster iteration times these variables, and many more, can be tweaked in the Character Blueprint
// instead of recompiling to adjust them
GetCharacterMovement()->JumpZVelocity = 700.f;
GetCharacterMovement()->AirControl = 0.35f;
GetCharacterMovement()->MaxWalkSpeed = 300.f;
GetCharacterMovement()->MinAnalogWalkSpeed = 20.f;
GetCharacterMovement()->BrakingDecelerationWalking = 100.f;
GetCharacterMovement()->PerchAdditionalHeight = 10.0f;
// Create a camera boom (pulls in towards the player if there is a collision)
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(RootComponent);
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
this->bReplicates = true;
this->SetReplicates(true);
this->SetReplicateMovement(true);
}
void AMonsterWarsCharacter::BeginPlay() {
Super::BeginPlay();
if (GetLocalRole() != ROLE_Authority) {
UE_LOG(LogTemp, Warning, TEXT("CHARACTER_START_CLIENT"));
} else {
UE_LOG(LogTemp, Warning, TEXT("CHARACTER_START_SERVER"));
}
}
void AMonsterWarsCharacter::Tick(float DeltaTime) {
Super::Tick(DeltaTime);
}
void AMonsterWarsCharacter::AdjustJumpingRotation() {
}
void AMonsterWarsCharacter::Jump() {
Super::Jump();
}
void AMonsterWarsCharacter::OnMovementModeChanged(EMovementMode PrevMovementMode, uint8 PreviousCustomMode) {
Super::OnMovementModeChanged(PrevMovementMode, PreviousCustomMode);
switch(GetCharacterMovement()->MovementMode) {
case 1:
GetCharacterMovement()->bOrientRotationToMovement = false;
bUseControllerRotationYaw = true;
GetCharacterMovement()->RotationRate = FRotator(0.f, 0.f, 0.f); // Adjust as needed
break;
case 3:
//TODO: Make this only able to happen between 45 degree offsets from camera forward position.
GetCharacterMovement()->bOrientRotationToMovement = true;
bUseControllerRotationYaw = false;
GetCharacterMovement()->RotationRate = FRotator(0.f, 150.f, 0.f); // Adjust as needed
break;
}
}
void AMonsterWarsCharacter::Landed(const FHitResult& Hit) {
Super::Landed(Hit);
if (GetLocalRole() != ROLE_Authority) {
//UE_LOG(LogTemp, Warning, TEXT("CLIENT: HasLanded"));
} else {
//UE_LOG(LogTemp, Warning, TEXT("SERVER: HasLanded"));
}
}
void AMonsterWarsCharacter::Move(const FInputActionValue& Value) {
FVector2D MovementVector = Value.Get<FVector2D>();
if (Controller != nullptr) {
// find out which way is forward
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// get forward vector
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
// get right vector
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// add movement
AddMovementInput(ForwardDirection, MovementVector.Y);
AddMovementInput(RightDirection, MovementVector.X);
}
}
void AMonsterWarsCharacter::StopMove(const FInputActionValue& Value) {
}
void AMonsterWarsCharacter::Run(const FInputActionValue& Value) {
CustomMovementComponent->SprintPressed();
}
void AMonsterWarsCharacter::StopRun(const FInputActionValue& Value) {
CustomMovementComponent->SprintReleased();
}
void AMonsterWarsCharacter::Look(const FInputActionValue& Value) {
// input is a Vector2D
FVector2D LookAxisVector = Value.Get<FVector2D>();
if (Controller != nullptr) {
// add yaw and pitch input to controller
AddControllerYawInput(LookAxisVector.X);
AddControllerPitchInput(LookAxisVector.Y);
}
}
void AMonsterWarsCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) {
// Set up action bindings
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent)) {
//Jumping
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &ACharacter::Jump);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
//Moving
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMonsterWarsCharacter::Move);
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Completed, this, &AMonsterWarsCharacter::StopMove);
//Running
EnhancedInputComponent->BindAction(RunAction, ETriggerEvent::Started, this, &AMonsterWarsCharacter::Run);
EnhancedInputComponent->BindAction(RunAction, ETriggerEvent::Completed, this, &AMonsterWarsCharacter::StopRun);
//Looking
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMonsterWarsCharacter::Look);
}
}
void AMonsterWarsCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty> & OutLifetimeProps) const {
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
}