Download

How to create recoil that's not instant?

I’m currently using the following logic for recoil (recoil is viewkick resulting from firing a bullet). The problem with this is that it moves pitch up instantaneously as opposed to smoothly. Anyone have any idea how to make it move the view smoothly?


FinalRecoil = Recoil * FMath::FRandRange(-1.0f, -1.25f);

MyPawn->APawn::AddControllerPitchInput(FinalRecoil);

I’m running into a similar wall with crouching, trying to make a none instant crouch/prone feature.


isCrouched = !isCrouched;

	if (isCrouched)
	{
		FirstPersonCameraComponent->RelativeLocation = FVector(0, 0, 32.f);
		AddMovementInput(FVector(1, 1, 1), 1);
	}
	else
	{
		FirstPersonCameraComponent->RelativeLocation = FVector(0, 0, 64.f);
		AddMovementInput(FVector(1, 1, 1), 1);
	}

I’m assuming you could use the EvaluateBezier to create an array of FVectors and make a loop to change the camera location to all the points in my scenario. (changing the pitch in your scenario)

You might also be able to do it by loading a line created from blue prints and looping.

But there has to be an easier way, i’ll be looking into the method described above, but am hoping for a simpler solution (or someone more intelligent to do it for me XD).

Also a side note, I’m using the “addmovementinput” to refresh the camera, without it nothing happens, until you either shoot or move to refresh the camera, anyone know of a better way to do this?

Alright I figured it out, the secret was Timers.

All props to this guy, made a pretty bad *** flying aircraft.
https://answers.unrealengine.com/questions/63894/barrel-roll-mechanic.html?sort=oldest

Your going to need to add a Tick method, a timer to the world, a stop recoiling method, and a boolean.

I also added a random recoil to the left and right to give it a little more randomness (but it moves both left and right within one shot, so to make it perfect you would have to add a boolean and another random generator to chose a direction and then apply the random number in that direction, not something that is hard to do, but I didn’t want to over complicate what was asked).

Now of course these are all linear, if you wanted to be crazy you could maybe find a way to manipulate FinalRecoil on each tick to make it more curvy, to simulate something more realistic. Unfortunately the linear movement look very robotic on a crouch feature and I’ll have to implement some sort of curve.



**void AShooterCharacter::Tick(float DeltaSeconds)
{
	if (isRecoiling)
	{
		FinalRecoilPitch = Recoil * FMath::FRandRange(-1.0f, -1.25f);
		//FinalRecoilYaw = Recoil * FMath::FRandRange(5.0f, -5.0f);
		AddControllerPitchInput(FinalRecoilPitch);
		//AddControllerYawInput(FinalRecoilYaw);
	}

	// call the parent class Tick implementation
	Super::Tick(DeltaSeconds);
}**


*void AShooterCharacter::OnFire()
{
	// try and fire a projectile
	if (ProjectileClass != NULL)
	{
		const FRotator SpawnRotation = GetControlRotation();
		// MuzzleOffset is in camera space, so transform it to world space before offsetting from the character location to find the final muzzle position
		const FVector SpawnLocation = GetActorLocation() + SpawnRotation.RotateVector(GunOffset);

		UWorld* const World = GetWorld();
		if (World != NULL)
		{
			// spawn the projectile at the muzzle
			World->SpawnActor<AShooterProjectile>(ProjectileClass, SpawnLocation, SpawnRotation);
		}
	}

	// try and play the sound if specified
	if (FireSound != NULL)
	{
		UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
	}

	// try and play a firing animation if specified
	if(FireAnimation != NULL)
	{
		// Get the animation object for the arms mesh
		UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
		if(AnimInstance != NULL)
		{
			AnimInstance->Montage_Play(FireAnimation, 1.f);
		}
	}*


	**GetWorldTimerManager().SetTimer(this, &AShooterCharacter::StopRecoil, .1f, true);
	isRecoiling = true;**
}

**void AShooterCharacter::StopRecoil()
{

	isRecoiling = false;
	GetWorldTimerManager().ClearTimer(this, &AShooterCharacter::StopRecoil);
}**




(The underlined is what needs to be added, the italicized is part of the default class)

I hope this helps

Thanks. That’s a good start. :slight_smile:

I tried it but it’s not working all that great. It recoils for 100ms with your code once a bullet is fired. Doesn’t matter how many bullets you fire during that time, it’ll continue to randomize recoil for the duration rather than bullets fired. Hmm…

Hmmmm, I’ve fiddled with it a little bit, and think I’ve fixed it, but I don’t think I completely understand what you want.

In the header, I would add recoil rate to Uproperty so you can edit it, to edit it, go to your blueprints and double click on your guy, then find the category recoil.
(Don’t know how common knowledge this is, but I’ve been editing the code numbers then rebuilding every time XD)




	bool isRecoiling;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Recoil)
	float RecoilRate;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Recoil)
	float RecoilPitchTop;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Recoil)
	float RecoilPitchBot;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Recoil)
	float RecoilYawLeft;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Recoil)
	float RecoilYawRight;

	float FinalRecoilPitch;
	float FinalRecoilYaw;
	float FinalRecoilYawDirection;







	RecoilRate = .1f;

	RecoilPitchTop = .25f;
	RecoilPitchBot = -1.25f;

	RecoilYawLeft = -1.f;
	RecoilYawRight = 1.f;

void AShooterCharacter::Tick(float DeltaSeconds)
{
	if (isRecoiling)
	{
		
		AddControllerPitchInput(FinalRecoilPitch);
		AddControllerYawInput(FinalRecoilYaw);
	}

	// call the parent class Tick implementation
	Super::Tick(DeltaSeconds);
}

void AShooterCharacter::OnFire()
{



	Recoil();
	
}

void AShooterCharacter::Recoil()
{
	FinalRecoilPitch = RecoilRate * FMath::FRandRange(0.25f, -1.25f);
	FinalRecoilYaw = RecoilRate * FMath::FRandRange(1.0f, -1.0f);
	GetWorldTimerManager().SetTimer(this, &AShooterCharacter::StopRecoil, .05f, true);
	isRecoiling = true;
}

void AShooterCharacter::StopRecoil()
{

	isRecoiling = false;
	GetWorldTimerManager().ClearTimer(this, &AShooterCharacter::StopRecoil);
}



When you shoot it will start recoiling in a random direction for 50ms, if shot before the timer is done recoiling it adds another 50ms onto the timer and re randoms the direction/rate. The numbers can probably be messed with quite a bit to make it more realistic, I’d also probably add a cap so if you shoot “x” shots it stops recoiling as much, sort of like the character gets control of the gun.

Next step would be to make Recoil to grab values from other guns so you could have custom recoil rates depending on the gun.

I’d also probably add a spread class so the projectile doesn’t always go to the middle, which might be what you were talking about XD.

Doesn’t work at all. “function ‘void AShooterCharacter::Tick(float)’ already has a body”. Also I’m using ShooterWeapon_Instant.cpp since I’m doing it for hitscan weapons.

:EDIT: Messed around and got something going. Using FInterp to smoothen it out. See the same command for your crouch thing. Also if you don’t mind sharing your crouch function I wouldn’t mind. :slight_smile:

You want to add the recoil instantaneously you just don’t want to apply the recoil directly to pitch, but apply it as rotational velocity. Then you can implement a simple spring system that corrects your aiming direction back towards center. That will give you realistic recoil recovery that will also work with any kind of other forces that can affect the character’s view rotation such as falls, being hit, sprinting, etc.

So every tick you want to add the current rotational velocity and solve/apply the spring forces. Solving the spring equation is pretty simple you just want to make sure you use a decent integration method–midpoint euler is probably good enough, but something like RK4 may give slightly more stable results. The springs will automatically stabilize the view back toward the center in a physically plausible way. You can tune the spring constants and applied forces to get the desired effects.

EDIT: I think i understand you now, just no idea how to really implement it. Any idea why

this->RotationRate = FRotator(FinalRecoilPitch, FinalRecoilYaw, 180);
this->AddActorLocalRotation(this->RotationRate * DeltaSeconds, true);

doesn’t work? (it’s in Tick)

To my understanding FInterp (lerp) creates a timer in itself and essentially does the same thing that I did above, only thing is its linear. The general world timer above can be used to make non-linear interpolations, which will be necessary for crouching (might not be needed for recoil).

If we step through what this is doing, it looks like you are changing the rate the character movement component rotates and then manually rotating it by that amount. Usually, RotationRate should be used to automatically rotate the character if either “UseControllerDesiredRotation” or “OrientRotationToMovement” are true. It gets the desired rotation from the controller and rotates the current rotation towards that. Changing it manually each Tick like that could be messing things up a bit.

Hmmm… I wouldn’t mess with the character movement component’s rotation or rotation rate directly, but go to the controller and change it how it tells the character movement component where it should be looking. Then the character controller should take care of it automatically.

I would go to where the controller collects the input, and add recoil there. Add recoil as a direct offset to your input and store it in a spring. It will also affect your aim because it will be altering the players input directly. Just looking through the player controller class I would probably override and add it to both “GetInputMouseDelta” and “GetInputAnalogStickState”. If you wanted to make it work with mobile, you would have to add it to touch input as well, but I don’t immediately see where that is.

What you would do is add two springs to your player controller: one for each axis. When you want to add recoil, simply add the recoil velocity to one or both springs. Each Tick you want to integrate both springs and calculate the spring deltas. Then when you return the mouse or analog stick delta, you simply add the spring delta onto the corresponding axis. It should be a pretty clean place to add the recoil.

Of course, I haven’t tried any of this before and I am relatively new to UE4 so this is kind of my first time actually looking at this source code. So… no warranties or guarantees that this will work or is the best way to do this, but it’s the first thing I would try. Things may not be working quite like I assume they are.

I also have some code I wrote for a simple one-dimensional RK4 spring solver if you want it. It’s in C#, but it should be a relatively trivial task to convert it over to C++. You use it by simply getting/setting Position and Velocity whenever you want, and then calling Spring.Integrate(deltaTime) every Tick so it can update the spring.



public struct SpringState
{
	public float position;
	public float velocity;
}

public class Spring  {
	
	public float springConstant;	
	public float dampening;
	public float minConstraint;
	public float maxConstraint;

	protected SpringState currentState;
	public float Position
	{
		get{return currentState.position;}
		set{currentState.position = value;}
	}
	public float Velocity
	{
		get{return currentState.velocity;}
		set{currentState.velocity = value;}
	}

	public Spring()
	{
	}

	public Spring(float springConstant, float dampening, float minConstraint, float maxConstraint)
	{
		this.springConstant = springConstant;
		this.dampening = dampening;
		this.minConstraint = minConstraint;
		this.maxConstraint = maxConstraint;
	}

	protected float Acceleration(ref SpringState state)
	{
		return (-this.springConstant * state.position - this.dampening*state.velocity);
	}

	protected SpringState Evaluate(ref SpringState initialState)
	{
		SpringState outputDerivative;
		outputDerivative.position = initialState.velocity;
		outputDerivative.velocity = Acceleration(ref initialState);

		return outputDerivative;
	}

	protected SpringState Evaluate(ref SpringState initialState, float deltaTime, ref SpringState derivative)
	{
		SpringState state;
		state.position = initialState.position + derivative.position*deltaTime;
		state.velocity = initialState.velocity + derivative.velocity*deltaTime;

		SpringState outputDerivative;
		outputDerivative.position = state.velocity;
		outputDerivative.velocity = Acceleration(ref state);

		return outputDerivative;
	}

	public void Integrate(float deltaTime)
	{
		SpringState derivativeA = Evaluate(ref currentState);
		SpringState derivativeB = Evaluate(ref currentState, deltaTime * 0.5f, ref derivativeA);
		SpringState derivativeC = Evaluate(ref currentState, deltaTime * 0.5f, ref derivativeB);
		SpringState derivativeD = Evaluate(ref currentState, deltaTime, ref derivativeC);	

		float positionDerivative = 1.0f/6.0f * (derivativeA.position + 2.0f*(derivativeB.position + derivativeC.position) + derivativeD.position);
		float velocityDerivative = 1.0f/6.0f * (derivativeA.velocity + 2.0f*(derivativeB.velocity + derivativeC.velocity) + derivativeD.velocity);

		currentState.position = Mathf.Clamp(currentState.position + positionDerivative*deltaTime, minConstraint, maxConstraint);
		currentState.velocity = currentState.velocity + velocityDerivative*deltaTime;
	}

}


FInterp doesn’t create a timer in itself, it returns a float from the curve specified by DeltaSeconds (I used your tick code’s delta). I don’t however understand the exact logic behind the InterpSpeed value it takes. But the end result is fast change in the beginning and slower in the end so it’s smooth.

I realize this is an old thread, however I was also looking to achieve a similar effect. I discovered a method which works quite nicely for me, hopefully it can help out someone else.

The following code assumes your custom character class is AMyCharacter and your custom player controller class is AMyPlayerController.

Add the following to MyCharacter.h:



float Recoil;            //Stores current negative (upwards pitch rotation) recoil
float RecoilRecovery;    //Stores current positive (downwards pitch rotation) recoil recovery


Override the Tick method within MyCharacter.cpp:



void AMyCharacter::Tick(float DeltaSeconds) {

    Super::Tick(DeltaSeconds);

    AMyPlayerController* MyPC = Cast<AMyPlayerController>(Controller);
    float ApplyPitch;

    Recoil = FMath::FInterpTo(Recoil, 0, DeltaSeconds, 10.0f);
    RecoilRecovery = FMath::FInterpTo(RecoilRecovery, -Recoil, DeltaSeconds, 20.0f);
    ApplyPitch = Recoil + RecoilRecovery;

    if (MyPC) {
        MyPC->AddPitchInput(ApplyPitch);
    }

}


The code should be pretty self explanatory. Simply decrement your character’s Recoil property to by a negative value to instigate the recoil kick (e.g. Recoil -= 0.5). Typically you would do this as soon as your weapon is fired. The view will return back to the original location (spring-like recoil). It works with fully automatic weapons. You can adjust the speed of both interpolations.

Note that this will only apply to the up/down rotation (pitch), however the exact same concept can be used for the left/right rotation (yaw).

^ This.

Another approach is to use vector curves for both the impulse and recovery.

Hi,

Sorry to bump an old, old thread, but following this example here has got me as close to the type of recoil I’m looking for as anything ever has. First of all, I’m using blueprint, and as far as I can see, the code posted by XWILKINX converts pretty much to this. https://blueprintue.com/render/d2ypnab0

It’s working ALMOST perfectly, with 2 problems.

The spring effect is not perfect. Its close, but not perfect, sometimes my characters pitch doesn’t return to where it started. The second thing is that when my character has higher frames, the recoil is stronger. When run at 120 fps, the kick is huge, when run at 20 fps, its almost imperceptible. I thought using the delta seconds from event tick would fix for that.

There’s one thing I’m noticing, and that’s that in the C++ code, you have the final parameter (Interp Speed?) with an f at the end of the number. Does that mean frames? Do I have to do an extra step in blueprint to tell my current recoil vertical that it should be Finterping To at a constant speed of 10frames, not 10float? Or is that just plain wrong?

Anything that points me in the right direction gets you a free copy of my game at launch and your name in the credits :stuck_out_tongue:

Thanks

Hello, the .f or f appended to numbers in C++ simply means that it should be interpreted as a float, not an integer or a double. I would not advice you to use the recoil setup as posted above, the problem with it is that the target is interpolated to zero as the recoil is trying to reach it, if the frame rate gets low enough the recoil could be completely skipped. What would probably work better is to interpolate the current recoil to the target, and when it has been reached set the recoil target to 0. Also don’t add the recoil to the control rotation which can mess with player input, rather just add the recoil as an offset to the camera rotation.

I know this is a very old thread. First of all thank you for this solution. However as stated below, the recoil kick is based on the frame rate. I did some research and in order to not depend on the frame rate, we have to multiply the recoil by DeltaTime after being interp. But what about the recoil recovery ? Since it is interp to recoil - which is already multiplied by DeltaTime. If we multiply this again it will be wrong. Thanks a lot