I have a recoil script that brings the mouse down the amount it recoiled when done firing, this however breaks when trying to manually compensate for the recoil. In this case, the camera is moved down farther than it was intended.
Here’s the code:
Recoil Object
extends Node3D
#Rotations
var current_rotation : Vector3
var target_rotation : Vector3
#Recoil vectors
var recoil : Vector3
var aim_recoil : Vector3
#Settings
var snap : float
var return_speed : float
func _process(delta):
# Lerp target rotation to (0,0,0) and lerp current rotation to target rotation
target_rotation = lerp(target_rotation, Vector3.ZERO, return_speed * delta)
current_rotation = lerp(current_rotation, target_rotation, snap * delta)
# Set rotation
rotation = current_rotation
# Camera z axis tilt fix, ignored if tilt intentional
# I have no idea why it tilts if recoil.z is set to 0
if recoil.z == 0 and aim_recoil.z == 0:
global_rotation.z = 0
func fire_recoil(is_aiming : bool = false):
if is_aiming:
target_rotation += Vector3(aim_recoil.x, randf_range(-aim_recoil.y, aim_recoil.y), randf_range(-aim_recoil.z, aim_recoil.z))
else:
target_rotation += Vector3(recoil.x, randf_range(-recoil.y, recoil.y), randf_range(-recoil.z, recoil.z))
func set_recoil(new_recoil : Vector3):
recoil = new_recoil
func set_aim_recoil(new_recoil : Vector3):
aim_recoil = new_recoil
func set_recoil_settings(new_snap:float, new_return_speed:float):
snap = new_snap
return_speed = new_return_speed
Class
class_name Gun extends Node3D
#Signals
signal fired
signal reload
#Refrences
@onready var recoil_handler = get_tree().get_first_node_in_group("recoil")
@onready var animation_player:AnimationPlayer = get_tree().get_first_node_in_group("gun_anim_player")
#Exports
@export_group("Ammo")
@export var mag_size:int
#Firing Group
@export_group("Firing")
#Recoil Vectors
@export var recoil:Vector3
@export var aim_recoil :Vector3
#Recoil Settings
@export var snap:float
@export var return_speed:float
#Auto
@export var auto:bool = true
#Animation
@export_group("Animations")
@export var anim_multi:float = 1
#Variables
@onready var ammo:int = mag_size
#Code
func _ready() -> void:
recoil_handler.set_recoil(recoil)
recoil_handler.set_aim_recoil(aim_recoil)
recoil_handler.set_recoil_settings(snap, return_speed)
animation_player.speed_scale = anim_multi
func _process(_delta: float) -> void:
#Firing
if auto == true:
if Input.is_action_pressed("fired") and ammo > 0:
fired.emit()
else:
if Input.is_action_just_pressed("fired") and ammo > 0:
fired.emit()
#Reloading
if Input.is_action_just_pressed("reload"):
reload.emit()
#Updating UI
global.game.gui.get_children()[0].text = str(ammo)+'/'+str(mag_size)
And Gun Extending The Class
extends Gun
@onready var aim:RayCast3D = $"../../Aim"
var bullet_scene = preload("res://Scenes/BulletHole.tscn")
func _on_fired() -> void:
if not animation_player.is_playing():
if aim.is_colliding():
var end = aim.get_collision_point()
var normal = aim.get_collision_normal()
bullet_decal(end, normal)
recoil_handler.fire_recoil()
animation_player.stop()
animation_player.play("gun_fire")
ammo -= 1
func _on_reload() -> void:
if ammo < mag_size:
animation_player.stop()
animation_player.play("reload")
ammo += 1
func bullet_decal(end, normal):
var bullet_hole = bullet_scene.instantiate()
get_tree().root.add_child(bullet_hole)
bullet_hole.position = end
bullet_hole.look_at(bullet_hole.global_transform.origin + normal, Vector3.UP, )
if normal != Vector3.UP and normal != Vector3.DOWN:
bullet_hole.rotate_object_local(Vector3(1,0,0), 90)
await get_tree().create_timer(1.5).timeout
var fade = get_tree().create_tween()
fade.tween_property(bullet_hole, "modulate:a", 0, 1.5)
await get_tree().create_timer(1.5).timeout
bullet_hole.queue_free()
And here’s the scenes image