天天看點

遊戲人生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,如需轉載請自行聯系原作者

繼續閱讀