第一个上架的APP,目前功能还比较少,空闲时间慢慢添加!
[奇淫巧计]OSX和Windows下分割合并文件
需求的产生
公司外网传内网的文件上传有大小限制,如今有一个数G的文件要传到内网去
环境:外网Mac,内网Windows
分包压缩
首先想到的是分包压缩,Mac下用BetterZip分卷压缩成xxx.zip.001~xxx.zip.014
内网用WinRAR解压,我凑,后缀为001的RAR不认识,直接打开告诉我压缩文件已损坏
心想分包解压可能是有一个固定的命名格式,另辟蹊径吧
文件分割
mac下有个命令 split
用如下命令可以将文件分割成100MB/个的小文件,分割后的文件叫 file.exe.aa file.exe.ab …
1 |
split -b 100m file.exe file.exe. |
文件合并
windows下用copy命令就可以轻松的合并文件
1 |
copy /b file.exe.a* file.exe |
Enjoy it!
[quick]关于UI组件的clone()方法的坑
问题的产生
项目是用ccs1.6做的UI,在某个需求中,需要把一个Sprite的SpriteFrame clone一份,去构造另一个
代码类似于
1 2 3 4 5 |
local sprite = cc.ui.UIImage.new("file1.png") sprite:pos(x,y):addTo(self) local sprite2 = cc.ui.UIImage.new(sprite:clone():getSpriteFrame()) sprite2:pos(x2,y2):addTo(self) |
这样并没有问题
但是如果sprite的SpriteFrame被set过一个新的,那sprite2得到的SpriteFrame,依然是老的
怎么回事?
查看源码,我们发现Node:clone()方法被重写了(在NodeEx.lua里)
1 2 3 4 5 6 7 8 9 |
function Node:clone() local cloneNode = self:createCloneInstance_() cloneNode:copyProperties_(self) cloneNode:copySpecialPeerVal_(self) cloneNode:copyCloneWidgetChildren_(self) return cloneNode end |
粗读一下代码,先克隆一个实例,然后克隆基础属性,最后递归把子节点也克隆了
那问题就应该出现在self:createCloneInstance_()上
查阅发现
1 2 3 |
function UIImage:createCloneInstance_() return UIImage.new(unpack(self.args_)) end |
好吧…直接用构造参数返回一个实例是什么鬼
既然出事了,就弄弄大
我们发现所有UI组件的createCloneInstance_方法都有这个问题
写一段比较简单的代码
1 2 3 4 5 6 |
local label = cc.ui.UILabel.new({text = "before"}) label:pos(x,y):addTo(self) label:setString("after") local label2 = label:clone() label2:pos(x2,y2):addTo(self) |
可以看到,屏幕上显示label显示after,label2显示before
如果不用UI组件呢?
我试了一下,直接使用Sprite和LabelTTF是没有问题的,那这个就可以定性为quick的bug了
[quick]解决3.5中的触摸bug
[quick]LazyLoader懒加载工具
懒加载
我最先接触懒加载(延时加载)是在Hibernate里,因为一次和数据库交互过多,导致应用程序卡顿,所以就延迟加载这些数据,等需要的时候再取.可以认为懒加载就是非即时的加载数据.
问题的产生
在游戏开发中,也会碰到一次处理太多数据造成游戏卡顿降帧,比较典型的比如”背包”模块,背包可以认为是一个ListView,由一个lua table里的一些元素作为数据源来构建,当用一个for循环去构造界面时,背包格子过多会导致游戏线程卡住,游戏降帧在所难免.如何做到不降帧?我们可以把一个复杂的逻辑分在多个帧里去做.
来看看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
local LazyLoader = class("LazyLoader") LazyLoader.ON_FUNC = "ON_FUNC" LazyLoader.ON_CALLBACK = "ON_CALLBACK" function LazyLoader:ctor(params) cc(self):addComponent("components.behavior.EventProtocol"):exportMethods() if params then self:setItemList(params.itemList) if params.func then self:onFunc(params.func) end if params.callback then self:onCallback(params.callback) end end end function LazyLoader:setItemList(itemList) self._itemList = itemList self._count = #self._itemList end function LazyLoader:onFunc(func) return self:addEventListener(LazyLoader.ON_FUNC, func) end function LazyLoader:onCallback(callback) return self:addEventListener(LazyLoader.ON_CALLBACK, callback) end function LazyLoader:start() if self.updateSchedulerEntry == nil then self.updateSchedulerEntry = cc.Director:getInstance():getScheduler():scheduleScriptFunc(handler(self,self.update),0,false) self._curIdx = 1 end end function LazyLoader:update(dt) if self._curIdx >= self._count then if self.updateSchedulerEntry ~= nil then cc.Director:getInstance():getScheduler():unscheduleScriptEntry(self.updateSchedulerEntry) end self:dispatchEvent({name = LazyLoader.ON_CALLBACK}) return end self:dispatchEvent({name = LazyLoader.ON_FUNC, item = self._itemList[self._curIdx], idx = self._curIdx}) self._curIdx = self._curIdx + 1 end return LazyLoader |
用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
local lazy = LazyLoader.new() local lazyLabel = cc.ui.UILabel.new({ text = "aaaa", color = cc.c3b(255, 255, 255), }) lazyLabel:align(display.LEFT_TOP,50,display.height - 270) self:addChild(lazyLabel) local itemList = {} for i=1,10000 do itemList[i] = "No."..i end lazy:setItemList(itemList) lazy:onFunc(function(event) lazyLabel:setString(event.item) end) lazy:onCallback(function(event) lazyLabel:setString("Finish!!!") end) lazy:start() |
Github
[cocos2dx]开启3D模型的抗锯齿功能
3D时代
当Unity3D开始支持2D的时候Cocos2d-x也开始慢慢支持3D
Cocos2d-x的3D
使用下来一是不习惯,二是问题比较多,比较明显的,就是模型在运动时白边比较严重
开启抗锯齿
找到项目的AppController.mm文件
在didFinishLaunchingWithOptions方法中将
1 2 3 4 5 6 7 |
CCEAGLView *eaglView = [CCEAGLView viewWithFrame: [window bounds] pixelFormat: (NSString*)cocos2d::GLViewImpl::_pixelFormat depthFormat: cocos2d::GLViewImpl::_depthFormat preserveBackbuffer: NO sharegroup: nil multiSampling: NO numberOfSamples: 0 ]; |
替换成
1 2 3 4 5 6 7 |
CCEAGLView *eaglView = [CCEAGLView viewWithFrame: [window bounds] pixelFormat: (NSString*)cocos2d::GLViewImpl::_pixelFormat depthFormat: cocos2d::GLViewImpl::_depthFormat preserveBackbuffer: NO sharegroup: nil multiSampling: YES numberOfSamples: 4 ]; |
模型白边的问题就好很多了
[Lua]去掉文本中的Html标签
直接上代码了
1 2 3 4 |
function clearHtml(htmlText) local s,c = string.gsub(htmlText,"<[%a%A]->","") return s end |
[Lua]cocos2dx/quick3.3中ScrollView的使用
ScrollView滚动视图
在游戏开发过程中,由于手机屏幕空间有限,加上手指的灵动性,滑动成了最好的替换翻页的操作方式
ScrollView就是在这种需求驱动下的产物
cc.ScrollView和cc.ui.UIScrollView
前者是cocos2dx3.3原生的c++类,继承于cc.Layer
后者是QuickUI组件中的lua类,继承于cc.ClippingRectangleNode
quick中的cc.ui.UIScrollView用法
1 2 3 4 5 6 7 8 9 10 11 |
local visibleSize = cc.Director:getInstance():getVisibleSize() local viewRect = cc.rect(0,0,visibleSize.width,visibleSize.height) local scrollView = cc.ui.UIScrollView.new({ viewRect = viewRect }) local scrollNode = display.newLayer() scrollView:addScrollNode(scrollNode) -- 注册滚动事件 通过event.name区分 scrollView:onScroll(function(event) -- TODO: sth end) |
在实际运用过程中,我发现quick的滚动组件存在性能问题(windows上),一旦组件多了之后滚动不流畅,卡顿严重
原生cc.ScrollView用法
1 2 3 4 5 6 7 8 9 |
local visibleSize = cc.Director:getInstance():getVisibleSize() local scrollView = cc.ScrollView:create(visibleSize) local scrollNode = cc.Layer:create() -- 等同于display.newLayer() scrollView:setContainer(scrollNode) -- 为scrollView添加滚动事件 scrollView:setDelegate() scrollView:registerScriptHandler(function() -- TODO: sth end,0) -- 0为滚动事件 1为缩放事件 |
[IDE]用Sublime Text开发属于自己的插件LuaTemplate
接上篇
CCI的出现和初衷虽然是极好的,但是使用过程中还是会有不尽人意的地方,比如复制黏贴卡顿/代码提示卡顿,或者不习惯用Eclipse等种种原因,还是有童鞋喜欢更轻量级一点的编辑器,比如Sublime Text
Sublime Text(简称ST)
ST是一款文本编辑器,它十分轻量级,以至于连IDE都称不上,而它最厉害的地方就是其丰富的插件库,使用插件的组合,可以迅速打造出一套属于自己的高效开发环境
插件开发
基于个人习惯原因,我们会有很多稀奇古怪的需求,这类需求可能是很小众的,大路的插件并不会予以支持,所以我们就得自己动手 下面的例子,就是我开发过程中碰到的需求,参见CCI版本的Cocos Code IDE的Lua模板加强
代码我上传到Github了
新建插件LuaTemplate
首先找到插件目录,菜单中
- OSX:Sublime Text->Preferences->Browse Packages…
- Windows:Preferences->Browse Packages…
新建一个文件夹,命名为LuaTemplate 选择Tools->New Plugin… 新建一个.py文件,保存到LuaTemplate下
1 2 3 4 5 |
import sublime, sublime_plugin class ExampleCommand(sublime_plugin.TextCommand): def run(self, edit): self.view.insert(edit, 0, "Hello, World!") |
“ExampleCommand”就是我们执行命令的名字,以大驼峰式命名,实际的命令为”example” 按Ctrl+`打开ST控制台,输入
1 |
view.run_command("example") |
此时已打开文件的文件头,就会被输入”Hello World!”的字样 这里是ST Plugin API
编写插件逻辑
我们还是以newclass newlayer newfunction这几个来做例子
PS:博主不会python,现学的,语法写的烂请轻喷
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
import sublime, sublime_plugin import time import subprocess import os import codecs import re process=None file_head_template="""--- -- -- ${date} -- @author ${author} -- """ new_class_template=file_head_template+""" local ${short_file_name} = class("${short_file_name}") function ${short_file_name}:ctor() end return ${short_file_name} """ new_layer_template=file_head_template+""" local ${short_file_name} = class("${short_file_name}",BaseLayer) function ${short_file_name}:ctor() ${short_file_name}.super.ctor(self) end function ${short_file_name}:onEnter() end function ${short_file_name}:onExit() end return ${short_file_name} """ new_function_template=""" function ${short_file_name}:name() end """ file_separator = "/" if sublime.platform() == "windows": file_separator = "\" def getConfig(config,key): if os.path.exists(config): f=codecs.open(config,"r","utf-8") while True: line=f.readline() if line: sps=line.split("=") if sps[0].strip() == key: return sps[1].strip().replace(""","") else: break def generateCode(self,template): file_name = self.view.file_name() sp_start = file_name.rfind(file_separator) + 1 sp_end = file_name.rfind(".") settings=sublime.load_settings("LuaTemplate.sublime-settings") author = settings.get("author","Your name") #短文件名 short_file_name = file_name[sp_start:sp_end] code = template code = code.replace("${date}",time.strftime("%Y-%m-%d %X", time.localtime())) code = code.replace("${short_file_name}",short_file_name) code = code.replace("${author}",author) return code # ltnewclass 新建一个类的模板 class LtnewclassCommand(sublime_plugin.TextCommand): def run(self, edit): code = generateCode(self,new_class_template) (row,col) = self.view.rowcol(self.view.sel()[0].begin()) self.view.insert(edit,self.view.text_point(row,col),code) #ltnewlayer 新建一个层的模板 class LtnewlayerCommand(sublime_plugin.TextCommand): def run(self, edit): code = generateCode(self,new_layer_template) (row,col) = self.view.rowcol(self.view.sel()[0].begin()) self.view.insert(edit,self.view.text_point(row,col),code) #ltnewfunction 新建一个函数的模板 class LtnewfunctionCommand(sublime_plugin.TextCommand): def run(self, edit): code = generateCode(self,new_function_template) (row,col) = self.view.rowcol(self.view.sel()[0].begin()) self.view.insert(edit,self.view.text_point(row,col),code) #ltrunwithplayer 跑起来 class LtrunwithplayerCommand(sublime_plugin.TextCommand): def run(self, edit): settings=sublime.load_settings("LuaTemplate.sublime-settings") quickroot = settings.get("quick_v3_root","") if sublime.platform() == "windows": playerPath = quickroot+"/quick/player/win32/player3.exe" if sublime.platform() == "osx": playerPath = quickroot+"/player3.app/Contents/MacOS/player3" file_name = self.view.file_name() workdir=file_name[0:file_name.rfind("src")] srcDirName=workdir+"/src" args=[playerPath] args.append("-workdir") args.append(workdir) args.append("-file") args.append(srcDirName+"/main.lua") global process if process: try: process.terminate() except Exception: pass if sublime.platform()=="osx": process=subprocess.Popen(args) elif sublime.platform()=="windows": process=subprocess.Popen(args) #ltrunwithsimulator 跑起来2 class LtrunwithsimulatorCommand(sublime_plugin.TextCommand): def run(self, edit): file_name = self.view.file_name() workdir=file_name[0:file_name.rfind("src")] if sublime.platform() == "windows": simulatorPath = workdir+"/runtime/win32/PrebuiltRuntimeLua.exe" if sublime.platform() == "osx": simulatorPath = workdir+"/runtime/mac/PrebuiltRuntimeLua.app/Contents/MacOS/PrebuiltRuntimeLua Mac" args=[simulatorPath] args.append("-workdir") args.append(workdir) global process if process: try: process.terminate() except Exception: pass if sublime.platform()=="osx": process=subprocess.Popen(args) elif sublime.platform()=="windows": process=subprocess.Popen(args) |
配置文件
新建LuaTemplate.sublime-settings
1 2 3 4 |
{ "author" : "yaoyl", "quick_v3_root" : "/Users/superyyl/Documents/quick-3.3" } |
入口快捷方式
新建Default.sublime-commands
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
[ { "caption": "Lua Template: New Class", "command": "ltnewclass" }, { "caption": "Lua Template: New Layer", "command": "ltnewlayer" }, { "caption": "Lua Template: New Function", "command": "ltnewfunction" }, { "caption": "Lua Template: Run With Player", "command": "ltrunwithplayer" }, { "caption": "Lua Template: Run With Simulator", "command": "ltrunwithsimulator" } ] |
这个是通过cmd+shift+p(windows:ctrl+shift+p)里输入命令来执行python代码的
还有菜单,侧边栏菜单,右键菜单等快捷入口,就不一一说了
跑一个
新建一个LoginLayer.lua文件,cmd+shift+p,然后输入new class,选择Lua Template: New Class
1 2 3 4 5 6 7 8 9 10 11 12 |
--- -- -- 2015-01-29 16:57:56 -- @author yaoyl -- local LoginLayer = class("LoginLayer") function LoginLayer:ctor() end return LoginLayer |
选择Lua Template: Run With Player也能顺利调用模拟器
[IDE]Cocos Code IDE的Lua模板加强
前言
Cocos Code IDE(以下简称CCI)是一款基于Eclipse和LDT的代码编辑器,其主要面向Cocos2d-X Lua/JS/Quick-lua的开发
问题的产生
在开发过程中,我们新建一个lua文件,都需要
1 2 3 4 5 6 |
local ClassName = class("ClassName") function ClassName:ctor() end return ClassName |
关键你复制了还需要把”ClassName”改成你要的
相当麻烦
So
我们有模板,在CCI中点击Windows->Preferences在左边选择Lua->Editor->Templates,单击”New”按钮
Name是触发字串,我们填newclass
Context选Lua
Description是提示信息,就叫”新建类”好了
Pattern:
1 2 3 4 5 6 7 8 9 10 11 |
--- -- -- ${time},${date} -- @author ${user} -- local ${module_short_name} = class("${module_short_name}") function ${module_short_name}:ctor() end return ${module_short_name} |
当你键盘敲出newclass(等代码提示),啪一个回车,一个类就创建好了,还烙上了你的大名,绝对酷炫
这么看大致就懂了,各种宏的含义如下
${date},${time},${user}这就是日期,时间,作者(os的登录名),用来标识这个文件是何时谁创建的,追查问题精确到人
${module_short_name} 短文件名,比如我一个文件”app/views/LoginView.lua”,就可以取到”LoginView”
举一反三
在我的项目中还有很多快捷的模板
比如 function
1 2 3 4 5 |
--- -- function ${module_short_name}:${name}(${parameters}) ${cursor} end |
比如newlayer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
--- -- -- ${time},${date} -- @author ${user} -- local ${module_short_name} = class("${module_short_name}",BaseLayer) function ${module_short_name}:ctor() ${module_short_name}.super.ctor(self) end function ${module_short_name}:onEnter() end function ${module_short_name}:onExit() end return ${module_short_name} |
恩 剩下的看大家创造力了 有好用的记得在文章下回复~