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

compass.png

map.png


CustomVertex.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
 
namespace CustomVertex
{
    struct VertexPosition : IVertexType
    {
        public Vector3 Position;
 
        public VertexPosition(Vector3 position)
        {
            Position = position;
        }
 
        static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration(
            new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0));
 
        VertexDeclaration IVertexType.VertexDeclaration { get { return VertexDeclaration; } }
    }
}
 

Game1.cs
/*
 * プロジェクトのプロパティ
 * [XNA Game Studio]タブ
 * Use HiDef to access the complete API
 */
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using CustomVertex;
 
namespace XnaCarRace
{
    class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch sprite;
        SpriteFont font;
        Effect effect;
        EffectParameter ViewProj;
        Texture2D map;
        Texture2D compass;
        Texture2D marker;
 
        // fps
        int fpsSec;
        int fpsDraw = 0;
        int fpsCount = 0;
 
        VertexBuffer instanceBuffer;
        VertexBuffer geometryBuffer;
        IndexBuffer indexBuffer;
        VertexDeclaration instanceVertexDeclaration;
        VertexBufferBinding[] bindings;
        Matrix projection;
        byte[,] objMap;
 
        // カメラ
        //Vector3 camPos = new Vector3(0, 2, 0);
        Vector3 camPos = new Vector3(22, 0.5f, 70);
        float camLon = 270;
        float velocity = 0;
        float steering = 0;
 
        struct InstanceInfo
        {
            public Matrix World;
            public Vector4 Color;
        }
 
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = 1280;
            graphics.PreferredBackBufferHeight = 720;
            Content.RootDirectory = "Content";
            IsMouseVisible = true;
        }
 
        protected override void LoadContent()
        {
            sprite = new SpriteBatch(GraphicsDevice);
            font = Content.Load<SpriteFont>("SpriteFont1");
            effect = Content.Load<Effect>("Effect1");
            ViewProj = effect.Parameters["ViewProj"];
            //effect.CurrentTechnique = effect.Techniques["Technique1"];
            map = Content.Load<Texture2D>("map"); // 128x128くらいのpng
            compass = Content.Load<Texture2D>("compass");
 
            GenerateGeometryBuffers();
            GenerateInstanceVertexDeclaration();
            GenerateInstanceInformation();
 
            bindings = new VertexBufferBinding[2];
            bindings[0] = new VertexBufferBinding(geometryBuffer, 0);
            bindings[1] = new VertexBufferBinding(instanceBuffer, 0, 1);
 
            projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45),
                GraphicsDevice.Viewport.AspectRatio, 0.1f, 100);
 
            // マーカー
            Color[] data = new Color[9];
            for (int n = 0; n < 9; n++)
            {
                data[n] = new Color(255, 255, 255);
            }
            marker = new Texture2D(GraphicsDevice, 3, 3);
            marker.SetData<Color>(data);
 
            base.LoadContent();
        }
 
        void GenerateGeometryBuffers()
        {
            // 頂点
            VertexPosition[] vertices = new VertexPosition[8];
            Vector3[] cube =
            {
                new Vector3(1, 1, 1), new Vector3(1, 1, -1),
                new Vector3(1, -1, 1), new Vector3(1, -1, -1),
                new Vector3(-1, -1, -1), new Vector3(-1, 1, -1),
                new Vector3(-1, -1, 1), new Vector3(-1, 1, 1),
            };
            for (int n = 0; n < 8; n++)
            {
                vertices[n] = new VertexPosition(cube[n] * 0.5f);
            }
 
            geometryBuffer = new VertexBuffer(GraphicsDevice,
                typeof(VertexPosition), 8, BufferUsage.WriteOnly);
            geometryBuffer.SetData(vertices);
 
            // 索引
            short[] indices =
            {
                0, 1, 2, 3, 2, 1, // +X面
                4, 5, 6, 7, 6, 5, // -X面
                0, 7, 1, 5, 1, 7, // +Y面
                4, 6, 3, 2, 3, 6, // -Y面
                0, 2, 7, 6, 7, 2, // +Z面
                4, 3, 5, 1, 5, 3, // -Z面
            };
 
            indexBuffer = new IndexBuffer(GraphicsDevice,
                typeof(short), 36, BufferUsage.WriteOnly);
            indexBuffer.SetData(indices);
        }
 
        void GenerateInstanceVertexDeclaration()
        {
            VertexElement[] elements = new VertexElement[5];
            for (int n = 0; n < 4; n++)
            {
                elements[n] = new VertexElement(sizeof(float) * 4 * n,
                    VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, n + 1);
            }
            elements[4] = new VertexElement(sizeof(float) * 16,
                VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 5);
            instanceVertexDeclaration = new VertexDeclaration(elements);
        }
 
        void GenerateInstanceInformation()
        {
            objMap = new byte[map.Width, map.Height];
            int dataNum = map.Width * map.Height;
            InstanceInfo[] instances = new InstanceInfo[dataNum];
            Color[] data = new Color[dataNum];
            map.GetData<Color>(0, null, data, 0, dataNum);
            int i = 0;
            for (int x = 0; x < map.Height; x++)
            {
                for (int z = 0; z < map.Width; z++)
                {
                    Color color = data[map.Width * (map.Height - 1 - x) + z];
                    if (color == Color.Black) continue;
 
                    instances[i].World = Matrix.CreateTranslation(
                        new Vector3(0.5f + x, 0.5f, 0.5f + z));
                    instances[i].Color = color.ToVector4();
                    i++;
                    objMap[z, x] = 1;
                }
            }
 
            instanceBuffer = new VertexBuffer(GraphicsDevice,
                instanceVertexDeclaration, i, BufferUsage.WriteOnly);
            instanceBuffer.SetData(instances, 0, i);
        }
 
        protected override void Update(GameTime gameTime)
        {
            float deltaVelocity = -0.001f;
            float deltaSteering = 0;
 
            KeyboardState kState = Keyboard.GetState();
            if (kState.IsKeyDown(Keys.Escape)) Exit();
            if (kState.IsKeyDown(Keys.Z)) deltaVelocity = -0.01f;
            if (kState.IsKeyDown(Keys.X)) deltaVelocity = 0.005f;
            if (kState.IsKeyDown(Keys.Left)) deltaSteering = -0.1f;
            if (kState.IsKeyDown(Keys.Right)) deltaSteering = 0.1f;
 
            // 速度
            velocity = MathHelper.Clamp(velocity + deltaVelocity, 0, 0.2f);
 
            // ステアリング
            if (deltaSteering == 0)
            {
                steering = (Math.Abs(steering) < 0.05f) ?
                    0 : steering - Math.Sign(steering) * 0.05f;
            }
            else
            {
                steering += deltaSteering;
            }
            steering = MathHelper.Clamp(steering, -1, 1);
 
            if (velocity == 0)
            {
                // 停止中
                camLon += Math.Sign(deltaSteering) * 2;
            }
            else
            {
                // 移動
                camLon += steering;
                float rad = MathHelper.ToRadians(camLon);
                float x = (float)Math.Cos(rad) * velocity;
                float z = (float)Math.Sin(rad) * velocity;
 
                // 衝突判定
                if (objMap[(int)(camPos.Z + z), (int)(camPos.X + x)] == 0)
                {
                    camPos.X += x;
                    camPos.Z += z;
                }
                else
                {
                    velocity = 0;
                }
            }
 
            base.Update(gameTime);
        }
 
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            //GraphicsDevice.BlendState = BlendState.Opaque;
            GraphicsDevice.DepthStencilState = DepthStencilState.Default;
 
            float rad = MathHelper.ToRadians(camLon);
            float x = camPos.X + (float)Math.Cos(rad);
            float z = camPos.Z + (float)Math.Sin(rad);
            Matrix view = Matrix.CreateLookAt(camPos, new Vector3(x, 0.5f, z), Vector3.Up);
 
            GraphicsDevice.Indices = indexBuffer;
            ViewProj.SetValue(view * projection);
            effect.CurrentTechnique.Passes[0].Apply();
            GraphicsDevice.SetVertexBuffers(bindings);
            GraphicsDevice.DrawInstancedPrimitives(
                PrimitiveType.TriangleList, 0, 0, 8, 0, 12, instanceBuffer.VertexCount);
 
            // fps
            fpsDraw++;
            if (gameTime.TotalGameTime.Seconds != fpsSec)
            {
                fpsCount = fpsDraw;
                fpsDraw = 0;
                fpsSec = gameTime.TotalGameTime.Seconds;
            }
 
            sprite.Begin();
            string text = string.Format("fps={0} lon={1} x={2:f1} z={3:f1}",
                fpsCount, camLon, camPos.X, camPos.Z);
            sprite.DrawString(font, text, new Vector2(0, 0), Color.White);
            text = string.Format("v={0:f2} s={1:f2}", velocity, steering);
            sprite.DrawString(font, text, new Vector2(0, 20), Color.White);
            sprite.Draw(map, new Vector2(20, 60), Color.White);
            sprite.Draw(marker, new Vector2(20 + camPos.Z, 60 + 128 - camPos.X), null, Color.White,
                0, new Vector2(1, 1), 1, SpriteEffects.None, 0);
            sprite.Draw(compass, new Vector2(20 + 64, 200 + 64), null, Color.White,
                MathHelper.ToRadians(camLon), new Vector2(64, 64), 1, SpriteEffects.None, 0);
            sprite.End();
 
            base.Draw(gameTime);
        }
    }
}
 

Effect1.fx
float4x4 ViewProj;
 
struct VertexShaderInput
{
	float4 Position : POSITION0;
};
 
struct VertexShaderOutput
{
	float4 Position : POSITION0;
	float4 Color : COLOR0;
};
 
VertexShaderOutput VertexShaderFunction(
	VertexShaderInput input, float4x4 instanceTransform : TEXCOORD1, float4 color : TEXCOORD5)
{
	VertexShaderOutput output;
	float4 pos = mul(input.Position, transpose(instanceTransform));
	output.Position = mul(pos, ViewProj);
	output.Color = color;
	return output;
}
 
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
	return input.Color;
}
 
technique Technique1
{
	pass Pass1
	{
		// 3.0の方が負荷が少ない
		VertexShader = compile vs_3_0 VertexShaderFunction();
		PixelShader = compile ps_3_0 PixelShaderFunction();
	}
}
 
最終更新:2012年12月22日 10:14
添付ファイル