Implement movement smoothing on Player entity

This commit is contained in:
Squinty 2024-04-19 22:02:37 +00:00
parent 4abe6f8374
commit 01e25ccedf
11 changed files with 587 additions and 77 deletions

21
addons/smoothing/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Lawnjelly
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,7 @@
[plugin]
name="Smoothing"
description="Smoothing nodes for fixed timestep interpolation."
author="Lawnjelly"
version="1.2.1"
script="smoothing_plugin.gd"

View file

@ -0,0 +1,235 @@
# Copyright (c) 2019 Lawnjelly
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
extends Node3D
@export
var target: NodePath:
get:
return target
set(v):
target = v
set_target()
var _m_Target: Node3D
var _m_trCurr: Transform3D
var _m_trPrev: Transform3D
const SF_ENABLED = 1 << 0
const SF_TRANSLATE = 1 << 1
const SF_BASIS = 1 << 2
const SF_SLERP = 1 << 3
const SF_INVISIBLE = 1 << 4
@export_flags("enabled", "translate", "basis", "slerp") var flags: int = SF_ENABLED | SF_TRANSLATE | SF_BASIS:
set(v):
flags = v
# we may have enabled or disabled
_SetProcessing()
get:
return flags
##########################################################################################
# USER FUNCS
# call this checked e.g. starting a level, AFTER moving the target
# so we can update both the previous and current values
func teleport():
var temp_flags = flags
_SetFlags(SF_TRANSLATE | SF_BASIS)
_RefreshTransform()
_m_trPrev = _m_trCurr
# do one frame update to make sure all components are updated
_process(0)
# resume old flags
flags = temp_flags
func set_enabled(bEnable: bool):
_ChangeFlags(SF_ENABLED, bEnable)
_SetProcessing()
func is_enabled():
return _TestFlags(SF_ENABLED)
##########################################################################################
func _ready():
_m_trCurr = Transform3D()
_m_trPrev = Transform3D()
set_process_priority(100)
set_as_top_level(true)
Engine.set_physics_jitter_fix(0.0)
func set_target():
if is_inside_tree():
_FindTarget()
func _set_flags(new_value):
flags = new_value
# we may have enabled or disabled
_SetProcessing()
func _get_flags():
return flags
func _SetProcessing():
var bEnable = _TestFlags(SF_ENABLED)
if _TestFlags(SF_INVISIBLE):
bEnable = false
set_process(bEnable)
set_physics_process(bEnable)
pass
func _enter_tree():
# might have been moved
_FindTarget()
pass
func _notification(what):
match what:
# invisible turns unchecked processing
NOTIFICATION_VISIBILITY_CHANGED:
_ChangeFlags(SF_INVISIBLE, is_visible_in_tree() == false)
_SetProcessing()
func _RefreshTransform():
if _HasTarget() == false:
return
_m_trPrev = _m_trCurr
_m_trCurr = _m_Target.global_transform
func _FindTarget():
_m_Target = null
# If no target has been assigned in the property,
# default to using the parent as the target.
if target.is_empty():
var parent = get_parent_node_3d()
if parent:
_m_Target = parent
return
var targ = get_node(target)
if ! targ:
printerr("ERROR SmoothingNode : Target " + str(target) + " not found")
return
if not targ is Node3D:
printerr("ERROR SmoothingNode : Target " + str(target) + " is not node 3D")
target = ""
return
# if we got to here targ is a spatial
_m_Target = targ
# certain targets are disallowed
if _m_Target == self:
var msg = str(_m_Target.get_name()) + " assigned to " + str(self.get_name()) + "]"
printerr("ERROR SmoothingNode : Target should not be self [", msg)
# error message
#OS.alert("Target cannot be a parent or grandparent in the scene tree.", "SmoothingNode")
_m_Target = null
target = ""
return
func _HasTarget() -> bool:
if _m_Target == null:
return false
# has not been deleted?
if is_instance_valid(_m_Target):
return true
_m_Target = null
return false
func _process(_delta):
var f = Engine.get_physics_interpolation_fraction()
var tr: Transform3D = Transform3D()
# translate
if _TestFlags(SF_TRANSLATE):
var ptDiff = _m_trCurr.origin - _m_trPrev.origin
tr.origin = _m_trPrev.origin + (ptDiff * f)
# rotate
if _TestFlags(SF_BASIS):
if _TestFlags(SF_SLERP):
tr.basis = _m_trPrev.basis.slerp(_m_trCurr.basis, f)
else:
tr.basis = _LerpBasis(_m_trPrev.basis, _m_trCurr.basis, f)
transform = tr
func _physics_process(_delta):
_RefreshTransform()
func _LerpBasis(from: Basis, to: Basis, f: float) -> Basis:
var res: Basis = Basis()
res.x = from.x.lerp(to.x, f)
res.y = from.y.lerp(to.y, f)
res.z = from.z.lerp(to.z, f)
return res
func _SetFlags(f):
flags |= f
func _ClearFlags(f):
flags &= ~f
func _TestFlags(f):
return (flags & f) == f
func _ChangeFlags(f, bSet):
if bSet:
_SetFlags(f)
else:
_ClearFlags(f)

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

View file

@ -0,0 +1,212 @@
# Copyright (c) 2019 Lawnjelly
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
extends Node2D
@export
var target: NodePath:
get:
return target
set(v):
target = v
if is_inside_tree():
_FindTarget()
var _m_Target: Node2D
var _m_Flip:bool = false
var _m_Trans_curr: Transform2D = Transform2D()
var _m_Trans_prev: Transform2D = Transform2D()
const SF_ENABLED = 1 << 0
const SF_GLOBAL_IN = 1 << 1
const SF_GLOBAL_OUT = 1 << 2
const SF_TOP_LEVEL = 1 << 3
const SF_INVISIBLE = 1 << 4
@export_flags("enabled", "global in", "global out", "top level") var flags: int = SF_ENABLED | SF_GLOBAL_IN | SF_GLOBAL_OUT:
set(v):
flags = v
# we may have enabled or disabled
_SetProcessing()
get:
return flags
##########################################################################################
# USER FUNCS
# call this checked e.g. starting a level, AFTER moving the target
# so we can update both the previous and current values
func teleport():
_RefreshTransform()
_m_Trans_prev = _m_Trans_curr
# call frame upate to make sure all components of the node are set
_process(0)
func set_enabled(bEnable: bool):
_ChangeFlags(SF_ENABLED, bEnable)
_SetProcessing()
func is_enabled():
return _TestFlags(SF_ENABLED)
##########################################################################################
func _ready():
set_process_priority(100)
Engine.set_physics_jitter_fix(0.0)
set_as_top_level(_TestFlags(SF_TOP_LEVEL))
func _SetProcessing():
var bEnable = _TestFlags(SF_ENABLED)
if _TestFlags(SF_INVISIBLE):
bEnable = false
set_process(bEnable)
set_physics_process(bEnable)
set_as_top_level(_TestFlags(SF_TOP_LEVEL))
func _enter_tree():
# might have been moved
_FindTarget()
func _notification(what):
match what:
# invisible turns unchecked processing
NOTIFICATION_VISIBILITY_CHANGED:
_ChangeFlags(SF_INVISIBLE, is_visible_in_tree() == false)
_SetProcessing()
func _RefreshTransform():
if _HasTarget() == false:
return
_m_Trans_prev = _m_Trans_curr
if _TestFlags(SF_GLOBAL_IN):
_m_Trans_curr = _m_Target.get_global_transform()
else:
_m_Trans_curr = _m_Target.get_transform()
_m_Flip = false
# Ideally we would use determinant core function, as in commented line below, but we
# need to workaround for backward compat.
# if (_m_Trans_prev.determinant() < 0) != (_m_Trans_curr.determinant() < 0):
if (_Determinant_Sign(_m_Trans_prev) != _Determinant_Sign(_m_Trans_curr)):
_m_Flip = true
func _Determinant_Sign(t:Transform2D)->bool:
# Workaround Transform2D determinant function not being available
# until 3.6 / 4.1.
# We calculate determinant manually, slower but compatible to lower
# godot versions.
var d = (t.x.x * t.y.y) - (t.x.y * t.y.x)
return d >= 0.0
func _FindTarget():
_m_Target = null
# If no target has been assigned in the property,
# default to using the parent as the target.
if target.is_empty():
var parent = get_parent()
if parent and (parent is Node2D):
_m_Target = parent
return
var targ = get_node(target)
if ! targ:
printerr("ERROR SmoothingNode2D : Target " + str(target) + " not found")
return
if not targ is Node2D:
printerr("ERROR SmoothingNode2D : Target " + str(target) + " is not Node2D")
target = ""
return
# if we got to here targ is correct type
_m_Target = targ
func _HasTarget() -> bool:
if _m_Target == null:
return false
# has not been deleted?
if is_instance_valid(_m_Target):
return true
_m_Target = null
return false
func _process(_delta):
var f = Engine.get_physics_interpolation_fraction()
var tr = Transform2D()
tr.origin = lerp(_m_Trans_prev.origin, _m_Trans_curr.origin, f)
tr.x = lerp(_m_Trans_prev.x, _m_Trans_curr.x, f)
tr.y = lerp(_m_Trans_prev.y, _m_Trans_curr.y, f)
# When a sprite flip is detected, turn off interpolation for that tick.
if _m_Flip:
tr = _m_Trans_curr
if _TestFlags(SF_GLOBAL_OUT) and not _TestFlags(SF_TOP_LEVEL):
set_global_transform(tr)
else:
set_transform(tr)
func _physics_process(_delta):
_RefreshTransform()
func _SetFlags(f):
flags |= f
func _ClearFlags(f):
flags &= ~f
func _TestFlags(f):
return (flags & f) == f
func _ChangeFlags(f, bSet):
if bSet:
_SetFlags(f)
else:
_ClearFlags(f)

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

View file

@ -0,0 +1,17 @@
@tool
extends EditorPlugin
func _enter_tree():
# Initialization of the plugin goes here
# Add the new type with a name, a parent type, a script and an icon
add_custom_type("Smoothing", "Node3D", preload("smoothing.gd"), preload("smoothing.png"))
add_custom_type("Smoothing2D", "Node2D", preload("smoothing_2d.gd"), preload("smoothing_2d.png"))
pass
func _exit_tree():
# Clean-up of the plugin goes here
# Always remember to remove_at it from the engine when deactivated
remove_custom_type("Smoothing")
remove_custom_type("Smoothing2D")

View file

@ -42,17 +42,17 @@ enum PlayerState { PLAYER_ALIVE, PLAYER_DEAD }
@export var nickname : String
@onready var input : PlayerInput = $PlayerInput
@onready var camera : Camera3D = $SpringArm3D/Camera3D
@onready var camera : Camera3D = $Smoothing/SpringArm3D/Camera3D
@onready var hud : CanvasLayer = $HUD
@onready var shape_cast : ShapeCast3D = $ShapeCast3D
@onready var weapon : Node3D = $SpringArm3D/Inventory/SpaceGun
@onready var weapon : Node3D = $Smoothing/SpringArm3D/Inventory/SpaceGun
@onready var animation_player : AnimationPlayer = $AnimationPlayer
@onready var health_component : Area3D = $HealthComponent
@onready var collision_shape : CollisionShape3D = $CollisionShape3D
@onready var flag_carry_component : FlagCarryComponent = $FlagCarryComponent
@onready var spring_arm_height : float = $SpringArm3D.position.y
@onready var spring_arm_height : float = $Smoothing/SpringArm3D.position.y
@onready var _original_weapon_transform : Transform3D = weapon.transform
@onready var flag_carry_attachment : Node3D = $SpringArm3D/FlagCarryAttachment
@onready var flag_carry_attachment : Node3D = $Smoothing/SpringArm3D/FlagCarryAttachment
@onready var _game_settings : Settings = get_node("/root/GlobalSettings")
signal died(player : Player)
@ -68,24 +68,25 @@ func _ready() -> void:
health_component.health_changed.connect(iff._on_health_changed)
health_component.health_changed.emit(health_component.health)
health_component.health_zeroed.connect(die)
input.fired_primary.connect(_fire_primary)
input.jumped.connect(_jump)
input.throwed_flag.connect(_throw_flag)
input.MOUSE_SENSITIVITY = _game_settings.mouse_sensitivity
input.inverted_y_axis = _game_settings.inverted_y_axis
if _is_pawn():
camera.current = true
camera.fov = _game_settings.fov
# set the spring arm translation to be about head height level
$SpringArm3D.transform = Transform3D().translated(Vector3(0, collision_shape.shape.height / 2, 0) * 0.9)
$Smoothing/SpringArm3D.transform = Transform3D().translated(Vector3(0, collision_shape.shape.height / 2, 0) * 0.9)
flag_carry_attachment.hide()
remove_child($ThirdPerson)
$Smoothing.remove_child($Smoothing/ThirdPerson)
else:
# set the iff attachment translation to be about head height level
$ThirdPerson/IFFAttachment.transform = Transform3D().translated(Vector3(0, collision_shape.shape.height / 2, 0) * 0.9)
$Smoothing/ThirdPerson/IFFAttachment.transform = Transform3D().translated(Vector3(0, collision_shape.shape.height / 2, 0) * 0.9)
remove_child(hud)
weapon.hide()
@ -128,13 +129,17 @@ func _handle_aerial_control(direction : Vector3) -> void:
if not input.jetting and not is_on_floor():
apply_force(direction * aerial_control_force)
func _handle_jetpack(delta : float, direction : Vector3) -> void:
func _handle_jetpack(direction : Vector3) -> void:
if input.jetting:
if energy > 0:
var up_vector : Vector3 = Vector3.UP * jetpack_vertical_force * jetpack_force_factor
var side_vector : Vector3 = direction * jetpack_horizontal_force * jetpack_force_factor
apply_force(up_vector + side_vector)
energy -= energy_drain_rate * delta
func _update_jetpack_energy(delta : float) -> void:
if input.jetting:
if energy > 0:
energy -= energy_drain_rate * delta
else:
energy += energy_charge_rate * delta
@ -148,7 +153,7 @@ func _process(_delta : float) -> void:
else:
iff.show()
if not _is_pawn():
$ThirdPerson/PlayerMesh.global_transform.basis = Basis.from_euler(Vector3(0.0, input.camera_rotation.x + PI, 0.0))
$Smoothing/ThirdPerson/PlayerMesh.global_transform.basis = Basis.from_euler(Vector3(0.0, input.camera_rotation.x + PI, 0.0))
else:
if animation_player.current_animation == "shoot":
pass
@ -157,20 +162,24 @@ func _process(_delta : float) -> void:
%SpringArm3D.global_transform.basis = Basis.from_euler(Vector3(input.camera_rotation.y, input.camera_rotation.x, 0.0))
func _physics_process(delta : float) -> void:
_update_jetpack_energy(delta)
func _handle_movement() -> void:
# retrieve user's direction vector
var _input_dir : Vector2 = input.direction
# compute direction in local space
var _direction : Vector3 = (transform.basis * Vector3(_input_dir.x, 0, _input_dir.y)).normalized()
_update_third_person_animations()
if _is_player_dead():
return
# adjust direction based on spring arm rotation
_direction = _direction.rotated(Vector3.UP, $SpringArm3D.rotation.y)
_direction = _direction.rotated(Vector3.UP, $Smoothing/SpringArm3D.rotation.y)
_handle_aerial_control(_direction)
_handle_jetpack(delta, _direction)
_handle_jetpack(_direction)
# handle ski
if _is_skiing():
@ -191,18 +200,20 @@ func _physics_process(delta : float) -> void:
linear_velocity = lerp(linear_velocity, _direction * ground_speed, .1)
func _integrate_forces(_state : PhysicsDirectBodyState3D) -> void:
if is_on_floor() and _jumping:
var v : float = sqrt(2 * g * jump_height)
apply_central_impulse(Vector3(0, mass * v, 0))
if _jumping:
var v : float = sqrt(2 * g * jump_height)
apply_central_impulse(Vector3(0, mass * v, 0))
_jumping = false
func _integrate_forces(_state : PhysicsDirectBodyState3D) -> void:
_handle_movement()
func _update_third_person_animations() -> void:
if _is_pawn():
return
var tp_player : Vanguard = $ThirdPerson/PlayerMesh
var tp_player : Vanguard = $Smoothing/ThirdPerson/PlayerMesh
if _is_player_dead():
tp_player.set_ground_state(Vanguard.GroundState.GROUND_STATE_DEAD)

View file

@ -1,15 +1,16 @@
[gd_scene load_steps=21 format=3 uid="uid://cbhx1xme0sb7k"]
[gd_scene load_steps=22 format=3 uid="uid://cbhx1xme0sb7k"]
[ext_resource type="Script" path="res://entities/player/player.gd" id="1_mk68k"]
[ext_resource type="PackedScene" uid="uid://drbefw6akui2v" path="res://entities/player/assets/vanguard.tscn" id="2_beyex"]
[ext_resource type="Shape3D" uid="uid://cb8esdlnottdn" path="res://entities/player/collision_shape.tres" id="2_vjqny"]
[ext_resource type="PackedScene" uid="uid://bcv81ku26xo" path="res://interfaces/hud/hud.tscn" id="3_ccety"]
[ext_resource type="PackedScene" uid="uid://c8co0qa2omjmh" path="res://entities/weapons/space_gun/space_gun.tscn" id="4_ehwwq"]
[ext_resource type="PackedScene" uid="uid://c8co0qa2omjmh" path="res://entities/weapons/space_gun/space_gun.tscn" id="4_6jh57"]
[ext_resource type="PackedScene" uid="uid://dn1tcakam5egs" path="res://entities/weapons/space_gun/projectile.tscn" id="5_2xh36"]
[ext_resource type="PackedScene" uid="uid://bof3mg7wgxrmn" path="res://components/health_component.tscn" id="5_t6i6e"]
[ext_resource type="PackedScene" uid="uid://dn1tcakam5egs" path="res://entities/weapons/space_gun/projectile.tscn" id="5_w56pa"]
[ext_resource type="Script" path="res://entities/player/player_input.gd" id="6_ymcrr"]
[ext_resource type="PackedScene" uid="uid://dsysi2rd3bu76" path="res://interfaces/hud/iff.tscn" id="7_8hc80"]
[ext_resource type="PackedScene" uid="uid://2t8ql8pkxv6c" path="res://components/flag_carry_component.tscn" id="7_e7s1a"]
[ext_resource type="Script" path="res://addons/smoothing/smoothing.gd" id="11_k330l"]
[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_clur0"]
resource_local_to_scene = true
@ -27,7 +28,7 @@ length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("SpringArm3D/Inventory/SpaceGun:position")
tracks/0/path = NodePath("Smoothing/SpringArm3D/Inventory/SpaceGun:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
@ -39,7 +40,7 @@ tracks/0/keys = {
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("SpringArm3D/Inventory/SpaceGun:rotation")
tracks/1/path = NodePath("Smoothing/SpringArm3D/Inventory/SpaceGun:rotation")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
@ -51,7 +52,7 @@ tracks/1/keys = {
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("SpringArm3D:position")
tracks/2/path = NodePath("Smoothing/SpringArm3D:position")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
@ -63,7 +64,7 @@ tracks/2/keys = {
tracks/3/type = "value"
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/path = NodePath("SpringArm3D:rotation")
tracks/3/path = NodePath("Smoothing/SpringArm3D:rotation")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/keys = {
@ -78,7 +79,7 @@ resource_name = "death"
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("SpringArm3D:position")
tracks/0/path = NodePath("Smoothing/SpringArm3D:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
@ -90,7 +91,7 @@ tracks/0/keys = {
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("SpringArm3D:rotation")
tracks/1/path = NodePath("Smoothing/SpringArm3D:rotation")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
@ -107,7 +108,7 @@ loop_mode = 1
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("SpringArm3D/Inventory/SpaceGun:position")
tracks/0/path = NodePath("Smoothing/SpringArm3D/Inventory/SpaceGun:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
@ -119,7 +120,7 @@ tracks/0/keys = {
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("SpringArm3D/Inventory/SpaceGun:rotation")
tracks/1/path = NodePath("Smoothing/SpringArm3D/Inventory/SpaceGun:rotation")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
@ -134,7 +135,7 @@ resource_name = "shoot"
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("SpringArm3D/Inventory/SpaceGun:position")
tracks/0/path = NodePath("Smoothing/SpringArm3D/Inventory/SpaceGun:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
@ -146,7 +147,7 @@ tracks/0/keys = {
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("SpringArm3D/Inventory/SpaceGun:rotation")
tracks/1/path = NodePath("Smoothing/SpringArm3D/Inventory/SpaceGun:rotation")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
@ -205,9 +206,10 @@ axis_lock_angular_y = true
axis_lock_angular_z = true
mass = 75.0
physics_material_override = SubResource("PhysicsMaterial_clur0")
can_sleep = false
continuous_cd = true
script = ExtResource("1_mk68k")
iff = NodePath("ThirdPerson/IFFAttachment/IFF")
iff = NodePath("Smoothing/ThirdPerson/IFF")
jump_height = 1.5
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
@ -234,49 +236,15 @@ shape = ExtResource("2_vjqny")
[node name="HUD" parent="." instance=ExtResource("3_ccety")]
[node name="SpringArm3D" type="SpringArm3D" parent="."]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
spring_length = 0.0
[node name="Camera3D" type="Camera3D" parent="SpringArm3D"]
fov = 100.0
near = 0.1
[node name="Inventory" type="Node3D" parent="SpringArm3D"]
[node name="SpaceGun" parent="SpringArm3D/Inventory" instance=ExtResource("4_ehwwq")]
transform = Transform3D(-1, 0, 2.53518e-06, 0, 1, 0, -2.53518e-06, 0, -1, 0.244668, -0.229311, -0.30332)
PROJECTILE = ExtResource("5_w56pa")
[node name="SpineIKTarget" type="Node3D" parent="SpringArm3D"]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
[node name="FlagCarryAttachment" type="Node3D" parent="SpringArm3D"]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, -0.620994, -0.287925)
[node name="HealthComponent" parent="." instance=ExtResource("5_t6i6e")]
[node name="CollisionShape3D" type="CollisionShape3D" parent="HealthComponent"]
shape = ExtResource("2_vjqny")
[node name="FlagCarryComponent" parent="." node_paths=PackedStringArray("attachment", "sensor") instance=ExtResource("7_e7s1a")]
attachment = NodePath("../SpringArm3D/FlagCarryAttachment")
attachment = NodePath("../Smoothing/SpringArm3D/FlagCarryAttachment")
sensor = NodePath("../Sensor")
[node name="ThirdPerson" type="Node3D" parent="."]
[node name="IFFAttachment" type="Marker3D" parent="ThirdPerson"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.1, 0)
[node name="IFF" parent="ThirdPerson/IFFAttachment" node_paths=PackedStringArray("player", "attach_point") instance=ExtResource("7_8hc80")]
player = NodePath("../../..")
attach_point = NodePath("..")
[node name="PlayerMesh" parent="ThirdPerson" node_paths=PackedStringArray("spine_ik_target_attachment") instance=ExtResource("2_beyex")]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
spine_ik_target_attachment = NodePath("../../SpringArm3D/SpineIKTarget")
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
libraries = {
"": SubResource("AnimationLibrary_hg307")
@ -291,3 +259,42 @@ replication_config = SubResource("SceneReplicationConfig_rqdp6")
root_path = NodePath(".")
replication_config = SubResource("SceneReplicationConfig_5j4ew")
script = ExtResource("6_ymcrr")
[node name="Smoothing" type="Node3D" parent="."]
script = ExtResource("11_k330l")
target = NodePath("..")
flags = 3
[node name="SpringArm3D" type="SpringArm3D" parent="Smoothing"]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
spring_length = 0.0
[node name="Camera3D" type="Camera3D" parent="Smoothing/SpringArm3D"]
fov = 100.0
near = 0.1
[node name="Inventory" type="Node3D" parent="Smoothing/SpringArm3D"]
[node name="SpaceGun" parent="Smoothing/SpringArm3D/Inventory" instance=ExtResource("4_6jh57")]
transform = Transform3D(-1, 0, 2.53518e-06, 0, 1, 0, -2.53518e-06, 0, -1, 0.244668, -0.229311, -0.30332)
PROJECTILE = ExtResource("5_2xh36")
[node name="SpineIKTarget" type="Node3D" parent="Smoothing/SpringArm3D"]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
[node name="FlagCarryAttachment" type="Node3D" parent="Smoothing/SpringArm3D"]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, -0.620994, -0.287925)
[node name="ThirdPerson" type="Node3D" parent="Smoothing"]
[node name="PlayerMesh" parent="Smoothing/ThirdPerson" node_paths=PackedStringArray("spine_ik_target_attachment") instance=ExtResource("2_beyex")]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
spine_ik_target_attachment = NodePath("../../SpringArm3D/SpineIKTarget")
[node name="IFFAttachment" type="Marker3D" parent="Smoothing/ThirdPerson"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.27457, 0)
[node name="IFF" parent="Smoothing/ThirdPerson" node_paths=PackedStringArray("player", "attach_point") instance=ExtResource("7_8hc80")]
player = NodePath("../../..")
attach_point = NodePath("../IFFAttachment")

View file

@ -30,11 +30,11 @@ extends Control
func _ready() -> void:
_player_name_label.text = player.name
func _process(_delta : float) -> void:
# retrieve camera for current viewport
var camera : Camera3D = get_viewport().get_camera_3d()
# if the player is NOT infront of the camera or camera does NOT have LOS to player
if camera.is_position_behind(attach_point.global_position) or !is_within_los:
_iff_container.hide() # hide the IFF and exit function
@ -60,21 +60,21 @@ func _physics_process(_delta : float) -> void:
var new_iff_position : Vector2 = camera.unproject_position(attach_point.global_position) # get the screen location of the players head
var viewport_size : Vector2 = get_viewport_rect().size
# check if the unprojected point lies inside the viewport
if !Rect2(Vector2(0, 0), viewport_size).has_point(new_iff_position):
_iff_container.hide() # hide the IFF and exit function
return
# if player is NOT in range to have its healthbar and name drawn
if (new_iff_position - viewport_size / 2).length() > iff_in_range_radius_ratio * viewport_size.length():
_iff_in_range_container.hide() # hide the in range IFF
else:
_iff_in_range_container.show()
new_iff_position.y -= _iff_container.size.y # move the IFF up so the bottom of it is at the players head
new_iff_position.x -= _iff_container.size.x / 2 # move the IFF left so it's centered horizontally above players head
position = new_iff_position # set the position of the IFF
func _update_health_bar(health : float) -> void:
@ -86,6 +86,6 @@ func _update_health_bar(health : float) -> void:
healthbar_fill_stylebox.bg_color = healthbar_low_health_color
else:
healthbar_fill_stylebox.bg_color = healthbar_high_health_color
func _on_health_changed(new_health : float) -> void:
_update_health_bar(new_health)

View file

@ -47,7 +47,7 @@ movie_writer/disable_vsync=true
[editor_plugins]
enabled=PackedStringArray("res://addons/gut/plugin.cfg", "res://addons/terrain_3d/plugin.cfg")
enabled=PackedStringArray("res://addons/gut/plugin.cfg", "res://addons/smoothing/plugin.cfg", "res://addons/terrain_3d/plugin.cfg")
[input]