打造轻量级Windows Phone7 游戏引擎-Samurai 第四话 Button(上)
来源:互联网 发布:单片机和fpga的区别 编辑:程序博客网 时间:2024/05/17 22:06
Button模块我打算分两部分来介绍,第一部分是介绍绘制精灵,第二部分是正式的Button。
首先什么是Button呢?或者说Button应该是什么样子的呢?
当然了,就我们平常的经验来说,Button不就是一个按钮吗,可以被点击然后实现相应的功能。它往往是一个矩形的区域,里面写上相关的功能,点击一下好像被按下去了一样。大致如此。
Samurai中的Button本质上是多了一些与SAInput交互的接口的精灵类。
什么是精灵类?就是我们用来绘制游戏角色的一系列动作的类。
我们先来定义精灵类的基类SASprite(当然还是抽象类)
public abstract class SASprite { public Texture2D texture; public Color color; public Vector2 position; public virtual Vector2 Size { get { return new Vector2(rectangle.Width, rectangle.Height); } } //获取Sprite的大小 public virtual Rectangle rectangle { get { return new Rectangle((int)position.X, (int)position.Y, (int)Size.X, (int)Size.Y); } }//获取Sprite的碰撞矩形 public virtual Rectangle sourceRectangle { get; set; } //截取绘制图像位置 public SASprite() { } public SASprite(Texture2D texture) : this(texture, Vector2.Zero) { } public SASprite(Texture2D texture, Vector2 positon) : this(texture, (int)positon.X, (int)positon.Y) { } public SASprite(Texture2D texture, int pos_x, int pos_y) { this.texture = texture; this.position = new Vector2(pos_x, pos_y); this.color = Color.White; } //判断是否有相交部分 public virtual bool IfCollide(Rectangle rect) { return rectangle.Intersects(rect); } //外部调用的Draw接口 public void Draw() { Draw(SAGlobal.spriteBatch); } public virtual void Draw(SpriteBatch spriteBatch) { spriteBatch.Draw(texture, position, color); } }这个类是最简单最基本的一个精灵类,它只有一个纹理(图像),有相关的颜色、位置的信息(这里为什么用属性,而且还是virtual的?看了后面你就明白了)
它包含了最基本的“碰撞检测”以及“绘制”的方法。
SASprite有两个子类,一个是只能绘制一帧图像的SASimpleSprite,一个是可以绘制多帧动画的SAActionSprite(好吧,他是SASprite的孙子类):
先来看看SASimpleSprite:
public class SASimpleSprite:SASprite { public SASimpleSprite() { } /// <summary> /// 在指定位置绘制整张纹理 /// </summary> /// <param name="texture">纹理</param> /// <param name="postion">位置</param> public SASimpleSprite(Texture2D texture, Vector2 postion) { this.texture = texture; this.sourceRectangle = new Rectangle(0,0,texture.Bounds.Width,texture.Bounds.Height); this.position = position; this.color = Color.White; } /// <summary> /// 直接绘制整张纹理 /// </summary> /// <param name="texture">纹理</param> public SASimpleSprite(Texture2D texture) : this(texture, Vector2.Zero) { } /// <summary> /// 在指定position绘制指定的sourceRectangle范围内的纹理 /// </summary> /// <param name="texture">纹理</param> /// <param name="sourceRectangle">纹理矩形</param> /// <param name="position">位置</param> /// <param name="color">指定颜色</param> public SASimpleSprite(Texture2D texture,Rectangle sourceRectangle,Vector2 position,Color color) { this.texture = texture; this.sourceRectangle = sourceRectangle; this.position = position; this.color = color; } public SASimpleSprite(string resource, Vector2 position) : this(SAGraphicUtil.GetImage(resource), position) { } public SASimpleSprite(string resource) : this(SAGraphicUtil.GetImage(resource)) { } public SASimpleSprite(string resource, Rectangle sourceRectangle, Vector2 position, Color color) : this(SAGraphicUtil.GetImage(resource), sourceRectangle, position, color) { } public override void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) { spriteBatch.Draw(texture, position, sourceRectangle, color); } }
再来看看SAActionSprite:
public class SAActionSprite:SASimpleSprite { /// <summary> /// 推荐的做法是: /// 在子类中的静态方法中将这些数据进行初始化(当然要另外设置相应的静态变量) /// 在构造方法中直接就为_sourceRectangles & _sizes赋值 /// </summary> public Rectangle[] _sourceRectangles; public Vector2[] _sizes; public int index; public int Count { get { return _sourceRectangles.Length; } } /// <summary> /// 直到此时,我才真正清楚原来Prop是如此的方便,还可以像方法一样被重写。 /// </summary> public override Vector2 Size { get { return _sizes[index]; } } //获取index帧的Sprite的大小 public override Rectangle rectangle { get { return new Rectangle((int)position.X, (int)position.Y, (int)Size.X, (int)Size.Y); } }//获取index帧的Sprite的碰撞矩形 public override Rectangle sourceRectangle { get { return _sourceRectangles[index]; } } //截取绘制图像位置 /// <summary> /// 不怕麻烦就用默认构造函数 /// </summary> public SAActionSprite() { } /// <summary> /// 默认动作第零帧的构造函数 /// </summary> /// <param name="texture"></param> /// <param name="sourceRectangles"></param> /// <param name="sizes"></param> /// <param name="position"></param> /// <param name="color"></param> public SAActionSprite(Texture2D texture, Rectangle[] sourceRectangles, Vector2[] sizes, Vector2 position, Color color) : this(texture, sourceRectangles, sizes, position, color, 0) { } /// <summary> /// 最全的构造函数了 /// </summary> /// <param name="texture">纹理</param> /// <param name="sourceRectangles">纹理矩形数组</param> /// <param name="sizes">大小数组</param> /// <param name="position">绘制位置</param> /// <param name="color">绘制颜色</param> /// <param name="index">动作的第几帧</param> public SAActionSprite(Texture2D texture, Rectangle[] sourceRectangles, Vector2[] sizes, Vector2 position, Color color, int index) { this.texture = texture; this._sourceRectangles = sourceRectangles; this._sizes = sizes; this.position = position; this.color = color; this.index = index; } public SAActionSprite(string resource, Rectangle[] sourceRectangles, Vector2[] sizes, Vector2 position, Color color, int index) : this(SAGraphicUtil.GetImage(resource), sourceRectangles, sizes, position, color, index) { } public SAActionSprite(string resource, Rectangle[] sourceRectangles, Vector2[] sizes, Vector2 position, Color color) : this(SAGraphicUtil.GetImage(resource), sourceRectangles, sizes, position, color, 0) { } //因为属性被Override,Draw方法反而不用重写了 }
其实就是多了个索引,然后原本的几个属性被Override了,不得不赞一句Prop用起来还是爽歪歪的。
细心看会发现,纳尼,怎么只有一个Texture而有一堆的sourceRectangle!!!请勿激动,因为作者我平时比较爱一张一张Texture地加载,而这样效率远不及在一张大Texture上将全部图像加载效率高,所以这里提醒自己,即使是加载多个Texture,至少在一个Action里还是不要加载多个Texture了吧~
那么回到正题上来,Button其实就是对SAActionSprite的扩展:
(因为Button其实有纯的图像Button,也有图像+文字Button,甚至还有纯的文字Button,所以先定义一个Button的基类,称之为SAControl)
public enum ControlStatus { Normal, Touch, Release } /// <summary> /// 控件基类,继承自ActionSprite /// </summary> public abstract class SAControl:SAActionSprite { //控件的状态,初始为Normal protected ControlStatus controlStatus; //关于响应点击的委托 public delegate void ClickDelegate(); protected ClickDelegate ClickHandler; //控件是否开启,默认开启 public bool Enable { get; set; } #region 初始化 public void Init() { this.index = 0; this.controlStatus = ControlStatus.Normal; this.Enable = true; this.color = Color.White; } #endregion #region 注册和注销(注意不能在响应方法中调用,因为Foreach中不可以增减) public void Add() { SAInput.AddButton(this); } public void Remove() { SAInput.RemoveButton(this); } #endregion #region 与SAInput之间的接口 /// <summary> /// 更新时,先重置状态为Normal,不过子类要重写(index的修改) /// </summary> public virtual void Update() { this.controlStatus = ControlStatus.Normal; } /// <summary> /// 更新时发现被触碰了 /// </summary> public virtual void OnTouch(Vector2 touchPos) { if (IfOnTouch(touchPos)) { this.controlStatus = ControlStatus.Touch; } } /// <summary> /// 辅助判断是否被触碰到 /// </summary> /// <param name="touchPosition"></param> /// <returns></returns> public bool IfOnTouch(Vector2 touchPosition) { if (touchPosition.X >= position.X && touchPosition.X <= position.X + Size.X && touchPosition.Y >= position.Y && touchPosition.Y <= position.Y + Size.Y) { return true; } return false; } public bool IfOnTouch(GestureSample gesture) { return IfOnTouch(gesture.Position); } #endregion #region 响应点击事件 public virtual void OnClick() { if (this.Enable) { if (ClickHandler != null) { this.controlStatus = ControlStatus.Release; ClickHandler.Invoke(); } } } #endregion }那么SAButton就好说了:
public class SAButton:SAControl { /// <summary> /// 最简单,一张图片搞定所有 /// </summary> public SAButton(string resource,Rectangle sourceRect,Vector2 pos,ClickDelegate clickDel) { Init(); //TODO this.texture = SAGraphicUtil.GetImage(resource); this._sourceRectangles = new Rectangle[3]; this._sourceRectangles[2] = this._sourceRectangles[1] = this._sourceRectangles[0] = sourceRectangle; this._sizes = new Vector2[3]; this._sizes[2] = this._sizes[1] = this._sizes[0] = new Vector2(rectangle.Width, rectangle.Height); this.position = pos; this.ClickHandler = clickDel; Add(); } /// <summary> /// 最全面 /// </summary> public SAButton(string resource, Rectangle[] sourceRects, Vector2 pos, ClickDelegate clickDel) { Init(); //TODO this.texture = SAGraphicUtil.GetImage(resource); this._sourceRectangles = new Rectangle[3]; this._sizes = new Vector2[3]; this.position = pos; this.ClickHandler = clickDel; #region 处理不同的数量 if (sourceRects.Length == 3) { this._sourceRectangles = sourceRects; for (int i = 0; i < 3; i++) { this._sizes[i] = new Vector2(_sourceRectangles[i].Width,_sourceRectangles[i].Height); } } else if (sourceRects.Length == 2) { this._sourceRectangles[2] = this._sourceRectangles[0] = sourceRects[0]; this._sourceRectangles[1] = sourceRects[1]; for (int i = 0; i < 3; i++) { this._sizes[i] = new Vector2(_sourceRectangles[i].Width, _sourceRectangles[i].Height); } } else { for (int i = 0; i < 3; i++) { this._sourceRectangles[i] = sourceRects[0]; this._sizes[i] = new Vector2(_sourceRectangles[i].Width, _sourceRectangles[i].Height); } } #endregion Add(); } #region 流程控制 public override void Update() { //TODO index = 0; //不能删除,更新状态 base.Update(); } public override void OnClick() { //TODO if (this.Enable) { index = 2; } //不能删除 base.OnClick(); } public override void OnTouch(Vector2 touchPos) { //TODO if (IfOnTouch(touchPos)) { index = 1; } //不能删除 base.OnTouch(touchPos); } /// <summary> /// 由于状态的改变以及index的变化进行了修改,所以没有必要重写Draw了 /// </summary> public override void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) { base.Draw(spriteBatch); } #endregion }
我其实对那段老长了的构造函数比较反感,但是因为平时我们用Button的时候比较懒,虽然Button有“正常”,“被触碰但是手指未离开”,“触碰后手指离开”三种状态,但是往往我们默认第一种和第三种状态用同样的图像进行显示,所以...
我们也可以看到SAButton里有一些与SAInput进行交互的地方,那么,到底是如何进行交互的呢?
我们在原本的SAInput中加入了如下的内容:
首先我们在注册手势之前就已经为Button们开了后门,已经偷偷注册了Tap手势:
static SAInput() { TouchPanel.EnabledGestures = GestureType.Tap; //默认初始可以处理Tap,用于Button enable = true; }我们添加了一个Button的list:
private static List<SAControl> buttonList = new List<SAControl>();在处理注册了的TouchCollection以及Gesture之前,其实我们已经偷偷地先处理了所有的Button了:
public static void UpdateGesture() { if (enable) { while (TouchPanel.IsGestureAvailable) { GestureSample gestureSample = TouchPanel.ReadGesture(); //先处理预定义Button if (gestureSample.GestureType == GestureType.Tap) { foreach (SAControl b in buttonList) { if (b.IfOnTouch(gestureSample.Position)) { b.OnClick(); break;//ATTENTION 可以在此中删除Button } } } //处理手势事件 foreach (GestureType g in inputDictionary.Keys) { if (gestureSample.GestureType == g) { inputDictionary[g].Invoke(gestureSample); break; //ATTENTION } } } } } public static void UpdateTouchCollection() { if (enable) { touchCollection = TouchPanel.GetState(); //先处理Button if (touchCollection.Count == 1) { foreach (TouchLocation t in touchCollection) { if (t.State == TouchLocationState.Pressed || t.State == TouchLocationState.Moved) { foreach (SAControl b in buttonList) { b.OnTouch(t.Position); } } } } if (OnTouchCollection != null) { OnTouchCollection.Invoke(touchCollection); } } }当然了还有一些无比easy的Button与SAInput的接口:
#region 处理Button public static void AddButton(SAControl button) { buttonList.Add(button); } public static void CleanButton() { buttonList.Clear(); } public static void RemoveButton(SAControl button) { if (buttonList.Contains(button)) { buttonList.RemoveAt(buttonList.IndexOf(button)); } } #endregion以上,这就是Button与SAInput的整合。
Button的第一部分就写到这里,另一部分是关于“文字Button”绘制的内容,敬请期待。另外,终于可以开始写SAInput问题解决篇二了~Yes!
- 打造轻量级Windows Phone7 游戏引擎-Samurai 第四话 Button(上)
- 打造轻量级Windows Phone7 游戏引擎-Samurai 第五话 使用Samurai创建游戏
- 打造轻量级Windows Phone7 游戏引擎-Samurai 第零话 前言
- 打造轻量级Windows Phone7 游戏引擎-Samurai 第二话(上) Inputs 问题提出篇
- 打造轻量级Windows Phone7 游戏引擎-Samurai 第一话 Globals & Music
- 打造轻量级Windows Phone7 游戏引擎-Samurai 第三话SADirector与SAScreen
- 打造轻量级Windows Phone7 游戏引擎-Samurai 第二话(中) Inputs 问题解决篇一
- windows phone7 button控件背景问题
- windows phone7 mango 多人在线游戏
- Windows上编译Torque游戏引擎
- 第一款基于Samurai引擎的WP游戏《MarioJump》成功登陆应用商店~
- 游戏引擎AndEngine总结(八):自定义Button
- Rokon游戏引擎第四讲
- Rokon游戏引擎第四讲
- Windows Phone7 判断程序是否运行在模拟器上
- 利用规则引擎打造轻量级的面向服务编程模式
- 【玩转.Net MF – 06】为Cortex-M3打造轻量级TinyGUI(上)
- Android 上专为视屏直播打造的轻量级弹幕库(100 多 kb)
- iOS编程中——id数据类型
- 素数
- Leetcode:Trapping Rain Water
- 【MySQL】重置MySQL的root密码与修改MySQL默认字符集
- 1.5 几种常见的上下文
- 打造轻量级Windows Phone7 游戏引擎-Samurai 第四话 Button(上)
- 在Linux下adb连接不上android手机的终极解决方案
- POJ A Simple Problem with Integers (Splay 伸展树 入门)
- JBoss 系列二十七:JBoss Data Grid(Infinispan)下载安装和运行
- 小议TCP的MSS(最大分段)以及MTU
- 约瑟夫环问题两解
- 1.6 应用程序的签名
- 关于框架学习的简单笔记
- Cocos2d-x 2.0 -- 从 点,线,面学起