战魂小筑

讨论群:309800774 知乎关注:http://zhihu.com/people/sunicdavy 开源项目:https://github.com/davyxu

   :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  257 随笔 :: 0 文章 :: 506 评论 :: 0 Trackbacks

#

    《洞穴Portal》是一款以HL2引擎制作的FPS游戏,该作与其他FPS在游戏方法上有很大不同,以往的FPS都是以使用各种武器屠杀敌人为主,而《洞穴Portal》在游戏方式上显得非常聪明,他不追求杀戮的快感,也不追求画面的华丽。游戏的关键在于利用空间传送,打开空间入口,完成各种迷题。

    本作强调解迷,挑战玩家们的逻辑思维能力。在游戏中,主角的武器就是一把可以打开通往任意空间入口的传送枪,传送枪不但可以打开空间入口,还可以挪动地上的物品攻击敌人,空间将再也无法束缚你的脚步,能神不知鬼不觉的消灭敌人!

    Valve还宣布,Valve将随PC版《半条命2:第二章》、PS3和X360版《半条命2》一起推出这款全新的单人游戏。

 

先来熟悉下Portal游戏怎么玩

http://v.youku.com/v_show/id_XODk3MTMwOA==.html

 

现在讨论3d实现方法,看图

GK}TW)$ESO4C{UDNS(PUBQA

图中,圆圈代表玩家,方块代表2个门,从圆圈发出的直线代表玩家行进方向,重力向下,侧向图

这个图表示在地面打开2个门,玩家从一个门进入,进入到另外一个世界

要实现这个图,需要建立4个世界

 

]DT4$O[HUJ1F04ZS_SMDEYS

这是更加复杂的例子,注意看左上角,玩家在墙上和地上分别打了洞。从墙上的洞进入,会从地面上出来

要实现图,需要建立4个世界

 

 

]RVSNZKH19@I89__9KKUENS

这是一个特例,玩家在世界的平台上打洞,和在世界边缘打了个洞。虽然是建立2个世界,但是左边的世界明显和右边的世界不是等大的

 

其实Portal的原理,我们早在叮当(现在叫多啦A梦)的任意门就实现了,但是打开门的世界与进入前的世界处于2个地方。当在同一个地方开2个洞,就是portal的实现

 

要实现Port有几个难题

1. 怎么进入到另外一个世界

看到另外一个世界很简单,通过另外一个Camera渲染出来做成贴图即可

但是要能进入,那必须是几何空间是通透的


 

2. 到底构建几个世界?

其实根据在游戏中的分析与一些资料的透露,建立2个世界即可

 

3. 切换到另外一个世界时,我(摄像机)不需要更换?

实际看下来可以是,我进入到另外一个世界,而另外一个世界进入到我的世界

 

4. 怎样处理世界递归?

这是个有趣的话题,现实世界也存在这样的问题,比如说,你用2面镜子对着,眼睛从当中的一面看另外一面,就会形成视觉递归,只不过你不能进入到那个世界而已

类似于本文开头的递归世界图片,这个在Portal游戏中是有设置递归层级的,应该是受到渲染限制的,因此世界创建的个数应该与递归层级有关系。

posted @ 2009-07-16 11:40 战魂小筑 阅读(604) | 评论 (1)编辑 收藏

现在是网络游戏当红的时代,因为其模式方便赚钱,玩家也喜欢交流。但毕竟真正的游戏感觉,动作感还是单机游戏。怎样让单机游戏重振雄风呢?

街霸4开始使用在线存档的设定。也就是说,玩家可以用盗版,但是存档时可能需要账号。这就需要注册或者付费。但是带来的好处是什么?玩家玩过的经典游戏都可以重新归来

但是类似于 C&C4必须要保持连接这种恶心设定就遭人鄙视

posted @ 2009-07-16 10:19 战魂小筑 阅读(248) | 评论 (0)编辑 收藏

试用了下lua专属的注释生成文档工具luaDoc,基本模式和doxygen差不多,不过感觉更简单一些

1. 前期准备工作,及编译方法

luaDoc需要依赖luaFileSystem和luaLogging,因此我使用的是一个整合包http://luaforwindows.luaforge.net/

要开始使用时,在5.1\lua下找到luadoc_start.lua文件,这个文件就是luadoc的启动文件

找到你需要制作的lua文件的目录制作一个类似这样的bat

lua "d:\Program Files\Lua\5.1\lua\luadoc_start.lua" xxx.lua

这里注意:lua是已经注册到系统路径的,如果你没有安装整合包,需要手动加一下

如果需要批量生成,只需要用*.lua既可以

生成出来的网页文件放置在你的源码目录,用index.html开始查看

 

2. 让luaDoc识别程序里的注释

看下基本注释结构:

--- 继承于某table的属性及函数
-- @param selfclass 某table
-- @param parentclass 需要继承的table
-- @return 继承后的类
-- @usage local _widgetnotebox =  _Inherit( {}, WIDGET_BASE )
function _Inherit( selfclass, parentclass )
    if not parentclass then
        error("parentclass null", 2 )
    end
    return setmetatable( selfclass, 
    {
    __index = function( t, k )
        local ret = parentclass[k]
        selfclass[k]= ret
        return ret
    end
    
    } )
end
luaDoc从---开始识别你的函数注释,这个被放置于函数主要说明中
@param用作参数说明,selfclass标记是哪个参数的说明
@return说明返回值
@usage就是一些例子
posted @ 2009-07-15 17:47 战魂小筑 阅读(778) | 评论 (0)编辑 收藏

lua的第三方库还是很丰富的,来看下

http://luaforwindows.luaforge.net/

 

luaCom 支持COM调用

LuaDoc 支持lua代码的文档生成,我最需要的

LuaExpat 支持XML解析

LuaFileSystem 文件系统访问

LuaLogging, 基于log4j的日志

LuaProfiler 性能测试工具

LuaSocket 不用说了吧,网络库,支持HTTP,FTP,SMTP, MIME, URL & LTN12协议

LuaSQL 支持PostgreSQL, ODBC, MySQL, SQLite, Oracle, ADO

LuaZip 支持zip读取及打包

LuaInteface .NET的lua包

wxLua 支持lua调试及使用wxWidgets UI库

LOOP支持OO方式的lua

posted @ 2009-07-14 17:45 战魂小筑 阅读(2336) | 评论 (0)编辑 收藏

魔兽的UI插件结构

1.使用lua+XML作为配置

    分析:XML虽然人机交互很好,但其实没有几个UI是真正用纯XML写的,大多还是用编辑器比较方便。速度很慢,但尚不清楚魔兽代码里是否进行优化

2. Interface\Addons为插件目录,文件夹可以堆叠

3. 每个插件组,需要一个toc文件来做文件读取列表描述,类似于:

# Libraries
embeds.xml

AceGUIWidget-DragLink.lua
Core.lua

# Localization
Locale-enUS.lua
Locale-zhCN.lua
Locale-zhTW.lua

AutoBarDB.lua
AutoBarOptions.lua
AutoBarSearch.lua

4.一个插件组里可以拥有多个lua文件,都共享一个独立的全局空间

5.WTF\Account\账号名\服务器名\角色名\AddOns.txt文件描述哪些插件需要读取

根据分析:每次魔兽启动时,都会扫描一次插件目录,并更新这个列表,但是原有的插件读取状态仍然保留,类似于:

Combuctor: enabled
Combuctor_Config: enabled
Parrot: disabled
BattleInfo: disabled
BigWigs: disabled
BigWigs_Extras: disabled
BigWigs_BlackTemple: disabled
leafZone: enabled
InFlight: disabled

6. WTF\下的很多SavedVariables目录都是用于保存插件状态的,没有对lua的扩展库进行研究(ACE2/3等等),但是这是一种很好的保存插件数据的方法

OZ_Config = {
    {
        ["bottomCol"] = {
            ["a"] = 1,
            ["r"] = 0,
            ["g"] = 0,
            ["b"] = 0.6,
        },
        ["maxBars"] = 40,
        ["barHeight"] = 16,
        ["titleHeight"] = 20,
        ["sort2"] = 0,
        ["fadeAlpha"] = 0.3999999761581421,
        ["textSize"] = 10,
        ["colour"] = 2,
        ["minBars"] = 1,
        ["heading"] = {
            3, -- [1]
            0, -- [2]
            0, -- [3]

7.暂时没有找到魔兽UI的核心API是否用纯脚本提供的证据,但是可以推断,按照暴雪的实力,应该是全lua api写成。

 

 

构建安全的lua沙箱

所谓沙箱,就是每个插件拥有独立的_G全局环境,即便用户误将print修改,其他的插件也不会受到影响. 同时,考虑到沙箱的安全性和权限,需要对沙箱函数访问进行订制.以下是本人摸索出的一种方案:

先看下我的UI环境及lua嵌入架构:

1. C++层将必要的API注册到lua层.但都是基于id的全局函数(考虑到效率及便捷),但是实际使用时再在lua层进行OO封装,这和WINDOWS API及MFC的原理类似

2. C++层只提供4种原生控件: Button,Label, EditBox,MultiLineEditBox。其他的控件都是由这些组成。

3. 可以将整个系统分为内核模式和用户模式。

    内核模式:可以使用完整的API访问及权限。

    用户模式:被沙箱保护,无法访问一些危险的API,例如io

 

这里,我们使用lua_setfenv进行沙箱构建,首先我们必须将创建每个沙箱对应的table

// 放入沙箱名称
lua_pushstring( mLua, "mysandbox" );
// 将一个table压入栈
dotlua::table ts( mLua , false );
// 调用之前载入好的一个订制沙箱环境的函数
gt.call<void>("SetupSandBox", ts ); 
// 将沙箱以mysandbox的key保存在注册表中
lua_settable( mLua, LUA_REGISTRYINDEX );

之后使用lua_loadfile载入需要放进沙箱的代码

 

// 这里将本沙箱对应的环境取出来
lua_pushstring( mLua, "mysandbox" );
lua_gettable( mLua, LUA_REGISTRYINDEX );
// 栈内的情形为
// -1  沙箱table
// -2 chunk function
// 这里必须调用chunk函数
lua_setfenv( mLua, -2 );
调用pcall执行代码
这里的chunk函数,来源于lua_loadfile或者lua_loadbuffer,这2个函数将代码读入,但并不会执行,包括定义全局函数之类的操作。
沙箱订制函数必须提前载入,这里发一个做参考

function SetupSandBox( e )
    e._G = e
    -- system lib
    e.print         = print
    e.printf         = printf
    e.table         = table
    e.string         = string
    e.debug         = debug
    e.math             = math
    e.assert         = assert
    e.getmetatable     = getmetatable
    e.ipairs         = ipairs
    e.pairs         = pairs
    e.pcall         = pcall
    e.setmetatable     = setmetatable
    e.tostring         = tostring
    e.tonumber         = tonumber
    e.type             = type
    e.unpack         = unpack
    e.collectgarbage = collectgarbage
    -- class
    e.TREENODE = WIDGET_TREENODE
    e.SERIALIZE = WIDGET_SERIALIZE
    -- function
    e.CreateWidget = CreateWidget
    e._Inherit = _Inherit
end

只有被订制的函数,才能被调用
 
扩展:
 为了获得完整的内核模式开发,但又拥有独立的沙箱环境,可以使用setmetatable方式设置一个对全局table的引用,虽然速度慢了点。。
当然,可以支持一套可载入dll订制权限,并注册更多的api给自己的脚本用
posted @ 2009-07-09 11:12 战魂小筑 阅读(2160) | 评论 (1)编辑 收藏

最近开源的Mono .NET框架被警告远离微软,因为引来一大批版权的问题。同时也在verycd上发现一个非常不错的想法,用Mono C#做脚本引擎

http://www.verycd.com/topics/2747101/

Unity Technology最近刚刚发布他们的游戏开发工具/游戏引擎Unity的最新版,Unity 2.5。
这是一个3D游戏开发工具和游戏引擎套件,其中包括了图形、音频、物理、网络等多方面的引擎支持,并且有一个非常强大的编辑器来整合这一切。原本是一个针对Mac、Windows和Linux的游戏开发套件,后来发展到也能够在iPhone和Wii上部署,或者在Web上部署。不但如此,最新的Unity 2.5中的编辑器本身就是用Unity来实现的,也因此Unity的开发套件本身也可以在Windows上运行了(在此之前开发游戏需要用Mac)。
Unity 使用了Mono作为脚本引擎的虚拟机,并以C#或者一种类似JavaScript的语言为脚本语言。这在游戏引擎中并不多见(另外一个例子是Second Life也采用了Mono作为脚本引擎,C#作为脚本语言)。在游戏引擎中的应用也促使了Mono自身的进步,包括Mono.Simd等,使得Mono或者说托管代码更适合于游戏开发

 

posted @ 2009-07-03 10:59 战魂小筑 阅读(2594) | 评论 (0)编辑 收藏

转自我的cnblogs

最近在搜索AngleScript bug时,突然发现一个新的轻量级OO脚本语言Squirrel 翻译过来就是松鼠的意思

来看下这个脚本的样子:

local table = { a = "10" subtable = { array = [1,2,3] }, [10 + 123] = "expression index"} local array=[ 1, 2, 3, { a = 10, b = "string" } ];foreach(i,val in array){ ::print("the type of val is"+typeof val);}

class Entity{ constructor(etype,entityname) { name = entityname; type = etype; } x = 0; y = 0; z = 0; name = null; type = null;}function Entity::MoveTo(newx,newy,newz){ x = newx; y = newy; z = newz;}class Player extends Entity { constructor(entityname) { Entity.constructor("Player",entityname) } function DoDomething() { ::print("something"); } } local newplayer = Player("da playar"); newplayer.MoveTo(100,200,300);

特性:

1. 开源,zlib的那种无限制使用的license

2. 动态类型

3. Delegation代理支持

4. 支持类,及继承

5. 高阶函数功能

6. Generator

7. 纤程(Coroutines)

8. 尾递归(tail recursion)

9. 异常捕获

10. 引用计数方式的垃圾回收(有点过时)

11. 弱引用

12. 6k行编译器及虚拟机代码

13. 可选的16位字符串

14. 支持64位cpu

总体观感,这种脚本语言语法结构类似C++及lua的杂合体,很多地方像lua。OO部分又像Python

这里是一个完整文档说明,可以详细了解其vm接口及api调用。

不过到目前为主,未看到有成熟的商业项目使用

posted @ 2009-07-03 10:09 战魂小筑 阅读(1204) | 评论 (0)编辑 收藏

2008年时,闲来无事,开始研究Lua Debugger,发现目前的很多debugger都很简单。诸如LuaIDE带的,只能调试简单脚本,无法挂接到工程项目;LuaPlus的RemoteLuaDebugger写的倒是不错,但是代码编译很严重,而且依赖LuaPlus。因此结合这2个debugger的优点。自己写出了一套纯粹的远程调试Lua调试器

luadebugger

特性:

1. 基于一套简单的网络库,完全的远程调试

2. 调试客户端命名为DebugToy,并不为lua专用,以后可以用作调试任何代码

3. 如果客户端没有代码,或者没有指定代码路径。自动从远程服务器下载后显示。服务器可以将这个作为一个可选项

4. 基于SCIntilla的编辑器,编写和查看非常方便,支持语法高亮及断点等等格式。以后可以扩充很强大的功能

5. 调试快捷键与VC相同,支持CallStack,LocalVariable,WatchSymbole及 鼠标下Symbol值查看等功能

6. 最重要的一点,这个调试库可以动态载入,并提供给任何C++程序调试,无论你使用怎样的lua架构,都可以使用这个调试器进入代码调试

最近的UI编辑器中使用这个调试器解决了很多问题,因此准备将这个Debugger功能加强,使用C++CLI封装后,换做C#做界面,并让调试lua代码和C++代码一样

这里还有一个商用的Lua调试器,写的还很不错http://www.unknownworlds.com/decoda/

posted @ 2009-06-27 10:51 战魂小筑 阅读(1099) | 评论 (1)编辑 收藏

      最近在使用自己封住的一套lua库使用中遇到这样一个问题,多注册一个类成员函数后,即便一个lua文件都没加载,都会在lua_close中的freestack函数中crash

      我的lua封装非常简单,支持C++类成员函数调用上也是在luaplus基础上改造而来的:

    void register_helper( lua_State* L, int tableindex, const char* funcname, lua_CFunction dispatcher, void* func, int sizeoffunc, const void* userdata, int sizeofud )
    {
        // function | userdata = lightuserdata
        unsigned char* buffer = (unsigned char*)lua_newuserdata( L, sizeoffunc + sizeofud );
        memcpy( buffer, (void*)func, sizeoffunc );
        memcpy( buffer + sizeoffunc, &userdata, sizeofud );

        lua_pushstring( L, funcname );
        lua_pushlightuserdata( L, (void*)buffer);  
        lua_pushcclosure(L, dispatcher, 1);
        lua_settable( L, tableindex );
    }
    
 

检查发现,貌似是lua_newuserdata的问题,将其换为malloc,问题解决了。

虽然支持google了一下类似的问题,并且也在gc库内打过断点,new出来的内存绝非简单的被gc回收,而有可能是lua某些机制造成的,毕竟这样给C++做绑定是很常见的

马上做了一个list保存指针,并在lua_close之前清除这些指针,所有问题迎刃而解

posted @ 2009-06-20 10:47 战魂小筑 阅读(690) | 评论 (0)编辑 收藏

最近遇到一个vs2008 C++中断点无法工作的问题,具体表现为:

1. 某个修改后,断点无法工作,之前版本依然正常

2. 只是某个文件中断点无法工作

3. 断点打在 引用另外DLL中的类对象A

文件结构:

Core.dll

A.cpp

class A 
{ 
public: 
    A( )
    { 
        printf("constructor of a");
    } 

} 
 

UI.dll

Manager.cpp

class Manager
{ 
public: 
    Manager( )
    { 
        printf("constructor of Manager");
    } 

} 

 

Button.cpp

A ins; // 断点打在这里

Run.exe

void main() 
{ 
    Manager ins;  // 实例化 
} 

这里发现A根本不会被C runtime在执行main之前构造,因此断点无法打到

已经排除特殊字符影响的原因,按照网上的方法是,将文件保存为UNICODE,问题依旧

开始用排除隔离法确定问题位置,最后发现这样一个规律:

只要在Manager.cpp里构造一个Button

Button insB;

这样做了以后,断定必定能起作用。

因此推断CRT层的全局构造是需要一个链表来推动的

因为Manager需要构造,因此CRT肯定会扫描这个CPP对应OBJ链接到exe里的段,让这个段里的全局构造函数都初始化,但是Button因为没有使用,因此也不会构造。这样局部扫描应该是出于效率.

解决这个问题还有一个方法就是全部使用静态链接方式,这样构造代码都放到最后的exe中,就不会出现这样的问题

posted @ 2009-06-14 10:51 战魂小筑 阅读(417) | 评论 (0)编辑 收藏

仅列出标题
共26页: First 18 19 20 21 22 23 24 25 26