Skip to content

Commit 105aa25

Browse files
authored
fix: One-Off Inputs tutorial duplicates one-off input during lag (#490)
Uses the snippets extension to copy example code from a .gd file into the docs. This enables writing tests for the example code. Closes #489
1 parent ccad763 commit 105aa25

File tree

11 files changed

+132
-51
lines changed

11 files changed

+132
-51
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,6 @@ buildtmp
2525
vest.log
2626

2727
plantuml-*.jar
28+
29+
# Only for local use
30+
sh/ensure-uids.sh

docs/netfox/tutorials/input-gathering-tips-and-tricks.md

Lines changed: 5 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ To read more about *netfox*'s *tick loop*, see the [Network tick loop].
3030

3131
## Continuous inputs
3232

33-
Consider player movement - if the player holds up, the character will move
34-
north, right for east, down for south, left for west. If the player holds two
35-
directions, the character will move diagonally.
33+
Consider player movement - if the player holds the button *up*, the character
34+
will move north, right for east, *down* for south, *left* for west. If the
35+
player holds two directions, the character will move diagonally.
3636

3737
Since the player needs to *hold* the buttons for movement to happen, it is
3838
considered a *continuous* input.
@@ -76,32 +76,7 @@ The solution is to sample player input on every `_process()` frame, and average
7676
the samples collected before each tick loop.
7777

7878
```gdscript
79-
extends BaseNetInput
80-
class_name PlayerInput
81-
82-
var movement: Vector3 = Vector3.ZERO
83-
84-
var _movement_buffer: Vector3 = Vector3.ZERO
85-
var _movement_samples: int = 0
86-
87-
func _process(_dt: float) -> void:
88-
_movement_buffer += Vector3(
89-
Input.get_axis("move_west", "move_east"),
90-
Input.get_action_strength("move_jump"),
91-
Input.get_axis("move_north", "move_south")
92-
)
93-
_movement_samples += 1
94-
95-
func _gather() -> void:
96-
# Average samples
97-
if _movement_samples > 0:
98-
movement = _movement_buffer / _movement_samples
99-
else:
100-
movement = Vector3.ZERO
101-
102-
# Reset buffer
103-
_movement_buffer = Vector3.ZERO
104-
_movement_samples = 0
79+
--8<-- "examples/snippets/input-gathering-tutorial/continuous-sampled-input.gd"
10580
```
10681

10782
This way, every known input is taken into account.
@@ -181,25 +156,7 @@ To solve both of these issues, *one-off inputs* can be buffered similarly to
181156
gathered - this way, the input will be true for *at most* a single tick:
182157

183158
```gdscript
184-
extends BaseNetInput
185-
class_name PlayerInput
186-
187-
var is_jumping: bool = false
188-
var _is_jumping_buffer: bool = false
189-
190-
func _ready():
191-
super()
192-
NetworkTime.after_tick.connect(func(_dt, _t): _reset())
193-
194-
func _process(_dt: float) -> void:
195-
if Input.is_action_just_pressed("move_jump"):
196-
_is_jumping_buffer = true
197-
198-
func _gather():
199-
is_jumping = _is_jumping_buffer
200-
201-
func _reset():
202-
_is_jumping_buffer = false
159+
--8<-- "examples/snippets/input-gathering-tutorial/one-off-input.gd"
203160
```
204161

205162
!!!tip

examples/input-gathering/scripts/player-input.gd

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ var _is_jumping_buffer: bool = false
1010

1111
func _ready():
1212
super()
13-
NetworkTime.after_tick.connect(func(_dt, _t): _reset())
13+
NetworkTime.after_tick.connect(func(_dt, _t): _gather_always())
1414

1515
func _process(_dt: float) -> void:
1616
_movement_buffer += Vector3(
@@ -34,8 +34,7 @@ func _gather() -> void:
3434
_movement_buffer = Vector3.ZERO
3535
_movement_samples = 0
3636

37+
func _gather_always():
3738
# Jumping
3839
is_jumping = _is_jumping_buffer
39-
40-
func _reset():
4140
_is_jumping_buffer = false
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
extends BaseNetInput
2+
3+
var movement: Vector3 = Vector3.ZERO
4+
5+
var _movement_buffer: Vector3 = Vector3.ZERO
6+
var _movement_samples: int = 0
7+
8+
func _process(_dt: float) -> void:
9+
_movement_buffer += Vector3(
10+
Input.get_axis("move_west", "move_east"),
11+
Input.get_action_strength("move_jump"),
12+
Input.get_axis("move_north", "move_south")
13+
)
14+
_movement_samples += 1
15+
16+
func _gather() -> void:
17+
# Average samples
18+
if _movement_samples > 0:
19+
movement = _movement_buffer / _movement_samples
20+
else:
21+
movement = Vector3.ZERO
22+
23+
# Reset buffer
24+
_movement_buffer = Vector3.ZERO
25+
_movement_samples = 0
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
uid://wbyota8jwim3
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
extends BaseNetInput
2+
3+
var is_jumping: bool = false
4+
var _is_jumping_buffer: bool = false
5+
6+
func _ready():
7+
super()
8+
NetworkTime.after_tick.connect(func(_dt, _t): _gather_always())
9+
10+
func _process(_dt: float) -> void:
11+
if Input.is_action_just_pressed("move_jump"):
12+
_is_jumping_buffer = true
13+
14+
func _gather_always():
15+
is_jumping = _is_jumping_buffer
16+
_is_jumping_buffer = false
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
uid://btckkptwyp8qj

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ markdown_extensions:
2020
- md_in_html
2121
- pymdownx.details
2222
- pymdownx.highlight
23+
- pymdownx.snippets
2324
- pymdownx.superfences
2425
- pymdownx.emoji:
2526
emoji_index: !!python/name:material.extensions.emoji.twemoji
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
extends VestTest
2+
3+
const OneOffInput = preload("res://examples/snippets/input-gathering-tutorial/one-off-input.gd")
4+
5+
func get_suite_name():
6+
return "OneOffInput"
7+
8+
func suite():
9+
var action = "move_jump"
10+
var input := OneOffInput.new()
11+
12+
# TODO(vest): Some good way to add nodes to the tree
13+
await NetworkTime.get_tree().process_frame
14+
NetworkTime.get_tree().root.add_child(input)
15+
16+
test("should return false with no input", func():
17+
expect_false(input.is_jumping)
18+
)
19+
20+
test("should return true for a single tick", func():
21+
# Activate input
22+
Input.action_press(action)
23+
# Process
24+
input._process(1. / 60.)
25+
# Deactive input
26+
Input.action_release(action)
27+
28+
# Start tick loop
29+
NetworkMocks.in_network_tick_loop(func():
30+
# Single tick
31+
NetworkMocks.run_network_tick()
32+
33+
# Check for result
34+
expect_true(input.is_jumping)
35+
)
36+
)
37+
38+
test("should return true only on the first tick", func():
39+
# Activate input
40+
Input.action_press(action)
41+
# Process
42+
input._process(1. / 60.)
43+
# Deactive input
44+
Input.action_release(action)
45+
46+
# Start tick loop
47+
NetworkMocks.in_network_tick_loop(func():
48+
# First tick
49+
NetworkMocks.run_network_tick()
50+
expect_true(input.is_jumping, "First tick should have input!")
51+
52+
# Second tick
53+
NetworkMocks.run_network_tick()
54+
expect_false(input.is_jumping, "Second tick should not have input!")
55+
)
56+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
uid://e32thkcpm64x

0 commit comments

Comments
 (0)