Open madmiraal opened 2 years ago
When I started implementing my own little engine for learning purposes, I've figured from the OpenGL perspective that model, view, and projection transformation matrices have a close relationship to the concepts of local and global coordinates. We could map how each stage of transformation corresponds to each Godot's specific transform pipeline.
Therefore, I think clarifying this somewhere in Godot's documentation could also be helpful in understanding those concepts from the graphics point of view. Except for things like normalized device coordinates which are rarely mentioned or relevant in the context of Godot, unless we talk about shaders specifically.
Edit: Now I understand the reasoning for the original proposal. Consider this post obsolete.
I would like to make a suggestion for the coordinates in events.
In the current proposal gui_input
uses different coordinate-systems than input
and unhandled_input
.
I believe, that a different distinction would make more sense:
CanvasItem
, in _input
, _gui_input
and _unhandled_input
the function InputEventMouse.get_position()
should return the mouse's position in this CanvasItem
.CanvasItem
, in _input
, _gui_input
and _unhandled_input
the function InputEventMouse.get_global_position()
should return the mouse's position in the CanvasLayer
that this CanvasItem
is in._input
and _unhandled_input
the function InputEventMouse.get_position()
should return the mouse's position in the viewport that this Node is in._input
and _unhandled_input
the function InputEventMouse.get_global_position()
should return the mouse's position in the root viewport.In the last two cases, _gui_input
is never called.
The main reason for this suggestion is:
There are nodes, for which mouse coordinates make sense to evaluate (CanvasItem
and their descendants) and other nodes, for which mouse coordinates make no sense, because they do not have a position.
When the evaluation of mouse coordinates makes sense, they should always be available in the same coordinate system, no matter if in input
, gui_input
or unhandled_input
, which is easier to understand for users.
Also this would allow to repurpose code from gui_input
to unhandled_input
without changes and remove the requirement to recalculate the coordinates for _gui_input
.
I created as a first attempt a graphical representation of the transform hierarchy
Node._input(event) and Node.unhandled_input(event) InputEventMouse.get_global_position() should return the mouse's position in the root viewport (not window coordinates).
I suggest, that we clarify, what "root viewport" means, because this could have several different interpretations:
Nr 3 doesn't make sense when dealing with non embedded windows. Not sure, which of the two other two would make more sense. I have a slight preference for nr 2.
I believe, that I have found a better way to illustrate the different coordinate systems, transforms and functions. Red marks the spots, where I currently see inconsistencies.
Counter-proposal:
Make the caller context irrelevant; deprecate global
entirely; and be explicit about the coordinate system used
Requiring devs to interpret "global" differently depending on the context (where it's being called from, and on what it's being called) is an unnecessary brainpower drain IMO. So I suggest that the functions and properties themselves described the context, eg: viewport_position
, canvas_position
.
This then encourages the user to be informed about the differences between the various coordinate systems, and prompts them to think more carefully about which one they actually mean to use.
It also brings the added benefit that a single class could then expose multiple such values, freeing the user from needing to manually convert (often incorrectly IME :see_no_evil:) from one coordinate system to another so often. Eg: having both Node2D.canvas_position
and Node2D.viewport_position
.
Further, it reduces backward-compatibility breakage, since the various global_position
s can stick around, with their current inconsistent behaviour, until they can safely be removed in a subsequent major engine version.
For reference, https://github.com/godotengine/godot-proposals/issues/4250 suggests standardising get_xxx_transform()
nomenclature in a similar manner, so I would say the xxx_position
names should mirror those as much as possible.
+100 to making caller context irrelevant. Also +100 to standardising like #4250 suggests
Renaming functions is considered as breaking compatibility during v4.x, so that could be done only for a future v5.
I think you can still keep both versions to maintain compatibility (marking the old one as deprecated).
Renaming functions is considered as breaking compatibility during v4.x, so that could be done only for a future v5.
Yeah, I think some of the meaning in my original reply might've got lost in a pre-submit edit, but I wasn't suggesting to rename any of the existing functions, but instead add new ones with more specific names.
Thus no breaking changes are needed until it's time to remove the global_
stuff, which could be done at any opportune time.
Today I face this. I used even.global_position and got the same as event.position (for mouse_event). I spent some time figuring out that the position of the viewport is different from the world.
The part most confusing was trying to use node.to_local(event.position) as it was wrong because the global position of mouse event is different from world.
I've been spending the last couple hours simply trying to drag a control with my mouse. For the life of me, it is always offset in some way. This is incredibly confusing and needs better documentation/refactoring
Ran into this topic this week when trying to use touch inputs, which according to the docs are in "screen (global)" coordinates vs. the CanvasLayer
global coordinates provided by get_global_mouse_position()
and needed to properly position CanvasItem
nodes on the layer.
I ended up having to figure out the offset with something like this:
func get_touch_pos_offset():
# Works as long as the current scene's position is 0, 0 & a camera2d is present
return get_viewport().get_camera_2d().get_screen_center_position() - get_viewport_rect().size / 2
func _input(event):
var global_event_pos
if event is InputEventScreenTouch and event.is_pressed():
global_event_pos = event.position + get_touch_pos_offset()
else: global_event_pos = get_global_mouse_position()
# ...Input processing using global_event_pos ...
Emulating mouse clicks with taps generally hides the difference by letting you work with mouse coordinates exclusively, but isn't perfect - and when working with input positions directly the coordinate confusion gets pretty frustrating.
Glad I found this issue before I misdiagnosed this as a bug and filed a new issue; I think this boils down to an eccentricity of Godot coordinate systems and when which ones are applied.
Describe the project you are working on
Godot engine
Describe the problem or limitation you are having in your project
The word "global" implies that there is one "true" coordinate system that everything else is relative to. But there are at least five coordinate systems:
And, there can be multiples of each of these.
Godot receives mouse positions in window coordinates and it needs to translate these into the appropriate position in the appropriate coordinate system. When referring to
position
especiallyglobal_position
in properties and methods, it's often unclear which coordinate system this position is or even should be in.When working in 3D it's easy to notice the difference between world coordinates and viewport coordinates, because one is in 3D and one is in 2D. However, until one adds a camera to a 2D game, the difference between world coordinates, canvas layer coordinates and viewport coordinates is not obvious, because they're all the same. The current best explanation for the difference between all of these in the documentation is: Viewport and canvas transforms. However, it adds to the confusion by identifying two types of global canvas transforms:
As already described in #2139, adding viewports is confusing. However, even without adding additional worlds, canvas layers, viewports or even a camera, the user is expected to understand the difference between viewport coordinates and window coordinates (which persistently remains a source of confusion see #2822, #3769 and godotengine/godot#47522).
Clarifying the difference between viewport coordinates and window coordinates is required before we can even fix issues like godotengine/godot#25023 and godotengine/godot#28665.
In addition, before we can fix issues like godotengine/godot#30950,
get_mouse_position()
andwarp_mouse(position)
need to refer to the same coordinate system, but which one? Viewport or Window?Furthermore, when working with additional canvas layers or viewports i.e. there is a difference between the coordinate systems, even the conversion between the location of the mouse pointer and the position in the world fails (see godotengine/godot#30215 and godotengine/godot#35965).
However, before we can fix issues like godotengine/godot/issues/35965, we need to understand whether
CanvasItem.get_global_mouse_position()
should return the "global" mouse position in the the canvas layer or the viewport (see https://github.com/godotengine/godot/issues/35965#issuecomment-1013777262). Similarly, just asInputEventMouse
position
changes, not only depending on which node is interested in the event, but whether the it's received in the_input(event)
,_gui_event(event)
or_unhandled_event(event)
callback, should theglobal_position
change to reflect the appropriate coordinate system i.e. the (root) viewport or canvas layer? Note: It currently returns the mouse's position in the window coordinates, which is practically useless.Moreover, before we can fix issues like godotengine/godot#30215, we need to be able to answer questions like https://github.com/godotengine/godot/issues/30215#issuecomment-846606123:
These questions are all pre-requisites to fixing problems like godotengine/godot#48368 and godotengine/godot#20619, which is actually a reopening of godotengine/godot#1383, which was closed by disabling the functionality in e997c0d with the comment:
Finally, as identified in godotengine/godot#38444, even the definition of position in
Control
andNode2D
is inconsistent. And the difference between a global point and a local point shouldn't require the current lengthy explanations forto_global
andto_local
, but addressing these issues is beyond the scope of this proposal.Describe the feature / enhancement and how it helps to overcome the problem or limitation
Ensure that anything referring to
position
; especiallyglobal_position
uses the correct coordinate system. Just likeNode2D
andNode3D
position
s refer to their position in world coordinates, andCanvasItem
s'position
refers to their position inCanvasLayer
coordinates, any reference toposition
orglobal_position
should be in the same coordinate system as the caller.Specifically:
Viewport.get_mouse_position()
should return the mouse's position in thisViewport
.Viewport.warp_mouse(position)
should take a position in thisViewport
's coordinates (not window coordinates) i.e.Viewport.warp_mouse(get_mouse_position())
should do nothing.CanvasItem.get_global_mouse_position()
should return the mouse's position in theCavasLayer
that thisCanvasItem
is in.CanvasItem.get_local_mouse_position()
should return the mouse's position in thisCanvasItem
.Node._input(event)
andNode.unhandled_input(event)
InputEventMouse.get_global_position()
should return the mouse's position in the root viewport (not window coordinates).Node._input(event)
andNode.unhandled_input(event)
InputEventMouse.get_position()
should return the mouse's position in the viewport that thisNode
is in.Control._gui_input.(event)
InputEventMouse.get_global_position()
should return the mouse's position in theCanvasLayer
that thisControl
is in (not theViewport
it's in) i.e. it should be the same asCanvasItem.get_global_mouse_position()
.Control._gui_input(event)
InputEventMouse.get_position()
should return the mouse's position in thisControl
.More generally, when providing or returning a mouse's
position
it should never be in window (never mind screen) coordinates. Mappings between the game world and the mouse should always be via theViewport
's coordinates.Finally, we need to make a point of not using the word "screen" when we mean "window", or "window" when we mean "viewport". This includes doing a better job of explaining the difference between
Viewport
coordinates and window coordinates and how stretch modes work, because the current documentation is failing to do this (see #2479). This will be especially important for 4.0 since we now have a newWindow
class that derives fromViewport
.Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
N/A
If this enhancement will not be used often, can it be worked around with a few lines of script?
N/A
Is there a reason why this should be core and not an add-on in the asset library?
N/A