Skip to main content

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.

kamera_yaw_pitchcamera_yaw_pitch

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;
    }
}