list name 在游戏name域名注册商里 是什么意思

更多频道内容在这里查看
爱奇艺用户将能永久保存播放记录
过滤短视频
暂无长视频(电视剧、纪录片、动漫、综艺、电影)播放记录,
使用您的微博帐号登录,即刻尊享微博用户专属服务。
使用您的QQ帐号登录,即刻尊享QQ用户专属服务。
使用您的人人帐号登录,即刻尊享人人用户专属服务。
按住视频可进行拖动
&正在加载...
请选择打赏金额:
收藏成功,可进入
查看所有收藏列表
当前浏览器仅支持手动复制代码
视频地址:
flash地址:
html代码:
通用代码:
通用代码可同时支持电脑和移动设备的分享播放
用爱奇艺APP或微信扫一扫,在手机上继续观看
当前播放时间:
一键下载至手机
限爱奇艺安卓6.0以上版本
使用微信扫一扫,扫描左侧二维码,下载爱奇艺移动APP
其他安装方式:手机浏览器输入短链接http://71.am/udn
下载安装包到本机:
设备搜寻中...
请确保您要连接的设备(仅限安卓)登录了同一爱奇艺账号 且安装并开启不低于V6.0以上版本的爱奇艺客户端
连接失败!
请确保您要连接的设备(仅限安卓)登录了同一爱奇艺账号 且安装并开启不低于V6.0以上版本的爱奇艺客户端
部安卓(Android)设备,请点击进行选择
请您在手机端下载爱奇艺移动APP(仅支持安卓客户端)
使用微信扫一扫,下载爱奇艺移动APP
其他安装方式:手机浏览器输入短链接http://71.am/udn
下载安装包到本机:
爱奇艺云推送
请您在手机端登录爱奇艺移动APP(仅支持安卓客户端)
使用微信扫一扫,下载爱奇艺移动APP
180秒后更新
打开爱奇艺移动APP,点击“我的-扫一扫”,扫描左侧二维码进行登录
没有安装爱奇艺视频最新客户端?
30秒后自动关闭
video-list-16.html
title= 游戏
video-list-16.html
title= 游戏
播放量数据:快去看看谁在和你一起看视频吧~
您使用浏览器不支持直接复制的功能,建议您使用Ctrl+C或右键全选进行地址复制
安装爱奇艺视频客户端,
马上开始为您下载本片
5秒后自动消失
&li data-elem="tabtitle" data-seq="{{seq}}"& &a href="javascript:void(0);"& &span>{{start}}-{{end}}&/span& &/a& &/li&
&li data-downloadSelect-elem="item" data-downloadSelect-selected="false" data-downloadSelect-tvid="{{tvid}}"& &a href="javascript:void(0);"&{{pd}}&/a&
选择您要下载的《
色情低俗内容
血腥暴力内容
广告或欺诈内容
侵犯了我的权力
还可以输入
您使用浏览器不支持直接复制的功能,建议您使用Ctrl+C或右键全选进行地址复制
本奖品由提供
红包雨下完了,下次早点来噢~{"debug":false,"apiRoot":"","paySDK":"/api/js","wechatConfigAPI":"/api/wechat/jssdkconfig","name":"production","instance":"column","tokens":{"X-XSRF-TOKEN":null,"X-UDID":null,"Authorization":"oauth c3cef7c66aa9e6a1e3160e20"}}
{"database":{"Post":{"":{"title":"Unity3D热更新LuaFramework入门实战(4)——Lua组件","author":"pyluo","content":"基于组件的编程模式是Unity3D的核心思想之一,然而使用纯lua编程,基本就破坏了这一模式。那么有没有办法做一些封装,让Lua脚本也能挂载到游戏物体上,作为组件呢?By 罗培羽
1、设计思想在需要添加Lua组件的游戏物体上添加一个LuaComponent组件,LuaComponent引用一个lua表,这个lua表包含lua组件的各种属性以及Awake、Start等函数,由LuaComponent适时调用Lua表所包含的函数。下面列举lua组件的文件格式,它包含一个表(如Component),这个表包含property1 、property2 等属性,包含Awake、Start等方法。表中必须包含用于派生对象的New方法,它会创建一个继承自Component的表o,供LuaComponent调用。Component=
\t--组件表\n{\n\tproperty1 = 100,\n\tproperty2 = “helloWorld”\n}\n\nfunction Component:Awake() \n\tprint(\"TankCmp Awake name = \"..self.name );\nend\n\nfunction Component:Start() \n\tprint(\"TankCmp Start name = \"..self.name );\nEnd\n\n--更多方法略\n\nfunction Component:New(obj) \n\tlocal o = {} \n
setmetatable(o, self)
self.__index = self
\n\treturn o\nend
2、LuaComponent组件LuaComponent主要有Get和Add两个静态方法,其中Get相当于UnityEngine中的GetComponent方法,Add相当于AddComponent方法,只不过这里添加的是lua组件不是c#组件。每个LuaComponent拥有一个LuaTable(lua表)类型的变量table,它既引用上述的Component表。Add方法使用AddComponent添加LuaComponent,调用参数中lua表的New方法,将其返回的表赋予table。Get方法使用GetComponents获取游戏对象上的所有LuaComponent(一个游戏对象可能包含多个lua组件,由参数table决定需要获取哪一个),通过元表地址找到对应的LuaComponent,返回lua表。代码如下:using UnityE\nusing System.C\nusing LuaI \nusing LuaF\n\npublic class LuaComponent : MonoBehaviour \n{\n\t//Lua表\n\tpublic LuaT\n\n\t//添加LUA组件
\n\tpublic static LuaTable Add(GameObject go, LuaTable tableClass)
\n\t\tLuaFunction fun = tableClass.GetLuaFunction(\"New\");\n\t\tif (fun == null)\n\t\t\\n\n\t\tobject[] rets = fun.Call (tableClass);\n\t\tif (rets.Length != 1)\n\t\t\\n\n\t\tLuaComponent cmp = go.AddComponent&LuaComponent&();
\n\t\tcmp.table = (LuaTable)rets[0];\n\t\tcmp.CallAwake ();\n\t\treturn cmp.\n\t}
\n\n\t//获取lua组件\n\tpublic static LuaTable Get(GameObject go,LuaTable table)
\n\t\tLuaComponent[] cmps = go.GetComponents&LuaComponent&();
\n\t\tforeach (LuaComponent cmp in cmps) \n\t\t{\n\t\t\tstring mat1 = table.ToString();\n\t\t\tstring mat2 = cmp.table.GetMetaTable().ToString();\n\t\t\tif(mat1 == mat2)\n\t\t\t{\n\t\t\t\treturn cmp.\n\t\t\t}\n\t\t}\n\t\
\n\t//删除LUA组件的方法略,调用Destory()即可
\n\n\tvoid CallAwake () \n\t{\n\t\tLuaFunction fun = table.GetLuaFunction(\"Awake\");\n\t\tif (fun != null)\n\t\t\tfun.Call (table, gameObject);\n\t}\n\n\tvoid Start () \n\t{\n\t\tLuaFunction fun = table.GetLuaFunction(\"Start\");\n\t\tif (fun != null)\n\t\t\tfun.Call (table, gameObject);\n\t}\n\n\tvoid Update () \n\t{\n\t\t//效率问题有待测试和优化\n\t\t//可在lua中调用UpdateBeat替代\n\t\tLuaFunction fun = table.GetLuaFunction(\"Update\");\n\t\tif (fun != null)\n\t\t\tfun.Call (table, gameObject);\n\t}\n\n\tvoid OnCollisionEnter(Collision collisionInfo)\n\t{\n\t\t//略\n\t}\n\n
//更多函数略\n}\n3、调试LuaCompomemt现在编写名为TankCmp的lua组件,测试LuaCompomemt的功能,TankCmp会在Awake、Start和Update打印出属性name。TankCmp.lua的代码如下:TankCmp = \n{\n\t--里面可以放一些属性\n\tHp = 100,\n\tatt = 50,\n\tname = \"good tank\",\n}\n\nfunction TankCmp:Awake()\n\tprint(\"TankCmp Awake name = \"..self.name );\nend\n\nfunction TankCmp:Start()\n\tprint(\"TankCmp Start name = \"..self.name );\nend\n\nfunction TankCmp:Update()\n\tprint(\"TankCmp Update name = \"..self.name );\nend\n\n--创建对象\nfunction TankCmp:New(obj)\n\tlocal o = {} \n
setmetatable(o, self)
self.__index = self
\n\treturn o\nend
编写Main.lua,给游戏对象添加lua组件。require \"TankCmp\"\n\n--主入口函数。从这里开始lua逻辑\nfunction Main()\t\n
--组件1\t\t\t\t\n\tlocal go = UnityEngine.GameObject ('go')\n\tlocal tankCmp1 = LuaComponent.Add(go,TankCmp)\n\ttankCmp1.name = \"Tank1\"\n\t--组件2\n\tlocal go2 = UnityEngine.GameObject ('go2')\n\tLuaComponent.Add(go2,TankCmp)\n\tlocal tankCmp2 = LuaComponent.Get(go2,TankCmp)\n\ttankCmp2.name = \"Tank2\"\nend运行游戏,即可看到lua组件的运行结果:图:程序运行结果图:程序运行结果4、坦克组件下面代码演示用lua组件实现“用键盘控制坦克移动”的功能,TankCmp.lua的代码如下:TankCmp = \n{\n\tname = \"good tank\",\n}\n\nfunction TankCmp:Update(gameObject)\n\tprint(\"TankCmp Update name = \"..self.name );\n\t\n\tlocal Input = UnityEngine.I\n\tlocal horizontal = Input.GetAxis(\"Horizontal\");\n\tlocal verticla = Input.GetAxis(\"Vertical\");\n\t\n\tlocal x = gameObject.transform.position.x + horizontal\n\tlocal z = gameObject.transform.position.z + verticla\n\tgameObject.transform.position = Vector3.New(x,0,z)\nend\n\n--创建对象\nfunction TankCmp:New(obj)\n\tlocal o = {} \n
setmetatable(o, self)
self.__index = self
\n\treturn o\nend
\nMain.lua先加载坦克模型,然后给他添加lua组件,代码如下:require \"TankCmp\"\n\n--主入口函数。从这里开始lua逻辑\nfunction Main()\t\t\t\t\t\n\tLuaHelper = LuaFramework.LuaH\n\tresMgr = LuaHelper.GetResManager();\n\tresMgr:LoadPrefab('tank', { 'TankPrefab' }, OnLoadFinish);\nend\n\n--加载完成后的回调--\nfunction OnLoadFinish(objs)\n\tgo = UnityEngine.GameObject.Instantiate(objs[0]);\n\tLuaComponent.Add(go,TankCmp)\nend运行游戏,即可用键盘的控制坦克移动。图:坦克组件运行结果最后是广告时间:《Unity3D网络游戏实战》是笔者即将出版的一本Unity3D实战类书籍。该书通过一个完整的多人坦克对战实例,详细介绍网络游戏开发过程中涉及到的知识和技巧。书中还介绍了服务端框架、客户端网络模块、UI系统的架构等内容。相信透过本书,读者能够掌握Unity3D网络游戏开发的大部分知识,也能够从框架设计中了解商业游戏的设计思路。《手把手教你用C#制作RPG游戏》(十二五全国高校数字游戏设计精品教材)是笔者出版的第一本书,书中将一款完整单机RPG游戏分解为角色、队伍、地图、NPC、界面系统、物品系统、技能系统、战斗系统、任务系统等模块,一步步介绍每个模块的实现方法,最后完成一款完整的游戏。图:《手把手教你用C#制作RPG游戏》项目截图","updated":"T15:42:47.000Z","canComment":false,"commentPermission":"anyone","commentCount":15,"likeCount":16,"state":"published","isLiked":false,"slug":"","isTitleImageFullScreen":false,"rating":"none","sourceUrl":"","publishedTime":"T23:42:47+08:00","links":{"comments":"/api/posts//comments"},"url":"/p/","titleImage":"/cbdf9e087d4d6a733bc6_r.png","summary":"","href":"/api/posts/","meta":{"previous":null,"next":null},"snapshotUrl":"","commentsCount":15,"likesCount":16},"":{"title":"Unity3D热更新LuaFramework入门实战(5)——UI","author":"pyluo","content":"界面系统在游戏中占据重要地位。游戏界面是否友好,很大程度上决定了玩家的体验;界面开发是否便利,也影响着游戏的开发进度。Unity3D 的UGUI系统,使用户可以“可视化地”开发界面,那么怎样用Lua去调用UGUI呢?微博 @罗培羽1、显示UI界面下面演示如何显示一个UI界面。由于UI界面也是一种资源,使用第二篇“资源热更新”的方法即可。这个例子中,制作一个含有按钮的界面,然后组成名为Panel1的UI预设,存放到Tank目录下。图:Panel1前面(第二篇)已在Packager类HandleExampleBundle方法中添加了一句“AddBuildMap(\"tank\" + AppConst.ExtName, \"*.prefab\", \"Assets/Tank\");”(当然也可以添加到其他地方),它会把Tank目录下的所有预设打包成名为tank的资源包。故而点击“Build xxx Resource”后,Panel1也会被打包到tank资源包中。修改Lua入口函数Main.lua中的Main方法,在加载资源后把panel1放到Canvas下(需要在场景中添加画布),然后调整它的位置和大小。--主入口函数。从这里开始lua逻辑\nfunction Main()\t\t\t\t\t\n\tLuaHelper = LuaFramework.LuaH\n\tresMgr = LuaHelper.GetResManager();\n\tresMgr:LoadPrefab('tank', { 'Panel1' }, OnLoadFinish);\nend\n \n--加载完成后的回调--\nfunction OnLoadFinish(objs)\n\t--显示面板\n\tgo = UnityEngine.GameObject.Instantiate(objs[0]);\n\tlocal parent = UnityEngine.GameObject.Find(\"Canvas\")\n
go.transform:SetParent(parent.transform);\n
go.transform.localScale = Vector3.\n
go.transform.localPosition = Vector3.\nend运行游戏,即可看到加载出来的界面。图:加载出来的界面2、事件响应c#中可以使用事件监听的方法给UI组件添加事件。例如,添加按钮点击事件的方法如下:\t\tButton btn = go.GetComponent&Button& ();\n\t\tbtn.onClick.AddListener\n\t\t(\n\t\t\tdelegate() \n\t\t\t{\n\t\t\t\tthis.OnClick(go)\n\t\t\t}\n\t\t);然而在LuaFramework的API中,没能找到合适的方法,只能根据第三篇中“自定义API”的方法,自己编写一套了。编写UIEvent类,它包含用于添加监听事件的AddButtonClick和清除监听事件的ClearButtonClick方法,代码如下所示(完成后记得要“修改CustomSetting”和“生成wrap文件”)。using UnityE\nusing System.C\nusing LuaI\nusing UnityEngine.UI;\n \npublic class UIEvent \n{\n\t//添加监听\n\tpublic static void AddButtonClick(GameObject go, LuaFunction luafunc) \n\t{\n\t\tif (go == null || luafunc == null) \n\t\t\\n \n\t\tButton btn = go.GetComponent&Button& ();\n\t\tif (btn == null)\n\t\t\\n \n\t\tbtn.onClick.AddListener\n\t\t(\n\t\t\tdelegate() \n\t\t\t{\n\t\t\t\tluafunc.Call(go);\n\t\t\t}\n\t\t);\n\t}\n \n\t//清除监听\n\tpublic static void ClearButtonClick(GameObject go) \n\t{\n\t\tif (go == null) \n\t\t\\n\t\t\n\t\tButton btn = go.GetComponent&Button& ();\n\t\tif (btn == null)\n\t\t\\n\t\t\n\t\tbtn.onClick.RemoveAllListeners();\n\t}\n}接下来测试下这套API,修改Main.lua,代码如下:--主入口函数。从这里开始lua逻辑\nfunction Main()\t\t\t\t\t\n\t略\nend\n \n--加载完成后的回调--\nfunction OnLoadFinish(objs)\n\t--显示面板\n\t略\n\t--事件处理\n\tlocal btn = go.transform:FindChild(\"Button\").gameObject\n\tUIEvent.AddButtonClick(btn, OnClick)\nend\n \nfunction OnClick()\n\tprint(\"触发按钮事件\")\nend运行游戏,点击按钮,OnClick方法即被调用。图:按钮的事件响应读者可以使用相似的方法监听其他UI组件,这里就只演示按钮事件了。3、界面管理器LuaFramework提供了一套简单的(不完善的)界面管理器,具体代码请参见PanelManager类。PanelManager类的CreatePanel方法完成异步加载资源,在加载完成后,会设置面板的大小和位置,然后调用回调函数。与上面用lua加载界面的方法完全一样。图:PanelManager的CreatePanel方法LuaFramework会给每个界面添加名为LuaBehaviour的组件,它拥有用于添加按钮监听的AddClick方法,相关代码如下,与UIEvent的AddButtonClick方法相似。图:LuaBehaviour的AddClick方法在LuaFramework的PureMVC架构中,如果要添加一个界面,需要编写对应的Controller、View,以及修改3个框架自带的lua文件,比较繁琐。因此在实际项目中有必要重写PanelManager,由它实现界面的加载及事件处理。广告时间:《Unity3D网络游戏实战》的第五章“代码分离的界面系统”介绍了一套通用UI系统的实现方法,有兴趣的读者可以看看。图:《Unity3D网络游戏实战》界面系统《专访仙剑同人游戏团队:这是我们的“心愿”》新浪游戏对心愿团队的采访稿,说出游戏开发过程中悲欢离合。","updated":"T15:45:55.000Z","canComment":false,"commentPermission":"anyone","commentCount":9,"likeCount":10,"state":"published","isLiked":false,"slug":"","isTitleImageFullScreen":false,"rating":"none","sourceUrl":"","publishedTime":"T23:45:55+08:00","links":{"comments":"/api/posts//comments"},"url":"/p/","titleImage":"/fb1071411bdf24d2b34e80_r.png","summary":"","href":"/api/posts/","meta":{"previous":null,"next":null},"snapshotUrl":"","commentsCount":9,"likesCount":10},"":{"title":"Unity3D热更新LuaFramework入门实战(6)——网络","author":"pyluo","content":"如今大部分的游戏都是网络游戏,网络游戏便涉及到网络连接发起、网络数据接收等内容。LuaFramework内置了网络模块(NetworkManager、SocketClient、ByteBuffer、Converter、Protocal),本篇将会介绍该模块的调用方法以及其原理。由于不是基础章节,文章不会详细讲解基础知识,如果读者想要了解Unity3D网络相关的内容,可以参考笔者即将出版的书籍《Unity3D网络游戏实战》中第六章“网络基础”。By 罗培羽
1、发起连接发起连接是客户端网络通信的第一步,LuaFramewor中,只需通过LuaFramework.AppConst.SocketAddress和LuaFramework.AppConst.SocketPort设置ip和端口,然后调用NetworkManager的SendConnect方法即可发起连接。Main.lua的代码如下:require \"Network\"\n \n--主入口函数。从这里开始lua逻辑\nfunction Main()\t\t\n\tlocal LuaHelper = LuaFramework.LuaHelper\n\tlocal networkMgr = LuaHelper.GetNetManager()\n\tlocal AppConst = LuaFramework.AppConst\n\t\n
AppConst.SocketPort = 1234;\n
AppConst.SocketAddress = \"127.0.0.1\";\n\tnetworkMgr:SendConnect();\nend在收到服务端回应后,LuaFramework会调用Network的OnSocket方法(写死)。新建名为Network.lua的文件,处理消息回调。在如下的代码中,Protocal代表协议号,比如“连接服务器”(Protocal.Connect)的协议号是101,在OnSocket的参数中,key便是收到的协议号,data是收到的数据。Network = {};\n \n--协议\nProtocal = {\n\tConnect\t\t= '101';\t--连接服务器\n\tException
= '102';\t--异常掉线\n\tDisconnect
= '103';\t--正常断线
\n\tMessage\t\t= '104';\t--接收消息\n}\n \n--Socket消息--\nfunction Network.OnSocket(key, data)\n\tif key == 101 then\n\t\tLuaFramework.Util.Log('OnSocket Connect');\t\t\n\telse\n\t\tLuaFramework.Util.Log('OnSocket Other');\t\n\tend\nend为了测试网络功能,需要编写服务端,这里使用c#编写一套简单的服务端程序,仅为调试使用,代码如下:using S\nusing System.N\nusing System.Net.S\nusing System.L\n \nclass MainClass\n{\n\tpublic static void Main(string[] args)\n\t{\n\t\tConsole.WriteLine(\"Hello World!\");\n\t\t//Socket\n\t\tSocket listenfd = new Socket(AddressFamily.InterNetwork,\n\t\t
SocketType.Stream, ProtocolType.Tcp);\n\t\t//Bind\n\t\tIPAddress ipAdr = IPAddress.Parse(\"127.0.0.1\");\n\t\tIPEndPoint ipEp = new IPEndPoint(ipAdr, 1234);\n\t\tlistenfd.Bind(ipEp);\n\t\t//Listen\n\t\tlistenfd.Listen(0);\n\t\tConsole.WriteLine(\"[服务器]启动成功\");\n\t\twhile (true)\n\t\t{\n\t\t\t//Accept\n\t\t\tSocket connfd = listenfd.Accept();\n\t\t\tConsole.WriteLine(\"[服务器]Accept\");\n\t\t}\n\t}\n}运行服务端和客户端,客户端会发起连接,服务端accept该连接后回应,客户端会显示“OnSocket Connect”图:服务端图:客户端此时把服务端关掉(断开连接),客户端会收到协议号为102的消息,即异常掉线(Exception)。图:异常掉线调用NetworkManager.SendConnect实际是调用BeginConnect发起连接。连接之后,回调OnConnect方法。图:连接过程OnConnect方法调用NetworkManager.AddEvent,排除设计模式的内容,相当于调用Network.lua的OnSocket方法。传入OnSocket的第1个参数为101(Protocal.Connect),指代协议名,第2个参数是空的字节流。网络模块中定义了101、102、103这3个固定的协议号,分别代表连接服务器、异常断线和正常断线。图:连接回调2、发送和接收接下来尝试发送和接收数据。LuaFramework默认(如果不去改它的代码)使用的协议格式如下图所示,前面的2个字节为消息长度,用于处理沾包分包,随后的2个字节代表协议号(如上面的101、102、103),最后才是消息的内容。图:协议修改Network.lua,在连接成功后(OnSocket方法的101协议),调用send发送一串协议号为104的数据。服务端收到数据后回射给客户端,客户端在收到回应后(OnSocket方法的104协议),读取并显示出来。send方法中新建了一个buffer,然后往buffer中添加协议号(104)和协议内容(字符串:《Unity3D网络游戏实战》是一本好书!),最后调用networkMgr:SendMessage()发送数据。networkMgr:SendMessage()会自动计算协议长度,并附加到buffer上发送出去。--Socket消息--\nfunction Network.OnSocket(key, data)\n\tif key == 101 then\n\t\tLuaFramework.Util.Log('OnSocket Connect');\t\n\t\tSend()\n\telseif key == 104 then\n\t\tLuaFramework.Util.Log('OnSocket Message ');\n\t\tlocal str = data:ReadString();\n\t\tLuaFramework.Util.Log('收到的字符串:'..str);\n\telse\n\t\tLuaFramework.Util.Log('OnSocket Other '..key);\t\n\tend\nend\n \nfunction Send()\n\t--组装数据\n
local buffer = LuaFramework.ByteBuffer.New();\n
buffer:WriteShort(Protocal.Message);\n
buffer:WriteString(\"《Unity3D网络游戏实战》是一本好书!\");\n\t--发送\n\tlocal LuaHelper = LuaFramework.LuaHelper\n\tlocal networkMgr = LuaHelper.GetNetManager()\n
networkMgr:SendMessage(buffer);\n\tLuaFramework.Util.Log('数据发送完毕');\t\nend修改服务端程序,读出接收到的内容,并echo回去。\tpublic static void Main(string[] args)\n\t{\n\t\t略,没有改动\n\t\twhile (true)\n\t\t{\n\t\t\t//Accept\n\t\t\tSocket connfd = listenfd.Accept();\n\t\t\tConsole.WriteLine(\"[服务器]Accept\");\n\t\t\t//Recv 不考虑各种意外,只做测试\n\t\t\tbyte[] readBuff = new byte[100];\n\t\t\tint count = connfd.Receive(readBuff);\n\t\t\t//显示字节流\n\t\t\tstring showStr = \"\";\n\t\t\tfor (int i = 0; i & i++)\n\t\t\t{\n\t\t\t\tint b = (int)readBuff[i];\n\t\t\t\tshowStr += b.ToString() + \" \";\n\t\t\t}\n\t\t\tConsole.WriteLine(\"[服务器接收]字节流:\"+ showStr);\n\t\t\t//解析协议\n\t\t\tInt16 messageLen = BitConverter.ToInt16(readBuff,0);\n\t\t\tInt16 protocal = BitConverter.ToInt16(readBuff,2);\n\t\t\tInt16 strLen = BitConverter.ToInt16(readBuff,4);\n\t\t\tstring str = System.Text.Encoding.UTF8.GetString(readBuff, 6, strLen);\n\t\t\tConsole.WriteLine(\"[服务器接收] 长度:\" + messageLen);\n\t\t\tConsole.WriteLine(\"[服务器接收] 协议号:\" + protocal);\n\t\t\tConsole.WriteLine(\"[服务器接收] 字符串:\" + str);\n\t\t\t//Send(echo)\n\t\t\tbyte[] writeBuff = new byte[count];\n\t\t\tArray.Copy(readBuff,writeBuff,count);\n\t\t\tconnfd.Send(writeBuff);\n\t\t}\n\t}运行游戏,可以看到服务端收到的如图所示的信息。字节流的前两位“53 0”表示消息长度为53字节,紧跟着的“104 0”代表协议号104。在字符串的封装中(buffer:WriteString),程序会先在buffer中添加字符串的长度,最后才是字符串的内容。“49 0”即表示“《Unity3D网络游戏实战》是一本好书!”占用49个字节(14个中文符号,每个3字节,7个英文符号,每个1字节)。协议长度53字节 = 协议号2个字节 + 字符串长度2字节 + 字符串内容49字节。图:服务端收到的信息客户端收到服务端回射的消息后,也会显示出来,如下图所示。图:客户端收到的消息在lua中调用networkMgr:SendMessage(buffer)时,实际上相当于调用了SocketClient的WriteMessage方法,该方法会计算协议的长度,然后将长度和内容组装在一起,调用BeginWrite发送数据。图:发送数据在建立连接后,SocketClient会调用BeginRead,当收到服务端的消息时,回调OnRead方法。OnRead又调用了OnReceive方法。图:接收数据过程OnReceive方法完成沾包分包处理,然后调用AddEvent方法分发消息(相当于调用了lua中NetWork表的OnSocket方法)。图:解析数据过程关于BeginRead、BeginConnect等方法的介绍,读者可以查看c#网络编程的资料或参照《Unity3D网络游戏实战》第6章“网络基础”。3、消息分发一款游戏往往涉及很多条网络通信协议,在Network.OnSocket中,如果只用ifelse语句处理不同协议,代码往往会混乱不堪。LuaFramework集成了消息分发的方法,用法如下所示。1、引用LuaFramework\\Lua\\events.lua,然后使用Event.AddListener添加监听,例如“Event.AddListener(Protocal.Connect, Network.OnConnect); ”表示当收到101协议(Protocal.Connect)时,回调Network.OnConnect方法。Main.lua代码如下:require \"Network\"\nEvent = require 'events'\n \n--主入口函数。从这里开始lua逻辑\nfunction Main()\t\t\n\tlocal LuaHelper = LuaFramework.LuaHelper\n\tlocal networkMgr = LuaHelper.GetNetManager()\n\tlocal AppConst = LuaFramework.AppConst\n\t\n
AppConst.SocketPort = 1234;\n
AppConst.SocketAddress = \"127.0.0.1\";\n\t\n\tEvent.AddListener(Protocal.Connect, Network.OnConnect); \n
Event.AddListener(Protocal.Message, Network.OnMessage); \n\t\n\tnetworkMgr:SendConnect();\nend2、在需要分发消息的地方调用Event.Brocast,然后编写相应的回调函数。Network.lua的部分代码如下:--Socket消息--\nfunction Network.OnSocket(key, data)\n\tLuaFramework.Util.Log('OnSocket 消息分发:'..key);\n\tEvent.Brocast(tostring(key), data);\nend\n \nfunction Network.OnConnect(data) \n
LuaFramework.Util.Log('Network.OnConnect');\t\n\tSend()\nend\n \nfunction Network.OnMessage(data) \n
LuaFramework.Util.Log('Network.OnMessage');\n\tlocal str = data:ReadString();\n\tLuaFramework.Util.Log('收到的字符串:'..str);\nend运行游戏,可以看到消息分发的结果。图:消息分发调用Event.AddListener,实际上是在一个表中添加数据,把某个协议号对应于某个方法的信息记录起来。图:AddListener的过程当调用Event.Brocast时,程序会查找这份表,然后执行回调方法。这里使用了协程来调用回调函数。使用协程的目的应该是不让回调逻辑阻碍主体逻辑,然而由于协程是单线程的,这点不起作用。除非回调函数也使用协程,相互配合。所以这里应该可以不用协程的。图:Brocast的过程至此,读者应该理解LuaFramework网络模块的使用方法了。《Unity3D网络游戏实战》中,第六章“网络基础”,第七章“服务端框架”,第八章“客户端网络模块”都讲解网络有关的内容,并介绍一套网络框架的实现方法,相信对读者能有帮助。最后依然是广告时间:两款好玩的flash枪战小游戏(模式很创新的,不要说是很坑爹!!)","updated":"T14:36:47.000Z","canComment":false,"commentPermission":"anyone","commentCount":19,"likeCount":8,"state":"published","isLiked":false,"slug":"","isTitleImageFullScreen":false,"rating":"none","sourceUrl":"","publishedTime":"T22:36:47+08:00","links":{"comments":"/api/posts//comments"},"url":"/p/","titleImage":"/d80b7b4eeaf_r.png","summary":"","href":"/api/posts/","meta":{"previous":null,"next":null},"snapshotUrl":"","commentsCount":19,"likesCount":8},"":{"title":"Unity3D热更新LuaFramework入门实战(7)——PureMVC","author":"pyluo","content":"LuaFramework使用了PureMVC框架。百度百科上说:“PureMVC是在基于模型、视图和控制器MVC模式建立的一个轻量级的应用框架”。PureMVC框架可以做到较好的解耦,减少游戏代码的相互调用。然而LuaFramework整合PureMVC属于“杀鸡用牛刀”,实质上只用到了事件分发(也可能是我理解得不够透彻)。如果单纯写一套事件分发系统,可能不到100行代码就能完成。by 罗培羽1、解耦的好处如果没有很好的解耦设计,游戏功能越多,代码就越乱,最后没人敢改动。举个例子,假如游戏中背包(item)和成就(Achieve)两项功能,各用一个类实现。当玩家获得100个经验豆(一种道具)时,会获得“拥有100个经验豆”的成就;当成就点数达到300时,会获得道具奖励。一种常见的实现方法是调用对方的public函数,代码如下所示。然而如果一款游戏有几百上千个类,之间又相互调用,如果某些功能需要大改(例如删掉成就功能),那其他的类也得改动。Class Item\n{\n
public AddItem()\n
if(经验豆 & 100)\n
achieve.AddAchieve(“拥有100个经验豆”)\n
}\n}\n \nClass Achieve\n{\n
public AddAchieve()\n
成就点数 + 10\n
if(成就点数 & 300)\n
item.AddItem(宝石)\n
}\n}如果使用事件分发,各个类之间的联系就减弱了。如下所示的代码中背包类(Item)监听了消息“添加道具”,成就类(Achieve)监听了消息“添加成就”。如果达成成就需要添加奖励,只需派发“添加道具”这条消息,由背包类去执行。这样类与类之间不存在相互调用,就算大改功能甚至删掉功能,其他类都受到的影响比较小。Class Item\n{\n
监听(“添加道具”,AddItem)\n
private AddItem()\n
if(经验豆 & 100)\n
分发(“添加成就”,“拥有100个经验豆”)\n
}\n}\n \nClass Achieve\n{\n
监听(“添加成就”,AddAchieve)\n
private AddAchieve()\n
成就点数 + 10\n
If(成就点数 & 300)\n
分发(“添加道具”, 宝石)\n
}\n}2、MVC的使用方法LuaFramework中的Framwork目录存放着PureMVC框架的代码,个人认为在LuaFramework中属于过度设计(毕竟从其他地方拷过来的)。它的原理并不复杂,用一个列表把监听信息保存起来,在派发消息时,查找对应的监听表,找到需要回调的对象。PureMVC框架便是实现了“注册/分发”模式(发布/订阅、观察者模式),可以调用RegisterCommand注册消息(命令),调用SendMessageCommand方法分发消息。RegisterCommand方法可以把某个继承ControllerCommand 的类注册到指定的消息下,在事件分发时调用该类的Execute方法。例如新建一个名为TestCommand的类,让它继承ControllerCommand,然后编写Execute方法处理具体事务。using UnityE\nusing System.C\n \npublic class TestCommand : ControllerCommand \n{\n\tpublic override void Execute(IMessage message) \n\t{\n\t\tDebug.Log(\"name=\" + message.Name);\n\t\tDebug.Log(\"type=\" + message.Type);\n\t}\n}接着,编写另一个类来处理消息。这个类先调用AppFacade.Instance.RegisterCommand()将TestCommand类注册到“TestMessage”消息下。然后使用SendMessageCommand()派发“TestMessage”消息。框架将会创建一个TestCommand实例,并调用它的Execute方法。public class Main : MonoBehaviour \n{\n
void Start() \n\t{\n\t\tAppFacade.Instance.RegisterCommand (\"TestMessage\", \n\t\t\t\t\t\t\ttypeof(TestCommand));\n\t\tAppFacade.Instance.SendMessageCommand (\"TestMessage\");\n
}\n}运行结果如下所示,可以看到分发消息后,TestCommand的Execute方法被调用。Execute方法的参数message包含了Name,Body,Type三个成员(如下图所示)。其中Name是命令名,Body是一个任意类型的参数。如下代码所示,在SendMessageCommand中可以给消息的Body传值,相应的Execute方法便可以获取它。void Start() \n{\n\tAppFacade.Instance.RegisterCommand (\"TestMessage\", \n\t\t\t\t\t\t\ttypeof(TestCommand));\n\tAppFacade.Instance.SendMessageCommand (\"TestMessage\", \"这是字符串\");\n}运行结果如下图所示。总而言之,LuaFramework中所谓的pureMVC只是一套“注册/分发”机制,完全可以用c#的事件来实现。另《Unity3D网络游戏实战》中的客户端网络模块部分也使用的“注册/分发”机制,有兴趣的读者可以看看。3、MVC与Unity3D组件的结合pureMVC与Unity3D组件之间有一些封装,只要让组件继承View类(View类继承MonoBehavior),即使用pureMVC框架的RegisterMessage和SendMessageComman方法实现“注册/分发”机制。例如,新建一个继承自View的TestManage组件,在Start 方法中它注册了“msg1”、“msg2”、“msg3”三个消息的监听。在Update方法中,当按下空格键时,分发消息“msg1”。当接收到消息后,指定对象(这里指定this)的OnMessage方法会被调用,参数message里面包含了命令名、Body等信息。代码如下所示。using UnityE\nusing System.C\nusing System.Collections.G\n \npublic class TestManage : View \n{\n \n\t// Use this for initialization\n\tvoid Start () \n\t{\n\t\tList&string& regList = new List&string&();\n\t\tregList.Add(\"msg1\");\n\t\tregList.Add(\"msg2\");\n\t\tregList.Add(\"msg3\");\n \n\t\tRegisterMessage(this,regList);\n\t}\n\t\n\t// Update is called once per frame\n\tvoid Update () \n\t{\n\t\tif (Input.GetKeyUp (KeyCode.Space)) \n\t\t{\n\t\t\tfacade.SendMessageCommand(\"msg1\", null);\n\t\t}\n\t}\n \n\tpublic override void OnMessage(IMessage message) \n\t{\n\t\tDebug.Log (\"OnMessage \" + message.Name);\n\t}\n}此外LuaFramework的各个Manager(如GameManager,LuaManager,SoundManager等)也都继承自View类,可以使用“注册/分发”机制。后记也许我对pureMVC的设计理解不足,但在LuaFramework中,我依然认为它过度设计。最后依然是广告时间。笔者即将出版的一本Unity3D实战类书籍《Unity3D网络游戏实战》。通过一个完整的多人坦克对战实例,详细介绍网络游戏开发过程中涉及到的知识和技巧。书中还介绍了服务端框架、客户端网络模块、UI系统的架构等内容。下图为《Unity3D网络游戏实战》的示例游戏。其他一些文章的连接:Unity3D热更新LuaFramework入门实战(1)——代码热更新Unity3D热更新LuaFramework入门实战(2)——资源热更新Unity3D热更新LuaFramework入门实战(3)——编写Lua逻辑AR仙剑:一款手机应用,配合手机或pad使用,能将二次元动漫人物零距离展现在你的身旁!《不会电脑也汇编》笔者N多年前写的汇编教程","updated":"T13:09:57.000Z","canComment":false,"commentPermission":"anyone","commentCount":9,"likeCount":4,"state":"published","isLiked":false,"slug":"","isTitleImageFullScreen":false,"rating":"none","sourceUrl":"","publishedTime":"T21:09:57+08:00","links":{"comments":"/api/posts//comments"},"url":"/p/","titleImage":"/8da3418eedcdde84f471d_r.png","summary":"","href":"/api/posts/","meta":{"previous":null,"next":null},"snapshotUrl":"","commentsCount":9,"likesCount":4},"":{"title":"Unity3D热更新LuaFramework入门实战(8)——声音管理器","author":"pyluo","content":"LuaFramework内置的管理器包括GameManager(处理热更新)、luaManager(lua脚本管理器)、PanelManager(界面管理器)、NetworkManager(网络管理器)、ResourceManager(资源管理器)、TimerManager(时间管理器)、线程管理器(ThreadManager)和SoundManager(声音管理器)。其中GameManager、luaManager、PanelManager、NetworkManager、ResourceManager在前面的文章中已经有过介绍,这一篇讲讲讲播放声音相关的SoundManager。by 罗培羽1、使用方法SoundManager估计是从其他地方拷过来的,并不能很好的与框架结合,这里我们先看看原来的SoundManager的使用方法,再介绍它的不足之处及改进方法。虽然SoundManager定义了好几个方法,但能直接在lua中使用的只有用于播放背景音乐的PlayBacksound。编写播放声音的代码前,需要在GameManager上挂载AudioSource组件,以播放背景音乐。把声音文件放到Resource目录下,由于SoundManager使用Resources.Load加载声音文件,声音文件必须放到这个目录下。如下图所示。然后编写lua代码,调用soundMgr:PlayBacksound即可,它的第一个参数指明Resource目录下的文件名,第二个参数为true表示开始播放,false表示停止播放。--主入口函数。从这里开始lua逻辑\nfunction Main()\t\t\t\t\t\n\tLuaHelper = LuaFramework.LuaH\n\tsoundMgr = LuaHelper.GetSoundManager();\n\tsoundMgr:PlayBacksound(\"motor\", true)\nend运行游戏,即可听到音效。关于Unity3D播放声音的内容,大家也可以参考《Unity3D网络游戏实战》哦!2、代码解析SoundManager的示意代码如下,实际上是使用Resources.Load来加载资源的,所以声音文件必须放在Resources目录下。这种用法违背了热更新框架的设计,因为在Resources目录下的文件并不能热更。3、改进的声音管理器这一部分我们需要改进声音管理器,实现这么几个功能:1)从本地“数据目录”读取打包后的声音文件,使它支持热更新;2)添加播放/停止背景音乐的PlayBackSound/StopBackSound和播放音效的PlaySound方法;3)使用缓存存储加载后的声音文件,以提高运行效率。为了支持加载AudioClip的加载,在ResourceManager中添加LoadAudioClip方法,该方法将会加载资源包abName的资源assetName,加载完AudioClip资源后调用回调函数func,代码如下。//载入音效资源\npublic void LoadAudioClip(string abName, \n
string assetName, Action&UObject[]& func) \n{\n\tLoadAsset&AudioClip&(abName, new string[] { assetName }, func);\n}修改SoundManager,使用Hashtable类型的sounds存储加载后的声音,包含PlayBackSound、StopBackSound和PlaySound三个API。代码如下所示。先是整体kuanusing UnityE\nusing System.C\nusing System.Collections.G\n \nnamespace LuaFramework \n{\n
public class SoundManager : Manager \n
{\n\tprivate AudioS\n\tprivate Hashtable sounds = new Hashtable();\n\tstring backSoundKey = \"\";\n \n\tvoid Start() \n\t{\n\t\taudio = GetComponent&AudioSource&();\n\t\tif (audio == null)\n\t\t\tgameObject.AddComponent&AudioSource& ();\n\t}\n \n\t//回调函数原型\n\tprivate delegate void GetBack(AudioClip clip, string key);\n \n\t//获取声音资源\n\tprivate void Get(string abName, string assetName, GetBack cb)\n\t{\n\t\tstring key = abName + \".\" + assetN\n\t\tif(sounds [key] == null) \n\t\t{\n\t\t\tResManager.LoadAudioClip(abName, assetName, (objs)=&\n\t\t\t{\n\t\t\t\tif(objs == null || objs[0] == null)\n\t\t\t\t{\n\t\t\t\t\tDebug.Log(\"PlayBackSound fail);\n\t\t\t\t\tcb(null,key);\n\t\t\t\t\\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tsounds.Add(key, objs[0]);\n\t\t\t\t\tcb(objs[0] as AudioClip ,key);\n\t\t\t\t\\n\t\t\t\t}\n\t\t\t});\n\t\t} \n\t\telse \n\t\t{\n\t\t\tcb(sounds [key] as AudioClip,key);\n\t\t\\n\t\t}\n\t}\n
}\n}PlayBackSound:\t//播放背景音乐\n\tpublic void PlayBackSound(string abName, string assetName)\n\t{\n\t\tbackSoundKey = abName + \".\" + assetN\n\t\tGet(abName, assetName,(clip, key)=&\n\t\t{\n\t\t\tif(clip == null)\n\t\t\t\\n\t\t\tif(key != backSoundKey)\n\t\t\t\\n \n\t\t\taudio.loop =\n\t\t\taudio.clip =\n\t\t\taudio.Play();\n\t\t});\n\t}\nStopBackSound:\t//停止背景音乐\n\tpublic void StopBackSound()\n\t{\n\t\tbackSoundKey = \"\";\n\t\taudio.Stop ();\n\t}\n \t//播放音效\n\tpublic void PlaySound(string abName, string assetName)\n\t{\n\t\tGet(abName, assetName,(clip, key)=&\n\t\t{\n \n\t\t\tif(clip == null)\n\t\t\t\\n\t\t\tif(Camera.main == null)\n\t\t\t\\n\t\tAudioSource.PlayClipAtPoint(clip, \n\t\t\t\t\tCamera.main.transform.position); \n\t\t});\n\t}\n修改代码后,需要重新生成wrap文件(点击菜单栏的Lua→Clear wrap files和Lua→Generate All)。然后编写lua代码调试它吧!这里演示的是先播放背景音乐,3秒后停止播放,每隔0.3秒播放一次音效的功能。--主入口函数。从这里开始lua逻辑\nfunction Main()\t\t\t\t\t\n\tLuaHelper = LuaFramework.LuaH\n\tsoundMgr = LuaHelper.GetSoundManager();\n\tsoundMgr:PlayBackSound(\"sound\", \"motor\")\n\tUpdateBeat:Add(Update, self)\nend\n \n \nlocal lastTime = 0\n \nfunction Update()\n\tif Time.time & 3 then\n\t\tsoundMgr:StopBackSound();\n\tend\n\t\n\t\n\tif Time.time - lastTime & 0.3 then\n\t\tsoundMgr:PlaySound(\"sound\", \"shoot\")\n\t\tlastTime = Time.time\n\tend\nend读者还可以使用类似 LoadAudioClip的方法加载其他资源,笔者也是刚刚接触LuaFramework不久,文章错误之处在所难免,请大家多加包涵。最后是广告时间:《16年的长度 记录中国独立游戏》从十多年前程序员写的小玩儿,到如今使用游戏引擎开发的炫酷产品,独立游戏作者的故事见证了我国游戏业的发展历程。来看看国产单机的造梦者们,在这十几年中做出怎么的作品,现在又过得如何。《仙剑5前传之心愿》是笔者两年前发起的一款仙剑同人游戏,使用Unity3D制作,是市面上第一款能够完成的3D仙剑同人游戏。如今我们各自踏上游戏开发一途,似乎要做点什么以致敬国产经典之作,在追求商业成就的同时,勿忘初心。可以在下载该游戏。《仙剑5前传之心愿》截图如下。","updated":"T13:29:28.000Z","canComment":false,"commentPermission":"anyone","commentCount":1,"likeCount":11,"state":"published","isLiked":false,"slug":"","isTitleImageFullScreen":false,"rating":"none","sourceUrl":"","publishedTime":"T21:29:28+08:00","links":{"comments":"/api/posts//comments"},"url":"/p/","titleImage":"/aa4d7bd7bec7e9eca27eb_r.png","summary":"","href":"/api/posts/","meta":{"previous":null,"next":null},"snapshotUrl":"","commentsCount":1,"likesCount":11},"":{"title":"Unity3D热更新LuaFramework入门实战(9)——线程管理器","author":"pyluo","content":"LuaFramework内置了线程管理器ThreadManager,一开始我以为这是个创建线程、终止线程等方法的封装。然而不是,它是热更新时使用线程下载资源的具体实现。那让我们来看看线程管理器的工作原理吧。@罗培羽1、GameManager的调用那么先看看在热更新过程中哪些地方调用到ThreadManager。热更新由GameManager执行(相关代码如下图所示),它在对比本地文件和网络资源的差异后,将需要下载的文件名存放到列表中,然后遍历列表,调用BeginDownload下载。从代码可以看出,它通过ISDownOK判断该文件是否下载完成,然后下载下一个文件,一个个的下载文件。BeginDownload(代码如下所示)便调用ThreadManager的AddEvent方法。ThreadManager并不是真正意义上的线程管理器,它只管理一条“下载线程”,通过AddEvent将要下载的文件名放到“代办列表”中,该线程依次下载它们。其中的OnThreadCompleted是“回调函数”,在下载该文件后,会通过消息的方式回调它。在“下载线程”下载完一个文件后,它以通知的形式调用“回调函数”OnThreadCompleted(代码如下所示),该方法将会设置“下载完成列表”downloadFiles。再看看IsDownOk(代码如下所示)方法,当“下载完成列表”包含该文件时,说明下载已经完成,可以进行下一个文件的下载。2、ThreadManager的启动ThreadManager启动时,开启一个线程“下载线程”,相关代码如下所示。由此ThreadManager仅仅是管理一条线程,而不是真正意义的线程管理器。3、AddEvent方法AddEvent是给线程添加任务的方法,代码如下,其实就是给events队列添加一个值。Events的定义如下所示:ThreadEvent包含事件名key和参数evParams,代码如下所示。3、下载过程“下载线程”执行了OnUpdate方法(代码如下所示),它调用OnDownloadFile下载文件。OnDownloadFile(代码如下所示)又调用了DownloadFileAsync下载文件,下载文件过程中ProgressChanged方法会被调用。ProgressChanged方法记录了下载进度,当进度为100%时,使用m_SyncEvent发送通知,相当于调用“回调函数”OnThreadCompleted。4、改进这套线程管理器依然有“杀鸡用牛刀”之嫌,“任务列表”并没有实际作用(因为GameManager控制了下载进度,一个个下载),消息分发部分也太复杂,实际上只用回调函数之类的方法便能够实现。个人认为线程管理器应当提供线程调度的方法,具体的下载逻辑可在GameManager中实现。而且下载功能不一定非要用线程,协程也能够解决,而且更简单。代码如下所示。由于热更新需要下载不少的文件,一个个下载实在太慢。如果能开启多个线程,同时下载,可在一定程度上提高下载速度。框架并没有处理下载失败的情况,一般情况下,当一个文件下载失败,应当重试,在重试多次依然无法下载时,才弹出错误。最后是广告时间:《16年的长度 记录中国独立游戏》从十多年前程序员写的小玩儿,到如今使用游戏引擎开发的炫酷产品,独立游戏作者的故事见证了我国游戏业的发展历程。来看看国产单机的造梦者们,在这十几年中做出怎么的作品,现在又过得如何。《仙剑5前传之心愿》是笔者两年前发起的一款仙剑同人游戏,使用Unity3D制作,是市面上第一款能够完成的3D仙剑同人游戏。如今我们各自踏上游戏开发一途,似乎要做点什么以致敬国产经典之作,在追求商业成就的同时,勿忘初心。可以在下载该游戏。《仙剑5前传之心愿》截图如下。","updated":"T13:54:21.000Z","canComment":false,"commentPermission":"anyone","commentCount":5,"likeCount":3,"state":"published","isLiked":false,"slug":"","isTitleImageFullScreen":false,"rating":"none","sourceUrl":"","publishedTime":"T21:54:21+08:00","links":{"comments":"/api/posts//comments"},"url":"/p/","titleImage":"","summary":"","href":"/api/posts/","meta":{"previous":null,"next":null},"snapshotUrl":"","commentsCount":5,"likesCount":3},"":{"title":"Unity3D热更新LuaFramework入门实战(10)——示例程序","author":"pyluo","content":"终于到了本系列完结的时候了。现在,大家对LuaFramework有个全方位的理解了吧!接下来通过一个例子总结ulua,作为“lua逻辑”的延伸,说明lua的写法。这个例子中玩家能够控制2D游戏角色走动,并且发射炮弹。1、目标制作如图所示的游戏,玩家可以通过键盘控制角色上下左右移动,角色有4个面向,走动过程中会播放行走动画。当玩家点击鼠标左键,角色会发射一颗炮弹。2、游戏资源使用下图所示的图片作为游戏角色(该图片来自rpg maker),在导入Unity后将它切割成12张小图。使用如下图所示的图片作为炮弹。在游戏场景中新建画布,画布下摆放一个名为Panel的面板,代表游戏场景。面板下有res和map两个子物体,map(Image)为一张场景图,role(Image)为游戏中的角色,bullet(Image)为游戏中的子弹(同一时间只能发射一颗子弹)。Res子物体存放12张角色图片(Image),之后会使用这些资源替换map.role的图片,以实现动画效果。然后将Panel做成预设,存放到SimpleGame目录下。并且在GameManager空物体上添加Game组件,以启动框架。修改Packager.cs,在HandleExampleBundle添加如下代码,将SimpleGame目录下的预设打包。然后点击LuaFramework→Build Windows Resource打包。//小游戏\nAddBuildMap(\"SimpleGame\" + AppConst.ExtName, \"*.prefab\", \"Assets/SimpleGame\");具体的框架设置请参加第一篇和第二篇,这里仅做简单描述。3、编写行走代码游戏使用UI组件,在CustomSettings.cs中添加如下两行,使tolua生成Image和Sprite相关的调用。_GT(typeof(Image)),\n_GT(typeof(Sprite)),打开main.lua(如何运行main.lua请参见第一篇)开始编写代码。程序从Main方法开始执行,使用LoadPrefab(请参见第二篇)加载之前打包的资源文件Panel。这里还定义几个变量,其中map代表游戏场景(panel.map),role代表游戏角色(panel.map.role),roleImage是游戏角色中的图片组件,roleRes代表各个面向的角色图片,比如roleRes[“UP”]将会包含panel.res中3张角色朝上的图。roleAnm代表当前角色的动画,每个面向有3个动画,对应于不同的图片。lastAnmTime代表展现角色动画帧的时间,用于控制动画播放速度。--主入口函数。从这里开始lua逻辑\nfunction Main()\t\t\t\t\t\n\tLuaHelper = LuaFramework.LuaH\n\tresMgr = LuaHelper.GetResManager();\n\tresMgr:LoadPrefab('SimpleGame', { 'Panel' }, OnLoadFinish);\nend\n\nlocal map\nlocal role\nlocal roleImage\nlocal roleRes = {\n\t[\"UP\"] = {},\n\t[\"DOWN\"] = {},\n\t[\"LEFT\"] = {},\n\t[\"RIGHT\"] = {},\n}\nlocal roleAnm = 1;\nlocal lastAnmTime = 0;\n\n--加载完成后的回调--\nfunction OnLoadFinish(objs)\n
--暂略\nend接着编写加载完成的回调方法OnLoadFinish,它处理下面几件事情。1、使用Instantiate实例化面板,并且设置面板的坐标,具体请参见第5篇。2、获取面板中的部件,给map、role、roleImage赋值。3、获取素材res中的图片,赋值给roleRes,之后roleRes [\"UP\"],roleRes [\"DOWN\"],roleRes [\"LEFT\"],roleRes [\"RIGHT\"]都包含3张同面向不同动画的图片。4、使用UpdateBeat:Add()初始化Update方法(具体参照第三篇)。function OnLoadFinish(objs)\n\t--显示面板\n\tgo = UnityEngine.GameObject.Instantiate(objs[0])\n\tlocal parent = UnityEngine.GameObject.Find(\"Canvas\")\n
go.transform:SetParent(parent.transform, false)\n\t--获取元素\n\tmap = go.transform:FindChild(\"map\").gameObject\n\trole = map.transform:FindChild(\"role\").gameObject\n\troleImage = role:GetComponent(\"Image\")\n\t--获取素材\n\tlocal res = go.transform:FindChild(\"res\").gameObject\n\troleRes[\"DOWN\"][1] = res.transform:FindChild(\"role (0)\").gameObject:GetComponent(\"Image\").sprite\n\troleRes[\"DOWN\"][2] = res.transform:FindChild(\"role (1)\").gameObject:GetComponent(\"Image\").sprite\n\troleRes[\"DOWN\"][3] = res.transform:FindChild(\"role (2)\").gameObject:GetComponent(\"Image\").sprite\n\troleRes[\"LEFT\"][1] = res.transform:FindChild(\"role (3)\").gameObject:GetComponent(\"Image\").sprite\n\troleRes[\"LEFT\"][2] = res.transform:FindChild(\"role (4)\").gameObject:GetComponent(\"Image\").sprite\n\troleRes[\"LEFT\"][3] = res.transform:FindChild(\"role (5)\").gameObject:GetComponent(\"Image\").sprite\n\troleRes[\"RIGHT\"][1] = res.transform:FindChild(\"role (6)\").gameObject:GetComponent(\"Image\").sprite\n\troleRes[\"RIGHT\"][2] = res.transform:FindChild(\"role (7)\").gameObject:GetComponent(\"Image\").sprite\n\troleRes[\"RIGHT\"][3] = res.transform:FindChild(\"role (8)\").gameObject:GetComponent(\"Image\").sprite\n\troleRes[\"UP\"][1] = res.transform:FindChild(\"role (9)\").gameObject:GetComponent(\"Image\").sprite\n\troleRes[\"UP\"][2] = res.transform:FindChild(\"role (10)\").gameObject:GetComponent(\"Image\").sprite\n\troleRes[\"UP\"][3] = res.transform:FindChild(\"role (11)\").gameObject:GetComponent(\"Image\").sprite\n\t--UpdateBeat\n\tUpdateBeat:Add(Update, self)\nend编写Update方法,它根据用户输入改变坐标(具体参见第三篇),并且根据不同的移动方向设置角色图片素材,将roleImage.sprite替换成roleRes[方向][动画索引]。最后判断“if Time.time - lastAnmTime & 0.1 then”,每隔0.1秒切换一次动画。--每帧执行\nfunction Update()\n\t\n\t--移动\n\tlocal Input = UnityEngine.I\n\tlocal horizontal = Input.GetAxis(\"Horizontal\");\n\tlocal verticla = Input.GetAxis(\"Vertical\");\n\t\n\tlocal x = role.transform.position.x + horizontal\n\tlocal y = role.transform.position.y + verticla\n\trole.transform.position = Vector3.New(x,y,0)\n\t--转向\n\tif horizontal & 0 then\n\t\troleImage.sprite = roleRes[\"LEFT\"][roleAnm]\n\telseif horizontal & 0 then\n\t\troleImage.sprite = roleRes[\"RIGHT\"][roleAnm]\n\telseif verticla & 0 then\n\t\troleImage.sprite = roleRes[\"UP\"][roleAnm]\n\telseif verticla & 0 then\n\t\troleImage.sprite = roleRes[\"DOWN\"][roleAnm]\n\tend\n\t--步伐(动画索引)\n\tif Time.time - lastAnmTime & 0.1 then\n\t\troleAnm = roleAnm+1\n\t\tif roleAnm & 3 then roleAnm = 1 end\n\t\tlastAnmTime = Time.time\n\tend\n\t\nend运行游戏,玩家可以通过键盘的方向键控制角色移动。3、编写射击代码在main.lua中添加炮弹相关的变量,如下所示。其中bullet代表炮弹元件(panel.map.bullet),lastShootTime 代表上一次发射炮弹的时间,bulletSpeedX代表炮弹的水平移动速度,bulletSpeedY代表炮弹的垂直移动速度,roleFace代表角色的面向。然后在OnLoadFinish中给bullet赋值。local bullet\nlocal lastShootTime = -100\nlocal bulletSpeedX = 0\nlocal bulletSpeedY = 0\nlocal roleFace = 0\n\nfunction OnLoadFinish(objs)\n\t……\n\t--子弹元素\n\tbullet = map.transform:FindChild(\"bullet\").gameObject\nEnd在Update中给roleFace赋值。--每帧执行\nfunction Update()\n\t\n\t--移动\n\t……\n\t--转向\n\tif horizontal & 0 then\n\t\troleImage.sprite = roleRes[\"LEFT\"][roleAnm]\n\t\troleFace = 1\n\telseif horizontal & 0 then\n\t\troleImage.sprite = roleRes[\"RIGHT\"][roleAnm]\n\t\troleFace = 2\n\telseif verticla & 0 then\n\t\troleImage.sprite = roleRes[\"UP\"][roleAnm]\n\t\troleFace = 3\n\telseif verticla & 0 then\n\t\troleImage.sprite = roleRes[\"DOWN\"][roleAnm]\n\t\troleFace = 4\n\tend\n\t--步伐\n\t……\nend在Update中添加处理炮弹的代码,它处理如下几件事情。1、炮弹在飞行1.2秒后,燃尽消失;2、当玩家按下鼠标左键时,发射炮弹,根据角色面向,bulletSpeedX和bulletSpeedY会有不同的值。3、根据bulletSpeedX和bulletSpeedY移动炮弹。--每帧执行\nfunction Update()\n\t……\n\t--子弹\n\tif Time.time - lastShootTime & 1.2 then\n\t\t--消失\n\t\tif bullet.transform.position.x ~= -999 then\n\t\t\tbullet.transform.position = Vector3.New(-999,-999,0)\n\t\tend\n\t\t--发射\n\t\tif Input.GetMouseButton(0) then\n\t\t\tbullet.transform.position = Vector3.New(x,y,0)\n\t\t\tif roleFace == 1 then\n\t\t\t\tbulletSpeedX = -10\n\t\t\t\tbulletSpeedY = 0\n\t\t\telseif roleFace == 2
then\n\t\t\t\tbulletSpeedX = 10\n\t\t\t\tbulletSpeedY = 0\n\t\t\telseif roleFace == 3 then\n\t\t\t\tbulletSpeedX = 0\n\t\t\t\tbulletSpeedY = 10\n\t\t\telseif roleFace == 4
then\n\t\t\t\tbulletSpeedX = 0\n\t\t\t\tbulletSpeedY = -10\n\t\t\tend\n\t\t\tlastShootTime = Time.time\n\t\tend\n\telse\n\t\t--运动\n\t\tlocal x = bullet.transform.position.x + bulletSpeedX\n\t\tlocal y = bullet.transform.position.y + bulletSpeedY\n\t\tbullet.transform.position = Vector3.New(x,y,0)\n\tend\nend运行游戏,点击鼠标左键,角色发射炮弹。另外也可以用基于组件的方法实现,这里就不展开了。最后依然到了广告时间:笔者即将出版的一本Unity3D实战类书籍《Unity3D网络游戏实战》。该书通过一个完整的多人坦克对战实例,详细介绍网络游戏开发过程中涉及到的知识和技巧。书中还介绍了服务端框架、客户端网络模块、UI系统的架构等内容。相信透过本书,读者能够掌握Unity3D网络游戏开发的大部分知识,也能够从框架设计中了解商业游戏的设计思路,感谢大家支持。","updated":"T14:46:58.000Z","canComment":false,"commentPermission":"anyone","commentCount":2,"likeCount":2,"state":"published","isLiked":false,"slug":"","isTitleImageFullScreen":false,"rating":"none","sourceUrl":"","publishedTime":"T22:46:58+08:00","links":{"comments":"/api/posts//comments"},"url":"/p/","titleImage":"","summary":"","href":"/api/posts/","meta":{"previous":null,"next":null},"snapshotUrl":"","commentsCount":2,"likesCount":2},"":{"title":"Unity特效(1)
梦幻旋屏","author":"pyluo","content":"游戏开发中,往往会用到一些屏幕特效。下图展现的是一种“旋屏”效果,它会旋转屏幕图像,且距离中心点越远的点旋转角度越大。这种效果特别适合营造“梦幻”感,比如,在RPG游戏中,经过一段“旋屏”特效,主角穿越到了10年前。1、编写Shader下面的着色器代码使用了顶点/片元着色器处理旋屏特效的功能。这里定义3个属性,其中_MainTex代表屏幕贴图,_Rot 代表基准的旋转角度。核心代码在片元着色器frag中实现。如下图所示,屏幕图像被归一到[0,1]的空间中,中心点为(0.5,0.5)。假设某个点的uv坐标为(x,y),经过一系列处理,它的坐标变为(x1,y1),而(x1,y1)便是实现旋转效果后的uv坐标。由“float distance = sqrt((i.uv.x - 0.5)*(i.uv.x - 0.5) +(i.uv.y - 0.5)*(i.uv.y - 0.5));”可以计算点到屏幕中心的距离distance。由于距离越远旋转角度越大,使用“_Rot *=distance”将角度增量基准与距离联系起来,即可获取需要旋转的角度:angle = _Rot*distance + A。由反正切公式可得∠A = atan((y - 0.5)/(x - 0.5)),由于atan的取值为[-π/2,π/2],还需根据y值确定∠A所在的象限,故而∠A = step(x,0.5)*PI+ atan((y - 0.5)/(x - 0.5)) 。计算∠A 后,便可由angle = _Rot*distance + A计算总的旋转角度。前面已经计算了点到屏幕中心的距离distance,故而:x1 = 0.5 + distance *cos(angle)y1 = 0.5 + distance *sin(angle)Shader代码如下所示:Shader \"Lpy/ScreenRot\"\n{\n
Properties\n
_MainTex (\"Main Tex\", 2D) = \"white\" {}\n
_Rot (\"Rotation\", float) = 0\n
SubShader\n
Tags {\"Queue\"=\"Geometry\"}\n
Tags { \"LightMode\"=\"ForwardBase\" }\n
ZWrite Off\n\n
CGPROGRAM\n
#pragma vertex vert
#pragma fragment frag\n
#include \"UnityCG.cginc\"\n
#define PI 3.79
sampler2D _MainT\n
float _R\n
struct a2v\n
float4 vertex : POSITION;\n
float3 texcoord : TEXCOORD0;\n
struct v2f\n
float4 pos : SV_POSITION;\n
float2 uv : TEXCOORD0;\n
v2f vert (a2v v)\n
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.\n
fixed4 frag (v2f i) : SV_Target\n
//与中心点(0.5,0.5)的距离\n
float distance = sqrt((i.uv.x - 0.5)*(i.uv.x - 0.5) +(i.uv.y - 0.5)*(i.uv.y - 0.5));\n
//距离越大,旋转角度越大\n
//计算旋转角度\n
float angle = step(i.uv.x,0.5)*PI+ atan((i.uv.y - 0.5)/(i.uv.x - 0.5)) + _R\n
//计算坐标\n
i.uv.x = 0.5 +
distance *cos(angle);\n
i.uv.y = 0.5 +
distance *sin(angle);\n \n
fixed4 c = tex2D(_MainTex, i.uv);\n
FallBack \"Specular\"\n}2、使用材质新建c#文件,编写ScreenRot类,它由一个共有变量mtl,在它的OnRenderImage方法中调用Graphics.Blit将屏幕图像(对应shader中的_MainTex)与材质混合起来。using UnityE\nusing System.C\n \npublic class ScreenRot : MonoBehaviour \n{\n
public M\n \n
void OnRenderImage(RenderTexture src, RenderTexture dest)\n
Graphics.Blit (src, dest,mtl);\n
}\n}然后给新建一个名为ScreenRot的材质,使用上述编写的Shader。然后给摄像机添加ScreenRot组件,设置刚刚创建的材质,如下图所示。运行游戏,调整材质的“Rotation”属性,即可看到旋转特效。3、代码中引用Shader中并没有涉及时间的控制,旋转速度需要由c#代码控制,将ScreenRot修改成下面的代码,即可让屏幕自动旋转。using UnityE\nusing System.C\n \npublic class ScreenRot : MonoBehaviour \n{\n
public M\n \n\n
// Update is called once per frame\n
void Update ()\n
rot += 0.1f;\n
void OnRenderImage(RenderTexture src, RenderTexture dest)\n
if (rot == 0.0)\\n
mtl.SetFloat(\"_Rot\", rot);\n
Graphics.Blit (src, dest,mtl);\n
}\n}这个效果能够运用在很多场合,比如使用“正向旋转→切换场景→反向旋转”实现切屏特效。最后依然到了广告时间:笔者出版了一本Unity3D实战类书籍《Unity3D网络游戏实战》。该书通过一个完整的多人坦克对战实例,详细介绍网络游戏开发过程中涉及到的知识和技巧。书中还介绍了服务端框架、客户端网络模块、UI系统的架构等内容。相信透过本书,读者能够掌握Unity3D网络游戏开发的大部分知识,也能够从框架设计中了解商业游戏的设计思路,感谢大家支持。Unity3D热更新框架教程:","updated":"T12:56:18.000Z","canComment":false,"commentPermission":"anyone","commentCount":23,"likeCount":60,"state":"published","isLiked":false,"slug":"","isTitleImageFullScreen":false,"rating":"none","sourceUrl":"","publishedTime":"T20:56:18+08:00","links":{"comments":"/api/posts//comments"},"url":"/p/","titleImage":"/v2-48ec859e357a2d46ef253a36ed7849df_r.png","summary":"","href":"/api/posts/","meta":{"previous":null,"next":null},"snapshotUrl":"","commentsCount":23,"likesCount":60},"":{"title":"Unity特效(2) 图片切割","author":"pyluo","content":"游戏开发中,常常把多张图片素材合并成图集,然后程序读取所需的部分显示。如下展示的是一张人物行走图,程序需要把它切分成12张小图加以显示。一种做法是使用Sprite Editor切分图集,当做多张小图来处理。如果Image能够只显示图集的一部分,程序还是把图集当做1张图片处理,可以减少不小工作量。本文提供一种使用shader实现上述功能的例子。1、编写Shader下面的着色器代码使用了顶点/片元着色器处理图片切割功能。这里定义5个属性,其中_MainTex代表图片贴图,_ColCount和_RowCount代表图片的列数和行数,_ColIndex和_RowIndex表示要显示哪一行哪一列的图片。核心代码是“o.uv.x = (_ColIndex + v.texcoord.x)/_ColCount”和“o.uv.y = (_RowIndex + v.texcoord.y)/_RowCount”,它们实现了UV坐标的变换。“o.uv.x = (_ColIndex + v.texcoord.x)/_ColCount”即是“o.uv.x = _ColIndex*(1/_ColCount) + v.texcoord.x *(1/_ColCount)”的化简式,由于纹理坐标被归一化到[0,1]的范围,1/_ColCount即表示每一张小图的宽度。Shader \"Lpy/ImageClip\" \n{\n\tProperties \n\t{\n\t\t_MainTex (\"Main Tex\", 2D) = \"white\" {}\n
\t_ColCount (\"Column Count\", int) = 4\n
\t_RowCount (\"Row Count\", int) = 4\n
\t_ColIndex (\"Col Index\", int) = 0\n
\t_RowIndex (\"Row Index\", int) = 0\n\t}\n\t\n\tSubShader \n\t{\n\t\tTags {\"Queue\"=\"Transparent\" \"IgnoreProjector\"=\"True\" \"RenderType\"=\"Transparent\"}\n\t\t\n\t\tPass \n\t\t{\n\t\t\tTags { \"LightMode\"=\"ForwardBase\" }\n\t\t\tZTest off\n\t\t\tZWrite Off\n\t\t\tBlend SrcAlpha OneMinusSrcAlpha\n\t\t\t\n\t\t\tCGPROGRAM\n\t\t\t#pragma vertex vert
\n\t\t\t#pragma fragment frag\n\t\t\t#include \"UnityCG.cginc\"\n\t\t\n\t\t\tsampler2D _MainT\n\t\t\tint _ColC\n\t\t\tint _RowC\n\t\t\tint _ColI\n\t\t\tint _RowI\n\t\t\t
\n\t\t\tstruct a2v \n\t\t\t{
float4 vertex : POSITION; \n\t\t\t
float2 texcoord : TEXCOORD0;\n\t\t\t};
\n\t\t\t\n\t\t\tstruct v2f \n\t\t\t{
float4 pos : SV_POSITION;\n\t\t\t
float2 uv : TEXCOORD0;\n\t\t\t};
\n\t\t\t\n\t\t\tv2f vert (a2v v) \n\t\t\t{
\n\t\t\t\tv2
\n\t\t\t\to.pos = mul(UNITY_MATRIX_MVP, v.vertex);
\n\t\t\t\t\n\t\t\t\to.uv.x = (_ColIndex + v.texcoord.x)/_ColC\n\t\t\t\to.uv.y = (_RowIndex + v.texcoord.y)/_RowC\n\t\t\t\\n\t\t\t}
\n\t\t\t\n\t\t\tfixed4 frag (v2f i) : SV_Target \n\t\t\t{\n\t\t\t\tfixed4 c = tex2D(_MainTex, i.uv);\n\t\t\t\\n\t\t\t}\n\t\t\tENDCG\n\t\t}
\n\t}\n\tFallBack \"Transparent/VertexLit\"\n}2、使用材质新建一个名为ImageClip的材质,选择上述编写的shader。将ColumnCount和RowCount设置为图集的列数和行数,如下图所示。将刚创建的材质应用于图片上,如下图所示。在Scene或Game视图中观察图片,改变材质的ColIndex和RowIndex属性,即可显示不同的小图。如下图所示。3、代码控制如下代码展示使用脚本控制材质属性的方法,当按下空格键时,改变材质的RowIndex属性,展现不同小图。using UnityE\nusing System.C\nusing UnityEngine.UI;\n\npublic class RoleCtrl : MonoBehaviour \n{\n\tpublic I\n\tprivate M\n\t// Use this for initialization\n\tvoid Start () \n\t{\n\t\tmtl = image.\n\t}\n\t\n\t// Update is called once per frame\n\tvoid Update () \n\t{\n\t\tif (Input.GetKeyDown (KeyCode.Space)) \n\t\t{\n\t\t\tint row = mtl.GetInt(\"_RowIndex\");\n\n\t\t\trow++;\n\t\t\tif(row &= 4) \n\t\t\t\trow = 0;\n\n\t\t\tmtl.SetInt(\"_RowIndex\", row);\n\t\t}\n\t}\n}\n最后依然到了广告时间:笔者即将出版的一本Unity3D实战类书籍《Unity3D网络游戏实战》。该书通过一个完整的多人坦克对战实例,详细介绍网络游戏开发过程中涉及到的知识和技巧。书中还介绍了服务端框架、客户端网络模块、UI系统的架构等内容。相信透过本书,读者能够掌握Unity3D网络游戏开发的大部分知识,也能够从框架设计中了解商业游戏的设计思路,感谢大家支持。","updated":"T14:14:02.000Z","canComment":false,"commentPermission":"anyone","commentCount":2,"likeCount":2,"state":"published","isLiked":false,"slug":"","isTitleImageFullScreen":false,"rating":"none","sourceUrl":"","publishedTime":"T22:14:02+08:00","links":{"comments":"/api/posts//comments"},"url":"/p/","titleImage":"","summary":"","href":"/api/posts/","meta":{"previous":null,"next":null},"snapshotUrl":"","commentsCount":2,"likesCount":2},"":{"title":"Unity特效(3) 图片翻转","author":"pyluo","content":"2D游戏开发中,往往会使用翻转的图片(如下图),然而Transform的旋转并不能完成这一功能,那么什么办法将图片翻转呢?1、编写Shader下面的着色器代码使用了顶点/片元着色器处理图片翻转功能。这里定义3个属性,其中_MainTex代表图片贴图,_Hor代表是否启用水平翻转,_Ver代表是否启用垂直翻转。核心代码是“o.uv.x = (1-v.texcoord.x)*_Hor + v.texcoord.x*(1-_Hor)”和“o.uv.y = (1-v.texcoord.y)*_Ver + v.texcoord.y*(1-_Ver);”它们实现了UV坐标的变换。在o.uv.x的计算式中,如果_Hor为0,那么“o.uv.x =
v.texcoord.x”即为原始UV,如果_Hor为1,由于纹理坐标被归一化到[0,1]的范围,那么“o.uv.x = 1-v.texcoord.x”即为翻转后的UV。Shader \"Lpy/ImageFlip\" \n{\n\tProperties \n\t{\n\t\t_MainTex (\"Main Tex\", 2D) = \"white\" {}\n\t\t\n
\t_Hor (\"Is Horizontal Filp\", Range (0, 1)) = 0\n
\t_Ver (\"Is Vertical Filp\", Range (0, 1)) = 0\n\t}\n\t\n\tSubShader \n\t{\n\t\tTags {\"Queue\"=\"Transparent\" \"IgnoreProjector\"=\"True\" \"RenderType\"=\"Transparent\"}\n\t\t\n\t\tPass \n\t\t{\n\t\t\tTags { \"LightMode\"=\"ForwardBase\" }\n\t\t\tZTest off\n\t\t\tZWrite Off\n\t\t\tBlend SrcAlpha OneMinusSrcAlpha\n\t\t\t\n\t\t\tCGPROGRAM\n\t\t\t#pragma vertex vert
\n\t\t\t#pragma fragment frag\n\t\t\t#include \"UnityCG.cginc\"\n\t\t\n\t\t\tsampler2D _MainT\n\t\t\tint _H\n\t\t\tint _V\n\t\t\t
\n\t\t\tstruct a2v \n\t\t\t{
float4 vertex : POSITION; \n\t\t\t
float2 texcoord : TEXCOORD0;\n\t\t\t};
\n\t\t\t\n\t\t\tstruct v2f \n\t\t\t{
float4 pos : SV_POSITION;\n\t\t\t
float2 uv : TEXCOORD0;\n\t\t\t};
\n\t\t\t\n\t\t\tv2f vert (a2v v) \n\t\t\t{
\n\t\t\t\tv2
\n\t\t\t\to.pos = mul(UNITY_MATRIX_MVP, v.vertex);
\n\t\t\t\t\n\t\t\t\to.uv.x = (1-v.texcoord.x)*_Hor + v.texcoord.x*(1-_Hor);\n\t\t\t\to.uv.y = (1-v.texcoord.y)*_Ver + v.texcoord.y*(1-_Ver);\n\t\t\t\\n\t\t\t}
\n\t\t\t\n\t\t\tfixed4 frag (v2f i) : SV_Target \n\t\t\t{\n\t\t\t\tfixed4 c = tex2D(_MainTex, i.uv);\n\t\t\t\\n\t\t\t}\n\t\t\tENDCG\n\t\t}
\n\t}\n\tFallBack \"Transparent/VertexLit\"\n}2、使用材质新建一个名为ImageFilp的材质,选择上述编写的shader,设置Is Horizontal Filp和Is Vertical Filp,如下图所示。将刚创建的材质应用于图片上,即可看到翻转的效果。如下图所示。最后依然到了广告时间:笔者出版的一本Unity3D实战类书籍《Unity3D网络游戏实战》。该书通过一个完整的多人坦克对战实例,详细介绍网络游戏开发过程中涉及到的知识和技巧。书中还介绍了服务端框架、客户端网络模块、UI系统的架构等内容。相信透过本书,读者能够掌握Unity3D网络游戏开发的大部分知识,也能够从框架设计中了解商业游戏的设计思路,感谢大家支持。","updated":"T12:53:35.000Z","canComment":false,"commentPermission":"anyone","commentCount":12,"likeCount":10,"state":"published","isLiked":false,"slug":"","isTitleImageFullScreen":false,"rating":"none","sourceUrl":"","publishedTime":"T20:53:35+08:00","links":{"comments":"/api/posts//comments"},"url":"/p/","titleImage":"/v2-f5ab4e30fe05aeb15bfdab172d2d5f09_r.png","summary":"","href":"/api/posts/","meta":{"previous":null,"next":null},"snapshotUrl":"","commentsCount":12,"likesCount":10},"":{"title":"Unity特效(4) 标题光效","author":"pyluo","content":"标题光效是一种常见的图片特效,“遮罩层”从左往右经过,起到强调游戏标题的作用,如下图所示。那么怎样用Shader实现这种效果呢?1、编写Shader
下面的着色器代码使用了顶点/片元着色器处理标题光效功能。这里定义4个属性,其中_MainTex代表图片贴图,_MaskColor代表遮罩颜色,Speed代表光效的移动速度,_MaskLimit控制着光效的宽度。核心代码为“float isMask = sin(_Time.y*_Speed
-i.uv.x*2*PI );”“isMask = step(_MaskLimit,isMask);”“c.rgb += _MaskColor*isM”这3句。如果isMask为1,代表该片元被遮罩,如果为0,表示不被遮罩,通过“c.rgb += _MaskColor*isM”便可计算片元的颜色。“float isMask = sin(_Time.y*_Speed -i.uv.x*2*PI );”将根据时间和uv的x坐标计算isMask,此时isMask的取值范围为[-1,1]。step(_MaskLimit,isMask)的功能相当于“if(isMask & MaskLimit) return 1; else return 0;”通过_MaskLimit将指定区间的值设为1,其他设为0。Shader \"Lpy/ImageEffect\" \n{\n\tProperties \n\t{\n\t\t_MainTex (\"Main Tex\", 2D) = \"white\" {}\n\t\t_MaskColor (\"Mask Color\", Color) = (1, 1, 1, 1)\n\t\t\n\t\t_Speed (\"Speed\", float) = 2\n\t\t_MaskLimit (\"MaskLimit\", float) = 0.8\n\t}\n\t\n\tSubShader \n\t{\n\t\tTags {\"Queue\"=\"Transparent\" \"IgnoreProjector\"=\"True\" \"RenderType\"=\"Transparent\"}\n\t\t\n\t\tPass \n\t\t{\n\t\t\tTags { \"LightMode\"=\"ForwardBase\" }\n\t\t\tZTest off\n\t\t\tZWrite Off\n\t\t\tBlend SrcAlpha OneMinusSrcAlpha\n\t\t\t\n\t\t\tCGPROGRAM\n\t\t\t#pragma vertex vert
\n\t\t\t#pragma fragment frag\n\t\t\t#include \"UnityCG.cginc\"\n\t\t\t#define PI 3.79
\n\t\t\t\n\t\t\t\n\t\t\tsampler2D _MainT\n\t\t\tfixed3 _MaskC\n\t\t\tfloat _S\n\t\t\tfloat _MaskL\n\t\t\t\n\t\t\tstruct a2v \n\t\t\t{
float4 vertex : POSITION; \n\t\t\t
float3 texcoord : TEXCOORD0;\n\t\t\t};
\n\t\t\t\n\t\t\tstruct v2f \n\t\t\t{
float4 pos : SV_POSITION;\n\t\t\t
float2 uv : TEXCOORD0;\n\t\t\t};
\n\t\t\t\n\t\t\tv2f vert (a2v v) \n\t\t\t{
\n\t\t\t\tv2
\n\t\t\t\to.pos = mul(UNITY_MATRIX_MVP, v.vertex);
\n\t\t\t\to.uv = v.\n\t\t\t\\n\t\t\t}
\n\t\t\t\n\t\t\tfixed4 frag (v2f i) : SV_Target \n\t\t\t{\n\t\t\t\tfixed4 c = tex2D(_MainTex, i.uv);\n\t\t\t\t\n\t\t\t\tfloat isMask = sin(_Time.y*_Speed
-i.uv.x*2*PI );\n\t\t\t\tisMask = step(_MaskLimit,isMask);\n\t\t\t\t\n\t\t\t\tc.rgb += _MaskColor*isM\n\t\t\t\\n\t\t\t}\n\t\t\tENDCG\n\t\t}
\n\t}\n\tFallBack \"Transparent/VertexLit\"\n}2、使用材质新建一个名为ImageEffect的材质,选择上述编写的shader。设置MaskColor、Speed、MaskLimit这3个参数,如下图所示。将刚创建的材质应用于图片上,即可看到效果。如下图所示。最后依然到了广告时间:最后依然到了广告时间:笔者在分享文章的同时也结识了一群兴趣相投的朋友,本周五将会作客游戏蛮牛,在线答疑。","updated":"T12:40:39.000Z","canComment":false,"commentPermission":"anyone","commentCount":0,"likeCount":1,"state":"published","isLiked":false,"slug":"","isTitleImageFullScreen":false,"rating":"none","sourceUrl":"","publishedTime":"T20:40:39+08:00","links":{"comments":"/api/posts//comments"},"url":"/p/","titleImage":"/v2-cdbdff03fa_r.png","summary":"","href":"/api/posts/","meta":{"previous":null,"next":null},"snapshotUrl":"","commentsCount":0,"likesCount":1},"":{"title":"Unity特效(5) 滚动的背景","author":"pyluo","content":"制作动画时,往往会使用到“滚动的背景”。如下图所示,一开始图片只显示素材的一部分,然后素材不断滚动。该效果可以模拟横版或飞行游戏的背景图,或实现一些动画效果。尽管有很多方法实现该功能,这里提供一种基于shader的方法。1、编写Shader下面的着色器代码实现了“滚动的背景”功能。这里定义了3个变量,其中_MainTex代表背景贴图,_Width代表显示背景的百分比,_Distance代表当前滚动的距离。核心代码为“i.uv.x = frac(i.uv.x*_Width + _Distance);”,其中frac是取小数的函数,如1.23 取出来是 0.23,其功能是将i.uv.x 控制在0到1的范围,进而显示出来。Shader \"Lpy/ImageRoll\" \n{\n\tProperties \n\t{\n\t\t_MainTex (\"Main Tex\", 2D) = \"white\" {}\n\t\t\n
\t_Width (\"Width\", float) = 0.5\n
\t_Distance (\"Distance\", float) = 0\n\t}\n\t\n\tSubShader \n\t{\n\t\tTags {\"Queue\"=\"Transparent\" \"IgnoreProjector\"=\"True\" \"RenderType\"=\"Transparent\"}\n\t\t\n\t\tPass \n\t\t{\n\t\t\tTags { \"LightMode\"=\"ForwardBase\" }\n\t\t\tZTest off\n\t\t\tZWrite Off\n\t\t\tBlend SrcAlpha OneMinusSrcAlpha\n\t\t\t\n\t\t\tCGPROGRAM\n\t\t\t#pragma vertex vert
\n\t\t\t#pragma fragment frag\n\t\t\t#include \"UnityCG.cginc\"\n\t\t\n\t\t\tsampler2D _MainT\n\t\t\tfloat _W\n\t\t\tfloat _D\n\t\t\t
\n\t\t\tstruct a2v \n\t\t\t{
float4 vertex : POSITION; \n\t\t\t
float2 texcoord : TEXCOORD0;\n\t\t\t};
\n\t\t\t\n\t\t\tstruct v2f \n\t\t\t{
float4 pos : SV_POSITION;\n\t\t\t
float2 uv : TEXCOORD0;\n\t\t\t};
\n\t\t\t\n\t\t\tv2f vert (a2v v) \n\t\t\t{
\n\t\t\t\tv2
\n\t\t\t\to.pos = mul(UNITY_MATRIX_MVP, v.vertex);
\n\t\t\t\t\n\t\t\t\to.uv.x = v.texcoord.x;\n\t\t\t\to.uv.y = v.texcoord.y;\n\t\t\t\\n\t\t\t}
\n\t\t\t\n\t\t\tfixed4 frag (v2f i) : SV_Target \n\t\t\t{\n\t\t\t\ti.uv.x = frac(i.uv.x*_Width + _Distance);\n\t\t\t\tfixed4 c = tex2D(_MainTex, i.uv);\n\t\t\t\\n\t\t\t}\n\t\t\tENDCG\n\t\t}
\n\t}\n\tFallBack \"Transparent/VertexLit\"\n}2、使用材质新建一个名为ImageRoll的材质,选择上述编写的shader。设置WIdth和Distance两个参数,如下图所示。只要调整Distance的值,即可看到滚动到不同位置的背景。3、代码中引用可以在代码中逐步增加distance 的值,造成背景不断向前滚动的效果。也可以在shader中使用Time相关的方法,实现同样的功能。using UnityE\nusing System.C\nusing UnityEngine.UI;\n\npublic class ImageRoll : MonoBehaviour {\n\n\tpublic I\n\tprivate M\n\\n\t// Use this for initialization\n\tvoid Start () {\n\t\tmtl = image.\n\t}\n\t\n\t// Update is called once per frame\n\tvoid Update () {\n\t\tdistance += 0.005f;\n\t\tmtl.SetFloat(\"_Distance\", distance);\n\t}\n}将刚创建的材质应用于图片上,即可看到效果。如下图所示。最后依然到了广告时间:笔者出版的一本Unity3D实战类书籍《Unity3D网络游戏实战》。该书通过一个完整的多人坦克对战实例,详细介绍网络游戏开发过程中涉及到的知识和技巧。书中还介绍了服务端框架、客户端网络模块、UI系统的架构等内容。相信透过本书,读者能够掌握Unity3D网络游戏开发的大部分知识,也能够从框架设计中了解商业游戏的设计思路,感谢大家支持。","updated":"T15:18:50.000Z","canComment":false,"commentPermission":"anyone","commentCount":8,"likeCount":25,"state":"published","isLiked":false,"slug":"","isTitleImageFullScreen":false,"rating":"none","sourceUrl":"","publishedTime":"T23:18:50+08:00","links":{"comments":"/api/posts//comments"},"url":"/p/","titleImage":"/v2-d9e7d6d51fa27f75b0e48436dee51b06_r.png","summary":"","href":"/api/posts/","meta":{"previous":null,"next":null},"snapshotUrl":"","commentsCount":8,"likesCount":25},"":{"title":"作为程序员,如何规划成长路线","author":"pyluo","content":"前不久参与了游戏蛮牛的专家在线活动,几十位网友提出了他们的问题,其中有些问题较为普遍,于是决定把这些问题整理出来,加以完善,希望能够帮助到更多人。
原贴地址:作为程序员,如何规划职业成长路线 问:
接触游戏开发也有一年多,感觉这一年基本上没学到什么东西。在机构学习的时候就常常听老师们说,框架,面向对象等,可是到做项目的时候基本都没用到过,每次做项目就是实现功能。到最后代码就乱得一塌糊涂。因为没有一套明确的方向,也不知道该去学那些。答:其中的一条可能的学习途径可以参考下面的三个阶段。第一年:能够做功能。尽量接触公司内比较完善的游戏项目,在参与编写逻辑的同时,不断完善自己的编码水平。同时了解该项目的设计思想、用到了哪些技术、用到了哪些第三方库,这些第三方库又是怎样使用的。第二年:用得起现有框架。经过第一年的学习,应该对游戏项目很熟悉了。此时要熟悉这一套框架的各部分功能,假如要让你做一款新的游戏,你能够在这套框架的基础上把游戏做出来。同时也多了解几套可用的框架,积累自己能够“撬得动”的代码资源。第三年:深入底层。深入底层了解这些源码,能够修改它们,或者能够重新写一个。同时再次学习计算机原理、图形学、数据库实现等课程,能够有不同的体会客户端、服务端具体的方向可能会有所不同,但大致的路径都是一样的。就是先能够使用、再能够使用某个现有资源重新搭建,再到深入底层。经过这3个阶段,相信便能够独当一面了。想做游戏服务端,应该学些什么 年少不懂事
如果走服务器需要会那些技能答:其实看看各大公司招聘基本就能够知道所需的技能了,大概有如下几点1、熟悉掌握一门主要编程语言,如c++或java2、熟悉掌握某一门脚本语言,如lua或python3、了解网络编程知识,了解TCP/UDP/IP协议的使用和实现细节。了解并能够使用一些现成的网络库,如libevent4、熟悉linux操作系统,能够操作它,并知道它的实现原理5、熟悉数据库,及其实现原理目前服务端相关的资料很少,推荐书籍的话首先当然是推荐我自己的书《Unity3D网络游戏实战》啦,里面第6和7章有实现一套简易的服务端程序,还有下面几本书对能够让读者对服务端有更好的了解《Linux多线程服务端编程 使用muduo C++网络库》
陈硕《Redis源码涉及与实现(第二版)》
黄健宏《TCP/IP详解:卷一》
看前面几章就行C++是必学的吗?能不能只用c# 问:做游戏开发,lua和c++是必学的吗,我只用c#,unity不可以吗答:游戏开发分前端和后端,使用到不同的技术。如果是后端,c++和lua是其中一种方案,如果是unity前端,c#是必须的,lua也有可能涉及,c++几乎不会用到。然而一些面试中,会以c++水平去代表编程水平,一般c++水平高的话,c#、lua都不在话下。但无论如何,数据结构和算法肯定是要很扎实的。大

我要回帖

更多关于 ora name list t 的文章

 

随机推荐