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


    [索引页]
    [源码下载]


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



    作者:webabcd
    物理引擎:Farseer Physics Engine


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


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


    在线DEMO
    Get Microsoft Silverlight


    思路
    1、使用一个开源的 Silverlight 物理引擎:Farseer Physics Engine
    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
    {
        
    /// <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);
                    }

                }

            }

        }

    }


    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 getset; }
            
    private List<Key> _downKeys getset; }
            
    private List<Key> _leftKeys getset; }
            
    private List<Key> _rightKeys getset; }
            
    private List<Key> _fireKeys getset; }

            
    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 getset; }

            
    public double MinFireInterval getset; }

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

        }

    }


    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 getset; }

            
    public double MinFireInterval getset; }

            
    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(50200/ 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;
            }

        }

    }



    OK
    [源码下载]

  • 相关阅读:
    20200601:百万级int数据量的一个array求和。
    20200602:千万级数据量的list找一个数据。
    20200531:假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来?
    20200530:主从数据库不一致如何解决?
    [USACO06DEC]Milk Patterns G
    [HAOI2016]找相同字符
    [AHOI2013]差异
    [SCOI2012]喵星球上的点名
    [APIO2014]回文串
    [TJOI2015]弦论
  • 原文地址:https://www.cnblogs.com/webabcd/p/1508042.html
Copyright © 2020-2023  润新知