XNA 3D 遊戲入門

基礎篇

C# 簡介

開發環境

變數與運算

流程控制

陣列

函數

物件

例外處理

函式庫篇

檔案處理

資料結構

正規表達式

Thread

應用篇

視窗程式

媒體影音

網路程式

遊戲程式

手機程式

資料庫

雲端運算

特殊功能

委派

擴展方法

序列化

LinQ

WPF

網路資源

教學影片

投影片

教學文章

軟體下載

考題解答

101習題

XNA 是微軟推出的遊戲設計引擎,以 C# 為主要語言。因此,您可以輕易的用 C# 在 Visual Studio 的 XNA 開發工具中撰寫 2D、3D 電腦遊戲。

筆者寫了一個打飛碟的簡單遊戲,以下是其執行畫面,您可以直接下載整個專案 3DFighterGameSimple.zip 回去試玩,並且從中學習 XNA 的程式設計方式,以下影片是筆者玩這個遊戲時所錄製下來的。

一個 XNA 的遊戲,是一個繼承 Microsoft.Xna.Framework.Game 這個類別的程式,其架構大致如下。

public class MyGame : Microsoft.Xna.Framework.Game 
{
        GraphicsDeviceManager graphics; // 繪圖裝置管理器
        GamePadState previousState; // XBOX 的按鈕狀態
#if !XBOX
        KeyboardState previousKeyboardState;      // 個人電腦的鍵盤狀態。
#endif
        GameObject gameObj = null;  // 模型物件
        SoundEffect sound = null; // 音效物件
        ...

        public GameMain()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize() // 初始化
        {
            camera = new Camera(this);
            this.Components.Add(camera);
            base.Initialize();
        }

        protected override void LoadContent()
        {
            gameObj.model = Content.Load<Model>("MyModel"); // 載入模型
            gameObj.scale = 0.1f; // 設定放大倍率
            gameObj.position = new Vector3(0.0f, 0.0f, -10.0f); // 設定起始位置
            ...
        }

        protected override void Update(GameTime gameTime)
        {
           // 1. 偵測鍵盤或滑鼠,根據輸入決定主角如何移動。
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            KeyboardState keyboardState = Keyboard.GetState();  //得到目前鍵盤每一個按鍵的狀況
            if (keyboardState.IsKeyDown(Keys.Space) && previousKeyboardState.IsKeyUp(Keys.Space))
            { 
               // 當空白鍵被按下時,...
            }

           // 2. 移動物件
           gameObj.position += gameObj.velocity;

           // 3. 播放聲音
           if (...)  explodeSound.Play();

           // 4. 呼叫基礎類別的 Update() 函數。
            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            gameObj.Draw(camera);

            base.Draw(gameTime);
        }

        public void Draw(Model model)
        {
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.PreferPerPixelLighting = true;

                    effect.World =
                        Matrix.CreateFromYawPitchRoll(
                        rotation.Y,
                        rotation.X,
                        rotation.Z) *

                        Matrix.CreateScale(scale) *

                        Matrix.CreateTranslation(position);

                    effect.Projection = camera.projection;
                    effect.View = camera.view;
                }
                mesh.Draw();
            }
        }
    }

    // 遊戲模型物件
    class GameObject
    {
        public Model model = null;
        public Vector3 position = Vector3.Zero;
        public Vector3 rotation = Vector3.Zero;
        public float scale = 1.0f;
        public Vector3 velocity = Vector3.Zero;
        public bool alive = false;

        public BoundingSphere boundingSphere()
        {
            BoundingSphere sphere = model.Meshes[0].BoundingSphere;
            sphere.Center = position;
            sphere.Radius *= scale;
            return sphere;
        }

        public void Draw(Camera camera)
        {
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.PreferPerPixelLighting = true;

                    effect.World =
                        Matrix.CreateFromYawPitchRoll(
                        rotation.Y,
                        rotation.X,
                        rotation.Z) *

                        Matrix.CreateScale(scale) *

                        Matrix.CreateTranslation(position);

                    effect.Projection = camera.projection;
                    effect.View = camera.view;
                }
                mesh.Draw();
            }
        }
    }

    // 相機物件
    class Camera : Microsoft.Xna.Framework.GameComponent
    {
        public Matrix view;         //  視覺矩陣
        public Matrix projection =        //  投影矩陣
                 Matrix.CreatePerspectiveFieldOfView(    
                                  MathHelper.PiOver4,  // 視角 45度
                                  1.333f, // 螢幕 寬高比
                                  1,      // 最近的Z軸截點
                                  10000);  // 最遠的Z軸截點 

        public Vector3 FrontVector = new Vector3(0, 0, -1); //相機的 往前向量
        public Vector3 Position = new Vector3(0.0f, 2.0f, 20.0f); // 相機的位置
        public float Yaw = 0.0f, Pitch = 0.0f, Roll = 0.0f;
        public float YawDelta = 0.2f, PitchDelta = 0.2f, RollDelta = 0.2f;
        public float MoveDelta = 200;   // 前後走 的 增減量

        public Camera(Game game)
            : base(game)
        {
            // TODO: Construct any child components here

        }

        //更新 相機 的位置
        public void UpdateCamera(Vector3 Position, float Yaw, float Pitch, float Roll)
        {
            //攝影機一開始是 朝向 負 Z 軸 看著(0, 0, -1) 的 
            Vector3 LookAt = Position + Vector3.TransformNormal(FrontVector,
                                            Matrix.CreateFromYawPitchRoll(Yaw, Pitch, Roll)); 

            // 根據 相機的位置 相機要看到的點 相機上方的向量 得到 視覺矩陣
            view = Matrix.CreateLookAt(Position,  // 相機的位置
                                       LookAt,    // 相機要看到的點
                                       Vector3.Up); // 相機上方的向量
        }

        public override void Initialize()
        {
            base.Initialize();
        }

        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here
            float elapsedTime = (float) gameTime.ElapsedGameTime.TotalSeconds;

            KeyboardState newState;  // 宣告一個KeyboardState 結構的變數
            newState = Keyboard.GetState();  //得到目前鍵盤每一個按鍵的狀況
            if (newState.IsKeyDown(Keys.Right)) Yaw -= YawDelta * elapsedTime; //右鍵按下
            if (newState.IsKeyDown(Keys.Left)) Yaw += YawDelta * elapsedTime;  //左鍵按下
            if (newState.IsKeyDown(Keys.Up)) Pitch += PitchDelta * elapsedTime;
            if (newState.IsKeyDown(Keys.Down)) Pitch -= PitchDelta * elapsedTime;
            UpdateCamera(Position, Yaw, Pitch, Roll);

            base.Update(gameTime);
        }
    }

參考文獻

  1. 2D/3D 遊戲程式設計入門 ─ 使用XNA3.0與C#(附光碟), 作者:鄞永傳、何振揚, 出版社:文魁, 出版日期:2009年02月06日, 語言:繁體中文 ISBN:9789866482120
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License