继完成游戏主体框架搭建后,接下来我将通过SL.XNA模式中Silverlight控件与XNA对象之间双向交互操作的例子,向大家进一步讲解框架的拓展使用及简单的承载演示。在此之前大家需要理解Windows Phone移动设备与传统桌面设备在操作方面的差异。

直观上看,桌面应用大多使用鼠标加键盘的操作方式:鼠标左键、中键、右键,包括单击(按下、放开)、双击以及滑动、拖动、滚动等;而Windows Phone的操作方式则以触控加虚拟键盘为主:按下、放开、按住滑动、各种(多指)手势等;当然,也可以实现像NDS那样通过麦克风实现语音与游戏的交互。而Windows Phone游戏开发中则触摸以为主要输入方式,下面我将分开讲解各种触控操作在Windows Phone中的代码实现。

一、XNA中的触控操作

在此之前,很有必要向新手朋友们简要阐述一下XNA的运行机制:XNA是通过传统的轮询(循环)方式呈现游戏的,即每秒钟对游戏逻辑进行N次数据更新(Update)并将对象重新绘制到屏幕上(Draw),这个频率N在代码中的设定为timer.UpdateInterval = TimeSpan.FromTicks(333333); 即30fps(30帧每秒)。

举个例子:假如我们想让一名战士从(0,0)坐标向(300,300)坐标移动,若该战士的移动速度系数Speed=5,那么我们只需在Update()方法中让他的位置属性(X,Y)分别加上Speed(X+Speed,Y+Speed),于是两秒钟后战士将到达目的地(X+2*5*30,Y+2*5*30)=(300,300)。根据前面所述游戏循环机制,每执行Update数据一次,屏幕就会擦除掉之前的画面然后重新Draw新的画面,于是在这两秒钟内,战士的位置会发生2*30=60次变化,依次为(0,0),(5,5),(10,10),(15,15)……(295,295),(300,300)。短短2秒钟的时间里战士位置连续变化了60次,游戏屏幕好比黑板一样,擦了60次,又画了60次,由于频率很高,好比电视欺骗人眼球同样原理,呈现的是一种连续的动态效果,最终便形成了游戏意义上的“战士移动”动画。

由此我们可以明确:XNA中无论编写什么都必须基于循环,因此XNA模式中无论是低级触控(按下、放开、按住滑动)还是高级(路由)手势,都得放在游戏循环(Update)中进行时时的监测(轮询式):

XNA 触控

private void Update(object sender, GameTimerEventArgs e) {
            inputHandler.MonitorTouch();
            inputHandler.MonitorGestures();
        }

/// <summary>
        /// 监测触摸(XNA模式,置于Update轮询中)
        /// </summary>
        public void MonitorTouch() {
            TouchCollection touchCollection = TouchPanel.GetState();
            foreach (TouchLocation location in touchCollection) {
                TouchEventArgs e = new TouchEventArgs() { Location = location };
                switch (location.State) {
                    case TouchLocationState.Pressed: if (Press != null) { Press(this, e); } break;
                    case TouchLocationState.Moved: if (Move != null) { Move(this, e); } break;
                    case TouchLocationState.Released: if (Release != null) { Release(this, e); } break;
                }
            }
        }

/// <summary>
        /// 监测手势(XNA模式,置于Update轮询中)
        /// </summary>
        public void MonitorGestures() {
            while (TouchPanel.IsGestureAvailable) {     //判断是否还有手势尚未被处理
                GestureSample gesture = TouchPanel.ReadGesture(); //读取尚未处理的手势
                GesturesEventArgs e = new GesturesEventArgs() { Gesture = gesture };
                switch (gesture.GestureType) {
                    case GestureType.Tap: if (Tap != null) { Tap(this, e); } break;
                    case GestureType.DoubleTap: if (DoubleTap != null) { DoubleTap(this, e); } break;
                    case GestureType.Hold: if (Hold != null) { Hold(this, e); } break;
                    case GestureType.VerticalDrag: if (VerticalDrag != null) { VerticalDrag(this, e); } break;
                    case GestureType.HorizontalDrag: if (HorizontalDrag != null) { HorizontalDrag(this, e); } break;
                    case GestureType.FreeDrag: if (FreeDrag != null) { FreeDrag(this, e); } break;
                    case GestureType.DragComplete: if (DragComplete != null) { DragComplete(this, e); } break;
                    case GestureType.Flick: if (Flick != null) { Flick(this, e); } break;
                    case GestureType.Pinch: if (Pinch != null) { Pinch(this, e); } break;
                    case GestureType.PinchComplete: if (PinchComplete != null) { PinchComplete(this, e); } break;
                }
            }
        }

如果还不能理解XNA的游戏循环原理,大家不妨将游戏循环频率设定为每3秒/次,再进一步测试XNA中触控操作,是否发现了什么?

二、Silverlight控件的触控操作

如果你是一位Silverlight(Web)开发者,你会发现Windows Phone中的Silverlight控件相当给力(以Image控件为例):

图中所有框住的事件均为路由触控事件,且慢,居然还有Mouse……事件,刹那间泪崩了,这不是BUG,我反正信了。就如智能感知提示的那样:Mouse这一系列事件是“触笔的笔尖接触屏幕”时发生的,类比桌面鼠标事件。而其余紫色框住的部分则为Windows Phone专有触控事件,其中Tap、DoubleTap、Hold和XNA中的一致,剩下的手势则可通过3个Manipulation连锁事件自由实现,灵活度相当高。此时或有有朋友会问:同样是路由的MouseLeftButtonDown和Tap到底有何区别?不妨拿起你手中的Windows Phone测试一下不就知道啦 ^ ^

当然,Silverlight同样也拥有类似XNA中的非路由低级触控。于是我将以上关于XNA与Silverlight的触控操作进行最终整合,以代码的形式封装成一个名为InputHandler(输入处理器)的类,内置了Silverlight与XNA的所有触控操作(注意了,如果使用该类中的XNA模式触控必须将MonitorTouch()和MonitorGestures()两个方法放置于主循环的Update()中方能起效):

InputHandler

/// <summary>
    /// 输入处理器
    /// </summary>
    public sealed class InputHandler {

/// <summary>触摸按下时触发</summary>
        public event EventHandler<TouchEventArgs> Press;
        /// <summary>触摸持续按住并移动时触发</summary>
        public event EventHandler<TouchEventArgs> Move;
        /// <summary>触摸放开时触发</summary>
        public event EventHandler<TouchEventArgs> Release;

/// <summary>
        /// 创建输入处理器
        /// </summary>
        /// <param name="mode">模式</param>
        public InputHandler(TouchModes mode) { Mode = mode; }

TouchModes _Mode;
        /// <summary>
        /// 获取或设置触控模式
        /// </summary>
        public TouchModes Mode {
            get { return _Mode; }
            set {
                _Mode = value;
                Touch.FrameReported -= Touch_FrameReported;
                TouchPanel.EnabledGestures = GestureType.None;
                if (value == TouchModes.Silverlight) {
                    Touch.FrameReported += Touch_FrameReported;
                } else if (value == TouchModes.XNA) {
                    //启动所有手势(也可根据实际情况选择部分启动)
                    TouchPanel.EnabledGestures = GestureType.Tap | GestureType.DoubleTap | GestureType.Hold | GestureType.FreeDrag | GestureType.Flick | GestureType.Pinch;
                }
            }
        }

#region Silverlight模式

/// <summary>
        /// 获取或设置参照物(Silverlight模式)
        /// </summary>
        public UIElement Reference { get; set; }

void Touch_FrameReported(object sender, TouchFrameEventArgs args) {
            TouchPoint point = args.GetPrimaryTouchPoint(Reference); //参数若为null则相对于屏幕垂直呈现时的左上角(按键右置水平呈现时的左下角)
            if (point != null) {
                TouchEventArgs e = new TouchEventArgs() { Point = point };
                switch (point.Action) {
                    case TouchAction.Down: if (Press != null) { Press(this, e); } break;
                    case TouchAction.Move: if (Move != null) { Move(this, e); } break;
                    case TouchAction.Up: if (Release != null) { Release(this, e); } break;
                }
            }
        }

#endregion

#region XNA模式

/// <summary>短暂地触控了屏幕上的一个点时触发</summary>
        public event EventHandler<GesturesEventArgs> Tap;
        /// <summary>快速连续点按了屏幕两次时触发</summary>
        public event EventHandler<GesturesEventArgs> DoubleTap;
        /// <summary>触控屏幕上的某一点约一秒钟时间时触发</summary>
        public event EventHandler<GesturesEventArgs> Hold;
        /// <summary>触控屏幕,然后执行水平(从左到右,或从右到左)手势时触发</summary>
        public event EventHandler<GesturesEventArgs> VerticalDrag;
        /// <summary>触控屏幕,然后执行垂直(从顶部到底部,或从底部到顶部)手势时触发</summary>
        public event EventHandler<GesturesEventArgs> HorizontalDrag;
        /// <summary>触控屏幕,然后执行自由格式的拖动手势时触发</summary>
        public event EventHandler<GesturesEventArgs> FreeDrag;
        /// <summary>触控屏幕上的两点,然后聚合或分开两点时触发</summary>
        public event EventHandler<GesturesEventArgs> DragComplete;
        /// <summary>同时执行触控与快速擦过屏幕的操作时触发</summary>
        public event EventHandler<GesturesEventArgs> Flick;
        /// <summary>拖动手势(VerticalDrag、HorizontalDrag 或 FreeDrag)已完成时触发</summary>
        public event EventHandler<GesturesEventArgs> Pinch;
        /// <summary>收缩操作已完成时触发</summary>
        public event EventHandler<GesturesEventArgs> PinchComplete;

/// <summary>
        /// 监测触摸(XNA模式,置于Update轮询中)
        /// </summary>
        public void MonitorTouch() {
            TouchCollection touchCollection = TouchPanel.GetState();
            foreach (TouchLocation location in touchCollection) {
                TouchEventArgs e = new TouchEventArgs() { Location = location };
                switch (location.State) {
                    case TouchLocationState.Pressed: if (Press != null) { Press(this, e); } break;
                    case TouchLocationState.Moved: if (Move != null) { Move(this, e); } break;
                    case TouchLocationState.Released: if (Release != null) { Release(this, e); } break;
                }
            }
        }

/// <summary>
        /// 监测手势(XNA模式,置于Update轮询中)
        /// </summary>
        public void MonitorGestures() {
            while (TouchPanel.IsGestureAvailable) {     //判断是否还有手势尚未被处理
                GestureSample gesture = TouchPanel.ReadGesture(); //读取尚未处理的手势
                GesturesEventArgs e = new GesturesEventArgs() { Gesture = gesture };
                switch (gesture.GestureType) {
                    case GestureType.Tap: if (Tap != null) { Tap(this, e); } break;
                    case GestureType.DoubleTap: if (DoubleTap != null) { DoubleTap(this, e); } break;
                    case GestureType.Hold: if (Hold != null) { Hold(this, e); } break;
                    case GestureType.VerticalDrag: if (VerticalDrag != null) { VerticalDrag(this, e); } break;
                    case GestureType.HorizontalDrag: if (HorizontalDrag != null) { HorizontalDrag(this, e); } break;
                    case GestureType.FreeDrag: if (FreeDrag != null) { FreeDrag(this, e); } break;
                    case GestureType.DragComplete: if (DragComplete != null) { DragComplete(this, e); } break;
                    case GestureType.Flick: if (Flick != null) { Flick(this, e); } break;
                    case GestureType.Pinch: if (Pinch != null) { Pinch(this, e); } break;
                    case GestureType.PinchComplete: if (PinchComplete != null) { PinchComplete(this, e); } break;
                }
            }
        }

#endregion

}

最后经过反复的对比测试,得出以下结论:Silverlight模式在触控方面对比XNA最大优势在于:Silverlight的触控是基于事件驱动的(触发式),是同步的。显而易见,针对低级触控操作,Silverlight模式性能更高;相对于具体控件,Silverlight触控事件效率更高,使用更方便;总体来说,不论是精确度、灵活度还是开发效率和维护效率,Silverlight触控模式都明显优于XNA模式。

因此,这也是为什么微软会在WP7.1开始重点推出SL.XNA模式的主要原因了:SL负责游戏的UI部分,而XNA则负责绘制游戏对象,分工明确,效率与性能兼具。

为了精确论证以上观点的完美可执行性,我特意编写了一个SL控件与XNA精灵交互的Demo:

如上图,不仅有2D精灵,也有3D骨骼动画模型;有Silverlight按钮(UI),也有XNA按钮(UI);所有对象均同屏显示,且可相互操作(封装了一个名为UIHandler的UI管理类,详见本文结尾处源码)。以最左边“SL控件+XNA 2D精灵”的“转向”交互为例,首先我们赋予XNA精灵一个Direction属性:

/// <summary>
        /// 获取或设置朝向
        /// </summary>
        public virtual Directions Direction { get; set; }

当点击“转向”Button时,触发Click事件修改该精灵的Direction属性:

button.Click += delegate {
            int direction = (int)role1.Direction + 1;
            role1.Direction = direction > 3 ? 0 : (Directions)direction;
        };

同时,该精灵的Direction属性新值重新反馈给Silverlight的TextBlock控件并显示出来:

textBlock.Text = ((Directions)role1.Direction).ToString();

由此便完成了SL -> XNA –> SL这样一个双向交互的演示。从该案例中大家是否体会到了Silvelight UI和XNA Sprite之间的亲密无间?当然了,SL与XNA 3D对象的交互也同样方便快捷(详见源码),不过需要注意一点,在绘制模型代码段中必须加入以下三句话方能正常显示(否则会出现因与SpriteBatch混合作用而导致的贴图呈现部分透明状态):

//呈现模型蒙皮网格
            foreach (ModelMesh mesh in currentModel.Meshes) {
                foreach (SkinnedEffect effect in mesh.Effects) {
                ......            
effect.GraphicsDevice.BlendState = BlendState.AlphaBlend;
                    effect.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
                    effect.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
                ......
                }
            }

至于纯XNA界面与XNA精灵的交互例子我就不再多说了,开发过XNA的朋友都清楚,需要将UI绘制出来后时时判断触控点与各UI间的位置关系最终做出判断,代码量大,编码繁琐;总之XNA做UI吃力不讨好,巨蛋痛,真的。

结尾,我认为很有必要提醒大家一下关于游戏开发中使用非常广泛的Point结构体和Math数学库。WPF/Silverlight/XNA中的Point设计各不相同,移植时需特别注意;System.Math和Microsoft.Xna.Framework.MathHelper针对的领域各不相同,前者在三角函数方面更全面(double),如正弦、余弦、正切、余切等;而后者则主要针对float类型的计算,比如弧度和角度之间的换算等常用的静态方法。

OK,至此我们摸透了SL.XNA中各元素的操控原理与实现,下一节我将对第5节的Demo进行移植,进而向大家展示该框架强而有力的实用性,敬请关注。

本节源码下载地址:SLXnaGame2.zip

手记小结:SL.XNA模式乃集大成者,无论开发2D或是3D游戏,Silverlight负责制作UI高效且便捷,XNA则专业绘制高性能精灵与模型,分工明确,结构合理,互利互补。掌握好SL.XNA开发模式,深刻透析游戏结构布局,分工合作处理好游戏各环节逻辑关系,游戏整体“效率”与“性能”必将最大化。

推荐参考:Nowpaper和Williams关于Windows Phone的游戏开发博客。

转载于:https://www.cnblogs.com/alamiye010/archive/2012/04/11/2442058.html

Silverlight.XNA(C#)跨平台3D游戏研发手记:(七)向Windows Phone移植之双向交互相关推荐

  1. Silverlight.XNA(C#)跨平台3D游戏研发手记:(八)向Windows Phone移植之2D跨平台迁移

    Silverlight 5集成了XNA,Windows Phone 7.1同样也拥有SL.XNA模式,基于Mono的开源产品Moonlight更能实现多平台部署:可见,Silverlight离跨平台的 ...

  2. Silverlight.XNA(C#)跨平台3D游戏研发手记:(十)3D 场景与控制设计①

    模型和骨骼动画仅仅是开启3D游戏的敲门砖,置入基于摄像机的场景设计方能呈现最完美的3D游戏.本节,我们依旧从简单着手,一步步创建基于模型的3D游戏场景. <XNA4.0学习指南(中文)>是 ...

  3. Silverlight.XNA(C#)跨平台3D游戏研发手记:(十一)3D SLG(策略战棋游戏)设计案例

    某天,当你一不小心发现已经够随心所欲的驾驭3D摄像机之时,任何类型的3D游戏都将成为囊中玩物,过往如烟. 回忆逝去的童年让我极度惦记的SLG策略战棋游戏,或许对于大多数玩家来说,它费时费力不被讨好:然 ...

  4. Silverlight.XNA(C#)跨平台3D游戏研发手记:(五)SLG动感增效之《幻影粒子》

    随着电子技术发展与普及,人们使用电脑时间越来越长,由于人类自身身体比如眩晕症.眼球衰老等客观因素存在,未来玩家会更加倾向于低视疲劳度/低神经刺激的绿色健康休闲2D游戏而非3D.<希魔复活> ...

  5. Silverlight.XNA(C#)跨平台3D游戏研发手记:(一)差集运算在SLG战斗范围设定中的应用...

    战棋游戏通常指以回合制为基础,角色在地图上按格移动作战的游戏,好比下棋一样,该类型游戏更侧重于策略,节奏较缓慢,注重精美.绚丽的画面,考验的是玩家运筹全局的智慧.耳熟能详的比如<梦幻模拟战> ...

  6. Silverlight.XNA(C#)跨平台3D游戏研发手记:(一)差集运算在SLG战斗范围设定中的应用

    战棋游戏通常指以回合制为基础,角色在地图上按格移动作战的游戏,好比下棋一样,该类型游戏更侧重于策略,节奏较缓慢,注重精美.绚丽的画面,考验的是玩家运筹全局的智慧.耳熟能详的比如<梦幻模拟战> ...

  7. Unity3D ——强大的跨平台3D游戏开发工具教程

    http://unity3d.9ria.com/?p=22 众所周知,Unity3D是一个能够实现轻松创作的多平台的游戏开发工具,是一个全面整合的专业游戏引擎.在现有的版本中,其强大的游戏制作功能已经 ...

  8. Silverlight游戏研发手记:(三)蜂窝拓扑结构在SLG地图布局中的应用

    上一节给大家讲解了如何在四边形单元格基础上构建SLG地图场景,并实现移动.战斗的基础框架:热爱SLG的朋友一定非常清楚,绝大多数的SLG游戏地形单元格都可归为四类:四边四向.四边八向和四边六向.六边六 ...

  9. Silverlight游戏研发手记:(五)SLG动感增效之《幻影粒子》

    随着电子技术发展与普及,人们使用电脑时间越来越长,由于人类自身身体比如眩晕症.眼球衰老等客观因素存在,未来玩家会更加倾向于低视疲劳度/低神经刺激的绿色健康休闲2D游戏而非3D.<希魔复活> ...

最新文章

  1. 部署并使用Docker(Alibaba Cloud Linux 2)
  2. 一篇不错的讲解Java异常的文章(转载)
  3. Amazon上最畅销的「操作系统书」有哪些?
  4. select、poll、epoll之间的区别(搜狗面试)
  5. 继续教育统考计算机和英语难度怎么样,网络教育英语统考90分的难度怎么样
  6. [转]关于频率、模拟角频率、数字角频率
  7. 2009年十大Java技术解决方案
  8. 遗传算法-附代码注释
  9. android+世界地图高清版大图片,世界地图全图高清版
  10. CAE软件有哪些?流体力学方面的软件有哪些?ANSYS是CAE软件吗?
  11. cf服务器延迟测试,Cloudflare-SpeedTest - 测试 CF CDN 延迟和速度,CF自选IP
  12. 网站页面篡改及挂马的应急处置
  13. Python中pandas.Dataframe数据筛选
  14. matlab的subplot--子图位置大小随心所欲
  15. 对项目工时的估算----( PERT “计划评审技术” ) 三点估算法
  16. 易乐游服务器系统,易乐游装在云服务器
  17. 计算机网络安全设计毕业设计,计算机网络安全及防护毕业设计论文01
  18. Riak 简介,第 1 部分: 与语言无关的 HTTP API
  19. 给女友的网页小惊喜,(生日,周年,表白通用) ☞谁说程序员不懂浪漫
  20. Apache Flink fault tolerance源码剖析(六)

热门文章

  1. jstree禁用父节点点击_Jstree 使用CheckBox插件 选中父节点时被禁用的子节点也会选中问题...
  2. cypress离线安装_【拆一个高端货】 美国NI公司 GPIB-USB转接卡 长标题
  3. android des ecb加密_Android逆向 | 基础知识篇 01
  4. android 连续调用方法是,android – SwitchPreferences多次调用onPreferenceChange()方法
  5. mysql explode函数_hive中,lateral view 与 explode函数
  6. c++ to_string 指定字符位数_你不知道的ES6字符串的扩展
  7. 两阶段提交与三阶段提交
  8. java 垃圾回收入门
  9. Windows核心编程_修改U盘图标
  10. 升级glibc库到glibc-2.14.1