微信登录android原生接入详解

接入流程

  1. sdk 下载 将 wechat-sdk-android-with-mta-1.0.2.jar包文件放到 lib目录下面
  2. 注册 sdkapi 在AppActivity添加方法regToWx
public static final String APP_ID = "xxxxxxxxxxxxxxx";
    public static final String  APP_SECRET =  "yyyyyyyyyyyyyyyyyyyyyyy";
	public  static IWXAPI api;
    public static AppActivity app = null;
	private  void regToWx(){
		
        api = WXAPIFactory.createWXAPI(this,APP_ID,true);
		api.registerApp(APP_ID);
	}

在oncreate 中调用regToWx方法

  1. 发送授权请求 在AppActivity添加方法 sendAuthRequest
public static void  sendAuthRequest()
	{
		final SendAuth.Req req = new SendAuth.Req();
		req.scope = "snsapi_userinfo";
		req.state = "wechat_sdk_demo_test";
		boolean  re = api.sendReq(req);
        Log.v("sendAuthRequest",String.valueOf(re));
       
	}

该方法是静态方法 方便在js中调用

  1. 添加授权回调文件 WXEntryActivity 这个文件一定要在包名对应的目录下的wxapi 文件夹下面包名修改在manifest 文件package 项,同时在manifest文件添加activity
<activity android:name="com.gbank.android.wxapi.WXEntryActivity"
                  android:exported="true" 
                  android:launchMode="singleTop"
                  android:theme="@android:style/Theme.NoDisplay">
</activity>

WXEntryActivity.java 文件内容详见代码

  1. 授权回调方法成功后接受 code
public void onResp(BaseResp baseResp) {//授权回调
        if (baseResp != null) {
            resp = baseResp;
            code = ((SendAuth.Resp) baseResp).code; 
            Log.v(TAG,"code"+code);
            
        }
    }
  1. 通过code 获取access_token
private void getAccessToken(final String code) {//获取access_token

        String serverURL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="
                + AppActivity.APP_ID //
                + "&secret="
                + AppActivity.APP_SECRET //
                + "&code="
                + code
                + "&grant_type=authorization_code";
       
        JSONObject jsonResult = null;
        String result = null;
        HttpGet httpRequest = new HttpGet(serverURL);
        try { 
                HttpResponse httpResponse = new DefaultHttpClient().execute(httpRequest);// 发出http请求
                if (httpResponse.getStatusLine().getStatusCode() == 200)
                {   
                    
                    result = EntityUtils.toString(httpResponse.getEntity());// 获取相应的字符串

                    jsonResult = new JSONObject(result);
                    
                    if (null != jsonResult) {
                        String openid = jsonResult.getString("openid").toString().trim();
                        String access_token = jsonResult.getString("access_token").toString().trim();
                        Log.i(TAG, "openid = " + openid);
                        Log.i(TAG, "access_token = " + access_token);
                        

                    }

                }
            
        } catch (IOException e) {  
            e.printStackTrace();  
        } catch (JSONException e) {
            e.printStackTrace();
        }  
        
                
    }

拿到token 后与服务器做验证即可

注意事项

  1. 更换 APP_ID 和 APP_SECRET 在 AppActivity.java 文件中
  2. 修改开放平台签名要用 Gen_Signature_Android2.apk工具获取二次签名串.工具在工程目录下
  3. 修改包名一定要更改WXEntryActivity.java 文件路径与之对应
  4. 游戏中调用方法已经封装在NativeWrapper.js 里面了放调用。代码示例:
let NativeWrapper = require("NativeWrapper");
NativeWrapper.sendAuthRequest();


需要源码的发邮件我

mysql c++connector 连接

mysql现在应用应该是很广泛的,网络的应用几乎离不开它,毕竟不要钱呵呵,我们今天主要介绍一下mysql 的c++connector的连接。

1. connector选择

在选择connector之前我们需要先选择我们使用的mysql版本,本人建议是用5.7,这个版本比较稳定支持功能也相对多些,尤其是一些编码问题的改进。安装好myql建好数据库字符编码选择utf8.

这时候我们选择connector来连接我们的数据库了,connector官网下载地址 最新版本是1.1.8,这个版本就是依赖于庞大的boost库的智能指针,如果你的工程本身用boost库的花,那么问题不大,要不是为了这个库还专门依赖这么大个库,有点得不偿失了,当然技术牛人可以自己封装也不是不可,那么还有另外的一条路就是现在早先的版本1.0.5这个。然后自己和mysql库编译一新的connector库也是可以用的。

2.connector的使用

我们在自己的工程中配好include库和连接库的路径就可以开搞了。下面给出代码:

[cpp]
#include <cppconn/exception.h>
#include <cppconn/resultset.h>
#include <cppconn/statement.h>
#include <cppconn/prepared_statement.h>

#define EXAMPLE_HOST "localhost"
#define EXAMPLE_USER "root"
#define EXAMPLE_PASS ""
#define EXAMPLE_DB "music_"
void showSqlError(sql::SQLException &e)
{
/*
MySQL Connector/C++ throws three different exceptions:

– sql::MethodNotImplementedException (derived from sql::SQLException)
– sql::InvalidArgumentException (derived from sql::SQLException)
– sql::SQLException (derived from std::runtime_error)
*/
std::cout << "# ERR: SQLException in " << __FILE__;
std::cout << "(" << __FUNCTION__ << ") on line " << __LINE__ << std::endl;
/* what() (derived from std::runtime_error) fetches error message */
std::cout << "# ERR: " << e.what();
std::cout << " (MySQL error code: " << e.getErrorCode();
std::cout << ", SQLState: " << e.getSQLState() << " )" << std::endl;
}
int main()
{
std::unique_ptr<sql::Connection> m_con;
std::string url(EXAMPLE_HOST);
const std::string user(EXAMPLE_USER);
const std::string pass(EXAMPLE_PASS);
const std::string database(EXAMPLE_DB);

try {
sql::Driver* driver = get_driver_instance();
m_con.reset(driver->connect(url, user, pass));
m_con->setSchema(database);

}
catch (sql::SQLException &e) {

showSqlError(e);

}

return 0;
}
[/cpp]

现在我们已经连接上数据库了,下面就是对数据库的操作

[cpp]
std::unique_ptr<sql::Statement> stmt ( m_con->createStatement());
stmt->execute("USE " EXAMPLE_DB);
stmt->execute("DROP TABLE IF EXISTS test");
stmt->execute("CREATE TABLE test(id INT, label CHAR(1))");
stmt->execute("INSERT INTO test(id, label) VALUES (1, ‘a’)");
[/cpp]

接下来就是sql::prepareStatement的使用了,可以方便传参了

[cpp]

std::unique_ptr<sql::PreparedStatement> pstmt(m_con->prepareStatement("update m_linkage set name = ? where linkageid = ?"));

pstmt->setString(1, "名字");
pstmt->setInt(2, 1);
pstmt->execute();
[/cpp]

3.注意:

在使用存储过程的时候要插入中文字符的时候相应的参数字段要设置字符集为utf8,数据库字段也要设置成utf8,切记切记。

 

 

 

lua 使用 protocolbuffer

protocolbuffer 简介

protocol buffer是google的一个开源项目,它是用于结构化数据串行化的灵活、高效、自动的方法,例如XML,不过它比xml更小、更快、也更简单。据测试比json还要快很多,你可以定义自己的数据结构,然后使用代码生成器生成的代码来读写这个数据结构。你甚至可以在无需重新部署程序的情况下更新数据结构,具体语法请参考Protocol Buffer使用简介

在lua中使用protocolbuffer

接下来我们进入正题,就是在lua中使用protocolbuffer,为了我让我们的游戏开发更快速,与服务器交互更方便,我们将在游戏lua脚本中使用protocolbuffer,但是google的protocolbuffer原生项目并不支持lua,但是我们有更多的牛人帮我实现了相关的支持,protoc-gen-lua是一个开源项目,这个是下载地址;下面就介绍怎么来使用protoc-gen-lua让我们的lua程序来使用pb。

1.安装

首先要配置环境,要安装python2.7下载地址:https://www.python.org/downloads/release/python-2713/

然后安装protocolbuffer 2.6 for  python下载地址:https://github.com/google/protobuf/releases具体的方法在此就不细说了

2.使用protoc-gen-lua

protoc-gen-lua目录下面有个plugin目录protoc-gen-lua和plugin_pb2.py是用来生成protocol的lua文件的

在win32下面:

先编写一个protoc-gen-lua.bat文件里面写上@python “%~dp0protoc-gen-lua”

我们可以 protoc.exe –lua_out=./ –plugin=protoc-gen-lua=”protoc-gen-lua.bat” PublicStruct.proto 命令来生成

在linux下面:

添加一个硬连 l protoc-gen-lua in your $PATH:

$cd /usr/local/bin && sudo ln -s /path/to/protoc-gen-lua/plugin/protoc-gen-lua

然后使用如下命令:

protoc --lua_out=./ PublicStruct.proto

生成PublicStruct_pb.lua的文件就是我们需要的

3.简单示例

你有一个protoc文件是这样的

person.proto :

[lua]
message Person {
required int32 id = 1;
required string name = 2;
optional string email = 3;
}
[/lua]

编译生成person_pb.lua 文件
然后在我们的lua工程中包含 protobuf 和c/c++工程中包含pb.c
在lua中使用我们的person_pb文件

 

[lua]
require "person_pb"

— Serialize Example
local msg = person_pb.Person()
msg.id = 100
msg.name = "foo"
msg.email = "bar"
local pb_data = msg:SerializeToString()

— Parse Example
local msg = person_pb.Person()
msg:ParseFromString(pb_data)
print(msg.id, msg.name, msg.email)
[/lua]

好了就这么多了

 

 

shell脚本使用 xcode builder 自动生成越狱包

shell脚本在自动化处理上还是很有用处的,我接下来就是用shell脚本语言来实现一个自动生成越狱渠道的包的程序,这里有个一个shell学习的网站具体语法可以参考这里,shell脚本攻略

这里我主要用shell基本调用苹果提供的打包工具xcode builder,如果没有安装的时需要提前安装一下的,使用builder是用到一下xcode环境变量的课参考这里我的个人百科的一个页面xcode 环境变量

[shell]
#!/bin/bash

#编译模式 Debug Release
MODE="Release"

#工程根路径
MY_PROJECT_DIR="/Users/zhc/RD/wow_yueyu_platform/frameworks/runtime-src/proj.ios_mac/"
#project tag name
PROJECT_TAG_NAME="MSBL-Mobile"

#目标工程名字数组
array_obj_project_name[0]="MSBL.35.xcodeproj" # xx助手(果盘)
array_obj_project_name[1]="MSBL.15.xcodeproj" # 快用
array_obj_project_name[2]="MSBL.30.xcodeproj" # xy助手
array_obj_project_name[3]="MSBL.17.xcodeproj" # 海马助手
array_obj_project_name[4]="MSBL.04.xcodeproj" # itools
array_obj_project_name[5]="MSBL.20.xcodeproj" # 爱思
array_obj_project_name[6]="MSBL.02.xcodeproj" # 同步推

#渠道ipa名字数组
array_channel_name[0]="xxzhushou" # xx助手(果盘)
array_channel_name[1]="kuaiyong" # 快用
array_channel_name[2]="xyzhushou" # xy助手
array_channel_name[3]="haimazhushou" # 海马助手
array_channel_name[4]="itools" # itools
array_channel_name[5]="aisi" # 爱思
array_channel_name[6]="tongbutui" # 同步推

#生成app–ipa root 路径

IPA_PATH="/Users/zhc/Desktop/yueyuipa/"

echo ${#array_channel_name[*]}

for ((i=1;i&lt;=${#array_channel_name[*]}-1;i++))
do
echo ${array_channel_name[i-1]}

if [ ! -d "${IPA_PATH}${array_channel_name[i-1]}" ]; then
mkdir -p "${IPA_PATH}${array_channel_name[i-1]}"
fi
echo "编译COCOS工程 start"
xcodebuild -project "${MY_PROJECT_DIR}${array_obj_project_name[i-1]}" -target "${PROJECT_TAG_NAME}" CONFIGURATION_BUILD_DIR="${IPA_PATH}${array_channel_name[i-1]}" EXECUTABLE_NAME="msbl" -configuration "${MODE}" clean build
echo "编译COCOS工程 end"

if [ ! -d "${IPA_PATH}${array_channel_name[i-1]}/build/Payload" ]; then
mkdir -p "${IPA_PATH}${array_channel_name[i-1]}/build/Payload"
fi
echo "${IPA_PATH}${array_channel_name[i-1]}/build/Payload/"
cd "${IPA_PATH}${array_channel_name[i-1]}"
echo "${IPA_PATH}${array_channel_name[i-1]}/msbl.app"
pwd
cp -r "${IPA_PATH}${array_channel_name[i-1]}"/msbl.app "${IPA_PATH}${array_channel_name[i-1]}"/build/Payload/
cd "${IPA_PATH}${array_channel_name[i-1]}/build/"
#用zip命令生成ipa
zip -r "${IPA_PATH}${array_channel_name[i-1]}.ipa" "Payload"
done
[/shell]

是不是很简单

注意:运行脚本的的时候一定要是管理员权限和脚本文件是否为可执行,否则会不错提示找不到各种命令。

 

 

lua onlyread table 的实现 与遍历

只读表的实现

游戏开发中有时候我们需要实现一个table初始化过后,就只能访问其值却不能修改其内容,采用代理思想很容易实现一个只读table,我们需要做得只是当我们监控到企图修改表时候抛出错误。通过__index metamethod,我们可以不使用函数而是用原始表本身来使用表,因为我们不需要监控查寻。这是比较简单并且高效的重定向所有查询到原始表的方法。但是,这种用法要求每一个只读代理有一个单独的新的metatable,使用__index指向原始表;lua程序设计给出了实现:

[lua]
function readOnly (t)
local proxy = {}
local mt = { — create metatable
__index = t,
__newindex = function (t,k,v)
error("attempt to update a read-only table", 2)
end
}

setmetatable(proxy, mt)
return proxy
end
[/lua]

注:(error的第二个参数2,将错误信息返回给企图执行update的地方)

下面我们测试一下我们的代码:

[lua]
days = readOnly{"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}

print(days[1]) –&gt; Sunday
days[2] = "Noday"
[/lua]

 

输出:

Sunday
lua: readTest.lua:18: attempt to update a read-only table
stack traceback:
[C]: in function ‘error’
readTest.lua:6: in function <readTest.lua:5>
readTest.lua:18: in main chunk
[C]: ?

我们看到,当我们试图去修改只读表时就会抛出错误

但是这样处理后的table我们的迭代器都将失效(pairs, ipairs, next

[lua]
days = readOnly{"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}

print(days[1]) –> Sunday
–days[2] = "Noday"
— prints nothing!
for k,v in pairs(days) do
print(k,v)
end

print(next(days)) — prints nil!
print(#days) — prints "0"!
[/lua]

输出:

Sunday
nil
0

可以看到我们现在无法遍历只读表的元素了接下来我没就来讨论怎么去遍历我们只读表

如何遍历只读表

我们想重新遍历只读表,只能重写next()方法了 ,重新用心的next()定义pairs()还要的对readOnly方法做点小的修改,下面改成全部代码:

[lua]
rawnext = next
function next(t,k) –重定义next
local m = getmetatable(t)
local n = m and m.__next or rawnext –添加__next
return n(t,k)
end
function readOnly (t)
local proxy = {}
local mt = { — create metatable
__index = t,
__newindex = function (t,k,v)
error("attempt to update a read-only table", 2)
end,
__next = function(tt, k) –添加__next元表实现
return next(t, k)
end
}

setmetatable(proxy, mt)
return proxy
end
days = readOnly{Sunday = 1, Monday = 2, Tuesday = 3, Wednesday = 4,
Thursday = 5, Friday = 6, Saturday = 7}

print(days["Sunday"]) –> 1
–days["Tuesday"] = 3

function pair (t)
return next, t, nil
end
function pairs(t) return next, t, nil end –重定义pairs
–for k,v in next, days do print(k,v) end
for k ,v in pairs(days) do

print(k..v)
end
[/lua]

输出:

1
Saturday7
Tuesday3
Wednesday4
Friday6
Sunday1
Thursday5
Monday2

可以看到我么可以用pairs()重新遍历我们的只读表了

更多参考Generalized Pairs And Ipairs

lua debug.sethook 使用

debug.getinfo简介

因为debug.sethook和debug.getinfo一般都是组合起来使用的,在使用之前我们先来介绍一下debug.getinfo

getinfo(optional thread, function or stack level, optional flag):

该函数返回有关功能的信息表。可以直接给该函数,或者可以给一个号码作为函数的值,这意味着该函数在给定线程的调用堆栈的水平仪功能运行:0级是当前功能(程序getinfo本身); 1级是所谓getInfo函数等等。如果函数比当前功能的数大一些,然后getinfo返回nil。下面一段代码:

[lua]
local funcInfo = debug.getinfo(1)
for k,v in pairs(funcInfo) do
print("key:"..tostring(k).."——value:"..tostring(v))
end

[/lua]

输出:
key:nups——value:0
key:what——value:main
key:func——value:function: 0074BCF0
key:lastlinedefined——value:0
key:source——value:@test_commad.lua
key:currentline——value:1
key:namewhat——value:
key:linedefined——value:0
key:short_src——value:test_commad.lua

返回信息表的字段含义是:

  1. source:标明函数被定义的地方。如果函数在一个字符串内被定义(通过loadstring),source就是那个字符串。如果函数在一个文件中定义,source是@加上文件名。
  2. short_src:source的简短版本(最多60个字符),记录一些有用的错误信息。
  3. linedefined,source中函数被定义之处的行号。
  4. what:标明函数类型。如果foo是一个普通得Lua函数,结果为 “Lua”;如果是一个C函数,结果为 “C”;如果是一个Lua的主chunk,结果为 “main”。
  5. name:函数的合理名称。
  6. namewhat:上一个字段代表的含义。这个字段的取值可能为:W”global”、”local”、”method”、”field”,或者 “”(空字符串)。空字符串意味着Lua没有找到这个函数名。
  7. nups:函数的upvalues的个数。
  8. func:函数本身
  9. currentline:当前函数行号

这个函数的效率比较低,所以尽量使用时只获取我们需要的字段,我们可以通过第二个参数来控制

  • ‘n’    selects fields name and namewhat
  • ‘f’    selects field func
  • ‘S’    selects fields source, short_src, what, and linedefined
  • ‘l’    selects field currentline
  • ‘u’    selects field nup

这个参数是字符串,每一个字母代表一种类型的信息,可以组合在一起使用列如:

[lua]
local funcInfo = debug.getinfo(1,"fl")
for k,v in pairs(funcInfo) do
print("key:"..tostring(k).."——value:"..tostring(v))
end
[/lua]

输出:
key:currentline——value:1
key:func——value:function: 0059BD10

下面我们研究debug.sethook

debug.sethook的使用

而debug.sethook在lua程序设计一书中是这样描述的:debug库的hook是这样一种机制:注册一个函数,用来在程序运行中某一事件到达时被调用。有四种可以触发一个hook的事件:当Lua调用一个函数的时候call事件发生;每次函数返回的时候,return事件发生;Lua开始执行代码的新行时候,line事件发生;运行指定数目的指令之后,count事件发 生。Lua使用单个参数调用hooks,参数为一个描述产生调用的事件:”call”、”return”、”line” 或 “count”。另外,对于line事件,还可以传递第二个参数:新行号。我们在一个hook内总是可以使用debug.getinfo获取更多的信息。

使用带有两个或者三个参数的debug.sethook 函数来注册一个hook:第一个参数是hook函数;第二个参数是一个描述我们打算监控的事件的字符串;可选的第三个参数是一个数字,描述我们打算获取count事件的频率。为了监控call、return和line事件,可以将他们的第一个字母(’c’、’r’ 或 ‘l’)组合成一个mask字符串即可。要想关掉hooks,只需要不带参数地调用sethook即可

[lua]
function trace (event, line)
local s = debug.getinfo(2).short_src
print(s .. ":" .. line)
end
debug.sethook(trace, "l")
print("hi")
[/lua]

输出:
test_commad.lua:8
hi

我们注册一个trace函数来监听执行新行的事件,当print(“hi”)执行时就会触发trace函数打印出print的行号,如果是简体call或者是return没有第二参数的

利用这个特性我们监控任何一个函数的执行情况

[lua]
local funcTab= {}
function testf(event,exarg)
–print("event—-&amp;gt;"..event)
local func = debug.getinfo(2).func
if funcTab[func] then
funcTab[func].callcount = funcTab[func].callcount +1
end
end

function myfunction ()
print()
return 10
end
funcTab[myfunction] = {}
funcTab[myfunction]["name"] = "myfunction"
funcTab[myfunction]["callcount"] = 0
debug.sethook(testf,"c")–注册hook
myfunction()
myfunction()
debug.sethook(nil)–关闭hook
for k,v in pairs(funcTab) do
print(v.name)
print(v.callcount)
end
[/lua]

输出:
myfunction
2

表示myfunction被执行了两次

funcTab是用来记录我们要监视的函数的列表,利用这个特性,我们很容易就扩展出一个profiler工具,代码就不解释了。

简单封装一个lua的cocos scene 管理类

cocos2d-x底层封装了scene管理器,但是我们使用起来会有些限制,尤其是采用多场景切换的游戏中,我们为了更好更方便地开发游戏,所以要在其上面多封装一些东西,这样使用起来就能得心应手了。

cocos 提供的一写场景的接口

场景操作接口是通过Director 类实现的其相关函数如下:

  • runWhithScene(scene) 该函数可以运行场景。只能在启动第一个场景的时候调用该函数,如果有一个场景则不能再调用该函数。
  • replaceScene(scene) 替换场景,用一个新的场景替换当前场景,当前场景会被释放掉。
  • pushScene(scene) 切换场景,将当前的场景挂起,压入场景堆栈中,然后切换到新的场景中。
  • popScene() 与pushScene()相反,弹出当前场景并释放掉,切换到上个被挂起的场景。
  • popToRootScene() 弹出所有的根场景以外的场景,回到根场景。被弹出的场景都将释放掉。
  • popToSceneStackLevel(level) 弹出场景到某一层级。
  • getRunningScene() 等到当前正在运行的场景。

下面我基于这个接口封装一个我更好用的scene 类

基础变量的定义

新建scenefun.lua 的文件头部加入如下

[lua]
module(…,package.seeall) –定义为一个模块类
local director = cc.Director:getInstance()
local curScene = nil; –当前场景
local newSceneCallback = nil; –切入新场景,外部设置的回调函数
local sceneTab = {} –场景名称管理table
[/lua]

sceneTab操作

主要是维护好sceneTab 这个Table 方便我们查找和操作场景

[lua]
function addItemToTable(name)
table.insert(sceneTab,name) — 添加到尾部
end
function removeItemsByIndex(index)– index以上的都被移除

for i = #sceneTab , index +1,-1 do —
table.remove(sceneTab,i)
end
end
function removeLatsItem()–移除最后一个
table.remove(sceneTab)
end
function getItemPosByName(name)
local index = 1
for i =1, #sceneTab do
if name == sceneTab[i] then
return index
end
index = index + 1
end
return 0
end

function replaceTheLastItem(name)
if #sceneTab == 0 then
sceneTab[1] = name
else
sceneTab[#sceneTab] = name
end
end
function getSceneNameByIndex(index)
return sceneTab[index]
end
function getLastSceneIndex()
return #sceneTab
end
function popToTheSceneByName(sceneName)
local sceneIndex = getItemPosByName(sceneName)
if sceneIndex ~= 0 then
popToSceneStackLevel(sceneIndex)

else
print(string.format("no have %s",sceneName))
end
end

—设置当前场景
function setCurScene(scene)
curScene = scene;
local curSceneName =scene:getName()
end

—取的当前场景
function getCurScene()
return curScene;
end

—push新场景
function pushScene(newScene)
director:pushScene(newScene);
addItemToTable( getCurScene():getName())
end

—弹出场景gFun.scene.popScene()
function popScene()
director:popScene()
removeLatsItem()
end

—弹到指定层级 gFun.scene.popToSceneStackLevel()
function popToSceneStackLevel(level)
director:popToSceneStackLevel(level)
removeItemsByIndex(level)
end
–popToRootScene();
function popToRootScene() — 弹出到root scene

director:popToRootScene();
removeItemsByIndex(1)

end
—replace新场景
function replaceScene(newScene)
director:replaceScene(newScene);
replaceTheLastItem( getCurScene():getName())
end
[/lua]

使用方法

[lua]
gFun.scene=require("common/sceneFun")
gFun.scene.popToRootScene()
[/lua]

场景的操作以后统一使用封装后的接口,方便统一管理

用qt读写操作cocos studio UI数据文件(xml)

qt是一个功能很强大的c++库,其中就支持xml文件的读写,今天我们就Dom的方式来读写cocos studio 的ui数据文件(还有一种SAX方式)
cocos studio ui数据文件(及时.csd格式的)就是一个标准的xml文档,其描述的cocos2d-x引擎中的所用ui元素及其相关属性,我们就用qt读取并操作csd文件,将所有的text UI元素都变成有黑色描边像素为2的字体
下面展示一个目标文件
[xml]
<GameProjectFile>
<PropertyGroup Type="Layer" Name="l_rolename" ID="f6121bb9-8449-4d10-a3ec-ac853464229a" Version="2.3.2.3" />
<Content ctype="GameProjectContent">
<Content>
<Animation Duration="0" Speed="1.0000" />
<ObjectData Name="Layer" Tag="4278" ctype="GameLayerObjectData">
<Size X="72.0000" Y="24.0000" />
<Children>
<AbstractNodeData Name="ListView" ActionTag="1039694078" Tag="294" IconVisible="False" RightMargin="-128.0000" TopMargin="-8.0000" BackColorAlpha="102" ColorAngle="90.0000" ScrollDirectionType="0" ctype="ListViewObjectData">
<Size X="200.0000" Y="32.0000" />
<Children>
<AbstractNodeData Name="Text_Name" ActionTag="-1681367417" Tag="4279" IconVisible="False" RightMargin="152.0000" TopMargin="12.0000" FontSize="24" LabelText="名字" HorizontalAlignmentType="HT_Center" VerticalAlignmentType="VT_Bottom" OutlineSize="2" OutlineEnabled="True" ShadowOffsetX="0.0000" ShadowOffsetY="0.0000" ctype="TextObjectData">
<Size X="50.0000" Y="32.0000" />
<AnchorPoint />
<Position />
<Scale ScaleX="1.0000" ScaleY="1.0000" />
<CColor A="255" R="255" G="255" B="255" />
<PrePosition />
<PreSize X="0.0000" Y="0.0000" />
<FontResource Type="Normal" Path="fonts/wqyhei.ttf" Plist="" />
<OutlineColor A="255" R="0" G="0" B="0" />
<ShadowColor A="255" R="110" G="110" B="110" />
</AbstractNodeData>
<AbstractNodeData Name="Text_Add" ActionTag="150656575" ZOrder="1" Tag="295" IconVisible="False" LeftMargin="48.0000" RightMargin="134.0000" TopMargin="1.0000" FontSize="24" LabelText="+" OutlineSize="2" OutlineEnabled="True" ShadowOffsetX="0.0000" ShadowOffsetY="0.0000" ctype="TextObjectData"><!– 目标ui元素 TextObjectData–>
<Size X="18.0000" Y="32.0000" />
<AnchorPoint ScaleX="0.5000" ScaleY="0.5000" />
<Position X="59.0000" Y="16.0000" />
<Scale ScaleX="1.0000" ScaleY="1.0000" />
<CColor A="255" R="255" G="255" B="255" />
<PrePosition X="0.2850" Y="0.4875" />
<PreSize X="0.0000" Y="0.0000" />
<FontResource Type="Normal" Path="fonts/wqyhei.ttf" Plist="" />
<OutlineColor A="255" R="0" G="0" B="0" />
<ShadowColor A="255" R="110" G="110" B="110" />
</AbstractNodeData>
<AbstractNodeData Name="Text_Stage" ActionTag="-1707735990" ZOrder="2" Tag="4280" IconVisible="False" LeftMargin="66.0000" RightMargin="119.0000" TopMargin="8.0000" FontSize="27" LabelText="1" HorizontalAlignmentType="HT_Center" VerticalAlignmentType="VT_Center" OutlineSize="2" OutlineEnabled="True" ShadowOffsetX="0.0000" ShadowOffsetY="0.0000" ctype="TextObjectData">
<Size X="13.0000" Y="35.0000" />
<AnchorPoint />
<Position X="68.0000" Y="-3.0000" />
<Scale ScaleX="1.0000" ScaleY="1.0000" />
<CColor A="255" R="255" G="255" B="255" />
<PrePosition X="0.3300" />
<PreSize X="0.0000" Y="0.0000" />
<FontResource Type="Normal" Path="fonts/wqyhei.ttf" Plist="" />
<OutlineColor A="255" R="0" G="0" B="0" />
<ShadowColor A="255" R="110" G="110" B="110" />
</AbstractNodeData>
</Children>
<AnchorPoint />
<Position />
<Scale ScaleX="1.0000" ScaleY="1.0000" />
<CColor A="255" R="255" G="255" B="255" />
<PrePosition />
<PreSize X="2.7778" Y="1.3333" />
<SingleColor A="255" R="150" G="150" B="255" />
<FirstColor A="255" R="150" G="150" B="255" />
<EndColor A="255" R="255" G="255" B="255" />
<ColorVector ScaleY="1.0000" />
</AbstractNodeData>
</Children>
</ObjectData>
</Content>
</Content>
</GameProjectFile>
[/xml]

在这段文本中 ctype=”TextObjectData” 的元素的就是我们的要找到并修改的ui元素

我们建个qt ui 工程。在.pro文件中添加 QT += xml 引入xml解析库

[cpp]
#include"mainwindow.h"
#include"ui_mainwindow.h"
MainWindow::MainWindow(QWidget*parent):
QMainWindow(parent),
ui(newUi::MainWindow)
{
ui->setupUi(this);
this->ShowFiles(1,"D:/qtworkspace/change_text_outline/cocosstudio"); //传入所有csd文件路径
}

MainWindow::~MainWindow()
{
deleteui;
}

boolMainWindow::matchLine(QStringpath)
{
bool ret=false;
QFile file(path);
if(!file.open(QIODevice::ReadWrite|QIODevice::Text))
{
return1;
}

QTextStream in(&file);//文本流操作

QString line=in.readAll();//读取到字符串
QDomDocument document;//dom 对象
QString strError;
int errLin=0,errCol=0;
if(!document.setContent(line,false,&strError,&errLin,&errCol))//设置dom内容,并做失败处理
{
printf("parsefilefailedatline%dcolumn%d,error:%s!\n",errLin,errCol,strError);
return false;
}

if(document.isNull())
{
printf("documentisnull!\n");
return false;
}

QDomElement root=document.documentElement();

this->changeItem(root);

QFile f(path);//重新打开文件将更改后的文本写入
if(!f.open(QFile::WriteOnly|QFile::Text))
return false;
QTextStream out(&f);
document.save(out,4);//写入保存
f.close();

file.close();
return ret;
}
voidMainWindow::changeItem(QDomElement&elementRef)//查找目标节点并更改属性
{
QDomElement element=elementRef.firstChildElement();//获取该节点的第一个子节点
while(!element.isNull()){//遍历子节点
if(element.hasAttribute("ctype"))
{
if(element.attributeNode("ctype").value().toStdString()=="TextObjectData") //找到ctype == TextObjectData的节点
{
this->isChange=true;
qDebug()<<element.attributeNode("ctype").value().toStdString().c_str();
element.setAttribute("OutlineSize",2);
element.setAttribute("OutlineEnabled","True");
QDomElement nodec=element.namedItem("OutlineColor").toElement(); //获取到描边属性节点
if(!nodec.isNull())
{
nodec.setAttribute("A",255); //设置描边颜色
nodec.setAttribute("R",0);
nodec.setAttribute("B",0);
nodec.setAttribute("G",0);
}
}
}
this->changeItem(element);//因为ui控有可能是嵌套的所以递归处理子节点
element=element.nextSiblingElement();
}

}
voidMainWindow::ShowFiles(intlevel,constQString&path)//递归遍历文件夹及其下面的文件
{
//这个函数可以执行任何任务,
//这里只是简单地输出各个文件(夹)的名字
QDirdir(path);
QStringListlist;
QStringList::Iteratoriter;
QStringtemp(level,’-‘);

list=dir.entryList(QDir::Dirs,QDir::Name);
for(iter=list.begin();iter!=list.end();++iter)
{
if("."==*iter||
".."==*iter)
continue;
//qDebug()<<temp+*iter+"(folder)";
ShowFiles(level+1,path+"/"+*iter);
}

list=dir.entryList(QDir::Files,QDir::Name);
for(iter=list.begin();iter!=list.end();++iter)
{

QStringtempstr=path+"/"+*iter;
//this->labelOut->setText(tempstr);

//this->labelOut->setText(this->strOut);
if(tempstr.right(3)=="csd")
{
this->isChange=false;
matchLine(tempstr);
if(this->isChange)
{
qDebug()<<temp+path+"/"+*iter;
}
}
}
}

[/cpp]
这样我们就很方便的把所有的csd文件做了处理。理论上我们可以做很多处理,比如我们和以自动生成一些我们想要的ui模板等等,我们可以尽情的发挥想象了

cocos2d-x 3.7 实现图片按钮 过滤点击透明区域 在lua中使用

首先在widget头文件添加:
[cpp]
virtual bool AlphaTouchCheck(const Vec2 &point);
virtual bool getAlphaTouchEnable();
virtual void setAlphaTouchEnable(bool isAlphaTouch);
bool _isAlphaTouchEnable = true;
[/cpp]
在widget cpp文件中简单实现
[cpp]
bool Widget::AlphaTouchCheck(const Vec2 &point)
{
return true;
}

bool Widget::getAlphaTouchEnable()
{
return _isAlphaTouchEnable;
}

void Widget::setAlphaTouchEnable(bool isAlphaTouch)
{
_isAlphaTouchEnable = isAlphaTouch;
}
[/cpp]
widget中的判断先默认返回true

把开启的开关放在widget中,方便以后layout 和 imageview的不规则点击。开关默认关闭

接下来就是imageview中重载AlphaTouchCheck 函数
[cpp]
bool ImageView::AlphaTouchCheck(const Vec2& point)
{
if (getAlphaTouchEnable())
{
Image* normalImage = new Image();
normalImage->initWithImageFile(_normalImageString);
auto data = normalImage->getData();
if (data == NULL)
{
return true;
}
auto locationInNode = this->convertToNodeSpace(point);
//图片的0,0 是左上角 所以要和触摸点的Y转换一下 也就是“(normalImage->getHeight() – (int)(locationInNode.y) – 1)”
//该data值是把二维数组展开成一个一维数组, 因为每个像素值由RGBA组成, 所以每隔4个char为一个RGBA, 并且像素以横向排列
int pa = 4 * ((normalImage->getHeight() – (int)(locationInNode.y) – 1) * normalImage->getWidth() + (int)(locationInNode.x)) + 3;
unsigned int ap = data[pa];
if (ap < 20)
{
CC_SAFE_DELETE(normalImage);
return false;
}
else
{
CC_SAFE_DELETE(normalImage);
return true;
}
}
return true;
}
[/cpp]

std::string _normalImageString 是记录创建时候的图片名。

编译工程
接下来在lua中使用
[lua]
imageButton:addTouchEventListener(function(sender, type)
if type == ccui.TouchEventType.began then
self.beginPos = gFun.scene.getMousePos();
self.effect = true;
if imageButton:AlphaTouchCheck(gFun.scene.getMousePos()) then –传入鼠标点击位置
imageButton:setScale(0.95);
end
elseif type == ccui.TouchEventType.moved then
if getDistance(self.beginPos, gFun.scene.getMousePos()) > 20 then –判断是不是有滑动
self.effect = false;
end
elseif type == ccui.TouchEventType.canceled then

imageButton:setScale(1);
elseif type == ccui.TouchEventType.ended then
if not self.effect then return end

imageButton:setScale(1);
if imageButton:AlphaTouchCheck(gFun.scene.getMousePos()) then –传入鼠标点击位置
–do something;
end
end
end
)

[/lua]

lua 代码调优profile 工具

cocos2d-x lua工程没自带的profile 工具,前段时间项目有好多代码的用时都要测试,所以就自己写了一个,在此贴出,有用的直接拿去,省的重复造车轮

[lua]
module("gProfiles", package.seeall)

local timerTab = {}

function ProfilingBeginTimingBlock(timerName)
if timerTab[timerName] then
timerTab[timerName].lastTime = os.clock()
else
timerTab[timerName] = {
count = 0,
minTime = 99999999999,
maxTime = 0,
totalTime = 0,
lastTime = os.clock(),
name = timerName
}
end

end
function ProfilingEndTimingBlock(timerName)
local data = timerTab[timerName]
if data then

local deltaTime = os.clock() – data.lastTime
data.totalTime = data.totalTime + deltaTime
data.count = data.count + 1
if data.minTime >= deltaTime then
data.minTime = deltaTime
end
if data.maxTime <= deltaTime then
data.maxTime = deltaTime
end
else
error("timerName error!!!")
end

end
function displayTimers()
print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ profiles start ")
for k,v in pairs(timerTab) do
print(tostring(v.name));
print(string.format("%s—%f","count",v.count));
print(string.format("%s—%f","minTime",v.minTime)) ;
print(string.format("%s—%f","maxTime",v.maxTime));
print(string.format("%s—%f","totalTime",v.totalTime));
print(string.format("%s—%f","averageTime",v.totalTime/v.count)) ;
print("———————————————")
end
print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ profiles end ")
end

–dome code
–[[

gProfiles.ProfilingBeginTimingBlock("initRoleList")
self:initRoleList()
gProfiles.ProfilingEndTimingBlock("initRoleList")

gProfiles.displayTimers()
–]]

[/lua]
require() 一下就可以使用了