開発環境 Microsoft Visual C# 2010 Express (SP1)
実行環境 Microsoft Windows XP Home Edition (SP3)
プロジェクトの種類 Windows Game (4.0)
プロジェクト名 XnaTexture

Game1.cs
/*
 * 移動は経路を最大20ステップくらいで計算し、経過時間で現在位置を割り出す。
 */
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
 
namespace XnaTexture
{
    class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch sprite;
        SpriteFont font;
 
        // fps
        DateTime prevTime;
        int draw = 0;
        int fps = 0;
 
        readonly short[] rectVert = { 0, 1, 2, 1, 3, 2 };
        const int fieldX = 40;
        const int fieldZ = 40;
        const int fieldXZ = fieldX * fieldZ;
 
        BasicEffect effect;
        Texture2D fieldTex;
        Texture2D texture;
        VertexBuffer fieldVertex;
        IndexBuffer fieldIndex;
        VertexPositionTexture[] cursorVertices;
 
        int objNum = 10;
        Vector3[] objPos;
 
        // カメラ
        Vector3 targetPos = new Vector3(fieldX / 2 + 0.5f, 0, fieldZ / 2 + 0.5f);
        float camDist = 15;
        int camLat = 45;
        int camLong = 180;
 
        MouseState mStateOld = new MouseState();
        int mouseX;
        int mouseY;
        int momentWheel = 0;
 
        // テクスチャ系(u,v) n=0{0,0} n=1{1,0} n=2{0,1} n=3{1,1}
        int TU(int n) { return n & 1; }
        int TV(int n) { return (n >> 1) & 1; }
 
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = 1280;
            graphics.PreferredBackBufferHeight = 720;
            Content.RootDirectory = "Content";
            IsMouseVisible = true;
        }
 
        protected override void Initialize()
        {
            sprite = new SpriteBatch(GraphicsDevice);
            prevTime = DateTime.Now;
            base.Initialize();
        }
 
        protected override void LoadContent()
        {
            // ソリューション エクスプローラーの(Content)に追加しておく
            font = Content.Load<SpriteFont>("SpriteFont1"); // 新しい項目:Sprite Font
            //fieldTex = Content.Load<Texture2D>("jt_campf_002");
            fieldTex = Content.Load<Texture2D>("Palette16");
            texture = Content.Load<Texture2D>("Palette16");
 
            effect = new BasicEffect(GraphicsDevice);
            effect.TextureEnabled = true;
            effect.Texture = fieldTex;
 
            effect.FogEnabled = true;
            effect.FogColor = Color.White.ToVector3();
            effect.FogStart = 25;
            effect.FogEnd = 35;
 
            effect.Projection = Matrix.CreatePerspectiveFieldOfView(
                MathHelper.ToRadians(45), GraphicsDevice.Viewport.AspectRatio, 1, 40);
 
            GenerateFieldBuffers();
 
            // カーソル
            cursorVertices = new VertexPositionTexture[4];
            for (int n = 0; n < 4; n++)
            {
                cursorVertices[n].TextureCoordinate =
                    new Vector2((2 + TU(n)) / 4.0f, (1 + TV(n)) / 4.0f);
            }
 
            // 物体
            objPos = new Vector3[objNum];
            Random rnd = new Random();
            for (int n = 0; n < objNum; n++)
            {
                objPos[n] = new Vector3(rnd.Next(fieldX) + 0.5f, 0, rnd.Next(fieldZ) + 0.5f);
            }
 
            base.LoadContent();
        }
 
        void GenerateFieldBuffers()
        {
            // 頂点バッファ
            int vertexNum = 4 * fieldXZ;
            VertexPositionTexture[] vertices = new VertexPositionTexture[vertexNum];
            int i = 0;
            for (int x = 0; x < fieldX; x++)
            {
                for (int z = 0; z < fieldZ; z++)
                {
                    for (int n = 0; n < 4; n++)
                    {
                        vertices[i].Position = new Vector3(x + 1 - TV(n), 0, z + TU(n));
                        vertices[i].TextureCoordinate =
                            new Vector2((1 + TU(n)) / 4.0f, TV(n) / 4.0f);
                        i++;
                    }
                }
            }
 
            fieldVertex = new VertexBuffer(GraphicsDevice,
                typeof(VertexPositionTexture), vertexNum, BufferUsage.WriteOnly);
            fieldVertex.SetData(vertices);
 
            // 索引バッファ
            int indexNum = 6 * fieldXZ;
            short[] indices = new short[indexNum];
            for (int n = 0; n < indexNum; n++)
            {
                indices[n] = (short)((n / 6) * 4 + rectVert[n % 6]);
            }
 
            fieldIndex = new IndexBuffer(GraphicsDevice,
                typeof(short), indexNum, BufferUsage.WriteOnly);
            fieldIndex.SetData(indices);
        }
 
        protected override void Update(GameTime gameTime)
        {
            // キーボード
            KeyboardState state = Keyboard.GetState();
            if (state[Keys.Escape] == KeyState.Down) Exit();
            if (state[Keys.W] == KeyState.Down) Move(180);
            if (state[Keys.S] == KeyState.Down) Move(0);
            if (state[Keys.A] == KeyState.Down) Move(90);
            if (state[Keys.D] == KeyState.Down) Move(-90);
            if (state[Keys.Up] == KeyState.Down) camLat = Math.Min(camLat + 1, 85);
            if (state[Keys.Down] == KeyState.Down) camLat = Math.Max(camLat - 1, 5);
            if (state[Keys.Left] == KeyState.Down) camLong = (camLong + 1) % 360;
            if (state[Keys.Right] == KeyState.Down) camLong = (camLong + 359) % 360;
            if (state[Keys.PageUp] == KeyState.Down) camDist = Math.Max(camDist - 0.1f, 10);
            if (state[Keys.PageDown] == KeyState.Down) camDist = Math.Min(camDist + 0.1f, 20);
 
            // マウス
            MouseState mState = Mouse.GetState();
            if (mStateOld.ScrollWheelValue != mState.ScrollWheelValue)
            {
                momentWheel = (mStateOld.ScrollWheelValue < mState.ScrollWheelValue) ? -10 : 10;
            }
            if (mState.RightButton == ButtonState.Pressed)
            {
                if (mStateOld.RightButton == ButtonState.Released)
                {
                    mouseX = mState.X;
                    mouseY = mState.Y;
                }
                else
                {
                    camLong += (mouseX - mState.X) / 2 % 360;
                    if (camLong < 0) camLong += 360;
                    if (360 <= camLong) camLong -= 360;
                    camLat += (mouseY - mState.Y) / 2;
                    if (camLat < 5) camLat = 5;
                    if (85 < camLat) camLat = 85;
                    Mouse.SetPosition(mouseX, mouseY);
                }
            }
            mStateOld = mState;
 
            // モーメント
            if (momentWheel < 0)
            {
                camDist = Math.Max(camDist + momentWheel * 0.02f, 10);
                momentWheel++;
            }
            if (0 < momentWheel)
            {
                camDist = Math.Min(camDist + momentWheel * 0.02f, 20);
                momentWheel--;
            }
 
            base.Update(gameTime);
        }
 
        void Move(int dir)
        {
            float rad = MathHelper.ToRadians(camLong + dir);
            float x = (float)Math.Cos(rad) * 0.1f;
            float z = (float)Math.Sin(rad) * 0.1f;
            targetPos.X += x;
            targetPos.Z += z;
        }
 
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            GraphicsDevice.DepthStencilState = DepthStencilState.Default;
 
            // カメラ位置
            float rad = MathHelper.ToRadians(camLat);
            float y = (float)Math.Sin(rad) * camDist;
            float r = (float)Math.Cos(rad) * camDist;
            rad = MathHelper.ToRadians(camLong);
            float x = (float)Math.Cos(rad) * r;
            float z = (float)Math.Sin(rad) * r;
            Vector3 cameraPos = targetPos + new Vector3(x, y, z);
            effect.View = Matrix.CreateLookAt(cameraPos, targetPos, Vector3.Up);
 
            // マウス
            MouseState mState = Mouse.GetState();
            Vector3 nearPos = GraphicsDevice.Viewport.Unproject(new Vector3(mState.X, mState.Y, 0),
                effect.Projection, effect.View, Matrix.Identity);
            Vector3 farPos = GraphicsDevice.Viewport.Unproject(new Vector3(mState.X, mState.Y, 1),
                effect.Projection, effect.View, Matrix.Identity);
            Ray ray = new Ray(cameraPos, Vector3.Normalize(farPos - nearPos));
            // この線と地平面との交点の座標を求める
            Plane plane = new Plane(Vector3.Up, 0);
            float? dist = ray.Intersects(plane); // Nullable
            Vector3 cursorPos = new Vector3();
 
            // 3D描画
            GraphicsDevice.SetVertexBuffer(fieldVertex);
            GraphicsDevice.Indices = fieldIndex;
 
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();
 
                GraphicsDevice.DrawIndexedPrimitives(
                    PrimitiveType.TriangleList, 0, 0, 4 * fieldXZ, 0, 2 * fieldXZ);
 
                if (dist.HasValue)
                {
                    cursorPos = ray.Position + ray.Direction * (float)dist;
                    int cx = (int)cursorPos.X;
                    int cz = (int)cursorPos.Z;
                    for (int n = 0; n < 4; n++)
                    {
                        cursorVertices[n].Position = new Vector3(cx + 1 - TV(n), 0.1f, cz + TU(n));
                    }
                    GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionTexture>(
                        PrimitiveType.TriangleList, cursorVertices, 0, 4, rectVert, 0, 2);
                }
            }
 
            // フレームレート
            draw++;
            DateTime now = DateTime.Now;
            TimeSpan t = now - prevTime;
            if (t.TotalMilliseconds >= 1000)
            {
                fps = draw;
                draw = 0;
                prevTime = now;
            }
 
            // 2D描画
            sprite.Begin(SpriteSortMode.BackToFront, null);
 
            // ターゲット
            int num = gameTime.TotalGameTime.Milliseconds / 250;
            DrawObj(targetPos, num, 2, cameraPos);
 
            // 物体
            foreach (Vector3 pos in objPos)
            {
                DrawObj(pos, 3, 0, cameraPos);
            }
 
            string text = string.Format(
                "fps={0} lat={1} long={2} dist={3:f1}", fps, camLat, camLong, camDist);
            sprite.DrawString(font, text, new Vector2(0, 0), Color.Lime);
            text = string.Format("x={0:f1} y={1:f1} z={2:f1}",
                targetPos.X, targetPos.Y, targetPos.Z);
            sprite.DrawString(font, text, new Vector2(0, 20), Color.Lime);
            text = string.Format("x={0} y={1} wheel={2} dist={3:f2}",
                mState.X, mState.Y, mState.ScrollWheelValue, dist);
            sprite.DrawString(font, text, new Vector2(0, 40), Color.Lime);
            text = string.Format("x={0:f1} y={1:f1} z={2:f1}",
                cursorPos.X, cursorPos.Y, cursorPos.Z);
            sprite.DrawString(font, text, new Vector2(0, 60), Color.Lime);
            sprite.End();
 
            base.Draw(gameTime);
        }
 
        // 関数内でループさせる予定
        void DrawObj(Vector3 objPos, int u, int v, Vector3 cameraPos)
        {
            Vector3 pos = GraphicsDevice.Viewport.Project(
                objPos, effect.Projection, effect.View, Matrix.Identity);
            float dist = Vector3.Distance(cameraPos, objPos);
            float scale = 28 / dist;
            Vector2 scaleV2 = new Vector2(scale, scale * (180 - camLat) / 180);
            Rectangle rect = new Rectangle(u * 32, v * 32, 32, 64);
            sprite.Draw(texture, new Vector2(pos.X, pos.Y), rect, Color.White,
                0, new Vector2(16, 64), scaleV2, SpriteEffects.None, pos.Z);
        }
    }
}
 
最終更新:2012年12月08日 20:30