??xml version="1.0" encoding="utf-8" standalone="yes"?>久久狠狠爱亚洲综合影院,亚洲色欲久久久久综合网 ,久久精品无码专区免费 http://www.shnenglu.com/swo2006/zh-cnSat, 10 May 2025 09:17:08 GMTSat, 10 May 2025 09:17:08 GMT60SDL: ?Linux 变得有趣 (zz)http://www.shnenglu.com/swo2006/archive/2007/03/23/20467.htmlswoswoFri, 23 Mar 2007 12:14:00 GMThttp://www.shnenglu.com/swo2006/archive/2007/03/23/20467.htmlhttp://www.shnenglu.com/swo2006/comments/20467.htmlhttp://www.shnenglu.com/swo2006/archive/2007/03/23/20467.html#Feedback0http://www.shnenglu.com/swo2006/comments/commentRss/20467.htmlhttp://www.shnenglu.com/swo2006/services/trackbacks/20467.html

未显C需?JavaScript 的文档选项





Sam Lantinga, 首席E序? Loki Entertainment Software

1999 q? 9 ? 01 ?/p>

Sam Lantinga ?Simple DirectMedia Layer (SDL) 库的作者和 Loki Entertainment 的首席开发h员,他将向?zhn)介绍一U将游戏UL?Linux上的优秀工具。SDL 是一个跨q_代码UL的理惛_P它支持许多^収ͼ如Linux、Solaris、IRIX、FreeBSD ?MacOSQ这对于那些认ؓ(f)可以? Linux上开发商业Y件的 Linux 开发者来说是一大进步。他向社区的前辈之一讨教SDL 如何?Linux 用户享受Mq_上最好的游戏QSDL如何帮助开发者跟上下一代计机游戏qL(fng)要求?/blockquote> Sam Lantinga ?Simple DirectMedia Layer (SDL) 库的作者和 Loki Entertainment 的首席开发h员,他将向?zhn)介绍一U将游戏UL?Linux 上的优秀工具。SDL 是一个跨q_代码UL的理惛_P它支持许多^収ͼ? Linux、Solaris、IRIX、FreeBSD ?MacOSQ这对于那些认ؓ(f)可以?Linux 上开发商业Y件的 Linux 开发者来说是一大进步。他向社区的前辈之一讨教 SDL 如何?Linux 用户享受Mq_上最好的游戏QSDL 如何帮助开发者跟上下一代计机游戏qL(fng)要求?/i>

自从 Linus 首先开发出 Linux 时开始,到现?Linux 成ؓ(f)所有黑客的梦想q且遍及(qing)全世界,Linux 开发最重要的元素之一是 OS 上游戏的质量和可用性。游戏是我们用来׃和休闲的。它们可以提高创造力q拓展思\。游戏还可以用来量操作pȝ的性能。由于游戏越来越复杂Q它们迫使每 个子pȝD极限。每当我装配一个系l时Q首先要做的是装入一个游戏ƈ试玩Q以“测试”每一的性能?/a>

Linux 上的游戏已经存在了很长时间。从早期?NetTrekQ到受高度赞扬的 DOOM!? L(fng) (Quake)Qh们已l可以在 Linux 上玩游戏了。但问题是没有够的游戏。没有哪家大公司?Linux 创作能生蘪动效应的游戏。但是,׃该操作系l变得日益流行,q种情况正开始改善?

Linux 上最早期的游戏?X11 协议。但是对于游戏来_(d)X11 实在太慢了,因ؓ(f)它是针对在网l上透明q行的基于菜单的应用而设计的协议。用它的游戏通常没有l丽的画面,而且q行得相当慢? DOOM!? 一个值得注意的例外,虽然它?X11Q但是它通过使用 MIT ׃n内存扩展可以使动L畅Qƈ提供了逼真的三l效果。还有一些游戏? SVGA 囑Ş库,SVGAlib。我最喜欢的一个老游戏是重力战争 (Gravity Wars)Q它对其模拟的?Amiga 游戏 Gravity Force 做了重大改动。但使用 SVGAlib 的程序只能适用于少数受支持的显卡?

早期 X11 游戏Q?/b> 争霸 (Craft)的图片? 话 2 (Myth 2)的图片,Loki 出品
单击以放大图? src= 单击以放大图? src=

今天Q游戏开发者有了更多的选择。仍然可以? X 工具或全屏 APIQ如 SVGAlib ?fbconQ来~写游戏Q但他们现在q有许多游戏库可以用。Simple DirectMedia Layer 库是 Linux 上最好的低层游戏开?API 之一?/a>

SDL 是什么?
Simple DirectMedia Layer 库,U? SDLQ是为数不多的商业游戏开发公怋用的免费软g库之一。它提供跨^台的二维帧缓冲区囑Ş和音频服务,它支?Linux、Win32 ? BeOS。也不同E度地支持其它^収ͼ包括 Solaris、IRIX、FreeBSD ? MacOS。除了大量的服务Q包括线E、独立于字节存储ơ序的宏?CD 音频QSDL q提供了一个简单的 APIQ它允许(zhn)尽可能接近本机g。?SDL 有三重优点:(x)E_、简单和灉|?

  • E_。如?SDL 不向 API 提供可靠的支持,那么那些爱好者和商业公司׃能用它。因Z用了 SDLQ就d了错误修正ƈ增强了性能Q也加Z API 的强健性。就像内核开发是分步q行的,SDL 的开发也是分步进行的Q其中一部分是可靠稳定的 APIQ其它部分是新功能和构思的沙箱?
  • ?/b>。SDL 被设计成一个简单的 APIQ以最的代码实现(zhn)的构思。比如,我最q从 Linux 演示l? Optimum中移植了一些演C程序,我将它们?X11 代码替换?SDL 代码Q请参见下面的列表)。?zhn)可以看到QSDL 代码非常易于~写和理解?

    X11 代码

    int init_x (int X, int Y,
    int W, int H, int bpp,
    const char *Name) {
    XPixmapFormatValues *formatList;
    int formatCount;
    int i;
    int formatOk;
    int scanlineLength;
    XGCValues gcVal;
    unsigned long gcMask;
    dis = XOpenDisplay ( NULL );
    if ( dis == NULL) {
    fprintf ( stderr , "Error :\n" );
    fprintf ( stderr , " Cannot connect to Display.\n");
    exit (1);
    }
    screen = DefaultScreen ( dis );
    depth = DefaultDepth ( dis , screen );
    width = DisplayWidth ( dis , screen );
    height = DisplayHeight ( dis , screen );

    winRoot = DefaultRootWindow ( dis );
    winAttr.border_pixel = BlackPixel ( dis , screen );
    winAttr.background_pixel = BlackPixel ( dis , screen );
    winMask = CWBackPixel | CWBorderPixel;

    formatList = XListPixmapFormats( dis, &formatCount);
    if (formatList == NULL){
    fprintf ( stderr , " Cannot get pixmap list\n");
    exit (1);
    }
    formatOk=-1;
    for (i=0; ibytes_per_line*xim->height,
    IPC_CREAT|0777);
    xim->data = SHMInfo.shmaddr = (char *)shmat(SHMInfo.shmid, 0, 0);
    SHMInfo.readOnly = False;
    XShmAttach(dis, &SHMInfo);
    XSync(dis, False);
    buffer=(unsigned char *)xim->data;
    #else
    buffer = (unsigned char *)calloc(W*H, pixmapFormat.bits_per_pixel/8);
    xim = XCreateImage ( dis , CopyFromParent , depth , ZPixmap , 0 ,
    (char *) buffer , W , H ,
    pixmapFormat.scanline_pad, scanlineLength);
    if (xim == NULL){
    fprintf(stderr, " Couldnt create Ximage..\n");
    exit(-1);
    }
    #endif
    gcVal.foreground = 0;
    gcVal.background = 0;
    gcMask = GCForeground | GCBackground;
    gc = XCreateGC ( dis , win , gcMask , &gcVal );
    if (depth==24)
    depth = pixmapFormat.bits_per_pixel;
    return (depth);
    }

    SDL 代码

    int init_x (int X, int Y,
    int W, int H, int bpp,
    const char *Name) {
    int i;
    if ( SDL_Init(SDL_INIT_VIDEO) < 0 )
    {
    fprintf ( stderr , "Erreur :\n" );
    fprintf ( stderr , " Impossible de se connecter au Display\n");
    exit (1);
    }
    screen = SDL_SetVideoMode(W, H, bpp, SDL_SWSURFACE|SDL_HWPALETTE);
    if ( screen == NULL )
    {
    fprintf ( stderr , "Erreur :\n" );
    fprintf ( stderr , " Impossible de se connecter au Display\n");
    exit (1);
    }
    SDL_WM_SetCaption ( Name, Name );
    for ( i=SDL_NOEVENT; iformat->BitsPerPixel;
    width = screen->w;
    height = screen->h;
    buffer = (unsigned char *)screen->pixels;
    return (depth);
    }

  • 灉|? q回C面的 Optimum 演示代码CZQ只要移植到 SDLQƈ定一些数据假设,那么Ҏ(gu)不必改动代码Q演C就可以?Win32、BeOS ?Linux 控制Cq行了。灵zL的另一斚w体现在尽代码完全是跨^台的Q但不会(x)把?zhn)和底层实现隔开。SDL 提供了函? SDL_GetWMInfo()Q该函数可以让?zhn)讉K底层驱动E序的专用窗口信息。Loki Entertainment Software q泛使用q一技术ؓ(f)它们的游戏智能窗口管理器交互?

    Optimum 演示版的屏幕快照

q种坚如石般的E_、简单和强大功能的组合已l给 Linux 带来了一些极其引人入胜的游戏Q包? Hopkins F.B.I.? 文明Q力量的呼唤 (Civilization: Call To Power)? 话 2QSoulblighter (MythII: Soulblighter)? 铁\大亨 2 (Railroad Tycoon II){等。编E爱好者和商业公司使用q个库的事实表示它正在日益提高其功能和稳定性。这W合实际游戏的实际需要?

文明Q力量的呼唤 (Civilization: Call To Power)的图? 天旋地{ 2 (Descent 2)的图?
单击以放大图? src= 单击以放大图? src=



swo 2007-03-23 20:14 发表评论
]]>
Iterators and the Generic for(LUA)http://www.shnenglu.com/swo2006/archive/2006/11/28/15725.htmlswoswoTue, 28 Nov 2006 04:59:00 GMThttp://www.shnenglu.com/swo2006/archive/2006/11/28/15725.htmlhttp://www.shnenglu.com/swo2006/comments/15725.htmlhttp://www.shnenglu.com/swo2006/archive/2006/11/28/15725.html#Feedback0http://www.shnenglu.com/swo2006/comments/commentRss/15725.htmlhttp://www.shnenglu.com/swo2006/services/trackbacks/15725.html在这一章我们讨Zؓ(f)范性for写P代器, 我们从一个简单的q代器开?然后我们学习(fn)如何通过利用范性for的强大之处写出更高效的P代器.
7.1 q代器与闭包
 q代器是一U支持指针类型的l构,它可以遍历集合的每一个元?在Lua中我们常怋用函数来描述q代?每次调用该函数就q回集合的下一个元?
  q代器需要保留上一ơ成功调用的状态和下一ơ成功调用的状?也就是他知道来自于哪里和要前往哪里.闭包提供的机制可以很Ҏ(gu)实现q个d.C:闭包 是一个内部函?它可以访问一个或者多个外部函数的外部局部变?每次闭包的成功调用后q些外部局部变量都保存他们的?状?.当然如果要创Z个闭? 必须要创建其外部局部变?所以一个典型的闭包的结构包含两个函?一个是闭包自己;另一个是工厂(创徏闭包的函?.
 举一个简单的例子,我们Z个list写一个简单的q代?与ipairs()不同的是我们实现的这个P代器q回元素的D不是烦(ch)引下?
  function list_iter (t)
       local i = 0
       local n = table.getn(t)
       return function ()
                i = i + 1
                if i <= n then return t[i] end
              end
     end
 q个例子中list_iter 是一个工?每次调用他都?x)创Z个新的闭?q代器本w?.闭包包村内部局部变?t,i,n),因此每次调用他返回list中的下一个元素?当list中没有值时,q回nil.我们可以在while语句中用这个P代器:
  t = {10, 20, 30}
     iter = list_iter(t)    -- creates the iterator
     while true do
       local element = iter()   -- calls the iterator
       if element == nil then break end
       print(element)
     end
 我们设计的这个P代器也很Ҏ(gu)用于范性for语句
  t = {10, 20, 30}
     for element in list_iter(t) do
       print(element)
     end
 范性forP代@环处理所有的薄记(bookkeeping):首先调用q代工厂;内部保留q代函数,因此我们不需要iter变量;然后在每一个新的P代处调用q代器函?当P代器q回nil时@环结?后面我们看到范性for能胜L多的d).
 下面看一个稍微高U一点的例子:我们写一个P代器遍历一个文件内的所有匹配的单词.Z实现目的,我们需要保留两个?当前行和在当前行的偏U量,我们使用两个外部局部变量line,pos保存q两个?
  function allwords ()
       local line = io.read()  -- current line
       local pos = 1           -- current position in the line
       return function ()      -- iterator function
         while line do         -- repeat while there are lines
           local s, e = string.find(line, "%w+", pos)
           if s then           -- found a word?
             pos = e + 1       -- next position is after this word
             return string.sub(line, s, e)     -- return the word
           else
             line = io.read()  -- word not found; try next line
             pos = 1           -- restart from first position
           end
         end
         return nil            -- no more lines: end of traversal
       end
     end
  q代函数的主体部分调用了string.find函数,string.find在当前行从当前位|开始查扑֌配的单词,例子中匹配的单词使用模式'%w+ '描述?如果查找C个单?q代函数更新当前位置pos为单词后的第一个位|?q且q回q个单词(string.sub函数从line中提取两个位|? 参数之间的子?.否则q代函数d新的一行ƈ重新搜烦(ch).如果没有line可读q回nill束.
 管q代函数有些复杂,但用v来是很直观的: 
  for word in allwords() do
        print(word)
     end
    通常情况?q代函数都难写易?q不是一个大问题:一般Lua~程不需要自己定义P代函?而是使用语言提供?除非实需要自己定?
7.2 范性for的语义?
  前面我们看到的P代器有一个缺?每次调用都需要创Z个闭?大多数情况下q种做法都没什么问?例如在allwordsq代器中创徏一个闭包的代h(hun)? 赯整个文g来说微不道,然后在有些情况下创徏闭包的代h不能忍受?在这些情况下我们可以使用范性for本n来保存P代的状?
 前面我们看到在@环过E中范性for在自己内部保存P代函?实际上它保存三个?q代函数,状态常量和控制变量.下面详细说明.
 范性for的文法如?
  for <var-list> in <exp-list> do
       <body>
     end
 <var-list>是一个或多个以逗号分割变量名的列表,<exp-list>是一个或多个以逗号分割的表辑ּ列表,通常情况下exp-list只有一个?q代工厂的调?
  for k, v in pairs(t) do
       print(k, v)     
        end       
    变量列表k,v;表达式列表pair(t),在很多情况下变量列表也只有一个变?比如:
     for line in io.lines() do        
         io.write(line, '\n')
        end              
    我们U变量列表中W一个变量ؓ(f)控制变量,其gؓ(f)nil时@环结?
    下面我们看看范性for的执行过E?
    首先,初始?计算in后面表达式的?表达式应该返回范性for需要的三个?q代函数,状态常量和控制变量;与多Dg?如果表达式返回的l果个数不三个?x)自动用nil补Q多出部分会(x)被忽?
    W二,状态常量和控制变量作ؓ(f)参数调用q代函数(注意:对于forl构来说,状态常量没有用?仅仅在初始化时获取他的值ƈ传递给q代函数).
    W三,P代函数返回的Dl变量列?
    W四,如果q回的第一个gؓ(f)nil循环l束,否则执行循环?
    W五,回到W二步再ơ调用P代函?
    更精的来说:
     for var_1, ..., var_n in explist do block end
 {h(hun)?br />  do
       local _f, _s, _var = explist
       while true do
         local var_1, ... , var_n = _f(_s, _var)
         _var = var_1
         if _var == nil then break end
         block
       end
     end
 如果我们的P代函数是fQ状态常量是s,控制变量的初始值是a0,那么控制变量@?a1=f(s,a0);a2=f(s,a1);...直到ai=nil
7.3 无状态的q代?br />   无状态的q代器是指不保留M状态的q代?因此在@环中我们可以利用无状态P代器避免创徏闭包p额外的代?
   每一ơP?q代函数都是用两个变?状态常量和控制变量)的g为参数被调用,一个无状态的q代器只利用q两个值可以获取下一个元?q种无状态P代器的典型的单的例子是ipairs,他遍历数l的每一个元?
    a = {"one", "two", "three"}
     for i, v in ipairs(a) do
       print(i, v)
     end
 q代的状态包括被遍历的表(循环q程中不?x)改变的状态常?和当前的索引下标(控制变量),ipairs和P代函数都很简?我们在Lua中可以这样实?
  function iter (a, i)
       i = i + 1
       local v = a[i]
       if v then
         return i, v
       end
     end
     
     function ipairs (a)
       return iter, a, 0
     end
  当Lua调用ipairs(a)开始@环时,他获取三个?q代函数iter,状态常量a和控制变量初始?;然后Lua调用iter(a,0)q回1, a[1](除非a[1]=nil);W二ơP代调用iter(a,1)q回2,a[2]...直到W一个非nil元素.
 Lua库中实现的pairs是一个用next实现的原始方?
  function pairs (t)
       return next, t, nil
     end
 q可以不使用ipairs直接使用next
  for k, v in next, t do
       ...
     end
 C:exp-listq回l果?x)被调整Z?所以Lua获取next,t,nil;切地说当他调用pairs时获?
7.4 多状态的q代?br />  很多情况?q代器需要保存多个状态信息而不是简单的状态常量和控制变量,最单的Ҏ(gu)是用闭?q有一U方法就是将所有的状态信息封装到table ?table作ؓ(f)q代器的状态常?因ؓ(f)q种情况下可以将所有的信息存放在table?所以P代函数通常不需要第二个参数.
 下面我们重写allwordsq代?q一ơ我们不是用闭包而是使用带有两个?line,pos)的table.
 开始P代的函数是很单的,他必返回P代函数和初始状?
  local iterator   -- to be defined later
     
     function allwords ()
       local state = {line = io.read(), pos = 1}
       return iterator, state
     end  真正的处理工作是在P代函数内完成:
  function iterator (state)
       while state.line do        -- repeat while there are lines
         -- search for next word
         local s, e = string.find(state.line, "%w+", state.pos)
         if s then                -- found a word?
           -- update next position (after this word)
           state.pos = e + 1
           return string.sub(state.line, s, e)
         else    -- word not found
           state.line = io.read() -- try next line...
           state.pos = 1          -- ... from first position
         end
       end
       return nil                 -- no more lines: end loop
     end
  我们应该可能的写无状态的q代?因ؓ(f)q样循环的时候由for来保存状?不需要创建对象花费的代h(hun)?如果不能用无状态的q代器实?应尽可能使用? ?可能不要用tableq种方式,因ؓ(f)创徏闭包的代仯比创建table?另外Lua处理闭包要比处理table速度快些.后面我们q将看到另一 U用协同来创徏q代器的方式,q种方式功能更强但更复杂.
7.4 真正的P代器
 q代器的名字有一些误?因ؓ(f)它ƈ没有q代,完成q代功能的是for语句,也许更好的叫法应该是'生成?;但是在其他语a比如java,C++q代器的说法已经很普遍了,我们也将沿用q种术语.
 有一U方式创Z个在内部完成q代的P代器.q样当我们用P代器的时候就不需要用@环了;我们仅仅使用每一ơP代需要处理的d作ؓ(f)参数调用q代器即?具体地说,q代器接受一个函C为参?q且q个函数在P代器内部被调?
 作ؓ(f)一个具体的例子,我们使用上述方式重写allwordsq代?
  function allwords (f)
       -- repeat for each line in the file
       for l in io.lines() do
         -- repeat for each word in the line
         for w in string.gfind(l, "%w+") do
           -- call the function
           f(w)
         end
       end
     end
 如果我们惌打印出单?只需?br />  allwords(print)
 更一般的做法是我们用匿名函CZ为参?下面的例子打印出单词'hello'出现的次?
  local count = 0
     allwords(function (w)
       if w == "hello" then count = count + 1 end
     end)
     print(count)
 用forl构完成同样的Q?
  local count = 0
     for w in allwords() do
       if w == "hello" then count = count + 1 end
     end
     print(count)
 真正的P代器风格的写法在Lua老版本中很流?那时q没有for循环.
 两种风格的写法相差不?但也有区?一斚w,W二U风格更Ҏ(gu)书写和理?另一斚w,forl构更灵z?可以使用break和continue语句 ;在真正的q代器风格写法中return语句只是从匿名函Cq回而不是退出@?

swo 2006-11-28 12:59 发表评论
]]>
虚拟世界http://www.shnenglu.com/swo2006/archive/2006/11/06/14735.htmlswoswoMon, 06 Nov 2006 09:27:00 GMThttp://www.shnenglu.com/swo2006/archive/2006/11/06/14735.htmlhttp://www.shnenglu.com/swo2006/comments/14735.htmlhttp://www.shnenglu.com/swo2006/archive/2006/11/06/14735.html#Feedback0http://www.shnenglu.com/swo2006/comments/commentRss/14735.htmlhttp://www.shnenglu.com/swo2006/services/trackbacks/14735.html

swo 2006-11-06 17:27 发表评论
]]>
lua |站http://www.shnenglu.com/swo2006/archive/2006/11/03/14619.htmlswoswoFri, 03 Nov 2006 05:54:00 GMThttp://www.shnenglu.com/swo2006/archive/2006/11/03/14619.htmlhttp://www.shnenglu.com/swo2006/comments/14619.htmlhttp://www.shnenglu.com/swo2006/archive/2006/11/03/14619.html#Feedback0http://www.shnenglu.com/swo2006/comments/commentRss/14619.htmlhttp://www.shnenglu.com/swo2006/services/trackbacks/14619.html

swo 2006-11-03 13:54 发表评论
]]>
关于sdl_gfx http://www.shnenglu.com/swo2006/archive/2006/11/01/14533.htmlswoswoWed, 01 Nov 2006 12:09:00 GMThttp://www.shnenglu.com/swo2006/archive/2006/11/01/14533.htmlhttp://www.shnenglu.com/swo2006/comments/14533.htmlhttp://www.shnenglu.com/swo2006/archive/2006/11/01/14533.html#Feedback0http://www.shnenglu.com/swo2006/comments/commentRss/14533.htmlhttp://www.shnenglu.com/swo2006/services/trackbacks/14533.htmlwww.devmaster.net



swo 2006-11-01 20:09 发表评论
]]>
Lua的多d机制——协E?coroutine) [转蝲]http://www.shnenglu.com/swo2006/archive/2006/11/01/14530.htmlswoswoWed, 01 Nov 2006 10:46:00 GMThttp://www.shnenglu.com/swo2006/archive/2006/11/01/14530.htmlhttp://www.shnenglu.com/swo2006/comments/14530.htmlhttp://www.shnenglu.com/swo2006/archive/2006/11/01/14530.html#Feedback0http://www.shnenglu.com/swo2006/comments/commentRss/14530.htmlhttp://www.shnenglu.com/swo2006/services/trackbacks/14530.html Lua的多d机制——协E?coroutine)




   q发是现实世界的本质特征Q而聪明的计算机科学家用来模拟q发的技术手D便是多d机制。大致上有这么两U多d技术,一U是抢占式多d (preemptive multitasking)Q它让操作系l来军_何时执行哪个d。另外一U就是协作式多Q?cooperative multitasking)Q它把决定权交给dQ让它们在自p为合适的时候自愿放弃执行。这两种多Q务方式各有优~点Q前者固有的同步问题使得E序l? 常有不可预知的行为,而后者则要求d具备相当的自律精?br />    协程(coroutine)技术是一U程序控制机Ӟ早在上世U?0q代已 提出Q用它可以很方便地实现协作式多Q务。在L的程序语a(如C++、Java、Pascal{?里我们很能看到协程的n影,但是现在不少动态脚本语 a(Python、Perl)却都提供了协E或与之怼的机Ӟ其中最H出的便是Lua?br />  
    Lua语言实现的协E是一U非对称 ?asymmetric)协程Q或U半对称?semi-asymmetric)协程Q又或干脆就叫半协程(semi-coroutine)。这U协E? 机制之所以被UCؓ(f)非对U的Q是因ؓ(f)它提供了两种传递程序控制权的操作:(x)一U是(?调用协程(通过coroutine.resume)Q另一U是挂v协程 q将E序控制权返回给协程的调用?通过coroutine.yield)。一个非对称协程可以看做是从属于它的调用者的Q二者的关系非常cM于例E? (routine)与其调用者之间的关系。既然有非对U式协程Q当然也有对称?symmetric)协程了,它的特点是只有一U传递程序控制权的操 作,卛_控制权直接传递给指定的协E。曾l有q么一U说法,对称式和非对U式协程机制的能力ƈ不等P但事实上很容易根据前者来实现后者。接下来我们q 代码来证明这个事实?br />  
  --对称式协E库coro.lua
  
  coro = {}
  --coro.main用来标识E序的主函数
  coro.main = function() end
  -- coro.current变量用来标识拥有控制权的协程Q?br />  -- 也即正在q行的当前协E?br />  coro.current = coro.main
  
  -- 创徏一个新的协E?br />  function coro.create(f)
    return coroutine.wrap(function(val)
                return nil,f(val)
               end)
  end
  
  -- 把控制权?qing)指定的数据val传给协程k
  function coro.transfer(k,val)
    if coro.current ~= coro.main then
     return coroutine.yield(k,val)
    else
     -- 控制权分z@?br />     while k do
       coro.current = k
       if k == coro.main then
        return val
       end
       k,val = k(val)
     end
     error("coroutine ended without transfering control...")
    end
  end
  
  如果暂时q弄不懂上面的程序,没关p,看看如何使用q个库后再回头分析。下面是使用CZQ?br />  
  require("coro.lua")
  
  function foo1(n)
    print("1: foo1 received value "..n)
    n = coro.transfer(foo2,n + 10)
    print("2: foo1 received value "..n)
    n = coro.transfer(coro.main,n + 10)
    print("3: foo1 received value "..n)
    coro.transfer(coro.main,n + 10)
  end
  
  function foo2(n)
    print("1: foo2 received value "..n)
    n = coro.transfer(coro.main,n + 10)
    print("2: foo2 received value "..n)
    coro.transfer(foo1,n + 10)
  end
  
  function main()
    foo1 = coro.create(foo1)
    foo2 = coro.create(foo2)
    local n = coro.transfer(foo1,0)
    print("1: main received value "..n)
    n = coro.transfer(foo2,n + 10)
    print("2: main received value "..n)
    n = coro.transfer(foo1,n + 10)
    print("3: main received value "..n)
  end
  
  --把main设ؓ(f)d?协程)
  coro.main = main
  --coro.main设ؓ(f)当前协程
  coro.current = coro.main
  --开始执行主函数(协程)
  coro.main()
  
  
   上面的示例定义了一个名为main的主函数Q整个程序由它而始Q也因它而终。ؓ(f)什么需要一个这L(fng)d数呢Q上面说了,E序控制权可以在对称式协E之? 自由地直接传递,它们之间无所谓谁从属于谁的问题,都处于同一个层U,但是应用E序必须有一个开始点Q所以我们定义一个主函数Q让它点燃程序运行的导火 Uѝ虽说各个协E都是^{的Q但做ؓ(f)E序q行原动力的dC然n有特D的C(q个世上哪有l对的^{!)Qؓ(f)此我们的库专门用了一? coro.main变量来保存主函数Qƈ且在它执行之前要它设ؓ(f)当前协程(虽然上面的main实际只是一个普通函数而非一个真正的协程Q但qƈ无太大的 关系Q以后主函数也被UCؓ(f)dE?。示例运行的l果是:(x)
  
  1: foo1 received value 0
  1: foo2 received value 10
  1: main received value 20
  2: foo2 received value 30
  2: foo1 received value 40
  2: main received value 50
  3: foo1 received value 60
  3: main received value 70
  
  协程的执行序列是Qmain->foo1->foo2->main->foo2->foo1->main->foo1->main?br />  
     coro.transfer(k,val)函数中k是将要接收程序控制权的协E,而val是传递给k的数据。如果当前协E不是主协程Q? tansfer(k,val)q单地利用coroutine.yield(k,val)当前协E挂起ƈ传回两项数据Q即E序控制权的下一站和传递给? 的数据;否则q入一个控制权分派(dispatch)循环Q该循环(?启动(resume)k协程Q等待它执行到挂?suspend)QƈҎ(gu)此时? E传回的数据来决定下一个要(?启动的协E。从应用CZ来看Q协E与协程之间g是用transfer直接传递控制权的,但实际上q个传递还是通过了主 协程。每一个在dE里被调?比较coro.current和coro.main是否相同卛_判断?的transfer都相当于一个协E管理器Q它? 断地(?启动一个协E,控制权交出去,然后{那个协E挂h又将控制权收回,然后??启动下一个协E?..Q这个动作不?x)停止,除?lt; 1>??启动的协E是dE;<2>某个协程没有提供控制权的下一个目的地。很昄Q每一轮分z@环开始时都由dE把握控制权Q? 在@环过E中如果控制权的下一站又是主协程的话意味着q个当初把控制权交出ȝdEtransfer操作应该l束了,所以函数直接返回val从而结? q轮循环。对于情?lt;2>Q因为coro.create(f)创徏的协E的体函?body function)实际是function(val) return nil,f(val) endQ所以当函数f的最后一条指令不是transferӞq个协程l将执行完毕q把nil和函数f的返回g赯回。如果k是这L(fng)协程Q? transfer执行完k,val = k(val)语句后k值就成了nilQ这被视Z个错误,因ؓ(f)E序此时没法定下一个应??启动的协E到底是谁。所以在对称式模型下Q每一个协E?? 然主协程出外)最后都必须昑ּ地将控制权传递给其它的协E。根据以上分析,应用CZ的控制权的分zֺ为:(x)
  
  W一轮分z? main->foo1->main->foo2->main->main(l束)
  W二轮分z? main->foo2->main->foo1->main->main(l束)
  W三轮分z? main->foo1->main->main(l束)
  
     ׃可以直接指定控制权传递的目标Q对U式协程机制拥有极大的自由,但得到这U自q代h(hun)却是牺牲E序l构。如果程序稍微复杂一点,那么即是非? 有经验的E序员也很难对程序流E有全面而清晰的把握。这非常cMgoto语句Q它能让E序跌{CQ何想ȝ地方Q但Z却很隄解充斥着goto的程序? 非对U式协程h良好的层ơ化l构关系Q??启动q些协程与调用一个函数非常类|(x)??启动的协E得到控制权开始执行,然后挂v(或结?q将控制 权返回给协程调用者,q与计算机先哲们倡导的结构化~程风格完全一致?br />  
    lg所qͼLua提供的非对称式协E不但具有与对称式协E一样强大的能力Q而且q能避免E序员滥用机制写出结构؜qE序?img src ="http://www.shnenglu.com/swo2006/aggbug/14530.html" width = "1" height = "1" />

swo 2006-11-01 18:46 发表评论
]]>
lua的网址http://www.shnenglu.com/swo2006/archive/2006/11/01/14528.htmlswoswoWed, 01 Nov 2006 09:40:00 GMThttp://www.shnenglu.com/swo2006/archive/2006/11/01/14528.htmlhttp://www.shnenglu.com/swo2006/comments/14528.htmlhttp://www.shnenglu.com/swo2006/archive/2006/11/01/14528.html#Feedback0http://www.shnenglu.com/swo2006/comments/commentRss/14528.htmlhttp://www.shnenglu.com/swo2006/services/trackbacks/14528.html
http://www.personal.kent.edu/~rmuhamma/Compilers/compiler.html

http://www.luachina.net/bbs/125/ShowPost.aspx

http://lua-users.org/wiki/WikiHelp

http://luaplus.org/tiki-index.php


swo 2006-11-01 17:40 发表评论
]]>
在你的游戏中应用LUA(ZT)Q{载)http://www.shnenglu.com/swo2006/archive/2006/11/01/14475.htmlswoswoWed, 01 Nov 2006 05:49:00 GMThttp://www.shnenglu.com/swo2006/archive/2006/11/01/14475.htmlhttp://www.shnenglu.com/swo2006/comments/14475.htmlhttp://www.shnenglu.com/swo2006/archive/2006/11/01/14475.html#Feedback0http://www.shnenglu.com/swo2006/comments/commentRss/14475.htmlhttp://www.shnenglu.com/swo2006/services/trackbacks/14475.html
在你的游戏中应用LuaQ?Q:(x)在你的游戏代码中q行解释?br />
  通常Q你希望在你的游戏开始的时候读取一些信息,以配|你的游戏,q些信息通常都是攑ֈ一个文本文件中Q在你的游戏启动的时候,你需要打开q个文gQ然后解析字W串Q找到所需要的信息?br />
  是的Q或怽认ؓ(f)q样p够了Qؓ(f)什么还要用Lua呢?

  应用于“配|”这个目的,Lua提供l你更ؓ(f)强大Q也更ؓ(f)灉|的表达方式,在上一U方式中Q你无法Ҏ(gu)某些条g来配|你的游戏,Lua提供l你灉|的表达方式,你可以类gq样来配|你的游戏:(x)

if player:is_dead() then
do_something()
else
do_else()
end

更ؓ(f)重要的是Q在你做了一些修改之后,完全不需要重新编译你的游戏代码?br />
通常Q在游戏中你q不需要一个单独的解释器,你需要在游戏来运行解释器Q下面,让我们来看看Q如何在你的代码中运行解释器Q?br />
//q是lua所需的三个头文g
//当然Q你需要链接到正确的lib
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

int main(int argc, char *argv[])
{
lua_State *L = lua_open();
luaopen_base(L);
luaopen_io(L);

const char *buf = "print('hello, world!')";

lua_dostring(buf);

lua_close(L);

return 0;
}

E序输出Qhello, world!

有时你需要执行一D字W串Q有时你可能需要执行一个文Ӟ当你需要执行一个文件时Q你可以q么做:(x)
lua_dofile(L, "test.lua");

看,非常单吧?br />






在你的游戏中应用LuaQ?Q:(x)Getting Value

在上一文章我们能够在我们的游戏代码中执行Lua解释器,下面让我们来看看如何从脚本中取得我们所需要的信息?br />
首先Q让我来单的解释一下Lua解释器的工作机制QLua解释器自w维护一个运行时栈,通过q个q行时栈QLua解释器向LE序传递参敎ͼ所以我们可以这h得到一个脚本变量的|(x)

lua_pushstring(L, "var"); //变量的名字攑օ?br />lua_gettatbl(L, LUA_GLOBALSINDEX);变量的值现在栈?br />
假设你在脚本中有一个变?var = 100
你可以这h得到q个变量|(x)
int var = lua_tonumber(L, -1);

怎么P是不是很单?

Lua定义了一个宏让你单的取得一个变量的|(x)
lua_getglobal(L, name)

我们可以q样来取得一个变量的|(x)
lua_getglobal(L, "var"); //变量的值现在栈?br />int var = lua_tonumber(L, -1);

完整的测试代码如下:(x)

#include "lua.h"
#inculde "lauxlib.h"
#include "lualib.h"

int main(int argc, char *argv[])
{
lua_State *L = lua_open();
luaopen_base(L);
luaopen_io(L);

const char *buf = "var = 100";

lua_dostring(L, buf);

lua_getglobal(L, "var");
int var = lua_tonumber(L, -1);

assert(var == 100);

lua_close(L);

return 0;
}






在你的游戏中应用LuaQ?Q:(x)调用函数

假设你在脚本中定义了一个函敎ͼ(x)

function main(number)
number = number + 1
return number
end

在你的游戏代码中Q你希望在某个时刻调用这个函数取得它的返回倹{?br />
在Lua中,函数{同于变量,所以你可以q样来取得这个函敎ͼ(x)

lua_getglobal(L, "main");//函数现在栈顶

现在Q我们可以调用这个函敎ͼq传递给它正的参数Q?br />
lua_pushnumber(L, 100); //参数压?br />lua_pcall(L, 1, 1, 0); //调用函数Q有一个参敎ͼ一个返回?br />//q回值现在栈?br />int result = lua_tonumber(L, -1);

result 是函数的返回?br />
完整的测试代码如下:(x)

#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

int main(int argc, char *argv[])
{
lua_State *L = lua_open();
luaopen_base(L);

const char *buf = "function main(number) number = number + 1 return number end";

lua_dostring(buf);

lua_getglobal(L, "main");
lua_pushnumber(L, 100);
lua_pcall(L, 1, 1, 0);

int result = lua_tonumber(L, -1);

assert(result == 101);

lua_close(L);

return 0;
}





在你的游戏中应用LuaQ?Q:(x)扩展Lua


Lua本n定位在一U轻量的,灉|的,可扩充的脚本语言Q这意味着你可以自q扩充LuaQؓ(f)你自q游戏量n定做一个脚本语a?br />
你可以在LE序中向脚本提供你自定的apiQ供脚本调用?br />
Lua定义了一U类型:(x)lua_CFunctionQ这是一个函数指针,它的原型是:(x)
typedef int (*lua_CFunction) (lua_State *L);

q意味着只有q种cd的函数才能向Lua注册?br />
首先Q我们定义一个函?br />
int foo(lua_State *L)
{
//首先取出脚本执行q个函数时压入栈的参?br />//假设q个函数提供一个参敎ͼ有两个返回?br />
//get the first parameter
const char *par = lua_tostring(L, -1);

printf("%s\n", par);

//push the first result
lua_pushnumber(L, 100);

//push the second result
lua_pushnumber(L, 200);

//return 2 result
return 2;
}

我们可以在脚本中q样调用q个函数

r1, r2 = foo("hello")

print(r1..r2)

完整的测试代码如下:(x)

#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

int foo(lua_State *L)
{
//首先取出脚本执行q个函数时压入栈的参?br />//假设q个函数提供一个参敎ͼ有两个返回?br />
//get the first parameter
const char *par = lua_tostring(L, -1);

printf("%s\n", par);

//push the first result
lua_pushnumber(L, 100);

//push the second result
lua_pushnumber(L, 200);

//return 2 result
return 2;
}

int main(int argc, char *argv[])
{
lua_State *L = lua_open();
luaopen_base(L);
luaopen_io(L);

const char *buf = "r1, r2 = foo("hello") print(r1..r2)";

lua_dostring(L, buf);

lua_close(L);

return 0;
}

E序输出Q?br />hello
100200




在你的游戏中应用LuaQ?Q:(x)using lua in cpp


lua和主机程序交换参数是通过一个运行时栈来q行的,q行时栈的信息放在一个lua_State的结构中Qlua提供的api都需要一个lua_State*的指针,除了一个:(x)

lua_open();

q个函数返回一个lua_State*型的指针Q在你的游戏代码中,你可以仅仅拥有一个这L(fng)指针Q也可以有多个这L(fng)指针?br />
最后,你需要释放这个指针,通过函数Q?br />
lua_close(L);

注意q个事实Q在你的LE序中,open()与close()永远是成对出现的Q在c++中,如果有一些事情是成对出现的,q通常意味着你需要一个构造函数和一个析构函敎ͼ所以,我们首先对lua_State做一下封装:(x)

#ifndef LUA_EXTRALIBS
#define LUA_EXTRALIBS /* empty */
#endif

static const luaL_reg lualibs[] =
{
{"base", luaopen_base},
{"table", luaopen_table},
{"io", luaopen_io},
{"string", luaopen_string},
{"math", luaopen_math},
{"debug", luaopen_debug},
{"loadlib", luaopen_loadlib},
/* add your libraries here */
LUA_EXTRALIBS
{NULL, NULL}
};

q是lua提供l用L(fng)一些辅助的libQ在使用lua_State的时候,你可以选择打开或者关闭它?br />
完整的类实现如下Q?br />

//lua_State
class state
{
public:
state(bool bOpenStdLib = false)
:
err_fn(0)
{
L = lua_open();

assert(L);

if (bOpenStdLib)
{
open_stdlib();
}
}

~state()
{
lua_setgcthreshold(L, 0);
lua_close(L);
}

void open_stdlib()
{
assert(L);

const luaL_reg *lib = lualibs;
for (; lib->func; lib++)
{
lib->func(L); /* open library */
lua_settop(L, 0); /* discard any results */
}
}

lua_State* get_handle()
{
return L;
}

int error_fn()
{
return err_fn;
}

private:
lua_State *L;

int err_fn;
};


通常我们仅仅在游戏代码中使用一个lua_State*的指针,所以我们ؓ(f)它实C个单Ӟ默认打开所有lua提供的lib:


//return the global instance
state* lua_state()
{
static state L(true);

return &L;
}




在你的游戏中应用LuaQ?Q:(x)using lua in cppQ封装栈操作Q 

前面提到了lua与主机程序是通过一个运行时栈来交换信息的,所以我们把Ҏ(gu)的访问做一下简单的装?br />
我们利用从c++的函数重载机制对q些操作做封装,重蝲提供l我们一U以l一的方式来处理操作的机制?br />
向lua传递信息是通过压栈的操作来完成的,所以我们定义一些Push()函数Q?br />
inline void Push(lua_State *L, int value);
inline void Push(lua_State *L, bool value);
...

对应单的c++内徏cdQ我们实现出相同的Push函数Q至于函数内部的实现是非常的单,只要利用lua提供的api来实现即可,例如Q?br />
inline void Push(lua_State *L, int value)
{
lua_pushnumber(L, value);
}

q种方式带来的好处是Q在我们的代码中我们可以以一U统一的方式来处理压栈操作Q如果有一U类型没有定义相关的压栈操作Q将产生一个编译期错误?br />
后面我会(x)提到Q如何将一个用戯定义cd的指针传递到l(f)ua中,在那U情况下Q我们的基本代码无须改变Q只要添加一个相应的Push()函数卛_?br />
Cclose-open原则吧,它的意思是对修Ҏ(gu)闭的,Ҏ(gu)充是开攄Q好的类库设计允怽扩充它,而无M改它的实玎ͼ甚至无须重新~译?br />
《c++泛型设计新思维》一书提C一U技术叫type2typeQ它的本质是很简单:(x)

template <typename T>
struct type2type
{
typedef T U;
};

正如你看到的Q它q没有Q何数据成员,它的存在只是Z携带cd信息?br />
cd到类型的映射在应用于重蝲函数时是非常有用的,应用type2typeQ可以实现编译期的分z?br />
下面看看我们如何在从栈中取得lua信息时应用type2typeQ?br />
试cdQ由于lua的类型系l与c++是不相同的,所以,我们要对栈中的信息做一下类型检?br />
inline bool Match(type2type<bool>, lua_State *L, int idx)
{
return lua_type(L, idx) == LUA_TBOOLEAN;
}

cM的,我们要ؓ(f)cpp的内建类型提供相应的Match函数Q?br />
inline bool Match(type2type<int>, lua_State *L, int idx);
inline bool Match(type2type<const char*>, lua_State *L, int idx);

...

可以看出Qtype2type的存在只是ؓ(f)了在调用Match时决议到正确的函CQ由于它没有M成员Q所以不存在q行时的成本?br />
同样Q我们ؓ(f)cpp内徏cd提供Get()函数Q?br />
inline bool Get(type2type<bool>, lua_State *L, int idx)
{
return lua_toboolean(L, idx);
}

inline int Get(type2type<int>, lua_State *L, int idx)
{
return static_cast<int>(lua_tonumber(L, idx));
}

...

我想你可能注意到了,在int Get(type2type<int>)中有一个{型的动作Q由于lua的类型系l与cpp的类型不同,所以{型动作必ȝ?br />
除此之外Q在Get重蝲函数QsQ中q有一个小的l节Q每个Get的函数的q回值是不相同的Q因为重载机制是依靠参数的不同来识别的,而不是返回倹{?br />
前面说的都是一些基的封装,下来我们介l如何向lua注册一个多参数的c函数。还记得吗?利用lua的api只能注册int (*ua_CFunction)(lua_State *)型的c函数Q别忘记了,lua是用c写的?br />



在你的游戏中应用LuaQ?Q:(x)using lua in cppQ注册不同类型的c函数Q之一 

前面说到Q我们可以利用lua提供的apiQ向脚本提供我们自己的函敎ͼ在lua中,只有lua_CFunctioncd的函数才能直接向lua注册Qlua_CFunction实际上是一个函数指针:(x)
typedef int (*lua_CFunction)(lua_State *L);

而在实际的应用中Q我们可能需要向lua注册各种参数和返回值类型的函数Q例如,提供一个add脚本函数Q返回两个值的和:(x)

int add(int x, int y);

Z实现q个目的Q首先,我们定义个lua_CFunctioncd的函敎ͼ(x)

int add_proxy(lua_State *L)
{
//取得参数
if (!Match(TypeWrapper<int>(), L, -1))
return 0;
if (!Match(TypeWrapper<int>(), L, -2))
return 0;

  int x = Get(TypeWrapper<int>(), L, -1);
  int y = Get(TypeWrapper<int>(), L, -1);
 
  //调用真正的函?br />  int result = add(x, y);
 
  //q回l果
  Push(result);
 
  return 1;
}

现在Q我们可以向lua注册q个函数Q?br />
lua_pushstring(L, “add?;
lua_pushcclosure(L, add_proxy, 0);
lua_settable(L, LUA_GLOBALINDEX);

在脚本中可以q样调用q个函数Q?br />
print(add(100, 200))

从上面的步骤可以看出Q如果需要向lua注册一个非lua_CFunctioncd的函敎ͼ需要:(x)
1Q?函数实现一个封装调用?br />2Q?在封装调用函C从lua栈中取得提供的参数?br />3Q?使用参数调用该函数?br />4Q?向lua传递其l果?br />
注意Q我们目前只是针对全局c函数Q类的成员函数暂时不涉及(qing)Q在cpp中,cȝ静态成员函Cc函数cM?br />
假设我们有多个非lua_CFunctioncd的函数向lua注册Q我们需要ؓ(f)每一个函数重复上面的步骤Q生一个封装调用,可以看出Q这些步骤大多是机械的,因此Q我们需要一U方式自动的实现上面的步骤?br />
首先看步?Q在cpp中,产生q样一个封装调用的函数的最佳的方式是用templateQ我们需要提供一个lua_CFunctioncd的模板函敎ͼ在这个函C调用真正的向脚本注册的函敎ͼcM于这P(x)
template <typename Func>
inline int register_proxy(lua_State *L)

现在的问题在于:(x)我们要在q个函数中调用真正的函数Q那么我们必要在这个函C取得一个函数指针,然而,lua_CFunctioncd的函数又不允怽在增加别的参数来提供q个函数指针Q现在该怎么让regisger_proxy函数知道我们真正要注册的函数呢?

在oop中,g可以使用cL解决q个问题Q?br />
template <Func>
struct register_helper
{
explicit register_helper(Func fn) : m_func(fn)
{}
int register_proxy(lua_State *L);

protected:
Func m_func;
};

可是不要忘记Qlua_CFunctioncd指向的是一个c函数Q而不是一个成员函敎ͼ他们的调用方式是不一L(fng)Q如果将上面的int register_proxy()讄为静态成员函C不行Q因为我们需要访问类的成员变量m_func;

让我们再观察一下lua_CFunctioncd的函敎ͼ(x)

int register_proxy(lua_State *L);

我们看到Q这里面有一个lua_State*型的指针Q我们能不能真正的函数指针攑ֈq里面存储,到真正调用的时候,再从里面取出来呢Q?br />
Lua提供了一个api可以存储用户数据Q?br />Lua_newuserdata(L, size)

在适当的时刻,我们可以再取个数据:(x)

lua_touserdata(L, idx)

okQ现在传递函数指针的问题我们已经解决了,后面再看W二步:(x)取得参数?br />




在你的游戏中应用LuaQ?Q:(x)using lua in cppQ注册不同类型的c函数Q之?br />
在解决了传递函数指针的问题之后Q让我们来看看调用函数时?x)有一些什么样的问题?br />
? 先,当我们通过函数指针调用q个函数的时候,׃我们面对的是未知cd的函敎ͼ也就是说Q我们ƈ不知道参数的个数Q参数的cdQ还有返回值的cdQ所以我 们不能直接从lua栈中取得参数Q当?dng)我们可以通过q行时测试栈中的信息来得到l(f)ua传递进来的参数的个数和cdQ这意味着我们在稍后通过函数指针调用 函数时也需要动态的Ҏ(gu)参数的个数和cd来决议到正确的函敎ͼq样Q除了运行时的成本,cpp提供l我们的强类型检查机制的好处也剩不了多少了,我们需? 的是一U静态的~译时的“多态”?br />
在cpp中,臛_有两U方法可以实现这炏V最直接单的是用函数重载,q有一U是利用模板特化机制?br />
单的介绍一下模板特化:(x)

在cpp中,可以针对一个模板函数或者模板类写出一些特化版本,~译器在匚w模板参数时会(x)L最合适的一个版本。类gq样Q?br />
templat <typename T>
T foo()
{
T tmp();
return tmp;
}

//提供特化版本
template <>
int foo()
{
return 100;
}

在main()函数中,我们可以昄指定使用哪个版本的fooQ?br />
int main(int argc, char **argv)
{
cout << foo<int>() << endl;
return 0;
}

E序输?00Q而不?Q以上代码在 g++中编译通过Q由于vc6对于模板的支持不是很好,所以有一些模板的技术在vc6中可能不能编译通过?br />
所以最好用重载来解决q个问题Q在装函数调用中,我们首先取得q个函数指针Q然后,我们要提供一个Call函数来真正调用这个函敎ͼcM于这P(x)
//伪代?br />int Call(pfn, lua_State *L, int idx)

可是我们q不知道q个函数指针的类型,现在该怎么写呢Q别忘记了,我们的register_proxy()是一个模板函敎ͼ它有一个参数表CZq个指针的类型:(x)

template <typename Func>
int register_proxy(lua_State *L)
{
//伪代码,通过L参数取得q个指针
unsigned char *buffer = get_pointer(L);

//对这个指针做强制cd转化Q调用Call函数
return Call(*(Func*)buffer, L, 1);
}

由重载函数Call调用真正的函敎ͼq样Q我们可以用lua api注册相关的函敎ͼ下来我们提供一个注册的函数Q?br />
template <typename Func>
void lua_pushdirectclosure(Func fn, lua_State *L, int nUpvalue)
{
//伪代码,向L存储函数指针
save_pointer(L);

//向lua提供我们的register_proxy函数
lua_pushcclosure(L, register_proxy<Func>, nUpvalue + 1);
}

再定义相关的注册宏:(x)
#define lua_register_directclosure(L, func) \
lua_pushstring(L, #func);
lua_pushdirectclosure(func, L, 1);
lua_settable(L, LUA_GLOBALINDEX)

现在Q假设我们有一个int add(int x, int y)q样的函敎ͼ我们可以直接向lua注册Q?br />
lua_register_directclosure(L, add);

看,最后用v来很方便吧,我们再也不用手写那么多的装调用的代码啦Q不q问题还没有完,后面我们q得解决Call函数的问题?br />



在你的游戏中应用LuaQ?Q:(x)using lua in cppQ注册不同类型的c函数Q之三 


下面Q让我们集中_֊来解决Call重蝲函数的问题吧?br />
前面已经说过来,Call重蝲函数接受一个函数指针,然后从lua栈中Ҏ(gu)函数指针的类型,取得相关的参敎ͼq调用这个函敎ͼ然后返回值压入lua栈,cM于这P(x)

//伪代?br />int Call(pfn, lua_State *L, int idx)

现在的问题是pfn该如何声明?我们知道q是一个函数指针,然而其参数Q以?qing)返回值都是未知的cdQ如果我们知道返回值和参数的类型,我们可以用一个typedef来声明它Q?br />
typedef void (*pfn)();

int Call(pfn fn, lua_State *L, int idx);

我们知道的返回g?qing)参数的cd只是一个模板参数TQ在cpp中,我们不能q样写:(x)

template <typename T>
typedef T (*Func) ();

一U解军_法是使用cL板:(x)

template <typename T>
struct CallHelper
{
typedef T (*Func) ();
};

然后在Call中引用它Q?br />
template <typename T>
int Call(typename CallHelper::Func fn, lua_State *L, int idx)

注意typename关键字,如果没有q个关键字,在g++中会(x)产生一个编译警告,它的意思是告诉~译器,CallHelperQ:(x)Func是一个类型,而不是变量?br />
如果我们q样来解冻I需要在CallHelper中ؓ(f)每种情况大量定义各种cd的函数指针,q有一U方法,写法比较古怪,考虑一个函C参数的声明:(x)

void (int n);

首先是类型,然后是变量,而应用于函数指针上:(x)

typedef void (*pfn) ();
void (pfn fn);

事实上,可以typedef直接在参数表中写出来Q?br />
void (void (*pfn)() );

q样Q我们的Call函数可以直接q样写:(x)

//针对没有参数的Call函数
template <typename RT>
int Call(RT (*Func) () , lua_State *L, int idx);
{
//调用Func
RT ret = (*Func)();

//返回gllua
PushQL, retQ;

//告诉lua有多个q回?br />return 1;
}

//针对有一个参数的Call
template <typename T, typename P1>
int Call(RT (*Func)(), lua_State *L, int idx)
{
//从lua中取得参?br />if (!Match(TypeWrapper<P1>(), L, -1)
return 0;

RT ret = (*Func) (Get(TypeWrapper<P1>(), L, -1));

Push(L, ret);
return 1;
}

按照上面的写法,我们可以提供L参数个数的Call函数Q现在回到最初的时候,我们的函数指针要通过lua_State *L来存储,q只要利用lua提供的api可以了Q还记得我们的lua_pushdirectclosure函数吗:(x)

template <typename Func>
void lua_pushdirectclosure(Func fn, lua_State *L, int nUpvalue)
{
//伪代码,向L存储函数指针
save_pointer(L);

//向lua提供我们的register_proxy函数
lua_pushcclosure(L, register_proxy<Func>, nUpvalue + 1);
}

其中Qsave_pointer(L)可以q样实现Q?br />
void save_pointer(lua_State *L)
{
unsigned char* buffer = (unsigned char*)lua_newuserdata(L, sizeof(func));
memcpy(buffer, &func, sizeof(func));
}


而在register_proxy函数中:(x)

template <typename Func>
int register_proxy(lua_State *L)
{
//伪代码,通过L参数取得q个指针
unsigned char *buffer = get_pointer(L);
//对这个指针做强制cd转化Q调用Call函数
return Call(*(Func*)buffer, L, 1);
}
get_pointer函数可以q样实现Q?br />
unsigned char* get_pointer(lua_State *L)
{
  return (unsigned char*) lua_touserdata(L, lua_upvalueindex(1));
}

q一点能够有效运作主要依赖于q样一个事实:(x)

我们在lua栈中保存q个指针之后Q在没有Ҏ(gu)做Q何操作的情况下,又把它从栈中取了出来Q所以不?x)弄乱lua栈中的信息,CQlua栈中的数据是q户保证来清空的?br />
到现在,我们已经可以向lua注册L个参数的c函数了,只需单的一行代码:(x)

lua_register_directclosure(L, func)可以啦?br />






在你的游戏中应用LuaQ?Q:(x)Using Lua in cpp(基本数据cd、指针和引用)之一

Using Lua in cpp(基本数据cd、指针和引用)

前面介绍的都是针对cpp中的内徏基本数据cdQ然而,即是这P在面Ҏ(gu)针和引用的时候,情况也会(x)变得复杂h?br />
使用前面我们已经完成的宏lua_register_directclosure只能注册by value形式的参数的函数Q当参数中存在指针和引用的时候(再强调一ơ,目前只针对基本数据类型)Q?br />
1?如果是一个指针,通常实现函数的意图是以这个指针传递出一个结果来?br />2?如果是一个引用,同上?br />3?如果是一个const指针Q通常只有面对char*的时候才使用constQ实现函数的意图是,不会(x)改变q个参数的内宏V其它情况一般都避免出现使用const指针?br />4?如果是一个const引用Q对于基本数据类型来_(d)一般都避免出现q种情况?br />
Lua和cpp都允许函数用某种方式q回多个|对于cpp来说Q多个返回值是通过上述的第1和第2U情况返回的Q对于lua来说Q多个返回值可以直接返回:(x)

--in Lua
function swap(x, y)
tmp = x
x = y
y = tmp

return x, y
end

x = 100
y = 200

x, y = swap(x, y)

print(x..y)

E序输出Q?00100

同样的,在主机程序中Q我们也可以向Luaq回多个|(x)

int swap(lua_State *L)
{
//取得两个参数
int x = Get(TypeWrapper<int>(), L, -1);
int y = Get(TypeWrapper<int>(), L, -2);

//交换?br />int tmp = x;
x = y;
y = tmp;

//向Luaq回?br />Push(L, x);
Push(L, y);

  //告诉Lua我们q回了多个?br />return 2;
}

现在我们可以在Lua中这栯用这个函敎ͼ(x)

x = 100
y = 200

x, y = swap(x, y)

在我们的register_proxy函数中只能对基本数据cd的by value方式有效Q根据我们上面的分析Q如果我们能够在~译期知道,对于一个模板参数TQ?br />1?q是一个基本的数据cdQ还是一个用戯定义的数据类型?
2?q是一个普通的指针Q还是一个iteratorQ?br />3?q是一个引用吗Q?br />4?q是一个const 普通指针吗Q?br />5?q是一个const 引用吗?

如果我们能知道这些,那么Q根据我们上面的分析Q我们希望:(x)Q只针对基本数据cdQ?br />1?如果q是一个指针,我们希望把指针所指的内容q回lLua?br />2?如果q是一个引用,我们希望把引用的指返回给Lua?br />3?如果q是const指针Q我们希望将从Lua栈中取得的参C?br />          l调用函数?br />4?如果q是一个const引用Q我们也希望把从Lua栈中取得的参
          C递给调用函数?/div>


swo 2006-11-01 13:49 发表评论
]]>
Lua脚本语言入门Q{载)http://www.shnenglu.com/swo2006/archive/2006/11/01/14474.htmlswoswoWed, 01 Nov 2006 05:40:00 GMThttp://www.shnenglu.com/swo2006/archive/2006/11/01/14474.htmlhttp://www.shnenglu.com/swo2006/comments/14474.htmlhttp://www.shnenglu.com/swo2006/archive/2006/11/01/14474.html#Feedback0http://www.shnenglu.com/swo2006/comments/commentRss/14474.htmlhttp://www.shnenglu.com/swo2006/services/trackbacks/14474.html Lua脚本语言入门
作者:(x) 沐枫

Lua E序设计初步

作者:(x) 沐枫 Q第二h生成员)
版权所有{载请注明原出?br />
  在这文章中Q我惛_大家介绍如何q行LuaE序设计。我假设大家都学q至一门编E语aQ比如Basic或CQ特别是C。因为Lua的最大用途是在宿ȝ序中作ؓ(f)脚本使用的?br />  Lua 的语法比较简单,学习(fn)h也比较省力,但功能却q不弱?br />  在Lua中,一切都是变量,除了关键字。请Cq句话?br />
I. 首先是注?br />  写一个程序,L不了注释的?br />  在Lua中,你可以用单行注释和多行注释?br />  单行注释中,q箋两个减号"--"表示注释的开始,一直gl到行末为止。相当于C++语言中的"http://"?br />  多行注释中,?--[["表示注释开始,q且一直gl到"]]"为止。这U注释相当于C语言中的"/*?/"。在注释当中Q?[["?]]"是可以嵌套的?br />II. Lua~程
  l典?Hello world"的程序L被用来开始介l一U语a。在Lua中,写一个这L(fng)E序很简单:(x)
  print("Hello world")
  在Lua中,语句之间可以用分?Q?隔开Q也可以用空白隔开。一般来_(d)如果多个语句写在同一行的话,L用分号隔开?br />  Lua 有好几种E序控制语句Q如Q?br />
  条g控制Qif 条g then ?elseif 条g then ?else ?end
  While循环Qwhile 条g do ?end
  Repeat循环Qrepeat ?until 条g
  For循环Qfor 变量 = 初|l点|步进 do ?end
  For循环Qfor 变量1Q变?Q?Q变量N in表或枚D函数 do ?end

  注意一下,for的@环变量L只作用于for的局部变量,你也可以省略步进|q时候,for循环?x)?作ؓ(f)步进倹{?br />  你可以用break来中止一个@环?br />  如果你有E序设计的基Q比如你学过BasicQC之类的,你会(x)觉得Lua也不难。但Lua有几个地Ҏ(gu)明显不同于这些程序设计语a的,所以请特别注意?br />
  Q语句块
    语句块在C++中是?{"?}"括v来的Q在Lua中,它是用do ?end 括v来的。比如:(x)
    do print("Hello") end
    你可以在 函数 中和 语句?中定局部变量?br />
  Q赋D?br />    赋D句在Lua被强化了。它可以同时l多个变量赋倹{?br />    例如Q?br />    a,b,c,d=1,2,3,4
    甚至是:(x)
    a,b=b,a -- 多么方便的交换变量功能啊?br />    在默认情况下Q变量L认ؓ(f)是全局的。假如你要定义局部变量,则在W一ơ赋值的时候,需要用local说明。比如:(x)
    local a,b,c = 1,2,3 -- a,b,c都是局部变?br />
  Q数D?br />    和C语言一P支持 +, -, *, /。但Luaq多了一?^"。这表示指数乘方q算。比?^3 l果?, 2^4l果?6?br />    q接两个字符Ԍ可以?.."q处W。如Q?br />    "This a " .. "string." -- {于 "this a string"

  Q比较运?br />    < > <= >= == ~=
    分别表示 于Q大于,不大于,不小于,相等Q不相等
    所有这些操作符Lq回true或false?br />    对于TableQFunction和Userdatacd的数据,只有 == ?~=可以用。相{表CZ个变量引用的是同一个数据。比如:(x)
    a={1,2}
    b=a
    print(a==b, a~=b) -- true, false
    a={1,2}
    b={1,2}
    print(a==b, a~=b) -- false, true

  Q逻辑q算
    and, or, not
    其中Qand ?or 与C语言区别特别大?br />    在这里,请先CQ在Lua中,只有false和nil才计ؓ(f)falseQ其它Q何数据都计算为trueQ?也是trueQ?br />    and ?or的运结果不是true和falseQ而是和它的两个操作数相关?br />    a and bQ如果a为falseQ则q回aQ否则返回b
    a or bQ如?a 为trueQ则q回aQ否则返回b

    丑և个例子:(x)
     print(4 and 5) --> 5
     print(nil and 13) --> nil
     print(false and 13) --> false
     print(4 or 5) --> 4
     print(false or 5) --> 5

    在Lua中这是很有用的特性,也是比较令hh的特性?br />    我们可以模拟C语言中的语句Qx = a? b : cQ在Lua中,可以写成Qx = a and b or c?br />    最有用的语句是Q?x = x or vQ它相当于:(x)if not x then x = v end ?br />
  Q运符优先U,从高C序如下Q?br />    ^
    not - Q一元运)
     * /
     + -
     ..Q字W串q接Q?br />     < > <= >= ~= ==
     and
     or

III. 关键?br />  关键字是不能做ؓ(f)变量的。Lua的关键字不多Q就以下几个Q?br />  and break do else elseif
  end false for function if
  in local nil not or
  repeat return then true until while

IV. 变量cd
  怎么定一个变量是什么类型的呢?大家可以用type()函数来检查。Lua支持的类型有以下几种Q?br />
  Nil I|所有没有用过的变量,都是nil。nil既是|又是cd?br />  Boolean 布尔?br />  Number 数|在Lua里,数值相当于C语言的double
  String 字符Ԍ如果你愿意的话,字符串是可以包含'\0'字符?br />  Table 关系表类型,q个cd功能比较强大Q我们在后面慢慢说?br />  Function 函数cdQ不要怀疑,函数也是一U类型,也就是说Q所有的函数Q它本n是一个变量?br />  Userdata 嗯,q个cd专门用来和Lua的宿L交道的。宿主通常是用C和C++来编写的Q在q种情况下,Userdata可以是宿ȝL数据cdQ常用的有Struct和指针?br />  Thread   U程cdQ在Lua中没有真正的U程。Lua中可以将一个函数分成几部䆾q行。如果感兴趣的话Q可以去看看Lua的文档?br />
V. 变量的定?br />  所有的语言Q都要用到变量。在Lua中,不管你在什么地方用变量,都不需要声明,q且所有的q些变量L全局变量Q除非,你在前面加上"local"?br />  q一点要特别注意Q因Z可能惛_函数里用局部变量,却忘了用local来说明?br />  至于变量名字Q它是大写相关的。也是_(d)A和a是两个不同的变量?br />  定义一个变量的Ҏ(gu)是赋倹{?Q?操作是用来赋值的
  我们一h定义几种常用cd的变量吧?br />  A. Nil
    正如前面所说的Q没有用过的变量的|都是Nil。有时候我们也需要将一个变量清除,q时候,我们可以直接l变量赋以nil倹{如Q?br />    var1=nil -- h?nil 一定要写

  B. Boolean
     布尔值通常是用在进行条件判断的时候。布?yu)(dng)值有两种Qtrue ? false。在Lua中,只有false和nil才被计算为falseQ而所有Q何其它类型的|都是true。比?Q空串等{,都是true。不要被 C语言的习(fn)惯所误导Q?在Lua中的的确是true。你也可以直接给一个变量赋以Booleancd的|如:(x)
    varboolean = true

  C. Number
    在Lua中,是没有整数类型的Q也不需要。一般情况下Q只要数g是很大(比如不超q?00,000,000,000,000Q,是不?x)生舍入误差的。在很多CPU上,实数的运ƈ不比整数慢?br />    实数的表C方法,同C语言cMQ如Q?br />    4 0.4 4.57e-3 0.3e12 5e+20

  D. String
    字符ԌL一U非常常用的高cd。在Lua中,你可以非常方便的定义很长很长的字W串?br />    字符串在Lua中有几种Ҏ(gu)来表C,最通用的方法,是用双引h单引h括v一个字W串的,如:(x)
    "This is a string."
    和C语言相同的,它支持一些{义字W,列表如下Q?br />    \a bell
    \b back space
    \f form feed
    \n newline
    \r carriage return
    \t horizontal tab
    \v vertical tab
    \\ backslash
    \" double quote
    \' single quote
    \[ left square bracket
    \] right square bracket

    ׃q种字符串只能写在一行中Q因此,不可避免的要用到转义字符。加入了转义字符的串Q看h实在是不敢恭l_(d)比如Q?br />    "one line\nnext line\n\"in quotes\", 'in quotes'"
    一大堆?\"W号让h看v来很倒胃口。如果你与我有同感,那么Q我们在Lua中,可以用另一U表C方法:(x)?[["?]]"多行的字符串括hQ如Q?br />    page = [[
    <HTML>
      <HEAD>
        <TITLE>An HTML Page</TITLE>
      </HEAD>
      <BODY>
        <A HREF="http://www.lua.org">Lua</A>
        [[a text between double brackets]]
      </BODY>
    </HTML>
    ]]

    值得注意的是Q在q种字符串中Q如果含有单独用的"[["?]]"׃然得?\["?\]"来避免歧义。当?dng)q种情况是极会(x)发生的?br />
E. Table
     关系表类型,q是一个很强大的类型。我们可以把q个cd看作是一个数l。只是C语言的数l,只能用正整数来作索引Q在Lua中,你可以用Lcd? 作数l的索引Q除了nil。同P在C语言中,数组的内容只允许一U类型;在Lua中,你也可以用Q意类型的值来作数l的内容Q除了nil?br />    Table的定义很单,它的主要特征是用"{"?}"来括起一pd数据元素的。比如:(x)

    T1 = {} -- 定义一个空?br />    T1[1]=10 -- 然后我们可以象C语言一h使用它了?br />    T1["John"]={Age=27, Gender="Male"}
    q一句相当于Q?br />    T1["John"]={} -- 必须先定义成一个表Q还记得未定义的变量是nilcd?br />    T1["John"]["Age"]=27
    T1["John"]["Gender"]="Male"
    当表的烦(ch)引是字符串的时候,我们可以写成Q?br />    T1.John={}
    T1.John.Age=27
    T1.John.Gender="Male"
    ?br />    T1.John{Age=27, Gender="Male"}
    q是一个很强的Ҏ(gu)?br />
    在定义表的时候,我们可以把所有的数据内容一起写?{"?}"之间Q这样子是非常方便,而且很好看。比如,前面的T1的定义,我们可以q么写:(x)

    T1=
    {
      10, -- 相当?[1] = 10
      [100] = 40,
      John= -- 如果你原意,你还可以写成Q["John"] =
      {
        Age=27,   -- 如果你原意,你还可以写成Q["Age"] =27
        Gender=Male   -- 如果你原意,你还可以写成Q["Gender"] =Male
      },
      20 -- 相当?[2] = 20
    }

    看v来很漂亮Q不是吗Q我们在写的时候,需要注意三点:(x)
    W一Q所有元素之_(d)L用逗号"Q?隔开Q?br />    W二Q所有烦(ch)引值都需要用"["?]"括v来;如果是字W串Q还可以L引号和中括号Q?br />    W三Q如果不写烦(ch)引,则烦(ch)引就?x)被认?f)是数字,q按序自动?往后编Q?br />
    表类型的构造是如此的方便,以致于常常被人用来代曉K|文件。是的,不用怀疑,它比ini文g要漂亮,q且强大的多?br />
  F. Function
    函数Q在Lua中,函数的定义也很简单。典型的定义如下Q?br />    function add(a,b) -- add 是函数名字,a和b是参数名?br />     return a+b -- return 用来q回函数的运行结?br />    end

    h意,return语言一定要写在end之前。假如你非要在中间放上一句returnQ那么请写成Qdo return end?br />    q记得前面说q,函数也是变量cd吗?上面的函数定义,其实相当于:(x)
    add = function (a,b) return a+b end
    当你重新ladd赋值时Q它?yu)׃再表C个函C。你甚至可以赋给addL数据Q包括nil Q这P你就清除了add变量Q。Function是不是很象C语言的函数指针呢Q?br />
    和C语言一PLua的函数可以接受可变参C敎ͼ它同h??来定义的Q比如:(x)
    function sum (a,b,?
    如果惛_得…所代表的参敎ͼ可以在函C讉Ka(chn)rg局部变量(表类型)得到?br />    ?sum(1,2,3,4)
    则,在函CQa = 1, b = 2, arg = {3, 4}
    更可늚是,它可以同时返回多个结果,比如Q?br />    function s()
      return 1,2,3,4
    end
    a,b,c,d = s() -- 此时Qa = 1, b = 2, c = 3, d = 4
前面说过Q表cd可以拥有Lcd的|包括函数Q因此,有一个很强大的特性是Q拥有函数的表,哦,我想更恰当的应该说是对象吧。Lua可以使用面向对象~程了。不信?那我举例如下Q?br />
    t =
    {
     Age = 27
     add = function(self, n) self.Age = self.Age+n end
    }
    print(t.Age) -- 27
    t.add(t, 10)
    print(t.Age) -- 37

    不过Qt.add(t,10) q一句实在是有点土对吧?没关p,在Lua中,你可以简写成Q?br />    t:add(10)   -- 相当?t.add(t,10)

  G. Userdata ?Thread
    q两个类型的话题Q超Z本文的内容,׃打算l说了?br />
VI. l束?br />  p么结束了吗?当然不是Q接下来Q需要用Lua解释器,来帮助你理解和实践了。这小文只是帮助你大体了解Lua的语法。如果你有编E基Q相信会(x)很快对Lua上手了?br />  pC语言一PLua提供了相当多的标准函数来增强语言的功能。用这些标准函敎ͼ你可以很方便的操作各U数据类型,q处理输入输出。有兌斚w的信息,你可以参考《Programming in Lua 》一书,你可以在|络上直接观看电(sh)子版Q网址为:(x)http://www.lua.org/pil/index.html
  当然QLua的最强大的功能是能与宿主E序亲蜜无间的合作,因此Q下一文章,我会(x)告诉大家Q如何在你的E序中用Lua语言作ؓ(f)脚本Q你的E序和Lua脚本q行交互?br />---------------------------------------------------------------------------------------------------------
使用程
1. 函数的?br />以下E序演示了如何在Lua中用函? ?qing)局部变?br />例e02.lua
-- functions
function pythagorean(a, b)  
local c2 = a^2 + b^2  
return sqrt(c2)
end
print(pythagorean(3,4))

q行l果
5

E序说明
在Lua中函数的定义格式?
function 函数?参数)
...
end
与Pascal语言不同, end不需要与begin配对, 只需要在函数l束后打个end可以了.
本例函数的作用是已知直角三角形直角边, 求斜辚w? 参数a,b分别表示直角辚w,
在函数内定义了local形变量用于存储斜边的qx. 与C语言相同, 定义在函数内的代
码不?x)被直接执? 只有ȝ序调用时才会(x)被执?
local表示定义一个局部变? 如果不加local刚表Cc2Z个全局变量, local的作用域
是在最里层的end和其配对的关键字之间, 如if ... end, while ... end{。全局变量?br />作用域是整个E序?br />
2. 循环语句
例e03.lua
-- Loops
for i=1,5 do  
print("i is now " .. i)
end

q行l果
i is now 1
i is now 2
i is now 3
i is now 4
i is now 5

E序说明
q里偶们用到了for语句
for 变量 = 参数1, 参数2, 参数3 do
循环?br />end
变量以参数3为步? 由参?变化到参?
例如:  
for i=1,f(x) do print(i) end
for i=10,1,-1 do print(i) end

q里print("i is now " .. i)中,偶们用到?.Q这是用来连接两个字W串的,
偶在(1)的试试看中提到的Q不知道你们{对了没有?br />虽然q里i是一个整型量QLua在处理的时候会(x)自动转成字符串型Q不需偶们费心?br />
3. 条g分支语句
例e04.lua
-- Loops and conditionals
for i=1,5 do
print(“i is now ?.. i)
  if i < 2 then    
  print(“small?  
  elseif i < 4 then    
  print(“medium?  
  else    
  print(“big?  
  end
end

q行l果
i is now 1
small
i is now 2
medium
i is now 3
medium
i is now 4
big
i is now 5
big

E序说明
if else用法比较? cM于C语言, 不过此处需要注意的是整个if只需要一个end,
哪怕用了多个elseif, 也是一个end.
例如
if op == "+" then
  r = a + b
elseif op == "-" then
  r = a - b
elseif op == "*" then
  r = a*b
elseif op == "/" then
  r = a/b
else
  error("invalid operation")
end


4.试试?br />Lua中除了for循环以外, q支持多U@? L(fng)while...do和repeat...until改写本文中的forE序
----------------------------------------------------------------------------------------------------------
数组的?br />
1.?br />Lua语言只有一U基本数据结? 那就是table, 所有其他数据结构如数组?
cd, 都可以由table实现.

2.table的下?br />例e05.lua
-- Arrays
myData = {}
myData[0] = “foo?
myData[1] = 42

-- Hash tables
myData[“bar”] = “baz?

-- Iterate through the
-- structure
for key, value in myData do  
print(key .. ??.. value)
end

输出l果
0=foo
1=42
bar=baz

E序说明
首先定义了一个table myData={}, 然后用数字作Z标赋了两个值给? q种
定义Ҏ(gu)cM于C中的数组, 但与数组不同的是, 每个数组元素不需要ؓ(f)相同cd,
像本例中一个ؓ(f)整型, 一个ؓ(f)字符?

E序W二部分, 以字W串做ؓ(f)下标, 又向table内增加了一个元? q种table非常
像STL里面的map. table下标可以为Lua所支持的Q意基本类? 除了nilg?

Lua对Table占用内存的处理是自动? 如下面这D代?br />a = {}
a["x"] = 10
b = a   -- `b' refers to the same table as `a'
print(b["x"]) --> 10
b["x"] = 20
print(a["x"]) --> 20
a = nil   -- now only `b' still refers to the table
b = nil   -- now there are no references left to the table
b和a都指向相同的table, 只占用一块内? 当执行到a = nil? b仍然指向table,
而当执行到b=nil? 因ؓ(f)没有指向table的变量了, 所以Lua?x)自动释放table所占内?br />
3.Table的嵌?br />Table的用还可以嵌套Q如下例
例e06.lua
-- Table ‘constructor?
myPolygon = {  
color=“blue?  
thickness=2,  
npoints=4;  
{x=0,   y=0},  
{x=-10, y=0},  
{x=-5, y=4},  
{x=0,   y=4}
}

-- Print the color
print(myPolygon[“color”])

-- Print it again using dot
-- notation
print(myPolygon.color)

-- The points are accessible
-- in myPolygon[1] to myPolygon[4]

-- Print the second point’s x
-- coordinate
print(myPolygon[2].x)

E序说明
首先建立一个table, 与上一例不同的是,在table的constructor里面有{x=0,y=0},
q是什么意思呢Q?q其实就是一个小table, 定义在了大table之内, table?br />table名省略了.
最后一行myPolygon[2].xQ就是大table里面table的访问方?
-----------------------------------------------------------------------------------------------------------
如何化你的宏.

虽然以上介绍让我们了解道宏可以完成非常强大的功能Q但暴雪实在太小气了Q仅仅只l我?55个字W来~写宏的内容Q假如你的宏的功能比较罗嗦,那就很麻?ch)了Q所以以下我介绍一下一些简化宏的小技巧:(x)

1、定义全局变量
? 完之前Lua介绍的h该都知道把,在Lua里,所有的变量都是全局变量Q也是说Q何一个变量只要你在开始游戏后做过定义Q那么到游戏l束时只要你不重? 定义他都是有效的。但Z不让我们自己不؜淆做全局用的变量和局部用的变量Q我们可以采用大写区分的办法,卛_写一律做为全局变量使用Q小写都用局? 变量?br />q样Q我们可以在一个宏里把自己常用的魔?技能都定义成变量来表示Q比如我是个术士Q就可以q样Q?br />F="腐蚀?{ 3)"   X="献祭({ 3)".......
之后Q我们要使用q样法的时候,只要直接用F或X来代替就可以了,q?"都可以省掉,是不是很方便呢~
或者还可以把一些常见的API函数变量也自己定义:(x)
T="target" P="player".....
使用的时候和上面一栗?br />
2、自定义函数
说实在话Q魔兽的有些函数实在长的q头Q很多时候珍늚字节都给函数占去了。所以必要的时候我们就得用自定义函数的Ҏ(gu)ȝ化这些函数?br />自定义函数的语句为:(x)
function 函数名称(函数变量1、函数变?....) return 函数q回?end
比如Q用法术的q个函数是CastByName()Q我们可以在宏里q样写:(x)
/scirpt function C(a) CastByName(a) end
q行后,我们其他宏用法术就只要直接用C()可以了Q是不是很方便呢Q?br />或是说话的函敎ͼ(x)
/script function S(a) SendChatMessage(a,"SAY") end
之后你要控制人物说话qS()可以了?br />
如果是有q回值的函数Q?br />/script
function N(a)
return UNitName(a)       --return之后是表示函数的返回?但return必须在end前面.
end
如果以后你要调用目标的名字,直接?x=N("target")Q如果按前面W一点定义了全局变量的话Q更单x=N(T)?br />
q样Q我们就可以把重要的字节都用在宏的判断内容上Q而不是沉长的函数上了。如果你q有什么更好的化方法,可以跟脓(chung)哦?br />-------------------------------------------------------------------------------------------------------
关于背包物品使用整理cȝ宏的制作

׃游戏提供的函数无法直接由物品名称调用该物品,所以通常单的使用物品宏是比较ȝ(ch)的,一定要把用的物品攑֜背包内特定的位置

Q或则大多术士都需要的问题Q能随时监视自己的灵碎片(当然Q有插g可以做到q一点)?br />
以下我写写关于如何制作这cdQ?br />
首先Q我们要在背包里扑ֈ自己需要的东西Q必ȝ循环里遍历这些包。由于放的位|有2个参敎ͼ1个是包的~号Q一个是包内槽位的编P

所以我们需要一个@环嵌套来搜烦(ch)Q?br />
以下假设我们w上都是16格的包:(x)
for bag=0,4,1 do     --包的~号Z叛_左,0,1,2,3,4
for cw=1,16,1 do   --槽位的编号ؓ(f)上到下,左到?1,2,3,4,5......16
..............   --q里我们可以写如判断物品是否为我们需要的东西的语?br />end         --表示内@环结?     
end           --外@环结?br />

或者用其他方式做这个@环:(x)
While循环Qwhile 条g do ?end

Repeat循环Qrepeat ?until 条g


然后Q要处理的是物品的判断:(x)
我们有两个函数可以?br />GetContainerItemLink() ?GetContainerItemInfo()
q两个函C用的变量都是2个,一个是包的~号Q一个是槽位的编P但他们的q回g?br />
GetContainerItemLink()是返回一个带着物品名字的连接,如果你用聊天函数把返回D出来可以看刎ͼ说出来的不光是物品的名称Q还?br />
一个可以连接到物品详细内容H口的连接?br />
比如Q你的包?Q?的位|放了一块熊肉,那么?script SendChatMessage(GetContainerItemLink(4,1),"SAY")后,可以看到自p“[?br />
肉]”,而且用鼠标点一下说的内容,q可以弹Z个描写这块肉的窗口?br />
但要注意Q直接用"[熊肉]"q样字符串来判断q个物品是不行的Q例如:(x)

if GetContainerItemLink(4,1)=="[熊肉]" then ..... end
q个判断是无效的?br />
正确的方法是Q先把要做判断的物品的赋一个变量,再用变量做出判断Q?br />
rou=GetContainerItemLink(4,1)     --把物品连接Dlrou

if GetContainerItemLink(4,1)==rou then ..... end   --现在可以正常判断物品了

最后要注意的是Q这个函数无法对术士的灵碎片做出正的判断Q意思就是,虽然灵魂片用这个函数显C出来是一L(fng)Q但q个函数却认

为所有的灵魂片都是不同的东西,即你把这个灵碎片的q接赋给一个变量后Q这个变量就只能判断q个灵魂片Q其他的灵魂片无?br />
作出判断Q奇怪把。所以要判断灵魂片Q就必须用到W二个函数GetContainerItemInfo()

GetContainerItemInfo()的返回值非常多Q几乎所有的物品信息都可以返回,但我们这里判断只用它q回的第一个倹{?br />我们可以先用聊天函数来看看第一个返回值是什么样子的Q?br />/script
a=GetContainerItemInfo(4,1)
SendChatMessage(a,"SAY")

可以看到Q返回值相当长的英文,但物品的关键字是在后面?br />
q样Q我们就?U方法来使用q个函数来判断物品?br />
1、和前一个函数的Ҏ(gu)一P用变量存储值后再判断,前提是要把判断的物品攑֜特定的位|赋一下倹{?br />2、只使用特定物品Q把物品的判断关键字写在函数里,然后用string.find()来判断他?br />例子Q某物品的关键字是bd
if string.find(GetContainerItemInfo(4,1),bd) then .....end --判断?Q?位置是否存在关键字ؓ(f)bd物品?br />

接着要处理的是物品的使用和交换?br />使用特定背包位置的物品函敎ͼ(x)UseContainerItem(index,slot)
q个好理解,不用多解释了把?br />
拑֏/放下物品的函敎ͼ(x)PickupContainerItem(index,slot)
q个函数有意思,你鼠标上没抓着东西的时候就是帮你拿L(fng)定位|的物品Q有的话变成放下物品到特定的位|ƈ交换拿v该位|的物品?br />
所以要完成2个物品在包内的交换要使用3ơ这个函敎ͼ(x)
PickupContainerItem(4,1) --拿v4Q?位置的物?br />PickupContainerItem(1,4) --攑֜1Q?位置q拿?Q?位置的物?br />PickupContainerItem(4,1) --?Q?位置的物品放?Q?位置


好拉Q把以上几点l合后宏基本完成了Q?br />
下面的例子是关于灵魂片的整理,把前4个包的灵碎片全攑ֈ最后一个包内:(x)


/script
bag=0 cw=1 sc=1   --定义好变量,bag是包的编Pcw表示查找包的槽位Qsc指向最后一个包内的槽位
for bag=0,3,1 do --?号包开始,?号包l束Q最后一个包不搜索?br />for cw=1,16,1 do   --q里假设所有的包都?6个槽位的Q如果没那么多槽位的包也可以用?br />if GetContainerItemLink(bag,cw)~=nil --判断q个槽位是否是空的,是空q接蟩C一个槽?br />  then
  if string.find(GetContainerItemInfo(bag,cw),"Gem") --判断q个槽位里是否是灵魂片QGem为灵碎片的关键?br />  then
    while string.find(GetContainerItemInfo(4,sc),"Gem") do sc=sc+1 end
        --q是一个小循环Q用于判断最后一个包里原来是否已l有灵魂片Q有的话指向包的下一个槽?br />    PickupContainerItem(bag,cw)
    PickupContainerItem(4,sc)
    PickupContainerItem(bag,cw)   --q?句控制灵碎片和最后一个包内物品的交换
    sc=sc+1   --重要Q不能忘记这个,每放|好一个碎片后p把最后一个包?br />        槽位指针指向下一个槽位,上面的小循环是无法判断刚刚放好的片的?br />  end
end
end
end   -循环l束

完了么,当然不行。。。因为宏的限制是255个字。所以要化我们的宏?br />
最长的内容估计是函数了,先从简化函数开始:(x)

建立以下宏:(x)


/script function P(c,d) PickupContainerItem(c,d) end
/script function I(e,f) if GetContainerItemInfo(e,f) then return string.find(GetContainerItemInfo(e,f),"Gem") else return nil end end

原来的宏变成了Q?br />
/script
bag=0 cw=1 sc=1  
for bag=0,3,1 do
for cw=1,16,1 do
if G(bag,cw)~=nil
  then
  if I(bag,cw)
  then
    while I(4,sc) do sc=sc+1 end
    P(bag,cw)
    P(4,sc)
    P(bag,cw)  
    sc=sc+1  
  end      
end
end
end

多余的变量定义和q长的变量都可以更改Q?br />

/script
s=1  
for g=0,3 do
for w=1,16 do
if G(g,w)
  then
  if I(g,w)
  then
    while I(4,s) do s=s+1 end
    P(g,w)
    P(4,s)
    P(g,w)  
    s=s+1  
  end      
end
end
end

现在写的下了吧。呵呵,至于使用物品的宏我虽然已l写好了Q但没有试q,{测试没问题后再攑և来把。有兴趣的朋友也可以自己写写?br />
但要注意一点,使用物品的宏只要扑ֈ物品可以马上蟩出@环,所以用Repeat循环做比较合适?/span>


swo 2006-11-01 13:40 发表评论
]]>
q样软g开发h才别的划分你同意吗http://www.shnenglu.com/swo2006/archive/2006/11/01/14473.htmlswoswoWed, 01 Nov 2006 05:29:00 GMThttp://www.shnenglu.com/swo2006/archive/2006/11/01/14473.htmlhttp://www.shnenglu.com/swo2006/comments/14473.htmlhttp://www.shnenglu.com/swo2006/archive/2006/11/01/14473.html#Feedback4http://www.shnenglu.com/swo2006/comments/commentRss/14473.htmlhttp://www.shnenglu.com/swo2006/services/trackbacks/14473.html q样软g开发h才别的划分你同意吗(ZT)
本h做Y件多q_(d)一直与软g开发行业的各种U别的Y件开发h才打交道Q很多时候,

  q扮演面视考官的角Ԍ很遗憾,本hq没有被面试q)?/p>

  写下q篇文章Q目的是区分各种层次的Y件开发h员,也让软g开发h员能够对照自己,看看自己在什么层ơ?/p>

  软g开发工作,其实是一U很复杂的工作,需要多斚w的技能。我认ؓ(f)Q尤其以学习(fn)能力和创新能力ؓ(f)丅R所以,我以下对软g人才的层ơ划分,也围l这两个能力展开?/p>

  一、门外汉型:(x)几乎没有学习(fn)能力Q更没有创新能力。比如,C一本《一步一步跟我学VB~程》之cȝ书,对照书上写的Q把例子E序l做出来了, q把例子E序的某些窗口标题给修改了一下。然后,p认ؓ(f)自己可以做Y件开发工作了。到处递简历,应聘的职位ؓ(f)软g开发工E师。这cMhQ以刚毕业的计算? 专业的大学生为多Q当?dng)刚毕业的学生中也有非帔RU的人才Q。读书期_(d)׃玩游戏ؓ(f)主,考试的时候,搞点舞弊过兟?/p>

  二、入门型Q该cd的h员(不叫人才Q所以叫人员Q,可能入门某一U到两种开发语aQ?0q前Q我上大学的时候,q类人的典型特点是热衷于 DOS命o(h)的nU用法。比如,dir命o(h)的各U参数。学?fn)过basic语言Q知道C语言中printf函数的各U参数的用法Q到?005q_(d)q类人是? 衷于windows下的注册表,热种于在自己的机器上安装各种开发工PVB,VC,dephiQasp{)。但是,仅仅停留在编译开发工具中自带的几? 例子E序中。(可能q会(x)做点修改Q。经q一D|间的学习(fn)Q可能还自己能够~写个简单的windows应用E序Q修Ҏ(gu)册表的程序等{。其很多旉q是在玩 游戏Q上QQ聊天泡MMQ看了一如何修Ҏ(gu)病毒的文章,一定会(x)对照文章上的说明Q把病毒l修改了Q然后到处发Q以昄自己的能力。当?dng)很多时候,该类 人即使对照文章的说明Q也不能病毒修攏V那找那些带配|工L(fng)黑客E序d吧,比如。BO{就是他们最常用来炫耀的。中国的破解者与初黑客Q绝大部 分是q一cMh。懂的不多,q喜Ƣ炫耀Qؓ(f)炫耀目的的破解和修改病毒是q一cMh的最大特点)。该cMh员,一般都没有在Y件公总事Y件开发工作?/p>

  三、基本型人才Q该cd一般是大学毕业Qƈ且从事Y件开发工作超q?q的Zؓ(f)多,臛_比较熟?zhn)一门语aQ以VB,dephi,javaQasp {其中的一U)。也有少Ch熟?zhn)C或者C++Q但是如果是C或者C++Q一般对指针{概念还是似懂非懂的状态。哦Q对了,该类人员可能q会(x)在自q机器? 安装qlinux或者sco unix{。但׃对自己没有信心,大部分h?x)在半个月之后把linux删除。该cd人才Q有一定学?fn)能力。创新能力ؓ(f)零。适合培养成ؓ(f)软g蓝领Q如果h 际交往能力q可以的话,可以培养成ؓ(f)一个初U营销人员。该cd的h典型的特Ҏ(gu)Q你要他做个目Q他首先׃(x)问:(x)用什么语aQ(因ؓ(f)用他不熟(zhn)的语言对他 来说Q他没有信心)Q该cMh员,?fn)惯看中文文档,不得以的情况下,才?x)看英文文档。另外,该类人员Q喜Ƣ购买Y件开发类的书c。该cMh员,一般在软g? 总事Y件开发工作,待遇?000元到10000元以下ؓ(f)丅R?/p>

  四、熟l工Q该cd一般是毕业5qƈ一直从事Y件开发工作,臛_熟?zhn)?VB,asp ,熟?zhn)数据库,知道什么叫存储q程Q什么叫触发器。知道Y件工E管理的基本概念Q如果做面象对象开发,可能q会(x)用到Rose{工兗有q?0Z下Y仉 目管理的l验。对于linuxQ至知道是个开源的目。由于做q比较大的Y仉目,目中带的小兵一般都不具备unix下的开发经验,所以,目中难? ?x)出现需要在unix下运行的代码Q所以,p己动手。用c~写q几DUnix下的程序。学?fn)能力比较强Q该cMh员,已经?fn)惯看英文文档,有时候看译 的别扭的中文文档?x)觉得不爽。干脆就找英文文档。该cMh员,是否喜欢C不得而知Q如果喜Ƣ买书,一般以非Y件开发类书籍Z了。在技术选型斚w具备一? 的创新能力,臛_Q你叫他做一个报表程序,他会(x)考虑用Excel的COM对象来实现。国内Y件公怸的项目经理,l大部分是这一cd的h才。待遇一般在 6000?5000元左叟?/p>

  五、聪明型Q该cMh员的工作l历不重要,可以是还没毕业的学生Q也可以是工作了10q的老鸟Q?周内Q甚至一时Q就熟?zhn)了一门语aQƈ且可? 开始用该语a开发,该类人员Q由于学?fn)能力很强,短时间内q(zhn)了许多语言Q即使从来没用过该语aQ也敢于在该语言上进行Y件开发,选择什么样的语aQ不 在于学没学过Q而在于是否适合解决当前问题。对技术充满好奇与Ȁ情,举个例子Q如果该cMh员接触过linuxQ马上就?x)被Linux的魅力所吸引。即使与 自己的工作无养I也会(x)一回家qIlinuxQ可以肯定的是,该类人员的笔记本?sh)脑上,肯定安装有linux Qƈ且,linux的启动次数和windows的启动次C样多甚至更多。如果该cMh员接触到了h工智能,臛_?x)编写一个推理机E序来用用。另外,该类? 人才的典型特Ҏ(gu)学习(fn)能力强。英语不一定很厉害Q但是,不害怕看英文资料。该cd人才Q许多ƈ不是计算Z业毕业,可以是学数学的,物理的,音乐的等{? 都有可能。我pq一个学p的学生属于这U类型。该cd的h才,几乎所有的病毒代码是他们写出来的(不算那些修改病毒代码的hQ。爱表现Q也是他们的? 炏V如果该cMh员在MQ那么,他们是Y件公叔R睐的人才Q绝对不?x)出现简历递出三䆾q没有h要的情况Q一旦进入公司,在半q内Q其才能一定会(x)得到公司? 导的认可Qƈ作ؓ(f)重点培养对象。ؓ(f)了留住这L(fng)人才QY件公怸般会(x)每听说有别的公司要挖他的消息׃(x)l他涨工资一ơ。薪水的增长速度往往令同事红眹{?/p>

  六、技术天才型Q该cMh才,技术方面一,如果只从技术方面的学习(fn)能力Q创新能力来Ԍ都要过以上的Q何一U类型的人才。上帝造hL很公q? 的,他们在技术方面是天才Q往往其他斚w几乎白痴Q不善与Z往Q甚臛_怕与Z往。另外,某些东西对他们有致命吸引力,比如Q有些hp恋自己做一个操 作系l,有些人就qh人工。该cMh员,不写软g则以Q一写,肯定是一的。全球一。从语言来讲Q因Z们几乎不用微软的开发工具做具体的项目,他们 所看的技术资料,全部是英文资料,在网上交的Q全是操p或者法语的人。即使是中国人,他们也习(fn)惯用p与别行技术沟通。该cMh才,如果在工作,一 般是在某实验室,或者是在某基金的资助下开展研IӞ如果在Y件公司,一定是L举世瞩目的Y仉目。或者,在自己开的小公司既当CEO又当CTO。由于其 技术的优势明显Q即使他不是一个很U职的CEOQ也能让q个公司l持下去?/p>

  七、数学家型:(x)该类型h才,也许Ҏ(gu)׃懂具体某U语a的开发(也可以懂Q,整天qI算法。徏模。一般不属于计算Z业。他们要把自q成果 变成现实Q往往?fn)惯找聪明型或者天才型人才帮他们实现。该cMh员,因ؓ(f)不学计算机,所以,无法描述他们在学?fn)技术方面的能力Q但是,创新能力l对一。该 cMh才,没有在Y件公司工作的Q当?dng)如果其成果有一定商业h(hun)|他们?x)成为某软g公司的顾问。或者干脆在某Y件公司的实验室中当个MQ什么的?/p>

  八、比?dng)型Q因为比?dng)的影响力巨大,所以,我们把具有一定Y件开发能力,又有很强的商业运作能力的人归到这一cR比?dng)型人才Q学?fn)能力,在聪? 型之上,在技术天才型之下。由于vC会(x)知识面非常广泛,所以,知道什么Y件能赚钱Q怎么样做能赚钱。该cMh写Y件的目的只有一个,那就是赚钱,而不?x)太? 乎采用什么样的技术。他们写软gQ会(x)极力q合用户Q迎合市场?/p>

  对h的划分,有时候是很难的,有的人是跨类型的。但是,~少创造的人,最多就到达熟练工型Q具有超强创造力的hQ可以达到技术天才型和数学家 型,如果q有商业头脑Q成为比?dng)型也是可能。最后一句话Q如果你q够的学习(fn)能力都没有,那么Q就请你d软g开发行业,另谋\比较合适?/p>

  q篇帖子Q我首发在共享Y件论坛,我认为,如果你不具备强的学?fn)能力,基本的创新能力和基本的商业能力,那么Q就请你早不要做共享Y件?/p>



swo 2006-11-01 13:29 发表评论
]]>
ݺɫþþۺ| þþƷ| þþþþþž99Ʒ| һAëƬѹۿþþƷ| 99þĻ| ŷþۺϾɫۺ| ޹þþþþþ| 91鶹Ʒ91þþþþ| þþþþþƵ| 99þó18վ| Ʒþþþþ벻| þþƷ| պŷþþwwwۺ| ޹Ʒþ98| ݺݾƷþþĻ| ղƷþþһ| ij뾫Ʒþþò| ƷۺϾþ| 鶹avþavʢav| ޾Ʒ׽þþþþ| þþƷһۺ| þɫ| 8090Ʒþһ | ޹˾þþƷӰ| 99þþù| ĻƷþþþ| XxŷʸƷþþþþ| þþƷ99Ʒ| þ97Ʒþþþþþò| þۺ³³| þ˳ƷCAOPOREN| Ʒһþ| þ99Ʒþþþþhb| ŷ츾BBBþþ| ޾ƷۺϾþһ| þþþ޾Ʒ˵| ˹ھƷþþþӰԺVR| þþƷվ| þۺ97ɫ| þþþ޾Ʒһ| vĻþ|