游戏开发设计模式之原型模式 & unity3d JSON的使用(unity3d 示例实现)
命令模式:游戏开发设计模式之命令模式(unity3d 示例实现)
对象池模式:游戏开发设计模式之对象池模式(unity3d 示例实现)
实现原型模式
原型模式带来的好处就是,想要构建生成任意独特对象的生成类,只需要一个生成类和一个原型即可。
当我们有一个抽象的敌人Monster类就有很多继承它的各种各样的敌人,人类、动物、龙等等,如果我们想为每个敌人做一个生成器父类Spawner,也会有与monster对应数量的子类,也许就会这样:
这样就会产生类的数量变多,而且这些类的功能是重复的。
开始的spawner类可能是这样的:
using UnityEngine;
using System.Collections; public class Spawner : MonoBehaviour {
public GameObject createPerson(GameObject Person)
{
return Instantiate(Person);
}
public GameObject createAnimal(GameObject Animal)
{
return Instantiate(Animal);
}
public GameObject createDragon(GameObject Dragon)
{
return Instantiate(Dragon);
}
}
上面的代码可见我们有重复的方法,而且随着敌人子类增多这种重复代码会越来越多。
我们可以视所有怪兽为一个原型,让Spawner类只生成这个原型,通过改变这个原型来生产不同的怪兽。
再进一步,我们可以让这个原型有一个生成自己的方法,就不需要在Spawner类中new了只需要在Spawner类调用原型的方法就可以,我们做一个monster生成(克隆)自己的方法clone()。
using UnityEngine;
using System.Collections; public class Monster : MonoBehaviour {
public string MonsterName;
public int attack;
public int defense;
public string weapon; // Use this for initialization
/* virtual public Monster clone()
{
return this;
}*/
public GameObject clone()
{
return Instantiate(this.gameObject) as GameObject;
}
}
这里存在一个深复制和浅复制的问题,C#数据类型大体分为值类型(valuetype)与引用类型
(referencetype)。对于值类型数据,复制的时候直接将数据复制给另外的变量,
而对于引用型变量而言,复制时,其实只是复制了其引用。复制引用的方式叫浅复制,而逐一复制被复制对象的数据成员的方式称为深复制。
unity的Instantiate就是深复制GameObject
如果你想浅复制clone函数为:
virtual public Monster clone()
{
return this;
}
浅复制返回它本身的引用,如果你想深复制,就Instantiate一个新的:
public GameObject clone()
{
return Instantiate(this.gameObject) as GameObject;
}
再看看子类
using UnityEngine;
using System.Collections; public class AnimalMonster : Monster
{
public AnimalMonster()
{
MonsterName = "Animal";
attack = ;
defense = ;
weapon = "tooth";
} }
这样每个敌人子类都有一个clone方法,就不需要每个都配一个Spawner类了,一个Spawner就可以。
using UnityEngine;
using System.Collections; public class Spawner : MonoBehaviour {
Monster prototype;
// Use this for initialization
public void setPrototype(Monster _prototype)
{
this.prototype = _prototype;
} public GameObject createMonster()
{
return prototype.clone();
}
}
创建他们的方法也很简单,设置原型,create。
spawner.setPrototype(People);
spawner.createMonster();
spawner.setPrototype(Animal);
spawner.createMonster();
克隆出的属性值都和原型相同,如果当前怪兽处在某种状态,比如,中毒、虚弱、灼烧,也可以被复制下来。
再进一步,我们可以通过构造函数实例化不同的spawner对象来代替不同的spawner子类,使spawner实体专一化的生成某种怪兽
u
sing UnityEngine;
using System.Collections; public class Spawner : MonoBehaviour {
Monster prototype;
// Use this for initialization
public Prototype(Monster _prototype)
{
this.prototype = _prototype;
} public GameObject createMonster()
{
return prototype.clone();
}
} Spawner PersonSpawner = new Spawner(People);
Spawner AnimalSpawner = new Spawner(Animal);
PersonSpawner.createMonster();
AnimalSpawner.createMonster();
利用泛型类实现原型模式
再进一步,我们可以建立一个SpawnerFor泛型类来更加专一的生成某种怪兽,SpawnerFor泛型类继承自Spawner类
using UnityEngine;
using System.Collections; public class SpawnerFor<T> : Spawner
where T : Monster
{
T prototype;
} Spawner s1;
Spawner s2;
void Start()
{
s1 = new SpawnerFor<PersonMonster>();
s2 = new SpawnerFor<AnimalMonster>();
s1.setPrototype(People);
s2.setPrototype(Animal);
s1.createMonster();
s2.createMonster();
}
如果不是返回gameobject,就要写一个这样的方法:
public GameObject createMonster()
{
return new T();
}
关于First Class type
最好的办法是把类型当做参数付给了生成类,这种类型叫做First Class类型,这样把要生成的怪兽的类型作为参数付给Spawner,Spawner就知道原型是要生成这种类型的参数了。
我们把类型分为三类:
First Class。该类型可以作为函数的参数和返回值,也可以赋给变量。
Second Class。该类型可以作为函数的参数,但不能从函数返回,也不能赋给变量。
Third Class。该类型作为函数参数也不行
也就是说First Class type可以把类型看作是对象来赋值、返回值等等。
但是在C++和C#中类的类型都不是First Class type,所以需要进行一些操作,像上面的方法那样。
实现结果
对原型进行数据建模
我们通过数据建模data modeling把代码变成实在的数据。
通
过这种方法我们不需要Monster的子类了,因为Monster里的属性都是相同的,我们只需要一个Monster类和一个存了各种敌人的数据的文件即
可。这种方式的好处在数据量大的游戏中尤为明显,省去大量代码。我们首先需要把每个怪兽的属性都储存起来,然后能提供给Spawner读取,这就是序列化
与反序列化。
也通过序列化和反序列化对原型进行数据建模。使用JSON,XML等等都可以,此处博主用JSON实现
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它基于ECMAScript的一个子集。易于人阅读和编写,同时也易于机器解析和生成(网络传输速率)。
JSON
有简易的语法,XML有范的标签形式,JSON和XML有一个很大的区别在于有效数据率。JSON作为数据包格式传输的时候具有更高的效率,这是因为
JSON不像XML那样需要有严格的闭合标签,这就让有效数据量与总数据包比大大提升,从而减少同等数据流量的情况下,网络的传输压力
具体可以查看写得很全很详细
我们可以在代码中生成JSON,也可以自己在txt中编写,大概的格式是这样的
{
"MonsterName": "Person",
"attack": ,
"defense": ,
"weapon": "Sword"
}
上面是本文的例子,但是如果敌人类是这样的:
{
"MonsterName": "dwarf saber",
"HP": ,
"characteristic": "DEF up",
"weapon": "sword",
"attacks": ["hack","chop"]
}
{
"MonsterName": "dwarf archer",
"HP": ,
"characteristic": "DEF up",
"weapon": "bow",
"attacks": ["shoot","sight"] }
{
"MonsterName": "dwarf caster",
"HP": ,
"characteristic": "DEF up",
"weapon": "wand",
"magic": ["fire ball","ice storm"] }
好吧,虽然在矮人中caster这个职阶并不常见(武器想写破尽万法之符来着,但是fate里没有矮人英雄啊。。矮人还是多出现在欧洲风游戏里= =;)
可
以明显发现里面的HP,characteristic属性是一样的,因为都是矮人,矮人本身的特性都是一样的,这里我们就出现了重复,解决方法就是使用享
元模式,把相同的单拿出来,或者放在最普通的“原怪兽”或者是一个比较简单的怪兽里,再在其他怪兽中加一个这个原模型方便取值,这里博主把它放在矮人平民
中。享元模式可以节省大量空间和读取时间:
{
"MonsterName": "dwarf civilian",
"HP": ,
"characteristic": "DEF up",
}
{
"MonsterName": "dwarf saber",
"prototype": "dwarf civilian",
"weapon": "sword",
"attacks": ["hack","chop"]
}
{
"MonsterName": "dwarf archer",
"prototype": "dwarf civilian",
"weapon": "bow",
"attacks": ["shoot","sight"] }
{
"MonsterName": "dwarf caster",
"prototype": "dwarf civilian",
"weapon": "wand",
"magic": ["fire ball","ice storm"]
}
也可以实现道具和武器的多元化,比如一把“火焰剑”就可以视为一把长剑,和附加伤害,还有一个好听的名字:
{
"Name": "FireBlaze",
"prototype": "long sword",
" additionalDamage ":
}
长剑中就包含了剑类所有的基本属性,比如基础伤害,武器相克效果,等等等。。这就是细微的改变带来丰富的变化!
接下来要说JSON在unity中的代码实现,
分为在txt中写入数据叫做序列化,和读取数据叫做反序列化,
首先我们需要LitJson.dll(文章最后github链接中含有本文测试所有游戏代码和LitJson.dll)
在类前面需要:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using LitJson;
using System.IO;
#if UNITY_EDITOR
using UnityEditor;
#endif
在.net中的解析方式,在unity中用最后一种
主要类 |
命名空间 |
先看写入文件方法:
void WriteJsonToFile(string path, string fileName)
{
System.Text.StringBuilder strB = new System.Text.StringBuilder();
JsonWriter jsWrite = new JsonWriter(strB);
jsWrite.WriteObjectStart();
jsWrite.WritePropertyName("Monster");//Monster为对象数组名 jsWrite.WriteArrayStart();//对象数组 jsWrite.WriteObjectStart();
jsWrite.WritePropertyName("MonsterName");
jsWrite.Write("Person");
jsWrite.WritePropertyName("attack");
jsWrite.Write();
jsWrite.WritePropertyName("defense");
jsWrite.Write();
jsWrite.WritePropertyName("weapon");
jsWrite.Write("Sword");
jsWrite.WriteObjectEnd();
jsWrite.WriteObjectStart();
jsWrite.WritePropertyName("MonsterName");
jsWrite.Write("Animal");
jsWrite.WritePropertyName("attack");
jsWrite.Write();
jsWrite.WritePropertyName("defense");
jsWrite.Write();
jsWrite.WritePropertyName("weapon");
jsWrite.Write("tooth");
jsWrite.WriteObjectEnd();
jsWrite.WriteObjectStart();
jsWrite.WritePropertyName("MonsterName");
jsWrite.Write("Dragon");
jsWrite.WritePropertyName("attack");
jsWrite.Write();
jsWrite.WritePropertyName("defense");
jsWrite.Write();
jsWrite.WritePropertyName("weapon");
jsWrite.Write("fire breath");
jsWrite.WriteObjectEnd(); jsWrite.WriteArrayEnd();
jsWrite.WriteObjectEnd();
Debug.Log(strB);
//创建文件目录
DirectoryInfo dir = new DirectoryInfo(path);
if (dir.Exists)
{
Debug.Log("This file is already exists");
}
else
{
Directory.CreateDirectory(path);
Debug.Log("CreateFile");
#if UNITY_EDITOR
AssetDatabase.Refresh();
#endif
}
//把json数据写到txt里
StreamWriter sw;
if (File.Exists(fileName))
{
//如果文件存在,那么就向文件继续附加(为了下次写内容不会覆盖上次的内容)
sw = File.AppendText(fileName);
Debug.Log("appendText");
}
else
{
//如果文件不存在则创建文件
sw = File.CreateText(fileName);
Debug.Log("createText");
}
sw.WriteLine(strB);
sw.Close();
#if UNITY_EDITOR
AssetDatabase.Refresh();
#endif }
然后是读出,读出方法我们放在Spawner类里
Monster ReadJsonFromTXT(string name)
{
//解析json
Monster monster = new Monster();
JsonData jd = JsonMapper.ToObject(txt.text);
print(jd.IsArray);
JsonData monsterData = jd["Monster"];
print(monsterData.IsArray);
//打印一下数组
for (int i = ; i < monsterData.Count; i++)
{
if (name == monsterData[i]["MonsterName"].ToString())
{
monster.MonsterName = monsterData[i]["MonsterName"].ToString();
monster.attack = int.Parse(monsterData[i]["attack"].ToString());
monster.defense = int.Parse(monsterData[i]["defense"].ToString());
monster.weapon = monsterData[i]["weapon"].ToString();
}
} return monster;
}
写好的JSON可以在这个网站中测试http://www.bejson.com/,这是博主生成的JSON
实现结果
JSON测试结果,成功生成Monster
总结
基本的好处就是对象可以深复制自己,可以很方便有无差错的生成实体,并且把本来大量的类和与之对应的生成类(而且还会随着扩充增加!),缩小成一个原型类, 一个生成类,一个数据文件,减少了大量重复的,甚至不重复的代码量!数据文件可以根据实际情况选择xml或者是JSON或者是别的。
进一步考虑,玩家们都喜欢丰富的游戏,像这样可以对数据进行微小的改动就会产生很多变化,代码量花费很少,还能产生丰富的游戏世界,何乐而不为?
测试用全部代码及dll文件已共享至GitHub
命令模式:游戏开发设计模式之命令模式(unity3d 示例实现)
对象池模式:游戏开发设计模式之对象池模式(unity3d 示例实现)
博主近期渲染:最近用unity5弄的一些渲染
---- by wolf96
游戏开发设计模式之原型模式 & unity3d JSON的使用(unity3d 示例实现)的更多相关文章
- 游戏开发设计模式之状态模式 & 有限状态机 & c#委托事件(unity3d 示例实现)
命令模式:游戏开发设计模式之命令模式(unity3d 示例实现) 对象池模式:游戏开发设计模式之对象池模式(unity3d 示例实现) 原型模式:游戏开发设计模式之原型模式 & unity3d ...
- 游戏开发设计模式之命令模式(unity3d 示例实现)
博主才学尚浅,难免会有错误,尤其是设计模式这种极富禅意且需要大量经验的东西,如果哪里书写错误或有遗漏,还请各位前辈指正. 打 算写设计模式的目的就是,首先自己可以理清思路,还有就是国内的设计模式资料很 ...
- 游戏开发设计模式之对象池模式(unity3d 示例实现)
前篇:游戏开发设计模式之命令模式(unity3d 示例实现) 博主才学尚浅,难免会有错误,尤其是设计模式这种极富禅意且需要大量经验的东西,如果哪里书写错误或有遗漏,还请各位前辈指正. 原理:从一个固定 ...
- C# Json反序列化 C# 实现表单的自动化测试<通过程序控制一个网页> 验证码处理类:UnCodebase.cs + BauDuAi 读取验证码的值(并非好的解决方案) 大话设计模式:原型模式 C# 深浅复制 MemberwiseClone
C# Json反序列化 Json反序列化有两种方式[本人],一种是生成实体的,方便处理大量数据,复杂度稍高,一种是用匿名类写,方便读取数据,较为简单. 使用了Newtonsoft.Json,可以自 ...
- 设计模式_11_原型模式(prototype)深拷贝、浅拷贝
设计模式_11_原型模式(prototype) 浅拷贝: package designPatternOf23; /** * 定义:用原型实例,指定创建对象的种类,并通过拷贝这些原型创建新的对象 * P ...
- C#设计模式(6)——原型模式(Prototype Pattern)
一.引言 在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创建这样的类实例,这未免会增加创建类的复杂度和耗费更多的内存空间,因为这样在 ...
- 乐在其中设计模式(C#) - 原型模式(Prototype Pattern)
原文:乐在其中设计模式(C#) - 原型模式(Prototype Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 原型模式(Prototype Pattern) 作者:weba ...
- C#设计模式之六原型模式(Prototype)【创建型】
一.引言 在开始今天的文章之前先说明一点,欢迎大家来指正.很多人说原型设计模式会节省机器内存,他们说是拷贝出来的对象,这些对象其实都是原型的复制,不会使用内存.我认为这是不对的,因为拷贝出来的每一个对 ...
- C#设计模式之五原型模式(Prototype Pattern)【创建型】
一.引言 在开始今天的文章之前先说明一点,欢迎大家来指正.很多人说原型设计模式会节省机器内存,他们说是拷贝出来的对象,这些对象其实都是原型的复制,不会使用内存.我认为这是不对的,因为拷贝出来的每一个对 ...
随机推荐
- Azure cache 的配置与应用
最近公司的项目要是用cloud Service 所以研究了下 Azure cache 的配置与使用. 首先创建项目 第二步 配置 cache worker role (1) 点击 cache work ...
- 从零开始 WIN8.1 下Android 开发环境搭建
一.JDK安装 当前最新版本是JDK8.0 地址http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-21331 ...
- [设计模式]解释器(Interpreter)之大胆向MM示爱吧
为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 “我刚写了个小程序,需要你来参与下.”我把MM叫到我的电脑旁,“来把下面这条命令打进去,这是个练习打(Pian)符(ni)号(de)的 ...
- 2013年12月26日 星期四 doxygen入门--很好
body{ font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI& ...
- 常用sql笔记
Student(S#,Sname,Sage,Ssex) 学生表Course(C#,Cname,T#) 课程表SC(S#,C#,score) 成绩表Teacher(T#,Tname) 教师表问题:1.查 ...
- 支付宝接口使用文档说明 支付宝异步通知(notify_url)与return_url.
支付宝接口使用文档说明 支付宝异步通知(notify_url)与return_url. 现支付宝的通知有两类. A服务器通知,对应的参数为notify_url,支付宝通知使用POST方式 B页面跳转通 ...
- #Leet Code# Same Tree
语言:Python 描述:使用递归实现 # Definition for a binary tree node # class TreeNode: # def __init__(self, x): # ...
- 为何要fork()两次来避免产生僵尸进程??
最近安装书上说的,开始搞多进程了..看到了一个好帖子,学习学习 http://blog.sina.com.cn/s/blog_9f1496990100y420.html 首先我们要明白,为什么要避免僵 ...
- 转: QtCreator调试程序时GDB崩溃
这个情况出现在QtCreator的2.5版以上,是由于新版QtCreator至少需要7.2 IIRC版的GDB.可以到:http://builds.qt-project.org/job/gdb-win ...
- HTML文档模式与盒模型
HTML文档根据文档顶部的doctype声明来决定渲染模式,有标准模式(Standards Mode)与怪异模式(Quirks mode,或叫做混杂模式)两种模式. IE5及以前默认总是表现为怪异模式 ...