- ScottFromDerby
- Posts: 9
- Joined:
Multiple Inputs (Splitscreen)
Hi!
We are looking to allow multiple users to interact with a subsection of the screen in our Windows app. I.e. a splitscreen pause menu, where 2 or more players can interact with their own menu independently and simultaneously. We have one xaml and multiple viewmodels:
This produces a window with two stack panels. I want to forward inputs from one gamepad to one window, and the other to the other.
Here is how I am attempting to do this. I parse the xaml, create one view for the whole screen using Noesis::GUI::CreateView(), then create 2 more views, passing in StackPanelContainer1 and StackPanelContainer2 as root.
We can isolate inputs from the gamepads and push them to the appropriate IView. The Focus on the last line appears to succeed, but interaction doesn't work, and causes an assert during GetNextDirection, at element->TransformToAncestor(root). root is the Noesis::View::RootVisual, but doesn't appear to be the one in the main scene. Calling GetView() on the appropriate elements seems to return what I would expect (the set View appears to propagate properly), but I feel I must be missing something.
Any advice greatly appreciated!
Cheers,
Scott
Edit: Clarification
We are looking to allow multiple users to interact with a subsection of the screen in our Windows app. I.e. a splitscreen pause menu, where 2 or more players can interact with their own menu independently and simultaneously. We have one xaml and multiple viewmodels:
Code: Select all
<Window
x:Class="OurProject.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:OurProject"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:noesis="clr-namespace:NoesisGUIExtensions"
mc:Ignorable="d"
Title="TestWindow"
Background="{x:Null}">
<Viewbox
Margin="0">
<Grid x:Name="mainCanvas" Width="1920">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Viewbox Name="StackPanelContainer1" HorizontalAlignment="Left" Grid.Column="0">
<StackPanel Name="StackPanel1" Margin="10" Width="80">
<ToggleButton Name="Btn11" Content="Left 1"/>
<ToggleButton Name="Btn12" Content="Left 2"/>
<ToggleButton Name="Btn13" Content="Left 3"/>
<ToggleButton Name="Btn14" Content="Left 4"/>
<ToggleButton Name="Btn15" Content="Left 5"/>
</StackPanel>
</Viewbox>
<Viewbox Name="StackPanelContainer2" HorizontalAlignment="Right" Grid.Column="1">
<StackPanel Name="StackPanel2" Margin="10" Width="50">
<ToggleButton Name="Btn21" Content="Right 1"/>
<ToggleButton Name="Btn22" Content="Right 2"/>
<ToggleButton Name="Btn23" Content="Right 3"/>
</StackPanel>
</Viewbox>
</Grid>
</Viewbox>
</Window>
Here is how I am attempting to do this. I parse the xaml, create one view for the whole screen using Noesis::GUI::CreateView(), then create 2 more views, passing in StackPanelContainer1 and StackPanelContainer2 as root.
Code: Select all
OurProject* pTheMainWindow = [...]; // : public Noesis::ContentControl
Noesis::StackPanel* pPanel1 = pTheMainWindow->FindName<Noesis::StackPanel>("StackPanel1");
Noesis::StackPanel* pPanel2 = pTheMainWindow->FindName<Noesis::StackPanel>("StackPanel2");
Noesis::Ptr<Noesis::IView> pNewView1 = Noesis::GUI::CreateView(pPanel1);
Noesis::Ptr<Noesis::IView> pNewView2 = Noesis::GUI::CreateView(pPanel2);
// All Noesis::Ptrs stored off, etc
pPanel1->DisconnectFromView();
pPanel1->ConnectToView(pNewView1);
pPanel2->DisconnectFromView();
pPanel2->ConnectToView(pNewView2);
pPanel1->GetChildren()->Get(0)->Focus();
Any advice greatly appreciated!
Cheers,
Scott
Edit: Clarification
-
sfernandez
Site Admin
- Posts: 2991
- Joined:
Re: Multiple Inputs (Splitscreen)
Hello,
The problem is you cannot have the same Visual used in different Views, they can only be owned by a single View. The StackPanels cannot be defined at root View and used by the inner Views at the same time. Although I see you are trying to Disconnect and Connect to the newly created views, that was not the expected use. You are probably getting some errors saying that pPanel1/2 already have a Visual parent when the inner Views are created.
You should use 2 independent Views with their own xamls, and render them to the corresponding part in the screen.
If you need to have a common UI then you'll probably need a third View (unless you can render that as part of one of the players UI).
Could you try that and let us know?
The problem is you cannot have the same Visual used in different Views, they can only be owned by a single View. The StackPanels cannot be defined at root View and used by the inner Views at the same time. Although I see you are trying to Disconnect and Connect to the newly created views, that was not the expected use. You are probably getting some errors saying that pPanel1/2 already have a Visual parent when the inner Views are created.
You should use 2 independent Views with their own xamls, and render them to the corresponding part in the screen.
If you need to have a common UI then you'll probably need a third View (unless you can render that as part of one of the players UI).
Could you try that and let us know?
- ScottFromDerby
- Posts: 9
- Joined:
Re: Multiple Inputs (Splitscreen)
Thank-you so much for your help! Trying this approach now, will get back to you asap
- ScottFromDerby
- Posts: 9
- Joined:
Re: Multiple Inputs (Splitscreen)
Got it now functional with your advice! Thanks for your help!
After calling Noesis::GUI::LoadXaml for our core Uri, we now additionally facilitate extra Uris to new Windows to be allocated (also via Noesis::GUI::LoadXaml), which themselves have a single associated view set up each. We then additionally update and render these windows alongside the main window, after having interrogated the resultant windows and removing some of the unwanted (duplicate) elements.
It seems important that these Xaml do not know about each-other, as attaching them to each-other quickly causes asserts within Noesis. This is not a problem, we can happily manage these bespoke Xaml alongside the main window safely.
Thanks again for your help!
After calling Noesis::GUI::LoadXaml for our core Uri, we now additionally facilitate extra Uris to new Windows to be allocated (also via Noesis::GUI::LoadXaml), which themselves have a single associated view set up each. We then additionally update and render these windows alongside the main window, after having interrogated the resultant windows and removing some of the unwanted (duplicate) elements.
It seems important that these Xaml do not know about each-other, as attaching them to each-other quickly causes asserts within Noesis. This is not a problem, we can happily manage these bespoke Xaml alongside the main window safely.
Thanks again for your help!
-
sfernandez
Site Admin
- Posts: 2991
- Joined:
Re: Multiple Inputs (Splitscreen)
Great it finally worked, but there a couple of things I don't understand:
What do you mean about duplicate elements?after having interrogated the resultant windows and removing some of the unwanted (duplicate) elements.
Do you mean moving elements from one view to another? What kind of asserts do you get? If you correctly remove an element from the tree, you should be able to add it to another view if they were created in the same thread.It seems important that these Xaml do not know about each-other, as attaching them to each-other quickly causes asserts within Noesis.
- ScottFromDerby
- Posts: 9
- Joined:
Re: Multiple Inputs (Splitscreen)
In our approach, we have one main window xaml, and one "SplitscreenElementsContainer" xaml which we instantiate multiple of. We only want the appropriate part of the splitscreen elements, and in our approach we want to fully define all splitscreen elements within one single container. We have code that can associate a view with a gamepad, so that the inputs uniquely filter through to exactly one of the views. Xaml pseudocode:
RemoveExcept() would iterate over children and remove any that do not match the given string tag. RestrictInput() would prevent later Updates from interacting with these views.
The asserts are typically to do with either KeyboardNavigation failing to find focus, or something similar. I'll provide further details asap.
Edit: on reflection, you're right it ought to be possible to do this without ending up with duplicate elements, will keep working on it
Code: Select all
MainWindow.xaml
<Window/>
Code: Select all
SplitscreenElementsContainer.xaml
<Window>
<StackPanel Tag="PLAYER_ONE_ONLY"/>
<StackPanel Tag="PLAYER_TWO_ONLY"/>
<StackPanel Tag="PLAYER_THREE_ONLY"/>
</Window>
Code: Select all
App.cpp
Noesis::Ptr<ProjectWindow> pMainWindow = Noesis::GUI::LoadXaml("MainWindow.xaml");
Noesis::Ptr<ProjectView> pMainView = Noesis::GUI::CreateView(pMainWindow);
Noesis::Ptr<ProjectWindow> pSubWindow1 = Noesis::GUI::LoadXaml("SplitscreenElementsContainer.xaml");
pSubWindow1->RemoveExcept("PLAYER_ONE_ONLY");
Noesis::Ptr<ProjectView> pSubView1 = Noesis::GUI::CreateView(pSubWindow1);
Noesis::Ptr<ProjectWindow> pSubWindow2 = Noesis::GUI::LoadXaml("SplitscreenElementsContainer.xaml");
pSubWindow2->RemoveExcept("PLAYER_TWO_ONLY");
Noesis::Ptr<ProjectView> pSubView2 = Noesis::GUI::CreateView(pSubWindow2);
// If using the splitscreen windows:
pMainView->RestrictInput(None);
pSubView1 ->RestrictInput(Gamepad_1);
pSubView2 ->RestrictInput(Gamepad_2);
// If not
pMainView->RestrictInput(Any);
pSubView1 ->RestrictInput(None);
pSubView2 ->RestrictInput(None);
The asserts are typically to do with either KeyboardNavigation failing to find focus, or something similar. I'll provide further details asap.
Edit: on reflection, you're right it ought to be possible to do this without ending up with duplicate elements, will keep working on it
-
sfernandez
Site Admin
- Posts: 2991
- Joined:
Re: Multiple Inputs (Splitscreen)
The way I see it which won't require searching for duplicates and removing elements:
MainWindow.xaml
Menu.xaml
When you only have 1 player:
When you have more players:
"menuPlayer1View" will be rendered on the left side of the screen, and gamepad 1 events will only be injected to that view.
"menuPlayer2View" will be rendered on the right side of the screen, and gamepad 2 events will only be injected to that view.
MainWindow.xaml
Code: Select all
<Window>
...
<Grid x:Name="MenuContainer"/>
</Window>
Code: Select all
<StackPanel>
<Button.../>
...
</StackPanel>
Code: Select all
Ptr<Window> window = GUI::LoadXaml<Window>("MainWindow.xaml");
Ptr<IView> mainView = GUI::CreateView(window);
Ptr<StackPanel> menu = GUI::LoadXaml<StackPanel>("Menu.xaml");
Grid* menuContainer = mainWindow->FindName<Grid>("MenuContainer");
menuContainer->GetChildren()->Add(menu);
Code: Select all
Ptr<Window> window = GUI::LoadXaml<Window>("MainWindow.xaml");
Ptr<IView> mainView = GUI::CreateView(window);
Ptr<StackPanel> menuPlayer1 = GUI::LoadXaml<StackPanel>("Menu.xaml");
Ptr<IView> menuPlayer1View = GUI::CreateView(menuPlayer1);
Ptr<StackPanel> menuPlayer2 = GUI::LoadXaml<StackPanel>("Menu.xaml");
Ptr<IView> menuPlayer2View = GUI::CreateView(menuPlayer2);
"menuPlayer2View" will be rendered on the right side of the screen, and gamepad 2 events will only be injected to that view.
- ScottFromDerby
- Posts: 9
- Joined:
Re: Multiple Inputs (Splitscreen)
Love it! Your explanation is really clear, it really made things understandable.
Our UI department had created the splitscreen menu by creating a xaml that contained four menus, one for each splitscreen player. I originally planned to keep the xaml as-is, instantiate it four times, then remove the unwanted elements.
It's much simpler (now in our current approach) to have a xaml with a single menu in, then instantiate that four times, once for each player, and manually offset the margin for each menu. It's all working perfectly with this approach.
Thank-you again so much for your well-written examples, we really appreciate it!
Edit: Forgot to mention, the assert we encountered was because we were calling
in our main loop, and with multiple windows we were calling
and it should be:
Our UI department had created the splitscreen menu by creating a xaml that contained four menus, one for each splitscreen player. I originally planned to keep the xaml as-is, instantiate it four times, then remove the unwanted elements.
It's much simpler (now in our current approach) to have a xaml with a single menu in, then instantiate that four times, once for each player, and manually offset the margin for each menu. It's all working perfectly with this approach.
Thank-you again so much for your well-written examples, we really appreciate it!
Edit: Forgot to mention, the assert we encountered was because we were calling
Code: Select all
Frame: UpdateRenderTree, RenderOffscreen, Render
Code: Select all
Frame: UpdateRenderTree(0), RenderOffscreen(0), UpdateRenderTree(1), RenderOffscreen(1), Render(0), Render(1)
Code: Select all
Frame: UpdateRenderTree(0), UpdateRenderTree(1), RenderOffscreen(0), RenderOffscreen(1), Render(0), Render(1)
Who is online
Users browsing this forum: Bing [Bot], Google [Bot] and 18 guests