quick-cocos2d-x tips

抽象根系
• 阅读 3084

原文请猛戳:
http://galoisplusplus.coding....

承接上一篇,这篇主要谈谈本渣在quickx用的一些脚本或自己折腾的一些定制,本文也将不时更新。

如无特殊说明,相关函数放在一个MyPackage的lua global table中:

MyPackage = MyPackage or {}

UI组件

滚动列表相关

--[[--
Refresh UIListView at the current postion.
NOTE: only needed in async mode
]]
function MyPackage.refreshUIListView(listView)
    if not listView.bAsyncLoad then
        listView:reload()
        return
    end

    if #listView.items_ <= 0 then
        listView:reload()
        return
    end

    local originPos = MyPackage.getOriginPosOfUIListView(listView)
    -- index of the previous beginning item
    local beginIdx = listView.items_[1].idx_

    listView:removeAllItems()
    listView.container:setPosition(0, 0)
    listView.container:setContentSize(cc.size(0, 0))

    MyPackage.drawUIListViewFromIdx(listView, beginIdx, originPos.x, originPos.y)
end

--[[--
NOTE: only needed in async mode
]]
function MyPackage.getOriginPosOfUIListView(listView)
    if not listView.bAsyncLoad then
        return
    end

    local getContainerCascadeBoundingBox = function (listView)
        local boundingBox
        for i, item in ipairs(listView.items_) do
            local w,h = item:getItemSize()
            local x,y = item:getPosition()
            local anchor = item:getAnchorPoint()
            x = x - anchor.x * w
            y = y - anchor.y * h

            if boundingBox then
                boundingBox = cc.rectUnion(boundingBox, cc.rect(x, y, w, h))
            else
                boundingBox = cc.rect(x, y, w, h)
            end
        end

        local point = listView.container:convertToWorldSpace(cc.p(boundingBox.x, boundingBox.y))
        boundingBox.x = point.x
        boundingBox.y = point.y
        return boundingBox
    end

    local cascadeBound = getContainerCascadeBoundingBox(listView)
--    local cascadeBound = listView.scrollNode:getCascadeBoundingBox()

    local localPos = listView:convertToNodeSpace(cc.p(cascadeBound.x, cascadeBound.y))

    local originPosX = 0
    local originPosY = 0
    if cc.ui.UIScrollView.DIRECTION_VERTICAL == listView.direction then
        -- ahead part of view
        originPosY = localPos.y + cascadeBound.height - listView.viewRect_.y - listView.viewRect_.height
    else
        -- left part of view
        originPosX = - listView.viewRect_.x + localPos.x
    end

    return cc.p(originPosX, originPosY)
end

--[[--
Draw UIListView from the `beginIdx`th item at position (`originPosX`, `originPosY`).
NOTE: only needed in async mode
]]
function MyPackage.drawUIListViewFromIdx(listView, beginIdx, originPosX, originPosY)
    if not listView.bAsyncLoad then
        listView:reload()
        return
    end

    listView:removeAllItems()
    listView.container:setPosition(0, 0)
    listView.container:setContentSize(cc.size(0, 0))

    local beginIdx = beginIdx or 1
    local originPosX = originPosX or 0
    local originPosY = originPosY or 0

    local count = listView.delegate_[cc.ui.UIListView.DELEGATE](listView, cc.ui.UIListView.COUNT_TAG)
    listView.items_ = {}
    local itemW, itemH = 0, 0
    local item
    local containerW, containerH = 0, 0
    for i = beginIdx, count do
        item, itemW, itemH = listView:loadOneItem_(cc.p(originPosX, originPosY), i)
        if cc.ui.UIScrollView.DIRECTION_VERTICAL == listView.direction then
            originPosY = originPosY - itemH
            containerH = containerH + itemH
        else
            originPosX = originPosX + itemW
            containerW = containerW + itemW
        end
        if containerW > listView.viewRect_.width + listView.redundancyViewVal
            or containerH > listView.viewRect_.height + listView.redundancyViewVal then
            break
        end
    end

    if cc.ui.UIScrollView.DIRECTION_VERTICAL == listView.direction then
        listView.container:setPosition(listView.viewRect_.x,
            listView.viewRect_.y + listView.viewRect_.height)
    else
        listView.container:setPosition(listView.viewRect_.x, listView.viewRect_.y)
    end

    listView:increaseOrReduceItem_()
end

function MyPackage.elasticMoveUIScrollView(scrollView, scrollToBottom, scrollToRight)
    local cascadeBound = scrollView:getScrollNodeRect()
    local disX, disY = 0, 0
    local viewRect = scrollView:getViewRectInWorldSpace()

    if cascadeBound.width < viewRect.width then
        if scrollToRight then
            disX = viewRect.x + viewRect.width - cascadeBound.x - cascadeBound.width
        else
            disX = viewRect.x - cascadeBound.x
        end
    else
        if cascadeBound.x > viewRect.x then
            disX = viewRect.x - cascadeBound.x
        elseif cascadeBound.x + cascadeBound.width < viewRect.x + viewRect.width then
            disX = viewRect.x + viewRect.width - cascadeBound.x - cascadeBound.width
        end
    end

    if cascadeBound.height < viewRect.height then
        if scrollToBottom then
            disY = viewRect.y - cascadeBound.y
        else
            disY = viewRect.y + viewRect.height - cascadeBound.y - cascadeBound.height
        end
    else
        if cascadeBound.y > viewRect.y then
            disY = viewRect.y - cascadeBound.y
        elseif cascadeBound.y + cascadeBound.height < viewRect.y + viewRect.height then
            disY = viewRect.y + viewRect.height - cascadeBound.y - cascadeBound.height
        end
    end

    if 0 == disX and 0 == disY then
        return
    end

    local posX, posY = scrollView.scrollNode:getPosition()
    scrollView.position_ = cc.p(posX + disX, posY + disY)
    scrollView.scrollNode:setPosition(scrollView.position_)
end

更新:以上改动已挪到yszheda/quickx-extensions的UIScrollView或UIListView中。

lua语言相关

bool转数字

function MyPackage.bool2number(bool)
    return bool and 1 or 0
end

table相关

function MyPackage.removeValueFromArray(array, value)
    local idx
    for i, v in ipairs(array) do
        if v == value then
            idx = i
            break
        end
    end
    if idx then
        table.remove(array, idx)
    end
end

function MyPackage.hasValueInArray(array, value)
    local hasValue = false
    for i, v in ipairs(array) do
        if v == value then
            hasValue = true
            break
        end
    end
    return hasValue
end

UTF8字符串

cocos2d-x的label默认为UTF8编码,一般场景下主要需要以下两个功能:

  • 字符串长度

  • 截取子串

原先本渣用cocos2d-x时写了个C++函数来求长度:

long long utf8StringSize(const std::string& str)
{
    char* charArray = new char[str.length() + 1];
    strcpy(charArray, str.c_str());
    char* s = charArray;
    /*-----------------------------------------------------------------------------
     *  References: http://stackoverflow.com/questions/4063146/getting-the-actual-length-of-a-utf-8-encoded-stdstring
     *-----------------------------------------------------------------------------*/
    long long len = 0;
    while (*s) len += (*s++ & 0xc0) != 0x80;
    delete [] charArray;
    return len;
}

cocos2d-x Helper也提供了接口来做字符串截取:

static std::string getSubStringOfUTF8String(const std::string& str,
                                   std::string::size_type start,
                                   std::string::size_type length);

在lua方面,quickx已经提供string.utf8len来求字符串长度,本渣仿照其实现写了个截取子串的函数:

function MyPackage.utf8str(str, start, num)
    local function utf8CharSize(char)
        local size = 0

        local arr = {0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc}
        local size = #arr
        while arr[size] do
            if char >= arr[size] then
                break
            end
            size = size - 1
        end

        return size
    end

    local startIdx = 1
    while start > 1 do
        local char = string.byte(str, startIdx)
        startIdx = startIdx + utf8CharSize(char)
        start = start - 1
    end

    local endIdx = startIdx
    while num > 0 do
        if endIdx > #str then
            endIdx = #str
            break
        end
        local char = string.byte(str, endIdx)
        endIdx = endIdx + utf8CharSize(char)
        num = num - 1
    end

    return str:sub(startIdx, endIdx - 1)
end

不过目前lua5.3已经有UTF8库,可以不用自行造轮子了。另外,关于其他UTF8相关的lua问题可以参考Lua Unicode

其他Helper Functions

更新view的callbackWrapper

我们经常碰到如下的情景:
游戏向后端请求数据,在拿到数据之后执行某个callback去更新某个view。
这种网络请求通常是异步的,如果所请求的数据回来时相关的view被释放,则执行操作该view的callback会导致问题(例如访问非法内存地址)。
这时候我们可以用tolua.isnull来判断相关的view对象是否被释放。
由于每个这种类型的callback都有必要加上这样的guard code,所以本渣干脆做了如下的接口:

function MyPackage.callbackWrapper(views, callback)
    return function(...)
        for _, view in pairs(views) do
            if tolua.isnull(view) then
                return
            end
        end
        if callback ~= nil then
            callback(...)
        end
    end
end

拿到一个node九个端点的坐标

--[[--
get the nine positions of a node (the following variables are defined in display.lua of quickx):
display.CENTER
display.LEFT_TOP
display.CENTER_TOP
display.RIGHT_TOP
display.CENTER_LEFT
display.CENTER_RIGHT
display.BOTTOM_LEFT
display.BOTTOM_RIGHT
display.BOTTOM_CENTER
]]
function MyPackage.getPositionOfNode(node, alignType)
    if not node or tolua.isnull(node) then
        return
    end

    local size = node:getContentSize()
    if size.width == 0 and size.height == 0 then
        size = node:getCascadeBoundingBox()
    end

    local pos = cc.p(node:getPosition())
    local anchorPoint = cc.p(node:getAnchorPoint())

    if alignType == display.LEFT_TOP or
        alignType == display.LEFT_CENTER or
        alignType == display.LEFT_BOTTOM then
        pos.x = pos.x - size.width * anchorPoint.x
    elseif alignType == display.CENTER_TOP or
        alignType == display.CENTER or
        alignType == display.CENTER_BOTTOM then
        pos.x = pos.x - size.width * anchorPoint.x + size.width * 0.5
    elseif alignType == display.RIGHT_TOP or
        alignType == display.RIGHT_CENTER or
        alignType == display.RIGHT_BOTTOM then
        pos.x = pos.x - size.width * anchorPoint.x + size.width
    end

    if alignType == display.BOTTOM_LEFT or
        alignType == display.BOTTOM_CENTER or
        alignType == display.BOTTOM_RIGHT then
        pos.y = pos.y - size.height * anchorPoint.y
    elseif alignType == display.CENTER_LEFT or
        alignType == display.CENTER or
        alignType == display.CENTER_RIGHT then
        pos.y = pos.y - size.height * anchorPoint.y + size.height * 0.5
    elseif alignType == display.TOP_LEFT or
        alignType == display.TOP_CENTER or
        alignType == display.TOP_RIGHT then
        pos.y = pos.y - size.height * anchorPoint.y + size.height
    end

    return pos
end

在某个container中加入sprite,可指定根据container大小进行缩放及对齐方式

function MyPackage.displaySpriteOnContainer(sprite, container, scaleToFit, alignType)
    if tolua.isnull(container) then
        return
    end

    -- default settings
    local scaleToFit = (scaleToFit ~= false)
    local alignType = alignType or display.CENTER

    if not tolua.isnull(sprite) then
        local originSize = sprite:getContentSize()
        if originSize.width == 0 or originSize.height == 0 then
            originSize = sprite:getCascadeBoundingBox()
        end

        local targetSize = container:getContentSize()
        if targetSize.width == 0 or targetSize.height == 0 then
            targetSize = container:getCascadeBoundingBox()
        end

        if scaleToFit then
            sprite:setScale(targetSize.width / originSize.width, targetSize.height / originSize.height)
        end

        -- NOTE: ignore container's anchor point
        local pos = MyPackage.getPositionOfNode(container, alignType)
        local leftBottomPos = MyPackage.getPositionOfNode(container, display.LEFT_BOTTOM)
        local posX = pos.x - leftBottomPos.x
        local posY = pos.y - leftBottomPos.y
        display.align(sprite, alignType, posX, posY)
        container:addChild(sprite)
    end
end
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这