????? 之前已经创建了一个简单的粒子系统(点这里 ),但是使用起来还是不是很方便,这次的任务就是为这个系统增加脚本支持。使用脚本来定义粒子系统的各项属性,再从主程序中将这些属性读出来,创建粒子系统。我对小巧简洁的东西有种特殊的喜好,因此我选择了Lua语言。Lua与C的交互需要频繁的操作栈,使用起来并不是很方便,因此先简单的封装一下,可以不用直接操作栈就可以了。
????? 下面的这个类用于加载一个lua脚本,并提供了几个读取变量值的方法。其中GetInt,GetFloat,GetString用于读取变量field,GetTInt,GetTFloat,GetTString用于读取表table中的第idx个字段,ret返回读取到的值。
EpLuaScriptEngine.h:
#ifndef _EPLUASCRIPTENGINE_H_
#define _EPLUASCRIPTENGINE_H_
#include <lua.hpp>
class
EpLuaScriptEngine
{
public
:
???
EpLuaScriptEngine ();
???
virtual
~
EpLuaScriptEngine ();
???
bool
LoadScript (
const
char
*
fileName );
???
bool
GetInt (
const
char
*
field
,
int
*
ret );
???
bool
GetFloat (
const
char
*
field
,
float
*
ret );
???
bool
GetString (
const
char
*
field
,
const
char
**
ret );
???
bool
GetTInt (
const
char
*
table
,
int
idx
,
int
*
ret );
???
bool
GetTFloat (
const
char
*
table
,
float
*
ret );
???
bool
GetTString (
const
char
*
table
,
const
char
**
ret );
protected
:
???
lua_State
*
m_L ;
};
#endif
EpLuaScriptEngine.cpp:
#include "EpLuaScriptEngine.h"
#include <iostream>
using
namespace
std ;
EpLuaScriptEngine
::
EpLuaScriptEngine ()
{
???
m_L
=
lua_open ();
}
EpLuaScriptEngine
::~
EpLuaScriptEngine ()
{
???
lua_close (
m_L );
}
bool
EpLuaScriptEngine
::
LoadScript (
const
char
*
fileName )
{
???
if (
luaL_loadfile (
m_L
,
fileName ) ||
lua_pcall (
m_L
,
0
,
0 ))
???
{
???????
cout
<<
lua_tostring (
m_L
,
-
1 )
<<
endl ;
???????
return
false ;
???
}
???
return
true ;
}
bool
EpLuaScriptEngine
::
GetInt (
const
char
*
field
,
int
*
ret )
{
???
int
top
=
lua_gettop (
m_L );
???
lua_getglobal (
m_L
,
field );
???
if (
lua_isnumber (
m_L
,
-
1 ))
???
{
???????
*
ret
= (
int )
lua_tonumber (
m_L
,
-
1 );
???????
lua_settop (
m_L
,
top );
???????
return
true ;
???
}
???
else
???
{
???????
lua_settop (
m_L
,
top );
???????
return
false ;
???
}
}
bool
EpLuaScriptEngine
::
GetFloat (
const
char
*
field
,
float
*
ret )
{
???
int
top
=
lua_gettop (
m_L );
???
lua_getglobal (
m_L
,
-
1 ))
???
{
???????
*
ret
= (
float )
lua_tonumber (
m_L
,
top );
???????
return
false ;
???
}
}
bool
EpLuaScriptEngine
::
GetString (
const
char
*
field
,
const
char
**
ret )
{
???
int
top
=
lua_gettop (
m_L );
???
lua_getglobal (
m_L
,
field );
???
if (
lua_isstring (
m_L
,
-
1 ))
???
{
???????
*
ret
=
lua_tostring (
m_L
,
top );
???????
return
false ;
???
}
}
bool
EpLuaScriptEngine
::
GetTInt (
const
char
*
table
,
table );
???
if (
!
lua_istable (
m_L
,
-
1 ))
???
{
???????
lua_settop (
m_L
,
top );
???????
return
false ;
???
}
???
lua_rawgeti (
m_L
,
-
1
,
idx );
???
if (
!
lua_isnumber (
m_L
,
top );
???????
return
false ;
???
}
???
*
ret
= (
int )
lua_tonumber (
m_L
,
-
1 );
???
lua_settop (
m_L
,
top );
???
return
true ;
}
bool
EpLuaScriptEngine
::
GetTFloat (
const
char
*
table
,
top );
???????
return
false ;
???
}
???
*
ret
= (
float )
lua_tonumber (
m_L
,
top );
???
return
true ;
}
bool
EpLuaScriptEngine
::
GetTString (
const
char
*
table
,
idx );
???
if (
!
lua_isstring (
m_L
,
top );
???????
return
false ;
???
}
???
*
ret
=
lua_tostring (
m_L
,
top );
???
return
true ;
}
有了这个类以后就可以用它读取粒子脚本中的属性。现在需要在之前的粒子系统类中,加入一个静态函数,用于从脚本加载一个粒子系统。
EpParticleSystem
*
EpParticleSystem
::
CreateFromFile (
LPDIRECT3DDEVICE9
device
,
const
char
*
fileName )
{
???
if (
!
fileName )
return
NULL ;
???
//初始化lua,加载粒子脚本
???
EpLuaScriptEngine
lua ;
???
lua
.
LoadScript (
fileName );
???
D3DVECTOR
position
=
{
0
}; ???
//粒子系统的位置
???
lua
.
GetTFloat (
"position"
,
1
,
&
position
.
x );
???
lua
.
GetTFloat (
"position"
,
2
,
&
position
.
y );
???
lua
.
GetTFloat (
"position"
,
3
,
&
position
.
z );
???
D3DVECTOR
range
=
{
0
}; ???
//长宽高范围
???
lua
.
GetTFloat (
"range"
,
&
range
.
x );
???
lua
.
GetTFloat (
"range"
,
&
range
.
y );
???
lua
.
GetTFloat (
"range"
,
&
range
.
z );
???
D3DVECTOR
accel
=
{
0
}; ???
//加速度
???
lua
.
GetTFloat (
"accel"
,
&
accel
.
x );
???
lua
.
GetTFloat (
"accel"
,
&
accel
.
y );
???
lua
.
GetTFloat (
"accel"
,
&
accel
.
z );
???
D3DVECTOR
emiPosMin
=
{
0
}; ???
//发射位置的范围
???
lua
.
GetTFloat (
"emiPosMin"
,
&
emiPosMin
.
x );
???
lua
.
GetTFloat (
"emiPosMin"
,
&
emiPosMin
.
y );
???
lua
.
GetTFloat (
"emiPosMin"
,
&
emiPosMin
.
z );
???
D3DVECTOR
emiPosMax
=
{
0
};
???
lua
.
GetTFloat (
"emiPosMax"
,
&
emiPosMax
.
x );
???
lua
.
GetTFloat (
"emiPosMax"
,
&
emiPosMax
.
y );
???
lua
.
GetTFloat (
"emiPosMax"
,
&
emiPosMax
.
z );
???
D3DXVECTOR3
veloMin ;???
//粒子初始速度范围
???
ZeroMemory (
&
veloMin
,
sizeof (
veloMin ));
???
lua
.
GetTFloat (
"veloMin"
,
&
veloMin
.
x );
???
lua
.
GetTFloat (
"veloMin"
,
&
veloMin
.
y );
???
lua
.
GetTFloat (
"veloMin"
,
&
veloMin
.
z );
???
D3DXVECTOR3
veloMax ;
???
ZeroMemory (
&
veloMax
,
sizeof (
veloMax ));
???
lua
.
GetTFloat (
"veloMax"
,
&
veloMax
.
x );
???
lua
.
GetTFloat (
"veloMax"
,
&
veloMax
.
y );
???
lua
.
GetTFloat (
"veloMax"
,
&
veloMax
.
z );
???
D3DCOLORVALUE
colorMin
=
{
0
}; ???????
//粒子颜色范围
???
lua
.
GetTFloat (
"colorMin"
,
&
colorMin
.
a );
???
lua
.
GetTFloat (
"colorMin"
,
&
colorMin
.
r );
???
lua
.
GetTFloat (
"colorMin"
,
&
colorMin
.
g );
???
lua
.
GetTFloat (
"colorMin"
,
4
,
&
colorMin
. b);
???
D3DCOLORVALUE
colorMax
=
{
0
};
???
lua
.
GetTFloat (
"colorMax"
,
&
colorMax
.
a );
???
lua
.
GetTFloat (
"colorMax"
,
&
colorMax
.
r );
???
lua
.
GetTFloat (
"colorMax"
,
&
colorMax
.
g );
???
lua
.
GetTFloat (
"colorMax"
,
&
colorMax
. b);
???
float
psizeMin
=
0.0f ;???
//粒子大小范围
???
lua
.
GetFloat (
"psizeMin"
,
&
psizeMin );
???
float
psizeMax
=
0.0f ;
???
lua
.
GetFloat (
"psizeMax"
,
&
psizeMax );
???
int
maxCount
=
0 ;???
//最大粒子数量
???
lua
.
GetInt (
"maxCount"
,
&
maxCount );
???
int
emiCount
=
0 ;???
//每次发射数量
???
lua
.
GetInt (
"emiCount"
,
&
emiCount );
???
float
emiInterval
=
1.0f ;
//发射间隔时间
???
lua
.
GetFloat (
"emiInterval"
,
&
emiInterval );
???
const
char
*
textureFile
=
NULL ;
???
lua
.
GetString (
"texture"
,
&
textureFile );
???
EpParticleSystem
* ps
=
new
EpParticleSystem (
position
,
range
,
accel
,
emiPosMin
,
???????
emiPosMax
,
veloMin
,
veloMax
,
colorMin
,
colorMax
,
psizeMin
,
psizeMax
,
maxCount
,
???????
emiCount
,
emiInterval
,
device
,
textureFile );
???
return ps;
}
这里粒子系统的构造函数稍微有一点变动。因为脚本中只能定义纹理使用的文件名,而真正创建纹理还是要在C++中做,因此把纹理的创建由外部改到了内部。对于字符串textureFile 因为是存在于Lua的栈中,所以在这个函数返回后就会被销毁,因此如果需要在EpParticleSystem的构造函数以外创建纹理的话,在构造函数中就需要复制该字符串。像下面这样:
m_TexFileName
=
NULL ;
if (
textureFileName )
{
???
int
length
=
strlen (
textureFileName )
+
1 ;
???
m_TexFileName
=
new
char
[
length
];
???
strcpy_s (
m_TexFileName
,
length
,
textureFileName );
}
对于上次的烟火的粒子,在Lua脚本中就可以这样写:
Firework.lua:
emiRange
=
0.05
veloRange
=
0.006
position
=
{
0.0
,
0.0
,
0.0
}
range
=
{
4.0
,
4.0
,
4.0
}
accel
=
{
0.0
,
-
0.0002
,
0.0
}
emiPosMin
=
{
-
emiRange
,
-
2.0
,
-
emiRange
}
emiPosMax
=
{
emiRange
,
emiRange
}
veloMin
=
{
-
veloRange
,
0.02
,
-
veloRange
}
veloMax
=
{
veloRange
,
0.03
,
veloRange
}
colorMin
=
{
0.0
,
0.0
}
colorMax
=
{
0.0
,
1.0
,
1.0
}
psizeMin
=
0.3
psizeMax
=
0.5
maxCount
=
3000
emiCount
=
10
emiInterval
=
0.01
texture
=
"
snow.tga"
现在就可以把之前创建粒子系统的那一大段代码改成下面这行了:
g_Firework
=
EpParticleSystem
::
CreateFromFile (
g_D3DDevice
,
"Firework.lua" );
???
if (
!
g_Firework )
return
false ;
?
脱离了硬编码,这个粒子系统变得灵活多了。比如我们可以再写一个雪花的脚本,然后只需要更改上面的文件名就可以了。
snow.lua:
emiRange
=
2.0
veloRange
=
0.006
position
=
{
0.0
,
4.0
}
accel
=
{
0.00005
,
-
0.0001
,
2.0
,
0.00
,
1.0
}
colorMax
=
{
0.0
,
1.0
}
psizeMin
=
0.3
psizeMax
=
0.5
maxCount
=
3000
emiCount
=
10
emiInterval
=
0.01
texture
=
"
snow.tga"
最新的源代码:
http://download.csdn.net/source/1663374