之前已经创建了一个简单的粒子系统(点这里 ),但是使用起来还是不是很方便,这次的任务就是为这个系统增加脚本支持。使用脚本来定义粒子系统的各项属性,再从主程序中将这些属性读出来,创建粒子系统。我对小巧简洁的东西有种特殊的喜好,因此我选择了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 , int idx , float * ret );
bool GetTString (const char * table , int idx , 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 , 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 , field );
if (lua_isnumber (m_L , - 1 ))
{
* ret = (float )lua_tonumber (m_L , - 1 );
lua_settop (m_L , top );
return true ;
}
else
{
lua_settop (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 , - 1 );
lua_settop (m_L , top );
return true ;
}
else
{
lua_settop (m_L , top );
return false ;
}
}
bool EpLuaScriptEngine :: GetTInt ( const char * table , int idx , int * ret )
{
int top = lua_gettop (m_L );
lua_getglobal (m_L , 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 , - 1 ))
{
lua_settop (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 , int idx , float * ret )
{
int top = lua_gettop (m_L );
lua_getglobal (m_L , 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 , - 1 ))
{
lua_settop (m_L , top );
return false ;
}
* ret = (float )lua_tonumber (m_L , - 1 );
lua_settop (m_L , top );
return true ;
}
bool EpLuaScriptEngine :: GetTString ( const char * table , int idx , const char ** ret )
{
int top = lua_gettop (m_L );
lua_getglobal (m_L , table );
if (! lua_istable (m_L , - 1 ))
{
lua_settop (m_L , top );
return false ;
}
lua_rawgeti (m_L , - 1 , idx );
if (! lua_isstring (m_L , - 1 ))
{
lua_settop (m_L , top );
return false ;
}
* ret = lua_tostring (m_L , - 1 );
lua_settop (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" , 1 , & range . x );
lua . GetTFloat ("range" , 2 , & range . y );
lua . GetTFloat ("range" , 3 , & range . z );
D3DVECTOR accel = { 0 }; //加速度
lua . GetTFloat ("accel" , 1 , & accel . x );
lua . GetTFloat ("accel" , 2 , & accel . y );
lua . GetTFloat ("accel" , 3 , & accel . z );
D3DVECTOR emiPosMin = { 0 }; //发射位置的范围
lua . GetTFloat ("emiPosMin" , 1 , & emiPosMin . x );
lua . GetTFloat ("emiPosMin" , 2 , & emiPosMin . y );
lua . GetTFloat ("emiPosMin" , 3 , & emiPosMin . z );
D3DVECTOR emiPosMax = { 0 };
lua . GetTFloat ("emiPosMax" , 1 , & emiPosMax . x );
lua . GetTFloat ("emiPosMax" , 2 , & emiPosMax . y );
lua . GetTFloat ("emiPosMax" , 3 , & emiPosMax . z );
D3DXVECTOR3 veloMin ; //粒子初始速度范围
ZeroMemory (& veloMin , sizeof (veloMin ));
lua . GetTFloat ("veloMin" , 1 , & veloMin . x );
lua . GetTFloat ("veloMin" , 2 , & veloMin . y );
lua . GetTFloat ("veloMin" , 3 , & veloMin . z );
D3DXVECTOR3 veloMax ;
ZeroMemory (& veloMax , sizeof (veloMax ));
lua . GetTFloat ("veloMax" , 1 , & veloMax . x );
lua . GetTFloat ("veloMax" , 2 , & veloMax . y );
lua . GetTFloat ("veloMax" , 3 , & veloMax . z );
D3DCOLORVALUE colorMin = { 0 }; //粒子颜色范围
lua . GetTFloat ("colorMin" , 1 , & colorMin . a );
lua . GetTFloat ("colorMin" , 2 , & colorMin . r );
lua . GetTFloat ("colorMin" , 3 , & colorMin . g );
lua . GetTFloat ("colorMin" , 4 , & colorMin . b);
D3DCOLORVALUE colorMax = { 0 };
lua . GetTFloat ("colorMax" , 1 , & colorMax . a );
lua . GetTFloat ("colorMax" , 2 , & colorMax . r );
lua . GetTFloat ("colorMax" , 3 , & colorMax . g );
lua . GetTFloat ("colorMax" , 4 , & 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 , - 2.0 , emiRange }
veloMin = { - veloRange , 0.02 , - veloRange }
veloMax = { veloRange , 0.03 , veloRange }
colorMin = { 0.0 , 0.0 , 0.0 , 0.0 }
colorMax = { 0.0 , 1.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 , 0.0 , 0.0 }
range = { 4.0 , 4.0 , 4.0 }
accel = { 0.00005 , - 0.0001 , 0.0 }
emiPosMin = { - emiRange , 2.0 , - emiRange }
emiPosMax = { emiRange , 2.0 , emiRange }
veloMin = { - veloRange , 0.00 , - veloRange }
veloMax = { veloRange , 0.00 , veloRange }
colorMin = { 0.0 , 1.0 , 1.0 , 1.0 }
colorMax = { 0.0 , 1.0 , 1.0 , 1.0 }
psizeMin = 0.3
psizeMax = 0.5
maxCount = 3000
emiCount = 10
emiInterval = 0.01
texture = " snow.tga"
最新的源代码:http://download.youkuaiyun.com/source/1663374
本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/ntwilford/archive/2009/09/14/4552629.aspx