Walk on walls Function

I’ve seen variants of this question asked around the fourms but sitll not entirely sure how to implment it. I’m trying to create the ability for the player to walk on walls, however, I want this to be controlled by a key function. What is the best way to go about applying this?

Thanks,
Hans

In Unreal 3, in the EPhysics enum, there was a PHYS_Spider. I never used it myself, so it might not be helpful to you, but it’s a place to start. On that note, here’s a video about walking on walls and spider physics in Unreal 4. I can’t watch the video to see how helpful it is because I’m at work, but it might be of use to you.

I had similar problem and I decided to write my own character class to solve this. My idea was to create whole game with switchable gravity, I thought it would be quite cool concept. After I saw Broken Dimensions gameplay I thought that it’s not something as innovative - and actually it works with specific types of genres. My concept changed with time, maybe I’ll try to implement it somewhere else. For now, you are free to use it.

I’ll post here some of my source code to make it use to you and others. One big flaw - since I had to write from the beginning whole Character class (I called it GravityCharacter), I have quite broken collision detection. It works, but not as well as original ACharacter class.

(GravityCharacter.h file)

#pragma once

#include "GameFramework/Character.h"
#include "ObjectMovementComponent.h"
#include "GravityCharacter.generated.h"

class UGravityMovementComponent;

UCLASS()
class GRAVITY_API AGravityCharacter : public ACharacter
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere)
		UCameraComponent *CameraComponent;
	UPROPERTY(EditAnywhere)
		UGravityMovementComponent *CharacterMovementComponent;


public:

	UGravityMovementComponent* GetCharacterMovementComponent() { return CharacterMovementComponent; }

	UCameraComponent* GetCameraComponent() { return CameraComponent; }
	FVector GetCameraLocation() { return CameraComponent->GetComponentLocation(); }
	FRotator GetCameraRotation() { return CameraComponent->GetComponentRotation(); }
	void HitLaser(ALaserBeam *Laser) {}

	AGravityCharacter();

	virtual void BeginPlay() override;
	virtual void Tick(float DeltaSeconds) override;
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	virtual void MoveForward(float Val);
	virtual void MoveRight(float Val);
	virtual void ChangeGravity(float Val);

	virtual void Jump();

};

(GravityCharacter.cpp file)

#include "Gravity.h"
#include "Runtime/Engine/Classes/Kismet/KismetSystemLibrary.h"
#include "GravityMovementComponent.h"
#include "Runtime/Engine/Classes/Engine/Player.h"
#include "GravityCharacter.h"



AGravityCharacter::AGravityCharacter() {

    //to obtain this I had to change one keyword in Character.h from private to protected. Pretty sick, huh?
	CharacterMovement->DestroyComponent();
	ACharacter::Mesh->DestroyComponent();

	RootComponent = CapsuleComponent;
	CapsuleComponent->SetSimulatePhysics(false);
	CapsuleComponent->SetEnableGravity(false);
	PrimaryActorTick.bCanEverTick = true;
	CameraComponent = CreateDefaultSubobject<UCameraComponent>("Camera Component");
	CameraComponent->SetupAttachment(CapsuleComponent);
	CameraComponent->SetRelativeLocation(FVector(0, 0, CapsuleComponent->GetScaledCapsuleHalfHeight() / 2));

	CharacterMovementComponent = CreateDefaultSubobject<UGravityMovementComponent>("Character Movement Component");
	CharacterMovementComponent->SetCharacterOwner(this);

	//AutoPossessPlayer = EAutoReceiveInput::Player0;
	//APawn::bUseControllerRotationPitch = false;
	//APawn::bUseControllerRotationYaw = false;
}

void AGravityCharacter::Tick(float DeltaTime) {

	Super::Tick(DeltaTime);

}

void AGravityCharacter::BeginPlay() {
	Super::BeginPlay();
}

void AGravityCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) {
	check(PlayerInputComponent);
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	PlayerInputComponent->BindAxis("MoveForward", this, &AGravityCharacter::MoveForward);
	PlayerInputComponent->BindAxis("MoveRight", this, &AGravityCharacter::MoveRight);
	PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
	PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
	PlayerInputComponent->BindAxis("ChangeGravity", this, &AGravityCharacter::ChangeGravity);

	PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AGravityCharacter::Jump);

}


void AGravityCharacter::MoveForward(float Value) {
	if (Value != 0.0f)CharacterMovementComponent->MoveForwardInput(Value);
}
void AGravityCharacter::MoveRight(float Value) {
	if (Value != 0.0f)CharacterMovementComponent->MoveRightInput(Value);
}
void AGravityCharacter::ChangeGravity(float Value) {
	if (Value != 0.0f)CharacterMovementComponent->ChangeGravityInput(Value);
}
void AGravityCharacter::Jump() {
	CharacterMovementComponent->JumpInput();
}

(GravityMovementComponent.h - after some time I realised it would be better to write UActorComponent inherited class instead of keeping everyting in one file)

#pragma once

#include "Components/ActorComponent.h"
#include "GravityCharacter.h"
#include "GravityMovementComponent.generated.h"

class AGravityCharacter;

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class GRAVITY_API UGravityMovementComponent : public UActorComponent
{
	GENERATED_BODY()

protected:
	AGravityCharacter *OwnerCharacter;

public:	
	UGravityMovementComponent();
	virtual void BeginPlay() override;
	virtual void TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction ) override;
	
	void SetCharacterOwner(AGravityCharacter *InOwnerCharacter) { OwnerCharacter = InOwnerCharacter; }

	FVector GetActorLocation() { return OwnerCharacter->GetActorLocation(); }
	FRotator GetActorRotation() { return OwnerCharacter->GetActorRotation(); }

	AController* GetController() { return OwnerCharacter->GetController(); }
	UCapsuleComponent* GetCapsuleComponent() { return OwnerCharacter->GetCapsuleComponent(); }
	UCameraComponent* GetCameraComponent() { return OwnerCharacter->GetCameraComponent(); }

	UPROPERTY(EditAnywhere)
		float GravityValue = 4000;
	UPROPERTY(EditAnywhere)
		float MovementSpeed = 5000;
	UPROPERTY(EditAnywhere)
		float FrictionParameter = 0.9f;
	UPROPERTY(EditAnywhere)
		float JumpVelocity = 2000;

	int GValue = 0;

	FVector Xaxis = FVector(1, 0, 0);
	FVector Yaxis = FVector(0, 1, 0);
	FVector Zaxis = FVector(0, 0, 1);

	FVector& operator[](int i) {
		if (i % 3 == 0)return Xaxis;
		if (i % 3 == 1)return Yaxis;
		return Zaxis;
	}

	EMovementMode MovementMode = MOVE_Flying;

	FVector Acceleration = FVector(0, 0, 0);
	FVector Velocity = FVector(0, 0, 0);

	AOptics *HoldedItem = NULL;
	bool bIsHoldingItem = false;

	bool bIsDroppingItem = false;

	FVector StartingItemLocation;
	FRotator StartingItemRotation;

	EMovementMode GetMovementMode() { return MovementMode; }
	void SetMovementMode(EMovementMode InMovementMode) { MovementMode = InMovementMode; }

	void ChangeGravityInput(float Val) {
		if (Val > 0)GValue++;
		if (Val < 0)GValue--;
		if (Val == 0)return;

		GValue = (GValue + 6) % 6;
		int j = GValue / 2;
		int k = 1 - GValue % 2 * 2;
		auto &Vec = *this;
		Vec[j] = FVector(k, 0, 0);
		Vec[j + 1] = FVector(0, k, 0);
		Vec[j + 2] = FVector(0, 0, k);
	}



	void MoveForwardInput(float Value) {
		FVector Vec = GetActorRotation().Vector();
		Vec -= Zaxis * FVector::DotProduct(Zaxis, Vec);
		Vec.Normalize();
		AddMovementInput(Vec, Value, true);
	}

	void MoveRightInput(float Value) {
		FVector Vec = GetActorRotation().Vector();
		Vec -= Zaxis * FVector::DotProduct(Zaxis, Vec);
		Vec.Normalize();
		AddMovementInput(FVector::CrossProduct(Zaxis, Vec), Value, true);
	}

	void AddMovementInput(FVector Direction, float Value, bool bForce) {
		Acceleration += Direction*Value*MovementSpeed;
	}




	float CheckCollision(FVector InVec, float D) {
		if (D <= 0)return 1;
		FVector TraceStart = GetActorLocation();
		FVector TraceEnd = TraceStart + InVec*D;
		FHitResult OutHit;

		FCollisionObjectQueryParams CollisionType = FCollisionObjectQueryParams(ECC_TO_BITFIELD(ECC_WorldStatic) | ECC_TO_BITFIELD(ECC_WorldDynamic));


		GetWorld()->LineTraceSingleByObjectType(OutHit, TraceStart, TraceEnd, CollisionType);
		if (OutHit.bBlockingHit) return OutHit.Distance;
		else return 2 * D;
	}

	void UpdateRotation() {
		if (!GetController()) return;

		OwnerCharacter->SetActorRotation(Zaxis.ToOrientationRotator());
		OwnerCharacter->AddActorLocalRotation(FRotator(-90, 0, 0));
		FRotator Rot = GetController()->GetControlRotation();
		OwnerCharacter->AddActorLocalRotation(FRotator(0, Rot.Yaw, 0));
		GetCameraComponent()->SetRelativeRotation(FRotator(Rot.Pitch, 0, 0));
	}



	void JumpInput() {
		Velocity += Zaxis * JumpVelocity;
	}

	void UpdatePhysics(float DeltaTime) {
		UWorld *World = GetWorld();
		if (World == NULL || DeltaTime == 0.f)return;
		Acceleration += -Zaxis * GravityValue; // part where I apply gravity

		Velocity += Acceleration*DeltaTime;
		Velocity *= FrictionParameter;
		Acceleration = FVector::ZeroVector;

		float H = GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
		float R = GetCapsuleComponent()->GetScaledCapsuleRadius();

		float z0 = CheckCollision(-Zaxis, H);
		float z1 = CheckCollision(Zaxis, H);

		float dot = FVector::DotProduct(Velocity, Zaxis);
		if (z1 < H && dot > 0) Velocity -= dot*Zaxis; //ceiling

		if (dot < 0) {
			bool IsStandingOnFloor = z0 < H;

			if (IsStandingOnFloor) {
				Velocity -= dot*Zaxis;
				SetMovementMode(MOVE_Walking);
			}
			else SetMovementMode(MOVE_Falling);
		}

		for (FVector Axis : {Xaxis, Yaxis}) { //check for walls
			float x0 = CheckCollision(-Axis, R);
			float x1 = CheckCollision(Axis, R);
			dot = FVector::DotProduct(Velocity, Axis);
			if (x0 < R && dot < 0) Velocity -= dot*Axis;
			if (x1 < R && dot > 0) Velocity -= dot*Axis;
		}


		OwnerCharacter->AddActorWorldOffset(Velocity*DeltaTime);

	}
};

(GravityMovementComponent.cpp file)

#include "Gravity.h"
#include "GravityCharacter.h"
#include "GravityMovementComponent.h"


UGravityMovementComponent::UGravityMovementComponent(){
	PrimaryComponentTick.bCanEverTick = true;

	Xaxis = FVector(1, 0, 0);
	Yaxis = FVector(0, 1, 0);
	Zaxis = FVector(0, 0, 1);
}


void UGravityMovementComponent::BeginPlay(){
	Super::BeginPlay();
}


void UGravityMovementComponent::TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction ){
	Super::TickComponent( DeltaTime, TickType, ThisTickFunction );
	UpdateRotation();
	UpdatePhysics(DeltaTime);
}

Ah, I could also include somewhere call to external GravityGameInstance, i thought it would be ok to keep here some global variable. Feel free to write here some const value.