代码改变世界

SilverXna初体验:通用鼠标输入设备

2011-12-12 22:15  独孤残云  阅读(1519)  评论(0编辑  收藏  举报

游戏中每一个逻辑实体都是一个小型的状态机,它们在游戏主循环的驱动之下不停的变换自身状态,游戏世界大千万物盛衰轮回由此产生。

习惯了Xna架构的程序员大都习惯这样定义一个生命体:

Class Being

{

  public Being(){}            // 构造函数

  public bool Init(){}        // 初始化

  public void Update(){}   // 逻辑驱动

  public void Draw(){}      // 渲染驱动

  public void Dispose(){}  // 释放对象

}

我们在Update中单独处理有关该生命体针对外部输入设备的响应动作,或者由update分离出一个

bool HandleInput(){}

方法来处理所有的输入设备响应。这样一来生命体之间则彼此独立,互不侵犯。

Silverlight中没有为我们提供必要的输入设备封装,而如果依赖于Sliverlight控件本身的应用特性,则我们将不得不把整个游戏世界绝大部分输入处理耦合到Sliverlight根容器中。——这显然不符合我们的要求。

在Xna中,我们可以很方便的使用

            MouseState mousestate = Mouse.GetState();
            KeyboardState keystate = Keyboard.GetState();

来获取鼠标和键盘设备状态。而封装相应的GUI时,则必须在此基础上人为的构建诸如控件的单击事件、拖动事件等等。——这个过程其实很繁琐。

而相对的,收集控件各个事件反推输入设备状态,却是一个相对容易的过程。

本节,我们就以这种思路构建一个通用于Xna状态机与Sliverlight事件响应二重机制的鼠标输入设备。

思路其实很简单。首先,声明通用枚举:

    // 鼠标设备动作
    public enum MouseAction
    {
        None,

        MouseLeftButtonDown,
        MouseLeftButtonUp,
        MouseRightButtonDown,
        MouseRightButtonUp,

        LostMouseCapture,
        MouseEnter,
        MouseLeave,
        MouseMove,

        MouseWheel
    }
    // 鼠标键状态
    public enum ButtonState
    {
        Released = 0,
        Pressed = 1
    }
    // 鼠标设备委托定义
    public delegate void MouseButtonHandler(MouseAction act, MouseButtonEventArgs e);
    public delegate void MouseCommonHandler(MouseAction act, MouseEventArgs e);
    public delegate void MouseWheelHandler(MouseAction act, MouseWheelEventArgs e);

比较好理解。接下来是一个可以容纳全部鼠标设备状态信息的类实体MouseState,我们可以完全仿Xna内部定义:

MouseState.cs
/*-------------------------------------

代码清单:MouseState.cs
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/

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;

namespace Microsoft.Xna.Framework.Input
{
    /// <summary>
    
/// 鼠标设备状态
    
/// </summary>
    public class MouseState
    {
        // 横坐标
        double _X;
        public double X
        { get { return _X; } }

        // 纵坐标
        double _Y;
        public double Y
        { get { return _Y; } }

        // 滚轮位移
        int _ScrollWheel;
        public int ScrollWheel
        { get { return _ScrollWheel; } }

        // 左键状态(按下/弹起)
        ButtonState _LeftButton;
        public ButtonState LeftButton
        { get { return _LeftButton; } }

        // 右键状态(按下/弹起)
        ButtonState _RightButton;
        public ButtonState RightButton
        { get { return _RightButton; } }

        // 鼠标状态构造函数
        public MouseState(double x, double y, int scrollWheel, ButtonState leftButton, ButtonState rightButton)
        {
            _X = x;
            _Y = y;
            _ScrollWheel = scrollWheel;
            _LeftButton = leftButton;
            _RightButton = rightButton;
        }

        // 重载 != 运算符
        public static bool operator !=(MouseState left, MouseState right)
        {
            return !(left == right);
        }

        // 重载 == 运算符
        public static bool operator ==(MouseState left, MouseState right)
        {
            return left._X == right._X &&
                left._Y == right._Y &&
                left._ScrollWheel == right._ScrollWheel &&
                left._LeftButton == right._LeftButton &&
                left._RightButton == right._RightButton;
        }
    }
}

之后就是最关键的一环了:如何通过控件的应用事件还原得到鼠标设备状态信息。

Silverlight中,各类控件通用的鼠标事件无外乎9种:鼠标左键按下、鼠标左键弹起、鼠标右键按下、鼠标右键弹起、丢失鼠标、鼠标进入、鼠标离开、鼠标移动、滚轮移动。

只要我们针对这9种事件分别处理,即可还原得到鼠标设备的全部状态信息:

MouseInput.cs
/*-------------------------------------

代码清单:MouseInput.cs
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/

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;

namespace Microsoft.Xna.Framework.Input
{
    /// <summary>
    
/// 鼠标输入设备
    
/// </summary>
    public class MouseInput
    {
        // 侦听根容器
        static UIElement _Handle;
        static public UIElement Handle
        { get { return _Handle; } }

        // Button类事件
        static public event MouseButtonHandler MouseButtonEvent;
        // Common类事件
        static public event MouseCommonHandler MouseCommonEvent;
        // Wheel类事件
        static public event MouseWheelHandler MouseWheelEvent;

        // 鼠标左键状态
        static ButtonState _LeftButton;
        // 鼠标右键状态
        static ButtonState _RightButton;
        // 鼠标横坐标
        static double _PosX;
        // 鼠标纵坐标
        static double _PosY;
        // 滚轮移动位移
        static int _ScrollWheel;

        // 鼠标输入设备初始化
        static public void InitDevice(UIElement handle)
        {
            _Handle = handle;

            // Button类事件响应:
            
// 左键按下
            _Handle.MouseLeftButtonDown += new MouseButtonEventHandler(_Handle_MouseLeftButtonDown);
            // 左键弹起
            _Handle.MouseLeftButtonUp += new MouseButtonEventHandler(_Handle_MouseLeftButtonUp);
            // 右键按下
            _Handle.MouseRightButtonDown += new MouseButtonEventHandler(_Handle_MouseRightButtonDown);
            // 右键弹起
            _Handle.MouseRightButtonUp += new MouseButtonEventHandler(_Handle_MouseRightButtonUp);

            // Common类事件相应:
            
// 丢失鼠标
            _Handle.LostMouseCapture += new MouseEventHandler(_Handle_LostMouseCapture);
            // 鼠标进入
            _Handle.MouseEnter += new MouseEventHandler(_Handle_MouseEnter);
            // 鼠标离开
            _Handle.MouseLeave += new MouseEventHandler(_Handle_MouseLeave);
            // 鼠标移动
            _Handle.MouseMove += new MouseEventHandler(_Handle_MouseMove);

            // Wheel类事件相应
            _Handle.MouseWheel += new MouseWheelEventHandler(_Handle_MouseWheel);
        }

        // 获得鼠标设备状态
        static public MouseState GetState()
        {
            return new MouseState(_PosX, _PosY, _ScrollWheel, _LeftButton, _RightButton);
        }

        // 设备内置左键按下事件处理
        static void _Handle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            // 左键状态置为按下
            _LeftButton = ButtonState.Pressed;
            // 支持外链左键按下事件
            if (MouseButtonEvent != null)
                MouseButtonEvent.Invoke(MouseAction.MouseLeftButtonDown, e);
        }

        // 设备内置左键弹起事件处理
        static void _Handle_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            // 左键状态置为弹起
            _LeftButton = ButtonState.Released;
            // 支持外链左键弹起事件
            if (MouseButtonEvent != null)
                MouseButtonEvent.Invoke(MouseAction.MouseLeftButtonUp, e);
        }

        // 设备内置右键按下事件处理
        static void _Handle_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            // 右键状态置为按下
            _RightButton = ButtonState.Pressed;
            // 支持外链右键按下事件
            if (MouseButtonEvent != null)
                MouseButtonEvent.Invoke(MouseAction.MouseRightButtonDown, e);
        }

        // 设备内置右键弹起事件处理
        static void _Handle_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            // 右键状态置为弹起
            _RightButton = ButtonState.Released;
            // 支持外链右键弹起事件
            if (MouseButtonEvent != null)
                MouseButtonEvent.Invoke(MouseAction.MouseRightButtonUp, e);
        }

        // 设备内置鼠标丢失事件处理
        static void _Handle_LostMouseCapture(object sender, MouseEventArgs e)
        {
            // 支持外链鼠标丢失事件
            if (MouseCommonEvent != null)
                MouseCommonEvent.Invoke(MouseAction.LostMouseCapture, e);
        }

        // 设备内置鼠标进入事件处理
        static void _Handle_MouseEnter(object sender, MouseEventArgs e)
        {
            // 支持外链鼠标进入事件
            if (MouseCommonEvent != null)
                MouseCommonEvent.Invoke(MouseAction.MouseEnter, e);
        }

        // 设备内置鼠标离开事件处理
        static void _Handle_MouseLeave(object sender, MouseEventArgs e)
        {
            // 支持外链鼠标离开事件
            if (MouseCommonEvent != null)
                MouseCommonEvent.Invoke(MouseAction.MouseLeave, e);
        }

        // 设备内置鼠标移动事件处理
        static void _Handle_MouseMove(object sender, MouseEventArgs e)
        {
            // 获取鼠标位置
            System.Windows.Point pos = e.GetPosition(_Handle);
            // 更新设备横坐标
            _PosX = pos.X;
            // 更新设备纵坐标
            _PosY = pos.Y;
            // 支持外链鼠标移动事件
            if (MouseCommonEvent != null)
                MouseCommonEvent.Invoke(MouseAction.MouseMove, e);
        }

        // 设备内置鼠标滚轮事件
        static void _Handle_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            // 更新滚轮位移
            _ScrollWheel = e.Delta;
            // 支持外链鼠标滚轮事件
            if (MouseWheelEvent != null)
                MouseWheelEvent.Invoke(MouseAction.MouseWheel, e);
        }
    }
}

之后,我们便可如Xna一样,通过调用GetState()方法即时获得鼠标设备状态信息。

除此之外,该类的设计依然保留了Silverlight事件响应的特性。原本9种事件被重新归为3类,以静态化方式提供全局事件注册。——这样做无疑是有意义的,即使在任何一个小型的子类甚至子容器中,我们都可以随时注册并侦听根容器的全部事件。

接下来我们将Silverlight工程默认生成的根容器注册给刚刚构建好的鼠标输入设备:

/*-------------------------------------

代码清单:MainPage.xaml.cs
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace SilverXna
{
    public partial class MainPage : UserControl
    {
        Game _Game;
        public MainPage()
        {
            InitializeComponent();
            _Game = new Game(_GameSurface);
            // 初始化鼠标输入设备
            MouseInput.InitDevice(this);
        }
    }
}

最后是主体代码:

/*-------------------------------------

代码清单:Game.cs
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/

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.Imaging;
using System.Windows.Media.Animation;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.IO;

namespace SilverXna
{
    public class Game : SilverGame
    {
        SpriteBatch _SpriteBatch;
        Texture2D texture;

        public Game(DrawingSurface GameSurface)
            : base(GameSurface)
        { }

        public override void Initialize()
        {
            base.Initialize();
        }

        public override void LoadContent()
        {
            _SpriteBatch = new SpriteBatch(this);

            Stream imageStream = Content.OpenResourceStream("SilverXna","Content/SLXNA.png");
            texture = Content.LoadTexture2D(imageStream);
            base.LoadContent();
        }

        public override void UnloadContent()
        {
            base.UnloadContent();
        }

        Vector2 pos = new Vector2(100100);
        public override void Update(TimeSpan DeltaTime, TimeSpan TotalTime)
        {
            // 获得鼠标设备状态
            MouseState mouseState = MouseInput.GetState();
            // 更新纹理位置到鼠标左键单击点
            if (mouseState.LeftButton == ButtonState.Pressed)
            {
                pos = new Vector2((float)mouseState.X, (float)mouseState.Y);
            }
            base.Update(DeltaTime, TotalTime);
        }

        public override void Draw(TimeSpan DeltaTime, TimeSpan TotalTime)
        {
            GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, new Color(100149237255), 1.0f0);

            _SpriteBatch.Begin(BlendState.AlphaBlend);
            _SpriteBatch.Draw(texture, pos, new Color(255255255255));
            _SpriteBatch.End();

            base.Draw(DeltaTime, TotalTime);
        }
    }
}

单击鼠标左键,纹理会自动移动到相应的位置。按住鼠标左键不放,则呈现拖动效果。大家不妨一试~

以上,谢谢 ^ ^