《孤胆之夜》


        刚出道时写过的3D游戏,以下是关于这款游戏的简单介绍:

类型:3DARPG+FPS

编译工具:VS2008

代码量:1万以上

资源来源:其中图片声音模型等资源皆来源于互联网或其他PC游戏中,仅供个人学习。

游戏概况:

游戏中有2个场景,第二个场景的界面方面就是模仿的抢滩登陆了。至于第一个场景开始有LOGO视频,加载界面,游戏中还有实际的人物踏船越过河流的过场动画.过场动画中的河里游走的海豚是唯一的关键帧动画。其它的都是纹理动画和骨骼动画,还有ARPG部分人物打斗,主角两个物理技能,包围盒碰撞检测,有第一人称摄像机和尾随摄像机的切换,人物蹲立,横跨等特殊动作。包含天空盒,高度地形,简单UI,2D和3D字体,人物伤害值计算,静态和动态模型,河流,粒子雪,粒子火焰和公告板爆炸,子弹射击,碰撞检测等技术。

功能实现:

各种菜单界面都有淡入淡出效果

场景选择界面

视频

加载界面

游戏场景中ARPG部分开始时的过场动画

ARPG部分的战斗

场景一中FPS第一部分场景

场景一中FPS第二部分场景

场景二中的菜单选择界面,包括后面几张图中门的推拉等这都是我模仿抢滩登陆自己写的UI逻辑,只是本人不是美工,所以直接用抢滩~的图片,什么?不信?不信我当场给你写一个。(使用ALPHA贴图,鼠标放到菜单选项上有不同的效果,下图为前后对比)

场景二中的进入菜单选项内部界面(点击前后箭头切换菜单选项并带有推拉门式的效果,下图为前后对比)

场景二中的FPS游戏界面(有玩家枪,准星,子弹,爆炸,2类NPC敌人,飞机绕8字形轨迹飞行)

场景二中的FPS游戏结束界面

将模型导出到Direct3D

(零)序言
你在3ds
max(以下称作max)或Maya(以下称作maya)中为一款游戏制作各种3D模型,你希望自己所做的模型能在一款3D游戏引擎中被活灵活现地显示出来。在那一刻你会感到非常喜悦,因为玩家那充满热情或惊叹的眼神使你感到自己的作品被承认、被赞赏。嗯,这的确值得我们去想象!

 
不过,先别急着去幻想那些情景啦,因为你在进入梦乡前会有足够的时间来想象这些情景。在此之前,你还是要务实一点,先要为能得到那样的眼神而付出相应程度的努力。

 
不知你是否清楚这样一个事实(或,我现在需要告诉你这个事实),那就是,你在max或maya中保存的.max或.ma、mb格式的文件是不能直接“放到”引擎中去显示的。一般来说,游戏引擎都不支持这些格式,通常它们会各自定义一些专门的文件格式来存储模型,而只有具有这种格式的文件才能被引擎直接使用。因此,你就需要将max或maya制作的模型导出为那些格式。

 
      

要知道一般3D游戏引擎都是基于Direct3D(以下简称D3D)或OpenGL编写的。而由于不同引擎支持的格式各不相同,所以在此很难逐一去讨论。正如文章的标题所示,本文主要讨论如何将模型导出到基于Direct3D编写的应用程序或引擎,再或者更精确地说(如果你了解Direct3D的话)导出到Direct3DX的.X文件格式。本文的首要目的就是让你了解导出一个3D模型的概念和具体做法;其次介绍了一种方法来验证你的模型是否能在D3D游戏中被正确的显示出来;最后指出在max中导出.X格式时会遇到的一个小麻烦及解决方法。

 
最后,我假设你用的3D建模软件是max或maya,整篇文章都是围绕在这两种软件中进行导出来说明的。并且我进一步假设你使用的是当前主流版本的max或maya,即3ds

max 5.0或Maya 4.0~5.0,如果你使用的是其它软件,或者版本号太旧或太新,则以下内容可能不是太适合于你。
 
      
OK,开始吧~!
(一)如何导出?
.X文件格式
.X文件是Direct3D开发包所直接支持的唯一一种3D模型格式,也就是说,如果你是一个程序员,你可以很轻松地写出一个Direct3D程序用于显示.X文件所存储的3D模型。.X文件包含了对于一款3D游戏而言有用的信息,诸如模型中顶点的位置、顶点颜色、材质、UV纹理坐标、动画关键帧等等。另外,.X文件有两种存储方式:文本和二进制,两者在功能和用途上没有任何差别,只是形式不同而已。

安装导出插件
      
另外,以下内容中的一些插图和说明以max为例。
 
l      
3ds max 5.0
1)       
如果max已经启动,则先关闭它。
2)       
复制:
XSkinExp.dle
到:
(3ds max所在目录)\plugins\
3)       
启动max后,选择菜单:
Customize-> Plug-in Manager...
检查弹出对话框的列表框中是否存在“XSKINEXP.DLE”,并且其Status是否为“loaded”,如下图:
 
       

l      
Maya 4.0~5.0
1)       
如果maya已经启动,则先关闭它。
2)       
复制:
xExport.mll
到:
(maya所在目录)\bin\plug-ins\
3)       
复制:
bicubicbezierpatches.mel和xfiletranslatoropts.mel
到:
(maya所在目录)\scripts\others\
4)       
启动maya后,选择菜单:
Window->Settings/Preferences->Plug-in
Manager...
检查弹出对话框的列表框中是否存在“xExport.mll”,并且是否构选了“loaded”和“auto
load”,如下图:
 
导出模型
l      
3ds max 5.0
1)       
选择菜单:
File->Export
在弹出对话框中选择保存类型为“X-File (*.X)”,并指定保存路径和文件名。
2)       
然后,可在弹出的导出选项对话框中进行设置,配置好以后按“Go!”按钮,导出选项和解释如下:
 
 
各个选项含义如下:
     
导出选项描述
     
Text文本
     
Binary
     
Binary Compressed二进制
     
压缩的二进制
     
Export Patch Data如果存在高次(high-order)表面则将导出patche以替代polygon网格
     
Include Animation Data是否同时导出动画数据,需要指定动画采样速率(单位:帧/秒)
     
Looping Animation Data是否循环播放动画

l      
Maya 4.0~5.0
1)       
选择菜单:
File->Export All…
在弹出对话框中选择保存类型为“xExport (*.*)”,并指定保存路径和文件名。
2)       

在文件保存的对话框中,按下左下角的“Options…”按钮后,可在弹出的导出选项对话框中进行设置,配置好以后按“Export”按钮,导出选项和解释如下:

 
 
各个选项含义如下:
     
导出选项描述
     
File Options选择文本、二进制、压缩的二进制
     
Tesselation
Options选择高次(high-order)和细分(subdivided)表面,或none为忽略这些表面
     
Material Options选择是否同时导出材质、是否翻转UV纹理坐标轴、纹理文件路径是采用限定路径,还是不限定路径
     
Animation Options“Export
     
Animation”用于选择是否保存动画属性。如果选择保存动画属性和“per-frame”后可以通过“Frame
     
Step”或拖动条来设置采样率;默认值是1,表示1:1的关系。该值与Maya的当前回放(playback)速度有关。
     
Skinning Options“Export
     
Skinning”允许你选择是否保存网格中skinning信息。如果没有选择该选项,则网格不能自我变形(deform),但如果网格继承于带有动画的层级的话,则它只可以在空间中移动。

观察导出结果
1)       
确认已安装了DirectX 9.0运行库(如果这个链接已失效,请点击这里来寻找最新的DirectX)。
2)       
运行Mesh Viewer,选择菜单:
File->Open Mesh File
选择相应的.X文件
1)       

你可尝试通过鼠标左、中、右键来旋转、缩放、移动地观察模型;你还可以选择显示法线、是否剔除(Culling)背面、以何种方式(点、线框、面)来显示模型等,以此来查看模型是否正确。

 
如果在Mesh
Viewer中一切显示正常,那么在基于Direct3D的程序中,一切也会表现良好。如果有些面没有正常显示或导出时就失败了,则说明你的模型存在一些问题,需要进一步检查一下模型,看看法线是否正确、多边形的边是否合拢、是否存在缝隙等。在max中进行导出时你会遇到一个小问题,你在导出前多做一件事情来解决这个问题。继续前进,来让我们看一下在max中导出时发生了什么“怪”事情。

(二)max的导出问题
起因
如果你以为将max制作的模型导出到.X文件后会有一样的外观的话,那你可把世事想得过于完美了。你至少会发现一个非常明显的错误,那就是:模型被关于XY平面作了镜像,譬如拿XY平面比作一面镜子,你在Direct3D中看到的将是那个在镜子中的模型(注意,我在这里没有提及maya,那是因为maya的导出插件已经处理了这个问题,真要感谢编写那个插件的那位作者!)。

 
以一个max模型(该模型系杨·为一同志所做,感谢他提供这个模型,正因为在导出这个模型时显露出来的错误使我更深入地研究了本文所涉及的主题J)为例,它显示出来导出前后模型的变换,导出前我们通过XY平面看到了狮子的背面,而导出后通过XY平面看到的却是狮子的腹部。如果想象原来的狮子站在一面镜子上,那么导出的就好比是那个镜子中的狮子。

 
         
(左图:在max中的模型;右图:导出后在Mesh Viewer中的模型)
 
好,现在你应该活动一下筋骨了,来尝试导出一个模型到.X文件吧。我建议你的试验模型最好不要是对称物体,并且具有一点能标识方向的几何体,我在这里提供了一个模型,如果你想偷点懒的话可以直接用它来做这个试验。然后亲身感受一下这个问题。

经过
      

那是什么原因造成了以上的问题呢?如果你是一个喜欢打破砂锅问到底的人,那这一段正是为你准备的。如果你是一个讲求实效或以“可用即可”作为行事准则的家伙,那么你可以直接跳到“结果”那段,“经过”对于这类人不是最重要的。

 
      

max和maya都使用了“右手坐标系”,以你计算机屏幕的左下角为坐标系原点的话,那么右手坐标系中X、Y、Z轴的正方向依次为向右、向上和向外(即指向你),如下左图。而Direct3D默认使用却是“左手坐标系”,与右手坐标系的唯一区别在于Z轴正方向相反,即向内(指向显示器内部)。另外,OpenGL使用的是右手坐标系,所以这些问题在OpenGL编程中不存在,无论你用max还是maya,直接导出原始的顶点数据即可通过OpenGL绘制出来。

 
(D3D使用的左手坐标系和max、maya、OpenGL使用的右手坐标系)
 
      
好了,当你明白这些知识后,让我们来看一下在从max或maya的右手坐标系转换成D3D的左手坐标系的过程中发生了什么。
 
      
考虑在使用右手坐标系的max中有一个顶点为(0, 0,
1),你应该可以想象它是位于Z轴的正方向那段上。当这个顶点被不加修改地以左手坐标系来解释的话,那么它还是位于Z轴的正向段的Z轴,但是绝对位置却发生了变换。那么当构成一个模型的所有顶点都被这样进行转变的话,那么得到的结果就好比是一个物体关于XY轴所构成的平面作了镜像转换。如下图所示:

        
(左图:建模完成的物体;右图:原来处于右手坐标系中的物体,直接转换到左手坐标系后的情况)
 
      

由上右图你可以看到,“镜子中”物体(即转换到左手坐标系后的物体)不是“现实”物体(原右手坐标系中的物体,也就是你建模完得到的物体)的副本,我希望你能明白我在这里所谓“副本”的含义,我指的是可以与原物体完全吻合的另一个物体、另一个复制品。而现在“现实”中物体无论如何旋转或移动是永远都不可能得到“镜子中”物体的,如果你不信可以想象或比划一下,是不是呢?不是吗?是的。J

结果
想知道解决方法吗?或许聪明的你已经知道该怎么做了。我这里有三种解决方法,可能你会更倾向于其中的某一种吧。
l      
美工解决(推荐)
众所周知,有一句话曰“负负得正”。既然导出后的结果是原模型的镜像结果,那何不在导出前先对模型作一次镜像呢?两次镜像的结果就是原来的样子。

 
在用max制作完一个模型准备导出前,做如下步骤:
1)       
选中整个模型
2)       
完成镜像操作,选择菜单:
Tools->Mirror
在弹出对话框中对于“Mirror Axis”选择“Z”,然后按“OK”按钮
3)       
导出
 
这样做的有两个前提:一、应选中场景中所有的物体来作镜像;二、物体应处于世界坐标系的原点。除非你知道当违反这两个前提时会得到什么结果,并对这样结果有所预期。不然你就应该保证这两点。

 
另外,你可以想象一下如果没有这两个前提的保证下会发生什么情况。对于不满足前提一来说,某些在max作了镜像的物体能如max中所显示的那样,而某些未作镜像的却在导出后被镜像了;而对于前提二来说,如果物体不处于世界原点,则导出结果的模型本身正确了,但位置却作了关于XY平面的镜像。

 
这个方法简单有效,不是吗?而且这种解决方法不是发生在游戏运行时刻,可以节省大量的运行时开销。顺便还能让程序员们可以轻松点!想想他们在成堆的代码中已经够可怜了(当然,有的程序员视为享受,天堂和地狱只在一念之间J),饶了他们吧。

l      
程序解决
你们注定是不平凡的,但同时也注定你们是最辛苦的。因为理论上任何开发相关的事情都可以由你们来完成,只是开发/运行效率存在高低之别而已。

 
      

在当前要解决的问题中,可以通过在程序中作一次镜像来完成,概念上与上述的“负负得正”一样。大致思想是:在进行单个导出物体的世界变换之前,先作关于XY平面的镜像(XY平面可以是世界坐标系的,也可以模型局部坐标系的,两者效果是不一样的),如果选择作关于世界坐标系的XY平面的镜像,则还需要将镜像后的结果移回到原来的位置,即按Z轴平移。以下代码演示了关于世界坐标系的一个镜像,然后将对象移回到原来位置。

 
D3DXMATRIX matWorld;
……
D3DXPLANE plane( 0.0f, 0.0f, 1.0f, 0.0f ); // 关于XY平面的镜像
D3DXMATRIX matReflect;
D3DXMatrixReflect( &matReflect,
&plane ); // 构建一个关于XY平面的反射(镜像)矩阵
float fZ = matWorld._43; // 记录下物体的原Z轴位置
D3DXMATRIX matMoveBack;
D3DXMatrixTranslation( &matMoveBack, 0, 0, -2 * fZ
); // 构建移回矩阵
matWorld = matReflect * matMoveBack * matWorld;
pDevice->SetTransform( D3DTS_WORLD,
&matWorld );
 
这段代码有几处值得进一步优化,但我现在这么写纯粹是出于让你看懂的目的,在你理解之后可作进一步的优化。
 
这种方法具有更多的灵活性,但你需要为此付出运行时开销。如果你用不到该方法带来的灵活性的话,还是从美工的角度来解决这个问题吧。

l      
工具解决
就我知道存在一个转换工具――Deep
Exploration(http://www.righthemisphere.com/),可以将诸如max、maya等多种格式的模型文件转换称.X文件,并且它已经处理了这个镜像的问题。该软件使用非常简单,选择相应的源文件,然后将其另存为.X类型的文件即可,在此就不展开讨论了。

 
可能还有更多工具有待你的发掘,如果找到别忘了告诉我一声。
(三)附录
l      
建模建议
1)       
如果建模软件支持更改坐标系的话,应尽量选择使用左手坐标系来制作将会在D3D用到的模型,由此避免了转换;
2)       
建模过程应尽量使物体的头方向朝着Y轴正方向,右方向朝着X轴正方向,前方向朝着Z轴正方向,这样也可以避免程序在运行时对物体模型进行旋转调整;

3)       
建模时应尽量使用相同比例的坐标单位,这也可以避免运行时的缩放;
4)       
导出模型前检查所有法线朝向是否正确,在max据说所知可以通过加一个XForm来检查。
文章转载自:

Homepage:http://www.zhouweidi.name
email & MSN Messenger:zhouweidi@hotmail.com

游戏开发工具浅谈

转载自http://job.17173.com/content/2008-10-30/20081030174833105.shtml

 作为目前国内游戏业的从业人员,我就我的工作经历把我知道的游戏开发知识分享给大家。所知有限,大家不要见笑哈~今天先说一下一个游戏制作团队需要使用的开发工具。毕竟“工欲善其事,必先利其器”,没有顺手的开发工具,表达和实现难免总是受限。

   
业界标准的游戏制作团队包括设计师(策划)、艺术家(美术)、架构师(程序)三部分,同时也要加上QC(测试)小组以保证迭代开发中的快速验证、除bug与模块稳健度。那么这次就主要介绍各个工作岗位的开发人员需要使用和建议使用的各种开发工具。

   
先从程序说起。程序员需要使用的工具,依据游戏平台的不同、开发端的不同有着不同的区分。但是一般都离不开C++,因此必备的开发工具就是Visual
Studio。以前PC平台上多用C开发,现在基本已经全部采用C++,但考虑到运行效率,有些网络游戏的服务器端开发仍然使用C。但是完全依靠人工写代码不但低效、劳累而且容易出错。因此出现了许多中间件(MiddleWare)开发套件,这些是封装好的图形、物理、粒子、AI、网络、声音、UI模块,内部嵌入了各种游戏中使用的函数。程序员只需要写好接口、加入针对性的运算函数便可以把这些部分的工作量减少很多了。而比较大型的中间件可能包含了这其中的许多模块,这便是游戏引擎(Game
Engine)。游戏引擎有很多种,有综合性的,比如unreal tournament、quake
3D、LithTech、jupiter等等。也有专注性的,最主要专注于两个方面——图形和物理。比如专注图形的ogre和专注物理的havok、ode,现在由于网络游戏开发比较热,引擎的网络部分也得到重视,一般采用C/S方式,也有P2P的方式,有些引擎会提供强大的分布式运算和ado技术。但也有一些引擎没有网络部分,一是由于调用数据的不确定,二是网络端会有专门的优化,许多程序员会写专门的算法处理这一部分。图形用户界面(GUI)有的为团队自己写,因为各种游戏界面需求不太一样。有的用引擎或第三方提供的GUI包写的。有很多……比较著名的是GTK+、wxWidgets、Qt

   
美术方面,二维的标准开发工具是painter和photoshop,三维的标准开发工具是3D
max和maya。二维不用多说,基本多少年都没什么变化。输出的标准格式早期是pcx、bmp,现在最常用的是tga、dds,tga是标准工业格式,已被业内大多数企业所接受很久。dds是为对应directX节省缓存的一种压缩格式,本身也有很多种格式,效果没有无损压缩图像那么好,但对于提高显示效率和节省游戏容量效果很好(图形是游戏中最占容量的部分)。png也是很好用的一种格式,正被逐渐接受。jpeg很少被使用,一是因为图像有损压缩,二是不支持alpha通道,无法实现一些特定效果。三维最早是max和lightwave,但maya的崛起使得其也逐渐成为游戏开发的标准工具。现在不少制作团队都开始转向maya,lightwave在欧美开发团队使用的比较少,日本的开发团队则非常喜欢lightwave。除此之外,由于CG产业的迅猛发展以及开发要求的提高,许多新型的优秀三维工具也进入游戏开发领域。比如softimage、motion
builder等,它们在动画制作方面有着强大的功能。三维输出的主要格式是max(流行)和obj。游戏中各种绚丽的效果是用粒子实现的,这也是美术工作的一部分。一般没有什么标准的工具,只要美术制作出粒子最基础的纹理材质图片(很小的二维图形,常用png格式),再导入到程序中计算路径、发射角、密度、生命周期、着色、受力等。也出现了一些独立的专门制作粒子特效的工具,二维游戏里面最常用的是particle
illusion(它本来是用于影视粒子特效制作的)。三维的有很多,比如particle accelerator、particle
editor等等。

   
策划方面的标准和使用的工具都很杂。由于策划是内容的开发者,所以首先要解决文字和数据的问题。MS 
word和excel是不可少的工具。虽然策划不是美术和程序,但是图像表达和逻辑表达也是策划应该尽量作到的。二维图像表达你至少要会使用word自带的绘图功能,但仍然建议掌握一些基础的平面绘图工具,比如photoshop、fireworks等等,如果你能用动画表示更好,比如flash、gif等;三维建议了解max、maya,我推荐使用轻型快速的三维工具进行表达,比如silo、sketchup等。它们都可以输出为obj格式,方便导入到max中(多用于查看,因为模型会有一定程度的破损,部分需要重新制作),也可以使用截图软件截屏然后制作二维示意图。策划的工作还涉及脚本(script)创作,比如游戏的部分AI、任务逻辑、物品(诞生、消失)控制、运算(hp减血、攻击数值)数据、物理碰撞锁死编辑、触发器等。这些方面的主要文件格式为ini、xml,还有直接封包为bin、pak以及程序自己开发的各种存储格式。不少游戏使用python、lua等通用的脚本语言进行解释,它们可以很方便的嵌入到宿主程序中,加快了调试效率。多数游戏引擎也会提供强大的脚本编译器。

   
其实策划拥有强大的编辑器就可以把上面这些全省了……不过需要程序先把框架搭好,美术先把资源做好,策划才可以拼接设计,这已经耗费了很多时间了。所以能保证制作之前完成规划性的设计是很重要的。策划还需要要考虑可用性、复用性、逻辑锁死以及提出的资源需求(包括质量、数量)对效率的影响。比如哪些美术资源是可以重复使用的,图像质量要求要达到什么程度(过高的要求会大大降低程序渲染效率,也会增加整个团队的工作量),数值变化的临界点以及控制机制。当然这些就是具体的工作内容了,这里不再详述。

   
音乐音效部分有专门的创作团队制作,多数游戏制作商为外包。但也有许多不太注重品质的制作商直接就让策划做了(无敌的音效库……)。标准的创作方法是,首先需要在录音室创作音源,用音频采集卡录制cd品质,在专业工具中进行编辑,包括采样、滤波、除噪、编辑通道、混音、剪辑和合成,最后输出为wav或ogg格式。ogg格式压缩率更高,品质也很好(强于mp3),比起wav格式更省体积;也可以直接创作数字音源,制作midi格式的音乐音效,这就要有更多的专业知识了。这方面的工具有很多,Cubase是标准的开发工具,也可以使用sound
forge、sound editor编辑。

教程:CS游戏服务器搭建全攻略

建立CS游戏服务器

  在CS游戏目录中,找到hlds.exe文件,创立快捷方式。

  右键单击快捷方式图标,察看快捷方式属性。

  在“目标”栏中,添加下面的参数

  hlds.exe -game cstrike -port 27015 +maxplayers 20 -nomaster
+sv_lan 1 -insecure +map de_dust2 +servercfgfile server.cfg

  注意,每个 + 号或者 - 号的前面都有一个空格。

  各个参数的意义如下

  -port 服务器端口

  +maxplayers 服务器最大多容纳人数

  -nomaster
让服务器不上WON认证,避免因为出国网络不通导致的服务器问题。这个参数使玩家可以使用盗版CDKey

  +sv_lan 1 指定其为一个LAN
server,即允许使用IPX协议进行游戏,同时不影响外网的人通过TCP/IP协议进入服务器,外网的人在internet
game里add server也可以进入服务器。值得一提的是,如果服务器及其使用的是双网卡或更多,那么sv_lan
1的话,服务器在建立的时候会自动搜索局域网的ip来作为服务器ip。单网卡服务器建议使用此参数。

  -insecure CS自带的VAC凡作弊系统,-号表示不使用,要使用的话就修改为 +insecure

  +map de_dust2 服务器起始地图

  +servercfgfile 服务器起始使用的config文件

  服务器的所有参数设置被纪录在一个cfg文件里面,+servercfgfile
server.cfg这个参数就是用来指定服务器初始化时读取的是哪个cfg的。

  我这里帖出一个本人根据2003WCG的比赛cfg修改得到的一个混战服务器的cfg。没有标明WCG比赛设置数值的,默认就是wcg设置了,由于混战服务器的需要做了修改的,都在后面标明了wcg设置的数值。

mp_maxrounds 0 最大局数,当双方开战的总局数达到多少时换地图,0表不限制

  mp_timelimit 30 最大时间,地图开始多少时间后换地图,以分钟记

  hostname "L.Doom CS1.5 Server" 服务器名字

  sv_password none 进入服务器的密码,none表没有

  sv_maxrate 10000 限制网络传输的资料最大值,最大25000,WCG比赛设置 25000

  sv_minrate 1000 限制网络传输的资料最小值

  sv_maxspeed 320 移动的最大速度

  pausable 0 玩家是否可以暂停游戏

  sv_cheats 0 是否允许作弊

  sv_aim 0 是否允许自动瞄准

  mp_flashlight 1 战术手电是否允许使用

  mp_footsteps 1 是否有脚步声

  mp_falldamage 1 高出落下伤害

  mp_autokick 3 睡死几局后自动踢出玩家

  allow_spectators 1 是否允许旁观

  mp_freezetime 6 每局开始时的买枪冰冻时间,以秒记,WCG 7

  mp_roundtime 3 单局的时间,以分钟记

  mp_buytime 1 可以买枪的时间,以分钟记,WCG 0.25

  mp_c4timer 35 C4的爆炸时间

  mp_startmoney 800 起始钱数

  mp_forcecamera 2 死后视角,0,自由移动,1,只能跟在玩家身后,2,固定不动

  mp_forcechasecam 2 死后视角,0,自由移动,1,只能看第一视角,2只能看队友第一视角

  mp_fadetoblack 0 死后黑屏,WCG 1

  mp_friendlyfire 1 友军伤害

  mp_tkpunish 1 杀死队友惩罚,即杀死队友后,下一局自动自杀

  mp_autoteambalance 2 自动人数调整,当双方人数差距多少时自动调整人数,WCG 0

  mp_limitteams 1 一方队伍的最少人数,WCG 0

  mp_hostagepenalty 10 杀了多少人质或队友后会被踢

  mp_fraglimit 0 最大杀人数模式,即有人杀人数到达多少时换地图,0表无限制

  mp_winlimit 0 最大胜利数模式,即某方胜利次数到达多少时换地图,0表无限制

  sv_allowdownload 1 允许玩家下载

  sv_send_logos 0 允许玩家上传logo贴图信息

  sv_send_resources 1 允许玩家发送资源信息

  sv_allowupload 0 允许玩家上传

  sv_gravity 800 重力设置,默认800

  mp_kickpercent 0.6 投票kick某玩家超过60%则kick,WCG 1

  mp_mapvoteratio 0.6 投票换某地图的人数达60%换地图,WCG 1

  mp_ghostfrequency 0.1 鬼魂每秒钟更新的频率

  sv_sendvelocity 0 设定1开启较复杂的花样以及物理设定,适用较快的Server

  mp_lowlag 0 子弹打在墙上的效果,速度快用0,慢用1

  mp_decals 300 最大同时显示贴图

  sv_voiceenable 1 是否允许语音通讯

  sv_alltalk 0 语音通讯对象,0 则语音通讯只有队友能听到,1 则所有人能听到

  log on 是否允许记录,关闭为log off

  mp_logfile 0 是否开启记录文件

  mp_logmessages 0 是否纪录玩家聊天内容,此项设定必需同时设定"log on""mp_logfile
1"才可生效

  sv_proxies 4 允许接入的hltv服务器的数量

  sv_rcon_maxfailures 5 试验op密码错误次数超过多少则ban ip

  sv_rcon_banpenalty 5 ban ip时间,以分钟记

  rcon_password "ldoom" 服务器远程op密码

  mapcyclefile maplst.txt 服务器地图循环文件

  maplst.txt文件举例:

  该文件是地图循环文件,默认为mapcycle.txt

里面的地图名字一行一个,不要加扩展名,例如

  de_dust2

  de_cbble

  de_aztec

  de_nuke

  de_train

  de_inferno

  de_prodigy

  远程op应用祥解

  作为客户端进入服务器后,在控制台输入 rcon_password oppw ,例如上面的设置,则输入
rcon_password ldoom

  如果控制台没有提示你输入了错误的密码,那么你就已经顺利的获得了管理员权限。

  获得远程op权限后,就可以远程遥控服务器了,可以更改几乎所有的服务器设置。

  命令格式为在服务器参数命令前加 rcon

  例如:

  rcon sv_restartround 1 1秒后刷新

  rcon mp_freezetime 3 将冰冻时间改为3秒

  rcon sv_restart 重新启动服务器

  rcon changelevel de_dust2 换地图到de_dust2

  rcon exec wcg.cfg 读取服务器端的wcg.cfg配置文件

  rcon sv_password welcome 设置进入服务器的密码为welcome

  rcon mp_friendlyfire 0 关闭友军伤害

  以此类推。

  有一个特殊的命令,就是是否允许暂停,这个命令不加rcon,也就是需要修改时,直接输入 pausable 0/1
就可以了。

关于“Class C”问题

  这是一个比较恼人的问题,但是解决该问题很简单,只要用一个破解程序破解swds.dll文件就可。只需服务器端破解该文件,与客户端无关。

  破解补丁下载,覆盖原文件即可。

  关于motd.txt文件

  这个文件是玩家进入游戏后看到的一个窗口,窗口上有个 “ok” 按钮,该窗口用以提供一些服务器信息等,可随意修改。

  简单建立和应用CS观众服务器

  如果要建立观众服务器,找到hltv.exe文件,创建快捷方式,添加参数如下:

  hltv.exe +connect ip:port -port 27020 +serverpassword ***
+maxclients 50

  参数意义:

  +connect ip:port 观众服务器连入哪一个游戏服务器

  -port 27020 观众服务器端口

  +maxclients 50 观众服务器最大容纳的观众人数

  +serverpassword *** 游戏服务器的密码

  这些信息也可以写在hltv.cfg文件中,但是要去掉+号和-号。

  观众服务器更多设置请用记事本打开hltv.cfg文件察看

  //进入游戏服务器所现实的名字

  name "HLTV Proxy"

  //hltv服务器的名字,加//屏蔽该命令的话,hltv服务器的名字将和连到的游戏服务器名字相同

  //hostname "HLTV Proxy"

  //转播延时时间,以秒记。

  delay 30.0

  slowmotion 0.5 0.2

  multicast 0

  //最高客户端速率,Internet互联网 3500 , Lan/IPX局域网 10000

  maxclientrate 4000

  // 是否记录HLTV日志文件到proxy.log文件

  logfile 0

  // 谈话模式 0禁止 1连接到同一个观众代理服务器的观众可以相互谈话 2 所有观众都能相互谈话

  chatmode 1

  //观众服务器远程op密码

  adminpassword "hltvadmin"

  // 允许客户端使用joingame命令加入游戏。

  allowjoingame 1

  //loopcmd 1 60(每隔60秒) localmsg "You're watching HLTV.
"(显示的信息内容) 5(显示5秒) -1(X轴-1表示中间) -1(Y轴-1表示中间)
7700FFFF(RGBA颜色值:红、绿、蓝、透明度 16进制2位表示)

  loopcmd 1 120 localmsg "You're watching HLTV. "5 -1 0.9
FFA000FF

  // 设置客户端的语音部分

  signoncommands "voice_scale 2; voice_overdrive 16; volume 0.5;
echo Voice adjusted for HLTV"

  录制录像的命令

  record *** 录制文件名为***_1的demo. 他在你的cstrike目录下

  用stoprecord命令来结束demo的录制

  当你开始运行hltv以后,他会自动不断地连接你所选择的服务器,每三秒
一次,直到连上为止。掉线也一样,他会自动将你得demo分段进行录制。

服务器反作弊插件

  仅以著名的反作弊插件Cheating-Death和WWCL为代表,简述反作弊插件的安装方法。

  首先需要安装metamod插件,将metamod.dll或metamod_i386.so解压到cstrike目录下的dlls目录中。

  用记事本打开cstrike目录下的liblist.gam文件。

  原文件内容为:

  game "Counter-Strike"

  url_dl ""

  version "1.5"

  size "184000000"

  svonly "0"

  secure "0"

  type "multiplayer_only"

  cldll "1"

  hlversion "1110"

  nomodels "1"

  nohimodel "1"

  mpentity "info_player_start"

  gamedll "dlls/mp.dll"

  gamedll_linux "dlls/cs_i386.so"

  trainmap "tr_1"

  修改gamedll行,保存得到新的liblist.gam文件,注意做好备份工作,这个文件至关重要。

  gamedll "dlls/metamod.dll"

  gamedll_linux "dlls/metamod_i386.so"

  然后在cstrike目录下创建一个文本文件保存为metamod.ini文件。

  安装C-D.

  将C-D服务器端解压到cstrike目录下,一般是一个addons文件夹。

  在metamod.ini文件中添加这样的命令行

  win32 addons/cdeath/cdmod.dll

  linux addons/cdeath/cdmod_i586.so

  保存后,运行服务器,将加载反作弊插件Cheating-Death,在服务器的DOS窗口中可以看到加载C-D的提示。

  安装WWCL.

  将WWCL服务器文件解压到cstrike目录下,一般是一个addons文件夹和一个wwclconfig.cfg文件。

  在metamod.ini文件中添加这样的命令行

  win32 addons/wwcl/dlls/pcawwclconfig_mm.dll

  linux addons/wwcl/dlls/pcawwclconfig_mm_i386.so

  保存后,运行服务器,将加载反作弊插件WWCL.

 

Lua游戏脚本语言入门

在这篇文章中,我想向大家介绍如何进行Lua程序设计。我假设大家都学过至少一门编程语言,比如Basic或C,特别是C。因为Lua的最大用途是在宿主程序中作为脚本使用的。

  Lua 的语法比较简单,学习起来也比较省力,但功能却并不弱。

  在Lua中,一切都是变量,除了关键字。请记住这句话。

I. 首先是注释

  写一个程序,总是少不了注释的。
  在Lua中,你可以使用单行注释和多行注释。
  单行注释中,连续两个减号"--"表示注释的开始,一直延续到行末为止。相当于C++语言中的"//"。
  多行注释中,由"--[["表示注释开始,并且一直延续到"]]"为止。这种注释相当于C语言中的""。在注释当中,"[["和"]]"是可以嵌套的。

II. Lua编程

  经典的"Hello world"的程序总是被用来开始介绍一种语言。在Lua中,写一个这样的程序很简单:
  print("Hello world")
  在Lua中,语句之间可以用分号";"隔开,也可以用空白隔开。一般来说,如果多个语句写在同一行的话,建议总是用分号隔开。
  Lua 有好几种程序控制语句,如:

  条件控制:if 条件 then … elseif 条件 then … else … end
  While循环:while 条件 do … end
  Repeat循环:repeat … until 条件
  For循环:for 变量 = 初值,终点值,步进 do … end
  For循环:for 变量1,变量2,… ,变量N in表或枚举函数 do … end

  注意一下,for的循环变量总是只作用于for的局部变量,你也可以省略步进值,这时候,for循环会使用1作为步进值。
  你可以用break来中止一个循环。
  如果你有程序设计的基础,比如你学过Basic,C之类的,你会觉得Lua也不难。但Lua有几个地方是明显不同于这些程序设计语言的,所以请特别注意。

  .语句块
    语句块在C++中是用"{"和"}"括起来的,在Lua中,它是用do 和 end 括起来的。比如:
    do print("Hello") end
    你可以在 函数 中和 语句块 中定局部变量。

  .赋值语句
    赋值语句在Lua被强化了。它可以同时给多个变量赋值。
    例如:
    a,b,c,d=1,2,3,4
    甚至是:
    a,b=b,a -- 多么方便的交换变量功能啊。
    在默认情况下,变量总是认为是全局的。假如你要定义局部变量,则在第一次赋值的时候,需要用local说明。比如:
    local a,b,c = 1,2,3 -- a,b,c都是局部变量

  .数值运算
    和C语言一样,支持 +, -, *, /。但Lua还多了一个"^"。这表示指数乘方运算。比如2^3 结果为8,
2^4结果为16。
    连接两个字符串,可以用".."运处符。如:
    "This a " .. "string." -- 等于 "this a string"

  .比较运算
    < > <=
>= == ~=
    分别表示 小于,大于,不大于,不小于,相等,不相等
    所有这些操作符总是返回true或false。
    对于Table,Function和Userdata类型的数据,只有 == 和
~=可以用。相等表示两个变量引用的是同一个数据。比如:
    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

  .逻辑运算
    and, or, not
    其中,and 和 or 与C语言区别特别大。
    在这里,请先记住,在Lua中,只有false和nil才计算为false,其它任何数据都计算为true,0也是true!

    and 和 or的运算结果不是true和false,而是和它的两个操作数相关。
    a and b:如果a为false,则返回a;否则返回b
    a or b:如果 a 为true,则返回a;否则返回b

    举几个例子:
     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中这是很有用的特性,也是比较令人混洧的特性。
    我们可以模拟C语言中的语句:x = a? b : c,在Lua中,可以写成:x = a and b or c。
    最有用的语句是: x = x or v,它相当于:if not x then x = v end 。

  .运算符优先级,从高到低顺序如下:
    ^
    not - (一元运算)
     * /
     + -
     ..(字符串连接)
     < > <=
>= ~= ==
     and
     or

III. 关键字

  关键字是不能做为变量的。Lua的关键字不多,就以下几个:
  and break do else elseif
  end false for function if
  in local nil not or
  repeat return then true until while

IV. 变量类型

  怎么确定一个变量是什么类型的呢?大家可以用type()函数来检查。Lua支持的类型有以下几种:

  Nil 空值,所有没有使用过的变量,都是nil。nil既是值,又是类型。
  Boolean 布尔值
  Number 数值,在Lua里,数值相当于C语言的double
  String 字符串,如果你愿意的话,字符串是可以包含''字符的
  Table 关系表类型,这个类型功能比较强大,我们在后面慢慢说。
  Function 函数类型,不要怀疑,函数也是一种类型,也就是说,所有的函数,它本身就是一个变量。
  Userdata
嗯,这个类型专门用来和Lua的宿主打交道的。宿主通常是用C和C++来编写的,在这种情况下,Userdata可以是宿主的任意数据类型,常用的有Struct和指针。

  Thread  
线程类型,在Lua中没有真正的线程。Lua中可以将一个函数分成几部份运行。如果感兴趣的话,可以去看看Lua的文档。

V. 变量的定义

  所有的语言,都要用到变量。在Lua中,不管你在什么地方使用变量,都不需要声明,并且所有的这些变量总是全局变量,除非,你在前面加上"local"。

  这一点要特别注意,因为你可能想在函数里使用局部变量,却忘了用local来说明。
  至于变量名字,它是大小写相关的。也就是说,A和a是两个不同的变量。
  定义一个变量的方法就是赋值。"="操作就是用来赋值的
  我们一起来定义几种常用类型的变量吧。
  A. Nil
    正如前面所说的,没有使用过的变量的值,都是Nil。有时候我们也需要将一个变量清除,这时候,我们可以直接给变量赋以nil值。如:

    var1=nil -- 请注意 nil 一定要小写

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

    varboolean = true

  C. Number
    在Lua中,是没有整数类型的,也不需要。一般情况下,只要数值不是很大(比如不超过100,000,000,000,000),是不会产生舍入误差的。在很多CPU上,实数的运算并不比整数慢。

    实数的表示方法,同C语言类似,如:
    4 0.4 4.57e-3 0.3e12 5e+20

  D. String
    字符串,总是一种非常常用的高级类型。在Lua中,你可以非常方便的定义很长很长的字符串。
    字符串在Lua中有几种方法来表示,最通用的方法,是用双引号或单引号来括起一个字符串的,如:
    "This is a string."
    和C语言相同的,它支持一些转义字符,列表如下:
    \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

    由于这种字符串只能写在一行中,因此,不可避免的要用到转义字符。加入了转义字符的串,看起来实在是不敢恭维,比如:
    "one line\nnext line\n\"in quotes\", 'in quotes'"
    一大堆的"\"符号让人看起来很倒胃口。如果你与我有同感,那么,我们在Lua中,可以用另一种表示方法:用"[["和"]]"将多行的字符串括起来,如:

    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>
    ]]

    值得注意的是,在这种字符串中,如果含有单独使用的"[["或"]]"就仍然得用"\["或"\]"来避免歧义。当然,这种情况是极少会发生的。

 

 

E. Table
    关系表类型,这是一个很强大的类型。我们可以把这个类型看作是一个数组。只是C语言的数组,只能用正整数来作索引;在Lua中,你可以用任意类型来作数组的索引,除了nil。同样,在C语言中,数组的内容只允许一种类型;在Lua中,你也可以用任意类型的值来作数组的内容,除了nil。

    Table的定义很简单,它的主要特征是用"{"和"}"来括起一系列数据元素的。比如:

    T1 = {} -- 定义一个空表
    T1[1]=10 -- 然后我们就可以象C语言一样来使用它了。
    T1["John"]={Age=27, Gender="Male"}
    这一句相当于:
    T1["John"]={} -- 必须先定义成一个表,还记得未定义的变量是nil类型吗
    T1["John"]["Age"]=27
    T1["John"]["Gender"]="Male"
    当表的索引是字符串的时候,我们可以简写成:
    T1.John={}
    T1.John.Age=27
    T1.John.Gender="Male"
    或
    T1.John{Age=27, Gender="Male"}
    这是一个很强的特性。

    在定义表的时候,我们可以把所有的数据内容一起写在"{"和"}"之间,这样子是非常方便,而且很好看。比如,前面的T1的定义,我们可以这么写:

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

    看起来很漂亮,不是吗?我们在写的时候,需要注意三点:
    第一,所有元素之间,总是用逗号","隔开;
    第二,所有索引值都需要用"["和"]"括起来;如果是字符串,还可以去掉引号和中括号;
    第三,如果不写索引,则索引就会被认为是数字,并按顺序自动从1往后编;

    表类型的构造是如此的方便,以致于常常被人用来代替配置文件。是的,不用怀疑,它比ini文件要漂亮,并且强大的多。

  F. Function
    函数,在Lua中,函数的定义也很简单。典型的定义如下:
    function add(a,b) -- add 是函数名字,a和b是参数名字
     return a+b -- return 用来返回函数的运行结果
    end

    请注意,return语言一定要写在end之前。假如你非要在中间放上一句return,那么请写成:do return
end。
    还记得前面说过,函数也是变量类型吗?上面的函数定义,其实相当于:
    add = function (a,b) return a+b end
    当你重新给add赋值时,它就不再表示这个函数了。你甚至可以赋给add任意数据,包括nil
(这样,你就清除了add变量)。Function是不是很象C语言的函数指针呢?

    和C语言一样,Lua的函数可以接受可变参数个数,它同样是用"…"来定义的,比如:
    function sum (a,b,…)
    如果想取得…所代表的参数,可以在函数中访问arg局部变量(表类型)得到。
    如 sum(1,2,3,4)
    则,在函数中,a = 1, b = 2, arg = {3, 4}
    更可贵的是,它可以同时返回多个结果,比如:
    function s()
      return 1,2,3,4
    end
    a,b,c,d = s() -- 此时,a = 1, b = 2, c = 3, d = 4
前面说过,表类型可以拥有任意类型的值,包括函数!因此,有一个很强大的特性是,拥有函数的表,哦,我想更恰当的应该说是对象吧。Lua可以使用面向对象编程了。不信?那我举例如下:

    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

    不过,t.add(t,10) 这一句实在是有点土对吧?没关系,在Lua中,你可以简写成:
    t:add(10) -- 相当于 t.add(t,10)

  G. Userdata 和 Thread
    这两个类型的话题,超出了本文的内容,就不打算细说了。

 

VI. 结束语

  就这么结束了吗?当然不是,接下来,需要用Lua解释器,来帮助你理解和实践了。这篇小文只是帮助你大体了解Lua的语法。如果你有编程基础,相信会很快对Lua上手了。

  就象C语言一样,Lua提供了相当多的标准函数来增强语言的功能。使用这些标准函数,你可以很方便的操作各种数据类型,并处理输入输出。有关这方面的信息,你可以参考《Programming
in Lua 》一书,你可以在网络上直接观看电子版,网址为:http://www.lua.org/pil/index.html
  当然,Lua的最强大的功能是能与宿主程序亲蜜无间的合作,因此,下一篇文章,我会告诉大家,如何在你的程序中使用Lua语言作为脚本,使你的程序和Lua脚本进行交互。

使用流程

1. 函数的使用

   
以下程序演示了如何在Lua中使用函数, 及局部变量
例e02.lua
-- functions
function pythagorean(a, b)
local c2 = a^2 + b^2
return sqrt(c2)
end
print(pythagorean(3,4))

运行结果
5

程序说明
在Lua中函数的定义格式为:
function 函数名(参数)
...
end
    与Pascal语言不同,
end不需要与begin配对, 只需要在函数结束后打个end就可以了.本例函数的作用是已知直角三角形直角边, 求斜边长度.
参数a,b分别表示直角边长,
   
在函数内定义了local形变量用于存储斜边的平方. 与C语言相同, 定义在函数内的代码不会被直接执行,
只有主程序调用时才会被执行.
   
local表示定义一个局部变量, 如果不加local刚表示c2为一个全局变量,
local的作用域是在最里层的end和其配对的关键字之间, 如if ... end, while ...
end等。全局变量的作用域是整个程序。

2. 循环语句

例e03.lua
-- Loops
for i=1,5 do
print("i is now " .. i)
end

运行结果
i is now 1
i is now 2
i is now 3
i is now 4
i is now 5

程序说明
   
这里偶们用到了for语句
for 变量 = 参数1, 参数2, 参数3 do
循环体
end
    变量将以参数3为步长,
由参数1变化到参数2
例如:
for i=1,f(x) do print(i) end
for i=10,1,-1 do print(i) end

   
这里print("i is now " ..
i)中,偶们用到了..,这是用来连接两个字符串的,偶在(1)的试试看中提到的,不知道你们答对了没有。虽然这里i是一个整型量,Lua在处理的时候会自动转成字符串型,不需偶们费心。

3. 条件分支语句

例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

运行结果
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

程序说明
    if
else用法比较简单, 类似于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.试试看

   
Lua中除了for循环以外, 还支持多种循环, 请用while...do和repeat...until改写本文中的for程序。

数组的使用

1.简介

   
Lua语言只有一种基本数据结构, 那就是table, 所有其他数据结构如数组啦,类啦, 都可以由table实现.

2.table的下标

例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

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

程序说明
    首先定义了一个table
myData={}, 然后用数字作为下标赋了两个值给它. 这种定义方法类似于C中的数组, 但与数组不同的是,
每个数组元素不需要为相同类型,就像本例中一个为整型, 一个为字符串.

    程序第二部分,
以字符串做为下标, 又向table内增加了一个元素. 这种table非常像STL里面的map.
table下标可以为Lua所支持的任意基本类型, 除了nil值以外.

   
Lua对Table占用内存的处理是自动的, 如下面这段代码
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时,
因为没有指向table的变量了, 所以Lua会自动释放table所占内存

3.Table的嵌套

   
Table的使用还可以嵌套,如下例
例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)

程序说明

    首先建立一个table,
与上一例不同的是,在table的constructor里面有{x=0,y=0}, 这是什么意思呢? 这其实就是一个小table,
定义在了大table之内,
小table的table名省略了.最后一行myPolygon[2].x,就是大table里面小table的访问方式.