Subscribe RSS Join our Facebook Group Follow us on Twitter!
in Search

Samsu's passion (.NET)

Blog mengenai kegiatan sebagai MSP, MIC, dan juga penyuka teknologi Microsoft

Camera Setting untuk 3D Environment pada XNA Framework

Selama berkutat dengan pengembangan 3D, hal yang paling mendasar namun sangat menentukan menurut saya adalah bagaimana menempatkan kamera di dunia 3D yang kita bangun. Ya,ya, model 3D memang penting namun seperti yang saya katakan di bagian sebelumnya itu adalah pekerjaan artist, bukan programmer!

Pada intinya pengaturan kamera hanyalah menempatkan sebuah objek pada dunia 3D di suatu posisi tertentu dan mengarah ke suatu posisi tertentu (target).

image

Untuk mencapai hal ini, kita membutuhkan 3 variabel vector3 yaitu :

  1. vektor posisi kamera,
  2. vektor posisi target, dan
  3. vektor arah yang didapat dengan rumus

Varah = Vtarget – Vposisi

Dalam XNA, untuk menampilkan kita membutuhkan 2 buah matriks yang disebut view matrix dan projection matrix. Projection matrix membangun apa yang disebut viewing fustrum (field of view) dari kamera. Secara sederhana, matriks ini mendefinisikan suatu area dalam area 3D yang dapat dilihat oleh kamera dan akan digambarkan ke layar. Sedangkan view matrix menyimpan informasi mengenai keberadaan kamera di dunia 3D, arah kamera, dan bagaimana orientasi kamera tersebut.

Selain itu, ada beberapa jenis rotasi yang sering digunakan yaitu : yaw, pitch, dan roll.

image

Yup, bagian teori sudah cukup, selanjutnya kita akan mencoba implementasi dengan XNA. Untuk itu kita akan membuat sebuah maze viewer. Ya, kita akan membuat labirin 3D yang dapat kita jelajahi dengan kamera kita!

1. Set up aplikasi (saya masih menggunakan XNA Game Studio 3.1, tapi tidak akan banyak berbeda dengan XNA 4.0 untuk apa yang akan kita buat sekarang)

image

image

2. Tambahkan dua kelas masing-masing Camera.cs,Maze.cs,MazeObject.cs dengan mengklik kanan proyek XNACamera dan pilih add new item. Pilih “Game Component”

image

Selanjutnya, kita akan membuat sebuah kelas kamera yang reusable.

Berikut adalah kode dari camera.cs (mungkin terlihat panjang, tapi sebenarnya hanya implementasi dari prinsip yang telah saya jelaskan sebelumnya).

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using Microsoft.Xna.Framework;
   5: using Microsoft.Xna.Framework.Audio;
   6: using Microsoft.Xna.Framework.Content;
   7: using Microsoft.Xna.Framework.GamerServices;
   8: using Microsoft.Xna.Framework.Graphics;
   9: using Microsoft.Xna.Framework.Input;
  10: using Microsoft.Xna.Framework.Media;
  11: using Microsoft.Xna.Framework.Net;
  12: using Microsoft.Xna.Framework.Storage;
  13:  
  14:  
  15: namespace XNACamera
  16: {
  17:     /// <summary>
  18:     /// This is a game component that implements IUpdateable.
  19:     /// </summary>
  20:     public class Camera : Microsoft.Xna.Framework.GameComponent
  21:     {
  22:         public Matrix view { get; protected set; }
  23:         public Matrix projection { get; protected set; }
  24:  
  25:         Vector3 pos;                //vektor posisi kamera
  26:         Vector3 target;             //vektor posisi target
  27:         Vector3 dir;                //vektor arah kamera
  28:         Vector3 up;                 //vektor yang tegak lurus terhadap bidang kamera
  29:  
  30:         MouseState prevMouseState;
  31:  
  32:         float speed = 3;
  33:         float rollValue = 0;
  34:         float currentPitch = 0;
  35:         float totalPitch = MathHelper.PiOver4 / 2;
  36:         float currentYaw = 0;
  37:         float totalYaw = MathHelper.PiOver4 / 2;
  38:         
  39:         public Camera(Game game, Vector3 _pos, Vector3 _target, Vector3 _up)
  40:             : base(game)
  41:         {
  42:             pos = _pos;
  43:             target = _target;
  44:             up = _up;
  45:             dir = target - pos;
  46:             dir.Normalize();    //membuat besar vector dir menjadi vektor normal (memiliki besar 1)
  47:  
  48:             prevMouseState = Mouse.GetState();
  49:         }
  50:  
  51:         /// <summary>
  52:         /// Allows the game component to perform any initialization it needs to before starting
  53:         /// to run.  This is where it can query for any required services and load content.
  54:         /// </summary>
  55:         public override void Initialize()
  56:         {
  57:             base.Initialize();
  58:         }
  59:  
  60:         /// <summary>
  61:         /// Allows the game component to update itself.
  62:         /// </summary>
  63:         /// <param name="gameTime">Provides a snapshot of timing values.</param>
  64:         public override void Update(GameTime gameTime)
  65:         {
  66:             KeyboardState keyState = Keyboard.GetState();
  67:             MouseState mouseState = Mouse.GetState();
  68:  
  69:             #region reset camera
  70:             if (keyState.IsKeyDown(Keys.R))
  71:             {
  72:                 pos = new Vector3(180, 700, 180);
  73:                 target = new Vector3(180, 0, 180);
  74:                 up = new Vector3(0, 0, -1);
  75:                 dir = target - pos;
  76:                 dir.Normalize();
  77:  
  78:                 currentPitch = 0;
  79:                 currentYaw = 0;
  80:             }
  81:             #endregion
  82:  
  83:  
  84:             #region scale camera
  85:             if (mouseState.ScrollWheelValue != rollValue)
  86:             {
  87:                 pos -= dir * speed * (rollValue - mouseState.ScrollWheelValue) / 15;
  88:                 rollValue = mouseState.ScrollWheelValue;
  89:             }
  90:  
  91:             if (keyState.IsKeyDown(Keys.W)) pos += up * speed;
  92:             if (keyState.IsKeyDown(Keys.S)) pos -= up * speed;
  93:             if (keyState.IsKeyDown(Keys.A)) pos += Vector3.Cross(up, dir) * speed;
  94:             if (keyState.IsKeyDown(Keys.D)) pos -= Vector3.Cross(up, dir) * speed;
  95:             #endregion
  96:  
  97:             #region yaw rotation
  98:             float yawAngle = (-MathHelper.PiOver4 / 150) * (mouseState.X - prevMouseState.X) / 2;
  99:             if ((Math.Abs(currentYaw + yawAngle) < totalYaw) && (mouseState.LeftButton == ButtonState.Pressed))
 100:             {
 101:                 dir = Vector3.Transform(dir, Matrix.CreateFromAxisAngle(up, yawAngle));
 102:                 currentYaw += yawAngle;
 103:             }
 104:             #endregion
 105:  
 106:             #region roll rotation
 107:             if (mouseState.RightButton == ButtonState.Pressed) up = Vector3.Transform(up, Matrix.CreateFromAxisAngle(dir, MathHelper.PiOver4 / 45));
 108:             #endregion
 109:  
 110:             #region pitch rotation
 111:             float pitchAngle = (MathHelper.PiOver4 / 150) * (mouseState.Y - prevMouseState.Y) / 4;
 112:             if (Math.Abs(currentPitch + pitchAngle) < totalPitch && mouseState.LeftButton == ButtonState.Pressed)
 113:             {
 114:                 dir = Vector3.Transform(dir, Matrix.CreateFromAxisAngle(Vector3.Cross(up, dir), pitchAngle));
 115:                 currentPitch += pitchAngle;
 116:             }
 117:             #endregion
 118:  
 119:             createLookAt();
 120:             prevMouseState = Mouse.GetState();
 121:             base.Update(gameTime);
 122:         }
 123:  
 124:         /// <summary>
 125:         /// Mengupdate projection matrix dan view matrix dari camera
 126:         /// </summary>
 127:         private void createLookAt() 
 128:         {
 129:             view = Matrix.CreateLookAt(pos, pos + dir, up);
 130:             projection = Matrix.CreatePerspectiveFieldOfView(
 131:                             MathHelper.PiOver4,
 132:                             Game.GraphicsDevice.Viewport.AspectRatio,
 133:                             1, 10000
 134:                         );
 135:         }
 136:     }
 137: }

Sekarang kamera yang kita miliki dapat dikendalikan sebagai berikut :

Keyboard ASWD –> untuk navigasi panning keempat arah (ingat yang kita ubah posisinya adalah kamera)
Mouse Right button –> untuk rotasi berjenis roll
Mouse Left Button –> untuk rotasi berjenis yaw dan pitch
Mouse Scroll –> untuk zoom out dan zoom in
Keyboard R –> menginisialisasi kamera ke posisi semula

Nah, bagian utama selesai, sekarang untuk membuat objek labirin yang akan kita lihat, telah saya implementasikan dalam file maze.cs dan juga
mazeObject.cs. Pada intinya kita memiliki 2 objek 3D yaitu wall (tembok) dan tile(lantai) sebagai komponen labirin kita.

Game1.cs akan bertindak sebagai controller dari aplikasi ini. Berikut kode cari game1.cs, saya hanya menambahkan objek camera dan maze,
menginisialisasi objek tersebut, dan menambahkannya camera sebagai base component game, juga memanggil method draw dari maze pada
method draw utama dari game. Bagian-bagian lainnya telah digenerate secara otomatis oleh XNA. That’s all!

   1: using Microsoft.Xna.Framework;
   2: using Microsoft.Xna.Framework.Graphics;
   3: using Microsoft.Xna.Framework.Input;
   4:  
   5: namespace XNACamera
   6: {
   7:     /// <summary>
   8:     /// This is the main type for your game
   9:     /// </summary>
  10:     public class Game1 : Microsoft.Xna.Framework.Game
  11:     {
  12:         GraphicsDeviceManager graphics;
  13:         SpriteBatch spriteBatch;
  14:  
  15:         Camera camera;
  16:         Maze maze;
  17:  
  18:         public Game1()
  19:         {
  20:             graphics = new GraphicsDeviceManager(this);
  21:             Content.RootDirectory = "Content";
  22:         }
  23:  
  24:         /// <summary>
  25:         /// Allows the game to perform any initialization it needs to before starting to run.
  26:         /// This is where it can query for any required services and load any non-graphic
  27:         /// related content.  Calling base.Initialize will enumerate through any components
  28:         /// and initialize them as well.
  29:         /// </summary>
  30:         protected override void Initialize()
  31:         {
  32:             IsMouseVisible = true;
  33:             camera = new Camera(this, new Vector3(180,700,180), new Vector3(180,0,180), new Vector3(0,0,-1));
  34:             maze = new Maze(this);
  35:  
  36:             Components.Add(camera);
  37:             base.Initialize();
  38:         }
  39:  
  40:         /// <summary>
  41:         /// LoadContent will be called once per game and is the place to load
  42:         /// all of your content.
  43:         /// </summary>
  44:         protected override void LoadContent()
  45:         {
  46:             // Create a new SpriteBatch, which can be used to draw textures.
  47:             spriteBatch = new SpriteBatch(GraphicsDevice);
  48:  
  49:             // TODO: use this.Content to load your game content here
  50:         }
  51:  
  52:         /// <summary>
  53:         /// UnloadContent will be called once per game and is the place to unload
  54:         /// all content.
  55:         /// </summary>
  56:         protected override void UnloadContent()
  57:         {
  58:             // TODO: Unload any non ContentManager content here
  59:         }
  60:  
  61:         /// <summary>
  62:         /// Allows the game to run logic such as updating the world,
  63:         /// checking for collisions, gathering input, and playing audio.
  64:         /// </summary>
  65:         /// <param name="gameTime">Provides a snapshot of timing values.</param>
  66:         protected override void Update(GameTime gameTime)
  67:         {
  68:             // Allows the game to exit
  69:             if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
  70:                 this.Exit();
  71:  
  72:             // TODO: Add your update logic here
  73:             base.Update(gameTime);
  74:         }
  75:  
  76:         /// <summary>
  77:         /// This is called when the game should draw itself.
  78:         /// </summary>
  79:         /// <param name="gameTime">Provides a snapshot of timing values.</param>
  80:         protected override void Draw(GameTime gameTime)
  81:         {
  82:             GraphicsDevice.Clear(Color.CornflowerBlue);
  83:  
  84:             // TODO: Add your drawing code here
  85:             maze.Draw(gameTime,camera);
  86:             base.Draw(gameTime);
  87:         }
  88:     }
  89: }

 

Maze Viewer (Camera Tutorial XNA Framework)

552 Views, 3 Comment(s), Published on: 11-02-2010 13:26 by samsu.sempena to Samsu's passion (.NET)
| More
Filed under: , ,

Comments

 

veri said:

wah, ini riset lo di jepang ya sam?

November 4, 2010 11:48 PM
 

samsu.sempena said:

bukan ver, ini mendokumentasikan tugas stima dulu :p

November 5, 2010 6:21 AM
 

Samsu's passion (.NET) said:

XNA seperti yang telah dibahas pada post sebelumnya, merupakan framework yang disediakan Microsoft untuk

May 30, 2011 12:31 PM

About samsu.sempena

2007-now Faculty of Informatics, Institute Teknologi Bandung