WP7平衡球遊戲開發教程(二)--在XNA渲染渲染Farseer物理對象

在本節中,我們將看到一些實質代碼和world定位對象根本原理。首先,我們需要了解Farseer物理引擎對對象的測量。

瞭解Farseer物理引擎的測量方式

Farseer 是Box2D引擎的一個版本, 在 Box2D,如果我們要創建矩形,我們得告訴系統 寬度的一半值,高度的一半值和形狀的中心點。

Farseer物理引擎也是使用相同的測量方式。在Silverlight或其他.NET應用程序中,我們試圖創建矩形前先指定它的左上角的位置,然後提的高度和寬度的有關信息。

但是,這並不是Farseer物理引擎的使用方式,所以要記住的是,一個物理對象的位置總是指的是中心點不是左上角,但高度和寬度,應指100%的值,而不像Box2D中只填一半,這是唯一的不同。
現在如果你想創建單位爲10X10矩形,並和你想要的Farseer物理對象一樣大,那麼你最後將得到一個非常大體積的對象,因爲Farseer物理使用米爲單位。

因此,我們必須始終使用非常小的大小或需要一個轉換器轉換成我們的像素值。我喜歡轉換,因爲它會讓我在整個應用程序只考慮像素。因此,我們需要選擇一個轉換率,如1米= 15像素或類似的東西。這個比例將用於在整個應用程序轉換顯示的單位(像素)和模擬的單位(米),
最後在Farseer物理引擎中,旋轉使用的是弧度單位,所以一定要在分配物理對象旋轉量前轉換爲弧度。

  在這裏我不得不感謝一直支持我的滷麪網版主,是他讓我提起興趣寫了這麼一篇文章,再次感謝滷麪網,一個非常不錯的wp7開發論壇,後面我也將再次向大家發佈幾篇高質量文章,請大家到滷麪上找我吧,呵呵

進入正題:

Farseer物理引擎的使用演示
到目前爲止,我們已經有了物理對象,我們知道我們需要遵循什麼樣的標準,但有沒有UI相關的東西,因爲Farseer 只是個物理引擎,不包括用戶界面。因此如果我們想繼續開發,在world中創造一個body,我們將不能在屏幕上看到任何東西。要想讓Farseer物理對象出現在屏幕上,我們需要使用XNA 繪製他們。我們可以使用SpriteBatch類和DrawText方法在屏幕上繪製紋理和文字。下面是畫10X10大小的矩形的示例,並貼了MyTexture的紋理。

SpriteBatch.Draw(MyTexture, new Rectangle(0, 0, 10, 10), Color.White);

  

因此,讓我們的創建一個讓矩形因重力下降的例子。
打開vs2010 for windows phone,創建新的項目,並選擇XNA Game Studio 4.0 的項目模板,並命名它FarseerXNADemo1。它會給你生成2個項目,第一個是XNA的項目,另一個是內容項目用來管理資源(如圖像,聲音,字體等)
- 首先,我們需要引用WP7的DLL(Farseer物理引擎上可以找到http://farseerphysics.codeplex.com下載頁面)。現在,我們需要得到需要繪製 的一些紋理(這裏的紋理是指圖像),添加空白PNG圖像(blank.png)到項目內容。經過上述步驟後,我們的解決方案資源管理器將如下圖:

public Game1()
{
Window.Title = "DEMO 1";
_graphics = new GraphicsDeviceManager(this);
_graphics.PreferredBackBufferWidth = 480;
_graphics.PreferredBackBufferHeight = 800;
_graphics.IsFullScreen = true;
Content.RootDirectory = "Content";
}


  

-我們先設置窗口標題,然後創建了一個新的GraphicsDeviceManager 對象,它使用了目前的遊戲類的實例。現在我們指定寬度480,高度800,因爲我們希望我們的電話是在豎屏模式。同時,我們正在開發遊戲,所以我們不想手機上部分顯示電池或連接相關的東西,因此打開全屏標誌。(請注意,有時它是很好的做法,顯示電池的詳細信息,否則玩家可能會用盡電池,播放時,電話突然被關閉,這將是非常惱人)
- 在同一個文件中聲明一個Texture2D對象名爲MyTexture,一個世界對象名爲MyWorld ,2個body的對象名爲BoxBody和FloorBody
- 在initialize方法中,初始化所有4個對象,並給他們設置物理性能相關屬性。

Texture2D MyTexture;
World MyWorld;
Body BoxBody, FloorBody;
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
base.LoadContent();
//Load Blank Texture so that we can render colors over it.
MyTexture = Content.Load<Texture2D>("blank");
//Create New World with gravity of 10 units, downward.
MyWorld = new World(Vector2.UnitY * 10);
//Create Floor
Fixture floorFixture = FixtureFactory.CreateRectangle(MyWorld, ConvertUnits.ToSimUnits(480), ConvertUnits.ToSimUnits(10), 10);
floorFixture.Restitution = 0.5f; //Bounceability
floorFixture.Friction = 0.5f; //Friction
FloorBody = floorFixture.Body; //Get Body from Fixture
FloorBody.IsStatic = true; //Floor must be stationary object
//Create Box, (Note:Different way from above code, just to show it otherwise there is no difference)
BoxBody = BodyFactory.CreateBody(MyWorld);
FixtureFactory.CreateRectangle(ConvertUnits.ToSimUnits(50), ConvertUnits.ToSimUnits(50), 10, Vector2.Zero, BoxBody);
foreach (Fixture fixture in BoxBody.FixtureList)
{
fixture.Restitution = 0.5f;
fixture.Friction = 0.5f;
}
BoxBody.BodyType = BodyType.Dynamic;
//Place floor object to bottom of the screen.
FloorBody.Position = ConvertUnits.ToSimUnits(new Vector2(240, 700));
//Place Box on screen, somewhere
BoxBody.Position = ConvertUnits.ToSimUnits(new Vector2(240, 25));
}


  

-讓我們每次分析一行(爲有關ConvertUnits類的更多信息,參見“ ConvertUnits “小節)。第一行初始化方法加載紋理並將對象保存供以後使用。然後我們創建一個新的世界,有着向下10個單位的重力。之後,我們創建地板的body,創建它的形狀,使用FixtureFactory.CreateRectangle方法創建fixture 。它創建高度爲20個單位的矩形。它同時也創建了正確的shape和body。然後我們設置地板的摩擦係數和彈性係數,並最終保存,以供以後的body對象使用
- 我們可以用同樣的方法來創建物理對象,就像我們前面創造地板的方式一樣,但我想向您展示其他方式,所以我們將創建略有不同的代碼。我們首先創建一個body,如果我們傳遞world的對象到構造函數,那麼我們同時將把該body加入該world。然後我們創建fixture,它會創建形狀,並將它綁定到body(注意,我們這次在CreateRectangle方法中傳遞BoxBody作爲參數)。然後我們提供body 彈性係數和摩擦係數。最後一行是設置正確的body類型。我們想在重力作用下移動箱子,所以我們這個對象爲動態類型。
- 現在我們已經創建了地板和箱子,現在我們需要來定位他們在屏幕上的位置,地板需要放置在屏幕底部,而箱子要放置頂部某處
-ConvertUnits:這個類是用來轉換Farseer物理單位(米)到顯示單位(像素)。我們用16個單位的比例,即對我們1米= 16像素。對於800x480像素的屏幕,這個比率已經足夠好了。在這個類中有3個靜態方法,也有許多重載,方法詳情如下:
ToSimUnits -這種方法將輸入的值轉換成模擬單位,也就是說把顯示單元(像素)轉換爲模擬單位(米)。
ToDisplayUnits -這種方法將輸入的值轉換成顯示單位,也就是說把模擬單位(米)轉換爲顯示單元(像素)。
SetDisplayUnitToSimUnitRatio -此方法允許應用程序設置顯示單位到模擬單位轉換的轉換率。請正確地使用它,因爲錯誤設置的比例可能會導致意想不到的物理的效果(比如物體看起來太重...)。

In Farseer Physics measurements are in meters not in pixels. And position signifies centre point of object not the top-left corner.

  - 做物理相關的東西后,我們需要使用XNA繪製這些對象。我們在Draw方法中添加以下代碼。

protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
//Draw Box, its Height is 50 and Width is 50.
spriteBatch.Draw(MyTexture, new Rectangle((int)(ConvertUnits.ToDisplayUnits(BoxBody.Position.X) - 25), (int)(ConvertUnits.ToDisplayUnits(BoxBody.Position.Y) - 25), 50, 50), Color.Green);
//Draw Floor, its Height is 10 and Width is 480.
spriteBatch.Draw(MyTexture, new Rectangle((int)ConvertUnits.ToDisplayUnits(FloorBody.Position).X - 240, (int)ConvertUnits.ToDisplayUnits(FloorBody.Position).Y - 5, 480, 10), null, Color.Gray, FloorBody.Rotation, new Vector2(0, 0), SpriteEffects.None, 0);
spriteBatch.End();
base.Draw(gameTime);
}


  

-我們使用SpriteBatch類的Draw方法。在繪圖box的同時,我們提供紋理,和目標矩形。第一和第二個參數分別是矩形的左上角座標(記得Farseer 保存的是物體中心點位置,所以我們需要從左上角的位置減去一半的寬度和一半的高度,)。接下來的2個參數分別是高度和寬度。地板對象也是同理。

-如果你現在運行該項目,你會看到箱子和地板,但箱子不受重力的影響。這是因爲我們沒有更新世界。update方法需要添加的方法,看起來會像下面一樣:,

protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// variable time step but never less then 30 Hz
MyWorld.Step(Math.Min((float)gameTime.ElapsedGameTime.TotalMilliseconds * 0.001f, (1f / 30f)));
base.Update(gameTime);
}


  -按上面步驟寫出的代碼,效果會是:world會每秒前進30+個單位。現在,如果你運行的項目,你會感覺box受到重力下降一樣。

-現在你可能會覺得用XNA整合物理對象是如此容易,我也這樣認爲,但如果您去嘗試旋轉,轉換並組合更多形狀於一體,你將不得不精確的計算每個物品在屏幕上應該怎麼繪製,相信我,是不容易的,它似乎也將消耗大量的時間,因此要用一個通用的方法精確的得出要繪製對象的當前狀態。我們應該怎麼做......?答案是DebugDraw。

請大家關注下一節:

DebugDraw(XNA渲染)

我將在最後一節中放出所有源碼與實例,毫無保留。

我希望你能喜歡我的文章!如果你有更多想法,請到滷麪網 wp7開發論壇(codewp7.com)問答區聯繫我,我會很高興知道你在想什麼。同時wp7交流QQ羣172765887中,也能找到我的身影,感謝大家

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章