游戏中每一个逻辑实体都是一个小型的状态机,它们在游戏主循环的驱动之下不停的变换自身状态,游戏世界大千万物盛衰轮回由此产生。
习惯了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
来自: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
来自: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(100, 100);
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(100, 149, 237, 255), 1.0f, 0);
_SpriteBatch.Begin(BlendState.AlphaBlend);
_SpriteBatch.Draw(texture, pos, new Color(255, 255, 255, 255));
_SpriteBatch.End();
base.Draw(DeltaTime, TotalTime);
}
}
}
单击鼠标左键,纹理会自动移动到相应的位置。按住鼠标左键不放,则呈现拖动效果。大家不妨一试~
以上,谢谢 ^ ^