Player Spirals Away from Object When Rotating Around it

Hello!

I recently started working on a little game idea I had. In this game, I wanted to have the player character be able to lock on to their opponent and essentially “orbit” them (Think the targeting systems from Dark Souls, Zelda, or most hack-and-slash games).

Because I want to have them orbit another actor, spring arm components aren’t really going to work (as far as I’m aware, considering it would require parenting a bunch of actors together which sounds like a nightmare).

Ideally, the player should be able to move side-to-side in an orbit around the opponent, and should be able to freely move forward/backwards as well (essentially increasing or decreasing the size of the orbit).

I have something that works pretty well right now, but I’ve encountered a very weird issue that I’ve spent hours trying to debug. As the player rotates around the target actor, they spiral outwards.

This is the result of me holding right or left. The rotation works perfectly! However, the player seems to continuously move backwards. I’ve tried just about everything, and the only successful thing was a brute-force method I found of adding a target distance. However, this system made moving forward and backwards troublesome.

The way I went about doing what you see above was basically to re-purpose some of the code already available in the MoveRight function that comes with the character.

void AMyProjectCharacter::MoveRight(float Value)
{
	if ( (Controller != NULL) && (Value != 0.0f) )
	{

		if (lockedTarget) {
			orbitVector = target->GetActorLocation() - GetActorLocation();
			orbitVector.Z = 0.0f;
			
			FRotator rot(0.0f,orbitVector.Rotation().Yaw, 0.0f);

			SetActorRotation(rot);

			FVector Direction = FRotationMatrix(rot).GetUnitAxis(EAxis::Y);
			
			AddMovementInput(Direction, Value);
			
		}
	}
}

Anyone have any ideas as to what might be causing that spiraling effect? Honestly, it’s a pretty cool effect! If I could find a way to control it, I’d have other uses for it. However it’s really frustrating that it’s not what I want, and no matter what I seem to try and change (aside from completely getting rid of everything), nothing seems to affect it.

I believe I’ve pinpointed it down to the Distance FVector, but I’m not sure what else I could do (or what’s going wrong). I tried Flooring the values of orbitVector and rot (Someone suggested it may be a floating point rounding error in another topic), but neither of those did anything. When I tried to floor the values of Direction, I was unable to move at all.

it’s an intresting problem, i suggest you to make a drawing, with frame by frame position simulation, and you will understand what happend and why.

The only way to fix that is to take use the initial distance with the target ( well not the only, but the ‘logic one’ )

and instead of simply

FVector Direction = FRotationMatrix(rot).GetUnitAxis(EAxis::Y); 

`

you would go to an other point of the circle ( or really close to it ), so even if at every frame you have a micro spiral effect starting ( unnoticeable on only one frame ), the next frame will fix it

here some pseudo code ( how i see it )

float _initialDistanceToTarget;
AActor* _target;

void Lock
    Set target & distance

MoveRight(float Value)
	FVector dirToTarget= target->GetActorLocation() - GetActorLocation();
	FVector _projectedPlayerPosition = target->GetActorLocation() - 
	dirToTarget.GetSafeNormal()*_initialDistanceToTarget;

	FVector DirectionToRight = FRotationMatrix(rot).GetUnitAxis(EAxis::Y);

	FVector Direction = (_projectedPlayerPosition + DirectionToRight*20 ) - GetActorLocation();
	Direction.Normalize();
	AddMovementInput(Direction, Value);

th part " DirectionToRight*3 " is the only approximation, to avoid calculate the position of a point on the circle 1 degree to the right of the player, bug should be ‘good enough’

maybe ( most likely ) the *20 is not enough and you will get a strange result if you player is fast so you can push it to 50 or even more ( the bigger the distance from the unit is, this mult will have a less negativ impact on behaviours )

First of all, thank you very much for the reply! I really appreciate it!

I have yet to actually implement this, but I must admit I’m a bit skeptical for one reason: won’t using a variable that indicates the initial distance to the target counteract the idea of being able to freely move inwards and outwards? I want the player to be able to walk closer and further away from the target as well. Their distance won’t always be the initial distance from the target.

I tried adding something similar (as detailed in my original post), but it got extremely messy and impossible to manage. I was adding/subtracting from a target distance when the player moves forward/backwards, but it looked very clunky and didn’t really feel like the player had much control.

Wouldn’t I be running into a similar situation here?

indeeed you are right, but i think you can handle this by updating the “_initialDistanceToTarget” variable when you move forward or backward ( new value would ben the current distance )

variable could be renamed to “_requestedDistanceToTarget”, just set the value and don’t use it for the forward/backward movement )

as it’s frame based even if you move forward and left at the same time it should look ok

as i havn’t tested the code nothing is sure, but sounds like the way i would do it :stuck_out_tongue:

The problem I ran into when implementing that exact solution was that I couldn’t get it to accurately move the player forward and backward by simply updating the distance to target variable in the “MoveForward” function. The problem became this disconnect between the amount of distance that AddMovementInput moved the player, and the amount changed to the distanceToTarget value. I couldn’t seem to figure out a way to consistently sync those up, making for a jarring experience where it only adjusted the player’s position in relation to the distanceToTarget value when they moved left or right

So, for example, the player might move forward, decreasing the distanceToTarget by 10. However, the player actually moved 20 units forward in world space. When the player moves side-to-side, the MoveRight function would adjust their position to move them back to a distance of 10.

This is because the adjustments based on distanceToTarget are only being applied on MoveRight. I was wary of adding an update every tick because I felt like that might be overkill. There HAD to be a better, more mathematical way to counter this spiraling effect. This brute force seemed really messy and made for a lot of spaghetti code.