-
-
Notifications
You must be signed in to change notification settings - Fork 99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add dual view to the Game editor for editing and playing at the same time #11091
Add dual view to the Game editor for editing and playing at the same time #11091
Comments
Edit: I've moved this comment to a more appropriate location based on later conversation. |
To clarify, do you mean that this would be used to toggle a single view of the game between floating and embedded, or to create a duplicate view that is not embedded which is used for playing? If it's the first case, the point of this specific proposal is to be able to work with two views at the same time so I just want to reiterate that. This is a good idea for toggling between embedded and not embedded, however @Hilderin 's PR godotengine/godot#99010 seems to have already tackled how the embedded view switches (and is more relevant to embedding functionality) so if this about button styling I'd recommend bringing the mockup over there to see if it can gain traction :) |
Ah, I misunderstood this proposal! You're right, I should post in the other PR; I wasn't aware of this PR yet, and that's correct that my comment only applies to that PR and not this proposal. Thanks! (When I read "2D/3D edit mode", I interpreted that to mean "2D/3D workspace", but this was intended to describe the Game workspace when input is disabled and 2D/3D selection is active.) |
I took a look at what it would take to get this working, and I was able to get the editor UI set up to handle 2 game views: However, both
But, reading this code in HWND DisplayServerWindows::_find_window_from_process_id(OS::ProcessID p_pid, HWND p_current_hwnd) {
DWORD pid = p_pid;
WindowEnumData ed = { pid, p_current_hwnd, NULL };
// First, check our own child, maybe it's already embedded.
if (!EnumChildWindows(p_current_hwnd, _enum_proc_find_window_from_process_id_callback, (LPARAM)&ed) && (GetLastError() == ERROR_SUCCESS)) {
if (ed.hWnd) {
return ed.hWnd;
}
}
// Then check all the opened windows on the computer.
if (!EnumWindows(_enum_proc_find_window_from_process_id_callback, (LPARAM)&ed) && (GetLastError() == ERROR_SUCCESS)) {
return ed.hWnd;
}
return NULL;
} It seems that the window chosen to be embedded is just the first window in the "list of all windows" that matches the process ID. If I'm understanding that right, I'm not sure how this could be changed so that it searches for a specific window; I'm not sure if it's guaranteed that they will show up in a consistent order, and even if they did we would want to make sure that it's only embedding the new window created by the debugger, not some other user window. Rather than go around in circles, maybe it's best to get some expert direction on this... @Hilderin if you have a chance, could you let me know if I'm on the right track here? Or is there some better solution to showing 2 embedded views without adding more functionality to the display server? I'm fairly certain I can handle the editor adjustments needed to get everything functional, but when it comes to potentially big display server changes I'm a bit lost... Thanks if you or anyone else is able to provide ideas :) |
Very interesting!! If I understand correctly, you want to have multiple instances of the running game embedded in the editor? To embed correctly each game window, you need to get the process id of each game instance from |
Actually I'm trying to get multiple windows from the same instance of the running game to become embedded, not multiple instances of the game! During my experiments I did try enabling multiple instance runs from the Debug menu to see if that would make things easier, and you're right that there is a clear path that way, but both instances of the game would get out of sync if you started to make changes in one. That wouldn't really work well for this proposal since it's about having 2 views of the same running game with one view getting runtime select and the other view getting game input. So from my research (though I might be wrong) I think both windows of the game instance would share the same process ID, and in that case Feel free to let me know if this isn't a reasonable idea lol and thank you for the info! |
Sorry, I think I now understand correctly. Okay, you have two windows of the same process that you want to embed. The first thing you need is to ensure that both windows have the editor as their parent. I’m not sure how you create the second window, but it must have the editor's window ID in the Second, you need a way to differentiate between windows of the same process that share the same parent. One simple approach is to use the window title. If you know the expected window title, you could add a Here’s an example of how to retrieve a window's title: wchar_t title[256];
int length = GetWindowTextW(hWnd, title, sizeof(title) / sizeof(title[0]));
if (length > 0) {
print_line("Embedded window found: " + String::utf16((const char16_t *)title));
} else {
print_line("Embedded window found: no title");
} This approach could also be implemented for Linux later. The only issue with this suggestion is that the main game window's title could be modified via a game script, so you can't always rely on the default game window title. As a workaround, you could add an option in the |
Ah, perfect! I should've figured that you could retrieve the window name via the Windows API, so I will definitely try this out and see how it goes :) thank you!
I'm less familiar with the Linux API and don't have a Linux machine to test, I'll look into it and see what can be done but then is it necessary to have both Linux & Windows implemented in the same PR because it would modify the
Got it. Let's say there's a "window title" param that it checks for and if the title string is empty, it will do what it does now and just grab the first window it finds that matches the PID. To solve the issue of users changing window names while giving them the most control over debugging, I was thinking each GameView could have a dropdown for "Embedded Window" that could either be "First Available" or "Custom Name" allowing them to choose which game window gets embedded (with "First Available" being the default so it matches the current behavior). This seems like the best way for them to choose the window they want, because they might not know the order in which their multiple game windows will appear (i.e. no idea what "index" each window is in the list), but they will probably know the name of each window. Here's a mockup to sort of demonstrate the idea: Finally, would it make sense to first implement the "specific window embedding" Display Server changes as its own PR before tackling the editor stuff? Might be easier to pinpoint issues that way. Thanks again for the help and I appreciate the ideas! |
I suggest you make it work on Window to begin with and the adjustment could be made for Linux afterward in another PR. But yes, you will need to adjust the
That will probably cause some issue because the order of the window found in One alternative way could be to send the window id from the game procress to the editor. It could be done via the Check #ifdef DEBUG_ENABLED
if (EngineDebugger::get_singleton() && window_id == DisplayServer::MAIN_WINDOW_ID && !Engine::get_singleton()->is_project_manager_hint()) {
Array arr;
arr.push_back(tr_title);
EngineDebugger::get_singleton()->send_message("window:title", arr);
}
#endif Once you have the window id, it could be passed to the That way the drop down with the window list could even come from the real list of existing windows from the game. That's just another suggestion, I'm still not exactly sure how the second window will be created and exactly what you want to achieve :)
I really suggest you do all in the same PR (except maybe for Linux if you can't do this part). That way, you will be sure the adjustments in |
Gotcha, will do.
Isn't it fine for single-window apps though, which is how the embedding currently works? I guess I should clarify my current idea: Users can either pick the "dual screen" Game View or the "single screen" game view. The single screen game view works exactly as it does now, therefore the "first found" option should work fine for most cases by default (because it's identical to the way it functions currently). But let's say you as a user create a second window in your game (with a Window Node), then you might want that second window to become embedded in the Game View instead of the "main" one. Again, this is a current limitation because if you do this right now on 4.4, it's unclear which window becomes embedded* (in theory, but I learned something while coding this that I'll share below). But with the option of selecting the window to be embedded, you can eliminate uncertainty and give the user more control. Then this applies to the "dual screen" Game View by just giving you the same "first found" or "custom name" option, so for each Game View you can pick which window you want to see in each one. So in the end you'll only want to use Dual Screen/embedded window option when you have created another window yourself, it won't automatically create a new debug window for you. While it asks to the user to perform a few extra steps for the dual screen to become useful, I think this might be more powerful in the long run, especially if you have a dual-window game to begin with. That's just where my mind is at right now, it seems the most straightforward option, but you can let me know if this is confusing or has issues I'm not seeing! *One last thing I want to note is that technically only the "main window" i.e. the window that is loaded first from the game will be considered for embedding in 4.4 because it is the only one that receives an
This seems a little more challenging to implement, but I think I get the idea. However, wouldn't this mean you need to the run the game at least once for the list to be populated? And couldn't that list get out of date if the user deletes a custom window? I really like the idea of having the dropdown reflect the window names though, so if these drawbacks are fine, or if there are solutions to them, I can definitely give it a shot! 😄
For now I was just thinking the user will be responsible for creating any additional windows they want to see embedded. Hopefully that sounds good as a more flexible implementation instead of having the dual screen mode create a new debugging window
Okay! I'll merge the display server adjustments into the editor changes and get the whole idea up for one big PR then :) Thanks to your guidance I was able to get secondary windows embedded, here's an example of the user-created "DebugWindow" viewed normally: And here's that same DebugWindow (which is a subwindow) now embedded: Obviously I'll have to sort out some issues (passing data through the GameView, editor changes, potentially changing the embedded_windows cache system to track specific windows instead of whole processes) but I feel like I'm on the right track now, so thanks a ton! |
So it took a fair amount of code changes (hopefully that's okay for the PR 😅 ) but I've got everything working together now and here's what I came up with: you can now choose between the "Main Window" and a "Custom Window", and when "Custom Window" is selected you get to input the title of the window that should become embedded in that view. "Main Window" works the same as it does now (where only the first loaded window in your app, what Godot calls the Main Window, becomes embedded) and is hopefully less confusing than "first found". Here's all of that in action: Capture2025-02-03.17-53-25.movI want to stress that I'm showing all this in the proposed "dual screen" mode, which you can toggle. By default, you'll still only be working with one game view, and the "embedded window selector" works even with one Game View :) so I very much hope both of those are welcome features! What remains is figuring out how to activate the debugger systems based on the focused window (which honestly sounds a little challenging now that I've thought about it but at least it should be possible) and polishing up the code and user workflow as best I can. My question for @Hilderin is: in order to get the custom window embedding to work, I had to send information to the built game from the editor so that the subwindow knows whether it should be parented or not. I learned that the existing embedding works by passing a Lastly, I just want to make sure the work I'm doing is as useful as possible and integrates smoothly with the codebase so if anyone has questions or ideas that I should look into feel free to let me know, thanks! |
That sounds a good approach to me.
Just be sure the dual view is not enabled by default and could be opt in easily. I believe most games created in Godot use only a single window. I'm not exactly sure how, but one potentially useful feature for many users would be having "built-in" secondary views/windows. For example, the override camera could appear in a second window without requiring any additional code in the game. I'm not an expert in long-term Godot game development, so maybe others have more ideas. |
Yup, I implemented the split screen selector as a radio button in the embed options menu as I had it in the mockup above, so it'll be easily accessible and off by default.
I was thinking that too originally, but it seemed too challenging for now. As I wrote somewhere above, I had wanted the debugger to be able to send a signal that creates a new window node and automatically had it become embedded, but doing that would still require solving all the stuff I did already. It would be nice but I can live without it since it's only a few steps to make a debugging window yourself. Hopefully that part can be added for a future PR as I think subwindow embedding is more generally useful for people who make multi-window games. Anyways thanks for keeping me on track! It'll take a bit to clean everything up but I have a clear path towards the PR :) |
Thanks to the help I've received from Hilderin I've opened up the PR that will close this issue so feel free to share your support here: godotengine/godot#104079 There are a few compromises that had to be made which can be mitigated in the future now that this core work is complete. I'll list them here for clarity:
As you can see, the current PR is just the first step towards a more streamlined debugging process, but I think this is a good point to split the work so that it's not too drastic of a change all at once. Appreciate the ideas and I'll be sure to keep an eye out for future suggestions :) |
Describe the project you are working on
Improvements for the Godot editor.
Describe the problem or limitation you are having in your project
Now that godotengine/godot#97257 has been merged and embedding to follow soon, I wanted to follow up on an idea by @ccioanca which would I believe would vastly improve the workflow of this new feature. The problem comes from having to press a button to switch between 2D/3D edit mode and game input on the same viewport.
In the example given, this makes it impossible to live edit through the new runtime tools while someone else plays the game, say for a presentation. Also, let's say that some animation glitch occurs when the player interacts with a lever and you wanted to take a closer look at it, but it's out of view of the player's regular camera. It would be really difficult to view the glitch up close in this case because you may have to toggle input/camera control to start the interaction, at which point you may miss the important moment you're trying to debug. Obviously you could code around this for specific cases to test, but it creates a larger burden on the developer for something that could already be available to them if more tooling options were available.
Describe the feature / enhancement and how it helps to overcome the problem or limitation
A good way to remedy these issues would be to have 2 views of the game, one with game input and one with the new runtime selection active. With this, you could set up a debugging view of the game before key moments happen and watch them occur from both the editor view and the player's view as you play through your project. This an approach other game engines use and it's extremely convenient for getting more visibility on complicated game worlds. Example:
Additionally, it would be neat to get options for different combinations of displaying the game, like edit view embedded but play view in its own window, or both views embedded, or both in their own window. I believe Unreal has somewhat similar options letting you choose how the game is run and how you debug it. Would definitely be tough to implement but I think the more customizable it is the better :)
Finally, a tangentially related idea would be that if we ever implement #7233 or something similar with fully customizable panels, you could sort of get this feature for free. It would be neat for the user to be able to create as many views into the game as they want and create their own layouts for editing/testing, but this is much further away of course, so it's best left for another time, just something to keep in mind.
Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
From a UX standpoint, I think the Game tab could look something like this:
For now, to preserve the original behavior, we could have something similar to the View dropdown in the regular editor:
Which lets you toggle between 1 embedded view or 2 embedded views, and then whichever button will be used to "pop out" the game in the upcoming embedding PR can be applied with a separate button for each view. This way, both can be embedded or standalone at user discretion.
From a programming standpoint, the key challenge is passing input into one view and not into the other view. I don't have a lot of ideas as to how this will be possible, but mainly we would need to cull out typical input events when the game view is not focused. Conversely, editor hotkeys, etc. should not be used when the edit view is not focused. This would involve setting the "input is active" bool that was added for runtime debugging (called
disable_input
) when the user focuses on a specific view (either by mouse hover or mouse click into the view) rather than when they click the game input button.However, this approach won't work for someone playing and editing at the same time. For now, I think the above idea is a reasonable start/initial PR, but to solve the problem of simultaneous view input, we would probably have to track the viewport source of each input event and cull out editor-specific keys/mouse events based on that. For rendering the individual views, we simply have to force the game to use another Viewport and add an additional piece of info to the viewport telling us whether it is in edit mode or not. I'm glossing over a bunch of details but I believe these basic concepts can kick off more detailed ideas. I'm happy to discuss implementation caveats/approaches if there's any suggestions!
If this enhancement will not be used often, can it be worked around with a few lines of script?
It can not be worked around easily.
Is there a reason why this should be core and not an add-on in the asset library?
All users would benefit from having additional options for debugging/running their game :)
The text was updated successfully, but these errors were encountered: