Bir önceki yazıda bir modelin nasıl çizdirileceğini görmüştük. Ancak bu işlemi bir model daha çizdirmek istesek yeniden mi yapamız gerekecek? Eğer oyunumuzda yüz tane model olsaydı yüz kere mi yazacaktık? Bu sorunları çözmek için bu yazıda çizim için gereken kodları tutan CModel adında jenerik bir sınıf yazmayı inceleyeceğiz. Bu sınıf modelin yüklenmesi için gereken dönüşümleri hesaplayıp, matrisleri ve efektleri ayarlayıp bize sunacak. Bu modeli daha sonra eklemek isteyeceğimiz özel efektler, dokuların yönetilmesi gibi durumlar için de kullanacağız. Ancak öncelikle modelin pozisyon (position), dönme (rotation), ölçekleme (scale), çizim için gerekli matrisler (world, view, projection) ve efektler (effect) gibi özellikleri ile başlayalım.
public class CModel { public Vector3 Position { get; set; } public Vector3 Rotation { get; set; } public Vector3 Scale { get; set; } public Model Model { get; private set; } private Matrix[] modelTransforms; private GraphicsDevice graphicsDevice; public CModel(Model Model, Vector3 Position, Vector3 Rotation, Vector3 Scale, GraphicsDevice graphicsDevice) { this.Model = Model; modelTransforms = new Matrix[Model.Bones.Count]; Model.CopyAbsoluteBoneTransformsTo(modelTransforms); this.Position = Position; this.Rotation = Rotation; this.Scale = Scale; this.graphicsDevice = graphicsDevice; } public void Draw(Matrix View, Matrix Projection) { // Dönme, Öteleme ve Ölçekleme bilgilerini kullanarak dönüşümleri hesapla Matrix baseWorld = Matrix.CreateScale(Scale) * Matrix.CreateFromYawPitchRoll(Rotation.Y, Rotation.X, Rotation.Z) * Matrix.CreateTranslation(Position); foreach (ModelMesh mesh in Model.Meshes) { Matrix localWorld = modelTransforms[mesh.ParentBone.Index] * baseWorld; foreach (ModelMeshPart meshPart in mesh.MeshParts) { BasicEffect effect = (BasicEffect)meshPart.Effect; effect.World = localWorld; effect.View = View; effect.Projection = Projection; effect.EnableDefaultLighting(); } mesh.Draw(); } } }
Artık CModel sınıfını kullanarak oyun kodumuzu daha sade hale getirebiliriz. Burada dikkat edilirse sınıfın yapıcı methodu (constructor) beş adet parametre almaktadır. Bunlar sırasıyla yüklemek istediğimiz model, modelin konumu, dönme miktarı, ölçeği ve ekran kartı bilgisidir. Draw() methodunda ise görünüm ve projeksiyon matrisleri parametre olarak istenmektedir. Bu matrisleri oyunu yazdığımız Game1 sınıfında hesaplayacağız. Bu işlemleri yaparken model dosyamızdan birden fazla örnek alıp kullanım kolaylığını da görelim.
public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; List<CModel> models = new List<CModel>(); 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); 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(); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); Matrix view = Matrix.CreateLookAt(new Vector3(0, 50, 400),new Vector3(0, 0, 0),Vector3.Up); Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), GraphicsDevice.Viewport.AspectRatio, 0.1f, 10000.0f); foreach (CModel model in models) model.Draw(view, projection); base.Draw(gameTime); } }