引言

征服玩家的不仅仅是创意,无比动人的视觉体验譬如精美的界面UI同样能让人倾慕,辅以优柔的旋律仿若一缕思绪让您身临其境而流连。深刻的第一印象无限大的冲击着玩家那份内敛的狂热,优秀的游戏作品价值将在欢呼声中被最大化激活。

9.1创建自适应布局之HUD (交叉参考:一切起源于这个真实的世界 制作精美的Mini地图① 制作精美的Mini地图② 制作游戏主菜单面板及鼠标左右键快捷技能栏)

在Silverlight网页游戏中一切看得见的对象我们都可称之为UI,如精灵、魔法、地图、图标等等。除此之外作为玩家,我们时常需要以“上帝”的名义通过一些按钮去影响游戏中的对象,并获取相应反馈。于是乎UI的另一位伟大成员诞生了,它就是HUD。每款游戏都拥有它独自的一套HUD,本节Demo中我将借用《十年一剑》的相关素材,大致包括主角信息、对象信息、雷达地图、聊天窗口、工具菜单等;它们各自布局于游戏窗口顶层的边缘,并以聊天中提交主角说话内容为例向大家讲解如何实现HUD与游戏中其他对象的交互。

首先我们需要在控件项目中针对HUD的每个部分创建相应的面板类:

它们均继承之Canvas,由于都以UI描述性语句为主,代码大同小异,因此这里我选择以ChatWindow为例向大家进一步作讲解:

   /// <summary>
    /// 聊天窗口面板
    /// </summary>
    public sealed class ChatWindow : Canvas {

/// <summary>
        /// 获取或设置X、Y坐标
        /// </summary>
        public Point Coordinate {
            get { return new Point(Canvas.GetLeft(this), Canvas.GetTop(this)); }
            set { Canvas.SetLeft(this, value.X); Canvas.SetTop(this, value.Y); }
        }

/// <summary>
        /// 获取或设置Z层次深度
        /// </summary>
        public int Z {
            get { return Canvas.GetZIndex(this); }
            set { Canvas.SetZIndex(this, value); }
        }

/// <summary>
        /// 发送说话内容
        /// </summary>
        public event EventHandler<SendEventArgs> Send;

/// <summary>
        /// 说话参数
        /// </summary>
        public sealed class SendEventArgs : EventArgs {
            public string Content { get; set; }
        }

TextBox textBox = new TextBox() {
            Text = "http://Silverfuture.cn 深蓝色右手",
            Width = 185,
            AcceptsReturn = false,
            Foreground = new SolidColorBrush(Colors.White),
            Background = new SolidColorBrush(Colors.Transparent)
        };
        Image image = new Image() { Source = Global.GetProjectImage("HUD/3.png") };
        public ChatWindow() {
            this.Width = 307;
            this.Height = 243;
            this.Background = new ImageBrush() { ImageSource = Global.GetProjectImage("HUD/4.png") };
            this.Children.Add(textBox); Canvas.SetLeft(textBox, 78); Canvas.SetTop(textBox, 212);
            textBox.KeyDown += (s, e) => {
                if (e.Key == Key.Enter) {
                    if (Send != null) { Send(this, new SendEventArgs() { Content = textBox.Text.Trim() }); }
                    textBox.Text = string.Empty;
                    e.Handled = true;
                }
            };
            this.Children.Add(image); Canvas.SetLeft(image, 272); Canvas.SetTop(image, 208);
            image.MouseLeftButtonDown += (s, e) => {
                if (Send != null) { Send(this, new SendEventArgs() { Content = textBox.Text.Trim() }); }
                textBox.Text = string.Empty;
                e.Handled = true;
            };
        }

}

这是一个仅仅包含背景的聊天窗口,为了实现它与游戏主角之间的交互,我在其中还特意放置了一个文本框及图形按钮,当按钮点击时触发Send事件:

然后在MainPage中为该聊天面板实例注册Send事件:

            chatWindow.Send += (s, e) => {
                hero.Say(e.Content);
            };

主角将执行说话行为(Say):

        /// <summary>
        /// 说话
        /// </summary>
        public void Say(string content) {
            if (content.Equals("")) { return; }
            Dialog dialog = new Dialog(5) {
                HostWidth = BodyWidth,
                TopOffset = 30
            };
            this.Children.Add(dialog);
            dialog.Completed += new EventHandler(dialog_Completed);
            dialog.Show(content);
        }

void dialog_Completed(object sender, EventArgs e) {
            Dialog dialog = sender as Dialog;
            dialog.Completed -= dialog_Completed;
            this.Children.Remove(dialog);
        }

其中Dialog是本节中我新建的RPG游戏中标准的说话小窗口控件类,通过为其内置一个DispatcherTimer实现该聊天小窗口定时消失:

代码

    /// <summary>
    /// 说话内容框
    /// </summary>
    public sealed class Dialog : Canvas {

#region 属性

/// <summary>
        /// 设置属寄主宽
        /// </summary>
        public double HostWidth {
            set { Canvas.SetLeft(this, (value - width) / 2); }
        }

/// <summary>
        /// 获取或设置具体头部偏移量
        /// </summary>
        public int TopOffset { get; set; }

#endregion

#region 构造

const int width = 140;
        Rectangle back = new Rectangle() {
            Width = width,
            Fill = new SolidColorBrush(Colors.Black),
            Stroke = new SolidColorBrush(Colors.Gray),
            StrokeThickness = 2,
            RadiusX = 7,
            RadiusY = 7,
            Opacity = 0.4
        };
        TextBlock content = new TextBlock() {
            Width = width - 10,
            Foreground = new SolidColorBrush(Colors.White),
            TextWrapping = TextWrapping.Wrap
        };

DispatcherTimer dispatcherTimer = new DispatcherTimer();
        /// <param name="duration">持续时间</param>
        public Dialog(int duration) {
            this.Children.Add(back);
            this.Children.Add(content);
            Canvas.SetLeft(content, 5); Canvas.SetTop(content, 5);
            dispatcherTimer.Interval = TimeSpan.FromSeconds(duration); 
            dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
        }

void dispatcherTimer_Tick(object sender, EventArgs e) {
            Hide();
            if (Completed != null) { Completed(this, new EventArgs()); }
        }

#endregion

#region 事件

/// <summary>
        /// 说话内容显示后
        /// </summary>
        public event EventHandler Completed;

#endregion

#region 方法

/// <summary>
        /// 显示说话框
        /// </summary>
        /// <param name="value">内容</param>
        public void Show(string value) {
            content.Text = value;
            back.Height = content.ActualHeight + 10;
            Canvas.SetTop(this, -back.Height + TopOffset);//减去精灵名字高度
            dispatcherTimer.Start();
        }

/// <summary>
        /// 隐藏说话框
        /// </summary>
        public void Hide() {
            content.Text = string.Empty;
            dispatcherTimer.Stop();
            dispatcherTimer.Tick -= dispatcherTimer_Tick;
        }

#endregion
    }

概括来说,委托和事件是C#中解耦最强有力的工具,通过它我们实现了HUD与其他游戏对象之间的双向交互。

另外,游戏中的HUD各部分布局必须随着Silverlight游戏窗口尺寸的改变而改变,因此我们需要在游戏窗口尺寸改变事件方法中添加对这些部分的自适应布局方案:

        /// <summary>
        /// 游戏窗口尺寸改变
        /// </summary>
        void Content_Resized(object sender, EventArgs e) {
            hero_CoordinateChanged(hero, new DependencyPropertyChangedEventArgs());
            if (transition.Visibility == Visibility.Visible) { transition.AdaptToWindowSize(); }
            //HUD各部件自适应窗体尺寸
            Canvas.SetLeft(targetInfo, Application.Current.Host.Content.ActualWidth / 2 - targetInfo.ActualWidth / 2); Canvas.SetTop(targetInfo, 0);
            Canvas.SetLeft(radarMap, Application.Current.Host.Content.ActualWidth - radarMap.ActualWidth); Canvas.SetTop(radarMap, 0);
            Canvas.SetLeft(menuBar, Application.Current.Host.Content.ActualWidth - menuBar.ActualWidth); Canvas.SetTop(menuBar, Application.Current.Host.Content.ActualHeight - menuBar.ActualHeight);
            Canvas.SetTop(chatWindow, Application.Current.Host.Content.ActualHeight - chatWindow.ActualHeight);
        }

以HUD各部分自己的宽、高以及游戏窗体的宽、高为依据进行边缘布局;如此,我们不论是拉伸窗体、最大化窗体、全屏还是OOB模式时,HUD将永远能保持在相对正确的位置上。

除此之外,如果游戏窗口尺寸小到一定程度后HUD中各面板会出现重叠,如果游戏中不允许出现这类现象解决方案大致有两种:

1)将HUD中的所有面板均设定成可拖动窗体;

2)在游戏窗口尺寸改变事件中检测新尺寸是否为最低限制,如果超出的取消新尺寸改变。

最后,我为游戏额外添加一个按钮用作“全屏/窗口”模式的切换以进一步测试HUD的自适应性:

代码

            button0.Click += (s, e) => {
                if (Application.Current.Host.Content.IsFullScreen) {
                    Application.Current.Host.Content.IsFullScreen = false;
                } else {
                    Application.Current.Host.Content.IsFullScreen = true;
                }
                Content_Resized(null, null);
            };

9.2场景之背景音乐

游戏的趣味性将伴随着恢弘磅礴的背景音乐无限延伸,Silverlight中自带的MediaElement控件已无法满足我们对游戏音乐的多方面操控,因而需要对其重新进行了封装,取名为MusicPlayer:

    /// <summary>
    /// 媒体播放器控件
    /// </summary>
    public sealed class MediaPlayer : Canvas {

#region 构造

MediaElement media = new MediaElement() {
            IsHitTestVisible = false,
            Visibility = Visibility.Collapsed,
            AutoPlay = true,
        };

public MediaPlayer() {
            this.Children.Add(media);
        }

#endregion

#region 属性

/// <summary>
        /// 获取或设置音量
        /// </summary>
        public double Volume {
            get { return media.Volume; }
            set { media.Volume = value; }
        }

#endregion

#region 方法

/// <summary>
        /// 播放媒体
        /// </summary>
        /// <param name="uri">路径</param>
        /// <param name="loop">是否循环</param>
        public void Play(string uri, bool loop) {
            media.Source = new Uri(Global.WebPath(string.Format("Media/{0}.mp3", uri)), UriKind.Relative);
            media.Position = TimeSpan.Zero;
            Start(loop);
        }

/// <summary>
        /// 媒体开始
        /// </summary>
        public void Start(bool loop) {
            media.MediaEnded -= media_MediaEnded;
            if (loop) { media.MediaEnded += media_MediaEnded; }
            media.Play();
        }

/// <summary>
        /// 媒体停止
        /// </summary>
        public void Stop() {
            media.MediaEnded -= media_MediaEnded;
            media.Stop();
        }

void media_MediaEnded(object sender, RoutedEventArgs e) {
            MediaElement media = sender as MediaElement;
            media.Position = TimeSpan.Zero;
            media.Play();
        }

#endregion

}

该播放器是一个简单实现,可以实现循环,同时使用起来也很简单:

music.Play(Media, true);

以场景为单位,通过在它们的配置文件中添加参数完全可以实现独立于场景的不同背景音乐,肆意张扬个性的同时大可放心,这些媒体文件都是以数据流的形式逐步获取,无须任何等待及预加载。强大的Silverlight框架为我们铺垫了一切。

本课小结:游戏界面与游戏音乐相辅相成,能否运筹帷幄关系到一款游戏产品的成与败毫不言过。十数年的经验告诉我们倾注设计者灵魂的游戏产品都将成就伟大史诗,选择进步亦或倒退??期待让世界热血沸腾那一刻的降临!

本课源码:点击进入目录下载

梦想起航:第一届Silverlight游戏开发者论坛

参考资料:中游在线[WOWO世界] Silverlight C# 游戏开发:游戏开发技术

教程Demo在线演示地址http://cangod.com

Silverlight MMORPG网页游戏开发课程[一期] 第九课:HUD与背景音乐相关推荐

  1. Silverlight MMORPG网页游戏开发课程[一期] 序言

    本课程Demo在线演示地址:http://silverfuture.cn/ (随课程进度不断更新,所有资源暂时放在xap) 2010年7月20日是一个不平凡的日子,应朋友的邀请在自己的家乡广西师范大学 ...

  2. Silverlight MMORPG网页游戏开发课程[一期] 第十课:面向对象的重构

    引言 在游戏的基本功能大体实现后适当的回过头来,重新审视当下的游戏框架并做一些有利于下阶段功能延伸的结构改进,以达到精简代码,优化性能,提高拓展性的目的:这就是本节我将要为课程示例游戏做的一次内科大手 ...

  3. Silverlight MMORPG网页游戏开发课程[一期] 第十一课:战斗系统之脚本与精灵捕获...

    引言 整个游戏的推进工作显得异常顺利,或许之前我们所做的一切都是为了接下来的核心内容打基础:RPG之战斗系统.在踏入这块神秘而又让人的垂涎的领域前,我们迫切需要充实更多的相关知识以适应并更好的处理即将 ...

  4. Silverlight MMORPG网页游戏开发课程(Game Lesson):目录

    [感谢 银光中国 提供本课程所有源码资源分流] 一次全新的开始,一次全新的构建与诠释.渴望在时空长廊中寻觅传说中光的起源,我决定用这部课程编写一首血脉喷张之进行曲.一年前,我一个人在战斗,前行,引导我 ...

  5. 走在网页游戏开发的路上(六)

    Flash动画原理 --动画是将静止的画面变为动态的艺术.实现由静止到动态,主要是靠人眼的视觉残留效应.利用人的这种视觉生理特性可制作出具有高度想象力和表现力的动画影片. 0.  前言 像所有的动画显 ...

  6. 走在网页游戏开发的路上(十)

    页游资源管理 现在页游的规模越来越来大,游戏内容丰富,资源管理变得很重要.现在一款SNS页游的所有资源可达50M,MMO页游更高达几百M,不可能把资源放到一个文件里面.也不可能一次性加载完所有资源.按 ...

  7. 走在网页游戏开发的路上——页游资源管理

    本文原创版权归 博客园 吴秦 所有,如有转载,请按如下方式详细标明原创作者及原文出处,以示尊重! 作者:吴秦 出处:http://www.cnblogs.com/skynet/ 本文基于署名 2.5 ...

  8. 走在网页游戏开发的路上

    本文原创版权归 博客园 吴秦 所有,如有转载,请按如下方式详细标明原创作者及原文出处,以示尊重! 作者:吴秦 出处:http://www.cnblogs.com/skynet/ 本文基于署名 2.5 ...

  9. Cocos2d-x入门(计算机游戏开发课程笔记)

    Cocos2d-x入门(计算机游戏开发课程笔记) 文章目录 Cocos2d-x入门(计算机游戏开发课程笔记) 一. 环境搭建 1. 查看版本 2. 创建项目 二. 基础架构 1.命名空间 2. 程序入 ...

最新文章

  1. 背包问题 贪心算法 java_JS基于贪心算法解决背包问题
  2. OpenGL.Vertex Array Object (VAO).
  3. (软考中级--信息安全工程师)三、密码学基本理论
  4. 皮尔逊相关系数和斯皮尔曼相关系数
  5. c语言课程设计管理系统的设计,c语言课程设计-学生管理信息系统设计.doc
  6. win7做ftp服务器是否稳定,win7可以做ftp服务器吗
  7. Arcgis中怎样设置调查路线线型(带箭头的虚线),附带1:1万地形图符号库
  8. 支付宝扫码转银行卡技术/隐藏部分卡号
  9. uniapp(H5) + signalr 制作的简单的卡牌游戏
  10. Windows提权基础:信息收集技巧及可用漏洞搜索
  11. 前端必会的 HTML+CSS 常用技巧 之 虚线的实现方式
  12. 2021-08-23 FM24C04写入数据时,跨页会导致指针指向本页的起始地址
  13. css纯代码实现圆边框和圆按钮
  14. 8.解析Kafka中的 Topic 和 Partition
  15. 为什么推荐这款固定资产管理软件?
  16. Vid2Vid:Video-to-Video Synthesis
  17. 使用IDM解决FTP下载缓慢问题
  18. OpenGL入门学习笔记(一)——简单实现FFT海洋
  19. 税控服务器 TC5002UpdatePackage 安装更新
  20. 《带你游校园》教学设计

热门文章

  1. Golang并发读取超大文件
  2. linux下golang编译环境搭建
  3. 深入理解BitMap
  4. 分库分表解决方案之ShardingSphere
  5. Spring AOP核心原理分析
  6. python的本地包下载地址
  7. 阿里研究院入选中国企业智库系统影响力榜 1
  8. VS C++/ClI调用C++ 外部Dll无法查看变量值
  9. 数据库只有mdf文件而没有ldf文件,如何恢复数据库
  10. 部分 I. 教程_第 2 章 SQL语言_2.2. 概念