游戏中会使用大量的菜单面板,而这些面板往往都带有选项卡。如果用Silverlight工具中的TabControl,则需要通过复杂的xaml重写模板来实现自定义样式,这一点时常让开发者头疼,毕竟界面的东西应该属于美工的范畴,这也是我所发现在目前Silverlight中唯一一处只能通过xaml而无法用代码实现的地方。当然,如果您对此特别感兴趣,同样可以到http://www.codeplex.com/Silverlight中下载最新的开源工具源码,其中的示例项目中有非常详细的模板重写案例。本节,我将通过创建用户控件的方式来创建自定义的TabControlRepeatButton,实现主角属性面板及其中的属性加点器。

首先,我创建一个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类型控件,它可以对内部控件进行横排或竖排,从而基本满足大多数情况需要;而BodyContentPresenter类型,在第四十二节中我曾提及过它,相当于一个万用变身控件,将它作为选项卡的身体部分再合适不过了。当我们点击不同的选项卡时,将不同的面板控件作为值赋予给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>();用于管理现有的所有选项卡;我还模仿TabControlQXTabControl中创建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;事件。其中配合控件自身的 MouseLeftButtonDownMouseLeftButtonUpMouseLeave来开停Timer及设置它的间隔。

接着,我们就可以将此控件应用到主角属性面板中制作属性加点器了。配合上相应逻辑,当属性点数加完并提交后,主角的新属性值会立即更新反映到界面中。按照第二十八节的属性设置,主角拥有5大基本属性,当修改这些属性时会分别影响相关的值数据。例如,默认情况下主角的智慧为30,魔法攻击基本伤害范围为460-615(不包括魔法自身的攻击力),此时用激光魔法攻击敌人可造成约600左右伤血:

而当我将智慧加到200并点击确定后,魔法攻击到了3010-4015,此时攻击敌人可以造成3500左右的伤害,很酷吧。嘿嘿:

本教程示例游戏中,我为主角赋予了1000点的潜能点,大家可以自由分配到不同的属性上,例如增加力量属性可以增加物理攻击力,增加体格可以提升血上限及防御等,增加敏捷可以加快移动及施法速度等,增加幸运可以提高暴击率等等,测试起来还是相当有趣的呢~

不过目前的属性加点器还不能通用,毕竟不同的游戏中加点器的实现都有差异。例如有些只有加没有减,每次点击都会直接提交,这种处理最简单;而有的每次加点都会直接反应到界面上,且中途如果不满意取消后又会恢复原样,这种模式做起来相对复杂些,需要一些临时字段来存储数据,只有提交后才更新到服务器。本节功能上我选择了折中的处理方式。

游戏中的面板基本上大同小异,前面章节中的对象监视面板、雷达地图面板、寻路地图面板、主角属性面板的制作基本上含盖了大多数情况,后面的章节我将不再围绕面板这个罗嗦的话题了,打算将重心放到装备、物品、技能存放与拖动的实现方面,敬请关注。

本节源码请到目录中下载,在线演示地址:http://silverfuture.cn

WPF/Silverlight
作者:深蓝色右手
出处:http://alamiye010.cnblogs.com/
教程目录及源码下载:点击进入(欢迎加入WPF/Silverlight小组 WPF/Silverlight博客团队)
本文版权归作者和博客园共有,欢迎转载。但未经作者同意必须保留此段声明,且在文章页面显著位置给出原文连接,否则保留追究法律责任的权利。
posted on 2009-12-17 17:25  深蓝色右手  阅读(5860)  评论(16编辑  收藏  举报