How to: Use Viewports for Split Screen Gaming

This example demonstrates how to use the Viewport property to display different scenes to different parts of the screen.

This sample assumes there are two camera classes in use, Camera1 and Camera2.

To create a split screen

  1. In your LoadGraphicsContent method, create two new Viewport objects to define the two new "split" regions of the screen. In this example, the screen is split in half vertically.

    Viewport defaultViewport;
    

Viewport leftViewport; Viewport rightViewport; protected override void LoadGraphicsContent( bool loadAllContent ) { if (loadAllContent) { ... }

defaultViewport = graphics.GraphicsDevice.Viewport;
leftViewport = defaultViewport;
rightViewport = defaultViewport;
leftViewport.Width = leftViewport.Width / 2;
rightViewport.Width = rightViewport.Width / 2;
rightViewport.X = leftViewport.Width + 1;</pre>
  1. Immediately after creating the two new viewports, create a projection matrix to fit each new viewport. In this case, because the screen is split in half, only one new projection matrix is necessary. It has the same settings as the 4:3 full screen projection matrix, except the aspect ratio is now 2:3.

        projectionMatrix = Matrix.CreatePerspectiveFieldOfView( MathHelper.PiOver4, 4.0f / 3.0f, 1.0f, 10000f );
    halfprojectionMatrix = Matrix.CreatePerspectiveFieldOfView( MathHelper.PiOver4, 2.0f / 3.0f, 1.0f, 10000f );
    
    

}

  1. In your Draw method, assign one of the viewports to draw as the GraphicsDevice Viewport. Then draw your scene as normal, using the camera (or view matrix) associated with this perspective, along with the proper projection matrix.

    protected override void Draw( GameTime gameTime )
    

{ graphics.GraphicsDevice.Clear( Color.Black );

// TODO: Add your drawing code here
graphics.GraphicsDevice.Viewport = leftViewport;

DrawScene( gameTime, Camera1, halfprojectionMatrix);</pre>
  1. After drawing the first scene, assign the other viewport to the Viewport property, and draw your scene again with the associated camera or view matrix, and the proper projection matrix.

        graphics.GraphicsDevice.Viewport = rightViewport;
    
    DrawScene( gameTime, Camera2, halfprojectionMatrix );
    
    base.Draw( gameTime );
    

}

The Complete Example

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;

public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    ContentManager content;

    Model Ring;
    float RingRotation;
    Vector3 RingPosition;

    SampleArcBallCamera Camera1;
    SampleArcBallCamera Camera2;
    Matrix projectionMatrix;
    Matrix halfprojectionMatrix;

    public Game1()
    {
        graphics = new GraphicsDeviceManager( this );
        content = new ContentManager( Services );

        Camera1 = new SampleArcBallCamera( SampleArcBallCameraMode.Free );
        Camera2 = new SampleArcBallCamera( SampleArcBallCameraMode.Free );

        Camera1.Target = new Vector3( 0, 0, 0 );
        Camera1.Distance = 50f;
        Camera2.Target = new Vector3( 0, 0, 0 );
        Camera2.Distance = -50f;

        RingPosition = Vector3.Zero;
        RingRotation = 0.0f;
    }


    protected override void Initialize()
    {
        base.Initialize();
    }

    Viewport defaultViewport;
    Viewport leftViewport;
    Viewport rightViewport;
    protected override void LoadGraphicsContent( bool loadAllContent )
    {
        if (loadAllContent)
        {
            Ring = content.Load<Model>( "ring16b" );
        }

        defaultViewport = graphics.GraphicsDevice.Viewport;
        leftViewport = defaultViewport;
        rightViewport = defaultViewport;
        leftViewport.Width = leftViewport.Width / 2;
        rightViewport.Width = rightViewport.Width / 2;
        rightViewport.X = leftViewport.Width + 1;

        projectionMatrix = Matrix.CreatePerspectiveFieldOfView( MathHelper.PiOver4, 4.0f / 3.0f, 1.0f, 10000f );
        halfprojectionMatrix = Matrix.CreatePerspectiveFieldOfView( MathHelper.PiOver4, 2.0f / 3.0f, 1.0f, 10000f );

        graphics.GraphicsDevice.RenderState.DepthBufferEnable = true;
        graphics.GraphicsDevice.RenderState.CullMode = CullMode.None;
    }

    protected override void UnloadGraphicsContent( bool unloadAllContent )
    {
        if (unloadAllContent == true)
        {
            content.Unload();
        }
    }


    protected override void Update( GameTime gameTime )
    {
        // Allows the default game to exit on Xbox 360 and Windows
        if (GamePad.GetState( PlayerIndex.One ).Buttons.Back == ButtonState.Pressed)
            this.Exit();

        GamePadState PlayerOne = GamePad.GetState( PlayerIndex.One );
        
        Camera1.OrbitUp( PlayerOne.ThumbSticks.Right.Y / 4 );
        Camera1.OrbitRight( PlayerOne.ThumbSticks.Right.X / 4 );

        GamePadState PlayerTwo = GamePad.GetState( PlayerIndex.Two );

        Camera2.OrbitUp( PlayerTwo.ThumbSticks.Right.Y / 4 );
        Camera2.OrbitRight( PlayerTwo.ThumbSticks.Right.X / 4 );

        base.Update( gameTime );
    }


    protected override void Draw( GameTime gameTime )
    {
        graphics.GraphicsDevice.Clear( Color.Black );

        // TODO: Add your drawing code here
        graphics.GraphicsDevice.Viewport = leftViewport;

        DrawScene( gameTime, Camera1, halfprojectionMatrix);
        graphics.GraphicsDevice.Viewport = rightViewport;

        DrawScene( gameTime, Camera2, halfprojectionMatrix );

        base.Draw( gameTime );
    }

    protected void DrawScene( GameTime gameTime, SampleArcBallCamera camera, Matrix projection )
    {
        //Draw the model, a model can have multiple meshes, so loop
        foreach (ModelMesh mesh in Ring.Meshes)
        {
            //This is where the mesh orientation is set, as well as our camera and projection
            foreach (BasicEffect effect in mesh.Effects)
            {
                effect.EnableDefaultLighting();
                effect.World = Matrix.Identity * Matrix.CreateRotationY( RingRotation )
                    * Matrix.CreateTranslation( RingPosition );
                effect.View = camera.ViewMatrix;
                effect.Projection = projection;
            }
            //Draw the mesh, will use the effects set above.
            mesh.Draw();
        }
    }
}

See Also

Concepts

Displays, Client Bounds, Viewports, and Back Buffers

Reference

GraphicsDevice.Viewport
Viewport Structure