godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.08k stars 69 forks source link

Add a scene instance at cursor position when right-clicking in the 3D editor (already implemented in the 2D editor) #435

Open KoBeWi opened 4 years ago

KoBeWi commented 4 years ago

Describe the project you are working on: 2D game with lots of different custom nodes.

Describe the problem or limitation you are having in your project: I instance scenes a lot. Well, this is pretty common, lots of users asked for "scene tiles". But I don't even use TileMaps, so I instance nodes manually anyways. I even bound a shortcut to Instance Child Scene to quickly spawn scenes. My problem is that the instances all always created at the origin. You can of course drag and drop from file system at desired position, but due to sheer amount of different scenes and the way my files are organized, this is non-viable. It's much easier for me to press shortcut, fuzzy-search the scene name and press Enter. But then I need to move the node from (0, 0) to positions like (1200, 4600), which wastes my time.

Describe how this feature / enhancement will help you overcome this problem or limitation: Would be cool if we had an option to instance a scene at the cursor position. Could be with Ctrl + Click or maybe then Selection List could be utilized (like, showing Add Instance on top of node list, dunno).

Show a mock up screenshots/video or a flow diagram explaining how your proposal will work: image image image (actually, it would be useful to have there "Add Child node" too)

Describe implementation detail for your proposal (in code), if possible: It's basically Instance Child Scene, but called from 2D viewport and positioning the node at cursor.

If this enhancement will not be used often, can it be worked around with a few lines of script?: It might be possible to replicate the node selection dialog and add a CanvasEditor plugin to do that, but I imagine it would be really cumbersome. Although if the quick open dialog was exposed to plugins... Still, that's more than few lines.

Is there a reason why this should be core and not an add-on in the asset library?: Not sure how many people have similar problem, but I can't be the only one (;_;). And it's a probably small QoL thing using already existing functionality in a way impossible for custom plugins.

Zireael07 commented 4 years ago

+1. I am working on a space game at a realistic scale, so you can guess stuff's placed really far from origin. Requesting similar thing for 3D, too. The distances involved aren't quite as large, but still, having to move stuff manually from origin when you have a playing field of 500x500 metres or so is a pain.

Jummit commented 4 years ago

but due to sheer amount of different scenes and the way my files are organized, this is non-viable.

You can favorite them, that's what I do when I need to instance a scene often. Also, a plugin like Scene Scattering Tool for 2D would solve this as well, maybe I'll look into making it.

KoBeWi commented 4 years ago

You can favorite them

And have hundreds of favorites? Well, I could only favorite the scenes I need right now, but there are some scenes I need pretty much always and I'd have to favorite/unfavorite other scenes a lot. That sounds really messy.

Although I never used favorites besides favorite nodes, I'll try it at least...

Jummit commented 4 years ago

Yeah, favorites get really clumsy when you have more than ten, but if you only have a few it works really great.

groud commented 4 years ago

Maybe we could implement a "paint mode" ? Where you could select a scene and instantiate it in one click on the viewport ? That could help in games where you have a lot of collectible to place on the screen.

KoBeWi commented 4 years ago

Maybe we could implement a "paint mode" ? Where you could select a scene and instantiate it in one click on the viewport ?

Could be useful, but we'd also need a preview of where the instance will be placed (obviously).

Also I just realized this could be useful for built-in nodes, not only instances. I just had to create a Node2D and move it very long distance :I (with no other node to use reparenting trick). I'll update the OP.

willnationsdev commented 4 years ago

@groud

Maybe we could implement a "paint mode" ? Where you could select a scene and instantiate it in one click on the viewport ?

I feel like this and some sort of easy-to-use grid placing of elements, complete with a layer system, would effectively fix a lot of people's problems with TileMap/GridMap by migrating the features people expect out of those nodes to the engine as a whole. I could easily imagine having a more Tiled or GameMaker: Studio like experience of painting instances into the world in specific grid locations (toggleable versus world space) and being able to create layers of elements without having to parent them under a specific node. Editor-level node groups would go a long way towards that since you'd be able to filter by group. There's already a WIP PR for adding editor-time global groups too.

Edit: I should probably convert this itself into an alternative proposal that could supercede a number of other proposals/Issues if done correctly.

KoBeWi commented 4 years ago

Although I never used favorites besides favorite nodes, I'll try it at least...

After some time of using favorites, I started to get lost in the list (17 right now). The problem is that they are really messy. There are some scenes I instance less frequently, but I still need them now and then. Also I often add something new, or add something temporarily, because I need it now and then almost not anymore. It makes it hard to sort the favorites, also it became hard to distinguish them and find the one I look for (some custom icons would be helpful there).

tl;dr I just confirmed that using favorites is not a viable alternative

mnemoli commented 3 years ago

I've started a paint mode plugin for 3D. I know there are existing 'scatter' plugins but I believe what people are looking for here is a simple single-object click-to-place functionality. https://github.com/mnemoli/godotplacer (There are a lot of things I would do differently if this were integrated into the engine code - for example, I'd like it to be an actual tool, but it looks like the tool set is not currently extendable via plugins.)

Unity and UE4 do not come with this functionality built-in. I think it'd really give Godot a productivity edge if we did have something like this in the main engine.

chucklepie commented 3 years ago

An alternative to right-click (not saying any way is better) would be to provide this via the dialog, this way there is still just one way to add a scene instance and less work. It's less fine grained but once placed you will always be moving it regardless.

image

KoBeWi commented 3 years ago

Except I already implemented it and been using it for months in a custom build: https://github.com/godotengine/godot/pull/41437 It proved to me to be very handy and with grid snapping enabled you can easily instance the node exactly where you want.

chucklepie commented 3 years ago

Which stable release will this make it in?

What would be really nice is this same feature but allow to move selected node on scene inspector to this position also 😁

On Mon, 1 Feb 2021, 12:30 Tomek, notifications@github.com wrote:

Except I already implemented it and been using it for months in a custom build: godotengine/godot#41437 https://github.com/godotengine/godot/pull/41437 It proved to me to be very handy and with grid snapping enabled you can easily instance the node exactly where you want.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/godotengine/godot-proposals/issues/435#issuecomment-770821820, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADCERRLKMCLNNYF5ZQDXSCDS42NH5ANCNFSM4KPJIL3A .

KoBeWi commented 3 years ago

3.2.4 won't get new features anymore, so this won't be added until 3.2.5 at least (if 3.2 goes that far).

KoBeWi commented 3 years ago

Implemented for 2D with https://github.com/godotengine/godot/pull/41437 The 3D counterpart could be done in similar way probably.

EDIT: Also the last comment aged poorly...

jordanlis commented 2 years ago

Another simple way to do that would be to add the scene in the active view. For example, let say you've panned the scene and that you are displaying in the editor a square starting from the coordinates X = 1000 and Y = 1000. Then, when you instance a scene, you create it in the active view at the displayed part of the scene, not the origin. If needed, we could add a setting where the customer could put his preference : instance at the origin or instance in the active view (or at the mouse coordinates) by default.

I don't have godot right now on this PC, but I think that you can instance scenes from the file explorer in godot, and then drag and drop the scene in the active view ? It is a small workaround, but this feature is still a must have (and I see it has been developed so this is really great ! Thanks to the devs ;p )

Dadaskis commented 1 year ago

@Calinou written a suggestion in the closed issue mentioned above. I think it deserves to be here:

Creating a node at the current camera position may be confusing. Instead, it may be better to queue node creation until you click somewhere, and create the node at the clicked location (using a raycast to determine where to place the node exactly).

me2beats commented 1 year ago

I'm for having it as a keyboard shortcuts as well. And an option in Editor Settings to disable this for RMB, because I often move right mouse to rotate the viewport camera

Dadaskis commented 11 months ago

I think this is the best place to share some code snippets for editor plugins, if you don't mind.

In this code, a new node is created. Here it is just "Spatial" but i think you can add more types quite easily. If you want - we can discuss it, but for now here it is, the bare-minimum editor plugin code that allows you to create a node in a view space, with a little bonus such as raycast-check that will place the node on the hit position if it hits anything.

It is written for Godot 3.5.1. and works on that one version. This is a hacky way, but worth sharing anyway.

tool
extends EditorPlugin

var camera: Camera
var current_scene

# Let's assume that somehow you call this one function
# through beautiful dock UI or something like that
func create_node():
    var node = Spatial.new()

    current_scene.add_child(node)
    node.owner = current_scene 
    # ^^^ Make sure to set owner to make it show up in editor

    # Just saying...
    # This way you can check if the node you are creating has transforms
    # This is useful if you are creating plugin that may create the ones
    # without transforms like WorldEnvironment and others
    #if node.get("transform") == null:
    #   return

    # Just doing a relatively simple raycast check
    # If it hits the surface, it will place the node right at the hit position
    # If it's not, placing somewhere in view space
    var space_state = \
        node.get_world().direct_space_state as PhysicsDirectSpaceState
    var forward = -camera.global_transform.basis.z
    var ray_start = camera.global_transform.origin
    var ray_end = ray_start + (forward * 10)
    var ray = space_state.intersect_ray(ray_start, ray_end)
    if ray:
        node.global_transform.origin = ray.position
    else:
        node.global_transform.origin = camera.global_transform.origin
        node.global_transform.origin += (-camera.global_transform.basis.z * 3.0)
        node.global_transform.origin += (-camera.global_transform.basis.y * 1.0)

    # That's it, you have "Spatial" placed near your view.
    # This is really, really minimalistic recreation of logic that my own
    # plugin is using, i hope it's going to be helpful for others.

func forward_spatial_gui_input(camera, event):
    camera = camera
    current_scene = get_tree().get_edited_scene_root()
    return false

func handles(node):
    if node.is_class("Resource"): 
        # Ignoring resources because it can cause deadly lag.
        return false
    return true

func _enter_tree():
    set_input_event_forwarding_always_enabled()