Unity3D流行的游戏开发人员构建高速检查站系统
好友,大家好,欢迎关注我的博客。我是秦培,我的博客地址blog.csdn.net/qinyuanpei。
今天,我想分享的是,现在在移动平台上的检查点系统更受欢迎,机游戏如《愤慨的小鸟》、《保卫萝卜》中对游戏内容的组织形式,玩家可通过已解锁的关卡(默认第一关是已解锁的)获取分数进而解锁新的关卡,或者是通过付费购买解锁新的关卡。那么好了,在今天的文章中博主将带领大家高速实现一个可扩展的关卡系统,这个实例的灵感来自博主近期的工作经历,希望对大家学习Unity3D游戏起到一定帮助性的作用。
原理
在本地配置一个Xml文件。在这个文件里定义当前游戏中关卡的相关信息。通过解析该文件并和UI绑定终于实现一个完整的关卡系统。
1、定义关卡
首先我们来定义一个关卡的基本结构:
public class Level
{
/// <summary>
/// 关卡ID
/// </summary>
public string ID;
/// <summary>
/// 关卡名称
/// </summary>
public string Name;
/// <summary>
/// 关卡是否解锁
/// </summary>
public bool UnLock = false;
}
在这里,我们假定关卡的名称和该关卡在Unity3D中场景名称一致。当中最为重要的一个属性是UnLock。该值是一个布尔型变量,表明该关卡是否解锁。由于在游戏中。仅仅有解锁的场景是能够訪问的。
2、定义关卡配置文件
从关卡的基本结构Level能够定义出例如以下的配置文件,这里使用Xml作为配置文件的存储形式:
<?
xml version="1.0" encoding="utf-8"?>
<levels>
<level id="0" name="level0" unlock="1" />
<level id="1" name="level1" unlock="0" />
<level id="2" name="level2" unlock="0" />
<level id="3" name="level3" unlock="0" />
<level id="4" name="level4" unlock="0" />
<level id="5" name="level5" unlock="0" />
<level id="6" name="level6" unlock="0" />
<level id="7" name="level7" unlock="0" />
<level id="8" name="level8" unlock="0" />
<level id="9" name="level9" unlock="0" />
</levels>
和关卡结构定义相似,这里使用0和1来表示关卡的解锁情况。0表示未解锁。1表示解锁,能够注意到默认情况下第一个关卡是解锁的。这符合我们在玩《愤慨的小鸟》这类游戏时的直观感受。那么好了,在完毕了关卡的结构定义和配置文件定义后,接下来我们開始思考怎样来实现一个关卡系统,由于此处并不涉及到Unity3D场景中的详细逻辑,因此我们在关卡系统中基本的工作就是维护好主界面场景和各个游戏场景的跳转关系,我们能够注意到这里要完毕两件事情,即第一要将配置文件里的关卡以一定形式载入到主界面中,并告诉玩家哪些关卡是已解锁的、哪些关卡是未解锁的。当玩家点击不同的关卡时能够得到不同的响应,已解锁的关卡能够訪问并进入游戏环节,未解锁的关卡则须要获得很多其它的分数或者是通过付费来解锁关卡;第二是要对关卡进行编辑,当玩家获得了分数或者是支付一定的费用后能够解锁关卡进入游戏环节。
这两点综合起来就是我们须要对关卡的配置文件进行读写,由于我们注意到一个关卡是否解锁仅仅取决于unlock属性。那么好了,明确了这一点后我们来动手编写一个维护关卡的类。
3、编写一个维护关卡的类
这里直接给出代码。由于从严格的意义上来说。这段代码并不是我们此刻关注的重点。可能这让大家感到难以适应,由于文章明明就是在教我们实现一个关卡系统。但是此刻博主却说这部分不重要了,请大家稍安勿躁。由于这里有比代码更为深刻的东西。
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
public static class LevelSystem
{
/// <summary>
/// 载入Xml文件
/// </summary>
/// <returns>The levels.</returns>
public static List<Level> LoadLevels()
{
//创建Xml对象
XmlDocument xmlDoc = new XmlDocument();
//假设本地存在配置文件则读取配置文件
//否则在本地创建配置文件的副本
//为了跨平台及可读可写,须要使用Application.persistentDataPath
string filePath = Application.persistentDataPath + "/levels.xml";
if (!IOUntility.isFileExists (filePath)) {
xmlDoc.LoadXml (((TextAsset)Resources.Load ("levels")).text);
IOUntility.CreateFile (filePath, xmlDoc.InnerXml);
} else {
xmlDoc.Load(filePath);
}
XmlElement root = xmlDoc.DocumentElement;
XmlNodeList levelsNode = root.SelectNodes("/levels/level");
//初始化关卡列表
List<Level> levels = new List<Level>();
foreach (XmlElement xe in levelsNode)
{
Level l=new Level();
l.ID=xe.GetAttribute("id");
l.Name=xe.GetAttribute("name");
//使用unlock属性来标识当前关卡是否解锁
if(xe.GetAttribute("unlock")=="1"){
l.UnLock=true;
}else{
l.UnLock=false;
}
levels.Add(l);
}
return levels;
}
/// <summary>
/// 设置某一关卡的状态
/// </summary>
/// <param name="name">关卡名称</param>
/// <param name="locked">是否解锁</param>
public static void SetLevels(string name,bool unlock)
{
//创建Xml对象
XmlDocument xmlDoc = new XmlDocument();
string filePath=Application.persistentDataPath + "/levels.xml";
xmlDoc.Load(filePath);
XmlElement root = xmlDoc.DocumentElement;
XmlNodeList levelsNode = root.SelectNodes("/levels/level");
foreach (XmlElement xe in levelsNode)
{
//依据名称找到相应的关卡
if(xe.GetAttribute("name")==name)
{
//依据unlock又一次为关卡赋值
if(unlock){
xe.SetAttribute("unlock","1");
}else{
xe.SetAttribute("unlock","0");
}
}
}
//保存文件
xmlDoc.Save (filePath);
}
}
这里我们首先将关卡配置文件levels.xml放置在Resources文件夹下,这是由于我们能够使用Resources.Load()这样的方式来载入本地资源。这样的方式对于Unity3D来说有着得天独厚的优势:
* 它使用相对于Resources文件夹的相对路径,所以在使用的时候不用考虑是相对路径还是绝对路径的问题
* 它使用名称来查找一个本地资源。所以在使用的时候不用考虑扩展名和文件格式的问题
* 它能够是Unity3D支持的随意类型。从贴图到预制体再到文本文件等等。能够和Unity3D的API完美地结合
说了这么多它的长处,我们自然要痛心疾首地说说它的缺点。它的缺点是什么呢?那就是不支持写入操作。这当然不能责备Unity3D,由于当Unity3D导出游戏的时候会将Rsources文件夹下的内容压缩后再导出,我们当然不能要求在一个压缩后的文件里支持写入操作啦,所以我们是时候来总结下Unity3D中资源读写的常见方案了,那么Unity3D中常见的资源读写方案由哪些呢?
1、Resources.Load:仅仅读,当我们的资源不须要更新且对本地存储无容量要求的时候能够採用这样的方式
2、AssetBundle:仅仅读。当我们的资源须要更新且对本地存储有容量要求的时候能够採用这样的方式
3、WWW:仅仅读。WWW支持http协议和file协议,因此能够WWW来载入一个网络资源或者本地资源
4、PlayerPrefs:可读可写,Unity3D提供的一种的简单的键-值型存储结构,能够用来读写float、int和string三种简单的数据类型,是一种较为松散的数据存储方案
5、序列化和反序列化:可读可写,能够使用Protobuf、序列化为Xml、二进制或者JSON等形式实现资源读写。
6、数据库:可读可写,能够使用MySQL或者SQLite等数据库对数据进行存储实现资源读写。
好了,在了解了Unity3D中资源读写的常见方案后,我们接下来来讨论下Unity3D中的路径问题:
1、Application.dataPath:这个路径是我们常常使用的一个路径,但是我们真的了解这个路径吗?我看这里要打个大大的问号。为什么这么说呢?由于这个路径在不同的平台下是不一样的。从官方API文档中能够了解到这个值依赖于执行的平台:
* Unity 编辑器:<project文件夹的路径>/Assets
* Mac:<到播放器应用的路径>/Contents
* IOS: <到播放器应用的路径>/
4、编写入口文件
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using System.Xml.Serialization;
public class Main : MonoBehaviour
{
//关卡列表
private List<Level> m_levels;
void Start ()
{
//获取关卡
m_levels = LevelSystem.LoadLevels ();
//动态生成关卡
foreach (Level l in m_levels)
{
GameObject prefab=(GameObject)Instantiate((Resources.Load("Level") as GameObject));
//数据绑定
DataBind(prefab,l);
//设置父物体
prefab.transform.SetParent(GameObject.Find("UIRoot/Background/LevelPanel").transform);
prefab.transform.localPosition=new Vector3(0,0,0);
prefab.transform.localScale=new Vector3(1,1,1);
//将关卡信息传给关卡
prefab.GetComponent<LevelEvent>().level=l;
prefab.name="Level";
}
//人为解锁第二个关卡
//在实际游戏中玩家须要满足一定条件方可解锁关卡
//此处仅作为演示
LevelSystem.SetLevels ("level1", true);
}
/// <summary>
/// 数据绑定
/// </summary>
void DataBind(GameObject go,Level level)
{
//为关卡绑定关卡名称
go.transform.Find("LevelName").GetComponent<Text>().text=level.Name;
//为关卡绑定关卡图片
Texture2D tex2D;
if(level.UnLock){
tex2D=Resources.Load("nolocked") as Texture2D;
}else{
tex2D=Resources.Load("locked") as Texture2D;
}
Sprite sprite=Sprite.Create(tex2D,new Rect(0,0,tex2D.width,tex2D.height),new Vector2(0.5F,0.5F));
go.transform.GetComponent<Image>().sprite=sprite;
}
}
在这段脚本中,我们首先载入了关卡信息,然后将关卡信息和界面元素实现绑定。从而实现一个简单的关卡选择界面,并人为地解锁了第二个关卡。好吧。假设这是一个正式游戏的配置关卡配置文件。相信大家都知道怎么免费玩解锁的关卡了吧。哈哈!当然。我不推荐大家这样做。由于作为一个程序猿,当你全身心地投入到一个项目中的时候,你就会明确完毕一款软件或者游戏须要投入多少精力。所以大家尽量还是不要想破解或者盗版这些这些事情,毕竟作为开发人员可能他的出发点是想做出来一个让大家都喜欢的产品,但是更现实的问题是开发人员一样要生活,所以请善待他们吧。好了。言归正传,这里的UI都是基于UGUI实现的,不要问我为什么不用NGUI,由于我就是喜欢UGUI!
我们知道我们须要为每一个关卡的UI元素绑定一个响应的事件。因此我们须要为其编写一个LevelEvent的脚本:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class LevelEvent : MonoBehaviour
{
//当前关卡
public Level level;
public void OnClick()
{
if(level.UnLock){
//假设关卡的名称即为相应场景的名称
//Application.LoadLevel(level.Name);
Debug.Log ("当前选择的关卡是:"+level.Name);
}else{
Debug.Log ("抱歉!当前关卡尚未解锁!");
}
}
}
记得在本文開始的时候,博主提到了一个假设,就是关卡的名称和其相应的游戏名称一致的假设,相信到此处大家都知道为什么了吧!为了让每一个关卡的UI元素知道自己相应于哪个关卡。我们设置了一个level变量。这个变量的值在载入关卡的时候已经完毕了初始化,所以此时我们能够在这里知道每一个关卡的详细信息,从而完毕事件的响应。
好了。今天的内容就是这样了。我们来看看终于的效果吧。
能够注意到在第二次打开游戏后,第二个关卡已经解锁了,说明我们在最開始设计的两个目标都达到了,那么内容就是这样子啦,假设大家有什么好的想法或者建议。欢迎在文章后面给我留言,谢谢大家!
2015年12月1日更新:
近期有朋友反映须要我给出IOHelper这个类的实现。但是其实这个类仅仅是对System.IO这个命名空间下的相关方法进行了简单的封装,我一直认为无论学习什么技术。我们都要有一个完整的系统性的学习路线。
尽管Unity3D入门十分简单,可说究竟它本质上仍然是C#的技术范畴,所以我们不能把眼光局限在Unity3D这个引擎和它丰富的插件上面,学插件、使用插件只是是邯郸学步。真正能让你成长的却是自己掌握的知识。我知道我说这些,大部分人都不会看,由于你认为来看我博客是给我面子,直接抄你博客里的代码是看得起你。
呵呵,我写这个博客的目的可不是为了给懒人提供代码库,懒人真正的代码库是Github。罢了。给这些懒人给出源码吧!
/*
* Unity3D脚本(C#)
* Author:
* Date:
*/
using UnityEngine;
using System.Collections;
using System.IO;
public static class IOUntility
{
/// <summary>
/// 创建文件夹
/// </summary>
/// <param name="path">文件夹路径</param>
public static void CreateFolder(string path)
{
if(!Directory.Exists(path)){
Directory.CreateDirectory(path);
}
}
/// <summary>
/// 创建文件
/// </summary>
/// <param name="filePath">文件路径</param>
/// <param name="content">文件内容</param>
public static void CreateFile(string filePath,string content)
{
//文件流
StreamWriter writer;
//推断文件文件夹是否存在
//不存在则先创建文件夹
Debug.Log (filePath);
string folder = filePath.Substring (0, filePath.LastIndexOf ("//"));
CreateFolder (folder);
//假设文件不存在则创建。存在则追加内容
FileInfo file=new FileInfo(filePath);
if(!file.Exists){
writer=file.CreateText();
}else{
file.Delete();
writer=file.CreateText();
}
//写入内容
writer.Write(content);
writer.Close();
writer.Dispose();
}
/// <summary>
/// 推断文件是否存在
/// </summary>
/// <param name="path">文件路径</param>
public static bool isFileExists(string path)
{
FileInfo file=new FileInfo(path);
return file.Exists;
}
public static void DeleteFile(string fileName)
{
if(!File.Exists(fileName)) return;
File.Delete(fileName);
}
}
Unity3D流行的游戏开发人员构建高速检查站系统的更多相关文章
- 游戏开发人员眼中的Unity 3D网页游戏測评报告
眼下.能够实现3D页游的主流技术有Silverlight.XNA.Flash.HTML5和Unity3D. 当中.Unity3D作为一款专注于3D游戏的浏览器插件.最近在国内外页游产品线骚动异常:本人 ...
- Unity3D手机斗地主游戏开发实战(04)_出牌判断大小(已完结)
之前我们实现了叫地主.玩家和电脑自动出牌主要功能,但是还有个问题,出牌的时候,没有有效性检查和比较牌力大小.比如说,出牌3,4,5,目前是可以出牌的,然后下家可以出任何牌如3,6,9. 问题1:出牌检 ...
- Unity3D手机斗地主游戏开发实战(04)_出牌判断大小
之前我们实现了叫地主.玩家和电脑自动出牌主要功能,但是还有个问题,出牌的时候,没有有效性检查和比较牌力大小.比如说,出牌3,4,5,目前是可以出牌的,然后下家可以出任何牌如3,6,9. 问题1:出牌检 ...
- [Unity3D]Unity3D圣骑士模仿游戏开发传仙灵达到当局岛
大家好,我是秦培.欢迎关注我的博客.我的博客地址blog.csdn.net/qinyuanpei. 在前面的文章中.我们分别实现了一个自己定义的角色控制器<[Unity3D]Unity3D游戏开 ...
- Unity3D手机斗地主游戏开发实战(01)_发牌功能实现
园子荒废多年,闲来无事,用Unity3D来尝试做一个简单的小游戏,一方面是对最近研究的Unity3D有点总结,一方面跟广大的园友相互学习和提高.话不多说,进入正题~ 一.创建项目 1.创建Unity2 ...
- Photon + Unity3D 线上游戏开发 学习笔记(一)
大家好. 我也是学习Photon + unity3D 的新手 有什么说错的地方大家见谅哈. 我的开发环境是 unity3D 4.1.3 , Visual Studio 是2010 版本号的 p ...
- [Unity3D]Unity3D连衣裙实现游戏开发系统
大家好,我是秦培.欢迎关注我的博客,我的博客地址">blog.csdn.net/qinyuanpei. 不知从什么时候開始,国产RPG单机游戏開始出现换装,仙剑系列中第一部实现了换装的 ...
- Unity3d/2d手机游戏开发第二版 (金玺曾) 随书资源
http://pan.baidu.com/s/1c0xpn4s Unity3d2d手机游戏开发配书资源文件.rar 1.36G 书上的链接坏掉了,我在论坛上面买了一份,放这分享给买了书找不到资源的同学 ...
- Unity3D手机斗地主游戏开发实战(02)_叫地主功能实现(不定期更新中~~~)
目录 Unity3D手机斗地主游戏开发实战(01)_发牌功能实现 Unity3D手机斗地主游戏开发实战(02)_叫地主功能实现 一.大体思路 前面我们实现了点击开始游戏按钮,系统依次给玩家发牌的逻辑和 ...
随机推荐
- UE4编码规范
翻译原文为Unreal 的官方!自己看着总结了一下,不一定每条都能对上.不足之处,请多多不吝赐教! 原文地址: unreal CodingStandard UE4编码规范 在Epic,有简单几条代码 ...
- MySQL初步研究数据库
我用的是环境Win7.开始学习PHP和MySQL,而买了这<Head First PHP & MySQL>,从能Head First Labs官网获得HeadFirst系列书籍的相 ...
- win10无法新建文件夹怎么办 win10右键新建菜单设置方法
有朋友安装了win10系统后发现右键菜单中没有新建项,而平时使用新建 - 文件夹项的机率很高.如何才能恢复桌面右键菜单中的新建项呢? 右键点击桌面空白处,在右键菜单中发现没有新建项: 桌面右键菜单没有 ...
- zoj 3288 Domination (可能dp)
///dp[i][j][k]代表i行j列件,并把一k的概率 ///dp[i][j][k]一种常见的方法有四种传输 ///1:dp[i-1][j][k-1] 可能 (n-(i-1))*j/(n*m-(k ...
- JSP简单的练习-用户登记表
<%@ page language="java" import="java.util.*" pageEncoding="gb2312" ...
- Linux:闪光的宝石,智慧 (在)
Linux:闪光的宝石,智慧的结晶(上) 老实说,这十几天以来.因为我违反了"家规",又被断网处罚(拔掉网线).没收手机与老年证(不许出家门). 因此.我平日里仅仅能面对一篇文章& ...
- Java常见问题3:周期之谜
谜24 byte是有符号的.范围是-128 - 127. 而0x90是int类型. 比較的时候.不相等. 假设想让其相等,须要进行类型转换:(byte & 0xff) 或者 (byte)0x9 ...
- java基础程序题
发现自己初学java时保存在word里的练习题,哈哈,放博客里面来作为纪念吧~~~ [程序1] 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第四个月后每个月又生一对兔 ...
- linux挂载U盘,及乱码问题解决
1. 首先使用切换到root用户. 2. 使用fdisk -l命令查看磁盘信息,找到u盘(能够依据显示的大小确定) 3. 在/mnt下创建挂载点,比如创建usb目录:mkdir /mnt/usb 4. ...
- android-将系统和应用程序级的屏幕亮度
/** * 获取当前屏幕亮度模式 * SCREEN_BRIGHTNESS_MODE_AUTOMATIC=1 为自己主动调节屏幕亮度 * SCREEN_BRIGHTNESS_MODE_MANUAL=0 ...