Skip to content
游戏流氓!

UE4中自定义Actor初始化相关备忘

UE4中自定义Actor初始化相关备忘 published on UE4中自定义Actor初始化相关备忘有2条评论

    这段时间在用UE4做一个生成美术素材插件,插件有2个Module一个是Runtime的,一个是Editor的,Runtime插件中我有些自定义的Actor,这些Actor就是用来在场景中显示我生成的游戏素材。

    使用时,我习惯在UE4 Editor中会先新建一个Class Blueprint 也就是类蓝图,派生于我的这个C++的Actor,如果从性能上考虑,这当然是不太好的,但是如果游戏是以原型开发模式来制作,那么做个Class BP就是必须的了,应为最终是策划在用这个东西,可以开放一些可以定制化的参数、暴露一些个接口出来给策划们去折腾。


    这里啰嗦几一句,在刚开始接触UE4的时候,我以为这些能在Content Browser里看到的Blueprint就类似于C++的类,其实这样理解是完全错误错误的,在做这个插件的时候,我才意识到这个BP其实已经是一个实例了。BP这个东西,其实类似Unity3D里的Prefab(预制体),它本身就能持久化存储数据,以至于你没有在Level里使用它,它上边保存的数据也不会丢失。当我们把一个BP拖到场景中的时候,其实它并不仅仅是new了一个新的Actor的实例出来,其实它是以当前这个BP实例作为模板,克隆了一个新的实例,呃……克隆是Unity3D的说法,用UE4的说法是Spawn了一个实例,用C++的说法就应该是对象的深拷贝,当然UE4实现这个过程不是用复制构造函数来做的,它门有一套流程(有兴趣的朋友具体可以跟一下就知道整个流程了)。


    现在扯回来,这个C++ Actor根据不同情况(注意:这里说的是你自定义的C++ Actor,并不是派生出来的BP Actor),可以在几个地方做初始化:


    A、C++的构造函数:

        特点:就是没有特点,是C++类实例化都必须调用此函数。

        这里是最最初级的初始化,万变不离其中嘛,只要是C++的类,都必须经过构造函数。

        在这里你可以创建RootComponent以及它的SubComponent,还可以给你的字段做一些低级基础的初始化工作,比如初始化非UE4架构中的类或者第三方类,可以对应析构中做释放,后面几个初始化函数初始的内容我实在找不到对应的地方做释放,可能做C++养成的坏习惯,比如内存分配这些,我老喜欢用一些成对的函数来做避免产生泄露。而UE4本身其实是做了GC这种事的,好像就是想简化C++开发人员要考虑的事情。

        需要注意的是这里的初始化工作,只会在编辑器或者游戏中关卡被反持久化时有效,如果在后面的函数中覆盖了这些初始化工作,那么这里的初始化其实是无效的。


    B、ACustomActor::PostLoad():

        特点:需要主动调用基类的函数。

        这是个Actor基类所实现过的虚函数,你如果需要override它,请记得调用Super::PostLoad(),切记!切记!

        这个函数有意思的地方在于,它刚好提供了一个反序列化或Spawn之后的构造函数调用的功能,你完全可以在这里初始化你类实例里的一些字段。

        注意:最好不要这里进行增量的行为,在Editor模式下如果做增量行为,当你不自觉的按下Ctrl+S 或者 编辑器AutoSave时,被增量的字段或内容会被永远保存下来,并影响到你所有的派生于此的BP Actor。这个函数是发生在反持久化之后,当Save的时会发生持久化操作。你的一切改变数据的操作,这里都将被永远保存到磁盘上!!!

    C、ACustomActor::PostActorCreated():

        特点:无需主动调用基类的函数,除非你想用基类的实现来做一些事情。

        这是一个Actor基类所拥有的虚函数,但Actor并未实现它。

        它工作在反序列化或者Spawn之后,也就是说你真的想在程序中改变当前实例的值的时候,在这里做才有效的。

        感觉在这里做初始化最好,当然这是我个人看法。


    D、ACustomActor::OnConstruction(const FTransform& Transform):

        特点:在此你可以初始化一些Component数据的初始化工作(包括派生于此的BP Actor的组件到这步也是创建完成了的喔~~~!!!

        这个函数是所有组件、子组件完全创建并初始化完成,也就是说在Editor里,你能在这里改变派生于此类的BP Actor的位置了。(父类控制子类初始化行为)

        比较烦的就是,要多敲一个很烦的参数。

        这个函数比较有意义的地方就是能用父类控制子类初始化行为


    E、ACustomActor::BeginPlay():

        特点:只在Level开始的时候调用一次。

        官方其实比较推荐用的是这个了。

        我觉得有利有弊吧,这个函数可能会在不同的地方被调用,那是不是容易发生重复把一些已经初始化完在使用的数据又给初始化了呢?呃,不过也许是我想多了……


    F、


    G、


    H、……


    这里还有其他几个,有些是Component相关的初始化的,没有太深入研究,以后用到再慢慢补进来吧,先占个位置吧。。。

UE4中的那些坑

UE4中的那些坑 published on UE4中的那些坑无评论

最近一直在玩UE4,从4.6.x到现在的4.10.4,期间遇到不少的坑。 

准备逐步记录下来,避免以后再遇到。

最后更新于:2016-03-29

坑一:4.10.4中,在蓝图中创建一个派生于C++插件中的Actor时,不重启编辑器便实例化(拖到场景中)这个Actor(类蓝图)的话,会丢失C++Actor类中对材质的赋值。

解决办法:创建Bp Actor后,保存并关闭Editor,然后重启。这时再使用就没有任何问题了。

故事:在写一个插件时,写了两个模块,一个是基于Runtime的一个是Editor的。准备在Game中创建一个基于Runtime中派生的蓝图类时,发现这个BPActor拖到场景中不工作。

    那个急啊,最关键是之前在另一个机器上测试都对的。为啥突换了个机器然不行了,然后开始改代码,翻论坛,耗费了大概5、6个小时无解。最后再头有点头晕眼花的情况下,

    创建了一个BPActor,但是因为眼花不小心点错了位置,把Editor关了,就在我一边重启Editor,一边骂自己是猪的时后,惊奇的发现这个BPActor上的所有材质、纹理都正常了。然后看了看时间,大骂了一句UE4搞你妹啊~~


关于Unity3D如何写游戏逻辑部分的思考

关于Unity3D如何写游戏逻辑部分的思考 published on 关于Unity3D如何写游戏逻辑部分的思考有97条评论

最近准备开始做一个手机网络棋牌游戏的Prototype,就在构思如何如何写游戏逻辑部分。在游戏开发过程中,Prototype版需要展示某个游戏核心玩法,它可以没有华丽的界面,没有游戏内各种元素的完美呈现,甚至可以没有声音,但它必须有“核心玩法”的体验。简单来说“核心玩法”就是在一个游戏里玩家用大量的时间来使用的某一种“系统”,没有了这个“系统”这个游戏就没有存在的意义。

在棋牌游戏中,最核心的就是“下棋”或“打牌”这个个过程,这个过程中所有玩家会触摸、点击、双击、拖放游戏中的一些元素,来推动游戏过程的进行,游戏完成后应该有一个对于胜负情况的统计和展示,然后当所有玩家准备好之后就开始下一次游戏。我们将这个系统称为“博弈”系统或者叫“对战”系统,其中它有一个重要子系统叫做“结算”子系统。这就是一款棋牌游戏的核心系统,使用它的过程就叫这个游戏的“核心玩法”。游戏Prototype版本的制作都是为围绕着这个核心玩法开展的。

上面好像扯远了,说回正题“逻辑”。写这篇博文也是为了整理一下思绪。基于Unity3D的客户端开发,逻辑代码要比较明确的分为两部分来做,不能混在一起,否则项目越到后期越难驾驭,第一部分是“控制”逻辑,第二部分是“呈现”逻辑。

为甚么说要“明确分为两部分”呢?我认为派生于MonoBehaviour的类,也就是我们在Unity3D中挂接在GameObject对象上的这种代码,我将其称为“逻辑呈现”程序,顾名思义这种类只可用来控制可显示对象的逻辑呈现过程,诸如:位置变换、动画、显示、声音等的控制。而另外一些对象都应该用继承于MonoBehaviour的类来实现,这种代码我将其称之为“逻辑控制”程序。它负责管理逻辑对象中的数据以及各种逻辑对象之前的关系。这样说吧,它负责“指导”逻辑呈现程序的工作,为逻辑呈现、图形显示等提供依据,但具体的呈现控制不是由它来做。

接下来看图说话:

 

 

 

Unity3D-uml1

 

基于以上思路我会将整个游戏逻辑控制根据需要编写多个类,并且由一个叫LogicMgr的单件类来管理。通过LogicMgr来管理所有的逻辑控制类,这个单件类中会提供一个名为update()的方法,在逻辑呈现程序的每帧中调用,以便更新所有的逻辑控制类的数据。LogicMgr类实例会在游戏的第一个level创建时由一个被挂接了专用逻辑呈现程序的GameObject来创建,假设这个逻辑呈现类叫LogicView,它是派生于MonoBehaviour的,在LogicView类中实现Awake()方法,并且将挂接LogicView程序的GameObject设置为“不会销毁”,同时在这个方法中的来初始化LogicMgr的实例,在这个逻辑呈现程序中的Update()方法中来调用这个单件的update()。

Unity3D 思考如何”填”资源打包的”坑”

Unity3D 思考如何”填”资源打包的”坑” published on Unity3D 思考如何”填”资源打包的”坑”有101条评论

unity47

最近一直在思考如何“填”Unity3D资源打包这个坑,这篇文章的目的是整理思路,所以正确与否就仁者见仁智者见智了。

首先说下资源打包的目的,我认为资源打包无非是为了以下几方面的目的:

  • 最重要的是使游戏资源可动态更新,而不用每次都走发布流程
  • 其次可快速解决一些因为资源带来BUG
  • 最后就是手动控制资源的使用,节约内存,提升游戏性能

我目前暂时想到这些,其他的目的暂时没想到,其他朋友想到的可以告知一下,我好添加进来。好了,基于打包的目的,我的思考出发点是Unity3D几个原生概念

  • Scene(场景)
  • Prefab(预制)
  • Asset(资源)

这几个东西其实是相辅相成的。

我对Scene(场景)的理解是用来保存相对静态的游戏对象,当然动态对象的实例也可以放里面,比如已经被做为Prefab(预制)的角色或模型的实例。我这里说的静态的游戏对象,其实主要是指场景中不动的、几乎不变化的物件,例如地形、房屋、树木、花草、山石、装饰物、常态粒子等等,其实Scene(场景)好比一个“房间”,里面可以放各种“家具”,场景就是用来保存这些“家具”的类型、组合方式、位置、旋转、缩放等这些基本信息的。

而Prefab(预制)呢,就是用来保存在场景中会动态创建的对象信息的,例如RPG游戏中的玩家角色、怪物、玩家的防具、武器等等。用类比的方式来讲,Prefab其实就是一个“模具”,根据同一个模具可以做出很多相同的“玩具”,这个“模具”保存了这个“玩具”是如何组合起来的,由哪些“零件”组成,也保存了这些“零件”的相对位置、相对旋转、缩放比例等信息等。总体来说我认为预制主要目的是用来保存需要在游戏中动态实例化对象的东东(当然静态实例化的对象也可以用这个来保存)。写着写着突然又想到一个更好的类比,程序猿一看就懂的:预制是你写的类,预制可以被实例化,就像你把类new出来一样。

Asset(资源)在Unity3D里面是一个很重要的概念,可以这样说构成游戏世界的80%的游戏对象都关联了一个资源。Unity3D中资源的类型很多例如:贴图、材质、模型、Shader、脚本、文本文件、声音、动画等,其实我们在Project面板下看到的所有东西都被称为资源。这些资源并不是一个一个独立的个体它们之间是可以存在依赖关系的,例如模型关联了贴图和材质可能还有动画。

插一句,在Unity3D中,选中一个目标资源,右键,便可以查看其依赖关系。dependencies

 

现在来思考打包方案,我先提出待解决的问题(要填的坑)

  • 同资源不能被重复打包或打包到多个AssetBundle里面
  • 打包后的资源可以独立更新
  • 加载要方便,不能而为了加载一个资源手写一堆预加载代码

然后是解决问题的思路(如何填坑)

  • 每个资源单独打包,这样做的目的是为了解决独立更新的问题
  • 按资源的依赖关系分级,从分级最低的开始打包,目的是为了避免资源被重复打包。比如,一个FBX模型,他依赖了材质、贴图等,其中材质又依赖贴图,而贴图不依赖任何东西,所以贴图这种资源的依赖分级就是最低的
  • 所有被打包的资源都需要被管理起来,包括资源的名称、打包后的名称、资源的依赖分级、打包时间、唯一标识、版本号等,保存一个列表文件中,方便读取和查找被打包的资源
  • 实现一个统一的资源管理器,目的是为了方便的更新资源、加载场景、实例化预制、将这些复杂的处理过程简化到极致。

大概的打包过程如下:

  • 预处理将要打包的目标,先处理要打包的场景,然后再处理场景中没有用到但游戏中要动态实例化的预制。需要说明的是,这里说的处理并不是指打包,而是将相关资源整理出来建立打包时需要的一些列信息。
  • 做场景预处理。先获取场景所依赖的所有资源,然后遍历这些资源,得到每个资源的依赖分级,因为有可能有同名资源,所以这时也要生成好每个资源被打包后的文件名,我想用“多级目录名_文件名”的方式应该可以保证其唯一性,例如static_texture_gun1_png.assetbundle。最后按依赖分级关系生成一个依赖文件,把之前整理出来的信息保存下来,这个文件不但会用于后面打包过程,也会用于运行期资源管理器加载场景时使用。需要注意的是,最后要把当前场景文件(*.unity)也要添加到依赖文件中,因为场景文件本身也需要打包的。正常来说对于每一个场景,都应该生成一个依赖文件。处理完一个场景后,要将这个场景的原始名和打包名称写到一个场景列表文件里,方便游戏运行期加载时读取。
  • 做需要被动态实例化的预制体预处理。这种预制体在开发的时候最好实现就放置在一个专用的文件夹下,以方便打包时的工作。和场景一样同样的,得到每个预制体的依赖资源,做好依赖分级处理,生成预制体的依赖文件。和场景一样,最后也要将这个预制体的名称写入到一个预制体列表文件里。对了,补充一下关于依赖文件最好也找个专用的文件夹保存,因为依赖文件最后也会打包成assetbundle文件发布。
  • 现在读取所有的依赖文件,在内存中生成一个待打包资源的列表,这个列表有清晰的分依赖分级关系,打包的时候就根据这个依赖分级来决定什么时候调用PushAssetDependencies()函数来建立依赖关系。为什么遍历所有依赖文件,目的是在于要在整理一次,剔除一下重复的资源,避免同一个资源被多次打包浪费时间。
  • 最后开始打包,根据上一步生成的待打包资源列表打包,打包文件存放也请指定好一个专用的文件夹。。
  • 打包完成后这里还需要生成资源更新文件,这个文件中保存了所有打包好的assetbundle文件的列表,以及这个assetbundle文件的唯一标识:比如MD5码、SHA码等、还有它的当前版本号。假如项目更新了,重头执行一遍打包过程,然后和上一次的这个文件做个比对,把变化的assetbundle文件升级版本号,然后放在服务端让客户端只下载最新的assetbundle文件。

 

Unity3D 资源打包吐槽

Unity3D 资源打包吐槽 published on Unity3D 资源打包吐槽有84条评论

unity47

Unity3D的资源打包以及它的加载机制真的是一个坑,非常的XX(粗鲁了!-_-)。

先回顾下传统PC网络游戏的资源打包。

一般各个游戏开发厂商会自己定义一种资源包格式,然后将资源分门别类的放到这个大的资源包文件中,文件中可以在头部包含一个列表文件,表示什么资源在什么位置、资源的大小、资源的唯一标示等。也有的游戏转门维护一个资源列表文件,让它和打包文件分开,这样的好处是可以独立升级资源。例如:某个模型变了,只需要将这个模型通过网络方式下载到本地,添加或合并到一个最优先加载的资源打包文件中。游戏启动后,先加载优先级最高的资源打包文件的资源列表,再加载老旧的资源打包文件的资源列表,如果新旧资源列表都有同一个资源,那当然以最新的为准,同时打开所有的资源打包文件,得到文件句柄,注意这里仅仅是打开文件得到一个操作句柄,并不占用内存,换句话说没有把整个打包文件加载到内存中,等程序需要某个资源的时候,根据资源的名字或是唯一标识,通过之前获得的文件句柄直接移动文件指针到对应的文件位置去取资源,然后才加载进内存。在打包过程中,不会存在什么依赖不依赖的关系,都是单独的将每一个资源压缩,然后放在相应的位置,一切都是写代码控制,虽然麻烦,但是没有坑,机制一旦建立好,用起来非常简单,运行期也是要什么加载什么,不用了就释放,再清晰不过了。但是,到了Unity3D中,这一切都不一样了。

我们来看Unity3D的资源打包……(忍住不说脏话)

啪,首先给你来个概念,依赖关系。意思就是你打包的时候,要考虑依赖关系,举个例子,一个建筑,那就有模型、贴图、材质什么的,这些就叫这个建筑的依赖文件,打包的时候可以把这个建筑做成一个预制体来打包,如果不考虑依赖关系,打包出来的预制里面就带模型、贴图、材质了,你使用的时候直接实例化这个预制就可以用了,你是不是感觉很爽?别急马上你就不爽了。如果还有另一个建筑也用到了相同的贴图和材质,你又把他做成预制体,然后又打包,实例化后也可以正常使用,这里开始坑就出现了,你的2个打包文件中,把同一个材质打包了2次!当同时你加载这两个建筑的时候,Unity3D,会把这两个AssetBundle文件都加载,自然的相同的贴图、材质这些也就加载了两次,其实这还算好,毕竟加载完AssetBundle完成,你要实例化这个预制,实例化完成后就可以释放AssetBundle文件占用的内存了,但是就在你实例化两个建筑的时候,更深的坑接着出现了!这个相同的贴图和材质,有2份相同的拷贝。只要游戏中存在这两中预制类型的建筑(实例可能无数个),就始终会存在两份相同的贴图、材质的拷贝,如果你使用完AssetBundle不立即释放,那么就是四份相同的拷贝,这绝对是天坑吧。

其实人家Unity3D也考虑到了,所以刚才就告诉你了,咋们可以依赖关系打包啊,你为啥不用?OK,接着刚才的2建筑我们用依赖关系来打包…(省略几行代码)……OK打包完成,现在2个预制打包到一个AssetBundle里面了,加载看看,嗯,只有一份贴图和材质的实例,这一个AssetBundle也别刚才那两个合起来的大小要小,不错不错,节约了内存,好高兴啊。别高兴的太早,坑来了,我发现我的一个建筑在模型上有点瑕疵,要更新一下,肿么办,能单独更新这个模型么?对不起,不行!!!问题放大一点,几十个建筑,都依赖了相同的贴图和材质,当然也有各自专用的贴图和材质,我都是这样打包的,我只有一个AssetBundle,算小点吧,20MB,你要我更新整个AssetBundle,这不是害人么?PC上估计还好点,手机上的话,很多有性格、有个性的用户立马不爽了,槽,昨天还好好的,今天一开游戏就要用老子20MB的流量,哥不耍了还不行么!!

Unity3D又说了,你傻啊,你先push那个什么依赖关系把公共贴图和材质各自打包,然后再打包那个建筑文件,最后pop那个什么依赖关系,这样建筑文件里的AssetBundle里就不包含那个公共的贴图和材质了,你试试看?好吧,我试试。还是刚才那个例子,这样两个建筑打包出来就4个AssetBundle了,贴图一个,材质一个,2建筑模型各一个。好了,来跑程序吧,咦,怎么是个白的建筑?Unity3D又说了,你要先加载你的贴图AssetBundle,再加载你的材质AssetBundle,最后才能加载建筑那个AssetBundle。我槽,这又是一坑吧。我的共享纹理不止一个,有几个咋办,有几十个咋办?

敢不敢提供 一个通用一点的机制啊?要么你就不要提供这种半残的机制,完全让我们自己可以控制打包文件的格式以及内容啊!

吐槽总结:

  1. AssetBundle内存控制不友好,我还没读内容就先要把这个加载到内存
  2. AssetBundle用单一文件打包相关内容,使用非常方便,但是项目越大越耗费的内存越多
  3. AssetBundle用单一文件打包相关内容,打包的内容越多,更新量越大(整个包都要更新)
  4. AssetBundle用多文件依赖方式打包单个资源,使用起来非常复杂,手动写一个个的载入根本不现实,要自己实现一套AssetBundle和Asset的管理机制才行。

纯属个人吐槽,不喜勿喷

乱谈游戏服务端分布式架构(二)

乱谈游戏服务端分布式架构(二) published on 乱谈游戏服务端分布式架构(二)有71条评论

architecture1

乱谈游戏服务端分布式架构(一),我们提取需求是以服务端在物理机在网络上的部署方式为出发点,这次我们将从另一个侧面来提取需求。

顺便说一下,要全面的描述一个系统的需要从多个不同的侧面来观察,从不同的方向和视角才能获得一个系统的全部需求。好比一个城市,在这个城市中有很多摄像机,每个摄像机都只能捕获这个城市的一个角落或者说一个局部信息,想了解这个城市的概貌,那么需要无数的摄像机。

……扯远了,回到正题上,这次我们立足的出发点为逻辑支撑情况,注意是对游戏逻辑的支撑情况,和物理机部署情况无关。先阐述一下我的期望:

  • 可以支持众多用户连接(>50K)
  • 用户连接本身和用户逻辑之间无强关联性,这样可以解决很多问题:例如用户中途掉线,随即又连接上来的情况,这样用户逻辑部分的数据就不用重读,用户状态也不用恢复,只需要替换一下和用户连接之间的关联性即可
  • 游戏逻辑端可以分类,如验证服务端、数据中心服务端、副本管理端、副本逻辑端、大厅服务端、比赛管理端、房间逻辑端等等
  • 逻辑端功能可以完全分离,比如副本管理端和副本逻辑端可以在同一进程下,也可以各自独立为一个进程运行,便于部署
  • 逻辑端采用多进程单线程模式,便于开发
  • 逻辑端之间会发生通讯,但又不希望众多的逻辑端之间互相连接
  • 数据包进入逻辑端需要有一种机制保证该连接的数据包合法
  • 相同的逻辑端可以启动多个实例,用于支撑更多的用户或者负载均衡。
  • 各个逻辑端可以自由扩展功能,这些扩展对原有逻辑无影响,如:对指定数据包加解密,数据包日志等

看着内容很多,其实不然,而且细细分析下发现,和之前的需求有很多共通之处,提取一下变为简要需求:

  • 一个独立的连接服务端,能支持>50K的用户连接
  • 该连接端同时能接受公网和私网的连接,来自公网的是客户端连接,来自私网的是逻辑端连接
  • 连接端需要管理用户连接和逻辑端连接,各种不同的连接需要分类管理
  • 连接端用于转发各种数据包,并且可以通过扩展模块来添加连接端功能
  • 连接端需管理Token,Token来自一个不需要Token的逻辑端,拥有Token的客户端连接发送的包才会被发往指定的逻辑端
  • 所有的逻辑端都连接到连接端,逻辑端之间的通讯由连接端负责转发。逻辑端连接不需要Token
  • 抽象逻辑端,目前将它分为2类,逻辑管理端和逻辑服务端
  • 逻辑端逻辑处理器实现为外部加载模块,可以随时替换
  • 连接端有OP管理机制,逻辑端负责注册自己能处理的OP,连接端根据OP来分发数据包

 

乱谈游戏服务端分布式架构(一)

乱谈游戏服务端分布式架构(一) published on 乱谈游戏服务端分布式架构(一)有105条评论

architecture1

首先说明一下分布式架构的含义,很多人将“分布式架构”和“分布式运算”两个词理解为同一个概念。这里提到的分布式架构是指服务端程序可以分开部署到多台物理机上(当然也可以部署到同一台物理机上),目的在于通过扩展物理机或硬件设备来提升服务端程序的能力。这里说的“服务端能力”是指服务端的逻辑处理能力、网络数据吞吐量、可承载的客户端连接量等等。

在开始后面的叙述之前我们先按物理机在网络上的部署不同把服务端分为两类,注意这里是按物理机在网络上的部署情况来分类。为何按“物理机在网络上”的部署情况来分类?接下来我们就细细道来。

对于网络游戏而言,一定会有客户端的接入,那么这台服务器一定是部署到公网环境下,如果服务端所有的程序都部署到公网环境,那可以说是灰常的不安全。试想如果负责逻辑处理的服务端程序在公网,黑客们就可以利用各种漏洞入侵了,可以做的事情如下:下载你的服务端程序、备份你的数据库内容等等。所以我们在设计之初就将负责连接的服务端程序和负责逻辑的服务端程序分开,这样至少可以在一定程度上避免这种问题,为什么是一定程度上呢,这样可以拦截掉一部分“新手”黑客。

按物理机在网络上的部署情况来分类,分为两类:网关服务器和逻辑服务器。

网关服务器部署在公网上

  • 接受连接,不管是来自公网的客户端连接,还是来自私网的逻辑服务器连接。反正接受连接就对了。
  • 接收来自公网客户端的数据包并按一定规则转发这些数据包到私网逻辑服务器。
  • 接受来自私网逻辑服务器的数据包并转发到目标公网客户端。
  • 转发所有逻辑服务器之间的内部通讯包。

逻辑服务器部署在私网上

  • 接收和处理来之网关服务器转发的客户端数据包。
  • 发送逻辑包到网关服务器。
  • 接受和处理来之其他逻辑服务器的数据包。

上面就是按物理机部署情况不同的分类,接下来的程序设计就要支持这种部署方式。

首先很明确的是网关服务器程序一定是个可以独立部署的程序,而逻辑服务器程序应该满足各种不同需要,比如支持不同游戏、支持各种逻辑端的管理、不同功能的逻辑端可多次部署、通过添加统一类型的逻辑端来支持更多数量的玩家等。

理解一下上面的文字,整理了一下简要需求,当然是非正式性质的:

  • 独立的网关服务端,独立的逻辑服务端
  • 网关服务端负责交换数据包,逻辑服务端主要负责处理数据包
  • 网关服务端要能控制包的流向
  • 每个端只是一个执行文件,框架而已,加载不同dll的实现不同的功能
  • 各dll要做的主要事情就是处理数据包
  • 网关服务端、逻辑服务端都可以加载多个不同的dll,各dll可以按顺序处理包
  • 各种dll来实现不同的逻辑,各dll比较独立,耦合性弱
  • 制订接口方案,开发人员只需要按接口规范写逻辑dll,能多个人同时开发

(译)DF-GUI快速入门(Tutorial – Getting Started)

(译)DF-GUI快速入门(Tutorial – Getting Started) published on (译)DF-GUI快速入门(Tutorial – Getting Started)有92条评论

翻译说明:采用英文-中文对照方式,以免翻译水平低劣,觉得翻译的不好的请勿喷,看原文就可以了。还有我当前下载的是1.0.15版本,所以翻译内容和原文有些出入。比如,某些菜单选项位置变更了。

官方原文地址:点击这里跳转 DF-GUI In this tutorial, we’ll be covering how to use the UI Wizard to create a new GUI in your scene. The following steps assume that you are starting from scratch with a new empty scene. 

在本教程中,我们将讲述在你的场景中如何使用UI向导创建一个新的GUI。接下来的步骤我们假定你是从一个新的空白场景开始建立GUI。 

The first thing that needs to be done is to set up a layer just for your user interface.  Open up the TagManager panel (Edit → Project Settings → Tags) and add a new “GUI” layer. All of your UI elements will reside on this layer. 

第一件事情是需要为GUI设置一个专用的层。打开标签管理面板(Edit → Project Settings → Tags and Layers)然后在用户自定义层中任选一个空白层命名为“GUI”。后面你需要将所有的UI元素都将关联到这一次层。

add-layer

Next, go to Assets → Daikon Forge → UI Wizard

下一步,点击GameObject → Daikon Forge → UI Wizard.

guiwizard-menu

This will cause the following wizard to pop up: 这个操作将弹出向导对话框:

image02

Let’s go through what each option does.

  • UI Layer is the layer your UI will reside on. Pick the GUI layer you just created.
  • Orthographic, if checked, means your UI will be displayed in 2D. Uncheck this if you need your UI to display in 3D.
  • Pixel Perfect, if checked, means your UI will perfectly map to screen pixels (for instance, text usually requires pixel perfect rendering, otherwise it will appear blurry or fuzzy). So a 100 pixel wide sprite will always be exactly 100 pixels wide on any screen (on larger resolutions it will appear smaller)

接下来我们对每一个选项进行简单的说明。

  • UI Layer 是你GUI对象关联层。你可以点击它并在下拉框中选择之前你创建的”GUI”层。
  • Orthographic 正交投影,选中此选项意味着你的UI将以2D方式显示。不选则将UI显示在3D环境中。
  • Pixel Perfect 像素完美匹配,选中的话表示你的UI将完美的匹配当前屏幕像素(举个例子,文本通常需要相熟完美匹配,否则将会出现模糊或毛边的情况)。所以100个像素宽的精灵总是以100个像素的宽显示在任何分辨率的屏幕上(在较大分辨率的屏幕上图片会看起来比较小)。

Additionally there are a number of options concerning input:

  • Use Joystick determines whether the UI will process joystick or gamepad input.
  • Joystick Click Button is used to generate click events. While a GUI element has focus, pressing this button will have the same effect as using the left mouse button.
  • Horizontal Axis and Vertical Axis are used to generate arrow key events. This way, your UI controls can listen for arrow key events to navigate the current selection, and joystick input will automatically work.  If a custom axis is specified, the names must match an axis defined in the Edit -> Project Settings -> Input Settings.

接下来还有一些关于输入相关的选项:

  • Use Joystick 使用摇杆勾选后UI将处理来自摇杆或手柄的输入。
  • Joystick Click Button 摇杆确定按钮 是用来产生点击事件的。当某个GUI元素是焦点时,按下你在此设置的按钮时会产生和用鼠标左键点击此GUI元素一样的效果。
  • Horizontal Axis and Vertical Axis 水平轴 和 垂直轴 是用来产生方向键事件的。这样你的UI控件就能监听来自摇杆或手柄的操作,比如用摇杆选中某个选项。如果指定了自定义的轴,那么这个自定义轴的名字必须和你Edit -> Project Settings -> Input里的设置完全相同。

After configuring these, simply press the Create button to create your starting user interface hierarchy. 

完成以上设置后,点击Create按钮创建你的用户界面。 

You will now have a new UI Root game object in your scene, with a UI Camera child object. 

现在你会在Unity3D的层级面板(Hierarchy)发现你的刚刚创建的 UI Root 游戏对象,它带有一个名为 UI Camera 的子对象。

dfgui-new-scene

Note that the UI Root object has two custom components assigned to it, an Input Manager and a GUI Manager. We will look at the Input Manager first: 

观察 UI Root 对象,它带有2个自定义组件,一个是输入管理器Input Manager)另一个是GUI管理器GUI Manager)。接下来我们将介绍输入管理器:

image01

The Input Manager specifies how DF-GUI will process user input. You’ll see some familiar settings here that you specified in the GUI Wizard, namely the Use Joystick, Joystick Click Button, Horizontal Axis, and Vertical Axis options

在DF-GUI系统中输入管理器决定了如何处理用户的输入。这里你将看到一些你在GUI向导中设置过的选项,比如 使用摇杆摇杆确定按钮水平轴 和 垂直轴 

In addition, we have:

  • Use Touch – This will enable touch support on mobile devices, including multi-touch support.
  • Axis Polling Interval – This controls the interval at which it polls the Horizontal and Vertical axes to generate arrow key events.
  • Retain Focus – This determines what happens when you click on nothing. If Retain Focus is enabled, the last focused control will continue to retain focus. Otherwise, the control will lose focus.

除此以外我们还看到一些新的设置项:

  • Use Touch 触摸控制 – 选中此选项将开启对移动设备的触摸控制支持,当然对多点触摸的支持是必须的。
  • Axis Polling Interval 轴轮询间隔 – 这个设置是控制以多少时间(目前单位是秒)间隔来轮流产生水平轴和垂直轴的方向键事件。
  • Retain Focus 保留焦点 – 如果你在屏幕上点击了一个什么都没有的位置,这个选项将决定是否保留上一个UI元素的焦点状态。如果没有勾选此设置,那么上一个元素将丢失焦点。

Below that is the GUI Manager component, which is responsible for rendering your user interface. It contains settings that determine how your controls are rendered, default options, and design-time options. 

接下来我们看看GUI管理器组件,它主要包含一些UI的呈现相关的设置。例如:渲染设置、默认纹理材质设置、分辨率设置等。 image00 

Let’s take a look at these settings:

  • Render Mode is whether the UI is rendered in Orthographic or Perspective. This is the same as the Orthographic checkbox in the UI Wizard.
  • Pixel Perfect determines whether controls map perfectly to screen pixels. This is the same as the Pixel Perfect checkbox in the UI Wizard.
  • Default Atlas is the texture atlas assigned to new controls by default. Texture atlases are used to keep draw calls low, and will be covered in another tutorial.
  • Default Font is the font assigned to new labels by default. Fonts contain the necessary data to render text, and will be covered in another tutorial.
  • Merge Materials causes the GUI Manager to attempt to sort controls by material to reduce draw calls. For instance, if you have a sprite from atlas A as the background, a sprite from atlas B on top of that, and another sprite from atlas A at the very front, DFGUI will generate three draw calls (background, middle, foreground) in order to ensure correct render order. However, you can force DFGUI to back the background and foreground together into one draw call rather than two, but this can cause render order issues.
  • Generate Normals causes the GUI Manager to generate normals for each vertex. This is used by shaders that require normals, such as shaders that use lighting.
  • Screen Width and Screen Height control the size of the design canvas. For instance, if you want to design your UI at 640×480 you can enter that here and click Apply. The UI will still properly display on all screen resolutions, however. You can also click Use Build Settings to copy the default display resolution from the build settings.
  • Show Wireframe shows the wireframe mesh of your UI.
  • Show Rulers shows ruler markings at the edge of the UI canvas. This is used to aid in UI design.
  • Snap To Grid snaps UI controls to the grid. This is used to aid in UI design.
  • Show Grid displays the grid used for grid snapping.
  • Grid Size controls the size of the grid cells used for grid snapping.
  • Show Safe Area displays a rectangle around the edges of your UI. This is used when designing UIs for display on TVs which may crop some percent of the image border.
  • Safe % is the percentage used for the safe zone. Use this to display different safe zones (5% – 10% should suffice for nearly all cases)
  • Force Refresh causes an immediate redraw of the entire UI. If something doesn’t appear to be updating, you can select Force Refresh to force a redraw.

我们来大概看一下有哪些设置:

  • Render Mode 渲染模式 指定你的UI是以正交方式渲染还是透视方式渲染,换句话说就是以2D方式还是3D方式。这个选项同等于UI向导中的正交投影(Orthographic )复选框。
  • Pixel Perfect 像素完美 决定是否完美的将UI映射到屏幕像素。这个选项等同于UI向导中的像素完美(Pixel Perfect)复选框。
  • Default Atlas 默认图集 是你设置一个纹理图集为默认值后,每当你创建一个新的控件这个图集将自动的赋值给新创建的控件。纹理图集主要目的是用于降低绘图次数(draw calls),对于它的详细说明将在另一个教程中来讲述。(这里达秀想补充说明一下,其实Unity3D 的draw calls其实主要的一个计算标准是来自于底层的贴图切换。众所周知DirextX或OpenGL在渲染时,如果每渲染一帧贴图切换得越多,那么绘制效率就越低,这个在显存越低的显卡上体现的越明显,draw calls数量的高低其实就是这一事实的真实写照。后面提到的合并材质,其实也是这个原理。)
  • Default Font 默认字体 是表示当你创建新的标签(Labels)时自动选用的字体。字体包含了渲染文本所必须的数据,对于字体我们也将在另一个教程中来讲述。
  • Merge Materials 合并材质 选项使GUI管理器试图通过材质的排序来降低draw calls的次数。举个例子来说,如果你的背景来自于图集A,一个精灵来自于图集B并且绘制在背景上,同时还在最上面绘制了另一个精灵它来之于图集A,一般情况下DFGUI系统将按顺序绘制三次(顺序是背景绘制、图集B精灵的绘制,图集A精灵的绘制)。但是你可以通过此选项强制DFGUI系统合并第一次和第三次的绘制将他们合并为一个(达秀注:这样就减少了一次切换纹理的过程),但这样有可能照成渲染顺序出现问题,导致画面看起来不是你期望的那样。
  • Generate Normals 生成法线 使GUI管理器为每一个顶点生成法线。它主要用于一些实用着色器(shaders)的情况,比如UI对于光源的不同反应。(达秀废话:这样可以是你的UI看起来很炫,NGUI也支持这种效果
  • Screen Width and Screen Height 屏幕宽度 和 屏幕高度 控制设计期画布的尺寸。举例来说,如果你预想的UI宽高为640×480像素那你可以在这里输入这两个值,然后点击应用按钮。UI会正常显示在所有屏幕分辨率下。你还可以点击使用编译设置 复制默认的分辨率设置到这里。
  • Show Wireframe 显示线框 表示将你的UI显示为线框图。
  • Show Rulers 显示标尺 选项会在你的UI画布周围显示标尺,在你设计UI时它会灰常灰常滴有用喔。
  • Snap To Grid 贴靠到网格 这个不用翻译了吧,就是你拖动空间的时候自动会往网格上贴靠。方便UI的位置布局的调整,但有时很烦。
  • Show Grid 显示网格 就是显示网格咯,对于上个选项的补充,让你看得到设计网格.
  • Grid Size 网格尺寸 控制网格的单元格大小,还是对上面两个选项的补充。
  • Show Safe Area 显示安全区域 用矩形显示UI的边界范围。This is used when designing UIs for display on TVs which may crop some percent of the image border.(达秀注:这句不理解也没遇到过这种情况,游戏UI用于显示在电视上的时候?PS3? XBOX360?
  • Safe % 安全百分比 is the percentage used for the safe zone. Use this to display different safe zones (5% – 10% should suffice for nearly all cases)(达秀注:这个应该是对上一个的补充,估计一般也用不到,除非你做的是PS3 XBOX360的游戏才用得到吧,如果有人用过麻烦给我说下到底神马状况下用的,在此先谢过
  • Force Refresh 强制刷新 如果某些应该显示东西没有被更新,使用这个设置立即强制重绘整个UI。

In the next tutorial, we’ll take a look at some basic controls – sprites, labels, and buttons. 

OK,这个教程就基本结束了。接下来的教程中我们会看到一些基础控件,比如 精灵、文本标签、还有按钮等。

FreeBSD 9.0 下 MySql 安装备忘

FreeBSD 9.0 下 MySql 安装备忘 published on FreeBSD 9.0 下 MySql 安装备忘有85条评论

freebsd_thumb.jpg

安装之前看了一下网上的一些博客和帖子,说要用cmake来配置,然后再装。我在potrs下试了下,除非手动下载解包,然后从解包目录装,否则 ports 下的mysql是没法用cmake的。

这里测试了一下直接 make install clean安装,装完后是可以用的,不过没发自己定义一些东西的安装目录了,不过测试嘛无所谓,废话说了很多了现在开始装.

# cd /usr/ports/databases/mysql55-server/
# make install clean

可能会弹出一个选项框,按你的需要选择后回车即可,我在虚拟机上大概需要15分钟就安装完成了。

安装完成后会自动生成一个 mysql 用户 和 一个 mysql组,不像网上一些朋友说的要手动建立。

接下来就是配置,安装完后首先复制配置文件 和 启动文件到指定目录,然后改一下新复制的文件权限(其实安装完成配置已经复制了一份,不过未必是你想要的默认配置)

(文件位置的一些设置可以看看ports下的Makefile文件,是在这里面配置的)

# cp /usr/local/share/mysql/my-large.cnf /usr/local/etc/my.cnf
# cp /usr/local/share/mysql/mysql.server /usr/local/etc/rc.d/mysqld
# chmod 755 /usr/local/etc/rc.d/mysqld

然后初始化数据库,这里—basedir 和 –datadir 很关键不要搞错了,如果搞错了就会出一堆错误信息

# mysql_install_db --user=mysql --basedir=/usr/local/ --datadir=/var/db/mysql/

如果看到下面的信息就是初始化完成了:

Installing MySQL system tables...
OK
Filling help tables...
OK

To start mysqld at boot time you have to copy
support-files/mysql.server to the right place for your system

PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !
To do so, start the server, then issue the following commands:

/usr/local//bin/mysqladmin -u root password 'new-password'
/usr/local//bin/mysqladmin -u root -h xxx.xxx password 'new-password'

Alternatively you can run:
/usr/local//bin/mysql_secure_installation

which will also give you the option of removing the test
databases and anonymous user created by default.  This is
strongly recommended for production servers.

See the manual for more instructions.

You can start the MySQL daemon with:
cd /usr/local/ ; /usr/local//bin/mysqld_safe &

You can test the MySQL daemon with mysql-test-run.pl
cd /usr/local//mysql-test ; perl mysql-test-run.pl

Please report any problems with the /usr/local//scripts/mysqlbug script!

接下来就按上面的内容的提示,就可以启动mysql了,安装完后记得运行一下下面的指令升级数据库。

# mysql_upgrade

FreeBSD 9.0 安装完成后的备忘

FreeBSD 9.0 安装完成后的备忘 published on FreeBSD 9.0 安装完成后的备忘有86条评论

freebsd

1:改变默认的SSHD配置,以后从终端登录,就不用再虚拟机和真机间切来切去了。我用的SecureCRT 7.0 用用户名密码登录方式感觉这样比较方便。

# cd /
# ee /etc/ssh/sshd_config

# The default requires explicit activation of protocol 1
# 把协议设置为2 记得取消最前面的#
Protocol 2

# 这里要打开root的登录权限 取消前面的# 同时把 no 改成 yes
PermitRootLogin yes

# 打开BSD内建账号登录 取消前面的# 同时把 no 改成 yes
# Change to yes to enable built-in password authentication.
PasswordAuthentication yes
PermitEmptyPasswords no

# 额~~忘了啥意思了,反正打开就对了,不然不能登录
# Kerberos options
KerberosAuthentication yes
KerberosOrLocalPasswd yes

然后 按 ESC 回车 回车 就保存退出了,在/etc/rc.conf增加一句,以便用 /ect/rc.d 来控制这个服务

# echo "sshd_enable='YES'" >> /etc/rc.conf

最后重启 sshd 服务, 搞定,现在可以用SecureCRT终端登录了

# /etc/rc.d/sshd restart

 

2:用portsnap获取最新的ports Tree。不要用cvsup了,理由很简单portsnap有数字签名,较安全,而cvsup是没有的,同时portsnap是压缩下载,速度会快很多,以后就不要cvsup了,不能混合使用的。

改成用国内的服务器吧,直接连官网估计得慢死。不过第一次还是推荐直接官方原始站点,镜像站点的Ports Tree 要好几天才能更新,有时要1、2个月

# ee /etc/portsnap.conf
# 注释掉原来的改成新的
SERVERNAME=portsnap.hshh.org
#SERVERNAME=portsnap.FreeBSD.org

保存退出,然后执行

# portsnap fetch extract

Looking up portsnap.hshh.org mirrors… 2 mirrors found.

Fetching public key from portsnap2.hshh.org… done.

Fetching snapshot tag from portsnap2.hshh.org… done.

Fetching snapshot metadata… done.

Fetching snapshot generated at Wed Jun 20 08:19:49 CST 2012:

d7fe72f04a6499ad2777992c3b53700d188a9a833a05e6 53% of   67 MB  522 kBps 01m02s

看吧,国内服务器果然快很多,对了上面这句是第一次更新用,以后执行就是用

# portsnap fetch update

 

3:安装axel加速下载器,和改/etc/make.conf。axel可以用多线程下载,比原始的快很多,可以自由设置线程数等,很好很强大。而make.conf里可以配置这个下载器,同时可以添加最近的ports镜像站点。

# cd /usr/ports/ftp/axel/
# make install clean

安装完然后 改 /etc/make.conf,发现没有,咋办?建一个吧,然后设置下载器为axel 10线程,下面是我现在用的配置,比较给力

PERL_VER=5.8.8
PERL_VERSION=5.8.8
PERL_ARCH=mach
NOPERL=yo
NO_PERL=yo
NO_PERL_WRAPPER=yo

FETCH_CMD = axel 
FETCH_BEFOR_ARGS = -n 10 -a 
FETCH_AFTER_ARGS = 
DISABLE_SIZE = yes 

CPUTYPE=i686

MASTER_SITE_OVERRIDE= \
http://ports.hshh.org/${DIST_SUBDIR}/ \
ftp://ftp.freebsdchina.org/pub/FreeBSD/ports/distfiles/${DIST_SUBDIR}/ \
ftp://ftp2.tsinghua.edu.cn/mirror/FreeBSD/ports/distfiles/ \
ftp://freebsd.csie.nctu.edu.tw/pub/FreeBSD/ports/distfiles/ \
ftp://ftp.hk.freebsd.org/pub/FreeBSD/ports/distfiles/ \

MASTER_SITE_OVERRIDE?= ${MASTER_SITE_BACKUP}

存盘退出。现在就随便make一个大点的东西感受下新的速度吧。

4:更新FreeBSD升级点

# ee /etc/freebsd-update.conf
# 注释掉老的 换成新的 再更新的时候就是飞一样的速度
ServerName freebsd-updates.mirrors.163.com   
#ServerName update.FreeBSD.org

保存退出ee,然后

# freebsd-update fetch
# freebsd-update install

更新完成后重启,搞定。

5:安装pkgconf,因为论坛上有说

new devel/pkgconf added to replace devel/pkg-config. new version of pkg-config
are no more self hosting so we are stuck with 0.25 version while pkgconf provide
the same set of features as 0.27 and a compatible frontend. A symlink to
pkg-config has been added for convenience and compatibility
大概就是说pkg-config不能独立打包了,而pkgconf可以,同时也支持原来的功能。

最要命的其实是很多新的软件包要依赖pkgconf,比如php5.4.5,非要让装pkgconf,但是pkgconf和pkg-config又是不能共存的,后面再来换的话必须rebuild所有原来依赖pkg-config的ports,疯都疯了啊。所以没办法,先装免得后面来搞。

6:安装bash,设置ls带颜色,让终端可以显示中文

安装bash很简单,直接用ports来装

# cd /usr/ports/shells/bash
# make install clean

然后编辑一下/etc/.profile,这里顺便把现改好的.profile贴出来

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:~/bin
export PATH
HOME=/root
export HOME
TERM=${TERM:-xterm}
export TERM
PAGER=more
export PAGER


# 中文支持


LC_CTYPE=en_US.ISO8859-1
LC_ALL=en_US.ISO8859-1
LANG=en_US.ISO8859-1
export LC_CTYPE LC_ALL LANG


# 让ls显示颜色 如果是文件夹 在最后显示一个/
alias ls='ls -GF'
alias ll='ls -GlF'
alias lla='ls –GlaF'

# 控制台提示符 显示 用户名 主机名 当前路径
PS1='[\u@\h \W]\$ '