忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

かんたんXNA4.0 その27 ビューポートで分割スクリーン

今回はゲーム画面を2つに分割する方法です。

2人のプレイヤーでゲーム対戦する場合を考えましょう。
その場合、2Dならともかく、3Dなら
プレイヤーによって画面が2つに分かれているべきでしょう。
画面が左と右の二つに分かれていて、
プレイヤーはそれぞれ自分の画面を見て相手を倒すのです!

そういったことをする場合、ゲームのモデルを全て
それぞれの画面に1回ずつ、計2回描画することになります。

その際、単に描画したのでは二人の画面が重なってしまいます。
別々の場所にモデルを描画しなければなりません。

そのように、別々の場所に描画するのに使うのが、ビューポートです。
これを使うと、描画する領域の位置や大きさを変更することが出来ます。




ビューポートはGraphicsDevice.Viewportプロパティで設定します。

public Viewport Viewport { get; set; }

このプロパティが扱っているのは
Microsoft.Xna.Framework.Graphics.Viewport構造体です。

[Serializable]
public struct Viewport


この構造体はモデルを描画する画面の位置やサイズを
表すプロパティを持っています。

public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
...


この構造体があらわす画面は普通は
ウィンドウのクライアントエリアと同じです。
しかし、今回のように半分に分割したい場合には
プロパティをいろいろと変更しないといけないでしょう。

  左スクリーン 右スクリーン
X 0 左スクリーンの横幅
Width 画面の横幅の半分 画面の横幅の半分

また、分割スクリーンをやるさいには、ビューポート以外にも
変更しなければいけないところがあります。
それはBasicEffect.Projectionプロパティです。
幅を半分にしたので、このプロパティにセットするマトリックスも
変えなければならないのです。
描画する画面の四角形の形をあらわすアスペクト比(横/縦)
を半分にしなければなりません。
そうしなければ、表示するモデルが横に2倍に引き伸ばされてしまいます。


using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;



class MyGame : Game
{
    GraphicsDeviceManager graphics;
    BasicEffect basicEffect;

    VertexPositionColor[] vertices =
    {
        new VertexPositionColor(new Vector3(0, 1, 0), Color.White),
        new VertexPositionColor(new Vector3(1, 0, 0), Color.Blue),
        new VertexPositionColor(new Vector3(-1, 0, 0), Color.Red)
    };

    public MyGame()
    {
        graphics = new GraphicsDeviceManager(this);
    }

    protected override void LoadContent()
    {
        basicEffect = new BasicEffect(GraphicsDevice)
        {
            VertexColorEnabled = true,
            View = Matrix.CreateLookAt
            (
                new Vector3(0, 0, 2),  //カメラの位置
                new Vector3(0, 0, 0),   //カメラの見る点
                new Vector3(0, 1, 0)    //カメラの上向きベクトル
            ),
            Projection = Matrix.CreatePerspectiveFieldOfView
            (
                MathHelper.ToRadians(45),   //視野の角度。ここでは45°
                GraphicsDevice.Viewport.AspectRatio / 2,//画面のアスペクト比(=横/縦)
                1,      //カメラからこれより近い物体は画面に映らない
                100     //カメラからこれより遠い物体は画面に映らない
            )
        };
    }

    protected override void UnloadContent()
    {
        basicEffect.Dispose();
    }

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

        Viewport fullViewport = graphics.GraphicsDevice.Viewport;
        Viewport leftViewport = fullViewport;
        leftViewport.Width /= 2;
        graphics.GraphicsDevice.Viewport = leftViewport;

        drawTriangle();

        Viewport rightViewport = leftViewport;
        rightViewport.X = leftViewport.Width;
        graphics.GraphicsDevice.Viewport = rightViewport;

        drawTriangle();

        graphics.GraphicsDevice.Viewport = fullViewport;

    }

    private void drawTriangle()
    {
        foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
        {
            pass.Apply();

            GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(
                PrimitiveType.TriangleList,
                vertices,
                0,
                vertices.Length / 3
                );
        }
    }
}




xna4.0SimplestViewport.jpg
このサンプルプログラムでは、1つの同じ三角形を
左右2つのビューポートに描画しています。
そのため、画面が真ん中で切れていて、
分割スクリーンになっているのです。

ここで強調したいのは、三角形の座標やカメラの位置は
左右で全く変わっていないということです。
違うのは、描画している領域、ビューポートだけです。
ビューポートの違いにより2つの三角形がずれているのです。

これを応用するといろいろと面白いことが出来ます。
カメラを2つの画面で全く違ったものにして二人対戦に使うことも出来るでしょうし、
あるいはカメラを左右で少しだけずらして
交差法や平行法で立体視をしてみるのも面白いかもしれません。

拍手[0回]

PR