如何在WP7上用XNA写2D游戏(四)
第 3 章 创建xna 游戏菜单
3.1 wp7里xna游戏的触控操作
上一章节,我们了解了制作xna 2d游戏的常用已及使用screenmanage管理场景。可以说对xna 2d游戏有了入门,不过我们玩游戏不会一开始就进入到游戏场景里的,总是会有启动界面,loading界面,然后到了游戏菜单。然后让用户选择“开始”,“继续”,“帮助”等选项,如下图3-1,就是一个常见的游戏界面。
图3-1
和pc上的运行的xna游戏不同,我们在wp7上是靠触摸屏操作的,这和用鼠标操作还是不同的。那么我们就需要先了解wp7里xna的触控操作。
现在的触摸屏手机基本都支持“多点触控”,比如拉伸,缩放,玩过iphone上《愤怒的小鸟》就明白游戏画面可以用两个手指拉伸和缩放。还有很有名的《水果忍者》也是“多点触控”的好游戏,你用三个手指在屏幕上划拉,屏幕上就显出三个爪子印。
当然啦"单点触摸"也是支持的,你用一个指头也能操作的,比如你用一个指头在《愤怒的小鸟》里拉动弹弓。我们就从简单的“单点触控”开始了解吧。
当我们把一个指头在屏幕上操作,可能会有这样三种动作:按,移动,移开。就拿《愤怒的小鸟》里拉动弹弓这个动作,首先我们是按下一个指头,然后向后移动,然后在屏幕上移开这个手指。
那么这三个操作在wp7的xna里如何获取呢?我们就需要了解xna里的touchpanel和touchcollection这两个类。
touchcollection touchstate= touchpanel.getstate();
foreach(touchlocation location in touchstate)
{
switch(location.state)
{
case touchlocationstate.pressed://按下
……
break;
case touchlocationstate.moved://移动
……
break;
case touchlocationstate.released://释放
……
break;
}
}
和触控操作类似的还有叫“手势”的,也算复杂的触控吧。
touchpanel.enabledgestures = gesturetype.freedrag;//用来指定手势,必须要先设定,否则报错
if (touchpanel.enabledgestures != gesturetype.none)
{
switch (touchpanel.readgesture())
{
case gesturetype.tap: //单击
break;
case gesturetype.doubletap://双击
break;
case gesturetype.freedrag://*拖动
break;
case gesturetype.dragcomplete://拖动完成
break;
case gesturetype.flick://轻弹
break;
case gesturetype.hold://按住不动
break;
case gesturetype.horizontaldrag://横向拖动
break;
case gesturetype.none://无手势
break;
case gesturetype.pinch://捏
break;
case gesturetype.pinchcomplete://捏完
break;
case gesturetype.verticaldrag://纵向拖动
break;
}
}
3.2 编写menuentry类
如图3-1每一个菜单界面里都有多个菜单项,为了表示单个菜单项,我们就需要写一个menuentry类。
菜单项可以在屏幕上输出文字来表示,为了美化还可以绘制图片来完成。菜单项一般都是纵向排列的,每个菜单项的坐标都不同,所以有这样两个属性:
/// <summary>
/// 菜单项文本内容
/// </summary>
public string text
{
get { return text; }
set { text = value; }
}
/// <summary>
/// 菜单项文本的位置
/// </summary>
public vector2 position
{
get { return position; }
set { position = value; }
}
由于每个菜单项都会响应点击事件,所以我们需要声明一个事件:
/// <summary>
///菜单项的选中事件
/// </summary>
public event eventhandler selected;
/// <summary>
///选中菜单项函数
/// </summary>
internal void onselectentry()
{
if (selected != null)
{
ispress = true;
}
}
如果菜单项是图片构成的,为了让菜单项有按下动感,我们用图3-2里的三个图片来做一个三帧动画。
图3-2
所以我们要用到四个变量:public texture2d menutexture,showtexture,presstexture,releasetexture;
在菜单项的update函数里要做这样的处理
private timespan duration = timespan.fromseconds(0.4);//菜单动画的执行时间为0.2秒
public void update(bool isselected, gametime gametime)
{
if(ispress)
{
duration -=gametime.elapsedgametime;
if (duration <= timespan.fromseconds(0.2)&& duration > timespan.fromseconds(0.1))
{
showtexture = presstexture; //0.2-0.1秒之间显示三张图片中第二个图片
}
else if (duration<= timespan.fromseconds(0.1) &&duration>timespan.zero)
{
showtexture =eleasetexture;//0.1-0秒间显示三张图片中第三个图片
}
else if (duration <= timespan.zero)
{
selected(this, new playerindexeventargs(playerindex));//菜单项按下动画完成触发selected事件
duration = timespan.fromseconds(0.4);
ispress = false;
}
}
}
3.3 编写menuscreen类
由于菜单界面也是一个特殊的场景,所以menuscreen继承于gamescreen。
如图3-3,menuscreen类结构如下:
图3-3
我们着重分析下handleinput方法:
public override void handleinput(inputhelper input)
{
touchcollection touchstate =touchpanel.getstate();
bool touchdetected = false; //是否触碰菜单
vector2 touchposition = new vector2();
foreach(touchlocation location in touchstate)
{
switch(location.state)
{
case touchlocationstate.pressed:
touchdetected = true;
touchposition = location.position;
break;
case touchlocationstate.moved:
break;
case touchlocationstate.released:
break;
}
}
if(touchdetected)
{
foreach (menuentrymenuentry in menuentries)
{
rectangle touchrect = new rectangle((int)touchposition.x - 5, (int)touchposition.y- 5,10, 10);
rectangle entryrect = new rectangle((int)menuentry.position.x- 5, (int)menuentry.position.y - 5,menuentry.getwidth(this), menuentry.getheight(this));
if(entryrect.intersects(touchrect)) //如果触摸点在菜单项的矩形区域内
menuentry.onselectentry();//触发菜单选中事件。
}
}
………………….
}
3.4编写mainmenuscreen类
对于游戏主菜单界面而言,点击“play” 菜单项意味着要切入到游戏主场景里,点击“exit game”菜单意味着退出游戏,那么就需要在menuscreen扩展游戏场景切换的方法。在前一章节里我们简单介绍了游戏场景的管理类screenmanager,在这里就派上用途了。
首先我们让mainmenuscreen继承于menuscreen:
public class mainmenuscreen:menuscreen
{
}
然后我们添加了如下等方法:
/// <summary>
/// 增加菜单项
/// </summary>
/// <param name="name">菜单文字</param>
/// <param name="screen">点击对应要启动的screen</param>
/// <param name="isexititem">是否是退出菜单</param>
/// <param name="isshowloading">是否显示loading界面</param>
/// <param name="position">菜单的坐标</param>
public void addmainmenuitem(string name, gamescreen screen, bool isexititem, bool isshowloading,vector2 position)
{
this.isshowloading= isshowloading;
menuentry entry = new menuentry(name,screen,isexititem, position);
entry.selected += new system.eventhandler(entry_selected);
menuentries.add(entry);
}
/// <summary>
/// 菜单点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void entry_selected(object sender, eventargs e)
{
menuentry menu = sender as menuentry;
if(menu.isexititem) //如果是退出菜单
{
screenmanager.game.exit();
}
else
{
screenmanager.addscreen(menu.screen,null);
}
}
此外,我们还需要写上inputhelper类,修改上一章节的gamescreen类,screenmanager类,在这两个类里加上对输入的支持。
在gamescreen类里我们加上handleinput方法:
public virtual void handleinput(inputhelper input)
{
}
在screenmanager里我们需要修改update方法,增加对screen的输入响应:
if(!otherscreenhasfocus)
{
screen.handleinput(input);
otherscreenhasfocus = true;
}
具体代码修改请看具体的xnagamesample3这个demo。
然后在game1.cs里的initialize()方法里我们就加上如下代码即可:
mainmenuscreen mainmenu = newmainmenuscreen("castle defense");
gamemainscreen main = new gamemainscreen();
vector2 position = new vector2{ x = 0f, y = 0f };
mainmenu.addmainmenuitem("play", main, false,true, position);
mainmenu.addmainmenuitem("exit", null,true, false,position);
screenmanager.addscreen(mainmenu, null);
最后的代码运行效果如图3-4:
demo 下载地址:/files/wangergo/xnagamesample3.rar
本文版权属于williams所有