KSFramework

新公司,新项目,新的框架,这又将是一个挑战。

现在我们项目用的是 KSFramework+Xlua  ,KSFramework是一个Unity 5 Asset Bundle开发框架和工具集,专注于运行时热重载,本身是使用了SLua作为脚本引擎。但是我们项目中给它改成了XLua作为脚本引擎,因为xlua效率方面比slua似乎更高一点,而且在KSframework中把slua换成xlua也是非常方便的,即使换成ulua也是可以的。

        但是最近发现的是 在xlua里面只要出错 在手机端里游戏就会出现闪退,打出的PC包也是直接崩溃掉,之前用tolua的时候一般lua代码报错是不会导致游戏直接崩溃的。

这一点确实很蛋疼。

在lua中实现类的思想

在lua中是没有类的概念的但是可以使用table和元表来实现类和类的继承,多继承。

代码如下:

--clsname 类名
-- ... 父类或者是被继承的类

function class(clsname, ...) --此函数用来返回一个类(其实就是一个表)
	local cls = {}
	local args = {...}  --保存父类列表
	if #args > 0 then
		setmetatable(cls, {__index=    --设置此类的元表并把元表的 __index 设为一个函数,此函数用来查找父类中的变量或函数
		function(_, k)
			for i, v in ipairs(args) do
				if v[k] ~= nil then
					return v[k]
				end
			end
		end})
	end
	cls.classname = clsname   --设置好类名
	cls.New = function()   --定义一个实例化对象的函数(其实就是创建一个新表)
		local instance = {}
		setmetatable(instance, {__index=cls}) --设置此对象的元表的__index方法 为这个类。
		instance.classtype = cls  -- 设置此类的类型(其实就是这个元表)
		return instance
	end
	return cls   --最好返回这个类(这个表)
end

使用方法如下:
local CParent = class("CParent") --定义一个父类

local CSon = class("CSon", CParent) --定义子类继承自父类

local son = CSon.New()  --创建一个孩子对象,此对象具有父类的所有属性

protobuf协议字段优化后的处理

特色

我们的游戏项目中使用了protobuf协议,但是由于协议优化了的缘故,当服务器发送字段值等于0 的时候,客户端协议解析出来后看不到这个值为0零的字段,但是如果直接用“.“去访问是可以访问的。于是问题就来了当我们数据需要更新的时候,如果在lua中 直接 for in pairs 去获得解析到的数据的键值,如果这个键的值是0 的话那么那么将会获取不到,也就是说这个时候无法判断这个字段是否是 0 ,还是根本服务器就没有发送过来。

于是我们就使用看 按位与的方式去检测和判断 服务器是否发了这个字段,对此,如果是需要更新的协议服务器都要多发一个mask 字段 这是一个 二进制转换后的十进制数,协议字段按照顺序,如果发了,,则这个位置为1,否则为0,如协议:

//申请入帮
message C2GSApplyJoinOrg {
    optional uint32 orgid = 1;
    optional uint32 flag = 2;
}

如果服务器发送了 orgid = 10 , flag = 0 过来,那么客户端只能看到 orgid = 10 这个字段,而看不到 flag 这个。

所以在客户端中我们就做了如下处理:

--dOri 是服务发送过来经过客户端解析后的数据
function CNetCtrl.DecodeMaskData(self, dOri)
	local d = {}
	local lKey = {"orgid","flag"}
	if lKey then
		local iMask = dOri.mask --服务发送时带一个二进制的mask字段
		if iMask then
			local right = 1
			for i=1, #lKey do
				right = right * 2
				if MathBit.andOp(iMask, right) ~= 0 then
					local key = lKey[i]
					d[key] = dOri[key]
				end
			end
			-- table.print(d, "CNetCtrl解析mask: "..sType)
			return d
		end
	end
	return dOri
end


function MathBit.__base(left, right, op)  
    if left < right then  
        left, right = right, left  
    end  
    local res = 0  
    local shift = 1  
    while left ~= 0 do  
        local ra = left % 2  
        local rb = right % 2  
        res = shift * op(ra,rb) + res
        shift = shift * 2
        left = math.modf( left / 2) --相当于右移一位
        right = math.modf( right / 2)
    end  
    return res
end
  
function MathBit.andOp(left, right)  
    return MathBit.__base(left, right, MathBit.__andBit)
end  

function MathBit.__andBit(left,right)  --按位相与
    return (left == 1 and right == 1) and 1 or 0      
end

这样处理完成之后的数据 客户端就是可以打印出来查看了。并且可以直接使用for in pairs 去打印他们为0的值。

2D场景坐骑飞行效果

        最近在公司做一个坐骑系统里面有一个飞行的功能,一开始觉得应该是提高他的Y轴,后来发现效果并不明显,于是觉得如果要在2D场景中实现纵深的效果,还是要有大小比例的变化才能看上去 有高度。

于是把飞行的模型放大到 1.2 倍,发现效果还不是很明显,于是又把今天拉伸了 一点 效果才明显一点了,发现其他2D游戏的飞行中还带有云层效果看上去 效果就好多了。而且这个跟你坐的坐骑也有很大的关系

如果本身不是飞行的东西 你坐上去感觉就很怪异了。

       所以总结得出要在2D场景中实现坐骑的飞行效果 1.放大模型  2.拉伸镜头  3.加入云彩特效或其他  

NGUI动态锚点设置

最近项目上需要动态去设置控件的锚点,之前很少会碰到这样的问题,一般都是直接在预制体上设置好的,于是用VS查看了一下API 发现了UIWidget 上都是自带锚点设置的,但是我试用了 

        widget.bottomAnchor 

        widget.leftAnchor 

        widget.rightAnchor 

        widget.topAnchor 

这几个属性发现都不行 设置完成后位置还是会乱掉,然后我又试用了widget.SetAnchor()这个函数这个函数有3个重载函数,是用来设置锚点的目标对象的,于是我用widget.SetAnchor(obj,left,bottom,right,top)

试了一下,设置锚点对象和 偏移位置,发现即使是这样设置也和面板上的设置不同,面板上得设置是可以设置锚点的相对偏移位置的,而这个函数只是设置了偏移位置,没有对锚点的相对位置做设置也就是说没有如下图设置的 相对偏移目标

QQ截图20170314231151.png

继续在VS上查找NGUI的锚点API发现 ,4个方向下的锚点属性还有一个绝对位置和 相对位置可以设置

 widget.rightAnchor.absolute,widget.rightAnchor.relative

我猜想这个可能就是设置相对位置的属性但是怎么设置呢?

这是一个问题,我把这个API贴到百度上果然很多人也遇到了这样的问题仔细研究下发现还挺麻烦的,相对的距离的设置 如果为1表示是 面板设置的 bottom 或者left ,为0.5表示center,为0表示top或者right,而绝对距离则是面板上填写的数值于是,用代码设置锚点就很简单了.

  1. 首先用widget.SetAnchor()这个函数设置好锚点目标和绝对距离

  2. 然后用 widget.rightAnchor.absolute,widget.rightAnchor.relative设置好相对距离和绝对距离(这里需要再设置一遍和SetAnchor函数设置的一样)

由于我们的项目逻辑层全是lua于是我只好把这些用到的函数封装了一遍。

如下图

QQ截图20170314232702.png

希望各位同行少入点坑。。。

UGUI Text 逐字出现

   在做剧情系统的时候,有个剧情模式叫立绘对话,也就是那种你说一句我说一句的那种,策划的要求是希望对话是逐字出现的,而且文字都是在表格里面配置的还需要支持字体颜色的配置.显示一句完成后自动显示下一句.

   读取表格显示字体是很简单的.但是要逐字出现,由于之前没做过,首先想到的是,字体应该都是已经全部放上去了,然后在Text的上面放上一层遮罩.没隔一段时间把上面这层遮罩缩短一个字.这样就可以实现逐字出现的样式了.但是后面发现不行,因为每个字的大小和宽度是不一样的,而且还会有标点符号的区分.

  于是只能是使用那种每次往Text组件上加上一个字,但是这样做的问题又出现了,因为是每次加上一个字的,也就是说事先定义的颜色标记是没有用的他会被逐字打印在Text上了,比如 <color>标记他会首先打印 "<" 这个符合然后陆续把<color>整个显示出来 而不会像直接显示文字那样把<color>标记里面的文字按照定义的颜色显示出来.

  为了实现这个功能我只好用个正则表达式,把<color></color>整个内容先截取出来然后再赋值颜色值整体显示出来,也就是说,只要遇到有颜色标记的字就截取出来然后整体显示出来,这样就可以实现所需要的功能了.

部分代码如下:

//截出带颜色部分字体
   Regex reg = new Regex("(?<=<color).*?(?=</color>)", RegexOptions.None);
   MatchCollection matchs = reg.Matches(dialogDeploy_.text);
   //Debug.Log(matchs[0].Index-6+"-----"+ matchs[0].Length+6+8);
   int k = 0;
   ch = dialogDeploy_.text.ToCharArray();//转换成数组
   for (int j = 0; j < ch.Length; ++j)
    {

     yield return new WaitForSeconds(0.1f);
     if (isNext)
     {
      break;
     }
      if (ch[j] == '<' && matchs[k].Length + 6 + 8 
      < dialogDeploy_.text.Length)
     {
       //匹配出带颜色的字体
       showText_.text += 
       dialogDeploy_.text.Substring(j, matchs[k].Length + 6 + 8);
       j += matchs[k++].Length + 6 + 8;
     }
     else
     {
       showText_.text += ch[j];//显示每个字
     }
               
            }
            yield return new WaitForSeconds(0.5f);
            showText_.text = "\n";
            ch = null;
            ++i;
 }

U3D动态建模

做项目时美术居然要把英雄的属性值设置成雷达图的样式,看起来直观一点,然而他们只给了我一张图片……….

我当时懵逼了,一张图让我怎么做动态改变? 难道不应该是做动画吗? 然而我想了一下似乎做动画也不是很好,然后我开始想什么东西可以让UI动态的改变呢? 根据输入的值显示不同的形状呢?

这是值得研究的问题.在网上找了一些资料发现 可以用一个面片来做,把面片放在UI上面然后动态改变面片的顶点,实现不同的形状.

雷达图如下:

QQ截图20161107163618.jpg

由于我们的UI部分是需要进行热更新的所以这个功能我是用LUA写的但是可以很容易的翻译成C#的:

 local attributeMesh = self.transform:Find("模型物体"):GetComponent(UnityEngine.MeshFilter)
 local verticesInit=attributeMesh.mesh.vertices
 this.SetHeroAttribute(attributeMesh,0,0,0.1)
 function this.SetHeroAttribute(attributeMesh,x,y,z)
    local  mesh = attributeMesh.mesh
    local vertices = mesh.vertices
    vertices[1] =verticesInit[1]-UnityEngine.Vector3(x,y,z)//把原来的顶点值减去设置的值再赋给模型的顶点
    vertices[2] =verticesInit[2]-UnityEngine.Vector3(x,y,z)//这里只改变了 第2和第3个顶点的值,这样原来的5角型就会变的不规则了.
    mesh.vertices = vertices
end

这个面片实际上有5个顶点,但是为了演示我在代码里只改变了它2个点.

Unity3D模型动态切割方案

在做项目时,美术那边提了一个需求,他们希望我们程序这边能用U3D来实现人物模型的被子弹击中爆炸的效果,也就是被子弹打中后怪物模型爆炸碎开的效果.

之前他们项目中的做法是,事先把模型切开,切成很多小块然后组装在一起,再在U3D中把每块模型步入刚体和碰撞器.

这次他们觉得这样工作量有点大,每个需要爆炸的模型都要事先切开,然后导入U3D,再处理. 于是把这个在U3D中动态切割模型的任务交给了我.我本能反应是先看看市面上的其他人的解决方案

发现并没有找到有用的方案,然后我开始找相关的插件.找到了 Shatter Toolkit 这个插件,这个插件就是用来切割模型的,而且可以自定义切开后的切口颜色.这样就很好办了.然后我按照插件上的

方法,用在了人物模型中,但是发现却切不开,于是我有弄了一个立方体,发现切开毫无问题.为什么我的人物模型切不开呢? 仔细研究发现,原来人物模型上有骨骼.如果模型带骨骼的话这个插件是切不开的.而且这个插件需要在被切割的模型上加上网格碰撞器,这是非常耗资源的.于是这个插件不可取.只能另找其他方法了.

然后我有陆续找了一下插件发现都是无法切开带骨骼的模型的,研究发现这些插件也是通过 UvMapper 获取模型上的顶点然后分解为小模块再新建出对象加上碰撞器和刚体,这样每个模块又可被分解.至于为什么带骨骼的切不开,当时没找到原因,但是可以确定的是确实是因为骨骼的原因.

于是,切割方案还是用了以前的方案进行.但是如果是普通的模型比如箱子之类的是可以使用Shatter Toolkit 这个插件的.

QQ截图20161107161634.jpg

微信自动点赞工具

  朋友圈的里面的动态更新太快,都没来得及看就被新的动态覆盖了,如果不给点赞可能很多朋友会以为我不关注他们呢,所以突发奇想,想要用按键精灵来做一个微信朋友圈自动点赞的工具.

于是利用之前学习到的按键精灵和MQ语言的知识轻轻松松就开发出来了哈哈! 

   

         

这个脚本现在只能适应在小米5 分辨率的手机上,因为我的安卓手机是小米5,如果要适应其他手机,其实只需要更改一下识别的坐标就可以了.

        

上脚本代码吧!

        

小米5版本:

Dim screenX,screenY,deviceID,colorDep,shijian
screenX = GetScreenX()
screenY = GetScreenY()
colorDep = GetScreenColorDep()
deviceID = GetDeviceID()

Dim dTime=1000

Dim dlTime=2000

Dim cTime=500 

Sub 信息提示(内容)
    TracePrint 内容
    ShowMessage 内容
    Delay dTime
End Sub

Sub 单击(x, y,含义)
Touch x, y, cTime
TracePrint 含义
Delay dTime
End Sub

Sub 单击T(x,y,t,含义)
Touch x,y,t
TracePrint 含义
Delay dTime
End Sub

Call 信息提示("启动微信~")
RunApp "com.tencent.mm"
Delay dlTime

Call 单击(342, 1467, "单击第一个微信")

Call 单击(672, 1830, "单击发现")

Call 单击(350, 330, "单击朋友圈")

While True
        
    Dim intX,intY
	FindMultiColor 978,589,1045,1248,"B09385","-13|-2|F8F8F8,10|-1|F8F8F8,-32|-1|B09385,
	19|0|B09385,-4|12|B09385,-5|-15|B09385,-21|-15|B09385,-24|17|B09385,17|-18|B09385",
	0,0.9,intX,intY
	If intX > -1 And intY > -1 Then
		Call 单击(intX, intY, "单击")	
		FindMultiColor 532,437,572,1287,"FFFFFF","7|17|F4F3F3,14|20|3D3B34,
		0|22|3D3B34,17|-2|FFFFFF,24|16|3D3B34,-9|15|FFFFFF,19|29|FFFFFF,9|31|3D3B34,
		9|-1|615F59",0,0.9,intX,intY
		If intX > -1 And intY > -1 Then
			Call 单击(intX, intY, "单击点赞")
		End If		
	End If
	If CmpColorEx("541|1876|DADADA,32|1821|F8F8F8,958|1825|F8F8F8,25|1769|D3D3D3,
	588|1769|D3D3D3,1060|1769|D3D3D3,759|1876|E1E1E1,561|1120|FFFFFF",0.9) = 1 Then
	   Exit While
	End If
	Swipe 1000, screenY / 2, 1000, screenY / 2 - 400
    Delay 1000
Wend

   

海马安卓模拟器版:

Dim screenX,screenY,deviceID,colorDep,shijian
screenX = GetScreenX()
screenY = GetScreenY()
colorDep = GetScreenColorDep()
deviceID = GetDeviceID()

Dim dTime=1000

Dim dlTime=2000

Dim cTime=500 

Sub 信息提示(内容)
    TracePrint 内容
    ShowMessage 内容
    Delay dTime
End Sub

Sub 单击(x, y,含义)
Touch x, y, cTime
TracePrint 含义
Delay dTime
End Sub

Sub 单击T(x,y,t,含义)
Touch x,y,t
TracePrint 含义
Delay dTime
End Sub

Call 信息提示("启动微信~")
RunApp "com.tencent.mm"
Delay dlTime

Call 单击(450, 1220,"单击发现")

Call 单击(250, 230, "单击朋友圈")

While True
        
    Dim intX,intY
	FindMultiColor 651,235,699,813,"B09385","-8|0|F8F8F8,8|1|F8F8F8,-19|2|B09385,-1|9|B09385"
	,0,0.9,intX,intY
	If intX > -1 And intY > -1 Then
		Call 单击(intX, intY, "单击")
		
		FindMultiColor 306,159,380,955,"3F3A39","24|-2|3F3A39,2|33|3F3A39,
		24|-26|3F3A39,48|8|3F3A39,23|21|3F3A39,44|4|FFFFFF,39|-8|C9C8C8,49|-3|FFFFFF,
		53|1|C5C4C4",0,0.9,intX,intY
		If intX > -1 And intY > -1 Then 
		    Call 单击(intX, intY, "单击点赞")
		End If

		//Call 单击(intX-367, intY, "单击点赞")
	End If
	
	If CmpColorEx("360|1249|DADADA,117|1249|F5F5F5,624|1251|EDEDED,363|1179|D3D3D3,
	632|1216|F8F8F8,31|1236|F8F8F8,355|1215|F8F8F8,165|1213|F8F8F8,672|1223|F8F8F8,
	315|1264|F8F8F8",0.9) = 1 Then
	    Exit While
	End If
	
	Swipe screenX / 2, screenY / 2, screenX / 2, screenY / 2 - 300
    Delay 1000
Wend

 如果你没有安卓手机可以试试,安装模拟器哦 把模拟器的分辨率设置到720*1280 的就可以了 ,快开始你的自动化之旅吧!

 如果你还不会使用按键精灵,请看我的这篇文章 https://huangyi.cc/?p=752