天行健 君子当自强而不息

Controlling Players and Characters(15)

 

To see more details about game script,see 游戏脚本的实现

 

Scripting and Characters


Scripting keeps popping its head up throughout these last few chapters, and true
to form, scripting plays a major role when dealing with characters. Scripts work
with conversations, spells, character movement, and much more.


What you need at this point is a clean-cut method of processing the game scripts.
The best way to do this is to create a class to entwine into your character and other
application processing.

 

The Script Class

it becomes even easier to execute scripts when you put the whole script processing in a class.

enum ENTRY_TYPE { ENTRY_NONE = 0, ENTRY_TEXT, ENTRY_BOOL, ENTRY_INT, ENTRY_FLOAT, ENTRY_CHOICE };

typedef 
struct sEntry
{
    
long    type;               // type of blank entry (ENTRY_TEXT, ENTRY_BOOL, )

    union
    {
        
long    io_value;       // used for saving/loading
        long    length;         // length of text (0 terminator)
        long    selection;      // selection in choice
        BOOL    bool_value;     
        
long    long_value;     
        
float   float_value;    
    };

    
char*   text;               // entry text buffer

    sEntry()
    {
        memset(
this, 0, sizeof(*this));
    }

    ~sEntry()
    {
        delete[] text;
    }
} *sEntryPtr;

////////////////////////////////////////////////////////////////////////////////////

typedef 
struct sScriptInfo
{
    
long        action_index;   // [0, number of actions - 1]

    
long        num_entries;    // number of entries in this action
    sEntry*     entries;        // array of entries

    sScriptInfo*     prev;      
// previous in linked list
    sScriptInfo*     next;      // next in linked list

    sScriptInfo()
    {
        memset(
this, 0, sizeof(*this));
    }

    ~sScriptInfo()
    {
        delete[] entries;
        delete next;
    }
} *sScriptInfoPtr;

////////////////////////////////////////////////////////////////////////////////////

typedef 
class cScript
{
private:
    
long            m_num_actions;
    sScriptInfo*    m_root_script_info;

private:
    
virtual bool prepare() { return true; }
    
virtual bool release() { return true; }

    
virtual sScriptInfo* process(sScriptInfo* script_info)
    {
        
return script_info->next;
    }

public:
    cScript()
    {
        m_num_actions = 0;
        m_root_script_info = NULL;
    }

    ~cScript()
    {
        free();
    }

    
void free()
    {
        delete m_root_script_info;
        m_root_script_info = NULL;

        m_num_actions = 0;
    }

    sScriptInfo* get_root_script_info()
    {
        
return m_root_script_info;
    }
    
    
bool load(const char* filename);
    
void execute(const char* filename);    
} *cScriptPtr;

Although deceptively small, the cScript class packs a punch. Loading a script is
accomplished via the Load function. Once it’s loaded, you can process the script
with a call to Execute. If you don’t want to hassle with loading a script
before processing, a call to the Execute function takes a script file
to load and execute in the same function call (plus it frees the script when execution is complete).


bool cScript::load(const char* filename)
{
    free();

    FILE* fp;

    
if((fp = fopen(filename, "rb")) == NULL)
        
return false;

    fread(&m_num_actions, 1, 
sizeof(m_num_actions), fp);

    sScriptInfo* head = NULL;
    sScriptInfo* ptr = NULL;

    
// loop through each script action
    for(long i = 0; i < m_num_actions; i++)
    {
        
// allocate a script structure and link in

        sScriptInfo* info = 
new sScriptInfo;
        info->next = NULL;

        
if(ptr == NULL)
            head = info;
        
else
            ptr->next = info;

        ptr = info;

        fread(&info->action_index, 1, 
sizeof(info->action_index), fp);
        fread(&info->num_entries, 1, 
sizeof(info->num_entries), fp);

        
// get entry data (if any)
        if(info->num_entries)
        {
            info->entries = 
new sEntry[info->num_entries];

            
// load in each entry
            for(long j = 0; j < info->num_entries; j++)
            {
                fread(&info->entries[j].type, 1, 
sizeof(info->entries[j].type), fp);
                fread(&info->entries[j].io_value, 1, 
sizeof(info->entries[j].io_value), fp);

                
// get text (if any)
                if(info->entries[j].type == ENTRY_TEXT && info->entries[j].length)
                {
                    info->entries[j].text = 
new char[info->entries[j].length];
                    fread(info->entries[j].text, 1, info->entries[j].length, fp);
                }
            }
        }
    }

    fclose(fp);

    m_root_script_info = head;

    
return true;
}

void cScript::execute(const char* filename)
{
    
// load script if none already
    if(filename)
        load(filename);

    
// prepare script data for execution
    if(! prepare())
        
return;

    
// start at beginning of script
    sScriptInfo* ptr = m_root_script_info;
    
if(ptr == NULL)
        
return;
    
    
// loop until no more script actions
    while(ptr)
    {
        
// Call script function and break on NULL return value.
        // Any other return type is the pointer to the next function, which is typically ptr->next.
        ptr = process(ptr);
    }

    release();

    
// release script if execute loaded it
    if(filename)
        free();
}

TIP
Using the Load function to load a script is useful if the script is processed many times
because you don’t have to free it between uses. Loading a script within the Execute
function forces the script to be loaded and freed every time, wasting precious time.

The way the cScript class processes scripts is ingenious. You actually have to derive the cScript class to
parse each script action as it is processed. That’s the purpose of the Process function.
Once a script is loaded, the Process function is called for every script action to process.

Each script pointer is queried for the script action number, and you must decide
what to do with the action. Then you need to update the script pointers by returning
the pointer to the next script action in the linked list.


posted on 2007-11-14 19:45 lovedday 阅读(136) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论