├── icon.png
├── smooth_icon.png
├── addons
└── smoothing
│ ├── smoothing.png
│ ├── smoothing_2d.png
│ ├── plugin.cfg
│ ├── smoothing_plugin.gd
│ ├── smoothing.png.import
│ ├── smoothing_2d.png.import
│ ├── LICENSE
│ ├── smoothing.gd
│ └── smoothing_2d.gd
├── default_env.tres
├── Target2D.gd
├── Target2D_flipped.gd
├── Root.gd
├── export_presets.cfg
├── icon.png.import
├── smooth_icon.png.import
├── project.godot
├── Target.gd
├── LICENSE
├── Root.tscn
└── README.md
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lawnjelly/smoothing-addon/HEAD/icon.png
--------------------------------------------------------------------------------
/smooth_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lawnjelly/smoothing-addon/HEAD/smooth_icon.png
--------------------------------------------------------------------------------
/addons/smoothing/smoothing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lawnjelly/smoothing-addon/HEAD/addons/smoothing/smoothing.png
--------------------------------------------------------------------------------
/addons/smoothing/smoothing_2d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lawnjelly/smoothing-addon/HEAD/addons/smoothing/smoothing_2d.png
--------------------------------------------------------------------------------
/default_env.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="Environment" load_steps=2 format=2]
2 |
3 | [sub_resource type="ProceduralSky" id=1]
4 |
5 | [resource]
6 | background_mode = 2
7 | background_sky = SubResource( 1 )
8 |
--------------------------------------------------------------------------------
/addons/smoothing/plugin.cfg:
--------------------------------------------------------------------------------
1 | [plugin]
2 |
3 | name="Smoothing"
4 | description="Smoothing nodes for fixed timestep interpolation."
5 | author="Lawnjelly"
6 | version="1.2.1"
7 | script="smoothing_plugin.gd"
8 |
--------------------------------------------------------------------------------
/Target2D.gd:
--------------------------------------------------------------------------------
1 | extends Node2D
2 |
3 | const MOVE_DIST = 100
4 | var m_Dir = MOVE_DIST
5 |
6 | func _physics_process(_delta):
7 | var x = get_position().x
8 |
9 | if x > 1000:
10 | m_Dir = -MOVE_DIST
11 | if x < 0:
12 | m_Dir = MOVE_DIST
13 |
14 | x += m_Dir * _delta
15 |
16 | position.x = x
17 |
18 | rotate(_delta)
19 |
20 | var sc = x / 1000.0
21 |
22 | set_scale(Vector2(sc * 3, (1.0 - sc) * 3))
23 |
24 | pass
25 |
--------------------------------------------------------------------------------
/Target2D_flipped.gd:
--------------------------------------------------------------------------------
1 | extends Node2D
2 |
3 | var _x = 0
4 | var _right = true
5 |
6 | func _ready():
7 | position = Vector2(200, 100)
8 | set_scale(Vector2(-1, 1))
9 |
10 |
11 | func _physics_process(delta):
12 |
13 | if _right:
14 | _x += 100
15 | if _x >= 800:
16 | _right = false
17 | set_scale(Vector2(1, 1))
18 | else:
19 | _x -= 100
20 | if _x <= 200:
21 | _right = true
22 | set_scale(Vector2(-1, 1))
23 |
24 | position.x = _x
25 |
26 | pass
27 |
--------------------------------------------------------------------------------
/Root.gd:
--------------------------------------------------------------------------------
1 | extends Spatial
2 |
3 |
4 | # Called every frame. 'delta' is the elapsed time since the previous frame.
5 | func _process(_delta):
6 | if Input.is_action_just_pressed("ui_cancel"):
7 | get_tree().quit()
8 |
9 | if Input.is_action_just_pressed("ui_select"):
10 | $Example3D/Target.translation = Vector3(0, 0, 0)
11 | $Example3D/Target/Smoothing.teleport()
12 |
13 | $Example2D/Target2D.position = Vector2(300, 300)
14 | $Example2D/Target2D/Smoothing2D.teleport()
15 |
--------------------------------------------------------------------------------
/addons/smoothing/smoothing_plugin.gd:
--------------------------------------------------------------------------------
1 | tool
2 | extends EditorPlugin
3 |
4 |
5 | func _enter_tree():
6 | # Initialization of the plugin goes here
7 | # Add the new type with a name, a parent type, a script and an icon
8 | add_custom_type("Smoothing", "Spatial", preload("smoothing.gd"), preload("smoothing.png"))
9 | add_custom_type("Smoothing2D", "Node2D", preload("smoothing_2d.gd"), preload("smoothing_2d.png"))
10 | pass
11 |
12 |
13 | func _exit_tree():
14 | # Clean-up of the plugin goes here
15 | # Always remember to remove it from the engine when deactivated
16 | remove_custom_type("Smoothing")
17 | remove_custom_type("Smoothing2D")
18 |
--------------------------------------------------------------------------------
/export_presets.cfg:
--------------------------------------------------------------------------------
1 | [preset.0]
2 |
3 | name="Linux/X11"
4 | platform="Linux/X11"
5 | runnable=true
6 | custom_features=""
7 | export_filter="all_resources"
8 | include_filter=""
9 | exclude_filter=""
10 | export_path="/home/baron/Pel/Godot/Export/Tests/SmoothPlug/Smooth Plug.x86_64"
11 | patch_list=PoolStringArray( )
12 | script_export_mode=1
13 | script_encryption_key=""
14 |
15 | [preset.0.options]
16 |
17 | texture_format/bptc=false
18 | texture_format/s3tc=true
19 | texture_format/etc=false
20 | texture_format/etc2=false
21 | texture_format/no_bptc_fallbacks=true
22 | binary_format/64_bits=true
23 | custom_template/release="/home/baron/Apps/Fork/godot/bin/godot.x11.opt.64"
24 | custom_template/debug=""
25 |
--------------------------------------------------------------------------------
/icon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="StreamTexture"
5 | path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex"
6 | metadata={
7 | "vram_texture": false
8 | }
9 |
10 | [deps]
11 |
12 | source_file="res://icon.png"
13 | dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ]
14 |
15 | [params]
16 |
17 | compress/mode=0
18 | compress/lossy_quality=0.7
19 | compress/hdr_mode=0
20 | compress/bptc_ldr=0
21 | compress/normal_map=0
22 | flags/repeat=0
23 | flags/filter=true
24 | flags/mipmaps=false
25 | flags/anisotropic=false
26 | flags/srgb=2
27 | process/fix_alpha_border=true
28 | process/premult_alpha=false
29 | process/HDR_as_SRGB=false
30 | process/invert_color=false
31 | process/normal_map_invert_y=false
32 | stream=false
33 | size_limit=0
34 | detect_3d=true
35 | svg/scale=1.0
36 |
--------------------------------------------------------------------------------
/smooth_icon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="StreamTexture"
5 | path="res://.import/smooth_icon.png-11111123e491159fd0c2d41967dd2dbc.stex"
6 | metadata={
7 | "vram_texture": false
8 | }
9 |
10 | [deps]
11 |
12 | source_file="res://smooth_icon.png"
13 | dest_files=[ "res://.import/smooth_icon.png-11111123e491159fd0c2d41967dd2dbc.stex" ]
14 |
15 | [params]
16 |
17 | compress/mode=0
18 | compress/lossy_quality=0.7
19 | compress/hdr_mode=0
20 | compress/bptc_ldr=0
21 | compress/normal_map=0
22 | flags/repeat=0
23 | flags/filter=true
24 | flags/mipmaps=false
25 | flags/anisotropic=false
26 | flags/srgb=2
27 | process/fix_alpha_border=true
28 | process/premult_alpha=false
29 | process/HDR_as_SRGB=false
30 | process/invert_color=false
31 | process/normal_map_invert_y=false
32 | stream=false
33 | size_limit=0
34 | detect_3d=true
35 | svg/scale=1.0
36 |
--------------------------------------------------------------------------------
/addons/smoothing/smoothing.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="StreamTexture"
5 | path="res://.import/smoothing.png-6b454a779e636eaa20b6c6ac618bf82a.stex"
6 | metadata={
7 | "vram_texture": false
8 | }
9 |
10 | [deps]
11 |
12 | source_file="res://addons/smoothing/smoothing.png"
13 | dest_files=[ "res://.import/smoothing.png-6b454a779e636eaa20b6c6ac618bf82a.stex" ]
14 |
15 | [params]
16 |
17 | compress/mode=0
18 | compress/lossy_quality=0.7
19 | compress/hdr_mode=0
20 | compress/bptc_ldr=0
21 | compress/normal_map=0
22 | flags/repeat=0
23 | flags/filter=true
24 | flags/mipmaps=false
25 | flags/anisotropic=false
26 | flags/srgb=2
27 | process/fix_alpha_border=true
28 | process/premult_alpha=false
29 | process/HDR_as_SRGB=false
30 | process/invert_color=false
31 | process/normal_map_invert_y=false
32 | stream=false
33 | size_limit=0
34 | detect_3d=true
35 | svg/scale=1.0
36 |
--------------------------------------------------------------------------------
/addons/smoothing/smoothing_2d.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="StreamTexture"
5 | path="res://.import/smoothing_2d.png-4942c58db397caab18506104d957cac1.stex"
6 | metadata={
7 | "vram_texture": false
8 | }
9 |
10 | [deps]
11 |
12 | source_file="res://addons/smoothing/smoothing_2d.png"
13 | dest_files=[ "res://.import/smoothing_2d.png-4942c58db397caab18506104d957cac1.stex" ]
14 |
15 | [params]
16 |
17 | compress/mode=0
18 | compress/lossy_quality=0.7
19 | compress/hdr_mode=0
20 | compress/bptc_ldr=0
21 | compress/normal_map=0
22 | flags/repeat=0
23 | flags/filter=true
24 | flags/mipmaps=false
25 | flags/anisotropic=false
26 | flags/srgb=2
27 | process/fix_alpha_border=true
28 | process/premult_alpha=false
29 | process/HDR_as_SRGB=false
30 | process/invert_color=false
31 | process/normal_map_invert_y=false
32 | stream=false
33 | size_limit=0
34 | detect_3d=true
35 | svg/scale=1.0
36 |
--------------------------------------------------------------------------------
/project.godot:
--------------------------------------------------------------------------------
1 | ; Engine configuration file.
2 | ; It's best edited using the editor UI and not directly,
3 | ; since the parameters that go here are not all obvious.
4 | ;
5 | ; Format:
6 | ; [section] ; section goes between []
7 | ; param=value ; assign values to parameters
8 |
9 | config_version=4
10 |
11 | _global_script_classes=[ ]
12 | _global_script_class_icons={
13 | }
14 |
15 | [application]
16 |
17 | config/name="Smoothing"
18 | run/main_scene="res://Root.tscn"
19 | config/icon="res://smooth_icon.png"
20 |
21 | [editor_plugins]
22 |
23 | enabled=PoolStringArray( "smoothing" )
24 |
25 | [physics]
26 |
27 | common/physics_fps=2
28 | common/physics_jitter_fix=0.0
29 | common/enable_object_picking=false
30 |
31 | [rendering]
32 |
33 | quality/directional_shadow/size=640
34 | quality/shadow_atlas/size=640
35 | quality/depth_prepass/enable=false
36 | environment/default_environment="res://default_env.tres"
37 |
--------------------------------------------------------------------------------
/Target.gd:
--------------------------------------------------------------------------------
1 | extends Spatial
2 |
3 | var m_Dir = 1
4 | var m_Scale = Vector3(1, 1, 1)
5 | var m_Angle = 0.0
6 |
7 |
8 | func _physics_process(_delta):
9 | var tr = transform
10 |
11 | var x = tr.origin.x
12 | if x >= 5:
13 | m_Dir = -1
14 | if x <= -5:
15 | m_Dir = +1
16 |
17 | x += m_Dir * _delta
18 | tr.origin.x = x
19 |
20 | m_Angle += _delta
21 | if m_Angle > (PI*2):
22 | m_Angle -= PI*2
23 |
24 | var rotvec = Vector3(1, 0.5, 0.2)
25 | rotvec = rotvec.normalized()
26 |
27 | tr.basis = Basis(rotvec, m_Angle)
28 |
29 | m_Scale.x = rand_scale(m_Scale.x)
30 | m_Scale.y = rand_scale(m_Scale.y)
31 | m_Scale.z = rand_scale(m_Scale.z)
32 |
33 | #m_Scale = Vector3(0.5, 0.5, 0.5)
34 | #tr.basis = tr.basis.scaled(m_Scale)
35 | #scale_object_local(m_Scale)
36 |
37 | transform = tr
38 |
39 | pass
40 |
41 | func rand_scale(var v):
42 | v = v + (randf() - 0.5) * 0.2
43 | v = clamp(v, 0.3, 3.0)
44 | return v
45 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Lawnjelly
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/addons/smoothing/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Lawnjelly
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Root.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=16 format=2]
2 |
3 | [ext_resource path="res://addons/smoothing/smoothing.gd" type="Script" id=1]
4 | [ext_resource path="res://addons/smoothing/smoothing.png" type="Texture" id=2]
5 | [ext_resource path="res://Target.gd" type="Script" id=3]
6 | [ext_resource path="res://addons/smoothing/smoothing_2d.gd" type="Script" id=4]
7 | [ext_resource path="res://icon.png" type="Texture" id=5]
8 | [ext_resource path="res://Target2D.gd" type="Script" id=6]
9 | [ext_resource path="res://Root.gd" type="Script" id=7]
10 | [ext_resource path="res://addons/smoothing/smoothing_2d.png" type="Texture" id=8]
11 | [ext_resource path="res://smooth_icon.png" type="Texture" id=9]
12 | [ext_resource path="res://Target2D_flipped.gd" type="Script" id=10]
13 |
14 | [sub_resource type="CubeMesh" id=3]
15 |
16 | [sub_resource type="SpatialMaterial" id=4]
17 | albedo_color = Color( 0.537255, 0.643137, 1, 1 )
18 |
19 | [sub_resource type="CubeMesh" id=1]
20 |
21 | [sub_resource type="SpatialMaterial" id=2]
22 | albedo_color = Color( 1, 0, 0, 1 )
23 |
24 | [sub_resource type="Environment" id=5]
25 |
26 | [node name="Root" type="Spatial"]
27 | script = ExtResource( 7 )
28 |
29 | [node name="Example2D" type="Node2D" parent="."]
30 |
31 | [node name="Target2D" type="Node2D" parent="Example2D"]
32 | position = Vector2( 0, 300 )
33 | scale = Vector2( 2, 2 )
34 | script = ExtResource( 6 )
35 |
36 | [node name="Sprite" type="Sprite" parent="Example2D/Target2D"]
37 | texture = ExtResource( 5 )
38 |
39 | [node name="Smoothing2D" type="Node2D" parent="Example2D/Target2D"]
40 | position = Vector2( 0, -150 )
41 | scale = Vector2( 0.5, 0.5 )
42 | script = ExtResource( 4 )
43 | __meta__ = {
44 | "_editor_icon": ExtResource( 8 )
45 | }
46 | flags = 63
47 |
48 | [node name="Sprite_smoothed" type="Sprite" parent="Example2D/Target2D/Smoothing2D"]
49 | texture = ExtResource( 5 )
50 |
51 | [node name="Target2D_flipped" type="Node2D" parent="Example2D"]
52 | script = ExtResource( 10 )
53 |
54 | [node name="Smoothing2D" type="Node2D" parent="Example2D/Target2D_flipped"]
55 | script = ExtResource( 4 )
56 |
57 | [node name="Sprite" type="Sprite" parent="Example2D/Target2D_flipped/Smoothing2D"]
58 | texture = ExtResource( 9 )
59 |
60 | [node name="Example3D" type="Spatial" parent="."]
61 |
62 | [node name="Target" type="Spatial" parent="Example3D"]
63 | script = ExtResource( 3 )
64 |
65 | [node name="MeshInstance" type="MeshInstance" parent="Example3D/Target"]
66 | mesh = SubResource( 3 )
67 | material/0 = SubResource( 4 )
68 |
69 | [node name="Smoothing" type="Spatial" parent="Example3D/Target"]
70 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 3, 0 )
71 | script = ExtResource( 1 )
72 | __meta__ = {
73 | "_editor_icon": ExtResource( 2 )
74 | }
75 |
76 | [node name="MeshInstance_smoothed" type="MeshInstance" parent="Example3D/Target/Smoothing"]
77 | mesh = SubResource( 1 )
78 | material/0 = SubResource( 2 )
79 |
80 | [node name="Camera" type="Camera" parent="."]
81 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 6.77548 )
82 |
83 | [node name="DirectionalLight" type="DirectionalLight" parent="."]
84 |
85 | [node name="WorldEnvironment" type="WorldEnvironment" parent="."]
86 | environment = SubResource( 5 )
87 |
--------------------------------------------------------------------------------
/addons/smoothing/smoothing.gd:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2019 Lawnjelly
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in all
11 | # copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | # SOFTWARE.
20 |
21 | extends Spatial
22 |
23 | export (NodePath) var target: NodePath setget set_target, get_target
24 |
25 | var _m_Target: Spatial
26 |
27 | var _m_trCurr: Transform
28 | var _m_trPrev: Transform
29 |
30 | const SF_ENABLED = 1 << 0
31 | const SF_TRANSLATE = 1 << 1
32 | const SF_BASIS = 1 << 2
33 | const SF_SLERP = 1 << 3
34 | const SF_INVISIBLE = 1 << 4
35 |
36 | export (int, FLAGS, "enabled", "translate", "basis", "slerp") var flags: int = SF_ENABLED | SF_TRANSLATE | SF_BASIS setget _set_flags, _get_flags
37 |
38 | ##########################################################################################
39 | # USER FUNCS
40 |
41 |
42 | # call this on e.g. starting a level, AFTER moving the target
43 | # so we can update both the previous and current values
44 | func teleport():
45 | var temp_flags = flags
46 | _SetFlags(SF_TRANSLATE | SF_BASIS)
47 |
48 | _RefreshTransform()
49 | _m_trPrev = _m_trCurr
50 |
51 | # do one frame update to make sure all components are updated
52 | _process(0)
53 |
54 | # resume old flags
55 | flags = temp_flags
56 |
57 |
58 | func set_enabled(bEnable: bool):
59 | _ChangeFlags(SF_ENABLED, bEnable)
60 | _SetProcessing()
61 |
62 |
63 | func is_enabled():
64 | return _TestFlags(SF_ENABLED)
65 |
66 |
67 | ##########################################################################################
68 |
69 |
70 | func _ready():
71 | _m_trCurr = Transform()
72 | _m_trPrev = Transform()
73 | set_process_priority(100)
74 | set_as_toplevel(true)
75 | Engine.set_physics_jitter_fix(0.0)
76 |
77 |
78 | func set_target(new_value):
79 | target = new_value
80 | if is_inside_tree():
81 | _FindTarget()
82 |
83 |
84 | func get_target():
85 | return target
86 |
87 |
88 | func _set_flags(new_value):
89 | flags = new_value
90 | # we may have enabled or disabled
91 | _SetProcessing()
92 |
93 |
94 | func _get_flags():
95 | return flags
96 |
97 |
98 | func _SetProcessing():
99 | var bEnable = _TestFlags(SF_ENABLED)
100 | if _TestFlags(SF_INVISIBLE):
101 | bEnable = false
102 |
103 | set_process(bEnable)
104 | set_physics_process(bEnable)
105 | pass
106 |
107 |
108 | func _enter_tree():
109 | # might have been moved
110 | _FindTarget()
111 | pass
112 |
113 |
114 | func _notification(what):
115 | match what:
116 | # invisible turns off processing
117 | NOTIFICATION_VISIBILITY_CHANGED:
118 | _ChangeFlags(SF_INVISIBLE, is_visible_in_tree() == false)
119 | _SetProcessing()
120 |
121 |
122 | func _RefreshTransform():
123 | if _HasTarget() == false:
124 | return
125 |
126 | _m_trPrev = _m_trCurr
127 | _m_trCurr = _m_Target.global_transform
128 |
129 | func _FindTarget():
130 | _m_Target = null
131 |
132 | # If no target has been assigned in the property,
133 | # default to using the parent as the target.
134 | if target.is_empty():
135 | var parent = get_parent_spatial()
136 | if parent:
137 | _m_Target = parent
138 | return
139 |
140 | var targ = get_node(target)
141 |
142 | if ! targ:
143 | printerr("ERROR SmoothingNode : Target " + target + " not found")
144 | return
145 |
146 | if not targ is Spatial:
147 | printerr("ERROR SmoothingNode : Target " + target + " is not spatial")
148 | target = ""
149 | return
150 |
151 | # if we got to here targ is a spatial
152 | _m_Target = targ
153 |
154 | # do a final check
155 | # certain targets are disallowed
156 | if _m_Target == self:
157 | var msg = _m_Target.get_name() + " assigned to " + self.get_name() + "]"
158 | printerr("ERROR SmoothingNode : Target should not be self [", msg)
159 |
160 | # error message
161 | #OS.alert("Target cannot be a parent or grandparent in the scene tree.", "SmoothingNode")
162 | _m_Target = null
163 | target = ""
164 | return
165 |
166 |
167 | func _HasTarget() -> bool:
168 | if _m_Target == null:
169 | return false
170 |
171 | # has not been deleted?
172 | if is_instance_valid(_m_Target):
173 | return true
174 |
175 | _m_Target = null
176 | return false
177 |
178 |
179 | func _process(_delta):
180 |
181 | var f = Engine.get_physics_interpolation_fraction()
182 | var tr: Transform = Transform()
183 |
184 | # translate
185 | if _TestFlags(SF_TRANSLATE):
186 | var ptDiff = _m_trCurr.origin - _m_trPrev.origin
187 | tr.origin = _m_trPrev.origin + (ptDiff * f)
188 |
189 | # rotate
190 | if _TestFlags(SF_BASIS):
191 | if _TestFlags(SF_SLERP):
192 | tr.basis = _m_trPrev.basis.slerp(_m_trCurr.basis, f)
193 | else:
194 | tr.basis = _LerpBasis(_m_trPrev.basis, _m_trCurr.basis, f)
195 |
196 | transform = tr
197 |
198 |
199 | func _physics_process(_delta):
200 | _RefreshTransform()
201 |
202 |
203 | func _LerpBasis(from: Basis, to: Basis, f: float) -> Basis:
204 | var res: Basis = Basis()
205 | res.x = from.x.linear_interpolate(to.x, f)
206 | res.y = from.y.linear_interpolate(to.y, f)
207 | res.z = from.z.linear_interpolate(to.z, f)
208 | return res
209 |
210 |
211 | func _SetFlags(f):
212 | flags |= f
213 |
214 |
215 | func _ClearFlags(f):
216 | flags &= ~f
217 |
218 |
219 | func _TestFlags(f):
220 | return (flags & f) == f
221 |
222 |
223 | func _ChangeFlags(f, bSet):
224 | if bSet:
225 | _SetFlags(f)
226 | else:
227 | _ClearFlags(f)
228 |
--------------------------------------------------------------------------------
/addons/smoothing/smoothing_2d.gd:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2019 Lawnjelly
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in all
11 | # copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | # SOFTWARE.
20 |
21 | extends Node2D
22 |
23 | export (NodePath) var target: NodePath setget set_target, get_target
24 |
25 | var _m_Target: Node2D
26 | var _m_Flip: bool = false
27 |
28 | var _m_Trans_curr: Transform2D = Transform2D()
29 | var _m_Trans_prev: Transform2D = Transform2D()
30 |
31 | const SF_ENABLED = 1 << 0
32 | const SF_GLOBAL_IN = 1 << 1
33 | const SF_GLOBAL_OUT = 1 << 2
34 | const SF_TOP_LEVEL = 1 << 3
35 | const SF_INVISIBLE = 1 << 4
36 |
37 | export (int, FLAGS, "enabled", "global in", "global out", "top level") var flags: int = SF_ENABLED | SF_GLOBAL_IN | SF_GLOBAL_OUT setget _set_flags, _get_flags
38 |
39 | ##########################################################################################
40 | # USER FUNCS
41 |
42 |
43 | # call this on e.g. starting a level, AFTER moving the target
44 | # so we can update both the previous and current values
45 | func teleport():
46 |
47 | _RefreshTransform()
48 | _m_Trans_prev = _m_Trans_curr
49 |
50 | # call frame upate to make sure all components of the node are set
51 | _process(0)
52 |
53 | func set_enabled(bEnable: bool):
54 | _ChangeFlags(SF_ENABLED, bEnable)
55 | _SetProcessing()
56 |
57 |
58 | func is_enabled():
59 | return _TestFlags(SF_ENABLED)
60 |
61 |
62 | ##########################################################################################
63 |
64 |
65 | func _ready():
66 | set_process_priority(100)
67 | Engine.set_physics_jitter_fix(0.0)
68 | set_as_toplevel(_TestFlags(SF_TOP_LEVEL))
69 |
70 |
71 | func set_target(new_value):
72 | target = new_value
73 | if is_inside_tree():
74 | _FindTarget()
75 |
76 |
77 | func get_target():
78 | return target
79 |
80 |
81 | func _set_flags(new_value):
82 | flags = new_value
83 | # we may have enabled or disabled
84 | _SetProcessing()
85 |
86 |
87 | func _get_flags():
88 | return flags
89 |
90 |
91 | func _SetProcessing():
92 | var bEnable = _TestFlags(SF_ENABLED)
93 | if _TestFlags(SF_INVISIBLE):
94 | bEnable = false
95 |
96 | set_process(bEnable)
97 | set_physics_process(bEnable)
98 | set_as_toplevel(_TestFlags(SF_TOP_LEVEL))
99 |
100 |
101 | func _enter_tree():
102 | # might have been moved
103 | _FindTarget()
104 |
105 |
106 | func _notification(what):
107 | match what:
108 | # invisible turns off processing
109 | NOTIFICATION_VISIBILITY_CHANGED:
110 | _ChangeFlags(SF_INVISIBLE, is_visible_in_tree() == false)
111 | _SetProcessing()
112 |
113 |
114 | func _RefreshTransform():
115 |
116 | if _HasTarget() == false:
117 | return
118 |
119 | _m_Trans_prev = _m_Trans_curr
120 |
121 | if _TestFlags(SF_GLOBAL_IN):
122 | _m_Trans_curr = _m_Target.get_global_transform()
123 | else:
124 | _m_Trans_curr = _m_Target.get_transform()
125 |
126 | _m_Flip = false
127 |
128 | # Ideally we would use determinant core function, as in commented line below, but we
129 | # need to workaround for backward compat.
130 | # if (_m_Trans_prev.determinant() < 0) != (_m_Trans_curr.determinant() < 0):
131 |
132 | if (_Determinant_Sign(_m_Trans_prev) != _Determinant_Sign(_m_Trans_curr)):
133 | _m_Flip = true
134 |
135 |
136 | func _Determinant_Sign(t:Transform2D)->bool:
137 | # Workaround Transform2D determinant function not being available
138 | # until 3.6 / 4.1.
139 | # We calculate determinant manually, slower but compatible to lower
140 | # godot versions.
141 | var d = (t.x.x * t.y.y) - (t.x.y * t.y.x)
142 | return d >= 0.0
143 |
144 |
145 | func _FindTarget():
146 | _m_Target = null
147 |
148 | # If no target has been assigned in the property,
149 | # default to using the parent as the target.
150 | if target.is_empty():
151 | var parent = get_parent()
152 | if parent and (parent is Node2D):
153 | _m_Target = parent
154 | return
155 |
156 | var targ = get_node(target)
157 |
158 | if ! targ:
159 | printerr("ERROR SmoothingNode2D : Target " + target + " not found")
160 | return
161 |
162 | if not targ is Node2D:
163 | printerr("ERROR SmoothingNode2D : Target " + target + " is not Node2D")
164 | target = ""
165 | return
166 |
167 | # if we got to here targ is correct type
168 | _m_Target = targ
169 |
170 |
171 | func _HasTarget() -> bool:
172 | if _m_Target == null:
173 | return false
174 |
175 | # has not been deleted?
176 | if is_instance_valid(_m_Target):
177 | return true
178 |
179 | _m_Target = null
180 | return false
181 |
182 |
183 | func _process(_delta):
184 |
185 | var f = Engine.get_physics_interpolation_fraction()
186 |
187 | var tr = Transform2D()
188 | tr.origin = lerp(_m_Trans_prev.origin, _m_Trans_curr.origin, f)
189 | tr.x = lerp(_m_Trans_prev.x, _m_Trans_curr.x, f)
190 | tr.y = lerp(_m_Trans_prev.y, _m_Trans_curr.y, f)
191 |
192 | # When a sprite flip is detected, turn off interpolation for that tick.
193 | if _m_Flip:
194 | tr = _m_Trans_curr
195 |
196 | if _TestFlags(SF_GLOBAL_OUT) and not _TestFlags(SF_TOP_LEVEL):
197 | set_global_transform(tr)
198 | else:
199 | set_transform(tr)
200 |
201 | func _physics_process(_delta):
202 | _RefreshTransform()
203 |
204 |
205 |
206 | func _SetFlags(f):
207 | flags |= f
208 |
209 |
210 | func _ClearFlags(f):
211 | flags &= ~f
212 |
213 |
214 | func _TestFlags(f):
215 | return (flags & f) == f
216 |
217 |
218 | func _ChangeFlags(f, bSet):
219 | if bSet:
220 | _SetFlags(f)
221 | else:
222 | _ClearFlags(f)
223 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # smoothing-addon v 1.2.2
2 | Fixed timestep interpolation gdscript addon for Godot 3.2 (and later versions)
3 |
4 | ### Update
5 | As of Godot 3.5, 3D physics interpolation is build into the engine, and as of 3.6 (beta 4 onward) 2D physics interpolation is built in. New users are recommended to use core interpolation, rather than this addon.
6 |
7 | https://docs.godotengine.org/en/3.6/tutorials/physics/interpolation/index.html
8 |
9 | However, the addon is maintained for compatibility purposes with existing games, and previous versions of the engine.
10 |
11 | ## Introduction
12 |
13 | If you were wondering how to use that new function `Engine.get_physics_interpolation_fraction()` in 3.2, feel free to use this as is, or to get ideas from for your own version.
14 |
15 | _If you find bugs / have suggestions, please add an issue to the issue tracker and I will look into it!_ :)
16 |
17 | The smoothing addon adds 2 new nodes to Godot, 'Smoothing' (for 3d) and 'Smoothing2d' (for 2d). They allow for fixed timestep interpolation without writing any code. See here for an explanation of fixed timestep interpolation:
18 | https://www.gamedev.net/blogs/entry/2265460-fixing-your-timestep-and-evaluating-godot/
19 |
20 | https://www.youtube.com/watch?v=lWhHBAcH4sM
21 |
22 | ## Installation
23 |
24 | This repository contains the addon (in the addons folder) and an example demo project.
25 |
26 | To use the addon in your own project:
27 | 1. Create a new project in Godot or use an existing project.
28 | 2. Copy the 'addons' folder from this repository to your Godot project folder.
29 | 3. Go to 'Project Settings' plugins tab.
30 | 4. Find the smoothing plugin and set status to 'Active'.
31 |
32 | ## Explanation
33 | In a game you would usually choose to create a Node2D, Spatial, RigidBody, Kinematic body etc node for a game object, which is affected by physics and / or AI and / or player input. This I will refer to as the PHYSICS REP (representation).
34 |
35 | The visual respresentation of this object (VISUAL REP) is often simply a child of this node, such as a MeshInstance, or Sprite. That way it inherits the transform of the parent physics rep. When you move the physics rep, the transform propagates to the child node, the visual rep, and it renders in the same place as the physics rep. In some games the visual rep can even be the same node as the physics rep (particularly when there is no actual physics).
36 |
37 | Usually transforms propagate from a parent to child. Fixed timestep interpolation works slightly differently - the VisualRep indirectly _follows_ the transform of the PhysicsRep, rather than being directly affected by it.
38 |
39 | In your gameplay programming, 99% of the time you would usually be mostly concerned with the position and rotation of the physics rep. Aside a few things like visual effects, the visual rep will follow the physics rep, and you don't need to worry about it. This also means that providing you drive your gameplay using `_physics_process` rather than `_process`, your gameplay will run the same no matter what machine you run it on! Fantastic.
40 |
41 | ### Note
42 | The 3D smoothing node automatically calls `set_as_toplevel()` when in global mode. This ensures that it only follows the selected target, rather than having the transform controlled directly by the parent. The default target to follow will however be the parent node, if a `Target` has not been assigned in the inspector.
43 |
44 | In 2D, *flips* are supported. That is, if you use negative scaling to flip a sprite, the interpolation will detect this and turn off for a tick to get an instantaneous flip, instead of having the sprite "turn inside out".
45 |
46 | ## Usage
47 |
48 | ### 3D
49 | 1. You would usually in a game choose to create a Spatial, RigidBody, Kinematic body etc node for your physics rep, and have a visual representation (e.g. a MeshInstance) as a child of this node.
50 | 2. Do this as normal so that you can see the object moving in the game.
51 | 3. Add the new 'Smoothing' node to the scene, as a child of your physics rep.
52 | 4. Drag the visual representation from being a child of the physics rep, to being a child of the Smoothing node.
53 | 5. That is mainly it! Just run the game and now hopefully the visual representation will follow the gameobject, but now with interpolation. You can test this is working by running at a low physics tick rate (physics_fps in project settings->physics).
54 |
55 | ### 2D
56 | The procedure for 2D is pretty much the same as with 3D except you would be using a node derived from Node2D as the physics rep (target) and the 'Smoothing2D' node should be used instead of 'Smoothing'.
57 |
58 | In 2D, for legacy support, the smoothing node can be set to `toplevel` if the property flag is enabled (this defaults to disabled). `toplevel` enables the transform of the smoothing node to be specified in true global space (which is more stable, and may play better with GPU snapping), and makes things simpler because the smoothing node can be a direct child of a target.
59 |
60 | You are recommended to try `toplevel` mode, however there are two downsides which may preclude its use:
61 | 1. Parent node visibility is not automatically propagated to `toplevel` nodes, thus you have to explicitly hide the smoothing node, rather than rely on hiding just the parent node.
62 | 2. Y-sorting does not work correctly for `toplevel` nodes.
63 |
64 | ### Following targets that are not the parent node
65 | When not using `toplevel` mode in 2D, and in other problematic situations you may see jitter. In this case you may want to use the smoothing node's ability to follow targets that are not parent nodes. You can do this by assigning a `Target` in the inspector panel for the Smoothing node.
66 |
67 | In this situation you are highly recommended to place the smoothing node on a separate branch in the scene tree, preferably inheriting no transform from a parent node (i.e. all the parents and grandparents will have zero translate, no rotate, and 1:1 scale).
68 |
69 | e.g. Instead of:
70 | ```
71 | Root
72 | PhysicsRep
73 | VisualRep (child of PhysicsRep)
74 | ```
75 | The relationship becomes:
76 | ```
77 | Root
78 | PhysicsRep
79 | VisualRep (child of Root)
80 | ```
81 | To enable interpolation instead of relying on the scenetree transforms being propagated to children, we specifically tell the VisualRep to follow the PhysicsRep. This way it can follow the position and rotation of the PhysicsRep WITHOUT being directly affected by the transform of the PhysicsRep.
82 |
83 | This may sound overly complicated, but because of the maths involved, it is usually essential to getting a good result.
84 |
85 | ### Teleporting
86 | There is one special case when using smoothing nodes - the case when you want a target to instantaneously move from one location to another (for instance respawning a player, or at level start) where you do not want interpolation from the previous position. In this special case, you should move the target node (by setting the translation or position), and then call the 'teleport' function in the Smoothing node. This ensures that interpolation will be switched off temporarily for the move.
87 | _Make sure to call `teleport` AFTER moving the target node, rather than before._
88 |
89 | ### Other options
90 | As well as choosing the Target, in the inspector for the Smoothing nodes there are a set of flags.
91 |
92 | #### 3D
93 | 1. enabled - chooses whether the smoothing node is active
94 | 2. translate - interpolation will be done for the position
95 | 3. basis - interpolation will be done for rotation and scale
96 | 4. slerp - this will do quaternion slerping instead of the simpler basis lerping. Note that this only works with no scaling applied to the target.
97 |
98 | #### 2D
99 | 1. enabled - as above
100 | 5. global in - will read the global transform of the target instead of local
101 | 6. global out - will set the global transform of the smoothing node instead of local
102 |
103 | (Local mode may be more efficient but you must understand the difference between local and global transforms.)
104 |
105 | ### Notes
106 |
107 | * Processing will also be turned off automatically for the smoothing nodes if they are hidden (either directly or through a parent).
108 | * You can also set the target for the smoothing node from script, using the `set_target` function and passing a NodePath argument (e.g. `mynode.get_path()`).
109 | * The best way to debug / develop smoothing is to set physics ticks per second (`ProjectSettings->Physics->Common->Physics_FPS`) to a low value (e.g. 10). Once you have it working you can set it back up to high value if desired.
110 | * If you encounter problems smoothing a `Camera2D` node, try setting the `ProcessMode` to `Idle` instead of `Physics`.
111 | * In order to prevent an unneeded extra delay of one tick, it is important that smoothing nodes are processed _AFTER_ target nodes. This should now be automatically taken care as the addon internally uses `process_priority` to achieve this. Previously we required smoothing nodes to be placed lower in the scene tree than the target. This should hopefully no longer be the case.
112 | * Fixed timestep interpolation may not work well in 2D pixel snapped games. For further info see https://github.com/lawnjelly/godot-snapping-demo .
113 |
114 | There is no need for JitterFix (`Project Settings->Physics->Common->Physics Jitter Fix`) when using fixed timestep interpolation, indeed it may interfere with getting a good result. The addon now enforces this by setting `Engine.set_physics_jitter_fix` to 0 as smoothing nodes are created.
115 |
116 | ### Authors
117 | Lawnjelly, Calinou
118 |
119 | __This addon is also available as a c++ module (slight differences), see:__
120 | https://github.com/lawnjelly/godot-smooth
121 |
122 | ### Addendum
123 | Physics Interpolation is now available in core Godot as of 3.6, and you are encouraged to use core interpolation rather than addons wherever available.
124 | It should be:
125 | * Easier to use.
126 | * Introduces new 2D mode to get around `toplevel` problems.
127 | * More accurate (particularly for pivots).
128 | * Faster.
129 |
130 | See the official documentation for details.
131 |
--------------------------------------------------------------------------------