Windows Phone XNA 4.0 3D遊戲開發

來源:互聯網
上載者:User

作者:馬寧

程式碼編譯環境Visual Stuido 2010, Windows Phone 7 SDK, XNA 4.0 Game Studio, 下載連結:

http://files.cnblogs.com/aawolf/XNA_aawolf_3D.rar

今天有點空餘時間,在看Windows Phone 7的開發。看到Silverlight for phone和XNA 4.0的開發文章已經有了不少,而且品質很高。我就來說說XNA 4.0的3D開發這個領域吧,正好跟目前的工作有些關係,而且XNA 4.0的3D類庫設計的非常好,比iPhone和Android的OpenGLES類庫高出一個檔次。以後學習3D開發,用XNA類庫也是個不錯的選擇,而且Windows Phone模擬器對3D的支援也非常好。唯一的遺憾是,Windows Phone不支援C++的3D開發。

如果做過Zune上XNA 3.1開發的朋友可能會記得,在XNA 3.1中是不支援3D開發的,XNA 4.0中加入的3D支援類,主要包含在Microsoft.Xna.Framework.Graphics命名空間中。如果XNA 4.0中的3D概念與OpenGLES十分相似,我們可以找到很多相對應的函數、方法等,某種意義上,XNA 4.0的3D支援是對OpenGLES 2.0的封裝。

一.一個簡單的3D程式

我們就從一個簡單的3D程式開始吧,這個程式的原來介紹在下面這個連結裡。

http://msdn.microsoft.com/en-us/library/bb203926.aspx

不過移植到Windows Phone 7上時,還是遇到了一些小問題,有的是文檔的問題,有的是介面變化。如何在Visual Studio 2010裡建立XNA 4.0的工程就不多說了,大家可以參考我寫的《Windows Phone開發工具初體驗》,連結如下:

http://www.cnblogs.com/aawolf/archive/2010/08/28/1811438.html

XNA 4.0的程式是派生自Microsoft.Xna.Framework.Game的類,開發人員需要重載Game的四個方法:Initialize(初始化)、LoadContent(載入內容)、UnloadContent(卸載內容)、Update(更新)和Draw(繪製)等方法。

首先,我們在Game1類中加入所需要的一些私人變數:

Matrix worldMatrix;

Matrix viewMatrix;

Matrix projectionMatrix;

VertexPositionNormalTexture[] cubeVertices;

VertexDeclaration vertexDeclaration;

VertexBuffer vertexBuffer;

BasicEffect basicEffect;

Martrix 的中文名叫“矩陣”,還有個翻譯叫“駭客帝國”……扯遠了,什麼是矩陣?我們就不解釋了,只要知道矩陣是一切3D線性變化的基礎就可以了。我們不知道矩陣是什麼,卻身處其中。在Game1類中用了三個矩陣:worldMatrix用來描述全局座標系;viewMatrix用來描述攝影機座標系;projectionMatrix用來描述投影座標系。這些都是3D圖形學的概念,不解釋了。

另外兩個重要的變數是vertexBuffer和basicEffect。vertexBuffer包含了一系列的向量,這些向量構成了我們要顯示的正方體的各個頂點;basicEffect用來描述一個基礎的渲染效果,其中描述了座標系、顏色和燈光等基本的要素,這些要素是3D圖形顯示的基礎。

接下來建立一個叫InitMatrices的方法,對各個座標系進行初始化,記得,這個InitMatrices的函數是我們自己建立的,代碼如下:

private void InitMatrices()

{

// Initialize the world, view, and projection matrices.

float tilt = MathHelper.ToRadians(0); // 0 degree angle

// Use the world matrix to tilt the cube along x and y axes.

worldMatrix = Matrix.CreateRotationX(tilt) * Matrix.CreateRotationY(tilt);

viewMatrix = Matrix.CreateLookAt(new Vector3(5, 5, 5), Vector3.Zero, Vector3.Up);

projectionMatrix = Matrix.CreatePerspectiveFieldOfView(

MathHelper.ToRadians(45), // 45 degree angle

(float)GraphicsDevice.Viewport.Width /

(float)GraphicsDevice.Viewport.Height,

1.0f, 100.0f);

}

算了,不解釋了,大家知道這段代碼在幹什麼就好了。接下來,建立一個叫做InitEffect的函數中,對basicEffect進行初始化,代碼如下:

private void InitEffect()

{

// Initialize BasicEffect with transformation and light values

basicEffect = new BasicEffect(graphics.GraphicsDevice);

basicEffect.World = worldMatrix;

basicEffect.View = viewMatrix;

basicEffect.Projection = projectionMatrix;

// primitive color

basicEffect.AmbientLightColor = new Vector3(0.1f, 0.1f, 0.1f);

basicEffect.DiffuseColor = new Vector3(1.0f, 1.0f, 1.0f);

basicEffect.SpecularColor = new Vector3(0.25f, 0.25f, 0.25f);

basicEffect.SpecularPower = 5.0f;

basicEffect.Alpha = 1.0f;

basicEffect.LightingEnabled = true;

if (basicEffect.LightingEnabled)

{

basicEffect.DirectionalLight0.Enabled = true; // enable each light individually

if (basicEffect.DirectionalLight0.Enabled)

{

// x direction

basicEffect.DirectionalLight0.DiffuseColor = new Vector3(1, 0, 0); // range is 0 to 1

basicEffect.DirectionalLight0.Direction = Vector3.Normalize(new Vector3(-1, 0, 0));

// points from the light to the origin of the scene

basicEffect.DirectionalLight0.SpecularColor = Vector3.One;

}

basicEffect.DirectionalLight1.Enabled = true;

if (basicEffect.DirectionalLight1.Enabled)

{

// y direction

basicEffect.DirectionalLight1.DiffuseColor = new Vector3(0, 0.75f, 0);

basicEffect.DirectionalLight1.Direction = Vector3.Normalize(new Vector3(0, -1, 0));

basicEffect.DirectionalLight1.SpecularColor = Vector3.One;

}

basicEffect.DirectionalLight2.Enabled = true;

if (basicEffect.DirectionalLight2.Enabled)

{

// z direction

basicEffect.DirectionalLight2.DiffuseColor = new Vector3(0, 0, 0.5f);

basicEffect.DirectionalLight2.Direction = Vector3.Normalize(new Vector3(0, 0, -1));

basicEffect.DirectionalLight2.SpecularColor = Vector3.One;

}

}

}

然後要對vertexDeclaration、cubeVertices和vertexBuffer變數進行初始化,我們將這部分代碼放在InitVertexBuffer函數中:

private void InitVertexBuffer()

{

// Create a vertex declaration for the type VertexPositionNormalTexture

vertexDeclaration = new VertexDeclaration(new VertexElement[]

{

new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),

new VertexElement(12, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),

new VertexElement(24, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)

}

);

// Create the per vertex data

cubeVertices = new VertexPositionNormalTexture[36];

Vector3 topLeftFront = new Vector3(-1.0f, 1.0f, 1.0f);

Vector3 bottomLeftFront = new Vector3(-1.0f, -1.0f, 1.0f);

Vector3 topRightFront = new Vector3(1.0f, 1.0f, 1.0f);

Vector3 bottomRightFront = new Vector3(1.0f, -1.0f, 1.0f);

Vector3 topLeftBack = new Vector3(-1.0f, 1.0f, -1.0f);

Vector3 topRightBack = new Vector3(1.0f, 1.0f, -1.0f);

Vector3 bottomLeftBack = new Vector3(-1.0f, -1.0f, -1.0f);

Vector3 bottomRightBack = new Vector3(1.0f, -1.0f, -1.0f);

Vector2 textureTopLeft = new Vector2(0.0f, 0.0f);

Vector2 textureTopRight = new Vector2(1.0f, 0.0f);

Vector2 textureBottomLeft = new Vector2(0.0f, 1.0f);

Vector2 textureBottomRight = new Vector2(1.0f, 1.0f);

Vector3 frontNormal = new Vector3(0.0f, 0.0f, 1.0f);

Vector3 backNormal = new Vector3(0.0f, 0.0f, -1.0f);

Vector3 topNormal = new Vector3(0.0f, 1.0f, 0.0f);

Vector3 bottomNormal = new Vector3(0.0f, -1.0f, 0.0f);

Vector3 leftNormal = new Vector3(-1.0f, 0.0f, 0.0f);

Vector3 rightNormal = new Vector3(1.0f, 0.0f, 0.0f);

// Front face.

cubeVertices[0] =

new VertexPositionNormalTexture(

topLeftFront, frontNormal, textureTopLeft);

cubeVertices[1] =

new VertexPositionNormalTexture(

bottomLeftFront, frontNormal, textureBottomLeft);

cubeVertices[2] =

new VertexPositionNormalTexture(

topRightFront, frontNormal, textureTopRight);

cubeVertices[3] =

new VertexPositionNormalTexture(

bottomLeftFront, frontNormal, textureBottomLeft);

cubeVertices[4] =

new VertexPositionNormalTexture(

bottomRightFront, frontNormal, textureBottomRight);

cubeVertices[5] =

new VertexPositionNormalTexture(

topRightFront, frontNormal, textureTopRight);

// Back face.

cubeVertices[6] =

new VertexPositionNormalTexture(

topLeftBack, backNormal, textureTopRight);

cubeVertices[7] =

new VertexPositionNormalTexture(

topRightBack, backNormal, textureTopLeft);

cubeVertices[8] =

new VertexPositionNormalTexture(

bottomLeftBack, backNormal, textureBottomRight);

cubeVertices[9] =

new VertexPositionNormalTexture(

bottomLeftBack, backNormal, textureBottomRight);

cubeVertices[10] =

new VertexPositionNormalTexture(

topRightBack, backNormal, textureTopLeft);

cubeVertices[11] =

new VertexPositionNormalTexture(

bottomRightBack, backNormal, textureBottomLeft);

// Top face.

cubeVertices[12] =

new VertexPositionNormalTexture(

topLeftFront, topNormal, textureBottomLeft);

cubeVertices[13] =

new VertexPositionNormalTexture(

topRightBack, topNormal, textureTopRight);

cubeVertices[14] =

new VertexPositionNormalTexture(

topLeftBack, topNormal, textureTopLeft);

cubeVertices[15] =

new VertexPositionNormalTexture(

topLeftFront, topNormal, textureBottomLeft);

cubeVertices[16] =

new VertexPositionNormalTexture(

topRightFront, topNormal, textureBottomRight);

cubeVertices[17] =

new VertexPositionNormalTexture(

topRightBack, topNormal, textureTopRight);

// Bottom face.

cubeVertices[18] =

new VertexPositionNormalTexture(

bottomLeftFront, bottomNormal, textureTopLeft);

cubeVertices[19] =

new VertexPositionNormalTexture(

bottomLeftBack, bottomNormal, textureBottomLeft);

cubeVertices[20] =

new VertexPositionNormalTexture(

bottomRightBack, bottomNormal, textureBottomRight);

cubeVertices[21] =

new VertexPositionNormalTexture(

bottomLeftFront, bottomNormal, textureTopLeft);

cubeVertices[22] =

new VertexPositionNormalTexture(

bottomRightBack, bottomNormal, textureBottomRight);

cubeVertices[23] =

new VertexPositionNormalTexture(

bottomRightFront, bottomNormal, textureTopRight);

// Left face.

cubeVertices[24] =

new VertexPositionNormalTexture(

topLeftFront, leftNormal, textureTopRight);

cubeVertices[25] =

new VertexPositionNormalTexture(

bottomLeftBack, leftNormal, textureBottomLeft);

cubeVertices[26] =

new VertexPositionNormalTexture(

bottomLeftFront, leftNormal, textureBottomRight);

cubeVertices[27] =

new VertexPositionNormalTexture(

topLeftBack, leftNormal, textureTopLeft);

cubeVertices[28] =

new VertexPositionNormalTexture(

bottomLeftBack, leftNormal, textureBottomLeft);

cubeVertices[29] =

new VertexPositionNormalTexture(

topLeftFront, leftNormal, textureTopRight);

// Right face.

cubeVertices[30] =

new VertexPositionNormalTexture(

topRightFront, rightNormal, textureTopLeft);

cubeVertices[31] =

new VertexPositionNormalTexture(

bottomRightFront, rightNormal, textureBottomLeft);

cubeVertices[32] =

new VertexPositionNormalTexture(

bottomRightBack, rightNormal, textureBottomRight);

cubeVertices[33] =

new VertexPositionNormalTexture(

topRightBack, rightNormal, textureTopRight);

cubeVertices[34] =

new VertexPositionNormalTexture(

topRightFront, rightNormal, textureTopLeft);

cubeVertices[35] =

new VertexPositionNormalTexture(

bottomRightBack, rightNormal, textureBottomRight);

vertexBuffer = new VertexBuffer(

graphics.GraphicsDevice,

typeof(VertexPositionNormalTexture),

cubeVertices.Length,

BufferUsage.None

);

vertexBuffer.SetData<VertexPositionNormalTexture>(cubeVertices);

}

請原諒我把36個頂點的代碼都貼出來了,如果不貼出來,肯定會有人不補全,然後就看不到完整的正方體了。

這裡就要說到第一個錯誤點了:文章中沒有列出所有36個頂點的定義,不過範例程式碼UseBasicEffect中列出了;另一個問題是VertexBuffer的建構函式發生了變化,原文和範例程式碼中的VertexBuffer建構函式是這樣的:

vertexBuffer = new VertexBuffer(

graphics.GraphicsDevice,

VertexPositionNormalTexture.SizeInBytes * cubeVertices.Length,

BufferUsage.None

);

而正確的寫法應該是:

vertexBuffer = new VertexBuffer(

graphics.GraphicsDevice,

typeof(VertexPositionNormalTexture),

cubeVertices.Length,

BufferUsage.None

);

VertexBuffer增加了一個Type類型的參數(第二個),我們必須傳入一個IVertexType介面的衍生類別型,建構函式會用類型和頂點列表的長度計算VertexBuffer的size,這顯然比上邊的實現好了許多。

分別實現了這三個初始化函數後,我們要在真正的初始化函數Initialize裡調用這三個函數,注意Initialize函數不是自己添加的,在Game1類中本來就有:

protected override void Initialize()

{

InitMatrices();

InitEffect();

InitVertexBuffer();

base.Initialize();

}

好了,我們在Draw函數裡增加繪製方法:

protected override void Draw(GameTime gameTime)

{

GraphicsDevice.Clear(Color.CornflowerBlue);

// TODO: Add your drawing code here

RasterizerState rasterizerState1 = new RasterizerState();

rasterizerState1.CullMode = CullMode.None;

graphics.GraphicsDevice.RasterizerState = rasterizerState1;

GraphicsDevice.SetVertexBuffer(vertexBuffer);

foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)

{

pass.Apply();

graphics.GraphicsDevice.DrawPrimitives(

PrimitiveType.TriangleList,

0,

36

);

}

base.Draw(gameTime);

}

這裡包含了第二個錯誤點,原文沒有下面這句(上文高亮標出):

GraphicsDevice.SetVertexBuffer(vertexBuffer);

如果沒有SetVertexBuffer的調用,程式在運行時會遇到下面的異常:

An unhandled exception of type 'System.InvalidOperationException' occurred in Microsoft.Xna.Framework.Graphics.dll

Additional information: A valid vertex buffer (and a valid index buffer if you are using indexed primitives) must be set on the device before any draw operations may be performed.

原文的調用方式和UseBasicEffect的實現方式完全不同,所以大家要注意一下。畢竟是Beta版,很多文檔還沒有最後完成。

好了,到這裡,其實我們編譯運行該程式的話,就可以看到繪製出的立方體來了。但是,我還想再加點——讓立方體旋轉起來。

在Update函數中增加下面兩句(高亮顯示):

protected override void Update(GameTime gameTime)

{

// Allows the game to exit

if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)

this.Exit();

// TODO: Add your update logic here

Matrix matrix = Matrix.CreateRotationX(0.1f);

basicEffect.World = basicEffect.World * matrix;

base.Update(gameTime);

}

我們建立了一個沿X軸旋轉0.1度的矩陣,與basicEffect中的全局座標系相乘,就可以使我們繪製出來的立方體每次Update時,都沿著X軸旋轉0.1f度。因為角度是float型,千萬別忘了0.1f之後的那個f。

好了,程式最後的樣子就是這樣的。在第一篇文章裡,我留了很多問題,比如3D的基本概念、座標系、燈光、材質、旋轉,希望在後邊能夠比較從容地解釋這些知識。我現在唯一的希望是,不要等到六個月後才有時間再寫第二篇……

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.