godotengine / godotengine/godot
Tool script can accidentally modify physics body initial velocity in `_physics_process` (saved in scene file but invisible in Inspector)
- Dominant language
- C++
- Stars
- 117k
- Forks
- 26.8k
- PR merge metrics
- PR metrics pending
Description
### Tested versions
- Reproducible in v4.6.stable.official [89cea1439]
### System information
Godot v4.6.stable - Windows 11 (build 26200) - Multi-window, 1 monitor - OpenGL 3 (Compatibility) - NVIDIA GeForce RTX 3070 (NVIDIA; 32.0.15.6636) - 12th Gen Intel(R) Core(TM) i9-12900K (24 threads) - 63.75 GiB memory
### Issue description
After working on some tool script for a character, I noticed that the character kept disappearing on game start. I found out in VCS diff that its initial velocity was huge!
I found the cause: after adding `@tool` to my script, I forgot to add a safety early return when inside editor in `_physics_process`, causing physics, in this case gravity, to be applied at edit time:
```gd
func _physics_process(delta: float) -> void:
# this was missing at first
if Engine.is_editor_hint():
return
apply_gravity(delta)
# ...
```
This stacked up a lot of downward velocity on my character. By the time I added the early return, the initial velocity Y was very high, and would be applied on every game start. After saving the scene, closing and reopening Godot, the value was still saved, but invisible in the Inspector (see [Velocity is not exposed for CharacterBody2D (to use in AnimationPlayer) #68968](https://github.com/godotengine/godot/issues/68968).
Fortunately I could still see it in VCS diff and revert the change by modifying scene text directly...
I can't exactly tell where this should be fixed. I suppose invisible properties should simply never be saved in scene files.
If later, velocity is exported and visible though, that means `@tool` script may accidentally set velocity again - but at least we'll have some way to inspect the faulty character node in the inspector and revert the bad change, besides checking VCS diff.
Related:
- https://github.com/godotengine/godot/issues/8735 would help avoiding accidentally running `_physics_process` when adding `@tool` until we opt-in for edit-time run, but wouldn't fix the issue of saving invisible properties when that still happens anyway
### Steps to reproduce
0. Load the MRP, or create your own node with a CharacterBody2D with this script, and some sprite:
```gd
@tool
extends CharacterBody2D
@export var gravity: float = 980.0
func _physics_process(delta: float) -> void:
# Comment this early return block out to trigger the bug
# We recommend keeping move_and_slide commented out for now to avoid node
# "falling" while in the editor already
if Engine.is_editor_hint():
return
apply_gravity(delta)
# Uncomment this after restoring early return
# just to test that at runtime, character is going out of screen very fast
# when playing with a huge initial velocity
#move_and_slide()
func apply_gravity(delta: float) -> void:
velocity.y += gravity * delta
```
1. Open project in Git client (or use `git diff` command-line) to check diff: no change at first
2. Comment out the early return block to trigger the bug
3. Wait one second and save, then check VCS diff. Repeat save and check diff to see velocity Y increasing very fast.
4. Comment early return block and save script to stop the madness, but it's too late.
5. Uncomment `move_and_slide()` too so we can see actual move in game
6. Play game: the character moves so fast it leaves screen instantly, we can't even see it
7. Revert velocity change via git or manually editing text file
8. Play game: character now falls normally with gravity, starting with velocity ZERO
### Minimal reproduction project (MRP)
[mrp-tool-script-modifies-velocity-under-the-hood.zip](https://github.com/user-attachments/files/26522621/mrp-tool-script-modifies-velocity-under-the-hood.zip)
Contributor guide
Research direction
Start with the attached MRP and reproduce the edit-time _physics_process updates while watching the scene file in git diff and reopening it. Trace how CharacterBody2D velocity is serialized despite being invisible in the Inspector. Done means the behavior has a reproducible regression test and unintended invisible velocity changes are no longer persisted or can be inspected and reverted.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- game-dev, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 38/100