Windows Phone 7開發海盜遊戲(有源碼)

大家好哦!在這篇文章中,我將放出一個 Windows 手機遊戲(或至少是一個遊戲的開始......),並希望它給大家慢慢帶來更多的樂趣。同時給大家介紹所需要的基本技術。然後,我將介紹其中的一些步驟,我希望這能一直引起你的興趣,直到文章的結尾。
同時我也不得不感謝一直支持我的滷麪網版主,是他讓我提起興趣寫了這麼一篇文章,再次感謝滷麪網,一個非常不錯的wp7開發論壇,後面我也將再次向大家發佈幾篇高質量文章,請大家到滷麪上找我吧,呵呵
好了,正題開始..

“海盜! 是一個wp7上的用C#和XNA做出來的遊戲,使用Farseer物理引擎。遊戲的想法,主要是啓發於Rovio的“憤怒的小鳥”的遊戲,在2011年年底達到500萬次的下載。憤怒的小鳥可能已經上癮了世界各地的許多人,但是從我個人而言,我不沉迷於遊戲本身,而是追求創作一個這樣的遊戲所需要的過程。

而不是鳥,而是大炮炮彈。而不是豬,而是大海盜船的海盜。在這裏你的任務是使用大炮瞄準,並銷燬所有海盜。

Farseer物理引擎

這是一個很酷的開源物理引擎,一個開放源碼項目(順便說一句,憤怒的小鳥也是使用Box2D的)。所不同的是用的 C + +的Box2D(並已被移植到多國語言),而本遊戲用的C#,Silverlight和XNA。

調整遊戲每秒60幀

1.爲了達到最大幀每秒(約60幀),你必須修改你下載的farseer的源代碼:
2.雙擊“Samples XNA WP7 ”解決方案。選擇“Upgrade Windows Phone Projects... “ 他們全部升級到最新的Windows Phone 7.5芒果。
在遊戲類的構造函數中,添加此事件處理程序:

_graphics.PreparingDeviceSettings +=
new EventHandler<PreparingDeviceSettingsEventArgs>(_graphics_PreparingDeviceSettings);


  3.然後添加此事件:

void _graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
{
e.GraphicsDeviceInformation.PresentationParameters.PresentationInterval = PresentInterval.One;
}


  4.在遊戲類的初始化方法中,確保修改PresentationInterval參數爲PresentationInterval.One:

protected override void Initialize()
{
base.Initialize();
this.GraphicsDevice.PresentationParameters.PresentationInterval =
Microsoft.Xna.Framework.Graphics.PresentInterval.One;
...


  幸運的是,我已經修改出了Farseer物理引擎兼容的編譯版本,它能在 Windows Phone 7.5上運行儘可能最好的幀率。這將給對farseer引擎與遊戲開發有興趣的讀者帶來非常有用的幫助,

轉換成物理對象的圖像
對遊戲中的所有動態創建對象進行貼圖。這是 Farseer一個非常不錯的功能,使我們的開發更容易。我們留下的空白與透明色,如圖,

Farseer使用BayazitDecomposer.ConvexPartition的方法,創建一個大凹多邊形的小凸多邊形:

TextureBody textureBody;
// 加載的紋理,將代表的物理身體
var physicTexture = ScreenManager.Content.Load<Texture2D>(string.Format("Samples/{0}", physicTextureName));
// 創建一個數組來保存紋理數據
uint[] data = new uint[physicTexture.Width * physicTexture.Height];
// 紋理數據傳送到陣列
physicTexture.GetData(data);
// 查找紋理的形狀輪廓頂點
Vertices textureVertices = PolygonTools.CreatePolygon(data, physicTexture.Width, false);
//在紋理中發現的頂點。
// 我們需要找到真正的中心(重心)的頂點,原因有二:
// 1。轉換多邊形的頂點,使周圍的重心集中。
Vector2 centroid = -textureVertices.GetCentroid() + bodyOrigin - centerScreen;
textureVertices.Translate(ref centroid);
// 2。到正確的位置繪製紋理。
var origin = -centroid;
// 我們簡化紋理的頂點。
textureVertices = SimplifyTools.ReduceByDistance(textureVertices, 4f);
// 因爲它是一個凹多邊形,我們需要進行分區成幾個較小的凸多邊形
List<Vertices> list = BayazitDecomposer.ConvexPartition(textureVertices);
// 調整對象爲WP7的較低分辨率
_scale = 1f;
Vector2 vertScale = new Vector2(ConvertUnits.ToSimUnits(1)) * _scale;
foreach (Vertices vertices in list)
{
vertices.Scale(ref vertScale);
}


  處理輸入循環

Farseer 引擎處理輸入循環的接口,使我們有機會來檢測和處理用戶的手勢。

此方法是負責:

檢測左邊搖桿運動,並轉化成大炮運動。
檢測的右邊“A”的按鈕,射擊大炮。


public override void HandleInput(InputHelper input, GameTime gameTime)
{
var cannonCenter = new Vector2(0, 0);
var cannonLength = 10f;
var leftX = input.VirtualState.ThumbSticks.Left.X;
var leftY = input.VirtualState.ThumbSticks.Left.Y;
var cos = -leftX;
var sin = leftY;
var newBallPosition = cannonCenter + new Vector2(cos * cannonLength, sin * cannonLength);
if (leftX < 0)
{
lastThumbSticksLeft.X = leftX;
lastThumbSticksLeft.Y = leftY;
}
if (leftX != 0 || leftY != 0)
{
var newAngle = cannonAngle + leftY / gameTime.ElapsedGameTime.Milliseconds;
if (newAngle < GamePredefinitions.MaxCannonAngle &&
newAngle > GamePredefinitions.MinCannonAngle)
{
cannonAngle = newAngle;
}
}
if (input.VirtualState.IsButtonDown(Buttons.A))
{
cannonBall.ResetHitCount();
smokeTracePositions.Clear();
VibrateController.Default.Start(TimeSpan.FromMilliseconds(20));
cannonBall.Body.AngularVelocity = 0;
cannonBall.Body.LinearVelocity = new Vector2(0, 0);
cannonBall.Body.SetTransform(new Vector2(0, 0), 0);
cannonBall.Body.ApplyLinearImpulse(new Vector2(GamePredefinitions.Impulse * (float)System.Math.Cos(cannonAngle),
GamePredefinitions.Impulse * (float)System.Math.Sin(cannonAngle)));
PlaySound("Audio/cannon.wav");
}
base.HandleInput(input, gameTime);
}


  Update循環

在XNA框架中,更新循環被調用時,需要處理遊戲邏輯。這可能包括遊戲狀態管理,處理用戶輸入,或數據的更新。通過重寫此方法,我們可以添加邏輯,具體到我們的海盜遊戲我下面也會慢慢講。

在XNA中, Update方法是和draw方法組合起來用的,你必須考慮到每個對象都有自己的特定角色。也就是說,一定不要把draw寫入到了update中,也不要在draw中去更新update。

update 類主要有以下用途:

計算新的雲的位置。有3層雲 ,每一層以自己的速度移動。
海的外觀是由4個現有的圖片紋理輪流顯示。
根據遊戲狀態轉換移動camera (也就是說,遊戲一開始是顯示你的海盜船,然後camera移動到你的船)。
要沿炮彈描述的路徑更新camera位置。這是有用的,以保持遊戲動作的軌道。
必要時控制camera變焦。
也許最重要的是:更新大炮球的位置(隨着煙霧留痕跡飛)

public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
{
base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
if (cannonBall.Body.LinearVelocity == Vector2.Zero)
{
cannonBall.Body.SetTransform(Vector2.Zero, 0f);
}
switch (seaStep)
{
case 0:
seaTexture = sea1Texture;
break;
case 1:
seaTexture = sea2Texture;
break;
case 2:
seaTexture = sea3Texture;
break;
case 3:
seaTexture = sea4Texture;
break;
}
lastSeaStepTime = lastSeaStepTime.Add(gameTime.ElapsedGameTime);
if (lastSeaStepTime.TotalSeconds > 1)
{
lastSeaStepTime = TimeSpan.Zero;
seaStep++;
if (seaStep == 4)
seaStep = 0;
}
lastCloudStep1Time = lastCloudStep1Time.Add(gameTime.ElapsedGameTime);
lastCloudStep2Time = lastCloudStep2Time.Add(gameTime.ElapsedGameTime);
lastCloudStep3Time = lastCloudStep3Time.Add(gameTime.ElapsedGameTime);
if (lastCloudStep1Time.TotalMilliseconds > GamePredefinitions.CloudStep1MaxTimeMs)
{
lastCloudStep1Time = TimeSpan.Zero;
cloudStep1++;
if (cloudStep1 == GamePredefinitions.MaxCloudStep)
cloudStep1 = 0;
}
if (lastCloudStep2Time.TotalMilliseconds > GamePredefinitions.CloudStep2MaxTimeMs)
{
lastCloudStep2Time = TimeSpan.Zero;
cloudStep2++;
if (cloudStep2 == GamePredefinitions.MaxCloudStep)
cloudStep2 = 0;
}
if (lastCloudStep3Time.TotalMilliseconds > GamePredefinitions.CloudStep3MaxTimeMs)
{
lastCloudStep3Time = TimeSpan.Zero;
cloudStep3++;
if (cloudStep3 == 800)
cloudStep3 = 0;
}
var ballCenter = ConvertUnits.ToDisplayUnits(cannonBall.Body.WorldCenter);
var ballX = ballCenter.X;
var ballY = ballCenter.Y;
var cameraX = GamePredefinitions.CameraInitialPosition.X;
var cameraY = GamePredefinitions.CameraInitialPosition.Y;
if (gameStateMachine.CurrentSate == GameState.Playing)
{
if (ballX < -scrollableViewport.Width / 6)
{
cameraX = -scrollableViewport.Width / 6;
}
else if (ballX > scrollableViewport.Width / 6)
{
cameraX = scrollableViewport.Width / 6;
}
else
{
cameraX = ballX;
}
if (ballY < -scrollableViewport.Height / 6)
{
cameraY = -scrollableViewport.Height / 6;
}
else if (ballY > scrollableViewport.Height / 6)
{
cameraY = scrollableViewport.Height / 6;
}
else
{
cameraY = ballY;
}
Camera.Position = new Vector2(cameraX, cameraY);
}
else if (gameStateMachine.CurrentSate == GameState.ShowingPirateShip)
{
if (gameStateMachine.EllapsedTimeSinceLastChange().TotalMilliseconds > GamePredefinitions.TotalTimeShowingPirateShipMs)
{
gameStateMachine.ChangeState(GameState.ScrollingToStartPlaying);
}
}
else if (gameStateMachine.CurrentSate == GameState.ScrollingToStartPlaying)
{
var newCameraPosX = Camera.Position.X + (float)-10.0 * (gameTime.ElapsedGameTime.Milliseconds);
Camera.Position = new Vector2(newCameraPosX, scrollableViewport.Height / 6);
if (Camera.Zoom < 1.0f)
{
Camera.Zoom += gameTime.ElapsedGameTime.Milliseconds / GamePredefinitions.CameraZoomRate;
}
if (Camera.Position.X < -scrollableViewport.Width / 6)
{
Camera.Position = new Vector2(-scrollableViewport.Width / 6, Camera.Position.Y);
gameStateMachine.ChangeState(GameState.Playing);
}
}
if (cannonBall.HitCount == 0)
{
if (smokeTracePositions.Count() == 0)
{
smokeTracePositions.Add(cannonBall.Body.Position);
}
else
{
var lastBallPosition = smokeTracePositions.Last();
var currentBallPosition = cannonBall.Body.Position;
var deltaX = Math.Abs((lastBallPosition.X - currentBallPosition.X));
var deltaY = Math.Abs((lastBallPosition.Y - currentBallPosition.Y));
if (deltaX * deltaX + deltaY * deltaY > GamePredefinitions.SmokeTraceSpace * GamePredefinitions.SmokeTraceSpace)
{
smokeTracePositions.Add(cannonBall.Body.Position);
}
}
}
}


  在update函數中一個很重要是使用 GameTime參數。此參數告訴我們時間,因爲遊戲是最後一次更新花了多少時。正如你可以在上面看到,它使我們能夠正確地計算cpu的速度波動的干擾。否則,你可能會看到比賽快或慢,取決於你手機在某一時刻的cpu的處理速度。

draw循環

是XNA框架繪製的重要模塊。我們重寫此方法來繪製我們的海盜遊戲所需的所有幀。

這個循環處理了下面所說的一部分內容:

繪製背景紋理
繪製的藍天,白雲,大海和船
繪製了炮彈,大炮和煙霧的痕跡
繪製了現場的海盜和其他對象。
得分和最高分數。

public override void Draw(GameTime gameTime)
{
ScreenManager.SpriteBatch.Begin(0, null, null, null, null, null, Camera.View);
if (gameStateMachine.CurrentSate != GameState.None)
{
var skyRect = GamePredefinitions.SkyTextureRectangle;
var seaRect = GamePredefinitions.SeaTextureRectangle;
ScreenManager.SpriteBatch.Draw(skyTexture, skyRect, Color.White);
ScreenManager.SpriteBatch.Draw(cloud1Texture, new Rectangle(skyRect.X + cloudStep1, skyRect.Y, skyRect.Width, skyRect.Height), Color.White);
ScreenManager.SpriteBatch.Draw(cloud2Texture, new Rectangle(skyRect.X + cloudStep2 * 2, skyRect.Y, skyRect.Width, skyRect.Height), Color.White);
ScreenManager.SpriteBatch.Draw(cloud3Texture, new Rectangle(skyRect.X + cloudStep3 * 3, skyRect.Y, skyRect.Width, skyRect.Height), Color.White);
ScreenManager.SpriteBatch.Draw(seaTexture, seaRect, Color.White);
ScreenManager.SpriteBatch.Draw(pirateShipTexture, GamePredefinitions.PirateShipTextureRectangle, Color.White);
ScreenManager.SpriteBatch.Draw(royalShipTexture, GamePredefinitions.RoyalShipTextureRectangle, Color.White);
}
smokeTracePositions.ForEach(e =>
ScreenManager.SpriteBatch.Draw(smokeTraceTexture, ConvertUnits.ToDisplayUnits(e) + GamePredefinitions.CannonCenter - new Vector2(10, 10),
null, Color.White, 0f, Vector2.Zero, _scale, SpriteEffects.None, 0f));
foreach (var textureBody in textureBodies)
{
ScreenManager.SpriteBatch.Draw(textureBody.DisplayTexture, ConvertUnits.ToDisplayUnits(textureBody.Body.Position),
null, Color.White, textureBody.Body.Rotation, textureBody.Origin, _scale, SpriteEffects.None, 0f);
}
ScreenManager.SpriteBatch.Draw(cannonTexture, GamePredefinitions.CannonCenter,
null, Color.White, cannonAngle, new Vector2(40f, 29f), 1f, SpriteEffects.None,
0f);
var hiScoreText = string.Format("hi score: {0}", scoreManager.GetHiScore());
var scoreText = string.Format("score: {0}", scoreManager.GetScore());
DrawScoreText(0, 0, hiScoreText);
DrawScoreText(0, 30, scoreText);
ScreenManager.SpriteBatch.End();
_border.Draw();
base.Draw(gameTime);
}


  首先,我們繪製的天空。然後我們繪製以水平不同的速度運動的雲 ,然後我們畫的大海,然後船。最後,我們得出的動態元素:炮彈,大炮,比分。

最後的思考

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

全部源碼下載請猛擊

原文請見

http://www.codeproject.com/Articles/322715/WPPirates


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