colorful

zc qq:1337220912

 

cocos2dx使用了第三方库照样移植android平台-解决iconv库的移植问题

http://www.cocoachina.com/bbs/read.php?tid=195310
当我写这篇文章的时候我是怀着激动的心情的,因为我又解决了一个技术问题。你可能对题目还一知半解,这是什么意思,我之所以要写这篇文章就是要解决当我们在cocos2dx中使用了第三方库的时候,移植到android平台的过程中是会报错的问题,典型的例子就是我在上几篇博客中使用了编码转换的库iconv,在我移植到android平台测试的时候就出现了错误,各种各样的错误,网上搜了一下,但是网上的方法感觉都很老了,有的也没说明白,今天通过摸索马上分享给大家,让大家也少走歪路。


如 果你还不会移植android平台,请先看我上一篇的博客,先换个其他的不包含iconv库的工程,移植成功了再来做今天的事情。今天我们不需要准备任何 工具,需要做的就是理解.mk文件的含义,知道怎么改。我们先来看一下我字体和字符编码这篇博客GBKToUTF8的头文件是怎么包含iconv库的。


1
2
3
4
5
#if(CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
#include "iconv\iconv.h"
#else
#include "../../../../libiconv/include/iconv.h"
#endif


如果是win32平台的话就用引擎里边的第三方库,这个iconv库所在的路径是:
E:\cocos2d-x-2.2\cocos2d-x-2.2\cocos2dx\platform\third_party\win32\iconv。但如果是移植到android平台的你需要加上你android平台的库的路径,也就是说你需要先下载好iconv的库,放到一个你的路径,这里我放到的是我引擎的根目录下,所以写的就是上边的代码,大家下去下载这个库,然后按我说的改了代码。然后我们就来看看这个.mk文件改怎么改,我们要修改的是jni目录下的.mk文件,我先截上几张图片,说说里边代码的含义。

上边的这张图片网上有不少的教程都说需要修改,但在我看来根本不是,因为当我在这里加了iconv.h的路径以后编译的时候任然报错,说找不到iconv.h这个文件。所以以后大家也不要改这里,没用的。


上 边的第一张图片看到了划线的地方了吗?这个是我加上去的,你需要改吗?答案是需要的,但是名字可以和我不一样,那名字改成什么样的呢,这得看另一个文件 了,我们等等再说。上面的第二张图片那个划线的地方也是我加上去的,你也需要修改,改成什么也需要看另一个文件。好了现在我们就来说到底看哪个文件。这个文件就是你下载的iconv库的根目录下的Android.mk文件,我再来截张图。

这个是文件中的俩句话,你要和上边我说的改的那俩个地方对照起来改。好了其实就是这么简单,Android.mk文件只需要对照的改上俩个地方就可以了,程序中的那个头文件包含也要修改。现在我们就来导入到工程中构建一下工程吧。在构建的时候也会出现一个问题,我想这个问题的原因可能是因为iconv库里边实现的函数不一样吧,出现的错误的语句是这句:
1
-1 == iconv(iconvH, pin, &strLength, &outbuf, &outLength)

我们需要做如下的修改,就是在pin的前边加个强转,因为Android下函数需要传入的参数是char**,而我们程序中的pin是const char **类型的。


1
-1 == iconv(iconvH, (char **)pin, &strLength, &outbuf, &outLength)

有了以上的这些操作问题就解决了,这里提醒一下大家,在eclipse中构建工程的时候如果可以编译通过了,但是工程中有错误提示(其实是没有错误的,也不知道这个eclipse是怎么回事),大家就重新导入工程一遍,问题就解决了,还有什么问题就给我留言吧。
本帖有小塔原创,转载请注明出处!

android 下添加iconv支持

NDK自带的iconv的 查看 android-ndk-r9d/sources/android/support/include/iconv.h

在Android.mk中加入
LOCAL_WHOLE_STATIC_LIBRARIES += android_support

$(call import-module,android/support)


如:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := mylib
LOCAL_SRC_FILES := mylib.cpp

LOCAL_WHOLE_STATIC_LIBRARIES += android_support
LOCAL_CXXFLAGS += -std=c++11 -fexceptions


include $(BUILD_SHARED_LIBRARY)
$(call import-module, android/support)

posted @ 2014-12-13 15:56 多彩人生 阅读(1271) | 评论 (0)编辑 收藏

如何在真机上调试Android应用程序

     摘要: http://www.cnblogs.com/lanxuezaipiao/archive/2013/03/11/2953564.html1、首先将手机设置为调试模式 方法:设置——应用程序——开发——USB调试,打上√即可     2、用数据线连接至电脑,在电脑上安装豌豆荚,此时豌豆荚会帮你安装...  阅读全文

posted @ 2014-12-12 15:49 多彩人生 阅读(607) | 评论 (0)编辑 收藏

一步一步了解Cocos2dx 3.0 正式版本开发环境搭建(Win32/Android)

http://www.cnblogs.com/ShadowLoki/p/3679929.html

 cocos2d-x 3.0发布有一段时间了,作为一个初学者,我一直觉得cocos2d-x很坑。每个比较大的版本变动,都会有不一样的项目创建方式,每次的跨度都挺大……

  但是凭心而论,3.0RC版本开始 环境搭建、项目创建、编译的方式更加人性化了。

  现在我们进入正题,一步一步搭建cocos2dx的开发环境

  

 

  cocos2d-x很多tools都是需要使用Python的,因此我们需要先安装Python环境

  

  python下载:点这里

    这里需要下载Python 2.X版本。曾经以为要下载3.x版本 后来装上发现cocos2d-x提供的python运行报错,所以卸载以后重新装的python2.X版本

  

  python安装的时候使用默认安装方式即可,但是安装完毕后我们需要配置环境变量

    在我的电脑(右键)->属性->高级系统设置->高级 选项卡->环境变量->系统变量 中找到Path 变量,并在其值中增加 指向Python的环境变量。格式为 [Python安装路径]; 例如我安装在C盘 我的环境变量就新增为 C:\Python27;

    配置完毕后记得注销再重新登陆系统环境变量才会生效

    

 

  下一步,我们要下载最新版本的cocos2d-x,到目前为止 cocos2d-x已经更新到 3.0rc2版本

  

  cocos2d-x官网: 点这里

  

  在下载完毕后我们解压缩到磁盘上,并打开cocos2d-x 文件夹找到setup.py这个文件,运行,会出现控制台如下

  

  由于现在不配置Android环境,因此只需要一直回车即可,这时候这段python脚本会帮助我们设置cocos2d-x运行所需要的环境变量。继续注销……重新登陆……

  在配置完这一切后 我们打开CMD 运行cocos命令,1来检查整个开发环境是否搭建成功,2来可以熟悉下命令行,如果设置没问题的情况下 会出现如下界面

  这里我们可以看到cocos.py这段脚本可以执行4个命令,每个命令的作用已经说明的很清楚了。

 

  下面,我们来使用new 命令创建我们的项目

  

  这里举个栗子,比如我要创建一个叫simple的项目,我们可以这么写 cocos new -p com.game.simple -l cpp -d e:\  simple 简单吧 运行这条命令会在E盘根目录下生成一个叫simple的文件夹里面放着我们的项目。

  

  这个时候我们就可以用VS2012打开proj.win32中的SLN项目文件进行我们愉快的编码和调试工作了。

 

  如果我们要运行我们刚刚由模板生成的项目,我们需要用到cocos run 命令 格式为 cocos run -p [平台]    栗如:cocos run -p win32 表示win32平台

  我们在执行这条命令时候需要注意一点 执行命令的目录 必须是游戏项目的根目录 如果不在根目录 需要使用 -s指定游戏根目录的位置

  

  现在我们在cmd中执行 cocos run -p win32 -s e:\simple 运行结果如下

  

  如果在编译过程中没有错误的话 编译完成会打开这个HelloWorld的DEMO

  至此在WIN32中的环境搭建以及调试发布就全部OK了 下面我们来看Android部分。

  
Android:

  Android的编译环境在3.0RC里也有了很大的改观,部署和编译方式比之前方便了很多。现在我们来做下准备工作。

  Android编译环境需要用到Andorid_SDK  Android_NDK   Ant 与JDK

  Android_SDK下载: 点这里

  Android_NDK下载: 点这里

  Android_JDK下载:  点这里

  Ant下载:         点这里

  

  其中除了JDK需要安装,其它的是绿色版 直接解压即可

    我们将SDK NDK 和ANT解压缩到同一个文件夹中 并修改sdk的文件夹名为ADT,NDK的NDK,ANT的为ANT 这是为了方便以后使用。 我解压后文件都放在了e:\Android文件夹下

  

  

  解压完后我们先来配置JDK

    在JDK安装完毕之后,需要手动进行环境变量的配置

  

     1)在系统变量里新建JAVA_HOME变量,变量值为:C:\Program Files\Java\jdk1.6.0_14(根据自己的安装路径填写)

    2)新建classpath变量,变量值为:.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar (注意前面的. 不要忘记了)

    3)在path变量(已存在不用新建)添加变量值:%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin(注意变量值之间用“;”隔开)

    4、重新注销系统并重新登陆后 “开始”-->“运行”-->输入“javac”-->"Enter",如果能正常打印用法说明配置成功!

    补充环境变量的解析:

      JAVA_HOME:jdk的安装路径

 

      classpath:java加载类路径,只有类在classpath中java命令才能识别,在路径前加了个"."表示当前路径。

 

      path:系统在任何路径下都可以识别java,javac命令。

  

 

  

   现在我们来配置ANT的环境变量。将[ANT目录]\bin文件夹添加到path中  如图

  

 

  至此准备工作就做完了,下面我们运行setup.py关联SDK NDK 和ANT

 

  

  在这里我们输入了ndk和sdk的路径 其中ndk指向根目录即可  而sdk需要指向adt中的sdk目录

  

  在由setup.py添加了这两个环境变量之后 会要求我们输入Ant的路径 这里我们需要指向ant中的bin文件夹 如下:

  

  这时候 基本环境已经搭建完成了,记得注销重新登陆使环境变量生效

   重新运行setup.py确认下所有的配置是否正确 如果全部正确 如图所示

  

 

  然后我们就可以在项目目录下使用 cocos compile -p android方式编译APK文件了

  

  注意:在打包apk之前需要修改proj.android/jni文件夹下的Android.mk文件 添加自己新增的CPP文件

 

  最后我们来看看编译好的APK文件吧,位置在[项目文件夹]\bin\debug\android\文件夹下

  

posted @ 2014-10-15 15:35 多彩人生 阅读(340) | 评论 (0)编辑 收藏

用VS2010编译luabind

http://www.cppblog.com/eros/archive/2009/04/29/81508.aspx

     学了一段时间Lua,显然直接在项目中使用是很不方便,google了一下,似乎大家都对luabind这个lua包装类青睐有加,于是我也随大势想用用看。
     先做好准备工作,下载了luabin 0.8.1源码,Boost 1.3.8源码和lua 5.1.4源码,编译环境是VS2008 SP1。之前在网上看到一些文章说这些开源软件之间的版本依赖比较敏感,可能会有这样那样的问题,动手之前有些惶恐。
     幸运的是,编译过程很顺利,如下:
     1.编译lua 5.1.4
     进入VS2008的命令行工具,定位到lua的源码目录下,执行命令etc\luavs.bat,没什么问题的话很快就可以编译好lua,得到lua51.lib和lua51.dll。
     2.编译luabind
     解压下载回来的luabind压缩包,假设解压到d:\luabind-0.8.1\,Boost解压到d:\boost 1_38_0\,lua解压到d:\lua 5.1.4\
     在 VS中新建一个静态库项目,将d:\luabind-0.8.1\src下的源码全部添加到项目中,然后在项目中新建luabind和luabind \detail\两个虚拟文件夹,对应的将d:\luabind-0.8.1\luabind和d:\luabind-0.8.1\luabind \detail下的文件添加到文件夹中。
     然后为项目添加附加包含目录,右键点击项目节点->属性->配置属性->C\C++标签下,在附加包含目录中填入d: \luabind-0.8.1\;d:\boost 1_38_0\k;d:\lua 5.1.4\src\。然后修改项目字符集为多字节字符集。
     准备就绪,生成项目。在我的环境中编译很顺利,没有出现任何问题,成功后会得到luabind.lib。

     接下来就按照惯例来写一个hello world程序作为使用luabind的第一步。
     在VS中新建一个控制台项目,类型为DLL,命名项目为Hello World,然后键入以下代码:

 1#include "stdafx.h"
 2#include <iostream>
 3#include <luabind/luabind.hpp>
 4
 5void greet()
 6{
 7    std::cout << "hello world!\n";
 8}

 9
10extern "C" int __declspec(dllexport) init(lua_State* L)
11{
12    using namespace luabind;
13
14    open(L);
15
16    module(L)
17    [
18        def("greet"&greet)
19    ];
20
21    return 0;
22}


   注意,在Windows环境下init函数之前是要加 __declspec(dllexport)才能将函数导出的,而luabind的文档中的环境是linux,默认不用加 __declspec(dllexport)也可以导出(就因为这个折腾了我半天才把hello word成功运行)。
   编译项目,(记得将luabind.lib和lua51.lib添加到链接选项中:项目属性->连接器->输入->附加依赖文件,加入luabind.lib和lua51.lib)。
   将hello world.dll放到lua51.dll和lua.exe所在的目录下。
   打开lua命令行,键入:
    
   测试成功,enjoy。

posted @ 2014-09-02 17:56 多彩人生 阅读(356) | 评论 (0)编辑 收藏

常函数与重载

关于常函数
1》常对象只能调用常函数,常函数里面不能对数据成员做出更改,否则编译出错
2》非常对象可以调用常函数
3》常量成员可以在构造函数的成员函数初始化列表初始化。
4》const关键字可以用于参与重载函数的区分。例如:
    void Print();
    void Print() const;
    这两个函数可以用于重载。重载的原则是:常对象调用常成员函数,一般对象调用一般成员函数。
5》在const成员函数中:  
  可以修改被定义成mutable的成员变量
6》函数重载要求编译器能够唯一地确定调用一个函数时应执行哪个函数代码,即采用哪个函数实现。确定函数实现时,要求从函数参数的个数和类型上来区分。这就是说,进行函数重载时,要求同名函数在参数个数上不同,或者参数类型上不同。否则,将无法实现重载。

posted @ 2014-08-29 20:56 多彩人生 阅读(559) | 评论 (0)编辑 收藏

浅析Lua中table的遍历

http://rangercyh.blog.51cto.com/1444712/1032925

当我在工作中使用lua进行开发时,发现在lua中有4种方式遍历一个table,当然,从本质上来说其实都一样,只是形式不同,这四种方式分别是:

  1. for key, value in pairs(tbtest) do  
  2. XXX  
  3. end 
  4.  
  5. for key, value in ipairs(tbtest) do  
  6. XXX  
  7. end 
  8.  
  9. for i=1, #(tbtest) do  
  10.     XXX  
  11. end 
  12.  
  13. for i=1, table.maxn(tbtest) do  
  14.     XXX  
  15. end 

前两种是泛型遍历,后两种是数值型遍历。当然你还会说lua的table遍历还有很多种方法啊,没错,不过最常见的这些遍历确实有必要弄清楚。

这四种方式各有特点,由于在工作中我几乎每天都会使用遍历table的方法,一开始也非常困惑这些方式的不同,一段时间后才渐渐明白,这里我也是把 自己的一点经验告诉大家,对跟我一样的lua初学者也许有些帮助(至少当初我在写的时候在网上就找了很久,不知道是因为大牛们都认为这些很简单,不需要 说,还是因为我笨,连这都要问)。

首先要明确一点,就是lua中table并非像是C/C++中的数组一样是顺序存储的,准确来说lua中的table更加像是C++中的map,通 过Key对应存储Value,但是并非顺序来保存key-value对,而是使用了hash的方式,这样能够更加快速的访问key对应的value,我们 也知道hash表的遍历需要使用所谓的迭代器来进行,同样,lua也有自己的迭代器,就是上面4种遍历方式中的pairs和ipairs遍历。但是lua 同时提供了按照key来遍历的方式(另外两种,实质上是一种),正式因为它提供了这种按key的遍历,才造成了我一开始的困惑,我一度认为lua中关于 table的遍历是按照我table定义key的顺序来的。

下面依次来讲讲四种遍历方式,首先来看for k,v in pairs(tbtest) do这种方式:

先看效果:

  1. tbtest = {  
  2.     [1] = 1,  
  3.     [2] = 2,  
  4.     [3] = 3,  
  5.     [4] = 4,  
  6.  
  7. for key, value in pairs(tbtest) do  
  8.     print(value)  
  9. end 

我认为输出应该是1,2,3,4,实际上的输出是1,2,4,3。我因为这个造成了一个bug,这是后话。

也就是说for k,v in pairs(tbtest) do 这样的遍历顺序并非是tbtest中table的排列顺序,而是根据tbtest中key的hash值排列的顺序来遍历的。

 

当然,同时lua也提供了按照key的大小顺序来遍历的,注意,是大小顺序,仍然不是key定义的顺序,这种遍历方式就是for k,v in ipairs(tbtest) do。

for k,v in ipairs(tbtest) do 这样的循环必须要求tbtest中的key为顺序的,而且必须是从1开始,ipairs只会从1开始按连续的key顺序遍历到key不连续为止。

  1. tbtest = {  
  2. [1] = 1,  
  3. [2] = 2,  
  4. [3] = 3,  
  5. [5] = 5,  
  6.  
  7. for k,v in ipairs(tbtest) do  
  8. print(v)  
  9. end 

只会打印1,2,3。而5则不会显示。

  1. local tbtest = {  
  2. [2] = 2,  
  3. [3] = 3,  
  4. [5] = 5,  
  5.  
  6. for k,v in ipairs(tbtest) do  
  7. print(v)  
  8. end 

这样就一个都不会打印。

 

第三种遍历方式有一种神奇的符号'#',这个符号的作用是是获取table的长度,比如:

  1. tbtest = {  
  2. [1] = 1,  
  3. [2] = 2,  
  4. [3] = 3,  
  5. }  
  6. print(#(tbtest)) 

打印的就是3

  1. tbtest = {  
  2. [1] = 1,  
  3. [2] = 2,  
  4. [6] = 6,  
  5. }  
  6. print(#(tbtest)) 

这样打印的就是2,而且和table内的定义顺序没有关系,无论你是否先定义的key为6的值,‘#’都会查找key为1的值开始。

如果table的定义是这样的:

  1. tbtest = {  
  2. ["a"] = 1,  
  3. [2] = 2,  
  4. [3] = 3,  
  5.  
  6. print(#(tbtest)) 

那么打印的就是0了。因为‘#’没有找到key为1的值。同样:

  1. tbtest = {  
  2. [“a”] = 1,  
  3. [“b”] = 2,  
  4. [“c”] = 3,  
  5. }  
  6. print(#(tbtest)) 

打印的也是0

所以,for i=1, #(tbtest) do这种遍历,只能遍历当tbtest中存在key为1的value时才会出现结果,而且是按照key从1开始依次递增1的顺序来遍历,找到一个递增不是1的时候就结束不再遍历,无论后面是否仍然是顺序的key,比如:

 

table.maxn获取的只针对整数的key,字符串的key是没办法获取到的,比如:

  1. tbtest = {  
  2. [1] = 1,  
  3. [2] = 2,  
  4. [3] = 3,  
  5. }  
  6. print(table.maxn(tbtest)) 
  7.  
  8.  
  9. tbtest = {  
  10. [6] = 6,  
  11. [1] = 1,  
  12. [2] = 2,  
  13. }  
  14. print(table.maxn(tbtest)) 

这样打印的就是3和6,而且和table内的定义顺序没有关系,无论你是否先定义的key为6的值,table.maxn都会获取整数型key中的最大值。

如果table的定义是这样的:

  1. tbtest = {  
  2. ["a"] = 1,  
  3. [2] = 2,  
  4. [3] = 3,  
  5. }  
  6. print(table.maxn(tbtest)) 

那么打印的就是3了。如果table是:

  1. tbtest = {  
  2. [“a”] = 1,  
  3. [“b”] = 2,  
  4. [“c”] = 3,  
  5. }  
  6. print(table.maxn(tbtest))  
  7. print(#(tbtest)) 

那么打印的就全部是0了。

 

 

换句话说,事实上因为lua中table的构造表达式非常灵活,在同一个table中,你可以随意定义各种你想要的内容,比如:

  1. tbtest = {  
  2. [1] = 1,  
  3. [2] = 2,  
  4. [3] = 3,  
  5. ["a"] = 4,  
  6. ["b"] = 5,  

同时由于这个灵活性,你也没有办法获取整个table的长度,其实在coding的过程中,你会发现,你真正想要获取整个table长度的地方几乎没有,你总能采取一种非常巧妙的定义方式,把这种需要获取整个table长度的操作避免掉,比如:

  1. tbtest = {  
  2. tbaaa = {  
  3. [1] = 1,  
  4. [2] = 2,  
  5. [3] = 3,  
  6. },  
  7. ["a"] = 4,  
  8. ["b"] = 5,  

你可能会惊讶,上面这种table该如何遍历呢?

  1. for k, v in pairs(tbtest) do  
  2. print(k, v)  
  3. end 

输出是:a 4 b 5 tbaaa table:XXXXX。

由此你可以看到,其实在table中定义一个table,这个table的名字就是key,对应的内容其实是table的地址。

当然,如果你用

  1. for k, v in ipairs(tbtest) do  
  2. print(k,v)  
  3. end 

来遍历的话,就什么都不会打印,因为没有key为1的值。但当你增加一个key为1的值时,ipairs只会打印那一个值,现在你明白ipairs是如何工作的吧。

既然这里谈到了遍历,就说一下目前看到的几种针对table的遍历方式:

for i=1, #tbtest do --这种方式无法遍历所有的元素,因为'#'只会获取tbtest中从key为1开始的key连续的那几个元素,如果没有key为1,那么这个循环将无法进入

for i=1, table.maxn(tbtest) do --这种方式同样无法遍历所有的元素,因为table.maxn只会获取key为整数中最大的那个数,遍历的元素其实是查找 tbtest[1]~tbtest[整数key中最大值],所以,对于string做key的元素不会去查找,而且这么查找的效率低下,因为如果你整数 key中定义的最大的key是10000,然而10000以下的key没有几个,那么这么遍历会浪费很多时间,因为会从1开始直到10000每一个元素都 会查找一遍,实际上大多数元素都是不存在的,比如:

  1. tbtest = {  
  2. [1] = 1,  
  3. [10000] = 2,  
  4. }  
  5. local count = 0  
  6. for i=1, table.maxn(tbtest) do  
  7. count = count + 1  
  8. print(tbtest[i])  
  9. end  
  10. print(count) 

你会看到打印结果是多么的坑爹,只有1和10000是有意义的,其他的全是nil,而且count是10000。耗时非常久。一般我不这么遍历。但是有一种情况下又必须这么遍历,这个在我的工作中还真的遇到了,这是后话,等讲完了再谈。

  1. for k, v in pairs(tbtest) do 

这个是唯一一种可以保证遍历tbtest中每一个元素的方式,别高兴的太早,这种遍历也有它自身的缺点,就是遍历的顺序不是按照tbtest定义的顺序来遍历的,这个前面讲到过,当然,对于不需要顺序遍历的用法,这个是唯一可靠的遍历方式。

  1. for k, v in ipairs(tbtest) do 

这个只会遍历tbtest中key为整数,而且必须从1开始的那些连续元素,如果没有1开始的key,那么这个遍历是无效的,我个人认为这种遍历方 式完全可以被改造table和for i=1, #(tbtest) do的方式来代替,因为ipairs的效果和'#'的效果,在遍历的时候是类似的,都是按照key的递增1顺序来遍历。

好,再来谈谈为什么我需要使用table.maxn这种非常浪费的方式来遍历,在工作中, 我遇到一个问题,就是需要把当前的周序,转换成对应的奖励,简单来说,就是从一个活动开始算起,每周的奖励都不是固定的,比如1~4周给一种奖励,5~8 周给另一种奖励,或者是一种排名奖励,1~8名给一种奖励,9~16名给另一种奖励,这种情况下,我根据长久的C语言的习惯,会把table定义成这个样 子:

  1. tbtestAward = {  
  2. [8] = 1,  
  3. [16] = 3,  

这个代表,1~8给奖励1,9~16给奖励3。这样定义的好处是奖励我只需要写一次(这里的奖励用数字做了简化,实际上奖励也是一个大的 table,里面还有非常复杂的结构)。然后我就遇到一个问题,即我需要根据周序数,或者是排名序数来确定给哪一种奖励,比如当前周序数是5,那么我应该 给我定义好的key为8的那一档奖励,或者当前周序数是15,那么我应该给奖励3。由此读者看出,其实我定义的key是一个分界,小于这个key而大于上 一个key,那么就给这个key的奖励,这就是我判断的条件。逻辑上没有问题,但是lua的遍历方式却把我狠狠地坑了一把。读者可以自己想一想我上面介绍 的4种遍历方式,该用哪一种来实现我的这种需求呢?这个函数的大致框架如下:

  1. function GetAward(nSeq)  
  2. for 遍历整个奖励表 do  
  3. if 满足key的条件 then  
  4. return 返回对应奖励的key  
  5. end  
  6. end  
  7. return nil  
  8. end 

我也不卖关子了,分别来说一说吧,首先因为我的key不是连续的,而且没有key为1的值,所以ipairs和'#'遍历是没用的。这种情况下理想 的遍历貌似是pairs,因为它会遍历我的每一个元素,但是读者不要忘记了,pairs遍历并非是按照我定义的顺序来遍历,如果我真的使用的条件是:序数 nSeq小于这个key而大于上一个key,那么就返回这个key。那么我无法保证程序执行的正确性,因为key的顺序有可能是乱的,也就是有可能先遍历 到的是key为16的值,然后才是key为8的值。

这么看来我只剩下table.maxn这么一种方式了,于是我写下了这种代码:

  1. for i=1, table.maxn(tbtestAward) do  
  2. if tbtestAward[i] ~= nil then  
  3. if nSeq <= i then  
  4. return i  
  5. end  
  6. end  
  7. end  

这么写效率确实低下,因为实际上还是遍历了从key为1开始直到key为table.maxn中间的每一个值,不过能够满足我上面的要求。当时我是 这么实现的,因为这个奖励表会不断的发生变化,这样我每次修改只需要修改这个奖励表就能够满足要求了,后来我想了想,觉得其实我如果自己再定义一个序数转 换成对应的奖励数种类的表就可以避免这种坑爹的操作了,不过如果奖励发生修改,我需要统一排查的地方就不止这个奖励表了,权衡再三,我还是没有改,就这么 写了。没办法,不断变化的需求已经把我磨练的忘记了程序的最高理想。我甚至愿意牺牲算法的效率而去追求改动的稳定性。在此哀悼程序员的无奈。我这种时间换 空间的做法确实不知道好不好。

后来我在《Programming In Lua》中看到了一个神奇的迭代器,使用它就可以达到我想要的这种遍历方式,而且不需要去遍历那些不存在的key。它的方法是把你所需要遍历的table 里的key按照遍历顺序放到另一个临时的table中去,这样只需要遍历这个临时的table按顺序取出原table中的key就可以了。如下:

首先定义一个迭代器:

  1. function pairsByKeys(t)  
  2.     local a = {}  
  3.     for n in pairs(t) do  
  4.         a[#a+1] = n  
  5.     end  
  6.     table.sort(a)  
  7.     local i = 0  
  8.     return function()  
  9.         i = i + 1  
  10.         return a[i], t[a[i]]  
  11.     end  
  12. end 

然后在遍历的时候使用这个迭代器就可以了,table同上,遍历如下:

  1. for key, value in pairsByKeys(tbtestAward) do  
  2.  if nSeq <= key then  
  3. return key  
  4. end  
  5. end 

并且后来我发现有了这个迭代器,我根本不需要先做一步获取是哪一档次的奖励的操作,直接使用这个迭代器进行发奖就可以了。大师就是大师,我怎么就没想到呢!

还有些话我还没有说,比如上面数值型遍历也并非是像看起来那样进行遍历的,比如下面的遍历:

  1. tbtest = {  
  2.     [1] = 1,  
  3.     [2] = 2,  
  4.     [3] = 3,  
  5.     [5] = 5,  
  6.  
  7. for i=1, #(tbtest) do  
  8.     print(tbtest[i])  
  9. end 

打印的顺序是:1,2,3。不会打印5,因为5已经不在table的数组数据块中了,我估计是被放到了hash数据块中,但是当我修改其中的一些key时,比如:

  1. tbtest = {  
  2.     [1] = 1,  
  3.     [2] = 2,  
  4.     [4] = 4,  
  5.     [5] = 5,  
  6.  
  7. for i=1, #(tbtest) do  
  8.     print(tbtest[i])  
  9. end 

打印的内容却是:1,2,nil,4,5。这个地方又遍历到了中间没有的key值,并且还能继续遍历下去。我最近正在看lua源码中table的实 现部分,已经明白了是怎么回事,不过我想等我能够更加清晰的阐述lua中table的实现过程了再向大家介绍。用我师傅的话说就是不要使用一些未定义的行 为方法,避免在工作中出错,不过工作外,我还是希望能明白未定义的行为中那些必然性,o(︶︿︶)o 唉!因果论的孩子伤不起。等我下一篇博文分析lua源码中table的实现就能够更加清晰的说明这些了。

posted @ 2014-08-25 14:29 多彩人生 阅读(265) | 评论 (0)编辑 收藏

lua require dofile loadfile区别

http://blog.163.com/hbu_lijian/blog/static/126129153201422902256778/

1.dofile与loadfile
dofile 当作Lua运行代码的chunk的一种原始的操作。dofile实际上是一个辅助的函数。真正完成功能的函数是loadfile;与dofile不同的是 loadfile编译代码成中间码并且返回编译后的chunk作为一个函数,而不执行代码;另外loadfile不会抛出错误信息而是返回错误代。我们可 以这样定义dofile:
function dofile (filename)
local f = assert(loadfile(filename))
return f()
end
如 果loadfile失败assert会抛出错误。loadfile更加灵活。在发生错误的情况下,loadfile返回nil和错误信息,这样我们就可以 自定义错误处理。另外,如果我们运行一个文件多次的话,loadfile只需要编译一次,但可多次运行。dofile却每次都要编译。
2.loadstring与loadfile
loadstring与loadfile相似,只不过它不是从文件里读入chunk,而是从一个串中读入。
f = loadstring("i = i + 1")
loadstring 函数功能强大,但使用时需多加小心。确认没有其它简单的解决问题的方法再使用。loadfile和loadstring都不会抛出错误,如果发生错误他们 将返回nil加上错误信息。另外,loadfile和loadstring都不会有边界效应产生,他们仅仅编译chunk成为自己内部实现的一个匿名函 数。通常对他们的误解是他们定义了函数。Lua中的函数定义是发生在运行时的赋值而不是发生在编译时。
loadstring通常用 于运行程序外部的代码,比如运行用户自定义的代码。注意:loadstring期望一个chunk,即语句。如果想要加载表达式,需要在表达式前加 return,那样将返回表达式的值。loadstring返回的函数和普通函数一样,可以多次被调用。
print "enter your expression:"
local l = io.read()
local func = assert(loadstring("return " .. l))
print("the value of your expression is " .. func())
3.require与dofile
。粗略的说require和dofile完成同样的功能但有两点不同:
1. require会搜索目录加载文件
2. require会判断是否文件已经加载避免重复加载同一文件。由于上述特征,require在Lua中是加载库的更好的函数。
require 使用的路径和普通我们看到的路径还有些区别,我们一般见到的路径都是一个目录列表。require的路径是一个模式列表,每一个模式指明一种由虚文件名 (require的参数)转成实文件名的方法。更明确地说,每一个模式是一个包含可选的问号的文件名。匹配的时候Lua会首先将问号用虚文件名替换,然后 看是否有这样的文件存在。如果不存在继续用同样的方法用第二个模式匹配。例如,路径如下:?;?.lua;c:\windows\?;/usr/local/lua/?/?.lua 
调用过程如下:
lili
lili.lua
c:\windows\lili
/usr/local/lua/lili/lili.lua
还会有so/dll文件。
为了确定路径,Lua首先检查全局变量LUA_PATH是否为一个字符串,如果是则认为这个串就是路径;否则require检查环境变量LUA_PATH的值,如果两个都失败require使用固定的路径(典型的"?;?.lua")
一个路径中的模式也可以不包含问号而只是一个固定的路径,比如:?;?.lua;/usr/local/default.lua。这种情况下,require没有匹配的时候就会使用这个固定的文件(当然这个固定的路径必须放在模式列表的最后才有意义)

posted @ 2014-08-22 09:24 多彩人生 阅读(1242) | 评论 (0)编辑 收藏

lua中的require机制

http://blog.chinaunix.net/uid-552961-id-2736410.html

lua中的require机制

    为了方便代码管理,通常会把lua代码分成不同的模块,然后在通过require函数把它们加载进来。
现在看看lua的require的处理流程。

1、require机制相关的数据和函数
    package.path:保存加载外部模块(lua中"模块"和"文件"这两个概念的分界比较含糊,因为这个值在不同的时刻会扮演不同的角色)的搜索 路径,这种路径是"模板式的路径",它里面会包含可替代符号"?",这个符号会被替换,然后lua查找这个文件是否存在,如果存在就会调用其中特定的接 口。典型的值为:
    "./?.lua;./?.lc;/usr/local/?/init.lua"
    如果lua代码中调用:require("hello.world")
    那么lua会依次查找:
    ./hello/world.lua ==>这里"hello.world"变成了"hello/world",并替换了模型"./?.lua"
    ./hello/world.lc
    .....
    (这种处理方式和python类似,只不过不需要__init__.py,也有调用python中的__init__.py)
    package.path在虚拟机启动的时候设置,如果存在环境变量LUA_PATH,那么就用该环境变量作为
    它的值,并把这个环境变量中的";;"替换为luaconf.h中定义的默认值,如果不存在该变量就直接使用
    luaconf.h定义的默认值
    
    package.cpath:作用和packag.path一样,但它是用于加载第三方c库的。它的初始值可以通过环境变量
    LUA_CPATH来设置
    
    package.loadlib(libname, func):相当与手工打开c库libname, 并导出函数func返回,loadlib其实是ll_loadlib
    

2.require的处理流程:
   require(modelname)
   require(在lua中它是ll_require函数)的查找顺序如下:
       a.首先在package.loaded查找modelname,如果该模块已经存在,就直接返回它的值
       b.在package.preload查找modelname, 如果preload存在,那么就把它作为loader,调用loader(L)
       c.根据package.path的模式查找lua库modelname,这个库是通过module函数定义的,对于顶层的lua库,文件名和库名是一 样的而且不需要调用显式地在lua文件中调用module函数(在ll_require函数中可以看到处理方式),也就是说lua会根据lua文件直接完 成一个loader的初始化过程。
       d.根据package.cpath查找c库,这个库是符合lua的一些规范的(export具有一定特征的函数接口),lua先已动态的方式加载该c库,然后在库中查找并调用相应名字的接口,例如:luaopen_hello_world
       e.已第一个"."为分割,将模块名划分为:(main, sub)的形式,根据package.cpath查找main,如果存在,就加载该库并查询相应的接口:luaopen_main_sub,例如:先查找 hello库,并查询luaopen_hello_world接口
       f.得到loder后,用modname作为唯一的参数调用该loader函数。当然参数是通过lua的栈传递的,所以loader的原型必须符合lua的规范:int LUA_FUNC(lua_State *L)
         
       ll_require会将这个loader的返回值符给package.loaded[modelname],如果loader不返回值同时 package.loaded[modelname]不存在时, ll_require就会把package.loaded[modelname]设为true。最后ll_reuqire把package.loaded [modelname]返回给调用者。
    

3.module的处理流程
    module(name, cb1, cb2, ...)
    
    a.如果package.loaded[name]是一个table,那么就把这个table作为一个mod
    b.如果全局变量name是一个table,就把这个全局变量作为一个mod
    c.创建table:t = {[name]=package.loaded[name], ["_NAME"]=name, ["_M"]=t, ["_PACKAGE"]=*name*(删除了最后的".XXXX"部分)}. 如果name是一个以点分割的串,那么得到的mod类似这个样子:
      hello.world==> {["hello"]={["world"]={XXXXXXX}}}
    d.依次调用cbs:
      cb1(mod), cb2(mod),...
      
    e.将当前模块的环境设置为mod,同时把package.loaded[name] = mod    
    

  清楚了lua关于模块的处理,就比较容易理解写lua扩展的细节了^_^。

posted @ 2014-08-21 17:08 多彩人生 阅读(302) | 评论 (0)编辑 收藏

cocos2dx3.2

新建项目
cocos new

编译so文件
cocos compile -p android

posted @ 2014-08-20 21:45 多彩人生 阅读(214) | 评论 (0)编辑 收藏

lua学习

----------------------------------------------------------------------------------

头文件lua.h定义了Lua提供的基础函数。其中包括创建一个新的Lua环境的函数(如lua_open),调用 Lua函数(如lua_pcall)的函数,读取/写入Lua环境的全局变量的函数,注册可以被Lua代码调用的新函数的函数,等等。所有在lua.h中 被定义的都有一个lua_前缀。

头文件lauxlib.h定义了辅助库(auxlib)提供的函数。同样,所有在其中定义的函数等都以luaL_打头(例如,luaL_loadbuffer)。辅助库利用lua.h中提供的基础函数提供了更高层次上的抽象;所有Lua标准库都使用了auxlib。


----------------------------------------------------------------------------------

在控制台 使用 require 老是失败

后来发现要修改一下 package.path

require搜索模块时是根据package.path设的路径来搜索的


require搜索路径

package.path   -- lua模块路径

package.cpath    -- dll so 库路径


package.path = package.path..';d:\?.lua'


// require mylua.lua

require 'mylua' 


------------------------------------------------------------------------------------

posted @ 2014-08-08 17:01 多彩人生 阅读(331) | 评论 (0)编辑 收藏

仅列出标题
共25页: 1 2 3 4 5 6 7 8 9 Last 

导航

统计

常用链接

留言簿(3)

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜