How to implement gun recoil

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By tproper

I’m using a first person controller like Jeremy Bullock’s tutorial and I am trying to make my camera recoil up and to the right when shooting a gun.

Would that be ‘apply_impulse’ to get that kind of movement? I want the gun to recoil but then fall back into position before the gun can be fired again.

Any ideas on how to accomplish this?

:bust_in_silhouette: Reply From: tproper

Found something that works for me…I do not think it’s the best solution by far but, as I said, I think it will work for me. I just used a timer to pull the cam up for a short time then stop. I decided not to drop the gun back down cause it seemed unnatural.

var recoil = 4

func aim():

    if recoiling.length():
	    camera_change += recoiling

    if camera_change.length() > 0:
	    $Head.rotate_y(deg2rad((-camera_change.x) * mouse_sensitivity))

	var change = camera_change.y * mouse_sensitivity
	if change + camera_angle < 90 and change + camera_angle > -90:
		$Head/MainCamera.rotate_x(deg2rad(change))
		camera_angle += change
	camera_change = Vector2()

func recoil_timeout():
	if recoiling == Vector2(recoil,-recoil - 2):
		recoiling = Vector2(0, 0)
		remove_child(recoil_timer)

func _process(delta):
	
	if Input.is_action_just_pressed("shoot"):
		
		var shot_bullet = bullet.instance()
		shot_bullet.rotation = $Head/MainCamera/Pistol/BulletEmitter.rotation
		shot_bullet.set_linear_velocity($Head/MainCamera/Pistol/BulletEmitter.get_global_transform().basis.z * SHOOT_SPEED)
		$Head/MainCamera/Pistol/BulletEmitter.add_child(shot_bullet)
		
		recoiling = Vector2(recoil, -recoil - 2)
		recoil_timer = Timer.new()
		recoil_timer.connect("timeout",self, "recoil_timeout")
		recoil_timer.set_wait_time(0.1)
		add_child(recoil_timer)
		recoil_timer.start()
:bust_in_silhouette: Reply From: xm_arch

If anyone finds this thread and is interested in other methods, the best one I found after quite a few days testing different approaches is to use an AnimationPlayer with two bezier curve tracks which animate two floats that you can then use to rotate camera’s x and y axis respectively based on whether or not you are in a recoiling state. Then you can just start, stop, and reset the animation based on if fire is pressed or what # bullet number is shot, etc. If you want to animate the ‘return’ state you’ll probably want to use a tween instead of an AnimationPlayer. You won’t know how much to move the rotation back ahead of time if you are compensating for player input, so adding incrementally to a ‘total recoil’ var then subtracting from that with player input rotation will make the ‘return’ distance shorter the better the player fights the recoil. Once you get used to drawing in the bezier curves (realizing you’re drawing the acceleration of the rotation here by changing the velocity of the rotation) it becomes quite easy and fun to ‘draw’ varying recoil patterns that the player can then try to fight against (it’s helpful to have a recoil_strength multiplier that multiplies the recoil rotation variable as well for fine-tuning). In retrospect I’m frustrated I spent so much time trying other implementations when this one was the simplest to do, and has the most control. Good luck!