locked
SwapChainBackgroundPanel letterboxing Monogame Windows Store App

    Question

  • I am porting my space shooter game from Windows Phone to Windows Store App. In WP it always play in full portrait orientation.


    For the Windows Store app though while in landscape mode, I want to center the game screen with letterboxing on the left and right. The problem is I can't adjust the margin property of  `SwapChainBackgroundPanel` so the game always aligned to the left and the black screen is on the right.

    Here's my code

     public Game1()
            {
                graphics = new GraphicsDeviceManager(this);
                GamePage.Current.SizeChanged += OnWindowSizeChanged;
                Content.RootDirectory = "Content";
            }
    
            private void OnWindowSizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
            {
                var CurrentViewState = Windows.UI.ViewManagement.ApplicationView.Value;
                double width = e.NewSize.Width;
                double height = e.NewSize.Height;
    
                // using Windows.Graphics.Display;
                ResolutionScale resolutionScale = DisplayProperties.ResolutionScale;
                string orientation = null;
    
                if (ApplicationView.Value == ApplicationViewState.FullScreenLandscape)
                {
                    orientation = "FullScreenLandscape";
                    //Does not work because it's start on the center of the screen
                    //Black screen is on the left and place the game screen on the right
                    GamePage.Current.HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Center;
                    
                    //Gives error - WinRT information: Setting 'Margin' property is 
                    //not supported on SwapChainBackgroundPanel.
                    GamePage.Current.Margin = new Thickness(centerMargin, 0, 0, 0);
                }
    
                else if (ApplicationView.Value == ApplicationViewState.FullScreenPortrait)
                {
                    orientation = "FullScreenPortrait";
                }
                else if (ApplicationView.Value == ApplicationViewState.Filled)
                {
                    orientation = "Filled";
                }
                else if (ApplicationView.Value == ApplicationViewState.Snapped)
                {
                    orientation = "Snapped";
                }
    
                Debug.WriteLine("{0} x {1}. Scale: {2}. Orientation: {3}", 
                    width.ToString(), height.ToString(), resolutionScale.ToString(),
                    orientation);
            }



    The GamePage.xaml is the default

        <SwapChainBackgroundPanel
            x:Class="SpaceShooterXW8.GamePage"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="using:SpaceShooterXW8"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            mc:Ignorable="d">    
        </SwapChainBackgroundPanel>


    Monday, April 14, 2014 5:12 AM

Answers

  • You are right Rob.

    After some researched I think I've figured it out thanks to this blog post. To those who are in a similar situation, here's what I did.

    The beauty of the solution is that the letterboxing is automatically managed by the `Resolution` class. All I have to do is update the `batch.begin()` lines in my code to something like

              batch.Begin(SpriteSortMode.Deferred,
                    null, SamplerState.LinearClamp ,
                    null,
                    null,
                    null,
                    Resolution.getTransformationMatrix());




    To handle resolution changes as the orientation changed I use this in my `Game1.cs`

        public Game1()
            {
                graphics = new GraphicsDeviceManager(this);
                GamePage.Current.SizeChanged += OnWindowSizeChanged;
                Content.RootDirectory = "Content";
                Resolution.Init(ref graphics);
                Resolution.SetVirtualResolution(480, 800);
            }
    
    
        private void OnWindowSizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
            {
                var CurrentViewState = Windows.UI.ViewManagement.ApplicationView.Value;
                App.AppWidth = (int)e.NewSize.Width;
                App.AppHeight = (int)e.NewSize.Height;
                Resolution.SetResolution(App.AppWidth, App.AppHeight, true);
            }



    The initial values of `App.AppWidth`  and `App.AppHeight` is set in the `GamePage.xaml.cs`.

        public GamePage(string launchArguments)
            {
                this.InitializeComponent();
                App.AppWidth = (int)Window.Current.Bounds.Width;
                App.AppHeight = (int)Window.Current.Bounds.Height;
                Current = this;
                // Create the game.
                _game = XamlGame<Game1>.Create(launchArguments, Window.Current.CoreWindow, this);
            }




    Both are global static  property created in the `App.xaml.cs`

            public static int AppWidth { get; set; }
            public static int AppHeight { get; set; }

    The only problem I've encountered so far, the mouse input does not scale to the screen resolution change. I do not have a touch screen monitor to test unfortunately but I think touch input should scale. If anyone tested touch, please share your findings. Thanks.

    Update

    I've managed to scale the Mouse input using:

            public static Vector2 ScaleGesture(Vector2 position)
            {
                int x = (int)(position.X / (float)App.AppWidth * (float)Screen.ScreenWidth);
                int y = (int)(position.Y / (float)App.AppHeight * (float)Screen.ScreenHeight);
                var scaledPosition = new Vector2(x, y);
                //Debug.WriteLine("scaledPosition: " + scaledPosition);
                return scaledPosition;
            }


    • Marked as answer by Sabahan Tuesday, April 15, 2014 3:17 AM
    • Edited by Sabahan Tuesday, April 15, 2014 5:26 AM
    Tuesday, April 15, 2014 3:01 AM

All replies

  • The SwapChainBackgroundPanel is always the app's full window. You'll need to do your letterboxing within your DX rendering. If you're writing for Windows 8.1 then check out the SwapChainPanel class instead.
    Monday, April 14, 2014 3:58 PM
    Owner
  • You are right Rob.

    After some researched I think I've figured it out thanks to this blog post. To those who are in a similar situation, here's what I did.

    The beauty of the solution is that the letterboxing is automatically managed by the `Resolution` class. All I have to do is update the `batch.begin()` lines in my code to something like

              batch.Begin(SpriteSortMode.Deferred,
                    null, SamplerState.LinearClamp ,
                    null,
                    null,
                    null,
                    Resolution.getTransformationMatrix());




    To handle resolution changes as the orientation changed I use this in my `Game1.cs`

        public Game1()
            {
                graphics = new GraphicsDeviceManager(this);
                GamePage.Current.SizeChanged += OnWindowSizeChanged;
                Content.RootDirectory = "Content";
                Resolution.Init(ref graphics);
                Resolution.SetVirtualResolution(480, 800);
            }
    
    
        private void OnWindowSizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
            {
                var CurrentViewState = Windows.UI.ViewManagement.ApplicationView.Value;
                App.AppWidth = (int)e.NewSize.Width;
                App.AppHeight = (int)e.NewSize.Height;
                Resolution.SetResolution(App.AppWidth, App.AppHeight, true);
            }



    The initial values of `App.AppWidth`  and `App.AppHeight` is set in the `GamePage.xaml.cs`.

        public GamePage(string launchArguments)
            {
                this.InitializeComponent();
                App.AppWidth = (int)Window.Current.Bounds.Width;
                App.AppHeight = (int)Window.Current.Bounds.Height;
                Current = this;
                // Create the game.
                _game = XamlGame<Game1>.Create(launchArguments, Window.Current.CoreWindow, this);
            }




    Both are global static  property created in the `App.xaml.cs`

            public static int AppWidth { get; set; }
            public static int AppHeight { get; set; }

    The only problem I've encountered so far, the mouse input does not scale to the screen resolution change. I do not have a touch screen monitor to test unfortunately but I think touch input should scale. If anyone tested touch, please share your findings. Thanks.

    Update

    I've managed to scale the Mouse input using:

            public static Vector2 ScaleGesture(Vector2 position)
            {
                int x = (int)(position.X / (float)App.AppWidth * (float)Screen.ScreenWidth);
                int y = (int)(position.Y / (float)App.AppHeight * (float)Screen.ScreenHeight);
                var scaledPosition = new Vector2(x, y);
                //Debug.WriteLine("scaledPosition: " + scaledPosition);
                return scaledPosition;
            }


    • Marked as answer by Sabahan Tuesday, April 15, 2014 3:17 AM
    • Edited by Sabahan Tuesday, April 15, 2014 5:26 AM
    Tuesday, April 15, 2014 3:01 AM