游戏人生Silverlight:星际竞技场

开发 后端
使用 Silverlight 2.0(c#, Farseer Physics Engine) 开发一个射击游戏:星际竞技场。思路:使用一个开源的 Silverlight 物理引擎:Farseer Physics Engine.....

介绍

使用 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

责任编辑:张伟 来源: webabcd的博客
相关推荐

2024-09-29 14:33:30

数据飞轮数据中台数字化转型

2013-09-12 11:17:02

2012-06-05 14:42:57

Silverlight

2024-05-31 14:23:15

2025-02-18 15:09:07

2022-04-12 18:35:03

元宇宙

2014-10-31 15:43:02

华为智慧

2013-03-22 14:08:14

智能手表IT巨头竞技场

2025-02-17 12:24:43

2024-11-21 12:09:26

2024-06-20 14:04:17

2024-04-22 08:40:00

LLM模型开源

2024-05-20 15:25:47

2025-02-28 09:00:00

2025-03-05 09:32:00

2024-07-24 12:40:44

2024-03-08 13:02:56

Claude 3GPT-4Opus

2024-12-31 12:35:46

2024-10-29 14:25:00

模型训练

2024-11-19 14:40:00

AI技术
点赞
收藏

51CTO技术栈公众号