Lua在游戏开发中的应用

2010 五月 18
Tags: , ,
作者: Luin

前些日子一直忙于开发BigTank项目(下载地址参见劣质设计网站:http://www.buaa-mstc.com,不支持IE),总结了一些Lua在C#项目中的应用方法。

Lua 是一个小巧的脚本语言。作者是巴西人。该语言的设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。它的主页是 www.lua.org。
Lua脚本可以很容易的被C/C++代码调用,也可以反过来调用C/C++的函数,这使得Lua在应用程序中可以被广泛应用。不仅仅作为扩展脚本,也可以作为普通的配置文件,代替XML,Ini等文件格式,并且更容易理解和维护。

在C#中使用Lua也十分简单。

LuaInterface is a library for integration between the Lua language and Microsoft .NET platform’s Common Language Runtime (CLR). Lua scripts can use it to instantiate CLR objects, access properties, call methods, and even handle events with Lua functions.

LuaInterface网站上可以下载到这个库。在你的项目中引用LuaInterface.dll后就可以开始了。

BigTank项目还没有确定是否要开源,所以我拿自己写的电子宠物程序演示一下(它也用了Lua,你可以在实验室页面找到它的全部源代码)。

C#:

//...
/// <summary>
/// Lua虚拟机
/// </summary>
private static Lua luaVM = null;
 
/// <summary>
/// 宠物的构造函数
/// </summary>
public Pet(PetForm _petForm, string _petName, string _petPath)
{
    petState = new PetState();
    petForm = _petForm;
    petName = _petName;
 
    //构造Lua虚拟机以解析宠物AI
    luaVM = new Lua();
    //注册提供给宠物AI的API函数
    Type tThis = this.GetType();
    luaVM.RegisterFunction("PetDo", this, tThis.GetMethod("LuaPetDo"));
    luaVM.RegisterFunction("PetDoFrame", this, tThis.GetMethod("LuaPetDoFrame"));
    luaVM.RegisterFunction("Sleep", this, tThis.GetMethod("LuaSleep"));
    //载入AI文件
    luaVM.DoFile(System.AppDomain.CurrentDomain.BaseDirectory + _petPath + "\\ai.lua");
}

其中RegisterFunction作用是注册C#代码中的一个public(最新版本的LuaInterface支持private)函数来供Lua脚本使用,其中无需关心参数的个数以及类型。

Lua:

PetDo("Sleep");

执行DoFile后会调用Lua脚本,后者则调用C#中的PetDo函数完成指定动作。

IE6的艺术: CSS文件中的中文注释会使样式全部失效

2010 三月 18
Tags: ,
作者: Luin

今天把自己刚做完的网站放到IE6下测试了一下(补充形容词: 心情忐忑不安分守己地),然后开始做hack,当大部分错误都被改好后,一个很奇怪的问题出现了:某个页面的CSS设置似乎全都失效了。心想IE6在那啥也不至于这么那啥吧,于是经过一段时间的摸索发现是由于CSS文件中含有中文注释,删掉后问题解决(当然把CSS文件存成utf8格式是更好的解决方法)。

下面附上别人写的一篇总结IE6 bug的文章:

IE6下的CSS BUG枚举

原文:Ultimate IE6 Cheatsheet: How To Fix 25+ Internet Explorer 6 Bugs 翻译:http://www.vfresh.org/w3c/727

1、终极方法:条件注释

<!–[if lte IE 6]> 这段文字仅显示在 IE6及IE6以下版本。 <![endif]–>

<!–[if gte IE 6]> 这段文字仅显示在 IE6及IE6以上版本。 <![endif]–>

<!–[if gt IE 6]> 这段文字仅显示在 IE6以上版本(不包含IE6)。 <![endif]–>

<!–[if IE 5.5]> 这段文字仅显示在 IE5.5。 <![endif]–>

<!–在 IE6及IE6以下版本中加载css–>

<!–[if lte IE 6]> <link type=”text/css” rel=”stylesheet” href=”css/ie6.css” mce_href=”css/ie6.css” /><![endif]–>

缺点是在IE浏览器下可能会增加额外的HTTP请求数。

2、CSS选择器区分

IE6不支持子选择器;先针对IE6使用常规申明CSS选择器,然后再用子选择器针对IE7+及其他浏览器。

/* IE6 专用 */

.content {color:red;}

/* 其他浏览器 */

div>p .content {color:blue;} –>

3、PNG半透明图片的问题

虽然可以通过JS等方式解决,但依然存在载入速度等问题,所以,这个在设计上能避免还是尽量避免为好。以达到网站最大优化。

4、IE6下的圆角

IE6不支持CSS3的圆角属性,性价比最高的解决方法就是用图片圆角来替代,或者放弃IE6的圆角。

5、IE6背景闪烁

如果你给链接、按钮用CSS sprites作为背景,你可能会发现在IE6下会有背景图闪烁的现象。造成这个的原因是由于IE6没有将背景图缓存,每次触发hover的时候都会重新加载,可以用JavaScript设置IE6缓存这些图片:

document.execCommand(“BackgroundImageCache”,false,true);

6、最小高度

IE6 不支持min-height属性,但它却认为height就是最小高度。解决方法:使用ie6不支持但其余浏览器支持的属性!important。

#container {min-height:200px; height:auto !important; height:200px;}

7、最大高度

//直接使用ID来改变元素的最大高度
var container = document.getElementById(‘container’);
container.style.height = (container.scrollHeight > 199) ? “200px” : “auto”;

//写成函数来运行
function setMaxHeight(elementId, height){
var container = document.getElementById(elementId);
container.style.height = (container.scrollHeight > (height – 1)) ? height + “px” : “auto”;
}

//函数示例
setMaxHeight(‘container1′, 200);
setMaxHeight(‘container2′, 500);

8、100% 高度

在IE6下,如果要给元素定义100%高度,必须要明确定义它的父级元素的高度,如果你需要给元素定义满屏的高度,就得先给html和body定义height:100%;。

继续阅读全部内容…

来,做个驴坝!用Delphi开发A片扫描器

2010 三月 6
作者: Luin

其实驴坝这个东西是挺好玩的,对被监控有爱的同学可以下来尝试一下。

这里,来讲一个用小创意干大事(俗称小炮打恐龙)的故事。

这个故事的结局是我们可以开发一个能找出电脑中所有A片的程序,这是个集合了影像分析、画面色块分层、人工智能等复杂算法的复杂的玩意。

A片扫描器

A片扫描器

好吧,其实这个程序只是简单读了下文件头而已(喂喂,那位拿板砖的同学请把板砖放下)。

我们假设一般人会把A片的扩展名改掉(例如改成txt、bmp等格式。曾经的realplayer年代有人会把rmvb格式的A片改成avi格式,现在由于暴风射手之流的出现导致这种方法不灵了),于是我们只有扫描一下文件的头部数据判断这个文件是不是一个影像文件,然后再判断这个文件的扩展名是不是一个影像文件的扩展名,如果不是自然就很可能是A片了(如果是的话应该就是阿凡达这类的电影了,你总不能说人家是A片吧)。

先送上队列遍历文件的代码(递归算法在这里更容易理解,但实际上很容易超栈溢出):

function EnumFileInQueue(path:PChar):Longint;stdcall;
var
    searchRec:TSearchRec;
    found:Integer;
    tmpStr:String;
    curDir:PChar;
    dirs:TQueue;
begin
    Result:=0;//查找结果(文件数)
    dirs:=TQueue.Create;//创建目录队列
    dirs.Push(path);//将起始搜索路径入队
    curDir:=dirs.Pop;//出队
    while (curDir&lt;&gt; nil) do
    begin
        tmpStr:=StrPas(curDir)+'\*.*';
        found:=FindFirst(tmpStr,faAnyFile,searchRec);
        while found=0 do
        begin
            if (searchRec.Attr and faDirectory)&lt;&gt;0 then
            begin
                if (searchRec.Name &lt;&gt; '.') and (searchRec.Name &lt;&gt; '..') then
                begin
                    tmpStr:=StrPas(curDir)+'\'+searchRec.Name;
                    dirs.Push(StrNew(PChar(tmpStr)));
                end;
            end
            //如果找到的是个文件
            else begin
                Result:=Result+1;
                //form1.Label2.Caption  := StrPas(curDir)+'\';
                //form1.Label1.Caption :=(StrPas(curDir)+'\'+searchRec.Name);
                if isA(StrPas(curDir)+'\'+searchRec.Name) then
                begin
                  Form1.Memo1.Lines.Add(StrPas(curDir)+'\'+searchRec.Name);
                  //form1.Button2.Click;
                end;
 
            end;
            found:=FindNext(searchRec);
        end;
        if dirs.Count &gt; 0 then
            curDir:=dirs.Pop
        else
            curDir:=nil;
    end;
    dirs.Free;
    FindClose(searchRec);
end;

然后是获得文件头部信息的代码:

Function GetFileBegin(const Filename: String): String;
Var
  i: integer;
  Source: tfilestream;
  s2, s3, s, s4, s5: String;
  Buf: Array[0..MaxBuf-1] Of byte;
Begin
  result:='';
  Try
    Source:=tfilestream.Create(Filename, fmOpenRead Or fmShareDenyNone);
    If Source.Size&lt;10 Then Exit;
    Source.Seek(0, sofrombeginning);
    Try
      Source.Read(Buf, SizeOf(Buf));
      For i:=0 To MaxBuf-1 Do
        s:=s+IntToHex(Buf[i], 2);
 
      Source.Read(Buf, SizeOf(Buf));
      For i:=0 To MaxBuf-1 Do
        s2:=s2+IntToHex(Buf[i], 2);
 
      Source.Read(Buf, SizeOf(Buf));
      For i:=0 To MaxBuf-1 Do
        s3:=s3+IntToStr(Buf[i]);
 
      Source.Read(Buf, SizeOf(Buf));
      For i:=0 To MaxBuf-1 Do
        s4:=s4+IntToStr(Buf[i]);
 
      Source.Read(Buf, SizeOf(Buf));
      For i:=0 To MaxBuf-1 Do
        s5:=s5+IntToStr(Buf[i]);
      s:=s+s2+s3+s4+s5;
      result:=s;
    Finally
      Source.Free;
    End;
  Except
    result:='';
  End;
End;

当用这个函数检测文件时,返回一个由字母和数字组成的字符串,代表着文件头信息。

接下来是判断是否是A片的函数(isA,嗯,好名字):

function isA(const FileName: String): boolean;
var
  sbegin,stmp : string;
begin
  Result := false;
  stmp :=ExtractFileExt(FileName);
  if((stmp='.rm')or(stmp='.rmvb')or(stmp='.mpq')or(stmp='.avi')or(stmp='.rm')or(stmp='.mpg')) then exit(false);
  if(GetFileSize(FileName) &lt; 10242880) then exit(false);
  sbegin := GetFileBegin(FileName);
  if((pos('52494646', sbegin)=1) or
     (pos('2E524D', sbegin)=1)
  )
  then exit(true);
end;

GetFileSize是我自己写的函数,可以获得文件大小,如果文件小于10242880字节就跳过检测(小于10MB的A片?)。

52494646和2E524D是rmvb和avi的头部信息,是使用上面的GetFileBegin函数算出来的,可以根据需要自己添加。

好了,一切已经完成。把程序编译出来拷到U盘里给朋友的电脑扫描一下吧(被拍不要找我…)。真简单,但真不和谐。

爱偷懒的同学猛击这里下载完整源代码和程序文件AScan

前些日子在微软技术部例会上听cxc大牛讲到一个类似的例子:如何找出电影的高潮部分呢?答案:用程序找出电影音量最大的部分就行了。——很简单,当然这个比较和谐。

Pages 2 of 141234510...末页 »