C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(四十三)制作游戏主菜单面板及鼠标左右键快捷技能栏

每款MMORPG都有一个主菜单,通常置于窗口的底部。游戏中主角大部分的设置操作都从这里开启。如人物属性、物品(包裹)、技能、任务、队伍、地图、家族、门派、商城、系统设置等等;当然,还包括快捷自定义菜单栏,以及类似《暗黑破坏神》中经典式的左右键快捷技能栏。这些内容在不同的游戏中往往会根据自身的特性稍做调整,但整体上大同小异。本节,我将同样以《剑侠世界》的游戏主界面为例,向家讲解如何在Silverlight中制作一个精美的主菜单面板及鼠标左右键快捷技能栏。

首先,我们需要整出一张主界面的框架素材:

在《剑侠世界》中,该面板从左至右分别是键盘快捷菜单,鼠标左右键快捷菜单及游戏主菜单,那么我首先从右边的主菜单说起。大家可以看到该界面中已经被镂空的部分,这些地方是用来填充相应的菜单按钮的。如果单纯的只是用一张图片来填充,游戏的精致程度将大打折扣;大家回想一下网页中的导航菜单栏,当鼠标进入时它会通过CssJs变换样式,从而达到突出美化的效果。我们游戏中同样可以采用类似的方案,即当鼠标进入主菜单按钮时,菜单按钮的图片由原先的灰暗切换成明亮,例如:

这是通过两张图片相互切换来实现的按钮突出效果;但在实际开发中,特别是Silverlight这样基于素材时时下载的游戏中,我们需要尽量减少素材资源的数量与容量,因此我特别推荐使用透明高亮遮罩来实现按钮的突出,例如:

C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(四十三)制作游戏主菜单面板及鼠标左右键快捷技能栏_第1张图片

    当鼠标进入按钮时,我们可以添加一个图标蒙板并使之重叠于按钮图标图片上方,从而同样可以达到高亮突出按钮的效果;不仅如此,通常一款游戏中所有的物品图标和技能图标尺寸是统一的,通过此方式,我们只需一张图标蒙板就能达到所有图标的高亮显示,既大幅节约了素材资源空间,同时也达到了美化效果,一举两得。~嘿嘿

需求都清晰了,该用什么控件来实现呢?当然是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时,效果即上叙第二种情况,通过图标蒙板来实现图标的高亮突出。

同时,我也对该QXIconToolkit进行了改造,使之更加新颖别致:

        <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;

            }

        }

最终效果如下:

C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(四十三)制作游戏主菜单面板及鼠标左右键快捷技能栏_第2张图片

    接下来我们还是利用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-56种魔法,且每种都到了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中的所有对象以图标的形式添加进名为rightButtonMagicListWrapPanel中,由于此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;

……

具体逻辑大家仔细看就明白了~还算比较简单的。实际效果如下:

C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(四十三)制作游戏主菜单面板及鼠标左右键快捷技能栏_第3张图片

另外的,大家不妨自行修改一下循环代码,多加几个魔法上去体验一下WrapPanel的强大:

C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(四十三)制作游戏主菜单面板及鼠标左右键快捷技能栏_第4张图片

游戏主界面菜单已初具雏形,接下来的任务就是去完善它的各面板及相应功能~敬请关注。

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

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

原文链接: http://www.cnblogs.com/alamiye010/archive/2009/12/08/1619251.html

你可能感兴趣的:(C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(四十三)制作游戏主菜单面板及鼠标左右键快捷技能栏)