游戏脚本管理 (一)
前言:
在自己制作了单机泡泡堂的游戏之后,感觉应该加入游戏脚本的支持,通过一段时间学习,我将学的一些步骤和经验包括代码写了下来.
一、实现以指令为基础的游戏脚本语言
现在说的是理论方法,你马上可以实现一种以指令为基础的语言。首先设计一组简单的指令,用于显示文本,并加入简单的循环。最终用MessageBox对话框显示出来。
设计语言第一个步骤,建立自己的指令,为了更直观我建立一个指令表,列表如下:
文本控制指令表
命令
参数
描述
PrintString
string
输出字符串
PrintStringLoop
string,count
根据count指定输出次数
NewLine
None
增加一个空行
WaitForKeyPress
None
等待按键
二、编写一个脚本
首先确认你认为所需要的功能,目前它就是上表列的4条指令。它最好为一个标准的文本文件。
PrintString "这是一种以指令为基础的语言"
PrintString "也是一个简单的脚本代码"
Newline
PrintString "但它相当的简单"
Newline
PrintStringLoop "将被输出4次" 4
Newline
PrintString "等下将执行等待按键操作"
WaitForKeyPress
三、编写TScript类
这段代码相当简单,主要体现于分解字符串的操作,根据不同的指令来执行不同的代码。
unit uScripting;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms;
const
//四个指令的常量定义
COMMAND_PRINTSTRING = 'PrintString';
COMMAND_NEWLINE = 'Newline';
COMMAND_PRINTSTRINGLOOP = 'PrintStringLoop';
COMMAND_WAITFORKEYPRESS = 'WaitForKeyPress';
type
TScript = class
private
FCommandList: TStrings; //保存脚本
procedure GetCommand(Index: integer; var strCommand: string); //获取一个命令
procedure GetStrParam(Index: integer; var strParam: string); //获取一个字符串参数
procedure GetIntParam(Index: integer; var intParam: integer); //获取一个整形参数
public
constructor Create;
destructor Destroy; override;
procedure LoadScript(AFile: string); //装入脚本
procedure UnLoadScript; //清空脚本
procedure RunScript(); //运行脚本
end;
implementation
{ TScript }
constructor TScript.Create;
begin
FCommandList := TStringList.Create;
end;
destructor TScript.Destroy;
begin
FCommandList.Clear;
FCommandList.Free;
inherited Destroy;
end;
procedure TScript.GetCommand(Index: integer; var strCommand: string);
begin
if Pos(#32, Trim(FCommandList.Strings[Index])) <> 0 then
strCommand := Trim(Copy(Trim(FCommandList.Strings[Index]), 1, Pos(#32,
Trim(FCommandList.Strings[Index]))))
else
strCommand := Trim(FCommandList.Strings[Index]);
end;
procedure TScript.GetIntParam(Index: integer;
var intParam: integer);
var
cCurrChar : string;
g_iCurrScriptLineChar: Integer;
begin
g_iCurrScriptLineChar := Pos('"', FCommandList.Strings[Index]) + 1;
g_iCurrScriptLineChar := Pos('"', Copy(FCommandList.Strings[Index],
g_iCurrScriptLineChar + 1, length(FCommandList.Strings[Index]))) +
g_iCurrScriptLineChar + 1;
intParam := 0;
while (g_iCurrScriptLineChar <= Length(FCommandList.Strings[Index])) do
begin
cCurrChar := copy(FCommandList.Strings[Index], g_iCurrScriptLineChar, 1);
if (cCurrChar = '#') or (cCurrChar = #13) or (cCurrChar = #10) then
break;
if (cCurrChar[1] in ['0'..'9']) then
intParam := StrToInt(IntToStr(intParam) + cCurrChar);
inc(g_iCurrScriptLineChar, 1);
end;
end;
procedure TScript.GetStrParam(Index: integer;
var strParam: string);
var
cCurrChar : string;
g_iCurrScriptLineChar: Integer;
begin
g_iCurrScriptLineChar := Pos('"', FCommandList.Strings[Index]) + 1;
strParam := '';
while (g_iCurrScriptLineChar < Length(FCommandList.Strings[Index])) do
begin
cCurrChar := copy(FCommandList.Strings[Index], g_iCurrScriptLineChar, 1);
if (cCurrChar = '"') or (cCurrChar = #13) or (cCurrChar = #10) then
break;
strParam := strParam + cCurrChar;
inc(g_iCurrScriptLineChar, 1);
end;
end;
procedure TScript.LoadScript(AFile: string);
begin
if FileExists(AFile) then
FCommandList.LoadFromFile(AFile)
else
raise Exception.Create('无效的脚本文件');
end;
procedure TScript.RunScript;
var
strCommand : string;
strParam : string;
intParam : integer;
i : Integer;
iCurr : integer;
sResult : string;
begin
if FCommandList.Count < 0 then
Exit;
sResult := '';
for i := 0 to FCommandList.Count - 1 do
begin
GetCommand(i, strCommand);
if Uppercase(strCommand) = Uppercase(COMMAND_PRINTSTRING) then //检查指令是否匹配
begin
GetStrParam(i, strParam); //获取字符串参数
sResult := sResult + strParam + #13#10; //保存结果
end
else if UpperCase(strCommand) = UpperCase(COMMAND_PRINTSTRINGLOOP) then
begin
GetStrParam(i, strParam);
GetIntParam(i, intParam); //获取整形参数
for iCurr := 0 to intParam - 1 do
begin
sResult := sResult + strParam + #13#10; //循环输出次数
end;
end
else if UpperCase(strCommand) = UpperCase(COMMAND_NEWLINE) then
begin
sResult := sResult + #13#10;
end
else if UpperCase(strCommand) = UpperCase(COMMAND_WAITFORKEYPRESS) then
begin
Application.Title := '等待按键';
while true do
begin
Application.ProcessMessages;
if GetInputState() then
break;
end;
Application.Title := '按键完成';
end
else
begin
sResult := sResult + '无效指令!' + #13#10;
end;
end;
Application.MessageBox(PAnsiChar(sResult), '运行结果', 0);
end;
procedure TScript.UnLoadScript;
begin
FCommandList.Clear;
end;
end.
这里是测试代码。
procedure TGameMain.Button1Click(Sender: TObject); //请在form中放入一个button 后粘贴代码到单击事件中
var
FScript : TScript;
begin
FScript := TScript.Create;
FScript.LoadScript('E:\projects\GameEngine\Scripts\Test.src'); //修改为你保存脚本的路径
(Sender as TButton).Enabled := FALSE;
FScript.RunScript; //等待按键时只需要单击一下form即可继续运行。
(Sender as TButton).Enabled := TRUE;
FScript.Free;
end;
这里是关于上面代码的图解说明:
PrintString
NewLine
WaitForKeyPress
RunScript()
Text Console
阅读完上述的内容后,下一回将讲解如何解释向这样的脚本指令:
DrawBitmap "gfx/copyright.bmp"
PlaySound "sound/ambient.wav"
Pause 3000
PlaySound "sound/wipe.wav"
FoldCloseEffectY
DrawBitmap "gfx/ynh_presents.bmp"
PlaySound "sound/ambient.wav"
Pause 3000
PlaySound "sound/wipe.wav"
FoldCloseEffectX
DrawBitmap "gfx/title.bmp"
PlaySound "sound/title.wav"
WaitForKeyPress
PlaySound "sound/wipe.wav"
FoldCloseEffectY
Exit
再见!2004年11月