第二章里我们提到了如何把2D的纹理用SpriteBatch显示。对于游戏而言,一个精灵(sprite,在游戏编程里指一个对象的原型,比如一个战士,一个怪物)不可能不做动作,就拿简单的走动来说,手脚一定会是要动的,那么如何真实得展现一个人物在屏幕上走动呢?在2D游戏编程里,我们通常就是把人物的动作拆成一帧帧图片,通过连续播放来欺骗人眼产生动画的效果,其实胶片电影也是这个原理。

  如下图4-1,是一个小兵的行走动画,可以看到是由12张图片组成,那么如何能连续循环播放呢

  图4-1

  一想到循环,我们就很容易想到for循环,如果我们把这12张图片的纹理放到一个Texture2D[] 数组里那么,通过for循环不就可以实现了么?

  for(int i=0;i

  {

  spriteBatch.Draw(SoldierTextures[i]);

  }

  当然,这个循环只能循环一遍,如果做到从头到尾不停的循环呢?

  前面我们提到XNA里的Update函数,它就是不停执行的一个函数,执行的时间间隔是一个固定值.我们就可以把这个函数当做是一个特殊的for循环.

  我们需要用上一个全局的计数器FrameCount.

  int FrameCount=0;

  void Update() //在update函数里改变计数器

  {

  FrameCount++;

  if(FrameCount>SoilderTextures.Length-1)//如果播完最后一帧

  {

  FrameCount=0; //就回到第一帧

  }

  }

  void Draw() //在Draw函数里绘制出纹理

  {

  spriteBatch.Draw(SoilderTextures[FrameCount]);

  }

  以上代码都是伪代码,便于大家理解,实际编程中的代码比这里要复杂点。

  1.在GameMainScreen类里写上构造函数:

  Texture2D[] soilderTextures;

  public GameMainScreen()

  {

  soilderTextures=new Texture2D[12];//初始化士兵纹理数组

  }

  2.把12张士兵跑动图片放到Content项目下的一个Enemy文件夹的子文件夹Run中,如果4-2:

  3.用for循环加载这12张图片纹理,如下:

  public override void LoadContent()

  {

  base.LoadContent();

  playerTexture = ScreenManager.Game.Content.Load("Player/1");

  for (int i = 0; i < 12;i++ )

  {

  soilderTextures[i] =ScreenManager.Game.Content.Load("Enemy/Run/"+(i+1));

  }

  }

  4.在Draw里绘制出当前帧:

  public override void Draw(GameTime gameTime)

  {

  ScreenManager.SpriteBatch.Begin();

  ScreenManager.SpriteBatch.Draw(soilderTextures[FrameCount],new Vector2(100,200), Color.White);

  ScreenManager.SpriteBatch.End();

  }

  

  图4-2

  5.在Update里更新当前帧的位置,依次向后播放:

  public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)

  {

  base.Update(gameTime, otherScreenHasFocus,coveredByOtherScreen);

  float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

  FrameCount++;

  if(FrameCount > soilderTextures.Length - 1)//如果播完最后一帧

  {

  FrameCount = 0; //就回到第一帧

  }

  }

  6.在模拟器最后运行效果如图4-3:

  我们执行上面写好的代码后,就在模拟器里发现人物就循环播放动画了,不过出现了新问题:士兵的跑动动作频率不够合理,像一个超人一样飞速得在奔跑。

  这是为什么呢?

  因为人物动画帧播放的时间间隔其实是要远大于update函数执行的时间间隔的,比如说人物动画是1秒播放一帧,而update函数是1/30秒(0.3333秒)播放一帧。那么我们如何处理这个问题呢?

  在回到上面的代码里,其实只要控制 FrameCount++执行的时间间隔就可以了。如何控制?那么我们需要用到另外一个计数器updateCount;

  updateCount++;

  if(updateCount>TimeSpan) //伪代码,TimeSpan为时间间隔量

  {

  FrameCount++;

  updateCount=0;

  }

  这样我们通过修改TimeSpan的值就能控制FrameCount++执行的时间间隔了。比如TimeSpan=30, 那么update函数要执行30次,FrameCount++才执行一次。也就说FrameCount++执行的时间间隔为 30*0.33333秒=1秒。

  经过合理调整,我们发现TimeSpan=2时,士兵的动作频率最协调,代码如下:

  int updateCount=0,timeSpan=2;

  public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)

  {

  base.Update(gameTime,otherScreenHasFocus, coveredByOtherScreen);

  float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

  updateCount++;

  if(updateCount > timeSpan) // timeSpan为时间间隔量

  {

  FrameCount++;

  updateCount = 0;

  }

  if(FrameCount > soilderTextures.Length - 1)//如果播完最后一帧

  {

  FrameCount = 0; //就回到第一帧

  }

  }

  写完以上代码,我们就能看到精灵动画能够自如的播放了。不过这个小兵精灵并不能移动,所以有些别扭。那么我们下面一节就要研究如何让精灵移动起来。

  4.2游戏精灵的矢量移动

  我们在前面第二章讲过可以指定得把一个2D纹理绘制在特定的坐标位置上。在WP7的XNA里,坐标系如下,原点的位置是左上角,X轴向右延伸,Y轴向下延伸。

  在XNA里,2维坐标用Vector2对象来表示:

  Vector2 position = new Vector2(X,Y);

  了解了XNA里2维坐标概念后,我们还需要知道一个概念就是运动矢量,什么叫运动矢量呢?比如我向右走,也就是笔直沿X轴增大的方向走,这样我行走的方向就确定了,但是我行走的时候可快可慢,这就涉及到速度的问题。运动矢量就包含了方向和速度两个概念。在2维坐标系里,运动矢量也用Vector2 对象来表示。我用20单位的速度向右走就可以表示为 new Vector2(20,0); 很容易理解此时在X轴方向上以20单位的速度在增大(向右运动),在Y轴上没有变化。

  那么随着时间流逝,我们就能得到运动中的精灵在当前时刻所在坐标位置,用矢量计算公式:

  Vector2 speed = new Vector2(20,0);

  Vector2 EndPosition = position + speed*elipsetime;

  计算出EndPosition后,我们就在Draw方法里

  spriteBatch.Draw(Texture2D,EndPosition);

  这样我们的精灵就能移动了,改变speed运动矢量,我们就能控制精灵的运动速度和方向。当然,要让精灵停下来也很简单,speed = new Vector2(0,0)就可以了。

  改进后的代码如下:

  int FrameCount= 0;

  Vector2 endPosition = new Vector2(0,200);

  public override void Draw(GameTime gameTime)

  {

  float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

  float totalTime = (float)gameTime.TotalGameTime.TotalSeconds;

  ScreenManager.SpriteBatch.Begin();

  ScreenManager.SpriteBatch.Draw(soilderTextures[FrameCount], endPosition,Color.White);

  ScreenManager.SpriteBatch.End();

  }

  int updateCount=0,timeSpan=2;

  Vector2 speed=new Vector2(100,0);

  public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)

  {

  base.Update(gameTime,otherScreenHasFocus, coveredByOtherScreen);

  float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

  updateCount++;

  if(updateCount > timeSpan) //TimeSpan为时间间隔量

  {

  FrameCount++;

  updateCount = 0;

  }

  if(FrameCount > soilderTextures.Length - 1)//如果播完最后一帧

  {

  FrameCount = 0; //就回到第一帧

  }

  endPosition += elapsedTime * speed;

  if(endPosition.X>800)

  {

  endPosition = new Vector2(0,200);

  }

  }

  按F5运行模拟器后,我们会看到一个不停奔跑的士兵。

  4.3制作可控的游戏精灵

  在手机游戏里,主角一般都是方向可控的,如下图4-4是一个带十字方向键的iphone游戏界面:

  

  图4-4

  结合我们第3章讲的WP7的触控操作,我们也能实现十字方向键控制的游戏精灵。

  1. 首先找到一个十字键图片,把它绘制到手机左下角,如图4-5:

  

  图4-5

  2. 设定4个方向点的矩形范围。

  Rectangle topRect = newRectangle

  {

  Location = newPoint { X = 40, Y = 380 },

  Width = 30,

  Height = 30,

  };

  Rectangle bottomRect = new Rectangle

  {

  Location = new Point { X = 40, Y = 460 },

  Width = 30,

  Height = 30,

  };

  Rectangle leftRect = new Rectangle

  {

  Location = new Point { X = 2, Y = 410 },

  Width = 30,

  Height = 30,

  };

  Rectangle rightRect = new Rectangle

  {

  Location = new Point { X = 80, Y = 410 },

  Width = 30,

  Height = 30,

  };

  2. 重写HandleInput函数,接受触控操作:

  public override void HandleInput(InputHelperinput)

  {

  TouchCollection touchState = TouchPanel.GetState();

  foreach(TouchLocation tl in touchState)

  {

  if(topRect.Contains(new Point{ X = (int)tl.Position.X, Y = (int)tl.Position.Y }) && tl.State == TouchLocationState.Pressed)

  {

  speed = new Vector2(0,-100);

  }

  if(bottomRect.Contains(new Point { X = (int)tl.Position.X,Y = (int)tl.Position.Y }) &&tl.State==TouchLocationState.Pressed)

  {

  speed = new Vector2(0,100);

  }

  if(leftRect.Contains(new Point{ X = (int)tl.Position.X, Y = (int)tl.Position.Y }) && tl.State == TouchLocationState.Pressed)

  {

  speed = new Vector2(-100,0);

  }

  if(rightRect.Contains(new Point { X = (int)tl.Position.X,Y = (int)tl.Position.Y }) && tl.State== TouchLocationState.Pressed)

  {

  speed = new Vector2(100,0);

  }

  if(tl.State==TouchLocationState.Released)

  {

  speed = new Vector2(0,0);

  }

  }

  }

  3. 写好以上代码后,按F5调试,在游戏主界面,我们就看到如图4-6的效果:

  

  图4-6

  当然这只是个简单的DEMO,士兵后退,向上,向下的动画都不对,不过原理和士兵前进的动画一样。更复杂的动画,我们会在下一章来讲解。

本文转自 wws5201985 51CTO博客,原文链接:http://blog.51cto.com/wws5201985/736040,如需转载请自行联系原作者

在Windows phone上用XNA写2D游戏第五季相关推荐

  1. 如何在WP7上用XNA写2D游戏(一)

    游戏背景: 时间是欧洲中世纪,一位英雄坚守着他国家最后的城堡.一旦敌人攻破这个城堡,他的任务就失败了.这位英雄有着百步穿杨的本领,还有强大的魔法技能.虽然敌人发起了一波波潮水般的攻击,但他都顽强的击退 ...

  2. 手把手教用XNA开发winphone7游戏(五)大结局

    Alien Game逻辑 在这最有一个部分你将创建game-specific logic和 helper方法和类.胜利就在眼前,你的第一个winphone7程序就要出现了,加油加油!!(感谢http: ...

  3. 1000行代码写小游戏(五)

    主角的一些属性更新和展示都已经做完了,下面主要做玩法方面的,添加小怪.矿和系统,并增加点击逻辑: -- 增加矿,敌人和系统 function MineSecretDialog:addMineAndEn ...

  4. 【使用Unity开发Windows Phone上的2D游戏】(1)千里之行始于足下

    写在前面的 其实这个名字起得不太欠当,Unity本身是很强大的工具,可以部署到很多个平台,而不仅仅是可以开发Windows Phone上的游戏. 只不过本人是Windows Phone 应用开发出身, ...

  5. 2D游戏开发 - SkyGameEngine2d 创建一个游戏项目

    2D游戏开发 - SkyGameEngine2d 创建一个游戏项目 一.前期准备 二.获取引擎工具 2.1 下载工具集 2.2 解压并且打开工具 三.使用工具 3.1 工具界面介绍 3.2 设置引擎路 ...

  6. pc手写识别_如何在Windows 10 PC上改善手写识别

    pc手写识别 Windows 10 lets you use handwriting input in any application, and many applications include f ...

  7. 在Windows平台上安装Node.js及NPM模块管理

    1. 下载Node.js官方Windows版程序:http://nodejs.org/#download     从0.6.1开始,Node.js在Windows平台上提供了两种安装方式,一是.MSI ...

  8. Redis进阶实践之三如何在Windows系统上安装安装Redis

    一.Redis的简介 Redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset( ...

  9. shell实现批量在多台windows服务器上执行同一命令并获取返回结果

    1.    需求 在对windows服务器的运维当中,如果要查看当前的主机名.资源使用.软件安装情况等,大家是怎么操作呢,是登进去鼠标挨着点击查看,还是通过命令呢?貌似命令的方法比较专业一点.但是,如 ...

最新文章

  1. Redis主从握手流程,你真的了解了吗?
  2. 【数据挖掘】数据挖掘简介 ( 6 个常用功能 | 数据挖掘结果判断 | 数据挖掘学习框架 | 数据挖掘分类 )
  3. java合并单元格同时导出excel
  4. linux nodejs 采集器,Linux记录-jstack采集namenode gc信息
  5. Transformer 杀疯了,图像去雨、人脸幻构、风格迁移、语义分割等通通上分
  6. java设计模式在线视频_Java设计模式之单例模式视频课程
  7. 机器学习代码实战——K折交叉验证(K Fold Cross Validation)
  8. c++ string取子串_LeetCode第三题 “无重复字符的最长子串” 从低效率到高效率
  9. opencv 鼠标点击处视频的坐标和rgbw值
  10. jmeter录制脚本及操作数据库
  11. 华为薪资等级结构表_华为内部考核体系
  12. 阿里云商标驳回复审申请收费价格、结果时间及常见问题解答
  13. 简述网卡的作用和工作原理_简述网卡的主要功能
  14. 办理加拿大普通学生签证 20180717
  15. ARM ELF 镜像结构
  16. 中冠百年|怎样才能提高个人理财的执行力
  17. webstorm-主题和配色
  18. Allegro PCB Design GXL (legacy) 将brd文件另存为低版本文件
  19. 电路设计_光耦的主要参数
  20. Intel 正式宣布8代酷睿处理器:14nm、性能提升15%

热门文章

  1. 贪吃蛇代码java_贪吃蛇 java代码
  2. Linux 部署dogecoin采集结点
  3. 【EasyUse】关于键盘加速键的几点思考
  4. PrimeNG之TreeTable
  5. 【Cisco Packet Tracer(思科)交换机和路由器配置实例】
  6. 离散数学-1 命题逻辑的基本概念
  7. 年轻人不用太过于努力
  8. show()方法和hide()方法
  9. 隐秘而伟大——纪念图灵诞辰110周年
  10. 斯坦福CS231N深度学习与计算机视觉