【3D技术宅公社】XR数字艺术论坛  XR技术讨论 XR互动电影 定格动画

 找回密码
 立即注册

QQ登录

只需一步,快速开始

调查问卷
论坛即将给大家带来全新的技术服务,面向三围图形学、游戏、动画的全新服务论坛升级为UTF8版本后,中文用户名和用户密码中有中文的都无法登陆,请发邮件到324007255(at)QQ.com联系手动修改密码

3D技术论坛将以计算机图形学为核心,面向教育 推出国内的三维教育引擎该项目在持续研发当中,感谢大家的关注。

查看: 2087|回复: 2

Windows Phone开发海盗游戏!带源码(一)

[复制链接]
发表于 2012-8-4 21:31:40 | 显示全部楼层 |阅读模式


   大家好哦!在这篇文章中,我将放出一个 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芒果。
在游戏类的构造函数中,添加此事件处理程序:
  1. _graphics.PreparingDeviceSettings +=
  2. new EventHandler<PreparingDeviceSettingsEventArgs>(_graphics_PreparingDeviceSettings);
复制代码
3.然后添加此事件:
  1. void _graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)   
  2. {            
  3.     e.GraphicsDeviceInformation.PresentationParameters.PresentationInterval = PresentInterval.One;
  4. }
复制代码
4.在游戏类的初始化方法中,确保修改PresentationInterval参数为PresentationInterval.One:
  1. protected override void Initialize()      
  2.     {            
  3.         base.Initialize();
  4.         this.GraphicsDevice.PresentationParameters.PresentationInterval =
  5.         Microsoft.Xna.Framework.Graphics.PresentInterval.One;
  6.         ...
复制代码
幸运的是,我已经修改出了Farseer物理引擎兼容的编译版本,它能在 Windows Phone 7.5上运行尽可能最好的帧率。这将给对farseer引擎与游戏开发有兴趣的读者带来非常有用的帮助,

  转换成物理对象的图像
对游戏中的所有动态创建对象进行贴图。这是 Farseer一个非常不错的功能,使我们的开发更容易。我们留下的空白与透明色,如图,

2012-2-25 14:38:56 上传
下载附件 (28.28 KB)

Farseer使用BayazitDecomposer.ConvexPartition的方法,创建一个大凹多边形的小凸多边形:
  1. TextureBody textureBody;
  2.    // 加载的纹理,将代表的物理身体
  3.     var physicTexture = ScreenManager.Content.Load<Texture2D>(string.Format("Samples/{0}", physicTextureName));

  4.     // 创建一个数组来保存纹理数据  
  5.     uint[] data = new uint[physicTexture.Width * physicTexture.Height];

  6.     // 纹理数据传送到阵列
  7.     physicTexture.GetData(data);

  8.     // 查找纹理的形状轮廓顶点
  9.     Vertices textureVertices = PolygonTools.CreatePolygon(data, physicTexture.Width, false);

  10.     //在纹理中发现的顶点。
  11.     // 我们需要找到真正的中心(重心)的顶点,原因有二:

  12.     // 1。转换多边形的顶点,使周围的重心集中。
  13.     Vector2 centroid = -textureVertices.GetCentroid() + bodyOrigin - centerScreen;
  14.     textureVertices.Translate(ref centroid);

  15.     // 2。到正确的位置绘制纹理。
  16.     var origin = -centroid;

  17.      // 我们简化纹理的顶点。
  18.     textureVertices = SimplifyTools.ReduceByDistance(textureVertices, 4f);

  19.     // 因为它是一个凹多边形,我们需要进行分区成几个较小的凸多边形
  20.     List<Vertices> list = BayazitDecomposer.ConvexPartition(textureVertices);

  21.     // 调整对象为WP7的较低分辨率
  22.     _scale = 1f;

  23.      
  24.     Vector2 vertScale = new Vector2(ConvertUnits.ToSimUnits(1)) * _scale;
  25.     foreach (Vertices vertices in list)
  26.     {
  27.         vertices.Scale(ref vertScale);
  28.     }
复制代码
处理输入循环

Farseer 引擎处理输入循环的接口,使我们有机会来检测和处理用户的手势。

此方法是负责:

检测左边摇杆运动,并转化成大炮运动。
检测的右边“A”的按钮,射击大炮。
 楼主| 发表于 2012-8-4 21:34:16 | 显示全部楼层
接上
  1. public override void HandleInput(InputHelper input, GameTime gameTime)
  2.         {
  3.             var cannonCenter = new Vector2(0, 0);
  4.             var cannonLength = 10f;

  5.             var leftX = input.VirtualState.ThumbSticks.Left.X;
  6.             var leftY = input.VirtualState.ThumbSticks.Left.Y;
  7.             var cos = -leftX;
  8.             var sin = leftY;
  9.             var newBallPosition = cannonCenter + new Vector2(cos * cannonLength, sin * cannonLength);

  10.             if (leftX < 0)
  11.             {
  12.                 lastThumbSticksLeft.X = leftX;
  13.                 lastThumbSticksLeft.Y = leftY;
  14.             }

  15.             if (leftX != 0 || leftY != 0)
  16.             {
  17.                 var newAngle = cannonAngle + leftY / gameTime.ElapsedGameTime.Milliseconds;
  18.                 if (newAngle < GamePredefinitions.MaxCannonAngle &&
  19.                     newAngle > GamePredefinitions.MinCannonAngle)
  20.                 {
  21.                     cannonAngle = newAngle;
  22.                 }
  23.             }

  24.             if (input.VirtualState.IsButtonDown(Buttons.A))
  25.             {
  26.                 cannonBall.ResetHitCount();
  27.                 smokeTracePositions.Clear();
  28.                 VibrateController.Default.Start(TimeSpan.FromMilliseconds(20));
  29.                 cannonBall.Body.AngularVelocity = 0;
  30.                 cannonBall.Body.LinearVelocity = new Vector2(0, 0);
  31.                 cannonBall.Body.SetTransform(new Vector2(0, 0), 0);
  32.                 cannonBall.Body.ApplyLinearImpulse(new Vector2(GamePredefinitions.Impulse * (float)System.Math.Cos(cannonAngle),
  33.                     GamePredefinitions.Impulse * (float)System.Math.Sin(cannonAngle)));

  34.                 PlaySound("Audio/cannon.wav");
  35.             }

  36.             base.HandleInput(input, gameTime);
  37.         }
复制代码
Update循环

在XNA框架中,更新循环被调用时,需要处理游戏逻辑。这可能包括游戏状态管理,处理用户输入,或数据的更新。通过重写此方法,我们可以添加逻辑,具体到我们的海盗游戏我下面也会慢慢讲。

在XNA中, Update方法是和draw方法组合起来用的,你必须考虑到每个对象都有自己的特定角色。也就是说,一定不要把draw写入到了update中,也不要在draw中去更新update。

update 类主要有以下用途:

计算新的云的位置。有3层云 ,每一层以自己的速度移动。
海的外观是由4个现有的图片纹理轮流显示。
根据游戏状态转换移动camera (也就是说,游戏一开始是显示你的海盗船,然后camera移动到你的船)。
要沿炮弹描述的路径更新camera位置。这是有用的,以保持游戏动作的轨道。
必要时控制camera变焦。
也许最重要的是:更新大炮球的位置(随着烟雾留痕迹飞)
  1. public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
  2.         {
  3.             base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

  4.             if (cannonBall.Body.LinearVelocity == Vector2.Zero)
  5.             {
  6.                 cannonBall.Body.SetTransform(Vector2.Zero, 0f);
  7.             }

  8.             switch (seaStep)
  9.             {
  10.                 case 0:
  11.                     seaTexture = sea1Texture;
  12.                     break;
  13.                 case 1:
  14.                     seaTexture = sea2Texture;
  15.                     break;
  16.                 case 2:
  17.                     seaTexture = sea3Texture;
  18.                     break;
  19.                 case 3:
  20.                     seaTexture = sea4Texture;
  21.                     break;
  22.             }

  23.             lastSeaStepTime = lastSeaStepTime.Add(gameTime.ElapsedGameTime);

  24.             if (lastSeaStepTime.TotalSeconds > 1)
  25.             {
  26.                 lastSeaStepTime = TimeSpan.Zero;

  27.                 seaStep++;
  28.                 if (seaStep == 4)
  29.                     seaStep = 0;
  30.             }

  31.             lastCloudStep1Time = lastCloudStep1Time.Add(gameTime.ElapsedGameTime);
  32.             lastCloudStep2Time = lastCloudStep2Time.Add(gameTime.ElapsedGameTime);
  33.             lastCloudStep3Time = lastCloudStep3Time.Add(gameTime.ElapsedGameTime);

  34.             if (lastCloudStep1Time.TotalMilliseconds > GamePredefinitions.CloudStep1MaxTimeMs)
  35.             {
  36.                 lastCloudStep1Time = TimeSpan.Zero;

  37.                 cloudStep1++;
  38.                 if (cloudStep1 == GamePredefinitions.MaxCloudStep)
  39.                     cloudStep1 = 0;
  40.             }

  41.             if (lastCloudStep2Time.TotalMilliseconds > GamePredefinitions.CloudStep2MaxTimeMs)
  42.             {
  43.                 lastCloudStep2Time = TimeSpan.Zero;

  44.                 cloudStep2++;
  45.                 if (cloudStep2 == GamePredefinitions.MaxCloudStep)
  46.                     cloudStep2 = 0;
  47.             }

  48.             if (lastCloudStep3Time.TotalMilliseconds > GamePredefinitions.CloudStep3MaxTimeMs)
  49.             {
  50.                 lastCloudStep3Time = TimeSpan.Zero;

  51.                 cloudStep3++;
  52.                 if (cloudStep3 == 800)
  53.                     cloudStep3 = 0;
  54.             }

  55.             var ballCenter = ConvertUnits.ToDisplayUnits(cannonBall.Body.WorldCenter);
  56.             var ballX = ballCenter.X;
  57.             var ballY = ballCenter.Y;

  58.             var cameraX = GamePredefinitions.CameraInitialPosition.X;
  59.             var cameraY = GamePredefinitions.CameraInitialPosition.Y;

  60.             if (gameStateMachine.CurrentSate == GameState.Playing)
  61.             {
  62.                 if (ballX < -scrollableViewport.Width / 6)
  63.                 {
  64.                     cameraX = -scrollableViewport.Width / 6;
  65.                 }
  66.                 else if (ballX > scrollableViewport.Width / 6)
  67.                 {
  68.                     cameraX = scrollableViewport.Width / 6;
  69.                 }
  70.                 else
  71.                 {
  72.                     cameraX = ballX;
  73.                 }

  74.                 if (ballY < -scrollableViewport.Height / 6)
  75.                 {
  76.                     cameraY = -scrollableViewport.Height / 6;
  77.                 }
  78.                 else if (ballY > scrollableViewport.Height / 6)
  79.                 {
  80.                     cameraY = scrollableViewport.Height / 6;
  81.                 }
  82.                 else
  83.                 {
  84.                     cameraY = ballY;
  85.                 }

  86.                 Camera.Position = new Vector2(cameraX, cameraY);
  87.             }
  88.             else if (gameStateMachine.CurrentSate == GameState.ShowingPirateShip)
  89.             {
  90.                 if (gameStateMachine.EllapsedTimeSinceLastChange().TotalMilliseconds > GamePredefinitions.TotalTimeShowingPirateShipMs)
  91.                 {
  92.                     gameStateMachine.ChangeState(GameState.ScrollingToStartPlaying);
  93.                 }
  94.             }
  95.             else if (gameStateMachine.CurrentSate == GameState.ScrollingToStartPlaying)
  96.             {
  97.                 var newCameraPosX = Camera.Position.X + (float)-10.0 * (gameTime.ElapsedGameTime.Milliseconds);
  98.                 Camera.Position = new Vector2(newCameraPosX, scrollableViewport.Height / 6);

  99.                 if (Camera.Zoom < 1.0f)
  100.                 {
  101.                     Camera.Zoom += gameTime.ElapsedGameTime.Milliseconds / GamePredefinitions.CameraZoomRate;
  102.                 }

  103.                 if (Camera.Position.X < -scrollableViewport.Width / 6)
  104.                 {
  105.                     Camera.Position = new Vector2(-scrollableViewport.Width / 6, Camera.Position.Y);
  106.                     gameStateMachine.ChangeState(GameState.Playing);
  107.                 }
  108.             }

  109.             if (cannonBall.HitCount == 0)
  110.             {
  111.                 if (smokeTracePositions.Count() == 0)
  112.                 {
  113.                     smokeTracePositions.Add(cannonBall.Body.Position);
  114.                 }
  115.                 else
  116.                 {
  117.                     var lastBallPosition = smokeTracePositions.Last();
  118.                     var currentBallPosition = cannonBall.Body.Position;
  119.                     var deltaX = Math.Abs((lastBallPosition.X - currentBallPosition.X));
  120.                     var deltaY = Math.Abs((lastBallPosition.Y - currentBallPosition.Y));

  121.                     if (deltaX * deltaX + deltaY * deltaY > GamePredefinitions.SmokeTraceSpace * GamePredefinitions.SmokeTraceSpace)
  122.                     {
  123.                         smokeTracePositions.Add(cannonBall.Body.Position);
  124.                     }
  125.                 }               
  126.             }
  127.         }
复制代码
在update函数中一个很重要是使用 GameTime参数。此参数告诉我们时间,因为游戏是最后一次更新花了多少时。正如你可以在上面看到,它使我们能够正确地计算cpu的速度波动的干扰。否则,你可能会看到比赛快或慢,取决于你手机在某一时刻的cpu的处理速度。

draw循环

是XNA框架绘制的重要模块。我们重写此方法来绘制我们的海盗游戏所需的所有帧。

这个循环处理了下面所说的一部分内容:

绘制背景纹理
绘制的蓝天,白云,大海和船
绘制了炮弹,大炮和烟雾的痕迹
绘制了现场的海盗和其他对象。
得分和最高分数。
 楼主| 发表于 2012-8-4 21:36:11 | 显示全部楼层
接上
  1. public override void Draw(GameTime gameTime)
  2.         {
  3.             ScreenManager.SpriteBatch.Begin(0, null, null, null, null, null, Camera.View);

  4.             if (gameStateMachine.CurrentSate != GameState.None)
  5.             {
  6.                 var skyRect = GamePredefinitions.SkyTextureRectangle;
  7.                 var seaRect = GamePredefinitions.SeaTextureRectangle;

  8.                 ScreenManager.SpriteBatch.Draw(skyTexture, skyRect, Color.White);
  9.                 ScreenManager.SpriteBatch.Draw(cloud1Texture, new Rectangle(skyRect.X + cloudStep1, skyRect.Y, skyRect.Width, skyRect.Height), Color.White);
  10.                 ScreenManager.SpriteBatch.Draw(cloud2Texture, new Rectangle(skyRect.X + cloudStep2 * 2, skyRect.Y, skyRect.Width, skyRect.Height), Color.White);
  11.                 ScreenManager.SpriteBatch.Draw(cloud3Texture, new Rectangle(skyRect.X + cloudStep3 * 3, skyRect.Y, skyRect.Width, skyRect.Height), Color.White);
  12.                 ScreenManager.SpriteBatch.Draw(seaTexture, seaRect, Color.White);
  13.                 ScreenManager.SpriteBatch.Draw(pirateShipTexture, GamePredefinitions.PirateShipTextureRectangle, Color.White);
  14.                 ScreenManager.SpriteBatch.Draw(royalShipTexture, GamePredefinitions.RoyalShipTextureRectangle, Color.White);
  15.             }

  16.             smokeTracePositions.ForEach(e =>
  17.                 ScreenManager.SpriteBatch.Draw(smokeTraceTexture, ConvertUnits.ToDisplayUnits(e) + GamePredefinitions.CannonCenter - new Vector2(10, 10),
  18.                     null, Color.White, 0f, Vector2.Zero, _scale, SpriteEffects.None, 0f));

  19.             foreach (var textureBody in textureBodies)
  20.             {
  21.                 ScreenManager.SpriteBatch.Draw(textureBody.DisplayTexture, ConvertUnits.ToDisplayUnits(textureBody.Body.Position),
  22.                                                null, Color.White, textureBody.Body.Rotation, textureBody.Origin, _scale, SpriteEffects.None, 0f);
  23.             }

  24.             ScreenManager.SpriteBatch.Draw(cannonTexture, GamePredefinitions.CannonCenter,
  25.                                 null, Color.White, cannonAngle, new Vector2(40f, 29f), 1f, SpriteEffects.None,
  26.                                 0f);

  27.             var hiScoreText = string.Format("hi score: {0}", scoreManager.GetHiScore());
  28.             var scoreText = string.Format("score: {0}", scoreManager.GetScore());

  29.             DrawScoreText(0, 0, hiScoreText);
  30.             DrawScoreText(0, 30, scoreText);

  31.             ScreenManager.SpriteBatch.End();
  32.             _border.Draw();
  33.             base.Draw(gameTime);
  34.         }
复制代码
首先,我们绘制的天空。然后我们绘制以水平不同的速度运动的云 ,然后我们画的大海,然后船。最后,我们得出的动态元素:炮弹,大炮,比分。

最后的思考

我希望你喜欢!如果你有话要说,请到卤面网(codewp7.com)问答区联系我,我会很高兴知道你在想什么。同时wp7交流QQ群172765887中,也能找到我的身影,感谢大家
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|3D数字艺术论坛 ( 沪ICP备14023054号 )

GMT+8, 2024-9-28 02:14

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表