游戏人生Silverlight:星际竞技场

使用 Silverlight 2.0(c#, Farseer Physics Engine) 开发一
首页 新闻资讯 行业资讯 游戏人生Silverlight:星际竞技场

介绍

使用 Silverlight 2.0(c#, Farseer Physics Engine) 开发一个射击游戏:星际竞技场

玩法

W 或者 ↑ = 前进;S 或者 ↓ = 后退:A 或者 ← = 左转;D 或者 → = 右转;J 或者 Ctrl = 开火

思路

1、使用一个开源的 Silverlight 物理引擎:Farseer Physics Engine

2、将 Farseer Physics Engine 中的物理运算器 PhysicsSimulator 放到一个全局变量中,对 Body 和 Geom 做即时运算,

3、写个 IPhysicsControl 接口,用于描述物理对象的各个属性,需要运动和碰撞的对象,要实现该接口抽象出来的各个属性

4、写个抽象类(Sprite),在其内封装好物理引擎。各种类型的物理对象的模拟器,都需要重写该抽象类的两个方法GetForce()和GetTorque()即可,其分别要返回对象在当前时刻所受到的牵引力和力矩

5、写个 IFire 接口,所有可开火的对象都要实现该接口

6、写个控件 PhysicsBox,用于包装 IPhysicsControl,从而将模拟器计算出的运动和碰撞结果呈现到界面上

关键代码Sprite.cs(Sprite 模拟器的基类)

复制

using System;  using System.Net;  using System.Windows;  using System.Windows.Controls;  using System.Windows.Documents;  using System.Windows.Ink;  using System.Windows.Input;  using System.Windows.Media;  using System.Windows.Media.Animation;  using System.Windows.Shapes;   using FarseerGames.FarseerPhysics;  using FarseerGames.FarseerPhysics.Mathematics;  using FarseerGames.FarseerPhysics.Dynamics;  using FarseerGames.FarseerPhysics.Collisions;   namespace YYArena.Core  {      /**//// <summary>     /// Sprite 基类      /// </summary>     public abstract class Sprite      {          private PhysicsSimulator _physicsSimulator;           protected PhysicsBox playerBox;          protected Geom playerGeometry;           /**//// <summary>         /// 构造函数          /// </summary>         /// <param name="physicsSimulator">PhysicsSimulator</param>         /// <param name="physicsControl">IPhysicsControl</param>         /// <param name="position">初始位置</param>         /// <param name="angle">初始转角</param>         /// <param name="originalVelocity">初始速度</param>         public Sprite(PhysicsSimulator physicsSimulator,              IPhysicsControl physicsControl, Vector2 position, float angle, float originalVelocity)          {              _physicsSimulator = physicsSimulator;               playerBox = new PhysicsBox(physicsControl);              playerBox.Body.Position = position;              playerBox.Body.Rotation = (float)Helper.Angle2Radian(angle);              playerBox.Body.LinearVelocity = Helper.Convert2Vector(originalVelocity, (float)Helper.Angle2Radian(angle));               // Body 和 Geom 的 Tag 保存为 Sprite,方便引用              playerBox.Body.Tag = this;              playerBox.Geom.Tag = this;               playerBox.Update();          }           /**//// <summary>         /// 即时计算力和力矩          /// </summary>         void CompositionTarget_Rendering(object sender, EventArgs e)          {              if (Enabled)              {                  var force = GetForce();                  var torque = GetTorque();                   playerBox.Body.ApplyForce(force);                  playerBox.Body.ApplyTorque(torque);                   playerBox.Update();              }          }           /**//// <summary>         /// 返回 Sprite 当前受的力          /// </summary>         protected abstract Vector2 GetForce();          /**//// <summary>         /// 返回 Sprite 当前受的力矩          /// </summary>         protected abstract float GetTorque();           public PhysicsBox PhysicsBox          {              get { return playerBox; }          }           private bool _enabled = false;          /**//// <summary>         /// 是否启用此 Sprite          /// </summary>         public bool Enabled          {              get { return _enabled; }              set              {                   _enabled = value;                   if (value)                  {                      CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);                       _physicsSimulator.Add(playerBox.Body);                      _physicsSimulator.Add(playerBox.Geom);                  }                  else                  {                      CompositionTarget.Rendering -= new EventHandler(CompositionTarget_Rendering);                       GC.SuppressFinalize(this);                      _physicsSimulator.Remove(playerBox.Body);                      _physicsSimulator.Remove(playerBox.Geom);                  }              }          }      }  }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

  • 38.

  • 39.

  • 40.

  • 41.

  • 42.

  • 43.

  • 44.

  • 45.

  • 46.

  • 47.

  • 48.

  • 49.

  • 50.

  • 51.

  • 52.

  • 53.

  • 54.

  • 55.

  • 56.

  • 57.

  • 58.

  • 59.

  • 60.

  • 61.

  • 62.

  • 63.

  • 64.

  • 65.

  • 66.

  • 67.

  • 68.

  • 69.

  • 70.

  • 71.

  • 72.

  • 73.

  • 74.

  • 75.

  • 76.

  • 77.

  • 78.

  • 79.

  • 80.

  • 81.

  • 82.

  • 83.

  • 84.

  • 85.

  • 86.

  • 87.

  • 88.

  • 89.

  • 90.

  • 91.

  • 92.

  • 93.

  • 94.

  • 95.

  • 96.

  • 97.

  • 98.

  • 99.

  • 100.

  • 101.

  • 102.

  • 103.

  • 104.

  • 105.

  • 106.

  • 107.

  • 108.

  • 109.

  • 110.

  • 111.

  • 112.

  • 113.

  • 114.

#p#

PlayerSprite.cs(玩家 Sprite 模拟器)

复制

using System;  using System.Net;  using System.Windows;  using System.Windows.Controls;  using System.Windows.Documents;  using System.Windows.Ink;  using System.Windows.Input;  using System.Windows.Media;  using System.Windows.Media.Animation;  using System.Windows.Shapes;   using System.Collections.Generic;  using FarseerGames.FarseerPhysics.Mathematics;  using FarseerGames.FarseerPhysics;  using FarseerGames.FarseerPhysics.Collisions;   namespace YYArena.Core  {      /**//// <summary>     /// 玩家 Sprite      /// </summary>     public class PlayerSprite : Sprite, IFire      {          private List<Key> _upKeys { get; set; }          private List<Key> _downKeys { get; set; }          private List<Key> _leftKeys { get; set; }          private List<Key> _rightKeys { get; set; }          private List<Key> _fireKeys { get; set; }           private KeyboardHandler _keyHandler;          private IPhysicsControl _physicsControl;           /**//// <summary>         /// 构造函数          /// </summary>         /// <param name="physicsSimulator">PhysicsSimulator</param>         /// <param name="physicsControl">IPhysicsControl</param>         /// <param name="position">初始位置</param>         /// <param name="angle">初始转角</param>         /// <param name="originalVelocity">初始速度</param>         /// <param name="keyboardHandler">KeyboardHandler</param>         /// <param name="up">操作玩家向前移动的按键集合</param>         /// <param name="down">操作玩家向后移动的按键集合</param>         /// <param name="left">操作玩家向左转动的按键集合</param>         /// <param name="right">操作玩家向右转动的按键集合</param>         /// <param name="fire">操作玩家开火的按键集合</param>         public PlayerSprite(PhysicsSimulator physicsSimulator,              IPhysicsControl physicsControl, Vector2 position, float angle, float originalVelocity,              KeyboardHandler keyboardHandler,              List<Key> up, List<Key> down, List<Key> left, List<Key> right, List<Key> fire)              : base(physicsSimulator, physicsControl, position, angle, originalVelocity)          {              PrevFireDateTime = DateTime.MinValue;              MinFireInterval = 500d;               _upKeys = up;              _downKeys = down;              _leftKeys = left;              _rightKeys = right;              _fireKeys = fire;               _keyHandler = keyboardHandler;              _physicsControl = physicsControl;               CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);          }           void CompositionTarget_Rendering(object sender, EventArgs e)          {              if (Enabled)              {                  // 如果按了开火键,是否可开火                  if (_keyHandler.AnyKeyPressed(_fireKeys) && (DateTime.Now - PrevFireDateTime).TotalMilliseconds > MinFireInterval)                  {                      PrevFireDateTime = DateTime.Now;                      if (Fire != null)                          Fire(this, EventArgs.Empty);                  }              }          }           public DateTime PrevFireDateTime { get; set; }           public double MinFireInterval { get; set; }           public event EventHandler<EventArgs> Fire;           protected override Vector2 GetForce()          {              Vector2 force = Vector2.Zero;               if (_keyHandler.AnyKeyPressed(_upKeys))                  force += Helper.Convert2Vector(_physicsControl.ForceAmount, playerBox.Body.Rotation);              if (_keyHandler.AnyKeyPressed(_downKeys))                  force += Helper.Convert2Vector(_physicsControl.ForceAmount, playerBox.Body.Rotation - Helper.Angle2Radian(180));               // 最大线性速度限制              if (playerBox.Body.LinearVelocity.Length() > _physicsControl.MaxLinearVelocity)                  force = Vector2.Zero;               return force;          }           protected override float GetTorque()          {              float torque = 0;               if (_keyHandler.AnyKeyPressed(_leftKeys))                  torque -= _physicsControl.TorqueAmount;              if (_keyHandler.AnyKeyPressed(_rightKeys))                  torque += _physicsControl.TorqueAmount;               // 用于修正 RotationalDragCoefficient (在没有任何 Torque 的情况下,如果转速小于 1.3 则设其为 0)              // 如果不做此修正的话,转速小于 1.3 后还会转好长时间              if (!_keyHandler.AnyKeyPressed(_leftKeys) && !_keyHandler.AnyKeyPressed(_rightKeys) && Math.Abs(playerBox.Body.AngularVelocity) < 1.3)                  playerBox.Body.AngularVelocity = 0;               // 最大转速限制              if (Math.Abs(playerBox.Body.AngularVelocity) > _physicsControl.MaxAngularVelocity)                  torque = 0;               return torque;          }      }  }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

  • 38.

  • 39.

  • 40.

  • 41.

  • 42.

  • 43.

  • 44.

  • 45.

  • 46.

  • 47.

  • 48.

  • 49.

  • 50.

  • 51.

  • 52.

  • 53.

  • 54.

  • 55.

  • 56.

  • 57.

  • 58.

  • 59.

  • 60.

  • 61.

  • 62.

  • 63.

  • 64.

  • 65.

  • 66.

  • 67.

  • 68.

  • 69.

  • 70.

  • 71.

  • 72.

  • 73.

  • 74.

  • 75.

  • 76.

  • 77.

  • 78.

  • 79.

  • 80.

  • 81.

  • 82.

  • 83.

  • 84.

  • 85.

  • 86.

  • 87.

  • 88.

  • 89.

  • 90.

  • 91.

  • 92.

  • 93.

  • 94.

  • 95.

  • 96.

  • 97.

  • 98.

  • 99.

  • 100.

  • 101.

  • 102.

  • 103.

  • 104.

  • 105.

  • 106.

  • 107.

  • 108.

  • 109.

  • 110.

  • 111.

  • 112.

  • 113.

  • 114.

  • 115.

  • 116.

  • 117.

  • 118.

  • 119.

  • 120.

  • 121.

  • 122.

  • 123.

  • 124.

  • 125.

#p#

AISprite.cs(敌军 Sprite 模拟器)

复制

using System;  using System.Net;  using System.Windows;  using System.Windows.Controls;  using System.Windows.Documents;  using System.Windows.Ink;  using System.Windows.Input;  using System.Windows.Media;  using System.Windows.Media.Animation;  using System.Windows.Shapes;   using System.Collections.Generic;  using FarseerGames.FarseerPhysics.Mathematics;   using FarseerGames.FarseerPhysics;  using FarseerGames.FarseerPhysics.Collisions;  using FarseerGames.FarseerPhysics.Dynamics;   namespace YYArena.Core  {      /**//// <summary>     /// 敌军 Sprite      /// </summary>     public class AISprite : Sprite, IFire      {          private Sprite _attackTarget;          private int _aiLevel;          private IPhysicsControl _physicsControl;           /**//// <summary>         /// 构造函数          /// </summary>         /// <param name="physicsSimulator">PhysicsSimulator</param>         /// <param name="physicsControl">IPhysicsControl</param>         /// <param name="position">初始位置</param>         /// <param name="angle">初始转角</param>         /// <param name="originalVelocity">初始速度</param>         /// <param name="attackTarget">攻击目标</param>         /// <param name="aiLevel">ai等级</param>         public AISprite(PhysicsSimulator physicsSimulator,              IPhysicsControl physicsControl, Vector2 position, float angle, float originalVelocity, Sprite attackTarget, int aiLevel)              : base(physicsSimulator, physicsControl, position, angle, originalVelocity)          {              // 上次开火时间              PrevFireDateTime = DateTime.Now.AddSeconds(3);              // 最小开火间隔              MinFireInterval = 3000d;               _attackTarget = attackTarget;              _aiLevel = aiLevel;              _physicsControl = physicsControl;               InitAI();               CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);          }           private void InitAI()          {              // 根据 ai 等级设置最小开火间隔              double fireCoefficient = 1 + 30 / _aiLevel;              MinFireInterval = Helper.GenerateRandom((int)MinFireInterval, (int)(fireCoefficient * MinFireInterval));          }           void CompositionTarget_Rendering(object sender, EventArgs e)          {              if (Enabled && AttackTarget.Enabled)              {                  // 是否开火                  if ((DateTime.Now - PrevFireDateTime).TotalMilliseconds > MinFireInterval)                  {                      PrevFireDateTime = DateTime.Now;                       if (Fire != null)                          Fire(this, EventArgs.Empty);                  }              }          }           public DateTime PrevFireDateTime { get; set; }           public double MinFireInterval { get; set; }           public event EventHandler<EventArgs> Fire;            public Sprite AttackTarget          {              get { return _attackTarget; }              set { _attackTarget = value; }          }           protected override Vector2 GetForce()          {              Vector2 force = Vector2.Zero;               if (!_attackTarget.Enabled)                  return force;               force += Helper.Convert2Vector(_physicsControl.ForceAmount, playerBox.Body.Rotation);               // 根据 ai 等级做最大线性速度限制              if (playerBox.Body.LinearVelocity.Length() > _physicsControl.MaxLinearVelocity * Helper.GenerateRandom(50, 200) / 1000)                  force = Vector2.Zero;               return force;          }           protected override float GetTorque()          {              float torque = 0f;               if (!_attackTarget.Enabled)                  return torque;               // 按某个方向旋转,原则是以最小的旋转角度对准目标              Vector2 relativePosition = _attackTarget.PhysicsBox.Body.Position - playerBox.Body.Position;              double targetRotation = Helper.Convert2Rotation(relativePosition);              double currentRotation = playerBox.Body.Rotation;              double relativeAngle = Helper.Radian2Angle(targetRotation - currentRotation);              if (relativeAngle < 0)                  relativeAngle += 360;               if (relativeAngle > 1)              {                  if (relativeAngle < 180 && relativeAngle > 0)                      torque += _physicsControl.TorqueAmount;                  else if (relativeAngle > 180 && relativeAngle < 360)                      torque -= _physicsControl.TorqueAmount;              }              else              {                  playerBox.Body.AngularVelocity = 0;              }                // 最大转速限制              if (Math.Abs(playerBox.Body.AngularVelocity) > _physicsControl.MaxAngularVelocity)                  torque = 0;                           return torque;          }      }  }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

  • 38.

  • 39.

  • 40.

  • 41.

  • 42.

  • 43.

  • 44.

  • 45.

  • 46.

  • 47.

  • 48.

  • 49.

  • 50.

  • 51.

  • 52.

  • 53.

  • 54.

  • 55.

  • 56.

  • 57.

  • 58.

  • 59.

  • 60.

  • 61.

  • 62.

  • 63.

  • 64.

  • 65.

  • 66.

  • 67.

  • 68.

  • 69.

  • 70.

  • 71.

  • 72.

  • 73.

  • 74.

  • 75.

  • 76.

  • 77.

  • 78.

  • 79.

  • 80.

  • 81.

  • 82.

  • 83.

  • 84.

  • 85.

  • 86.

  • 87.

  • 88.

  • 89.

  • 90.

  • 91.

  • 92.

  • 93.

  • 94.

  • 95.

  • 96.

  • 97.

  • 98.

  • 99.

  • 100.

  • 101.

  • 102.

  • 103.

  • 104.

  • 105.

  • 106.

  • 107.

  • 108.

  • 109.

  • 110.

  • 111.

  • 112.

  • 113.

  • 114.

  • 115.

  • 116.

  • 117.

  • 118.

  • 119.

  • 120.

  • 121.

  • 122.

  • 123.

  • 124.

  • 125.

  • 126.

  • 127.

  • 128.

  • 129.

  • 130.

  • 131.

  • 132.

  • 133.

  • 134.

  • 135.

  • 136.

  • 137.

  • 138.

  • 139.

  • 140.

  • 141.

  • 142.

  • 143.

  • 144.

[源码下载]

原文链接:http://www.cnblogs.com/webabcd/archive/2009/06/22/1508042.html

21    2012-05-31 14:20:14    Silverlight