每款MMORPG都有一个主菜单,通常置于窗口的底部。游戏中主角大部分的设置操作都从这里开启。如人物属性、物品(包裹)、技能、任务、队伍、地图、家族、门派、商城、系统设置等等;当然,还包括快捷自定义菜单栏,以及类似《暗黑破坏神》中经典式的左右键快捷技能栏。这些内容在不同的游戏中往往会根据自身的特性稍做调整,但整体上大同小异。本节,我将同样以《剑侠世界》的游戏主界面为例,向家讲解如何在Silverlight中制作一个精美的主菜单面板及鼠标左右键快捷技能栏。
首先,我们需要整出一张主界面的框架素材:
在《剑侠世界》中,该面板从左至右分别是键盘快捷菜单,鼠标左右键快捷菜单及游戏主菜单,那么我首先从右边的主菜单说起。大家可以看到该界面中已经被镂空的部分,这些地方是用来填充相应的菜单按钮的。如果单纯的只是用一张图片来填充,游戏的精致程度将大打折扣;大家回想一下网页中的导航菜单栏,当鼠标进入时它会通过Css或Js变换样式,从而达到突出美化的效果。我们游戏中同样可以采用类似的方案,即当鼠标进入主菜单按钮时,菜单按钮的图片由原先的灰暗切换成明亮,例如:
这是通过两张图片相互切换来实现的按钮突出效果;但在实际开发中,特别是Silverlight这样基于素材时时下载的游戏中,我们需要尽量减少素材资源的数量与容量,因此我特别推荐使用透明高亮遮罩来实现按钮的突出,例如:
当鼠标进入按钮时,我们可以添加一个图标蒙板并使之重叠于按钮图标图片上方,从而同样可以达到高亮突出按钮的效果;不仅如此,通常一款游戏中所有的物品图标和技能图标尺寸是统一的,通过此方式,我们只需一张图标蒙板就能达到所有图标的高亮显示,既大幅节约了素材资源空间,同时也达到了美化效果,一举两得。~嘿嘿
需求都清晰了,该用什么控件来实现呢?当然是QXIcon,不过这次我们得对它进行一些改造,使之目前至少能兼容3种情况:
public enum IconTypes {
/// <summary>
/// 无
/// </summary>
None = 0,
/// <summary>
/// 变换突出
/// </summary>
Transform = 1,
/// <summary>
/// 高亮突出
/// </summary>
Highlights = 2,
}
QXIcon的主要构造如下:
/// <summary>
/// 图标控件
/// </summary>
public QXIcon(IconTypes iconTypes) {
InitializeComponent();
switch (iconTypes) {
case IconTypes.Transform:
this.MouseEnter += delegate { Container.Background = NewSource; };
this.MouseLeave += delegate { Container.Background = _BodySource; };
break;
case IconTypes.Highlights:
Rectangle mask = new Rectangle() { Visibility = Visibility.Collapsed };
Container.Children.Add(mask);
this.MouseEnter += delegate {
mask.Width = this.Width;
mask.Height = this.Height;
mask.Fill = NewSource;
mask.Visibility = Visibility.Visible;
};
this.MouseLeave += delegate { mask.Visibility = Visibility.Collapsed; };
break;
}
}
Brush _BodySource;
/// <summary>
/// 获取或设置图标笔刷
/// </summary>
public Brush BodySource {
get { return _BodySource; }
set { Container.Background = _BodySource = value; }
}
/// <summary>
/// 获取或设置变换后(或蒙板)笔刷
/// </summary>
public Brush NewSource { get; set; }
默认情况下,即参数为IconTypes.None时,该图标控件仅仅是一个带Toolkit的普通图标;当参数为IconTypes.Transform时,图标控件中的图片会根据鼠标进入与离开分别在NewSource和_BodySource之间切换,即前文中提到的第一种情况;而当参数为IconTypes.Highlights时,效果即上叙第二种情况,通过图标蒙板来实现图标的高亮突出。
同时,我也对该QXIcon的Toolkit进行了改造,使之更加新颖别致:
<ToolTipService.ToolTip>
<ToolTip>
<ToolTip.Template>
<ControlTemplate>
<ContentPresenter Content="{TemplateBinding Content}"/>
</ControlTemplate>
</ToolTip.Template>
<ToolTip.Content>
<Canvas x:Name="Dialog">
<Rectangle x:Name="DialogBack" Fill="Black" RadiusX="7" RadiusY="7" Stroke="Gray" StrokeThickness="2" Opacity="0.4" />
<TextBlock x:Name="Details" Foreground="Snow" TextWrapping="Wrap" Width="150" Canvas.Left="5" Canvas.Top="5" />
</Canvas>
</ToolTip.Content>
</ToolTip>
</ToolTipService.ToolTip>
当提示文字发生变化时,它的背景将根据文字的长度与宽度自适应尺寸,且此背景为一个透明度40%的黑色圆角矩形,很漂亮的哦~:
/// <summary>
/// 获取或设置图标悬停提示
/// </summary>
public string Tip {
get { return Details.Text; }
set {
Details.Text = value;
DialogBack.Width = Details.ActualWidth + 10;
DialogBack.Height = Details.ActualHeight + 10;
}
}
最终效果如下:
接下来我们还是利用QXIcon来实现鼠标左右键快捷技能栏,这里我定义左键只负责主角的跑动、对象选中及主角的物理攻击,前面章节中已经全部实现了。而右键则负责施放魔法,此方案应该算是Silverlight的极限了。需求就是当我们在右键快捷按钮图标上点击时,会像《暗黑破坏神》一样,弹出主角已学会(可用)的所有法术和技能。在Silverlight中,我们可以用微软开源工具包中WrapPanel轻松将之实现。WrapPanel是一个可以实现内部控件排列超出限定宽度后自动换行的容器,继承自Panel,和Canvas等容器控件属同一等级,大家可以到http://www.codeplex.com/Silverlight下载最新的版本。本教程示例游戏中,我直接引用它的dll,除它外,该dll还包含其他的一些控件,一方面由于时间有限,我暂时不去单独分离了;另一方面,后期制作中很有可能还会用到其中的某些控件,因此先让它这样吧~,还挺大的呢(112K)。
如果是网络游戏,那么在主角初始化后,我们将从服务器接收到主角的数据,当然包括主角已经掌握的魔法技能,这里我用一个Magic类来表示:
public class Magic {
/// <summary>
/// 代号
/// </summary>
public int Code { get; set; }
/// <summary>
/// 等级
/// </summary>
public int Level { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
}
暂时先就这几个参数吧,接下来定义一个主角可用魔法列表List<Magic> availableMagic = new List<Magic>();这里我假设主角已经掌握了代号为0-5的6种魔法,且每种都到了9级,那么我将这6种魔法按如下方式加入到availableMagic表中:
//初始化主角可用的所有魔法
availableMagic.Clear();
for (int i = 0; i < 6; i++) {
availableMagic.Add(new Magic() {
Code = i,
Level = 9,
Name = string.Format("名称:{0}\r\n描述:{1}",
Super.GetXElement(Data.Settings["Arguments"], "Magic",
"Code", i.ToString()).Attribute("Name").Value,
Super.GetXElement(Data.Settings["Arguments"], "Magic", "Code",i.ToString()).Attribute("Description").Value) });
}
当鼠标在右键快捷按钮图标上点击时,我将availableMagic中的所有对象以图标的形式添加进名为rightButtonMagicList的WrapPanel中,由于此Demo中图标的宽高均为27像素,因此我约束rightButtonMagicList宽为270,即一行如果超过10个图标则自动换行。最后为它赋予相应的事件,当点中某个魔法技能,主角当前的右键默认魔法更改为此魔法,且右键快捷按钮图标的魔法也换成它:
……
//右键魔法选择
rightButtonMagicIcon.MouseLeftButtonDown += delegate(object sender, MouseButtonEventArgs e) {
if (rightButtonMagicList == null) {
rightButtonMagicList = new WrapPanel() {
Orientation = Orientation.Horizontal,
Width = 270, //一行放10个图标,每个27宽
};
foreach (Magic magic in availableMagic) {
QXIcon magicIcon = new QXIcon(IconTypes.Highlights) {
BodySource = new ImageBrush() { ImageSource = Super.GetImage(string.Format("/Image/Magic/{0}/0.png", magic.Code)) },
NewSource = new ImageBrush() { ImageSource = Super.GetImage("/Image/Icon/34.png") },
Width = 27,
Height = 27,
Tip = magic.Name,
Tag = magic,
};
magicIcon.MouseLeftButtonDown += delegate(object ss, MouseButtonEventArgs ee) {
QXIcon icon = ss as QXIcon;
Magic m = icon.Tag as Magic;
rightButtonMagicIcon.BodySource = icon.BodySource;
rightButtonMagicIcon.Tip = m.Name;
Leader.CurrentMagic.Code = m.Code;
Leader.CurrentMagic.Level = m.Level;
rightButtonMagicList.Visibility = Visibility.Collapsed;
ee.Handled = true;
};
rightButtonMagicList.Children.Add(magicIcon);
}
BottomMenu.Children.Add(rightButtonMagicList);
Canvas.SetLeft(rightButtonMagicList, 437); Canvas.SetTop(rightButtonMagicList, -Math.Ceiling(rightButtonMagicList.Children.Count / (rightButtonMagicList.Width / 27)) * 27);
} else {
if (rightButtonMagicList.Visibility == Visibility.Collapsed) {
rightButtonMagicList.Visibility = Visibility.Visible;
Canvas.SetTop(rightButtonMagicList, -rightButtonMagicList.ActualHeight);
} else {
rightButtonMagicList.Visibility = Visibility.Collapsed;
}
}
e.Handled = true;
……
具体逻辑大家仔细看就明白了~还算比较简单的。实际效果如下:
另外的,大家不妨自行修改一下循环代码,多加几个魔法上去体验一下WrapPanel的强大:
游戏主界面菜单已初具雏形,接下来的任务就是去完善它的各面板及相应功能~敬请关注。
源码请到目录中下载,在线演示地址:http://cangod.com