/*
* 移動は経路を最大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);
}
}
}