基于Direct3D实现简单的粒子系统(二) - 增加LUA脚本支持

34 篇文章 0 订阅

   之前已经创建了一个简单的粒子系统(点这里 ),但是使用起来还是不是很方便,这次的任务就是为这个系统增加脚本支持。使用脚本来定义粒子系统的各项属性,再从主程序中将这些属性读出来,创建粒子系统。我对小巧简洁的东西有种特殊的喜好,因此我选择了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.csdn.net/source/1663374


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ntwilford/archive/2009/09/14/4552629.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值