【COCOS2DX-LUA 脚本开发之十一】C/C++与LUA之间进行数据函数交互以及解决“PANIC: UNPROTECTED ERROR IN CALL TO LUA API (ATTEMPT TO INDEX A NIL VALUE)”的问题

本站文章均为 李华明Himi 原创,转载务必在明显处注明: 
转载自【黑米GameDev街区】 原文链接: http://www.himigame.com/lua-game/1343.html

在使用Cocos2d-x 时候,难免需要C/C++调用Lua函数、数据或Lua调用C/C++函数,那么本篇讲详细介绍C/C++与Lua之间的数据、函数交互。

首先让我们来简单了解几个Lua API函数:

int   luaL_dofile (lua_State *L, const char *filename)  :

加载并运行指定文件,没有错误返回0

void  lua_settop (lua_State *L, int index):

参数允许传入任何可接受的索引以及 0 。 它将把堆栈的栈顶设为这个索引。 如果新的栈顶比原来的大,超出部分的新元素将被填为 nil 。 如果 index 为 0 ,把栈上所有元素移除。

void   lua_getglobal (lua_State *L, const char *name):

把全局变量 name 里的值压入堆栈。

void   lua_pop (lua_State *L, int n):

从堆栈中弹出 n 个元素。相当于清除!

void   lua_pushstring (lua_State *L, const char *s):

把指针 s 指向的以零结尾的字符串压栈。 Lua 对这个字符串做一次内存拷贝(或是复用一个拷贝), 因此 s 处的内存在函数返回后,可以释放掉或是重用于其它用途。 字符串中不能包含有零字符;第一个碰到的零字符会认为是字符串的结束。

更多的API请参考:http://www.codingnow.com/2000/download/lua_manual.html

了解了以上几个函数,为了方便童鞋们使用,Himi直接贴出封装好的类 HclcData,其中主要包括如下几个功能:

1. C/C++ 调用 Lua 全局变量

2. C/C++ 调用 Lua 全局Table 某元素

3. C/C++ 调用 Lua 全局Table

4. C/C++ 调用 Lua 函数

5. Lua 调用C/C++ 函数

 

下面直接贴出代码:HclcData.h

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

//

//  HclcData.h

//  CppLua

//

//  Created by Himi on 13-4-17.

//

//

 

#ifndef __CppLua__HclcData__

#define __CppLua__HclcData__

 

#include "cocos2d.h"

using namespace cocos2d;

using namespace std;

 

extern "C" {

#include "lua.h"

#include "lualib.h"

#include "lauxlib.h"

};

 

class HclcData{

public:

    static HclcData* sharedHD();

 

        //------------  c++ -> lua ------------//

 

    /*

        getLuaVarString : 调用lua全局string

 

        luaFileName  = lua文件名

        varName = 所要取Lua中的变量名

     */

    const char* getLuaVarString(const char* luaFileName,const char* varName);

 

    /*

     getLuaVarOneOfTable : 调用lua全局table中的一个元素

 

     luaFileName  = lua文件名

     varName = 所要取Lua中的table变量名

     keyName = 所要取Lua中的table中某一个元素的Key

     */

    const char* getLuaVarOneOfTable(const char* luaFileName,const char* varName,const char* keyName);

 

    /*

     getLuaVarTable : 调用lua全局table

 

     luaFileName  = lua文件名

     varName = 所要取的table变量名

 

     (注:返回的是所有的数据,童鞋们可以自己使用Map等处理)

     */

    const char* getLuaVarTable(const char* luaFileName,const char* varName);

 

    /*

     callLuaFunction : 调用lua函数

 

     luaFileName  = lua文件名

     functionName = 所要调用Lua中的的函数名

     */

    const char* callLuaFunction(const char* luaFileName,const char* functionName);

 

      //------------  lua -> c++ ------------//

 

    void callCppFunction(const char* luaFileName);

 

private:

    static int cppFunction(lua_State* ls);

 

    static bool _isFirst;

    static HclcData* _shared;

    const char* getFileFullPath(const char* fileName);

    ~HclcData();

};

 

#endif /* defined(__CppLua__HclcData__) */

 

HclcData.cpp

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

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

//

//  HclcData.cpp

//  CppLua

//

//  Created by Himi on 13-4-17.

//

//

 

#include "HclcData.h"

#include "CCLuaEngine.h"

 

bool HclcData::_isFirst;

HclcData* HclcData::_shared;

 

HclcData* HclcData::sharedHD(){

    if(!_isFirst){

        _shared = new HclcData();

    }

    return _shared;

}

 

const char* HclcData::getLuaVarString(const char* luaFileName,const char* varName){

 

    lua_State*  ls = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();

 

    int isOpen = luaL_dofile(ls, getFileFullPath(luaFileName));

    if(isOpen!=0){

        CCLOG("Open Lua Error: %i", isOpen);

        return NULL;

    }

 

    lua_settop(ls, 0);

    lua_getglobal(ls, varName);

 

    int statesCode = lua_isstring(ls, 1);

    if(statesCode!=1){

        CCLOG("Open Lua Error: %i", statesCode);

        return NULL;

    }

 

    const char* str = lua_tostring(ls, 1);

    lua_pop(ls, 1);

 

    return str;

}

 

const char* HclcData::getLuaVarOneOfTable(const char* luaFileName,const char* varName,const char* keyName){

 

    lua_State*  ls = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();

 

    int isOpen = luaL_dofile(ls, getFileFullPath(luaFileName));

    if(isOpen!=0){

        CCLOG("Open Lua Error: %i", isOpen);

        return NULL;

    }

 

    lua_getglobal(ls, varName);

 

    int statesCode = lua_istable(ls, -1);

    if(statesCode!=1){

        CCLOG("Open Lua Error: %i", statesCode);

        return NULL;

    }

 

    lua_pushstring(ls, keyName);

    lua_gettable(ls, -2);

    const char* valueString = lua_tostring(ls, -1);

 

    lua_pop(ls, -1);

 

    return valueString;

}

 

const char* HclcData::getLuaVarTable(const char* luaFileName,const char* varName){

    lua_State*  ls = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();

 

    int isOpen = luaL_dofile(ls, getFileFullPath(luaFileName));

    if(isOpen!=0){

        CCLOG("Open Lua Error: %i", isOpen);

        return NULL;

    }

 

    lua_getglobal(ls, varName);

 

    int it = lua_gettop(ls);

    lua_pushnil(ls);

 

    string result="";

 

    while(lua_next(ls, it))

    {

        string key = lua_tostring(ls, -2);

        string value = lua_tostring(ls, -1);

 

        result=result+key+":"+value+"\t";

 

        lua_pop(ls, 1);

    }

    lua_pop(ls, 1);

 

    return result.c_str();

}

 

const char* HclcData::callLuaFunction(const char* luaFileName,const char* functionName){

    lua_State*  ls = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();

 

    int isOpen = luaL_dofile(ls, getFileFullPath(luaFileName));

    if(isOpen!=0){

        CCLOG("Open Lua Error: %i", isOpen);

        return NULL;

    }

 

    lua_getglobal(ls, functionName);

 

    lua_pushstring(ls, "Himi");

    lua_pushnumber(ls, 23);

    lua_pushboolean(ls, true);

 

    /*

     lua_call

     第一个参数:函数的参数个数

     第二个参数:函数返回值个数

     */

    lua_call(ls, 3, 1);

 

    const char* iResult = lua_tostring(ls, -1);

 

    return iResult;

}

 

void  HclcData::callCppFunction(const char* luaFileName){

 

    lua_State*  ls = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();

 

    /*

     Lua调用的C++的函数必须是静态的

     */

    lua_register(ls, "cppFunction", cppFunction);

 

    int isOpen = luaL_dofile(ls, getFileFullPath(luaFileName));

    if(isOpen!=0){

        CCLOG("Open Lua Error: %i", isOpen);

        return;

    }

}

 

int HclcData::cppFunction(lua_State* ls){

    int luaNum = (int)lua_tonumber(ls, 1);

    int luaStr = (int)lua_tostring(ls, 2);

    CCLOG("Lua调用cpp函数时传来的两个参数: %i  %s",luaNum,luaStr);

 

    /*

     返给Lua的值

     */

    lua_pushnumber(ls, 321);

    lua_pushstring(ls, "Himi");

 

    /*

     返给Lua值个数

     */

    return 2;

}

 

const char* HclcData::getFileFullPath(const char* fileName){

    return CCFileUtils::sharedFileUtils()->fullPathForFilename(fileName).c_str();

}

 

HclcData::~HclcData(){

 

    CC_SAFE_DELETE(_shared);

    _shared=NULL;

}

 

大家可以直接拿来用的,使用简单,测试如下:

首先C++测试代码:

1

2

3

4

5

6

7

8

9

10

11

12

#include "HclcData.h"

 

    CCLOG("Str = %s",HclcData::sharedHD()->getLuaVarString("Test.lua","luaStr"));

    CCLOG("Str2 %s",HclcData::sharedHD()->getLuaVarString("Test.lua","luaStr2"));

    CCLOG("age = %s",HclcData::sharedHD()->getLuaVarOneOfTable("Test.lua", "luaTable","age"));

    CCLOG("name = %s",HclcData::sharedHD()->getLuaVarOneOfTable("Test.lua", "luaTable","name"));

    CCLOG("sex = %s",HclcData::sharedHD()->getLuaVarOneOfTable("Test.lua", "luaTable","sex"));

    CCLOG("Table = %s",HclcData::sharedHD()->getLuaVarTable("Test.lua", "luaTable"));

    CCLOG("Call Lua Function Back: %s",HclcData::sharedHD()->callLuaFunction("Test.lua", "luaLogString"));

 

    HclcData::sharedHD()->callCppFunction("Test.lua");

    HclcData::sharedHD()->callLuaFunction("Test.lua", "call_Cpp");

 

对应测试的Test.Lua文件:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

luaStr  = "I' m Himi"

 

luaStr2 = "are you ok!"

 

luaTable={age = 23,name="Himi",sex="男"}

 

function luaLogString(_logStr,_logNum,_logBool)

 

    print("Lua 脚本打印从C传来的字符串:",_logStr,_logNum,_logBool)

    return "call lua function OK"

end

 

function call_Cpp(_logStr,_logNum,_logBool)

    num,str = cppFunction(999,"I'm a lua string")

    print("从cpp函数中获得两个返回值:",num,str)

end

 

运行测试结果如下:

1

2

3

4

5

6

7

8

9

10

Cocos2d: Str = I' m Himi

Cocos2d: Str2 are you ok!

Cocos2d: age = 23

Cocos2d: name = Himi

Cocos2d: sex = 男

Cocos2d: Table = name:Himi age:23 sex:男

Lua 脚本打印从C传来的字符串: Himi 23 true

Cocos2d: Call Lua Function Back: call lua function OK

Cocos2d: Lua调用cpp函数时传来的两个参数: 999  I'm a lua string

从cpp函数中获得两个返回值: 321 Himi

 

在Himi做这些交互时出现了如下错误:

1

“PANIC: unprotected error in call to Lua API (attempt to index a nil value)

如下图:

 

最后Himi发现造成此问题的原因有两种情况:

 1. 是你的lua文件位置路径!

      细心的童鞋应该看到,每次我使用 luaL_dofile 函数时传入的都是调用了一个getFileFullPath的函数进行获取文件的完整路径!

在HclcData中包装了一个函数:

1

2

3

const char* HclcData::getFileFullPath(const char* fileName){

    return CCFileUtils::sharedFileUtils()->fullPathForFilename(fileName).c_str();

}

 

2. 如果你是cpp调用lua函数,那么你的这个lua函数不能是local的!

    反之,如果你lua调用cpp函数,同理,cpp函数肯定是static的!

 

另外,如果你cpp调用lua,等同于重新加载了这个lua文件,是不同的对象!因此你应该建立一个新的lua文件,主要用于交互所用!

     例如你a.lua中有一个tab的成员变量,那么你使用cpp调用lua函数后,这个tab是新的对象!

 

最后附上HclcData和Test.lua 下载地址:http://vdisk.weibo.com/s/y0zws

OK,本篇就到这里,有什么问题及时联系Himi!

 

时间: 2024-09-15 17:51:42

【COCOS2DX-LUA 脚本开发之十一】C/C++与LUA之间进行数据函数交互以及解决“PANIC: UNPROTECTED ERROR IN CALL TO LUA API (ATTEMPT TO INDEX A NIL VALUE)”的问题的相关文章

nginx-在用Nginx+Lua进行开发: failed to send data through the output filters

问题描述 在用Nginx+Lua进行开发: failed to send data through the output filters 2014/08/08 15:06:34 [error] 744#0: 204300 lua entry thread aborted: runtime error: openresty/lualib/aps/getCptDroup.lua:66: **failed to send data through the output filters* stack t

用Nginx + Lua(OpenResty)开发高性能Web应用

在互联网公司,Nginx可以说是标配组件,但是主要场景还是负载均衡.反向代理.代理缓存.限流等场景;而把Nginx作为一个Web容器使用的还不是那么广泛.Nginx的高性能是大家公认的,而Nginx开发主要是以C/C++模块的形式进行,整体学习和开发成本偏高;如果有一种简单的语言来实现Web应用的开发,那么Nginx绝对是把好的瑞士军刀;目前Nginx团队也开始意识到这个问题,开发了nginxScript:可以在Nginx中使用JavaScript进行动态配置一些变量和动态脚本执行;而目前市面上

【转】使用Nginx+Lua(OpenResty)开发高性能Web应用

在互联网公司,Nginx可以说是标配组件,但是主要场景还是负载均衡.反向代理.代理缓存.限流等场景:而把Nginx作为一个Web容器使用的还不是那么广泛.Nginx的高性能是大家公认的,而Nginx开发主要是以C/C++模块的形式进行,整体学习和开发成本偏高:如果有一种简单的语言来实现Web应用的开发,那么Nginx绝对是把好的瑞士军刀:目前Nginx团队也开始意识到这个问题,开发了nginxScript:可以在Nginx中使用JavaScript进行动态配置一些变量和动态脚本执行:而目前市面上

《Lua游戏开发实践指南》一导读

前 言 Lua游戏开发 游戏开发是一个激动人心的过程,创造出让玩家花费数小时并乐在其中的游戏,给人带来的成就感是任何事情都无法比拟的.然而,这个创造的过程正在变得越来越难.那种奋战几个晚上或者几周就能单枪匹马设计出热门游戏的日子已经一去不复返,现在的游戏往往需要数十人的开发团队工作很多个月甚至几年才能完成.就算是那些可以从网上下载的最简单的"休闲游戏"也通常是由专业开发者组成的团队开发数月的成果. 尽管游戏开发的规模不断增大,但始终有一个不变的追求--测试.更新.调整,以及快速验证游戏

Lua 脚本怎么样调用外部脚本

在游戏脚本开发中,我们往往会发现脚本量非常大,而且我们经常会在一些核心脚本文件中定义常用的功能函数,但是Lua脚本没有提供include关键词,那又是怎样调用外部函数的呢?如何实现脚本的Include功能? test.lua脚本定义main函数如下: function main(szName, num1, num2) print("main()", szName, num1, num2); local nRandMax = 10000; local nRand = math.rando

redis4.0之Lua脚本新姿势

前言 Redis内嵌了Lua环境来支持用户扩展功能,但是出于数据一致性考虑,要求脚本必须是纯函数的形式,也就是说对于一段Lua脚本给定相同的参数,写入Redis的数据也必须是相同的,对于随机性的写入Redis是拒绝的. 从Redis 3.2开始Lua脚本支持随机性写入,最近在总结4.0的新特性,索性就都归到4.0里,方便查阅. Redis中的Lua脚本 1. Lua脚本简介 在Redis中使用Lua脚本不可避免的要用到以下三个命令:EVAL.EVALSHA和SCRIPT,下面我们来简单介绍一下:

Redis脚本插件之————执行Lua脚本示例

Redis在2.6推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行.使用脚本的好处如下: 1.减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在redis服务器上完成.使用脚本,减少了网络往返时延. 2.原子操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入. 3.复用:客户端发送的脚本会永久存储在Redis中,意味着其他客户端可以复用这一脚本而不需要使用代码完成同样的逻辑. 实现一个访问频率控制,某个ip在短时间内频繁访问页面

【COCOS2DX-LUA 脚本开发之十三】解决COCOS2DX-LUA编译到ANDROID找不到CCLUAENGINE、HELLOWORLD或出现GET DATA FROM FILE(XXX.LUA) FAILED/CAN NOT GET FILE DATA OF XXX.LUA、COCOS2DX

本站文章均为 李华明Himi 原创,转载务必在明显处注明:  转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/lua-game/1368.html 对于跨平台整合,Himi已经写了1.x 与 2.x 的了,还不知道如何整合的请移步到 [Cocos2d-X(2.x) 游戏开发系列之二]cocos2dx最新2.x版本跨平台整合NDK+Xcode,Xcode编写&编译代码,Android导入打包运行即可!) 本篇只是解决在整合cocos2dx-lua项目会

【COCOS2DX-LUA 脚本开发之一】LUA语言基础在COCOS2DX游戏中使用LUA脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途!

本站文章均为 李华明Himi 原创,转载务必在明显处注明:  转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/iphone-cocos2dx/681.html 对于游戏公司而言,采用游戏脚本lua.python等进行开发也很常见,但是很多童鞋对脚本并没有很熟悉的概念,本篇则向大家简单介绍脚本的用途以及在Cocos2dx基础用法: Lua和python这些详细介绍的话,请不太熟悉的童鞋自行百度百科哈,那么对于lua和python则是两个常用的脚本语言,