• 它可以轻松解决很多其它引擎不能解决的问题,此外还有很多出众的地方,这都是现在越来越多的人选择unity3d游戏开发的原因,下面就让我们来看一下这些优点所在吧! 1可定制的IDE环境 U3D这种ALL IN ONE的...

    0?wx_fmt=gif&wxfrom=5&wx_lazy=1

    640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1


    unity3D是如今绝大多数游戏开发团队的首 选3D引擎,并且它在2D上的表现也及为优秀。它可以轻松解决很多其它引擎不能解决的问题,此外还有很多出众的地方,这都是现在越来越多的人选择unity3d游戏开发的原因,下面就让我们来看一下这些优点所在吧!


    1可定制的IDE环境


    U3D这种ALL IN ONE的设计思路,我在一个叫神咒的代码中见到过,集所有编辑器于一身。虽然神咒的编辑器不能自由扩展,但由于是公司内部的引擎,所以它的使用也很方便。比如,在场景中突然想要对一个模型的材质进行编辑,则选中此模型,右键,弹出材质编辑器即可。


    组件式思路,将这种关系变得更加紧密。你都感觉不到自己在使用一个材质编辑器,你会觉得自己是在操作这个模型本身,它的材质,它的碰撞器,它的对象结构等等。而一个好的引擎,必定得先有一个功能完备的编辑器。


    2基于Mono的开发脚本


    C/C++无疑是图形界的宠儿,也没有人想过用另一种语言来替 代它。即使是U3D,亦是如此。但早期使用C/C++编写的引擎,都理所当然地使用C/C++来作为上层逻辑的开发。


    又有一些,采用了纯脚本的模式,比如Python、LUA。脚本的好处在于更低的编码成本,而大家熟知的虚幻引擎以及有一个名不见经转的Torque,则自己整了一套开发语言。我想,它们的目的就是为了使大家能够以一种更安全的方式来编程, C++一不小心,则会带来内存和效率问题。


    它的使用成本,人员成本其实是高于其它语言的,而Mono C#JS,BOO的出现,则又一次让大家眼睛一亮。Mono的桥接,使得高效的C++图形引擎与带GC的内存安全语言进行结合。不仅减少了安全隐患,也使得大家编写跨平台代码时更佳容易。


    3基于组件的对象系统


    这是一个我最喜欢的系统,我也使用irrlicht引擎山寨过,山寨的过程中,几乎看完了它的组件参考手册,使我对unity3d游戏开发引擎的组件系统又有了新的认识。 同时,目前公司自主研发的引擎,也是这样的思想。不管我是在工作中,还是业余捣鼓都受组件系统的影响,慢慢的喜欢上了这种对象模式。


    4所见即所得


    这可以说是许多人最喜欢的特性,这也是G3D群里,问的人最多的特性,三天两头就有人问,G3D能不能像U3D一样在编辑器里预览游戏效果呀。


    U3D除了编辑后立即运行,还能在运行过程中时实编辑,查看效果。当然,运行过程中编辑对象的数据,会在停止后失效。(注意,对文件属性的修改,不会失效)


    5代码驱动的开发模式


    这种模式,可以使我们快速地构建一个原型。


    6多平台发布


    unity3d游戏开发支持的平台,无疑是当下较为流行的平台。满足绝大部分项目需求。早期的引擎,多以PC和CONSOLE为主。支持INDOWS,XBOX,PS2已经是很不错了。U3D便利的多平台发布特性,也使得它成为了当前性价比最 高的引擎的原因之一。


    7良好的生态圈


    AssetStore的出现,不仅使U3D的生态圈更加稳固,同时也提供了许多机会。 你可以制作插件放网上卖,赚取一些利益,也可以购买别人的插件,作为使用或者参考也好。有时候,购买一些插件,可以让你快速脱离当前的困境。 一个是解决进度问题,一个是解决思路问题,这是之前其它引擎不具备的。

    AI先锋

    ID:EnlightAI

    关注人工智能行业发展,介绍人工智能技术与产品

    展开全文
  • 各位朋友,大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.csdn.net/...比如博主在写《[Unity3D]Unity3D游戏开发之自由视角下的角色控制》和《[Unity3D]Unity3D游戏开发之角色控制漫谈》这两篇

            各位朋友,大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.csdn.net/qinyuanpei。今天我们来做点简单的东西,做个什么呢?答案就是截屏。作为一名热爱单机游戏的玩家,博主每次在玩游戏的同时截取游戏中比较喜欢的画面,特别是学习了Unity3D以后玩游戏的时候更多地是从一个游戏设计者的角度来看待游戏,换句话说,可能关注技术的成分更多一点吧。比如博主在写[Unity3D]Unity3D游戏开发之自由视角下的角色控制》[Unity3D]Unity3D游戏开发之角色控制漫谈》这两篇文章时,碰到博主无法把握的问题的时候就是通过玩游戏来体会的,在玩游戏的过程中博主总是会抓取博主比较喜欢的画面,如图,下面是博主收集的部分截图:




           好了,欣赏完美丽的风景,下面我们就来一起学习在Unity3D实现截屏,先给出实现截屏的三种实现方式:

    /// <summary>
    	/// 使用Application类下的CaptureScreenshot()方法实现截图
    	/// 优点:简单,可以快速地截取某一帧的画面、全屏截图
    	/// 缺点:不能针对摄像机截图,无法进行局部截图
    	/// </summary>
    	/// <param name="mFileName">M file name.</param>
    	private void CaptureByUnity(string mFileName)
    	{
    		Application.CaptureScreenshot(mFileName,0);
    	}
    
    	/// <summary>
    	/// 根据一个Rect类型来截取指定范围的屏幕
    	/// 左下角为(0,0)
    	/// </summary>
    	/// <param name="mRect">M rect.</param>
    	/// <param name="mFileName">M file name.</param>
    	private IEnumerator CaptureByRect(Rect mRect,string mFileName)
    	{
    		//等待渲染线程结束
    		yield return new WaitForEndOfFrame();
    		//初始化Texture2D
    		Texture2D mTexture=new Texture2D((int)mRect.width,(int)mRect.height,TextureFormat.RGB24,false);
    		//读取屏幕像素信息并存储为纹理数据
    		mTexture.ReadPixels(mRect,0,0);
    		//应用
    		mTexture.Apply();
    		
    		
    		//将图片信息编码为字节信息
    		byte[] bytes = mTexture.EncodeToPNG();  
    		//保存
    		System.IO.File.WriteAllBytes(mFileName, bytes);
    		
    		//如果需要可以返回截图
    		//return mTexture;
    	}
    
    	private IEnumerator  CaptureByCamera(Camera mCamera,Rect mRect,string mFileName)
    	{
    		//等待渲染线程结束
    		yield return new WaitForEndOfFrame();
    
    		//初始化RenderTexture
    		RenderTexture mRender=new RenderTexture((int)mRect.width,(int)mRect.height,0);
    		//设置相机的渲染目标
    		mCamera.targetTexture=mRender;
    		//开始渲染
    		mCamera.Render();
    		
    		//激活渲染贴图读取信息
    		RenderTexture.active=mRender;
    		
    		Texture2D mTexture=new Texture2D((int)mRect.width,(int)mRect.height,TextureFormat.RGB24,false);
    		//读取屏幕像素信息并存储为纹理数据
    		mTexture.ReadPixels(mRect,0,0);
    		//应用
    		mTexture.Apply();
    		
    		//释放相机,销毁渲染贴图
    		mCamera.targetTexture = null;   
    		RenderTexture.active = null; 
    		GameObject.Destroy(mRender);  
    		
    		//将图片信息编码为字节信息
    		byte[] bytes = mTexture.EncodeToPNG();  
    		//保存
    		System.IO.File.WriteAllBytes(mFileName,bytes);
    		
    		//如果需要可以返回截图
    		//return mTexture;
    	}
    
    }
    

          接下来,我们来调用这三个方法实现一个简单的截图的例子:

    //定义图片保存路径
    	private string mPath1;
    	private string mPath2;
    	private string mPath3;
    
    	//相机
    	public Transform CameraTrans;
    
    	void Start()
    	{
    		//初始化路径
    		mPath1=Application.dataPath+"\\ScreenShot\\ScreenShot1.png";
    		mPath2=Application.dataPath+"\\ScreenShot\\ScreenShot2.png";
    		mPath3=Application.dataPath+"\\ScreenShot\\ScreenShot3.png";
    	}
    
    	//主方法,使用UGUI实现
    	void OnGUI()
    	{
    		if(GUILayout.Button("截图方式1",GUILayout.Height(30))){
    			CaptureByUnity(mPath1);
    		}
    		if(GUILayout.Button("截图方式2",GUILayout.Height(30))){
    			StartCoroutine(CaptureByRect(new Rect(0,0,1024,768),mPath2));
    		}
    		if(GUILayout.Button("截图方式3",GUILayout.Height(30))){
    			//启用顶视图相机
    			CameraTrans.camera.enabled=true;
    			//禁用主相机
    			Camera.main.enabled=false;
    			StartCoroutine(CaptureByCamera(CameraTrans.camera,new Rect(0,0,1024,768),mPath3));
    		}
    	}
    
    
    	
           在第三中截图方式中,博主在场景里放了一个名为TopCamera的摄像机,它垂直向下投影到游戏场景里,这样可以使玩家看到场景的顶视图。这里我们用这个相机来测试第三个方法,此时需要先激活该相机。场景设置如图:


            我们下面来看三种方法截图的效果:





            从截图的效果来看,第一种方法的效果是最好的,不过定制化是个问题。第二种方法效果一般吧,感觉这里TextureFormat没有选好吧。第三种效果基本达到了博主想要的要求,不过摄像机的投影范围似乎没有设计好。这里我们发现第二张截图会把编辑器的窗口渲染到里面,博主认为是程序运行的时候,即使将Game窗口放到最大,仍然会受到窗口的影响,后来博主就把程序编译成可执行文件,不过程序运行完之后,博主却没有找到对应的截图。后来查找了官方的API才知道原因是这样的:

    Description

    Contains the path to the game data folder (Read Only).

    The value depends on which platform you are running on:

    Unity Editor: <path to project folder>/Assets

    Mac player: <path to player app bundle>/Contents

    iPhone player: <path to player app bundle>/<AppName.app>/Data

    Win player: <path to executablename_Data folder>

    Web player: The absolute url to the player data file folder (without the actual data file name)

    Flash: The absolute url to the player data file folder (without the actual data file name)

    Note that the string returned on a PC will use a forward slash as a folder separator
     

            显然,我们从这里可以知道Application.datapath在不同的平台上对应的位置。对于可执行(.exe,Windows平台)的文件,它对应在和应用程序对应的一个文件夹里,例如可执行文件的名字叫做UnityGame,那么对应的位置就是UnityGame_Data这个文件啦。所以博主的问题应该是出在没有在这里建一个ScreenShot的文件夹,希望大家以后做相关项目的时候注意一下吧。好了,这就是今天的内容了,希望大家喜欢啊。


    每日箴言:无所谓好或不好,人生一场虚空大梦,韶华白首,不过转瞬。惟有天道恒
    在,往复循环,不曾更改——慕容紫英。




       喜欢我的博客请记住我的名字:秦元培,我博客地址是blog.csdn.net/qinyuanpei。
       转载请注明出处,本文作者:秦元培,本文出处:http://blog.csdn.net/qinyuanpei/article/details/39185195




    展开全文
  • 09年还在和其它小伙伴开发引擎的时候,Unity3D就初露头角。 当时就对这种基于组件式的设计结构很不理解。 觉得拆分过于细致,同时影响效率。 而时至今日,UNITY3D已经成为了众多团队的首选3D引擎。 并且,随着...

    09年还在和其它小伙伴开发引擎的时候,Unity3D就初露头角。 当时就对这种基于组件式的设计结构很不理解。 觉得拆分过于细致,同时影响效率。

    而时至今日,UNITY3D已经成为了众多团队的首选3D引擎。 并且,随着Unity3D 4.3的发布,原生的2D支持也让人大开眼界。虽然Unity3d的原生2D功能还有很长的路要走,但也阻挡不了它称霸当下。

    2011年中,公司的引擎项目停止之后,我的目光便转到了U3D的身上,经过几番挣扎后,终于对基于组件式的对象模型有了新的认识。 而如今,这种模式,成为了我最推崇的模式。 因为它能解决我在设计引擎对象时的纠结。 而这些纠结,是我在先前的引擎开发中,一直不能优雅地解决的。

    首先,我们来说说U3D的好处。可能总结得不够完善,如果有不足的地方,就表示我自己没有体验到。

    声明:为了不打扰大家的雅兴,所以,这里只讲它的优点,想看缺点的朋友,请看此文章的姐妹篇 Unity3D使用经验总结 缺点篇

    一、可定制的IDE环境

    U3D这种ALL IN ONE的设计思路,我在一个叫神咒的代码中见到过。 集所有编辑器于一身。 虽然神咒的编辑器不能自由扩展,但由于是公司内部的引擎,所以,它的使用,也很方便。 比如,在场景中突然想要对一个模型的材质进行编辑,则选中此模型,右键,弹出材质编辑器即可。  U3D的组件式思路,将这种关系变得更加紧密。 你都感觉不到自己在使用一个材质编辑器。 你会觉得,你是在操作这个模型本身。 它的材质,它的碰撞器,它的对象结构等等。

    回想一开始进入游戏行业的时候,天天啃着代码。 当时觉得代码就是一切,各种认为很牛X的代码,都忍不住读上一番。 而随着时间的推移,特别是经过项目的洗礼后。 突然发现编辑器是多么的重要。 就我做的第一个页游来说,起手前两个星期,我们就做了动画编辑器,场景编辑器。而最终证明,因为这两个简陋的编辑器,使我们后面的工作变得更加容易。

    因此,一个好的引擎,必定得先有一个功能完备的编辑器

    二、基于Mono的开发脚本

    C/C++无疑是图形界的宠儿,也没有人想过用另一种语言来替代它。即使是U3D,亦是如此。 但是,早期使用C/C++编写的引擎,都理所当然地使用C/C++来作为上层逻辑的开发。 又有一些,采用了纯脚本的模式。比如Python,LUA。 脚本的好处在于更低的编码成本(经过仔细研究,我发现,这是由于写脚本语言的心态和写C++的心态导致的。 写C++的时候,总是想着代码的复用度,而在脚本的时候,很多时间会认为,这个脚本,就是为这个对象服务的,那我就按照策划需求来写就可以了。 我想,这也是许多时候,脚本语言存在的意义。特别是早期引擎中,使用脚本来处理一些关键的事件响应)。  而大家熟知的虚幻引擎以及有一个名不见经转的Torque,则自己整了一套开发语言。 我想,它们的目的,就是为了使大家能够以一种更安全的方式来编程, C++一不小心,则会带来内存和效率问题。 它的使用成本,人员成本其实是高于其它语言的。  Mono C# JS,BOO的出现,再一次让大家的眼睛一亮,原来,引擎可以这样整。

    Mono的桥接,使得高效的C++图形引擎与带GC的内存安全语言进行结合。不仅减少了安全隐患,也使得大家编写跨平台代码时更佳容易。 同时,这类语言的反射机制,更适合做编辑器。而比起先前的一些DIY语言和像LUA这样的小巧型语言,Mono使脚本编程可以进行DEBUG,而不单纯的靠PRINT输出。

    这里就顺带说一下三个语言的区别

    C# 这是我见过的大型项目中使用得最多的语言,也是我比较喜欢的语言。 因为它和C++很像,同时严格的类型和语法检查。

    JS 在帮一些朋友做小东西的时候,使用过这个语言,由于mono自带的提示功能,写起来还是挺顺手。 但总给我一种摸不着头脑的感觉。 并且U3D给的JS,不是严格的JS,有些语法不支持,而有些语法又很特别。

    BOO 完全没有使用过,貌似也很少有人使用。

    三、基于组件的对象系统

    这是一个我最喜欢的系统,我也使用irrlicht引擎山寨过,山寨的过程中,几乎看完了它的组件参考手册,使我对U3D引擎的组件系统又有了新的认识。 同时,目前公司自主研发的引擎,也是这样的思想。 不管我是在工作中,还是业余捣鼓都受组件系统的影响。 慢慢的,喜欢上了这种对象模式。

    之前在做一个RTS游戏项目的时候,参考了著名开源项目 0.A.D的代码。 当时只是为了去寻找LOS和多单位协同寻路的方案。 但在参考其代码的时候,发现了它整个系统,都是基于组件式的。又一次,对组件式有了好感。 而经过仔细思索后。 回到了我一直坚持的子系统划分法的游戏框架。 当我不禁感叹,原来,自己也一直是在组件式。 只不过,我的组件式,是MANAGER方式,MANANGER内部进行对应的实体管理、。 比如,背包系统,则只负责玩家背包数据,背包使用,背包相关的功能。 不管是数据存储,还是与前端通信,都是背包系统自己在负责,其它模块完全不需要干涉。  而U3D中的组件系统,则将这个粒度划得更仔细了……。  这对于早期的像OGRE的entity系统。仅仅是认为对象可以由子对象构成,可以说是一个质的变化。

    早期的引擎,基本上都是继承优先的设计方案,更多时候考虑的是编码的便利性,且引擎的走向都具有针对性。 而当面对一些复杂情况的时候,继承式的编码是十分麻烦的。 并且,对于JAVA,C#这样的语言,并没有提供多继承能力。 因此,继承式的编程,在面对越来越广泛的游戏需求的时候。显得无能为力。 组件式则是一种聚合优先的编码方式,它的复用度和伸缩度,都远远大于继承。 唯一让一些C++程序员觉得不太顺眼的,可能就是过多的变量和虚函数调用开销吧。 但这些,在当下来说,都不是问题。 影响大众步伐的,早已不是那种语言特性本身导致的开销。更多的,是如何使我们高效率,高质量地完成一个游戏。 因此组件模式已经成为必然。 从新版的UE4的变革,以及畅游的G3D,国外一个开源的godot引擎,就可以看出来,大家对组件模式,已经有了深深的好感和接受度。

    四、所见即所得

    这可以说是许多人最喜欢的特性,这也是G3D群里,问的人最多的特性,三天两头就有人问,G3D能不能像U3D一样在编辑器里预览游戏效果呀。

    U3D除了编辑后立即运行,还能在运行过程中时实编辑,查看效果。当然,运行过程中编辑对象的数据,会在停止后失效。(注意,对文件属性的修改,不会失效)

    五、代码驱动的开发模式

     这种模式,可以使我们快速地构建一个原型。 对于U3D中的MonoBehaviour来说,它扮演的,就是如何驱动它的目标对象。 因此,你可以将你的对象的各种能力分配到不同的脚本组件中,然后根据对象的需求来挂接。

    六、多平台发布

    U3D支持的平台,无疑是当下较为流行的平台。 满足绝大部分项目需求。 早期的引擎,多以PC和CONSOLE为主。 支持WINDOWS,XBOX,PS2已经是很不错了。 U3D便利的多平台发布特性,也使得它成为了当前性价比最高的引擎的原因之一。

    也有许多公司正在自主研发引擎,或者是将先前的PC引擎修改为多平台(IOS+ANDROID居多)。 但这也档不了U3D的步伐。

    七、良好的生态圈

    在使用公司引擎的时候,我就发现,若我遇上一个问题,只能问公司的老员工们,或者找其它引擎TEAM寻求帮助。而U3D这种生态圈,不是一天两天能形成的。GOOGLE,百度,各种论坛,都能很容易找到自己想要解决的问题。 而对于一些经验上的问题,也有不少人总结。 这使得后来者,可以快速上手引擎。

    而AssetStore的出现,不仅使U3D的生态圈更加稳固,同时也提供了许多机会。 你可以制作插件放网上卖,赚取一些利益,也可以购买别人的插件,作为使用或者参考也好。 有时候,购买一些插件,可以让你快速脱离当前的困境。 一个是解决进度问题,一个是解决思路问题。 这是之前其它引擎不具备的。

    展开全文
  • 今天想和大家分享的内容是RPG游戏游戏存档的实现,因为最近在做一个RPG游戏的项目,所以遇到这个问题就随时记录下来,在对知识进行总结的同时可以将这种思路或者想法分享给大家,这是一件快乐而幸运的事情。...

    喜欢我的博客请记住我的名字:秦元培,我的博客地址是:http://qinyuanpei.com
    转载请注明出处,本文作者:秦元培, 本文出处:http://blog.csdn.net/qinyuanpei/article/details/47775979

      大家好,我是秦元培,欢迎大家关注我的博客。近期博客的更新频率基本直降到冰点,因为这段时间实在是忙得没有时间来写博客了。今天想和大家分享的内容是RPG游戏中游戏存档的实现,因为最近在做一个RPG游戏的项目,所以遇到这个问题就随时记录下来,在对知识进行总结的同时可以将这种思路或者想法分享给大家,这是一件快乐而幸运的事情。我讨厌写按部就班的技术教程,因为我觉得学习是一种自我的探索行为,如果一切都告诉你了,探索的过程便会变得没有意义了。

      游戏存档是一种在单机游戏中特别常见的机制,这种机制是你在玩网络游戏的时候无法体验到的,你知道每次玩完一款单机游戏都会把游戏存档保存起来是一种怎样的感觉吗?它就像是一个征战沙场的将军将陪伴自己一生金戈铁马的宝剑静静地收入剑匣,然而每一次打开它的时候都会不由自主的热泪盈眶。人的本性其实就是游戏,我们每一天发生的故事何尝不是一个游戏?有时候让我们怀念的可能并不是游戏本身,而只是搁浅在时光里的那时的我们。好了,游戏存档是我们在游戏世界里雪泥鸿爪,它代表了我们曾经来到过这个世界。以RPG游戏为例,一个一般化的游戏存档应该囊括以下内容:

    • 角色信息:指一切表征虚拟角色成长路线的信息,如生命值、魔法值、经验值等等。
    • 道具信息:指一切表征虚拟道具数量或者作用的信息,如药品、道具、装备等等。
    • 场景信息:指一切和游戏场景相关的信息,如场景名称、角色在当前场景中的位置坐标等等。
    • 事件信息:指一切和游戏事件相关的信息,如主线任务、支线任务、触发性事件等等。

      从以上信息划分的层次来看,我们可以发现在游戏存档中要储存的信息相对是比较复杂的,那么我们这里不得不说说Unity3D中的数据持久化方案PlayerPrefs。该方案采用的是一种键值型的数据存储方案,支持int、string、float三种基本数据类型,通过键名来获取相对应的数值,当值不存在时将返回一个默认值。这种数据存储方案本质上是将数据写入到一个Xml文件。这种方案如果用来存储简单的信息是没有问题的,可是如果用它来存储游戏存档这样负责的数据结构就显得力不从心了。一个更为重要的问题是在数据持久化的过程中我们希望得到是一个结构化的【游戏存档】实例,显然此时松散的PlayerPrefs是不能满足我们的要求的。因此我们想到了将游戏数据序列化的思路,常见的数据序列化思路主要有Xml和JSON两种形式,在使用Xml的数据序列化方案的时候通常有两种思路,即手动建立数据实体和数据字符间的对应关系基于XmlSerializer的数据序列化。其中基于XmlSerializer的数据序列化是利用了[Serializable]这样的语法特性来帮助.NET完成数据实体和数据字符间的对应关系,两种思路本质上一样的。可是我们知道Xml的优点是可读性强,缺点是冗余信息多,因此在权衡了两种方案的利弊后,我决定采用JSON来作为数据序列化的方案,而且JSON在数据实体和数据字符间的对应关系上有着天然的优势,JSON所做的事情不就是将数据实体转化为字符串和从一个字符串中解析出数据实体吗?所以整个方案基本一气呵成。好了,下面我们来看具体的代码实现过程吧!

    一、JSON的序列化和反序列化

      这里我使用的是Newtonsoft.Json这个类库,相信大家都是知道的了!因此,序列化和反序列化特别简单。

    /// <summary>
        /// 将一个对象序列化为字符串
        /// </summary>
        /// <returns>The object.</returns>
        /// <param name="pObject">对象</param>
        /// <param name="pType">对象类型</param>
        private static string SerializeObject(object pObject)
        {
            //序列化后的字符串
            string serializedString = string.Empty;
            //使用Json.Net进行序列化
            serializedString = JsonConvert.SerializeObject(pObject);
            return serializedString;
        }
    
        /// <summary>
        /// 将一个字符串反序列化为对象
        /// </summary>
        /// <returns>The object.</returns>
        /// <param name="pString">字符串</param>
        /// <param name="pType">对象类型</param>
        private static object DeserializeObject(string pString,Type pType)
        {
            //反序列化后的对象
            object deserializedObject = null;
            //使用Json.Net进行反序列化
            deserializedObject=JsonConvert.DeserializeObject(pString,pType);
            return deserializedObject;
        }

    二、Rijandel加密/解密算法

      因为我们这里要做的是一个游戏存档的方案设计,因为考虑到存档数据的安全性,我们可以考虑采用相关的加密/解密算法来实现对序列化后的明文数据进行加密,这样可以从一定程度上保证游戏存档数据的安全性。因为博主并没有深入地研究过加密/解密方面的内容,所以这里仅仅提供一个从MSDN上获取的Rijandel算法,大家感兴趣的话可以自行去研究。

    /// <summary>
        /// Rijndael加密算法
        /// </summary>
        /// <param name="pString">待加密的明文</param>
        /// <param name="pKey">密钥,长度可以为:64位(byte[8]),128位(byte[16]),192位(byte[24]),256位(byte[32])</param>
        /// <param name="iv">iv向量,长度为128(byte[16])</param>
        /// <returns></returns>
        private static string RijndaelEncrypt(string pString, string pKey)
        {
            //密钥
            byte[] keyArray = UTF8Encoding.UTF8.GetBytes(pKey);
            //待加密明文数组
            byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(pString);
    
            //Rijndael解密算法
            RijndaelManaged rDel = new RijndaelManaged();
            rDel.Key = keyArray;
            rDel.Mode = CipherMode.ECB;
            rDel.Padding = PaddingMode.PKCS7;
            ICryptoTransform cTransform = rDel.CreateEncryptor();
    
            //返回加密后的密文
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
        }
    
        /// <summary>
        /// ijndael解密算法
        /// </summary>
        /// <param name="pString">待解密的密文</param>
        /// <param name="pKey">密钥,长度可以为:64位(byte[8]),128位(byte[16]),192位(byte[24]),256位(byte[32])</param>
        /// <param name="iv">iv向量,长度为128(byte[16])</param>
        /// <returns></returns>
        private static String RijndaelDecrypt(string pString, string pKey)
        {
            //解密密钥
            byte[] keyArray = UTF8Encoding.UTF8.GetBytes(pKey);
            //待解密密文数组
            byte[] toEncryptArray = Convert.FromBase64String(pString);
    
            //Rijndael解密算法
            RijndaelManaged rDel = new RijndaelManaged();
            rDel.Key = keyArray;
            rDel.Mode = CipherMode.ECB;
            rDel.Padding = PaddingMode.PKCS7;
            ICryptoTransform cTransform = rDel.CreateDecryptor();
    
            //返回解密后的明文
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
            return UTF8Encoding.UTF8.GetString(resultArray);
        }

    三、完整代码

      好了,下面给出完整代码,我们这里提供了两个公开的方法GetData()和SetData()以及IO相关的辅助方法,我们在实际使用的时候只需要关注这些方法就可以了!

    /**
     * Unity3D数据持久化辅助类
     * 作者:秦元培
     * 时间:2015年8月14日
     **/
    
    using UnityEngine;
    using System.Collections;
    using System;
    using System.IO;
    using System.Text;
    using System.Security.Cryptography;
    using Newtonsoft.Json;
    
    public static class IOHelper
    {
        /// <summary>
        /// 判断文件是否存在
        /// </summary>
        public static bool IsFileExists(string fileName)
        {
            return File.Exists(fileName);
        }
    
        /// <summary>
        /// 判断文件夹是否存在
        /// </summary>
        public static bool IsDirectoryExists(string fileName)
        {
            return Directory.Exists(fileName);
        }
    
        /// <summary>
        /// 创建一个文本文件    
        /// </summary>
        /// <param name="fileName">文件路径</param>
        /// <param name="content">文件内容</param>
        public static void CreateFile(string fileName,string content)
        {
            StreamWriter streamWriter = File.CreateText(fileName);
            streamWriter.Write(content);
            streamWriter.Close();
        }
    
        /// <summary>
        /// 创建一个文件夹
        /// </summary>
        public static void CreateDirectory(string fileName)
        {
            //文件夹存在则返回
            if(IsDirectoryExists (fileName))
                return;
            Directory.CreateDirectory(fileName);
        }
    
        public static void SetData(string fileName,object pObject)
        {
            //将对象序列化为字符串
            string toSave = SerializeObject(pObject);
            //对字符串进行加密,32位加密密钥
            toSave = RijndaelEncrypt(toSave, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
            StreamWriter streamWriter = File.CreateText(fileName);
            streamWriter.Write(toSave);
            streamWriter.Close();
        }
    
        public static object GetData(string fileName,Type pType)
        {
            StreamReader streamReader = File.OpenText(fileName);
            string data = streamReader.ReadToEnd();
            //对数据进行解密,32位解密密钥
            data = RijndaelDecrypt(data, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
            streamReader.Close();
            return DeserializeObject(data,pType);
        }
    
        /// <summary>
        /// Rijndael加密算法
        /// </summary>
        /// <param name="pString">待加密的明文</param>
        /// <param name="pKey">密钥,长度可以为:64位(byte[8]),128位(byte[16]),192位(byte[24]),256位(byte[32])</param>
        /// <param name="iv">iv向量,长度为128(byte[16])</param>
        /// <returns></returns>
        private static string RijndaelEncrypt(string pString, string pKey)
        {
            //密钥
            byte[] keyArray = UTF8Encoding.UTF8.GetBytes(pKey);
            //待加密明文数组
            byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(pString);
    
            //Rijndael解密算法
            RijndaelManaged rDel = new RijndaelManaged();
            rDel.Key = keyArray;
            rDel.Mode = CipherMode.ECB;
            rDel.Padding = PaddingMode.PKCS7;
            ICryptoTransform cTransform = rDel.CreateEncryptor();
    
            //返回加密后的密文
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
        }
    
        /// <summary>
        /// ijndael解密算法
        /// </summary>
        /// <param name="pString">待解密的密文</param>
        /// <param name="pKey">密钥,长度可以为:64位(byte[8]),128位(byte[16]),192位(byte[24]),256位(byte[32])</param>
        /// <param name="iv">iv向量,长度为128(byte[16])</param>
        /// <returns></returns>
        private static String RijndaelDecrypt(string pString, string pKey)
        {
            //解密密钥
            byte[] keyArray = UTF8Encoding.UTF8.GetBytes(pKey);
            //待解密密文数组
            byte[] toEncryptArray = Convert.FromBase64String(pString);
    
            //Rijndael解密算法
            RijndaelManaged rDel = new RijndaelManaged();
            rDel.Key = keyArray;
            rDel.Mode = CipherMode.ECB;
            rDel.Padding = PaddingMode.PKCS7;
            ICryptoTransform cTransform = rDel.CreateDecryptor();
    
            //返回解密后的明文
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
            return UTF8Encoding.UTF8.GetString(resultArray);
        }
    
    
        /// <summary>
        /// 将一个对象序列化为字符串
        /// </summary>
        /// <returns>The object.</returns>
        /// <param name="pObject">对象</param>
        /// <param name="pType">对象类型</param>
        private static string SerializeObject(object pObject)
        {
            //序列化后的字符串
            string serializedString = string.Empty;
            //使用Json.Net进行序列化
            serializedString = JsonConvert.SerializeObject(pObject);
            return serializedString;
        }
    
        /// <summary>
        /// 将一个字符串反序列化为对象
        /// </summary>
        /// <returns>The object.</returns>
        /// <param name="pString">字符串</param>
        /// <param name="pType">对象类型</param>
        private static object DeserializeObject(string pString,Type pType)
        {
            //反序列化后的对象
            object deserializedObject = null;
            //使用Json.Net进行反序列化
            deserializedObject=JsonConvert.DeserializeObject(pString,pType);
            return deserializedObject;
        }
    }

      这里我们的密钥是直接写在代码中的,这样做其实是有风险的,因为一旦我们的项目被反编译,我们这里的密钥就变得很不安全了。这里有两种方法,一种是把密钥暴露给外部方法,即在读取数据和写入数据的时候使用同一个密钥即可,而密钥可以采取由机器MAC值生成的方法,这样每台机器上的密钥都是不同的可以防止数据被破解;其次可以采用DLL混淆的方法让反编译者无法看到代码中的内容,这样就无法获得正确的密钥从而无法获得存档里的内容了。

    四、最终效果

    好了,最后我们来写一个简单的测试脚本:

    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    
    public class TestSave : MonoBehaviour {
    
    
        /// <summary>
        /// 定义一个测试类
        /// </summary>
        public class TestClass
        {
            public string Name = "张三";
            public float Age = 23.0f;
            public int Sex = 1;
    
            public List<int> Ints = new List<int> ()
            {
                1,
                2,
                3
            };
        }
    
        void Start () 
        {
            //定义存档路径
            string dirpath = Application.persistentDataPath + "/Save";
            //创建存档文件夹
            IOHelper.CreateDirectory (dirpath);
            //定义存档文件路径
            string filename = dirpath + "/GameData.sav";
            TestClass t = new TestClass ();
            //保存数据
            IOHelper.SetData (filename,t);
            //读取数据
            TestClass t1 = (TestClass)IOHelper.GetData(filename,typeof(TestClass));
    
            Debug.Log(t1.Name);
            Debug.Log(t1.Age);
            Debug.Log(t1.Ints);
        }
    
    
    }

      脚本执行结果:

    p1

      加密后游戏存档:

    p2

      好了,这就是今天的内容了,希望大家能够喜欢,有什么问题可以给我留言,谢谢!
      感谢风宇冲Unity3D教程宝典之两步实现超实用的XML存档一文提供相关思路!

    喜欢我的博客请记住我的名字:秦元培,我的博客地址是:http://qinyuanpei.com
    转载请注明出处,本文作者:秦元培, 本文出处:http://blog.csdn.net/qinyuanpei/article/details/39717795

    展开全文
  • Unity3d游戏开发入门难不难?2019版Unity3d游戏开发学习指南,你值得拥有。游戏开发需要懂几种语言?基础语言C/C++,这个是必选项。要提高效率,就还得会点汇编语言。一些东西需要重复性的工作来做,我们是不是应该...

    如今游戏越来越火热,让人觉得好玩的同时,也不禁想自己动手做游戏开发,那么如何做游戏开发,做游戏开发又需要哪些技术呢?Unity3d游戏开发入门难不难?2019版Unity3d游戏开发学习指南,你值得拥有。
    游戏开发需要懂几种语言?基础语言C/C++,这个是必选项。要提高效率,就还得会点汇编语言。一些东西需要重复性的工作来做,我们是不是应该会点脚本语言?至少不用重复去干某一件事情。
    另外,Direct X是建立在OpenGL上的,OpenGL语法类似C语言,游戏执行时被编译加载到显卡上。在实现硬件渲染速度的同时,又实现了软件编程的灵活性。游戏开发人员,都对游戏开发的绝大部分知识都有一定了解。2019版Unity3d游戏开发学习指南
    如果是客户端程序员,基本上需要了解以下知识:图形学,图形API(D3D/OGL),算法/数据结构,物理,游戏逻辑,音乐音效,人工智能,编程语言,设计模式,操作系统,工具编写,UI。
    首先要从基础开始,慢慢熟悉引擎基本信息后,再学编程语言。当然,也可以先学习编程语言。次序不重要,重要的是兴趣。
    Unity3D技术的适用平台:iOS、Android、Windows phone 8、Tizen、Microsoft Windows、Windows Store应用程序、Mac、Linux/Steam OS、网络播放器、WebGL、PlayStation3.
    除此以外,还可以用在PlayStation4、PlayStation Vita版、Xbox One、Xbox 360、Wii U、Android TV、Samsung SMART TV、Oculus Rift、Gear VR、Microsoft Hololens、Playstation VR.
    编程语言包括:C#、JavaScript、Boo。
    优点有:方便易用,中文资料丰富,跨平台,商店资源丰富,可视化开发。
    希望对大家有帮助~

    转载于:https://blog.51cto.com/14070516/2316527

    展开全文
  • 游戏开发者眼中的Unity 3D网页游戏测评报告
  • 整合多种DCC文件格式,包含3dsMax,Maya,Lightwave,Collade等文档,可直接拖拽到Unity中,除原有内容外,还包含Mesh、多UVs,Vertex,Colors、骨骼动画等功能,提升7游戏制作的资源应用。 2.高性能的灯光照明系统 ...
  • 今天博主想和大家探讨的是RPG游戏中剧情呈现的相关策略,我们知道一个RPG游戏主要是通过将玩家带入到游戏世界中进行某种“体验”进而影响角色的成长。这句话其实指出了RPG游戏的两个特点,即高度的代入感和角色的可...
  • 博主今天想和大家分享的是Unity3D中的动态阴影,昨天博主重温了一下自己喜欢的游戏《古剑奇谭2》,发现更新后游戏增加了三个免费的DLC。这三个DLC主要是对主线剧情的一个扩展,博主这里最喜欢的是可控角色初七。从...
  • unity-横向比较unity3d与其他的游戏引擎的优势与不足
  • 免费。门槛低。插件多。受众很多。 因此 有才能的人更能 展现出自身的优势。...因此使用Unity3D,会 吸引更多 的开发者 开发出 更多 有趣 好玩的 独立游戏。                  ...
  • 本节书摘来自异步社区《Unity 3D 游戏开发技术详解与典型案例》一书中的第1章,第1.1节Unity 3D基础知识概览,作者 吴亚峰 , 于复兴,更多章节内容可以访问云栖社区“异步社区”公众号查看 1.1 Unity 3D基础知识概览...
  • Unity3D如何快速入门

    2018-11-29 12:47:08
    目录Unity3D游戏引擎介绍Unity3D学习历程Unity3D快速入门途径 Unity3D游戏引擎介绍 Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的...
  • 日前,网络游戏行业正以肉眼可见的速度发展,行业受众,也就是玩家也随着行业的发展变得愈来愈挑剔。从画面到音乐音效,从玩法到社会系统,玩家都能从中...在页游手游的发展过程中,Unity3D这个字眼相比也是逐步进入大
  • 今天想和大家分享的是目前在移动平台上较为流行的关卡系统,关卡系统通常是单机手机游戏如《愤怒的小鸟》、《保卫萝卜》中对游戏内容的组织形式,玩家可通过已解锁的关卡(默认第一关是已解锁的)获取分数进而解锁新的...
  • unity3d 各大插件评测

    2018-01-30 15:00:43
    引言:想用Unity3D制作优秀的游戏,插件是必不可少的。工欲善其事必先利其器。本文主旨是告诉使用Unity3D引擎的同学们如何根据需求选择适当的工具。为此我写了插件的经验及理论讲解,涉及插件的 学习/辨别/选择/...
1 2 3 4 5 ... 20
收藏数 2,896
精华内容 1,158