游戏中会使用大量的菜单面板,而这些面板往往都带有选项卡。如果用Silverlight工具中的TabControl,则需要通过复杂的xaml重写模板来实现自定义样式,这一点时常让开发者头疼,毕竟界面的东西应该属于美工的范畴,这也是我所发现在目前Silverlight中唯一一处只能通过xaml而无法用代码实现的地方。当然,如果您对此特别感兴趣,同样可以到http://www.codeplex.com/Silverlight中下载最新的开源工具源码,其中的示例项目中有非常详细的模板重写案例。本节,我将通过创建用户控件的方式来创建自定义的TabControl和RepeatButton,实现主角属性面板及其中的属性加点器。
首先,我创建一个QXTabControl用户控件,该控件界面可以很简单,只需要包含一个头和一个身体即可:
<UserControl x:Class="QXGameEngine.Control.QXTabControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas>
<StackPanel x:Name="Head"/>
<ContentPresenter x:Name="Body"/>
</Canvas>
</UserControl>
Head 用做TabItem的容器,作为StackPanel类型控件,它可以对内部控件进行横排或竖排,从而基本满足大多数情况需要;而Body是ContentPresenter类型,在第四十二节中我曾提及过它,相当于一个万用变身控件,将它作为选项卡的身体部分再合适不过了。当我们点击不同的选项卡时,将不同的面板控件作为值赋予给Body,轻松实现高度自由的选项内容变换。
那么用什么控件来替代TabItem呢?在《剑侠世界》中,选项卡做得别具特色,不光在默认情况下鼠标进入与离开会呈现不同的图片;当被点中后,同样实现另外的两张图片间的切换。这样的效果相当精致,而我们又该如何将它实现呢?此时,不得不想到我们可爱的QXIcon控件,我为它添加了新的类型:IconTypes.HitModes,定义很简单,根据该控件是否被点击从而对4张图片进行相应切换:
case IconTypes.HitModes:
this.MouseEnter += (s, e) => { Container.Background = Hit ? HitNewBodySource : NewSource; };
this.MouseLeave += (s, e) => { Container.Background = Hit ? HitBodySource : _BodySource; };
this.MouseLeftButtonDown += (s, e) => { Hit = Hit ? false : true; };
break;
实现后的效果如下图:
在QXTabControl中包含一个List<QXIcon> tabItemList = new List<QXIcon>();用于管理现有的所有选项卡;我还模仿TabControl在QXTabControl中创建SelectionChanged事件:
public delegate void SelectionChangedEventHandler(object sender, QXIcon tabItem);
public event SelectionChangedEventHandler SelectionChanged;
当某个选项卡被点击时,触发该事件:
……
tabItem.MouseLeftButtonDown += (sender, e) => {
foreach (QXIcon icon in tabItemList) {
if (icon == sender) {
icon.Hit = true;
icon.Container.Background = icon.HitNewBodySource;
} else {
icon.Hit = false;
icon.Container.Background = icon.BodySource;
}
}
SelectionChanged(this, sender as QXIcon);
e.Handled = true;
};
……
这样我们就可以在游戏窗口中对已创建的选项卡控件注册SelectionChanged事件了:
//主角属性选项卡内容
QXTabControl tc = new QXTabControl() {
TabItemOrientation = Orientation.Horizontal,
TabItemHeight = 28,
BodyLeft = -4,
BodyTop = 27
};
//添加4个选项卡
tc.AddItem(63, 28, 1, "/Image/Icon/39.png", "/Image/Icon/40.png", "/Image/Icon/41.png", "/Image/Icon/42.png", "属 性");
tc.AddItem(63, 28, 1, "/Image/Icon/39.png", "/Image/Icon/40.png", "/Image/Icon/41.png", "/Image/Icon/42.png", "声 望");
tc.AddItem(63, 28, 1, "/Image/Icon/39.png", "/Image/Icon/40.png", "/Image/Icon/41.png", "/Image/Icon/42.png", "称 号");
tc.AddItem(63, 28, 1, "/Image/Icon/39.png", "/Image/Icon/40.png", "/Image/Icon/41.png", "/Image/Icon/42.png", "荣 誉");
……
tc.SelectionChanged += (sender, item) => {
QXTabControl tabControl = sender as QXTabControl;
switch (item.Text) {
case "属 性":
tabControl.SetBody(leaderAttributePart);
break;
case "声 望":
tabControl.SetBody(new Canvas() {
Background = new ImageBrush() { ImageSource = Super.GetImage("/Image/Plate/RoleAttributeBack1.png") },
Width = 350,
Height = 389,
});
break;
case "称 号":
tabControl.SetBody(new Canvas() {
Background = new ImageBrush() { ImageSource = Super.GetImage("/Image/Plate/RoleAttributeBack2.png") },
Width = 350,
Height = 389,
});
break;
default:
tabControl.SetBody(null);
break;
};
};
运行时效果:
很棒吧?嘿嘿。在角色属性面板里除了显示角色的属性值等个人资料外,还有装备管理及属性加点器两个重要部分。关于装备,后面的章节再细说。下面我向大家讲讲如何制作这个属性加点器。
如果不论样式,我们直接可以使用官方提供的NumericUpDown控件即可,该控件非常强大,看了它的源码,其本身为一个组合控件,由4大部分组成:文本(TextBlock)、文本容器(ContentPresenter)、加按钮(RepeatButton)及减按钮(RepeatButton),且模式很多,你能想到的基本都有。当然同样的,要重写它的样式实在是麻烦之事,其实该控件的重点就就在RepeatButton上,如何实现这个Repeat动作又是关键中的关键。我们不妨从它的原理出发,当鼠标在此按钮上按下时开始计时,如果鼠标一直未放开,则当到达预先设定的Delay时间间隔后即触发后面的连续重复动作,且这些动作以Interval为间隔不断重复下去直到鼠标左键被放开或鼠标离开该控件。此时,我又想到了美丽的QXIcon,再次为它添加一种新模式:IconTypes.RepeatButton:
case IconTypes.RepeatButton:
DispatcherTimer timer = new DispatcherTimer();
timer.Tick += (s, e) => { timer.Interval = TimeSpan.FromMilliseconds(Interval); RepeatClick(this, e); };
this.MouseEnter += (s, e) => { Container.Background = NewSource; };
this.MouseLeave += (s, e) => { Container.Background = _BodySource; timer.Stop(); };
this.MouseLeftButtonDown += (s, e) => { timer.Interval = TimeSpan.FromMilliseconds(Delay); timer.Start(); };
this.MouseLeftButtonUp += (s, e) => { timer.Stop(); e.Handled = true; };
break;
根据前面对RepeatButton工作原理的描述,在这种模式下,我通过创建一个DispatcherTimer,当它Tick时触发public event EventHandler RepeatClick;事件。其中配合控件自身的 MouseLeftButtonDown、MouseLeftButtonUp及MouseLeave来开停Timer及设置它的间隔。
接着,我们就可以将此控件应用到主角属性面板中制作属性加点器了。配合上相应逻辑,当属性点数加完并提交后,主角的新属性值会立即更新反映到界面中。按照第二十八节的属性设置,主角拥有5大基本属性,当修改这些属性时会分别影响相关的值数据。例如,默认情况下主角的智慧为30,魔法攻击基本伤害范围为460-615(不包括魔法自身的攻击力),此时用激光魔法攻击敌人可造成约600左右伤血:
而当我将智慧加到200并点击确定后,魔法攻击到了3010-4015,此时攻击敌人可以造成3500左右的伤害,很酷吧。嘿嘿:
本教程示例游戏中,我为主角赋予了1000点的潜能点,大家可以自由分配到不同的属性上,例如增加力量属性可以增加物理攻击力,增加体格可以提升血上限及防御等,增加敏捷可以加快移动及施法速度等,增加幸运可以提高暴击率等等,测试起来还是相当有趣的呢~
不过目前的属性加点器还不能通用,毕竟不同的游戏中加点器的实现都有差异。例如有些只有加没有减,每次点击都会直接提交,这种处理最简单;而有的每次加点都会直接反应到界面上,且中途如果不满意取消后又会恢复原样,这种模式做起来相对复杂些,需要一些临时字段来存储数据,只有提交后才更新到服务器。本节功能上我选择了折中的处理方式。
游戏中的面板基本上大同小异,前面章节中的对象监视面板、雷达地图面板、寻路地图面板、主角属性面板的制作基本上含盖了大多数情况,后面的章节我将不再围绕面板这个罗嗦的话题了,打算将重心放到装备、物品、技能存放与拖动的实现方面,敬请关注。
本节源码请到目录中下载,在线演示地址:http://silverfuture.cn
出处:http://alamiye010.cnblogs.com/
教程目录及源码下载:点击进入(欢迎加入WPF/Silverlight小组 WPF/Silverlight博客团队)
本文版权归作者和博客园共有,欢迎转载。但未经作者同意必须保留此段声明,且在文章页面显著位置给出原文连接,否则保留追究法律责任的权利。