Serbest kamera sabit nesnelerden oluşan bir sahnede gezinmek için ideal bir kameradır. Ayrıca serbest kamera ile “First Person Shooter” oyunlarındaki gibi W,A,S,D tuşları ile yönlendirme yaparak, fare ile kameranın açısını ayarlayarak, oyuncunun gözünden oynama seçeneği de yapabiliriz.
Serbest Kamera sınıfını için daha önceki yazıda hazırladığımız temel kamera sınıfından miras alarak oluşturacağız.
public class FreeCamera : Camera { public float Yaw { get; set; } public float Pitch { get; set; } public Vector3 Position { get; set; } public Vector3 Target { get; private set; } private Vector3 translation; public FreeCamera(Vector3 Position, float Yaw, float Pitch, GraphicsDevice graphicsDevice) : base(graphicsDevice) { this.Position = Position; this.Yaw = Yaw; this.Pitch = Pitch; translation = Vector3.Zero; } }
Serbest kamera sınıfı Hedef kamerası sınıfından farklı olarak rotadan sapma açısı (yaw) ve eğim (pitch) değerlerine sahiptir. Bu iki değer radyan olarak sırasıyla Y ve X eksenleri etrafında dönmeye karşılık gelir.
Kameranın dönmesini sağlamak ve ilerlemesini sağlamak için ise Rotate() ve Move() methodları yazarak, klavyeden veya fareden gelen girişlere kameranın tepki vermesini sağlayabiliriz.
public void Rotate(float YawChange, float PitchChange) { this.Yaw += YawChange; this.Pitch += PitchChange; } public void Move(Vector3 Translation) { this.translation += Translation; }
Değişen kamera parametrelerine göre görünüm matrisini güncelleme işlemlerini ise Update() methodunun üzerine yazarak yapacağız.
public override void Update() { // Dönme matrisini hesapla Matrix rotation = Matrix.CreateFromYawPitchRoll(Yaw, Pitch, 0); // İlerleme miktarını hesapla ve dönüşümü sıfırla translation = Vector3.Transform(translation, rotation); Position += translation; translation = Vector3.Zero; // Yeni hedefi hesapla Vector3 forward = Vector3.Transform(Vector3.Forward, rotation); Target = Position + forward; // Kameranın üst kısmını gösteren vektörü hesapla Vector3 up = Vector3.Transform(Vector3.Up, rotation); // Görünüm matrisini hesapla View = Matrix.CreateLookAt(Position, Target, up); }
Bu methodları da sınıf içine yerleştirdiğimizde kameramız kullanıma hazır hale gelmiş oluyor. Bu kamerayı yazdığımız oyun içinde kullanmak istersek aşağıdaki kodları eklememiz gerekecek. Öncelikle global’de kamera ve fare durumlarını kaydetmek için gerekli değişkenleri oluşturalım.
List<CModel> models = new List<CModel>(); Camera camera; MouseState lastMouseState;
Daha sonra tanımlamış olduğumuz kamerayı LoadContent() içerisinde serbest kamera ile değiştirelim.
camera = new FreeCamera(new Vector3(100, 0, -200), // (1000,0,-2000) konumunda MathHelper.ToRadians(153), // 153 derece döndürülmüş MathHelper.ToRadians(5), // 5 derece eğilmiş GraphicsDevice);
Son olarak ise Update() methodu içinde kameranın klavye ve fare girişlerine güncellenmesi işlemlerini yapalım.
updateCamera(gameTime);
Burada kullanılan updateCamera() methodu ise şu şekildedir. Bu kodu Game1 class’ı içinde kalacak şekilde en sona yerleştirebiliriz.
void updateCamera(GameTime gameTime) { // Klavye ve Fare durumlarını kaydet MouseState mouseState = Mouse.GetState(); KeyboardState keyState = Keyboard.GetState(); // Kameranın ne kadar döneceğini hesapla float deltaX = (float)lastMouseState.X - (float)mouseState.X; float deltaY = (float)lastMouseState.Y - (float)mouseState.Y; // Kamerayı döndür ((FreeCamera)camera).Rotate(deltaX * .005f, deltaY * .005f); Vector3 translation = Vector3.Zero; // Kameranın ilerleme yönünü hesapla if (keyState.IsKeyDown(Keys.W)) translation += Vector3.Forward; if (keyState.IsKeyDown(Keys.S)) translation += Vector3.Backward; if (keyState.IsKeyDown(Keys.A)) translation += Vector3.Left; if (keyState.IsKeyDown(Keys.D)) translation += Vector3.Right; // FPS'den bağımsız olarak, 1 Milisaniyede 1 birim ilerleyecek şekilde öteleme miktarını hesapla translation *= 1 * (float)gameTime.ElapsedGameTime. TotalMilliseconds; // Kamerayı hareket ettir ((FreeCamera)camera).Move(translation); // Kamerayı güncelle camera.Update(); // Fare durumunu güncelle lastMouseState = mouseState; }
Game1.cs dosyasında yazılı Game1 sınıfının son durumunu incelemek isterseniz aşağıdaki koda bakabilirsiniz.
public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; List<CModel> models = new List<CModel>(); Camera camera; MouseState lastMouseState; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; graphics.PreferredBackBufferWidth = 1280; graphics.PreferredBackBufferHeight = 800; } protected override void Initialize() { base.Initialize(); } protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); camera = new FreeCamera(new Vector3(100, 0, -200), MathHelper.ToRadians(153), MathHelper.ToRadians(5), GraphicsDevice); lastMouseState = Mouse.GetState(); for (int y = 0; y < 3; y++) for (int x = 0; x < 3; x++) { Vector3 position = new Vector3(-100 + x * 100, -100 + y * 100, 0); models.Add(new CModel(Content.Load<Model>("Skyline"), position, new Vector3(0, MathHelper.ToRadians(90) * (y * 3 + x), 0), new Vector3(0.25f), GraphicsDevice)); } } protected override void UnloadContent() { } protected override void Update(GameTime gameTime) { // ESC'ye veya Joystick'den Geri tuşuna basarak oyundan çıkmak için if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Escape)) this.Exit(); updateCamera(gameTime); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); foreach (CModel model in models) model.Draw(camera.View, camera.Projection); base.Draw(gameTime); } void updateCamera(GameTime gameTime) { // Klavye ve Fare durumlarını kaydet MouseState mouseState = Mouse.GetState(); KeyboardState keyState = Keyboard.GetState(); // Kameranın ne kadar döneceğini hesapla float deltaX = (float)lastMouseState.X - (float)mouseState.X; float deltaY = (float)lastMouseState.Y - (float)mouseState.Y; // Kamerayı döndür ((FreeCamera)camera).Rotate(deltaX * .005f, deltaY * .005f); Vector3 translation = Vector3.Zero; // Kameranın ilerleme yönünü hesapla if (keyState.IsKeyDown(Keys.W)) translation += Vector3.Forward; if (keyState.IsKeyDown(Keys.S)) translation += Vector3.Backward; if (keyState.IsKeyDown(Keys.A)) translation += Vector3.Left; if (keyState.IsKeyDown(Keys.D)) translation += Vector3.Right; // FPS'den bağımsız olarak, 1 Milisaniyede 1 birim ilerleyecek şekilde öteleme miktarını hesapla translation *= 1 * (float)gameTime.ElapsedGameTime. TotalMilliseconds; // Kamerayı hareket ettir ((FreeCamera)camera).Move(translation); // Kamerayı güncelle camera.Update(); // Fare durumunu güncelle lastMouseState = mouseState; } }