Silverlight游戏设计(Game Design):(九)三国策(Demo) 之 “江山一统”②

2024-03-27 03:59

本文主要是介绍Silverlight游戏设计(Game Design):(九)三国策(Demo) 之 “江山一统”②,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目前市面上的游戏无论单机的还是网游,具备多角度、多类型场景早已屡见不鲜。经典的如《轩辕剑3》,最传统的中国风RPG角色扮演游戏,整个游戏包含3大类场景:世界(大地图)场景、具体(城市、洞穴等)场景及战斗(回合)场景。精灵在各场景中的移动、视角、事件等方面均有不同约束与实现;三国策同样不例外,游戏中除了上一节讲解的RPG场景外,当战役开始时,游戏将切换到SLG回合对战场景。因此,2D游戏要做到丰富多彩则游戏引擎架构必须搭建在以场景(Scene)为核心的框架上,这也印证了贯穿教程始终的唯一理念:场景编辑器让游戏开发更美好!

废话先说这么多,接下来我将继续为大家讲解如何实现三国策中的SLG部分场景。

如何让一款游戏同时具备多种场景而不产生混乱?这是我们首要解决的一大关键问题。我的思路是建立一个枚举:

    /// <summary>

    /// 场景类型

    /// </summary>

    public enum SceneKinds {

        RPG = 0,

        SLG = 1,

        ……

}

    在需要按场景分类处理的情况下,通过Switch实现不同的逻辑。此时有朋友肯定会问:这样不是无形中增大了游戏框架的耦合度?其实不然,从性能方面讲就算场景类型有10数个之多,Switch所消耗的几乎可以忽略不计;另外,通过我的亲身体会,除了游戏窗口(Window.cs)中会有一些场景类型枚举判断外,其他地方几乎不会用到,这其实也是将游戏中所有对象放置在Window中进行交互所带来的好处,同时也再次证明了前面几节我为大家介绍的基于场景(Scene)为核心,以窗口(Window)为载体的游戏框架设计的正确性。

接下来是为Scene.xml添加SLG战役场景节点参数。同样的,我们打开场景编辑器,设置坐标系偏移及绘制不同地形后,通过点击“导出场景信息”得到该新场景的xml信息,本节Demo中我将其代号(Code)设定为100以示区别于RPG类型场景:

接下来是添加SLG场景中的精灵素材:

其相应的配置xml如下:

  <Sprite Code="5" FullName="铁骑" DirectionType="1" Width="139" Height="118" Speed="220" MoveMode="" MoveLimit="10" BodyCenter="70,75" Icon="1" Frames="……"/>

  <Sprite Code="6" FullName="铁刀" DirectionType="1" Width="108" Height="112" Speed="220" MoveMode="" MoveLimit="4" BodyCenter="54,65" Icon="1" Frames="……"/>

  <Sprite Code="7" FullName="铁枪" DirectionType="1" Width="148" Height="118" Speed="220" MoveMode="" MoveLimit="7" BodyCenter="73,70" Icon="1" Frames="……"/>

  <Sprite Code="8" FullName="藤甲" DirectionType="1" Width="110" Height="110" Speed="220" MoveMode="" MoveLimit="6" BodyCenter="49,65" Icon="1" Frames="……"/>

  <Sprite Code="9" FullName="战象" DirectionType="1" Width="120" Height="120" Speed="220" MoveMode="" MoveLimit="8" BodyCenter="62,58" Icon="1" Frames="……"/>

与上一节相比多了一个名为MoveLimit的属性,该属性代表精灵每次(回合)能够移动的格子数限制。其在SLG战棋类游戏中的地位举足轻重。通过不同地形设定配合上A*寻路,我们可以非常简单的实现显示精灵可移动范围及模拟精灵移动路径等看似令人头疼不已的复杂功能,具体过程且看我一一道来。

首先要做的是将场景坐标系(coordinates)中的层次分配好。由底层到顶层我总共定义了5Canvas分别用于存放不同的对象:layoutReference(布局参照系容器)rangePathReference(范围路径参照系容器)pathReference(模拟移动路径参照系容器)cursor(鼠标光标)Container(实体对象容器)

通过分层处理而不是将所有物体全部堆进一个Canvas里的效果是显而易见:可以更加轻松的应对各种子对象的添加与移除。之前有朋友问我为何场景中还要分坐标系、参照系等层次,这就是原因了。你想想,主角精灵周围的可移动范围及模拟移动路径是完全动态的,随时可能添加或清空;用特定的Canvas来收容这些对象,处理的时候仅仅需要一条类似:pathReference.Children.Add(obj);pathReference.Children.Clear();的语句即可实现而无须对唯一的容器进行暴力遍历判断啥是啥对象,啥是啥类型,啥是啥名字,然后才做相应的逻辑处理,从性能上说后者完全不可取。因此分层(Canvas)架设场景是游戏场景类设计的最佳思路,好比Div+Css作为Web新标准一样,结构清晰,代码设计起来才会更轻松、惬意。

结构层次理清后,接下来我们将实现其中最引人注目的主角精灵可移动路径范围及模拟移动路径这两大功能。在上一节中我曾有提到为enum枚举定义不同的数字,不仅仅是作为一个标识;以其中的enum Terrains(地形枚举)来说,它在A*寻路中有着非凡的作用。为了大家更好的理解后面的关键内容,这里我还是将其定义再罗列一次:

    /// <summary>

    /// 地形

    /// </summary>

    public enum Terrains {

        /// <summary>

        /// 障碍物(无法通过的任何地形)

        /// </summary>

        Obstacle = 0,

        /// <summary>

        /// 平地

        /// </summary>

        Flat = 1,

        /// <summary>

        /// 森林

        /// </summary>

        Grassland = 2,

        /// <summary>

        /// 山地

        /// </summary>

        Desert = 3,

        /// <summary>

        /// 河流

        /// </summary>

        River = 4,

        /// <summary>

        ///

        /// </summary>

        None = 100,

    }

    通过定义Terrains,我将三国策SLG场景中的地形划分成5种:障碍就不用说了,无论如何精灵都是移动不过去的;平地、森林、山地、河流分别用数字1234表示,在如下配置的A*寻路中分别代表精灵移动到此类型单元格所消耗的F(F=H+G)值:

PathFinderFast pathFinderFast = new PathFinderFast(this.LocatedScene.TerrainMatrix) {

    Diagonals = false,

    HeuristicEstimate = 2,

};

    接着以点destination为目标,通过A*寻路类执行寻找路径操作:

List<PathFinderNode> path = pathFinderFast.FindPath(this.Coordinate, destination);

寻路完成后,通过判断路径是否存在以及最重要的:该路径的最后一格即目的地单元格的F值是否在前文提到的该精灵的MoveLimit范围内,如果是则说明该目的地是可到达的。最后,按照前面几节讲解的场景编辑器中绘制参照系的方式绘制单元格到相应的位置上即可:

if (path != null && path[0].F <= this.MoveLimit) {

    //绘制所有格子

    ……

}

    找寻模拟移动路径仅需要执行以上方法一次即可,但如果是查找精灵可移动路径的范围则相对复杂些,需要从该精灵为中心的周围各方向MoveLimit距离开始到主角脚底单元格进行编历循环如上方法,核心算法如下(详细见源码)

int startX = (int)this.Coordinate.X - this.MoveLimit;

int endX = (int)this.Coordinate.X + this.MoveLimit;

bool plus = true; //Y方向加

int count = 0;

for (int x = startX; x <= endX; x++) {

     int startY = (int)this.Coordinate.Y - count;

     int endY = (int)this.Coordinate.Y + count;

     for (int y = startY; y <= endY; y++) {

          List<PathFinderNode> path = pathFinderFast.FindPath(this.Coordinate, new Point(x, y));

          //假如有路径且在限制移动格数内则收集该点

          if (path != null && path[0].F <= this.MoveLimit) {

              Point p = new Point(path[0].X, path[0].Y);

              points.Add(p);

          }

      }

      if (count == this.MoveLimit) { plus = false; }

          count = plus ? count + 1 : count - 1;

      }

      points.Remove(this.Coordinate); //去掉主角脚底的那格

//绘制所有格子

      ……

}

当然,为了模拟战棋回合制动模式,我们还必须为整个游戏定义一个全局的鼠标左键锁:

     /// <summary>

     /// 鼠标左键锁(锁定则左键无法点击)

/// </summary>

public static bool MouseLeftButtonLock { get; set; }

当主角移动开始时锁住左键,只有移动结束后才解除该锁定:

        /// <summary>

        /// SLG中主角移动开始

        /// </summary>

        private void leader_MovingStart(object sender, EventArgs e) {

            GlobalState.MouseLeftButtonLock = true;

            ……

        }

 

        /// <summary>

        /// SLG中主角移动完成

        /// </summary>

        private void leader_MovingCompleted(object sender, EventArgs e) {

            GlobalState.MouseLeftButtonLock = false;

            ……

        }

同时,SLG中我们是可以选择不同精灵作为主角的,思路是当点击某个非主角精灵时,首先移除该精灵的所有事件并设置该精灵所占的位置为障碍物:

……

leader.IsMainView = false;

leader.MovingStart -= leader_MovingStart;

leader.MovingCompleted -= leader_MovingCompleted;

leader.CoordinateChanged -= leader_CoordinateChanged;

leader.ClearRangePath();

leader.LocatedScene.TerrainMatrix[(byte)leader.Coordinate.X, (byte)leader.Coordinate.Y] = 0;

……

接着将主角的地位赋予鼠标点击的新精灵,并为该精灵装备上这些主角所该拥有的事件等:

……

leader = leader.LocatedScene.sprites.Single(X => X.Coordinate == p);

leader.LocatedScene.sprites.Remove(leader);

leader.IsMainView = true;

leader.MovingStart += leader_MovingStart;

leader.MovingCompleted += leader_MovingCompleted;

leader.CoordinateChanged += leader_CoordinateChanged;

leader.ShowRangePath();

leader.LocatedScene.TerrainMatrix[(byte)leader.Coordinate.X, (byte)leader.Coordinate.Y] = 1;

……

整个过程用到了C#“类”为“引用类型”这么一个特性,面向对象语言的优势在此时体现得尤为充分。

另外,在SLG中我们不再需要通过命中测试去拾取想要的精灵了,按照SLG中“一个萝卜一个坑”的场景概念,我们可以通过LINQ去查找场景中精灵容器内XY在鼠标坐标的精灵即为点中的精灵,这样处理无论性能及逻辑上效果都非常不错:

        Point mouseGameCoordinate = new Point(); //鼠标当前坐标(游戏坐标系)

        Sprite hitSprite; //选中的精灵

        /// <summary>

        /// 窗口中鼠标移动

        /// </summary>

        private void Window_MouseMove(object sender, MouseEventArgs e) {

            Point p = leader.GetGameCoordinate(leader.LocatedScene.Gradient, e.GetPosition(leader.LocatedScene.Container), leader.LocatedScene.GridSize);

            if (mouseGameCoordinate != p) {

                mouseGameCoordinate = p;

                leader.LocatedScene.SetCursorPosition(p);

                if (!GlobalState.MouseLeftButtonLock) { leader.ShowPath(p); }

                if (hitSprite != null && hitSprite.Coordinate != p) {

                    hitSprite.ShadowVisible = false;

                }

                if (leader.LocatedScene.sprites.Where(X => X.Coordinate == p).Count() > 0) {

                    hitSprite = leader.LocatedScene.sprites.Single(X => X.Coordinate == p);

                    hitSprite.ShadowVisible = true;

                }

            }

        }

呼呼,一口气写了这么多,休息时间先来张截图欣赏一下我们的劳动成果吧:

至此,我们基本实现了SLG场景及游戏逻辑中的所有基础功能。最后,我还需要为大家补充一些关于此Demo的细节与技术要点。

一、关于精灵可移动范围单元格的样式设定。漂亮且立体与地形完美吻合的单元格形状在视觉方面更具冲击力及震撼力。在游戏中,我绘制的单元格均为Polygon(矢量多边形),配合上Silverlight中不同类型的笔刷(Brush)理论上可以实现几乎所有形状格子的绘制。例如我们想要将主角模拟移动路径设置为一些圆点,只需使用RadialGradientBrush作为格子的Fill即可轻松实现:

Polygon polygon = new Polygon() {

    ……

    Fill = new RadialGradientBrush() {

        GradientStops = new GradientStopCollection() {

            new GradientStop() {

                Color = Colors.Transparent,

                Offset = 0.3

            },

            new GradientStop() {

                Color = Colors.White,

                Offset = 0.3

            },

            new GradientStop() {

                Color = Colors.Transparent,

                Offset = 0.5

            },

        }

    },

    ……

};

    实际的效果是非常酷的,截图如下:

二、关于游戏中BitmapImage的获取、呈像及缓存。上一节我为大家讲解了如何通过WebClient下载图片。通过WebClient下载的图片会被缓存到Temporary Internet Files文件夹中,这就意味着只要WebClient将图片下载过一次,那么下次我们可以直接通过

BitmapImage bitmapImage = new BitmapImage(new Uri(WebPath(uri), UriKind.Relative));

的方式直接从Temporary Internet Files文件夹中提取该图片而不会启动再次下载了,共用缓存的性质使得Silverlight开发网络游戏更加简单,更加华丽。

但是,这里我想向大家强调的是一个非常重要且容易疏漏的问题:请大家严重清楚认识BitmapImageCreateOptions的三种缓存模式BitmapCreateOptions.NoneBitmapCreateOptions.DelayCreationBitmapCreateOptions.IgnoreImageCache

CreateOptions设定为CreateOptions = BitmapCreateOptions.None时,不仅Temporary Internet Files文件夹中会保存一份该图片的原件,而且在内存中同样也会自动存一份;这就意味着我们就算人为的将Temporary Internet Files文件夹清空,当Silverlight程序再次请求获取该图片时,BitmapImage会直接从内存里提取而不会再次去下载或寻找Temporary Internet Files文件夹。

CreateOptions设定为CreateOptions = BitmapCreateOptions. DelayCreation时,顾名思义图象是被延迟加载的,它是BitmapImage的默认值。图片仅存于Temporary Internet Files中,虽然减少了内存的占用,但在大图片重新加载时会相对卡些,毕竟读取内存的速度肯定是优于硬盘的;这还其次了,更大的问题是经过我反复测试,GC会时不时的作怪以至图像时常无法正确显示,大家可以自行尝试在特定地方例如场景切换完毕后执行GC.Collect()会出现一些相当诡异的事情,特别提醒大家注意。

至于把CreateOptions设定为CreateOptions = BitmapCreateOptions. IgnoreImageCache则更加危险了,细节还请大家自行体会。

结束啦~一个完整的几乎具备《三国策》所有基础功能的Demo完成啦~嘿嘿,号令千军万马驰骋疆场真乃快哉快哉:

 

相当有成就感呢,时间花费不算多,代码是绝对的简单易懂,没有一句反射,低耦合高内聚,这就是场景编辑器为我们创造的游戏奇迹!一同加油吧~后续的章节更加精彩~一定要关注哦。

在线演示地址:http://silverfuture.cn

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

原文链接: http://www.cnblogs.com/alamiye010/archive/2010/03/25/1695234.html

转载于:https://my.oschina.net/chen106106/blog/43659

这篇关于Silverlight游戏设计(Game Design):(九)三国策(Demo) 之 “江山一统”②的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/850866

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

怎么让1台电脑共享给7人同时流畅设计

在当今的创意设计与数字内容生产领域,图形工作站以其强大的计算能力、专业的图形处理能力和稳定的系统性能,成为了众多设计师、动画师、视频编辑师等创意工作者的必备工具。 设计团队面临资源有限,比如只有一台高性能电脑时,如何高效地让七人同时流畅地进行设计工作,便成为了一个亟待解决的问题。 一、硬件升级与配置 1.高性能处理器(CPU):选择多核、高线程的处理器,例如Intel的至强系列或AMD的Ry

国产游戏崛起:技术革新与文化自信的双重推动

近年来,国产游戏行业发展迅猛,技术水平和作品质量均得到了显著提升。特别是以《黑神话:悟空》为代表的一系列优秀作品,成功打破了过去中国游戏市场以手游和网游为主的局限,向全球玩家展示了中国在单机游戏领域的实力与潜力。随着中国开发者在画面渲染、物理引擎、AI 技术和服务器架构等方面取得了显著进展,国产游戏正逐步赢得国际市场的认可。然而,面对全球游戏行业的激烈竞争,国产游戏技术依然面临诸多挑战,未来的

基于51单片机的自动转向修复系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机

fzu 2275 Game KMP

Problem 2275 Game Time Limit: 1000 mSec    Memory Limit : 262144 KB  Problem Description Alice and Bob is playing a game. Each of them has a number. Alice’s number is A, and Bob’s number i

SprinBoot+Vue网络商城海鲜市场的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍:CSDN认证博客专家,CSDN平台Java领域优质创作者,全网30w+

单片机毕业设计基于单片机的智能门禁系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍程序代码部分参考 设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订

火柴游戏java版

代码 /*** 火柴游戏* <p>* <li>有24根火柴</li>* <li>组成 A + B = C 等式</li>* <li>总共有多少种适合方式?</li>* <br>* <h>分析:</h>* <li>除去"+"、"="四根,最多可用火柴根数20根。</li>* <li>全部用两根组合成"1",最大数值为1111。使用枚举法,A和B范围在0~1111,C为A+B。判断</li>** @

Spring的设计⽬标——《Spring技术内幕》

读《Spring技术内幕》第二版,计文柯著。 如果我们要简要地描述Spring的设计⽬标,可以这么说,Spring为开发者提供的是⼀个⼀站式的轻量级应⽤开发框架(平台)。 作为平台,Spring抽象了我们在 许多应⽤开发中遇到的共性问题;同时,作为⼀个轻量级的应⽤开发框架,Spring和传统的J2EE开发相⽐,有其⾃⾝的特点。 通过这些⾃⾝的特点,Spring充分体现了它的设计理念:在

国产游戏行业的崛起与挑战:技术创新引领未来

国产游戏行业的崛起与挑战:技术创新引领未来 近年来,国产游戏行业蓬勃发展,技术水平不断提升,许多优秀作品在国际市场上崭露头角。从画面渲染到物理引擎,从AI技术到服务器架构,国产游戏已实现质的飞跃。然而,面对全球游戏市场的激烈竞争,国产游戏技术仍然面临诸多挑战。本文将探讨这些挑战,并展望未来的机遇,深入分析IT技术的创新将如何推动行业发展。 国产游戏技术现状 国产游戏在画面渲染、物理引擎、AI