RichLabel基于Cocos2dx+Lua v3.x
RichLabel
简介
RichLabel基于Cocos2dx+Lua v3.x
解析字符串方面使用了labelparser,它可以将一定格式的字符串,转换为lua中的表结构
扩展标签极其简单,只需添加一个遵守规则的标签插件即可,无需改动已存在代码!!!
(标签插件都在labels文件夹下)
labelparser的详解
labelparser在github上的源码
RichLabel在github上的源码
- 支持图片(缩放,旋转,是否可见)
- 支持文本属性(字体,大小,颜色,阴影,描边,发光)
- 支持标签嵌套修饰文本,但是内部标签不会继承嵌套标签的属性
- 支持标签扩展(labels文件夹中可添加标签支持)
- 支持渐入动画,动画逐字回调
- 支持设置最大宽度,自动换行布局
- 支持手动换行,使用'\n'换行
- 支持设置行间距,字符间距
- 支持添加debug绘制文字范围和锚点
- 支持获得文字的精灵节点
- 支持设置标签锚点,透明度,颜色...
- 支持遍历字符,行等
- 支持获得任意行的行高
效果:
------------------------------------------------------
------------ TEST RICH-LABEL
------------------------------------------------------ local test_text = {
"<div fontcolor=#ff0000>hello</div><div fontcolor=#00ff00>hello</div><div fontsize=12>你</div><div fontSize=26 fontcolor=#ff00bb>好</div>ok",
"<div outline=1,#ff0000 >hello</div>",
"<div glow=#ff0000 >hello</div>",
"<div shadow=2,-2,0.5,#ff0000 >hello</div>",
"hello<img src='res/test.png' scale=0.5 rotate=90 visible=true />world",
}
for i=, #test_text do
local RichLabel = require("richlabel.RichLabel")
local label = RichLabel.new {
fontName = "res/msyh.ttf",
fontSize = ,
fontColor = cc.c3b(, , ),
maxWidth=,
lineSpace=,
charSpace=,
}
label:setString(test_text[i])
label:setPosition(cc.p(,-i*))
label:playAnimation()
sceneGame:addChild(label) label:debugDraw()
end
由于解析字符串使用了labelparser,那么我们先简单了解一下它,详细了解 传送门
下面就是使用labelparser解析富文本串用法和产生的table格式,大概了解一下
local text1 = "hello worldd <div>hello world</div> 你好 <div fontName='nihao' fontColore=#ff33ee>hello,world</div><div></div>"
local parsedtable = labelparser.parse(text1)
-- output:
<parsedtable> = {
{
content = "hello worldd ",
labelname = "div",
},
{
content = "hello world",
labelname = "div",
},
{
content = " 你好 ",
labelname = "div",
},
{
content = "hello,world",
fontname = "nihao",
fontsize = "#123456",
labelname = "div",
},
}
这种格式十分方便我们处理,它将每个文本片段及其属性分拆成table
,然后按顺序组织好返回给我们
这样我们处理就可以简单多了
原理
首先要说一下,这位前辈灵动君心他也有一个RichLabel,但是无法满足我的需求,大体思路和他类似。
大体思路:
1.解析字符串
2.解析出来的表中元素的处理
- 对于字符串的处理就是将字符串拆分成一个个字符,然后每个字符创建一个Label
- 对于图片的处理就是直接创建精灵
3.将创建好的node布局即可
第一步
解析字符串,解析字符串使用了我的另一个开源工具labelparser,直接解析就可以返回table
第二步
我们按照顺序从表第一项开始处理
每一项都可以获得对应的标签名,根据标签名调用对应的标签的处理函数,同时要将表项中的属性传入标签处理函数
(这个处理函数是以插件形式提供的,很便于扩展)
下面是img标签解析的的代码(label_img.lua)
--
-- <img/> 标签解析
-- return function (self, params, default)
if not params.src then return
end
-- 创建精灵,自动在帧缓存中查找,屏蔽了图集中加载和直接加载的区别
local sprite = self:getSprite(params.src)
if not sprite then
self:printf("<img> - create sprite failde")
return
end
if params.scale then
sprite:setScale(params.scale)
end
if params.rotate then
sprite:setRotation(params.rotate)
end
if params.visible ~= nil then
sprite:setVisible(params.visible)
end
return {sprite}
end
第三步
此时我们就获得了设置好属性的node,我们要做的就是布局文本
首先我们处理换行,什么时候换行呢?
+ 如果设置了MaxWidth
那么,每行最大宽度不能超过MaxWidth
,否则就换行
+ 如果文本内容中存在换行符\n
,则直接换行
我们遍历所有的node(node存在顺序)然后检测是否为Label,为Label则检测内容是否为\n
,然后检测此时累加宽度若超过了最大宽度,则将当前的node直接放到下一行
代码
-- 自动适应换行处理方法,内部会根据最大宽度设置和'\n'自动换行
-- 若无最大宽度设置则不会自动换行
function RichLabel:adjustLineBreak_(allnodelist, charspace)
-- 如果maxwidth等于0则不自动换行
local maxwidth = self._maxWidth
if maxwidth <= then maxwidth =
end
-- 存放每一行的nodes
local alllines = {{}, {}, {}}
-- 当前行的累加的宽度
local addwidth =
local rowindex =
local colindex =
for _, node in pairs(allnodelist) do
colindex = colindex +
-- 为了防止存在缩放后的node
local box = node:getBoundingBox()
addwidth = addwidth + box.width
local totalwidth = addwidth + (colindex - ) * charspace
local breakline = false
-- 若累加宽度大于最大宽度
-- 则当前元素为下一行第一个元素
if totalwidth > maxwidth then
rowindex = rowindex +
addwidth = box.width -- 累加数值置当前node宽度(为下一行第一个)
colindex =
breakline = true
end -- 在当前行插入node
local curline = alllines[rowindex] or {}
alllines[rowindex] = curline
table.insert(curline, node) -- 若还没有换行,并且换行符存在,则下一个node直接转为下一行
if not breakline and self:adjustContentLinebreak_(node) then
rowindex = rowindex +
colindex =
addwidth = -- 累加数值置0
end
end
return alllines
end -- 判断是否为文本换行符
function RichLabel:adjustContentLinebreak_(node)
-- 若为Label则有此方法
if node.getString then
local str = node:getString()
-- 查看是否为换行符
if str == "\n" then
return true
end
end
return false
end
这样我们就将混在一块的node拆分成一个table中存一行
虽然我们知道哪些node在第一行,哪些在第二行... ...
但是我们还没有布局呢!!!
下面我们就遍历每一行,然后调用行布局函数layoutLine_, 行累加函数还返回行的真实宽度和高度,这样我们就可以计算出最宽的一行,即为RichLabel的宽度
精简后代码
for index, line in pairs(alllines) do
local linewidth, lineheight = self:layoutLine_(basepos, line, , charspace)
-- todo
end
行布局函数(精简后)
-- 布局单行中的节点的位置,并返回行宽和行高
function RichLabel:layoutLine_(basepos, line, anchorpy, charspace)
local pos_x = basepos.x
local pos_y = basepos.y
local lineheight =
local linewidth =
for index, node in pairs(line) do
local box = node:getBoundingBox()
-- 设置位置
node:setPosition((pos_x + linewidth + box.width/), pos_y)
-- 累加行宽度
linewidth = linewidth + box.width + charspace
-- 查找最高的元素,为行高
if lineheight < box.height then lineheight = box.height
end
end
return linewidth, lineheight
end
这样我们就一行行布局好了
工具函数
问题:文本标签处理函数,首先要先将字符串拆分成一个个字符,如果字符串中存在中文那么直接拆分肯定是不行的
拆分字符串,支持Unicode编码
function RichLabel:stringToChars(str)
-- 主要用了Unicode(UTF-8)编码的原理分隔字符串
-- 简单来说就是每个字符的第一位定义了该字符占据了多少字节
-- UTF-8的编码:它是一种变长的编码方式
-- 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
-- 对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。
-- 剩下的没有提及的二进制位,全部为这个符号的unicode码。
local list = {}
local len = string.len(str)
local i =
while i <= len do
local c = string.byte(str, i)
local shift =
if c > and c <= then
shift =
elseif (c >= and c <= ) then
shift =
elseif (c >= and c <= ) then
shift =
elseif (c >= and c <= ) then
shift =
end
local char = string.sub(str, i, i+shift-)
i = i + shift
table.insert(list, char)
end
return list, len
end
问题:处理颜色也要说一下,由于使用HTML方式标记颜色,所以要解析#FF0099这种类型的颜色
这里需要注意返回的是cc.c4b ,因为我们可能使用颜色设置Label阴影,而Label的阴影函数要求cc.c4b,但是如果传入cc.c3b的话,alpha值会为0,结果就是没效果!!!
对于这种情况的详细讨论见 Cocos2dx+lua中Color参数的坑
-- 解析16进制颜色rgb值
function RichLabel:convertColor(xstr)
if not xstr then return
end
local toTen = function (v)
return tonumber("0x" .. v)
end local b = string.sub(xstr, -, -)
local g = string.sub(xstr, -, -)
local r = string.sub(xstr, -, -) local red = toTen(r)
local green = toTen(g)
local blue = toTen(b)
if red and green and blue then
return cc.c4b(red, green, blue, )
end
end
问题:因为也支持了图片,所以图片的加载必须要考虑,无论是从图集中加载还是碎图加载都应该正常
-- 创建精灵,现在帧缓存中找,没有则直接加载
-- 屏蔽了使用图集和直接使用碎图创建精灵的不同
function RichLabel:getSprite(filename)
local spriteFrameCache = cc.SpriteFrameCache:getInstance()
local spriteFrame = spriteFrameCache:getSpriteFrameByName(filename) if spriteFrame then
return cc.Sprite:createWithSpriteFrame(spriteFrame)
end
return cc.Sprite:create(filename)
end
要详细了解还是去看看代码吧!!!
RichLabel基于Cocos2dx+Lua v3.x的更多相关文章
- Cocos2dx+lua合适还是Cocos2dx+js合适?
问题: 开发cocos2dx手游Cocos2dx+lua合适还是Cocos2dx+js合适 百牛信息技术bainiu.ltd整理发布于博客园 回答: 作者:廖宇雷链接:https://www.zhih ...
- 开源基于lua gc管理c++对象的cocos2dx lua绑定方案
cocos2dx目前lua对应的c++对象的生命周期管理,是基于c++析构函数的,也就是生命周期可能存在不一致,比如c++对象已经释放,而lua对象还存在,如果这时候再使用,会有宕机的风险,为此我开发 ...
- 【转】cocos2d-x Lua
Call custom c++ from Lua cocos2d-x lua binds c++ class, class functions ,enum and some global functi ...
- 基于cocos2d-x的游戏框架设计——李成
视频:http://v.youku.com/v_show/id_XMzc5ODUyMTI4.html?f=17330006 网易科技讯 3月31日,第四届CocoaChina开发者大会暨Cocos2d ...
- Cocos2d-x Lua 阅读Csv文件,使用数据更方便
在我的书或出售之前,我的源代码,有Csvshadow文件. 也许这是偏见.我与工作将是最长的轮廓Csv,所以,我会帮助不大喜欢它的游戏. Csv文件,非常格式easy,也就是说,一个数据线,字段之间用 ...
- cocos2d-x lua 中使用protobuf并对http进行处理
cocos2d-x lua 中使用protobuf并对http进行处理 本文介绍 cocos2d-x lua 中使用http 和 基于cocos2d-x 对lua http的封装(部分ok) 本博客链 ...
- Cocos2d-x教程(26)-Cocos2d-x + Lua脚本实现大地图缩放功能
欢迎增加 Cocos2d-x 交流群: 193411763 视频教程地址:http://www.tudou.com/programs/view/qRiOfppMghM/ 转载请注明原文出处:http: ...
- cocos2d-x lua绑定解析
花了几天时间看了下cocos2d-x lua绑定那块,总算是基本搞明白了,下面分三部分解析lua绑定: 一.lua绑定主要用到的底层函数 lua绑定其本质就是有一个公用的lua_Stack来进行C和L ...
- 基于cocos2d-x的Android游戏中使用fmod音频引擎
cocos2d-x的音频引擎是cocosDenshion, 它的Android版比较弱, 只能播放一个背景音乐和些许音效, 如果要实现稍微复杂一点的音频播放, 比如同时播放几个音轨就不能了. 这一点远 ...
随机推荐
- ArrayList、LinkedList、Vector的区别
Arraylist和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加插入元素,都允许直接序号索引元素,但是插入数据要涉及到数组元素移动等内存操作,所以插入数据慢,查找有下标, ...
- 为什么Linux的fdisk分区时第一块磁盘分区的First Sector是2048?
这个问题曾经困扰我很久,在了解了MBR之后,我曾认为第一块分区之前为一个block.但是用fdisk查看是2048,一直不了解其中的缘由,今天查了一下资料,大概了解了,其中的细节留着慢慢去了解. 最直 ...
- HTML5中的Canvas精品教程
http://javascript.ruanyifeng.com/htmlapi/canvas.html
- Window.document对象(1)
1.Window.document对象 一.找到元素: docunment.getElementById("id"):根据id找,最多找一个: var a =docunme ...
- mp3 切割
开源的东东很不错,摘了一段好文: 常在听mp3或其他格式音乐的朋友,有时会有特别喜欢的片段,例如副歌的部份会想拿来做手机的铃声.这时候就需要一些处理音效的软体,例如之前提过的 Audacity.其实还 ...
- IDEA for Mac 解决控制台乱码问题
近期发现 idea for mac 版本中 tomcat 控制台有中文的地方出现乱码问题,其实很简单就可以解决. 这里做个笔记,以后可以方便不会的人来解决 ---------------------- ...
- 【BZOJ】【3530】【SDOI2014】数数
AC自动机/数位DP orz zyf 好题啊= =同时加深了我对AC自动机(这个应该可以叫Trie图了吧……出边补全!)和数位DP的理解……不过不能自己写出来还真是弱…… /************* ...
- [haoi2009]毛毛虫 树形dp
这道题细节处理不少,但要AC不难: 设以i节点为根节点的子树能形成的最大的毛毛虫长度为f[i],则f[i]=max(f[j])+i节点的孩子数: 答案需要f最大和次大的两个子树合并,而且若合并的位置不 ...
- 【ASP.Net MVC】在AspNet Mvc使用JQuery AutoComplete组件
在AspNet Mvc使用JQuery AutoComplete组件 官方文档: http://api.jqueryui.com/autocomplete/#entry-examples 要使用JQu ...
- 关于WSDL
Message Operation 最核心的在于Operation 只要关心Operation就可以了,Operation只有Input, Output没有其他内容,是相对固定的.只要关心一下Inpu ...