天天看点

游戏人生Silverlight(5) - 星际竞技场[Silverlight 2.0(c#, Farseer Physics Engine)]

<a href="http://down.51cto.com/data/101271" target="_blank">[源码下载]</a>

游戏人生Silverlight(5) - 星际竞技场[Silverlight 2.0(c#, Farseer Physics Engine)]

介绍

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

玩法

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

在线DEMO

思路

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

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

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

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

5、写个控件 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 

        /// &lt;summary&gt; 

        /// Sprite 基类 

        /// &lt;/summary&gt; 

        public abstract class Sprite 

        { 

                private PhysicsSimulator _physicsSimulator; 

                protected PhysicsBox playerBox; 

                protected Geom playerGeometry; 

                /// &lt;summary&gt; 

                /// 构造函数 

                /// &lt;/summary&gt; 

                /// &lt;param name="physicsSimulator"&gt;PhysicsSimulator&lt;/param&gt; 

                /// &lt;param name="physicsControl"&gt;IPhysicsControl&lt;/param&gt; 

                /// &lt;param name="position"&gt;初始位置&lt;/param&gt; 

                /// &lt;param name="angle"&gt;初始转角&lt;/param&gt; 

                /// &lt;param name="originalVelocity"&gt;初始速度&lt;/param&gt; 

                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(); 

                } 

                /// 即时计算力和力矩 

                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(); 

                        } 

                /// 返回 Sprite 当前受的力 

                protected abstract Vector2 GetForce(); 

                /// 返回 Sprite 当前受的力矩 

                protected abstract float GetTorque(); 

                public PhysicsBox PhysicsBox 

                        get { return playerBox; } 

                private bool _enabled = false; 

                /// 是否启用此 Sprite 

                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); 

        } 

}

PlayerSprite.cs(玩家 Sprite 模拟器)

using System.Collections.Generic; 

        /// 玩家 Sprite 

        public class PlayerSprite : Sprite, IFire 

                private List&lt;Key&gt; _upKeys { get; set; } 

                private List&lt;Key&gt; _downKeys { get; set; } 

                private List&lt;Key&gt; _leftKeys { get; set; } 

                private List&lt;Key&gt; _rightKeys { get; set; } 

                private List&lt;Key&gt; _fireKeys { get; set; } 

                private KeyboardHandler _keyHandler; 

                private IPhysicsControl _physicsControl; 

                /// &lt;param name="keyboardHandler"&gt;KeyboardHandler&lt;/param&gt; 

                /// &lt;param name="up"&gt;操作玩家向前移动的按键集合&lt;/param&gt; 

                /// &lt;param name="down"&gt;操作玩家向后移动的按键集合&lt;/param&gt; 

                /// &lt;param name="left"&gt;操作玩家向左转动的按键集合&lt;/param&gt; 

                /// &lt;param name="right"&gt;操作玩家向右转动的按键集合&lt;/param&gt; 

                /// &lt;param name="fire"&gt;操作玩家开火的按键集合&lt;/param&gt; 

                public PlayerSprite(PhysicsSimulator physicsSimulator, 

                        IPhysicsControl physicsControl, Vector2 position, float angle, float originalVelocity, 

                        KeyboardHandler keyboardHandler, 

                        List&lt;Key&gt; up, List&lt;Key&gt; down, List&lt;Key&gt; left, List&lt;Key&gt; right, List&lt;Key&gt; 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); 

                                // 如果按了开火键,是否可开火 

                                if (_keyHandler.AnyKeyPressed(_fireKeys) &amp;&amp; (DateTime.Now - PrevFireDateTime).TotalMilliseconds &gt; MinFireInterval) 

                                        PrevFireDateTime = DateTime.Now; 

                                        if (Fire != null) 

                                                Fire(this, EventArgs.Empty); 

                public DateTime PrevFireDateTime { get; set; } 

                public double MinFireInterval { get; set; } 

                public event EventHandler&lt;EventArgs&gt; 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() &gt; _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) &amp;&amp; !_keyHandler.AnyKeyPressed(_rightKeys) &amp;&amp; Math.Abs(playerBox.Body.AngularVelocity) &lt; 1.3) 

                                playerBox.Body.AngularVelocity = 0; 

                        // 最大转速限制 

                        if (Math.Abs(playerBox.Body.AngularVelocity) &gt; _physicsControl.MaxAngularVelocity) 

                                torque = 0; 

                        return torque; 

AISprite.cs(敌军 Sprite 模拟器)

        /// 敌军 Sprite 

        public class AISprite : Sprite, IFire 

                private Sprite _attackTarget; 

                private int _aiLevel; 

                /// &lt;param name="attackTarget"&gt;攻击目标&lt;/param&gt; 

                /// &lt;param name="aiLevel"&gt;ai等级&lt;/param&gt; 

                public AISprite(PhysicsSimulator physicsSimulator, 

                        IPhysicsControl physicsControl, Vector2 position, float angle, float originalVelocity, Sprite attackTarget, int aiLevel) 

                        // 上次开火时间 

                        PrevFireDateTime = DateTime.Now.AddSeconds(3); 

                        // 最小开火间隔 

                        MinFireInterval = 3000d; 

                        _attackTarget = attackTarget; 

                        _aiLevel = aiLevel; 

                        InitAI(); 

                private void InitAI() 

                        // 根据 ai 等级设置最小开火间隔 

                        double fireCoefficient = 1 + 30 / _aiLevel; 

                        MinFireInterval = Helper.GenerateRandom((int)MinFireInterval, (int)(fireCoefficient * MinFireInterval)); 

                        if (Enabled &amp;&amp; AttackTarget.Enabled) 

                                // 是否开火 

                                if ((DateTime.Now - PrevFireDateTime).TotalMilliseconds &gt; MinFireInterval) 

                public Sprite AttackTarget 

                        get { return _attackTarget; } 

                        set { _attackTarget = value; } 

                        if (!_attackTarget.Enabled) 

                                return force; 

                        force += Helper.Convert2Vector(_physicsControl.ForceAmount, playerBox.Body.Rotation); 

                        // 根据 ai 等级做最大线性速度限制 

                        if (playerBox.Body.LinearVelocity.Length() &gt; _physicsControl.MaxLinearVelocity * Helper.GenerateRandom(50, 200) / 1000) 

                        float torque = 0f; 

                                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 &lt; 0) 

                                relativeAngle += 360; 

                        if (relativeAngle &gt; 1) 

                                if (relativeAngle &lt; 180 &amp;&amp; relativeAngle &gt; 0) 

                                        torque += _physicsControl.TorqueAmount; 

                                else if (relativeAngle &gt; 180 &amp;&amp; relativeAngle &lt; 360) 

                                        torque -= _physicsControl.TorqueAmount; 

                        else 

OK

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

继续阅读