Silverlight 2.5D RPG游戏技巧与特效处理:(十五)任务剧情

  任务是贯穿游戏剧情发展的核心线索,具有极强的多元性、组合性、循环性与随机性;它的设计原则浓缩起来便是:触发-执行-完成。别小看这短短6个字,里面的学问可大了,由什么触发、如何触发的,因素很多;怎样执行、什么样的过程,一切随便;怎么算完成,完成后的奖励是啥,什么都行。而不同的故事背景、不同的操作玩法,在任务设计方面都会大相径庭。比如RPG游戏,角色扮演即是虚拟人生,需要还原一个完整而虚幻的世界,因此它的任务系统通常会被设计得极其丰富,可以比喻:人生有多复杂,RPG的任务系统就有多庞大;又比如SLG游戏,大致以独立的场景为线索进行剧情串联,因而任务系统设计起来便相对简单得多;除此之外还有RTS、FPS等类型游戏,它们的侧重点在于策略和战斗操控,通常只需面对面的对话、走到指定地点或杀掉某个对象便可触发/完成剧情任务,被视为最简单的任务模式之一。由此可见,不同类型游戏,所强调的核心玩法及需要表达的世界观都不同,作为游戏进展的纽带,任务系统也需酌情设计。

  因此,MMORPG的任务系统确实非常非常复杂,一款成功的MMORPG离不开其背后优秀的任务系统,我们需要不断借鉴与微创新,比如《任务系统设计思路》、《游戏任务多样化分析》等在分析魔兽世界的任务系统方面都具有很好的参考价值,网上还能搜罗到相当多的类似资料,作为策划的主要工作,我们暂且就不掺和了。

  本节,我只想将其中一个微小却又极具传统韵味的分支:任务剧情功能实现呈现给大家;通过它,最终将游戏中的所有角色都关联了起来,作为庞大而复杂的任务系统的开端,今后若有充足时间我会陆续一一补充。

  剧情任务通常是游戏的主线,尤其在单机游戏中。玩家可能不会记得为哪个工匠打了把刀,为哪个NPC杀了些怪,但是肯定会记住游戏中那些贯穿始终的儿女情长,乱世纷争。于是模仿经典的武侠网游《剑侠世界》的剧情功能设计,我们同样可以在Silverlight中制作出独具特色的剧情写实功能:

  以上是最终效果截图,我将剧情对话控件由上至下分成功能窗口、特写窗口和剧情窗口;通过一些渐进渐出的动画,每次与NPC对话时触发剧情都会平缓的隐藏掉UI并突出剧情界面,此类特写便是让玩家更加重视主线内容的重要手法。剧情的呈现作为单独的一个控件,其中集成了阻断向下路由的鼠标操作,同时也需要处理好游戏画面的层次感让玩家更有身临其境之感觉,即便是在战斗中,如触发了剧情,游戏世界的时间(循环)依旧运转而不会因为你的私事造成一丝滞留:

/// <summary>

/// 剧情对话板

/// </summary>

public sealed class DramaDialogue : UIBase {

/// <summary>

/// 显示时触发

/// </summary>

public event EventHandler Showing;

/// <summary>

/// 消失时触发

/// </summary>

public event EventHandler Hiding;

Rectangle topRect = new Rectangle() { Fill = new ImageBrush() { ImageSource = GlobalMethod.GetImage("UI/DramaLine.jpg", UriType.Project), Stretch = Stretch.Fill } };

Rectangle middleRect = new Rectangle() { Fill = new ImageBrush() { ImageSource = GlobalMethod.GetImage("UI/GrayRect.png", UriType.Project), Stretch = Stretch.Fill } };

Rectangle bottomRect = new Rectangle() { Fill = new ImageBrush() { ImageSource = GlobalMethod.GetImage("UI/DramaLine.jpg", UriType.Project), Stretch = Stretch.Fill } };

Canvas topCanvas = new Canvas();

Canvas bottomCanvas = new Canvas();

EntityObject closer = new EntityObject() { Source = GlobalMethod.GetImage("UI/EndDialogue.png", UriType.Project) };

TextBlock tip = new TextBlock() { Text = "【按Esc退出对话】", Foreground = new SolidColorBrush(Colors.Orange), FontSize = 18, TextWrapping = TextWrapping.Wrap };

TextBlock content = new TextBlock() { Foreground = new SolidColorBrush(Colors.White), FontSize = 24, TextWrapping = TextWrapping.Wrap };

EntityObject avatar = new EntityObject() { Source = GlobalMethod.GetImage("UI/Avatar0.png", UriType.Project) };

DispatcherTimer timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(30) };

/// <summary>

/// 是否正在显示

/// </summary>

public bool IsShowing { get; private set; }

/// <summary>

///
获取或设置剧情播放速度

/// </summary>

public int DialogSpeed {

get { return timer.Interval.Milliseconds; }

set { timer.Interval = TimeSpan.FromMilliseconds(value); }

}

public DramaDialogue() {

this.Children.Add(topCanvas);

topCanvas.Children.Add(topRect);

topCanvas.Children.Add(closer); Canvas.SetTop(closer, 10);

this.Children.Add(middleRect);

this.Children.Add(bottomCanvas);

bottomCanvas.Children.Add(bottomRect);

bottomCanvas.Children.Add(tip); Canvas.SetLeft(tip, 15); Canvas.SetTop(tip, 65);

bottomCanvas.Children.Add(content); Canvas.SetTop(content, 10);

bottomCanvas.Children.Add(avatar); Canvas.SetLeft(avatar, 10);

timer.Tick += delegate {

countText++;

if (countText > drama.Content[countDrama].Length) {

if (timer.IsEnabled) {

timer.Stop();

countDrama++;

}

} else {

content.Text = drama.Content[countDrama].Substring(0, countText);

}

};

this.MouseLeftButtonDown += (s, e) => {

if (IsShowing) {

Point p = e.GetPosition(closer);

if (p.X >= 0 && p.X <= closer.RealWidth && p.Y >= 0 && p.Y <= closer.RealHeight) { Hide(); e.Handled = true; return; }

if (timer.IsEnabled) {

content.Text = drama.Content[countDrama];

timer.Stop();

countDrama++;

} else {

if (countDrama == drama.Content.Count) {

Hide();

} else {

countText = 0;

avatar.Source = GlobalMethod.GetImage(string.Format("UI/Avatar{0}.png", drama.Avatar[countDrama]), UriType.Project);

timer.Start();

}

}

}

e.Handled = true;

};

}

Drama drama;

int countText = 0;

int countDrama = 0;

public void Show(Drama drama) {

IsShowing = true;

if (Showing != null) { Showing(this, null); }

this.drama = drama;

avatar.Source = GlobalMethod.GetImage(string.Format("UI/Avatar{0}.png", drama.Avatar[countDrama]), UriType.Project);

Storyboard storyboard = new Storyboard();

Duration duration = TimeSpan.FromMilliseconds(500);

PowerEase powerEase = new PowerEase() { EasingMode = EasingMode.EaseOut };

storyboard.Children.Add(GlobalMethod.CreateDoubleAnimation(topCanvas, "(Canvas.Top)", -topRect.Height, 0, duration, powerEase));

storyboard.Children.Add(GlobalMethod.CreateDoubleAnimation(middleRect, "Opacity", 0, 1, duration, powerEase));

storyboard.Children.Add(GlobalMethod.CreateDoubleAnimation(bottomCanvas, "(Canvas.Top)", Application.Current.Host.Content.ActualHeight, Application.Current.Host.Content.ActualHeight * 4 / 5, duration, powerEase));

storyboard.Children.Add(GlobalMethod.CreateDoubleAnimation(avatar, "Opacity", 0, 1, duration, powerEase));

EventHandler handler = null;

storyboard.Completed += handler = delegate {

storyboard.Completed -= handler;

timer.Start();

};

storyboard.Begin();

}

public void Hide() {

IsShowing = false;

Storyboard storyboard = new Storyboard();

Duration duration = TimeSpan.FromMilliseconds(500);

PowerEase powerEase = new PowerEase() { EasingMode = EasingMode.EaseOut };

storyboard.Children.Add(GlobalMethod.CreateDoubleAnimation(topCanvas, "(Canvas.Top)", 0, -topRect.Height, duration, powerEase));

storyboard.Children.Add(GlobalMethod.CreateDoubleAnimation(middleRect, "Opacity", 1, 0, duration, powerEase));

storyboard.Children.Add(GlobalMethod.CreateDoubleAnimation(bottomCanvas, "(Canvas.Top)", Application.Current.Host.Content.ActualHeight * 4 / 5, Application.Current.Host.Content.ActualHeight, duration, powerEase));

storyboard.Children.Add(GlobalMethod.CreateDoubleAnimation(avatar, "Opacity", 1, 0, duration, powerEase));

EventHandler handler = null;

storyboard.Completed += handler = delegate {

storyboard.Completed -= handler;

countText = 0;

countDrama = 0;

content.Text = string.Empty;

timer.Stop();

if (Hiding != null) { Hiding(this, null); }

};

storyboard.Begin();

}

public override void AdaptiveWindowSize() {

try {

topRect.Width = middleRect.Width = bottomRect.Width = Application.Current.Host.Content.ActualWidth;

topRect.Height = Application.Current.Host.Content.ActualHeight / 14;

middleRect.Height = Application.Current.Host.Content.ActualHeight;

bottomRect.Height = Application.Current.Host.Content.ActualHeight * 1 / 5;

tip.Width = Application.Current.Host.Content.ActualWidth * 1 / 5 - 15;

content.Width = Application.Current.Host.Content.ActualWidth * 4 / 5 - 15;

Canvas.SetLeft(closer, Application.Current.Host.Content.ActualWidth - closer.RealWidth -15);

Canvas.SetLeft(content, Application.Current.Host.Content.ActualWidth * 1 / 5);

Canvas.SetTop(avatar, -avatar.RealHeight);

Canvas.SetTop(bottomCanvas, Application.Current.Host.Content.ActualHeight * 4 / 5);

} catch { }

}

}

  到此,本系列Demo的编写已告一段落。有些技巧和功能太小不便作为单独章节详细讲述了,比如地图的视口缓动、地形的预测与碰撞处理、角色的特殊动画体系(如弹开、击飞、颤栗等)处理等;当然也还存在很多可拓展元素,比如《博得之门》中的游戏暂停、录像功能;格斗游戏中增强体验的烟尘、声效和印记效果等。代码方面除还可能进一步优化的小部分外,整体框架在反复的思考与重构后已近乎成熟;相比两年前的QXGameEngine,可谓翻天覆地的变化。

  最后剩下几节,我将为本系列Demo焊接上游戏登陆模块,赋予它一个相对完整的商业产品流程。之前写过的两篇《动态资源》和《多国语言本地化》便是其开头,重新整编入本系列作为章节。

  本系列源码请到

时间: 2024-07-28 14:06:50

Silverlight 2.5D RPG游戏技巧与特效处理:(十五)任务剧情的相关文章

一起谈.NET技术,Silverlight 2.5D RPG游戏技巧与特效处理:(四)天气系统

如同动态光影一样,天气系统同样为2.5D RPG游戏所不可或缺的元素之一.从视觉角度讲,天气系统让游戏场景空间层次分明,立体感更强:从用户角度讲,天气系统赋予游戏更贴近现实的亲历体验,试想下游戏中的花花草草随风而动,云雾缭绕风雨雷电一切来得自然而平滑,玩家即似身临其境般享受:从功能角度讲,不同的气候状况会对角色及魔法效果产生各异影响,极大的提升游戏趣味性.  模拟相对较真实的天气效果,粒子系统必不可少,天气中的雨.雪.闪电.云雾等均可看做是大量粒子的集合.因此首先我们新建一个名为Particle

Silverlight 2.5D RPG游戏技巧与特效处理:“.NET研究”(四)天气系统

如同动态光影一样,天气系统同样为2.5D RPG游戏所不可或缺的元素之一.从视觉角度讲,天气系统让游戏场景空间层次分明,立体感更强:从用户角度讲,天气系统赋予游戏更贴近现实的亲历体验,试想下游戏中的花花草草随风而动,云雾缭绕风雨雷电一切来得自然而平滑,玩家即似身临其境般享受:从功能角度讲,不同的气候状况会对角色及魔法效果产生各异影响,极大的提升游戏趣味性.  模拟相对较真实的天气效果,粒子系统必不可少,天气中的雨.雪.闪电.云雾等均可看做是大量粒子的集合.因此首先我们新建一个名为Particle

一起谈.NET技术,Silverlight 2.5D RPG游戏技巧与特效处理:(二)纸娃娃系统

纸娃娃系统,或许大家听起来并不陌生.早在十几年前,当时不论是文字游戏"泥巴(Mud)"或是交友.社交网站,我们只能通过屏幕上的文字来传达与交互信息:随着技术不断进步,2D/3D图形技术高速崛起,通过在基础模型上由客户随意挑选.任意更换各种造型(素材),即可打造出真正属于"自我"独特风格的网络虚拟形象,QQ秀便是我们耳熟能详的代表,更贴近真实的如(RPG)游戏及虚拟现实中的换装/换肤系统同样亦得益于纸娃娃机制. 本节,我将向大家讲解如何最好的实现Silverlight

一起谈.NET技术,Silverlight 2.5D RPG游戏技巧与特效处理:(九)粒子系统

粒子系统通常用于三维场景中进行真实环境模拟,比如第四节的天气系统.天气的实现是粒子系统最重要的应用领域之一,所有的一切无论是云.雨.雾,还是落叶.陨石及闪电,都可视作基础粒子:除此之外,游戏中常常还会用到粒子系统来渲染像发光.轨迹等抽象效果.当然,有时简单并不意味着就不能缔造奇迹,比如本节大家将要看到的就是基于简单粒子系统所创建的当下主流2.5D RPG中极其拉风之装备粒子发散动画特效. 一提到Silverlight中的粒子,首先想到的肯定是WriteableBitmap.没错,今天的主角就是它

Silverlight 2.5D RPG游戏技巧与特效处理:(五“.NET研究”)圣赞之HLSL渲染动画

或许大家依旧对上一节中的"黑夜"及"梦回过去"记忆犹新,追问下去HLSL到底是何方神圣能实现如此炫酷之效果?HLSL(高级着色器语言)an>作为微软的独门兵器,仅供Direct3D使用.Silverlight无比幸运,从第二个版本开始便已获得了这把旷世利器,虽然目前仅能发挥其不到3层之功力,不过前辈Moonlight近期已向世界宣布全面突破技术壁垒,HLSL的威力提升至7层左右.我们完全可以预料的是,未来Silverlight将手握HLSL,踏着GPU 3D

一起谈.NET技术,Silverlight 2.5D RPG游戏技巧与特效处理:(五)圣赞之HLSL渲染动画

或许大家依旧对上一节中的"黑夜"及"梦回过去"记忆犹新,追问下去HLSL到底是何方神圣能实现如此炫酷之效果?HLSL(高级着色器语言)作为微软的独门兵器,仅供Direct3D使用.Silverlight无比幸运,从第二个版本开始便已获得了这把旷世利器,虽然目前仅能发挥其不到3层之功力,不过前辈Moonlight近期已向世界宣布全面突破技术壁垒,HLSL的威力提升至7层左右.我们完全可以预料的是,未来Silverlight将手握HLSL,踏着GPU 3D API飞扬驰

一起谈.NET技术,Silverlight 2.5D RPG游戏技巧与特效处理:(一)序言

不得不说的事,过年期间用了两个多星期时间将17173首页所推荐的2010年新测的几乎60多款网游彻底的扫荡了一遍:过瘾哪!确实过瘾,好久没玩网游了,一次让我爽个够.快的2小时就到了40级,还有更快的5小时冲到了80级.感慨啊真想长叹一声:天!现在的游戏咋被整成了这样!于是愤愤写下如此感受,仅当到此一游:国产3D网游质量恒古不变,敢问开发商你自己想吐了没?市场上充斥着更多的依旧是2D/2.5D ARPG,换汤不换药,十个开发商有九个都在估量着换个美工班即得款新网游,那招个美工连岂不得登上年度大作?

一起谈.NET技术,Silverlight 2.5D RPG游戏技巧与特效处理:(十二)魔法系统

世界首款Silverlight – MMORPG:<<窝窝世界>>震撼登场!伴着与XNA合体后的Silverlight 5 强势发布,一波Silverlight网游研发海啸即将席卷全球!  多磨的好事依旧让人激动,于是一不小心写下10款全新的魔法效果旨在祝贺.今天的教程不会让你失望,没错,又是一场超豪华魔法盛宴!接下来您将看到的是本教程ARPG Demo战斗实景,一切灵感与临摹均来源于近期即将内测的2.5D大作<倩女幽魂Online>(以下左边为倩女幽魂实景,右边为De

一起谈.NET技术,Silverlight 2.5D RPG游戏技巧与特效处理:(三)动态光影

通常来说,只要谈到影子及影子制作,首先想到的不外乎3D.游戏中的影子设计大致可分为硬实现和软实现两种,比如像"游戏影子制作技术"这篇文章所谈到3D游戏影子制作方案Projective Shadow.Shadow Map以及Shadow Volume均属于硬实现.硬实现的效果最逼真,相对的运算量也较高:而对于Silverlight开发2.5D网游来说,我们通常会采用折中的软实现去表述场景中的动态光影效果.精致的光影能让玩家充分体验到空间立体感,当然也是2.5D网游所不可或缺的条件之一.