不会飞的鸟

2010年12月10日 ... 不鸟他们!!! 我要用自己开发的分布式文件系统、分布式调度系统、分布式检索系统, 做自己的搜索引擎!!!大鱼有大志!!! ---杨书童

#

[linux]ld.so.conf 和 ldconfig

今天重新编译以前的一个程序,里面用到iconv库:gcc test.cc -liconv
运行时:a.out:error while loading shared libraries: libiconv.so.2: cannot open shared object file: No such file or directory
以前编译运行是可以的,可能是不久前升级了iconv库影响。在/usr/local/lib下可以找到libiconv.so.2,把/usr/local/lib加到路径中也不行。
google了一下,解决了:在/etc/ld.so.conf中加一行/usr/local/lib,运行ldconfig。再运行a.out,行了。
ld.so.conf和ldconfig是维护系统动态链接库的。真不明白为什么iconv库安装时不把这一步也做了。



//注意
如果你不是root,ldconfig也运行不了的,解决的方法就是,设置环境变量 LDFLAGS=-L/usr/local/lib

posted @ 2009-09-24 12:46 不会飞的鸟 阅读(486) | 评论 (0)编辑 收藏

例解 autoconf 和 automake 生成 Makefile 文件

本文介绍了在 linux 系统中,通过 Gnu autoconf 和 automake 生成 Makefile 的方法。主要探讨了生成 Makefile 的来龙去脉及其机理,接着详细介绍了配置 Configure.in 的方法及其规则。

引子

无论是在Linux还是在Unix环境中,make都是一个非常重要的编译命令。不管是自己进行项目开发还是安装应用软件,我们都经常要用到make或 make install。利用make工具,我们可以将大型的开发项目分解成为多个更易于管理的模块,对于一个包括几百个源文件的应用程序,使用make和 makefile工具就可以轻而易举的理顺各个源文件之间纷繁复杂的相互关系。

但是如果通过查阅make的帮助文档来手工编写Makefile,对任何程序员都是一场挑战。幸而有GNU 提供的Autoconf及Automake这两套工具使得编写makefile不再是一个难题。

本文将介绍如何利用 GNU Autoconf 及 Automake 这两套工具来协助我们自动产生 Makefile文件,并且让开发出来的软件可以像大多数源码包那样,只需"./configure", "make","make install" 就可以把程序安装到系统中。





回页首


模拟需求

假设源文件按如下目录存放,如图1所示,运用autoconf和automake生成makefile文件。


图 1文件目录结构
图 1文件目录结构

假设src是我们源文件目录,include目录存放其他库的头文件,lib目录存放用到的库文件,然后开始按模块存放,每个模块都有一个对应的目录,模块下再分子模块,如apple、orange。每个子目录下又分core,include,shell三个目录,其中core和shell目录存放.c文件,include的存放.h文件,其他类似。

样例程序功能:基于多线程的数据读写保护(联系作者获取整个autoconf和automake生成的Makefile工程和源码,E-mail:normalnotebook@126.com)。





回页首


工具简介

所必须的软件:autoconf/automake/m4/perl/libtool(其中libtool非必须)。

autoconf是一个用于生成可以自动地配置软件源码包,用以适应多种UNIX类系统的shell脚本工具,其中autoconf需要用到 m4,便于生成脚本。automake是一个从Makefile.am文件自动生成Makefile.in的工具。为了生成Makefile.in,automake还需用到perl,由于automake创建的发布完全遵循GNU标准,所以在创建中不需要perl。libtool是一款方便生成各种程序库的工具。

目前automake支持三种目录层次:flat、shallow和deep。

1) flat指的是所有文件都位于同一个目录中。

就是所有源文件、头文件以及其他库文件都位于当前目录中,且没有子目录。Termutils就是这一类。

2) shallow指的是主要的源代码都储存在顶层目录,其他各个部分则储存在子目录中。

就是主要源文件在当前目录中,而其它一些实现各部分功能的源文件位于各自不同的目录。automake本身就是这一类。

3) deep指的是所有源代码都被储存在子目录中;顶层目录主要包含配置信息。

就是所有源文件及自己写的头文件位于当前目录的一个子目录中,而当前目录里没有任何源文件。 GNU cpio和GNU tar就是这一类。

flat类型是最简单的,deep类型是最复杂的。不难看出,我们的模拟需求正是基于第三类deep型,也就是说我们要做挑战性的事情:)。注:我们的测试程序是基于多线程的简单程序。





回页首


生成 Makefile 的来龙去脉

首先进入 project 目录,在该目录下运行一系列命令,创建和修改几个文件,就可以生成符合该平台的Makefile文件,操作过程如下:

1) 运行autoscan命令

2) 将configure.scan 文件重命名为configure.in,并修改configure.in文件

3) 在project目录下新建Makefile.am文件,并在core和shell目录下也新建makefile.am文件

4) 在project目录下新建NEWS、 README、 ChangeLog 、AUTHORS文件

5) 将/usr/share/automake-1.X/目录下的depcomp和complie文件拷贝到本目录下

6) 运行aclocal命令

7) 运行autoconf命令

8) 运行automake -a命令

9) 运行./confiugre脚本

可以通过图2看出产生Makefile的流程,如图所示:


图 2生成Makefile流程图
图 2生成Makefile流程图




回页首


Configure.in的八股文

当我们利用autoscan工具生成confiugre.scan文件时,我们需要将confiugre.scan重命名为confiugre.in文件。confiugre.in调用一系列autoconf宏来测试程序需要的或用到的特性是否存在,以及这些特性的功能。

下面我们就来目睹一下confiugre.scan的庐山真面目:


# Process this file with autoconf to produce a configure script.
            AC_PREREQ(2.59)
            AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
            AC_CONFIG_SRCDIR([config.h.in])
            AC_CONFIG_HEADER([config.h])
            # Checks for programs.
            AC_PROG_CC
            # Checks for libraries.
            # FIXME: Replace `main' with a function in `-lpthread':
            AC_CHECK_LIB([pthread], [main])
            # Checks for header files.
            # Checks for typedefs, structures, and compiler characteristics.
            # Checks for library functions.
            AC_OUTPUT
            

每个configure.scan文件都是以AC_INIT开头,以AC_OUTPUT结束。我们不难从文件中看出confiugre.in文件的一般布局:


AC_INIT
            测试程序
            测试函数库
            测试头文件
            测试类型定义
            测试结构
            测试编译器特性
            测试库函数
            测试系统调用
            AC_OUTPUT
            

上面的调用次序只是建议性质的,但我们还是强烈建议不要随意改变对宏调用的次序。

现在就开始修改该文件:


$mv configure.scan configure.in
            $vim configure.in
            

修改后的结果如下:


            #                                -*- Autoconf -*-
            # Process this file with autoconf to produce a configure script.
            AC_PREREQ(2.59)
            AC_INIT(test, 1.0, normalnotebook@126.com)
            AC_CONFIG_SRCDIR([src/ModuleA/apple/core/test.c])
            AM_CONFIG_HEADER(config.h)
            AM_INIT_AUTOMAKE(test,1.0)
            # Checks for programs.
            AC_PROG_CC
            # Checks for libraries.
            # FIXME: Replace `main' with a function in `-lpthread':
            AC_CHECK_LIB([pthread], [pthread_rwlock_init])
            AC_PROG_RANLIB
            # Checks for header files.
            # Checks for typedefs, structures, and compiler characteristics.
            # Checks for library functions.
            AC_OUTPUT([Makefile
            src/lib/Makefile
            src/ModuleA/apple/core/Makefile
            src/ModuleA/apple/shell/Makefile
            ])
            

其中要将AC_CONFIG_HEADER([config.h])修改为:AM_CONFIG_HEADER(config.h), 并加入AM_INIT_AUTOMAKE(test,1.0)。由于我们的测试程序是基于多线程的程序,所以要加入AC_PROG_RANLIB,不然运行automake命令时会出错。在AC_OUTPUT输入要创建的Makefile文件名。

由于我们在程序中使用了读写锁,所以需要对库文件进行检查,即AC_CHECK_LIB([pthread], [main]),该宏的含义如下:



其中,LIBS是link的一个选项,详细请参看后续的Makefile文件。由于我们在程序中使用了读写锁,所以我们测试pthread库中是否存在pthread_rwlock_init函数。

由于我们是基于deep类型来创建makefile文件,所以我们需要在四处创建Makefile文件。即:project目录下,lib目录下,core和shell目录下。

Autoconf提供了很多内置宏来做相关的检测,限于篇幅关系,我们在这里对其他宏不做详细的解释,具体请参看参考文献1和参考文献2,也可参看autoconf信息页。





回页首


实战Makefile.am

Makefile.am是一种比Makefile更高层次的规则。只需指定要生成什么目标,它由什么源文件生成,要安装到什么目录等构成。

表一列出了可执行文件、静态库、头文件和数据文件,四种书写Makefile.am文件个一般格式。


表 1Makefile.am一般格式
表 1Makefile.am一般格式

对于可执行文件和静态库类型,如果只想编译,不想安装到系统中,可以用noinst_PROGRAMS代替bin_PROGRAMS,noinst_LIBRARIES代替lib_LIBRARIES。

Makefile.am还提供了一些全局变量供所有的目标体使用:


表 2 Makefile.am中可用的全局变量
表 2 Makefile.am中可用的全局变量

在Makefile.am中尽量使用相对路径,系统预定义了两个基本路径:


表 3Makefile.am中可用的路径变量
表 3Makefile.am中可用的路径变量

在上文中我们提到过安装路径,automake设置了默认的安装路径:

1) 标准安装路径

默认安装路径为:$(prefix) = /usr/local,可以通过./configure --prefix=<new_path>的方法来覆盖。

其它的预定义目录还包括:bindir = $(prefix)/bin, libdir = $(prefix)/lib, datadir = $(prefix)/share, sysconfdir = $(prefix)/etc等等。

2) 定义一个新的安装路径

比如test, 可定义testdir = $(prefix)/test, 然后test_DATA =test1 test2,则test1,test2会作为数据文件安装到$(prefix)/ /test目录下。

我们首先需要在工程顶层目录下(即project/)创建一个Makefile.am来指明包含的子目录:


SUBDIRS=src/lib src/ModuleA/apple/shell src/ModuleA/apple/core
            CURRENTPATH=$(shell /bin/pwd)
            INCLUDES=-I$(CURRENTPATH)/src/include -I$(CURRENTPATH)/src/ModuleA/apple/include
            export INCLUDES
            

由于每个源文件都会用到相同的头文件,所以我们在最顶层的Makefile.am中包含了编译源文件时所用到的头文件,并导出,见蓝色部分代码。

我们将lib目录下的swap.c文件编译成libswap.a文件,被apple/shell/apple.c文件调用,那么lib目录下的Makefile.am如下所示:


noinst_LIBRARIES=libswap.a
            libswap_a_SOURCES=swap.c
            INCLUDES=-I$(top_srcdir)/src/includ
            

细心的读者可能就会问:怎么表1中给出的是bin_LIBRARIES,而这里是noinst_LIBRARIES?这是因为如果只想编译,而不想安装到系统中,就用noinst_LIBRARIES代替bin_LIBRARIES,对于可执行文件就用noinst_PROGRAMS代替bin_PROGRAMS。对于安装的情况,库将会安装到$(prefix)/lib目录下,可执行文件将会安装到${prefix}/bin。如果想安装该库,则Makefile.am示例如下:


bin_LIBRARIES=libswap.a
            libswap_a_SOURCES=swap.c
            INCLUDES=-I$(top_srcdir)/src/include
            swapincludedir=$(includedir)/swap
            swapinclude_HEADERS=$(top_srcdir)/src/include/swap.h
            

最后两行的意思是将swap.h安装到${prefix}/include /swap目录下。

接下来,对于可执行文件类型的情况,我们将讨论如何写Makefile.am?对于编译apple/core目录下的文件,我们写成的Makefile.am如下所示:


noinst_PROGRAMS=test
            test_SOURCES=test.c
            test_LDADD=$(top_srcdir)/src/ModuleA/apple/shell/apple.o $(top_srcdir)/src/lib/libswap.a
            test_LDFLAGS=-D_GNU_SOURCE
            DEFS+=-D_GNU_SOURCE
            #LIBS=-lpthread
            

由于我们的test.c文件在链接时,需要apple.o和libswap.a文件,所以我们需要在test_LDADD中包含这两个文件。对于Linux下的信号量/读写锁文件进行编译,需要在编译选项中指明-D_GNU_SOURCE。所以在test_LDFLAGS中指明。而test_LDFLAGS只是链接时的选项,编译时同样需要指明该选项,所以需要DEFS来指明编译选项,由于DEFS已经有初始值,所以这里用+=的形式指明。从这里可以看出,Makefile.am中的语法与Makefile的语法一致,也可以采用条件表达式。如果你的程序还包含其他的库,除了用AC_CHECK_LIB宏来指明外,还可以用LIBS来指明。

如果你只想编译某一个文件,那么Makefile.am如何写呢?这个文件也很简单,写法跟可执行文件的差不多,如下例所示:


noinst_PROGRAMS=apple
            apple_SOURCES=apple.c
            DEFS+=-D_GNU_SOURCE
            

我们这里只是欺骗automake,假装要生成apple文件,让它为我们生成依赖关系和执行命令。所以当你运行完automake命令后,然后修改apple/shell/下的Makefile.in文件,直接将LINK语句删除,即:


…….
            clean-noinstPROGRAMS:
            -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
            apple$(EXEEXT): $(apple_OBJECTS) $(apple_DEPENDENCIES)
            @rm -f apple$(EXEEXT)
            #$(LINK) $(apple_LDFLAGS) $(apple_OBJECTS) $(apple_LDADD) $(LIBS)
            …….
            

通过上述处理,就可以达到我们的目的。从图1中不难看出为什么要修改Makefile.in的原因,而不是修改其他的文件。

posted @ 2009-06-21 16:25 不会飞的鸟 阅读(235) | 评论 (0)编辑 收藏

实战Makefile.am

实战Makefile.am

Makefile.am是一种比Makefile更高层次的规则。只需指定要生成什么目标,它由什么源文件生成,要安装到什么目录等构成。

表一列出了可执行文件、静态库、头文件和数据文件,四种书写Makefile.am文件个一般格式。


表 1Makefile.am一般格式


 

对于可执行文件和静态库类型,如果只想编译,不想安装到系统中,可以用noinst_PROGRAMS代替bin_PROGRAMS,noinst_LIBRARIES代替lib_LIBRARIES。

Makefile.am还提供了一些全局变量供所有的目标体使用:

表 2 Makefile.am中可用的全局变量

在Makefile.am中尽量使用相对路径,系统预定义了两个基本路径:

表 3Makefile.am中可用的路径变量

在上文中我们提到过安装路径,automake设置了默认的安装路径:

1)标准安装路径

默认安装路径为:$(prefix) = /usr/local,可以通过./configure --prefix=<new_path>的方法来覆盖。

其它的预定义目录还包括:bindir = $(prefix)/bin, libdir = $(prefix)/lib, datadir = $(prefix)/share, sysconfdir = $(prefix)/etc等等。

2) 定义一个新的安装路径

比如test, 可定义testdir = $(prefix)/test, 然后test_DATA =test1 test2,则test1,test2会作为数据文件安装到$(prefix)/ /test目录下。

我们首先需要在工程顶层目录下(即project/)创建一个Makefile.am来指明包含的子目录:

SUBDIRS=src/lib src/ModuleA/apple/shell src/ModuleA/apple/core

CURRENTPATH=$(shell /bin/pwd)

INCLUDES=-I$(CURRENTPATH)/src/include -I$(CURRENTPATH)/src/ModuleA/apple/include

export INCLUDES

由于每个源文件都会用到相同的头文件,所以我们在最顶层的Makefile.am中包含了编译源文件时所用到的头文件,并导出,见蓝色部分代码。

我们将lib目录下的swap.c文件编译成libswap.a文件,被apple/shell/apple.c文件调用,那么lib目录下的Makefile.am如下所示:

noinst_LIBRARIES=libswap.a

libswap_a_SOURCES=swap.c

INCLUDES=-I$(top_srcdir)/src/includ

细心的读者可能就会问:怎么表1中给出的是bin_LIBRARIES,而这里是noinst_LIBRARIES?这是因为如果只想编译,而不想安装到系统中,就用noinst_LIBRARIES代替bin_LIBRARIES,对于可执行文件就用noinst_PROGRAMS代替bin_PROGRAMS。对于安装的情况,库将会安装到$(prefix)/lib目录下,可执行文件将会安装到${prefix}/bin。如果想安装该库,则Makefile.am示例如下:

bin_LIBRARIES=libswap.a

libswap_a_SOURCES=swap.c

INCLUDES=-I$(top_srcdir)/src/include

swapincludedir=$(includedir)/swap

swapinclude_HEADERS=$(top_srcdir)/src/include/swap.h

最后两行的意思是将swap.h安装到${prefix}/include /swap目录下。

接下来,对于可执行文件类型的情况,我们将讨论如何写Makefile.am?对于编译apple/core目录下的文件,我们写成的Makefile.am如下所示:

noinst_PROGRAMS=test

test_SOURCES=test.c

test_LDADD=$(top_srcdir)/src/ModuleA/apple/shell/apple.o $(top_srcdir)/src/lib/libswap.a

test_LDFLAGS=-D_GNU_SOURCE

DEFS+=-D_GNU_SOURCE

#LIBS=-lpthread

由于我们的test.c文件在链接时,需要apple.o和libswap.a文件,所以我们需要在test_LDADD中包含这两个文件。对于Linux下的信号量/读写锁文件进行编译,需要在编译选项中指明-D_GNU_SOURCE。所以在test_LDFLAGS中指明。而test_LDFLAGS只是链接时的选项,编译时同样需要指明该选项,所以需要DEFS来指明编译选项,由于DEFS已经有初始值,所以这里用+=的形式指明。从这里可以看出,Makefile.am中的语法与Makefile的语法一致,也可以采用条件表达式。如果你的程序还包含其他的库,除了用AC_CHECK_LIB宏来指明外,还可以用LIBS来指明。

如果你只想编译某一个文件,那么Makefile.am如何写呢?这个文件也很简单,写法跟可执行文件的差不多,如下例所示:

noinst_PROGRAMS=apple

apple_SOURCES=apple.c

DEFS+=-D_GNU_SOURCE

我们这里只是欺骗automake,假装要生成apple文件,让它为我们生成依赖关系和执行命令。所以当你运行完automake命令后,然后修改apple/shell/下的Makefile.in文件,直接将LINK语句删除,即:

…….

clean-noinstPROGRAMS:

    -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)

apple$(EXEEXT): $(apple_OBJECTS) $(apple_DEPENDENCIES)

    @rm -f apple$(EXEEXT)

#$(LINK) $(apple_LDFLAGS) $(apple_OBJECTS) $(apple_LDADD) $(LIBS)

…….

posted @ 2009-06-21 16:19 不会飞的鸟 阅读(885) | 评论 (0)编辑 收藏

带你轻松接触PowerDesigner中的反向工程

Power Designer是Sybase公司的CASE工具集,使用它可以方便地对管理信息系统进行分析设计,它几乎包括了数据库模型设计的全过程。利用Power Designer可以制作数据流程图、概念数据模型、物理数据模型,可以生成多种客户端开发工具的应用程序,还可为数据仓库制作结构模型,也能对团队设计模型进行控制。

Power Designer的4种模型:概念数据模型 (CDM)物理数据模型 (PDM) 面向对象模型 (OOM) 业务程序模型 (BPM) 我主要介绍一下PDM。

PDM 叙述数据库的物理实现,帮助你考虑真实的物理实现的细节。你能通过修正PDM来适合你的表现或物理约束。主要目的是把CDM中建立的现实世界模型生成特定的DBMS脚本,产生数据库中保存信息的储存结构,保证数据在数据库中的完整性和一致性。

PDM是适合于系统设计阶段的工具。简单说:就是PDM可以自动生成诸如''create table''之类的sql脚本.在数据建模过程中,我们建立概念数据模型,通过正向工程生成物理数据模型,生成数据库建库脚本,最后将物理数据模型生成关系数据库。

系统数据库设计人员希望能够将数据库设计和关系数据库生成无缝地集成起来,如何保证物理数据模型与其对应数据库之间的双向同步成为数据建模非常关键的一点。

Powerdesigner作为强大的Case工具,为我们提供了方便的逆向工程特性。可以将目前所有流行的后端数据库(包括Sybase、DB2、Oracle等)的结构信息通过逆向工程加入到PowerDesigner的物理数据模型和概念数据模型中,包括表、索引、触发器、视图等。

用PowerDesigner进行逆向工程

◆1.我用的数据库是oracle9i,我为了访问oracle数据库,在我的机器上安装了oracle客户端(提供了oracle客户端的驱动程序,而精简客户端则不可以),配置一个名称为mylcl的服务:MYLCL = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.3.106)(PORT = 1521)) ) (CONNECT_DATA = (SID = pwsc) ) )用户名为:testuser,密码为test。

◆2.在pd中,新建一个pdm,选择数据库为oracle9i。

◆3.选择Database->configure connections,转到system dsn标签,点击"添加",选择驱动程序,由于我的数据库是oracle,所以我选择"oracle in oraclient10g_home1"(安装了oracle客户端才有这个驱动,而精简客户端没有此驱动)。

◆4.在data source name 中,可以随便命名一个"ora-test",在tns-server name中选择第一步中的服务名称:mylcl.点击"test connection",输入用户名密码,connection ok!

◆5.点击database->reverse engineer database ,选择odbc datasource:ora-test.然后点击确定。(责任编辑:卢兆林)

posted @ 2009-05-28 06:44 不会飞的鸟 阅读(918) | 评论 (0)编辑 收藏

linux c 一个autotools的最简单例子

     摘要:   1、准备:        需要工具autoscan aclocal autoheader automake autoconf make 等工具.  2、测试程序编写:         &...  阅读全文

posted @ 2009-05-14 17:31 不会飞的鸟 阅读(2034) | 评论 (1)编辑 收藏

游戏引擎基础(十一)(最后的章节)

     摘要: 第11部份: 最后的章节前端  你已经看到了菜单系统,你可能理解游戏内的头顶显示(HUDs)时常是游戏经历中被忽视和诽谤的部分。最近,这个领域开始被给人印象非常深刻的Black and White所关注,这款游戏实际上没有HUD。在Peter Molyneux经历了Dungeon Keeper以后,它在屏幕上大量的图标,他决定游戏的大部分被这些图标占用了,主要的屏幕没有被足够利用。因此他决定废除所...  阅读全文

梦在天涯 2007-12-04 13:31 发表评论

posted @ 2009-04-10 10:44 不会飞的鸟 阅读(79) | 评论 (0)编辑 收藏

游戏引擎基础(十)(人工智能和导航)

10部分: 人工智能和导航(路径发现)


人工智能(AI
  我们上面已经用了其他九个章节介绍了游戏引擎,现在让我们深入到非常有趣和重要的人工智能主题。人工智能如今正在变成被谈论得最多的仅次于游戏引擎渲染能力的游戏开发领域之一,确实如此。直到大约两年半以前,游戏似乎主要是在考虑你能够渲染多少个多边形,眼睛是多么的漂亮,和劳拉的胸部是多么的有弹性...既然我们现在已经能够渲染出非常真实的乳房,中心就开始转移到我们实际上用那些多边形做什么了(即玩游戏)。因为它给你提供实际玩游戏的刺激作用和参与游戏世界中正在进行的事情,所以人工智能在这个领域非常关键。

  人工智能包括了全部的东西,从在Tetris中决定哪一块新砖头掉落(这很大程度上知识一个随即数产生器), 一直到创造基于小组的策略游戏,这些游戏和你交互,并且实际上在你玩的时候向你学习。人工智能包含了许多规则,如果你(作为一个游戏开发者)没有花费足够多的时间让它正确地工作,它会反过来在你屁股上咬一口。所以让我们谈论一些哪些规则?这样你能更好地理解人工智能系统会确实是多么的复杂。为了避免法律上的纠纷,我们将使用一个假设的游戏而不是一个真实的游戏作为例子。

  假设我们的游戏中有坏份子生活在3D世界中,干着他们的事情,而且如果你打搅了他们的正常次序他们就会反抗你(玩家)。你必须决定的第一件事情就是他们正在从事的到底是什么事情呢?他们正在守卫什么东西吗?在巡查?在计划一个聚会?在购买食品杂货?在整理床铺?建立行为的基线是游戏开发者的工作之一。一旦有了这个,你就总有NPC(非玩家角色)或计算机控制的能够恢复去做的事情,玩家与他们的交互就应当能被完成。

  一旦我们知道一个NPC角色需要做什么比如它在守卫一扇门,并且在这个区域小巡逻,NPC也必须有世界意识。游戏设计者需要决定NPC的人工智能将如何看见世界,和它的知识范围。你将会仅仅说计算机知道正在进行的每件事情吗?这通常被认为是一件糟糕的事情,因为非常明显计算机能够看见和听见你不能看见和听见的事情,这被当成是在作弊。不是一种有趣的经历。或者你将模拟他的视野,这样他只能够对他能看见的事物作出反应吗?当有墙壁出现时这里就有问题了,因为你开始进入那些我在第九部分提到的追踪例程,看看NPC是否试图对被墙壁挡住的人作出反应。这是一个很明显的人工智能问题,但是当涉及到门和窗户时,这个甚至变得更加复杂了。

  当你开始为AI刺激例程增加听觉意识时,这依然变得更加复杂了。但是,这个意识是那些关键的小事情之一,这些使得假想的游戏世界似乎更加真实,或者能够去除怀疑的悬念。如果你碰到过这样的事情,请举手:你在枪战中跟一个NPC交战,免除了一个NPC,你绕着角落行走并遇到了另外一个NPC依然保持他的缺省行为模式,没有意识到刚刚发生的事情。现在,枪是嘈杂的事物,枪战可能已经明显地提醒了一个倾听NPC有些事情正在进行。避免这种事情的技巧在于找到一个有效的方式来决定声源(即你武器的发射)的距离是否足够接近到NPC能够听见。

  接下来就是决策例程。当我们的巡逻NPC角色能够听到但不能看见某物时,你试图实现什么样的行为呢?他去寻找它吗?不理睬它?你如何决定什么是重要的声音他应该去或者不去调查?如同你看见的一样,这会很快变得非常的复杂。有很多方法来建造处理这些事情的代码,但通常这样是一个好主意,建立一个不是对特定的NPC而是对所有的NPC都起作用的系统,该系统基于你能够在游戏引擎以外的文本文件中建立的属性。这样就不需要程序员为一个给定的角色而改变AI,并且如果你对游戏代码做了改动,它将立即自动地应用到所有的角色,这在大多数情况下是一件好事情。

  其他的世界意识问题会冒出来,比如这样的情形,两个守卫彼此紧挨着站立,你用狙击武器干掉了一个,而另外一个站在哪儿完全不知已经发生的事情。再者,遵守真实世界行为的细节是一款好游戏和一款伟大游戏的之间的区别。

  让我们说你已经把所有的刺激-响应部分准备好了你已经扫描了世界,决定NPC应当对正在进行的一些事情作出反应他听到了玩家角色发出了声响并且你(游戏开发者)决定了他应当对这个做些什么他将去调查。现在更加复杂的事情来了。他如何离开现在的位置,到达他认为发出声音的地方,而不会想通常的数字傻瓜一样跑到墙壁里面,碰到家具呢?继续往下看


有关正确的路径 --- 世界导航
  快速,准确的世界导航( 也叫做路径-发现) 近来已经成为游戏开发者的圣杯。 让它看起来非常信服是一件非常困难的事情。你需要有局部世界的地理知识墙壁的位置,台阶,悬崖和建筑物等的边缘。你也需要世界中的对象的知识比如家具,汽车,尤其是其他人的位置。真正最后的因素是问题所在,一会儿我们将回到这一点上。

  世界导航通常被分为两个领域,世界导航和局部导航。二者实际上只是范围上的区别,但大多数的程序员分别对待它们,因为这样处理起来容易一些。世界导航例程处理理解房间,门和一般的地理学,并计算出让玩家或者角色从世界中的A点到达B点的一条路径。它将让你从A点到达B,这是一句很容易说的话,不是吗?说起来容易,但做起来很困难。理解世界是一个非常复杂问题,我已经看到过许多尝试过的解决办法。QuakeIII的机器人遵照建造的预先处理过的地图,一般的说法,使用原来地图的地面。预处理器检测地面元素,由地图建造者作上标记,并自己建造一个只使用地面的世界简化地图。机器人并不关心墙壁,因为他们从不接近它们,就像他们遵照地面的地图一样,设计上已经把避免墙壁构造在里面了。

  其他方法在地图本身里面建造一些小的结点,AI可以追随它们。这些结点通常被建造在彼此的视线里面,有从一个结点到其他所有结点的连接,角色AI能够直接看见,所以你就确保了从一个结点移动到另外一个结点时AI不会试图穿越墙壁。如果有门或者降落物,你能够事先用这些结点对路径的信息编码,于是NPC能够采用适当的行为等候电梯,打开一扇门,或者从一点跳到另外一点。这实际上是HereticII使用的系统,也是Raven在他们其他的大多数游戏中使用的系统。

  关于这个主题,3D RealmsJess Crable,现在为Duke Nukem Forever工作,如是说:

  "导航在许多方面是个巨大的挑战,主要是当游戏中有大量正在发生的事情和一些非计划性的东西,比如障碍。为了避免和(或)真实地对非计划性的障碍物导航(例如像另外的AI),AI需要很好地知道正在它周围发生的事情。比较而言另外一个巨大的挑战就是真实感。如果AI正在表现玩家在实际生活中看到的一些东西,比如说一个人,或者一条狗, 那么让它看上去真实可信就更加困难。"

  然后就是局部导航。我们可能有一条路径让我们的 NPC 从他在世界中的位置,移动到他认为听到声音的地方,但你不能盲目地按照这个执行并期望得到看起来不错的结果。这种性质的路径倾向于非常特定于一个给定的目的。当你沿着走廊从一个房间跑到另外一个房间时,它很好,但如果你试图指导他穿越一个巨大的房间时,路径结点方法容易最终得到一些看起来很奇怪的发现路径。这些路径也不是动态的。因为他们被预先建造,他们不容易考虑到世界的任何动态变化。桌子可能有被移动过了,椅子被破坏了,墙壁被摧残,当然,人们会移动。这就是局部导航不同于世界导航的地方。它必须考虑局部世界并导航NPC在里面穿越。它必须知道周围的环境,存在哪些可以选择的路径,并决定选择哪一条。

  在局部导航中最大的问题是其他的NPC。给定一个发现路径的具体例程,如果你在相同的一般区域中有不止一个NPC,他们都试图到达世界的同一地点,结果是他们都非常容易有相同的路径。然后他们试图沿着这个路径行进,结果彼此遇到一起,然后花费他们所有的时间试图将彼此分开,并且一旦成功地分开了,他们再次试图到达目标,然后我们又再次看到同样的事情发生。这一切看起来都是非常的愚蠢,这不是大多数人想要的效果。所以需要一些路径发现中的变化来避免这种情形,需要一些妥善处理避免的代码。有大量能够帮助解决这种情形的算法。


人工智能和角色动画问题
  当然,当角色自己在世界中行走时你必须完全地决定你想要角色播放什么动画。听起来无足轻重?不是的。关于这个主题,Raven Chris Reed—Soldier of FortuneII使用名为LICHAI系统的现在的负责人如是说:

  "此刻我能告诉你,我们在平滑移动上正有着最大的困难。在一个多丘陵的长满草的丛林中试图让五个角色在彼此附近行走是一个非常困难的问题。让底层系统完美是重要的,因为除非角色在较低层次上(避免墙壁,适当的动画)看起来真实,他们不能够有效地表达任何较高层次决定的智能。由于这个单独的原因,动画和底层的移动是最重要的和最难实现的。它确实需要完美。"

  因此我们已经让我们的角色从A点到达了B点,他自己穿越世界,在途中避免障碍物,正确播放动画,现在到达了这里。他看见了你。接下来做什么呢?很明显更多的是作出决策。他将向你射击。太棒了。你回应射击。现在干什么?当他试着逃走的时候,现在你再次经历全部同样的事情。

  为了让这些情形看起来令人信服,你看见了这里必须要处理的大量问题。如果你建立你的AI使用没有动画的行为让NPC执行,这能被混合。一些Soldier of Fortune中的AI就是这样的例子。他们受到了指责,因为坏家伙没有以适当的方式对刺激作出反应。当他们明显应该这样做的时候,敌方NPC不扫射,或者不逃跑。部分问题是他们没有扫射敌人NPC的动画,或者让他们往回跑,因为空间的问题。因此世界上所有最伟大的AI代码都不能够解决这个问题。这是所有要考虑的重要事情。

  想知道隐藏的难点吗?看看我前面所有的描述,然后试着将它应用到一组NPC上,这些NPC彼此必须说话,设定目标,彼此沟通,但不妨碍彼此的方式。一旦你这么做了,试试那些代码,作为玩家的队友做上面所描述的这些,然而不要在枪战中妨碍他。现在这是复杂的。然后这成为乐趣。这是最困难的部分。Raven Chris Reed关于AI‘感觉的一些评论:

  "我认为反馈是AI的一个极大的问题。如果角色对于他周围环境的变化不产生反应,游戏的真实感就被完全打破了。这有许多明显的例子(听见枪炮声,看见同伴被击中...),以及一些更加微妙的事情(当两个人通过门厅时看着彼此并点头致意)。玩家是乐意接受一些生硬和可预测性的,但是这些事物容易把游戏带到现实生活。"

  并且Jess Crable 赞同:

  "平衡是非常重要的对玩家将会有多大的乐趣至关重要,但还有其他的问题要平衡。游戏玩家时常说他们想在游戏中看见更加真实的人工智能。然而,太多的真实感开始把乐趣带走。在这两者之间必须要有一个好的平衡。变化和随机同样也很重要行为的变化,和保持在可信范围内的一定程度的不可预测性。"


游戏规则与自然发生的游戏
  在我们关于AI的所有描述中,我们采用的是FPS的方式。有不止一种的AI。我们已经描述的是处理3D世界一组规则。AI远远不止这些。时常最好的AI实际上非常的简单。它就是一组规则,玩家必须响应和处理的响应(或开始)动作的规则。

  这里应当处理一个被称为自然发生的游戏的专业术语。 自然发生的游戏本质上创造游戏将遵守的规则,那将会造成游戏程序员不能预见的情形。

  举例来说,象棋能被认为是自然发生的游戏。有一组规则,但游戏能够陷入各种程序员不能够以个别方式处理的情形。你不能为每一种可能的棋局情形编码规则。很清楚,游戏玩家每次不会总是面临相同的游戏情景。一定程度上,进行中的游戏情形会根据他的行动而发生变化。Black and White是这种情形的一个完美的例子,和The Sims一样游戏有它自己的规则,但你如何运用和调和他们是你自己的事情。实际上,你在玩游戏的过程中创造着游戏,而不是照着游戏设计者/程序员已经为你定义的路线进行。

  有可能把基于规则的,自然发生的游戏方式和FPS环境混合在一起。Half Life中的一些海军陆战队士兵的行为就是这样做的压制火力和侧翼攻击从设定的规则中动态完成。它看起来是动态的,而且一定程度上它是这样。然而,在FPS世界中仅仅有一组规则时常是不够的。几何和其他AI时常能够打败简单的规则,这让保持正确并依然有趣变得更加困难。所以对那些可怜的AI程序员有一些同情心吧。他们的工作不容易。


  好吧,下面还有一个章节,仅仅还剩下一个章节了。在最后的章节里,我们将讨论头顶显示,菜单系统,游戏定制和配置,游戏引擎版权与建造,最后是游戏“mods”



梦在天涯 2007-12-04 13:29 发表评论

posted @ 2009-04-10 10:44 不会飞的鸟 阅读(199) | 评论 (0)编辑 收藏

游戏引擎基础(九)(现成产品与定做的游戏引擎设计工具,游戏特定主题)

     摘要: 第9部分: 现成产品与定做的游戏引擎设计工具,游戏特定主题现成产品与定做的设计工具  我们从第8部份的脚本引擎来到这一章节中的许多主题,我们认为那些铁杆游戏玩家和有志成为游戏开发者的那些人将会发现它们相当有趣。我们将开始讨论现成产品与定制的设计工具。   你的工具的选择是你引擎设计的一个非常重要的部份,因为这是你将用来给你的游戏产生内容的东西,是最耗时的部份。在这个过程中有助于节省时间和资源的任何...  阅读全文

梦在天涯 2007-12-04 13:28 发表评论

posted @ 2009-04-10 10:44 不会飞的鸟 阅读(99) | 评论 (0)编辑 收藏

游戏引擎基础(八)(脚本系统)

     摘要: 第8部份: 脚本系统脚本系统  我们从第七部分的游戏网络问题来到了脚本系统,因为其呈现的故事叙述机会,最近已经形成一种很大的游戏元素。在一个需要以受控制的方式解释的情景,预先编制的电影脚本是解决问题的方法。在电影中,这通常用来处理或者由主角向一个伙伴解释情形,或者敌人对英雄解释。当然,有其它的方法来做这件事情 -- 叙事者,倒叙,等等 – 但通常是使用实时情景的人们和事件来完成。当然,...  阅读全文

梦在天涯 2007-12-04 13:27 发表评论

posted @ 2009-04-10 10:44 不会飞的鸟 阅读(149) | 评论 (0)编辑 收藏

游戏引擎基础(七)(网络和连线游戏环境)

7部份: 网络和连线游戏环境


网络游戏
  我记得一些年前坐在GDC(游戏开发者大会)听负责开发X-Wing Vs TIE Fighter的家伙们题为淹没在Internet” 的演讲,全是关于让网络游戏实时地在Internet上工作的东西。他们选择那个题目是多么的正确啊。当它开始处理数据包的丢失,乱序,潜伏(一个数据包发送到它的目的地所花的时间)等等时,它确实淹没了。然而它是可能的。对于Internet需要一些聪明和经验,但它是肯定可能的。看看今天大量的连线游戏,从Quake IIIUnreal TournamentCounter Strike一直到EverQuestUltima Online

  如今大多数真正有长久生命力的游戏都至少有一些连线成分。最纯粹的单人游戏容易玩一次,也许两次,或者甚至三次如果它是非常好的游戏,但一旦游戏结束,就被束之高阁了。如果你想要有任何长久生命力,那么多人连线游戏就是形势的核心所在,并且那意味着和Internet打交道,为编码者打开了那个潘多拉的盒子。

  那么跟Internet打交道包括些什么呢?首先是要理解Internet是怎么工作的,和点对点与客户机/服务器体系结构的快速讨论。点对点就是你在两台机器上运行游戏,并简单地在它们之间共享输入。每个单独的游戏假定它是正确的,并仅仅在它一幀接一幀的刷新中合并来自另外一台机器的输入。客户机/服务器是一台机器有效地运行游戏,别的机器仅仅是一个终端,接受来自玩家的输入,并渲染服务器让它渲染的任何东西。

  客户机/服务器的优点是每台机器都将会展现相同的游戏,因为所有的处理都在一个地方完成,没有跨越多台机器,你可以不用考虑每台机器相互之间的同步问题。不足之处是,服务器本身需要有一些重要的CPU可用时间来处理每一个连接的客户机,和一个合适的网络连接来确保每一个客户机及时地接收到它的更新。


了解IP
  我们都已经听说过TCP/IP(传输控制协议/网间协议)和UDP(用户数据包协议), Web网络上有大量关于这些协议的深奥的技术资讯。实际上,在Cisco网站上有一些极好的TCP/IP指导。我们将在较高层面上介绍一些TCP/IP的基本知识,目的是让你更好地了解使用这些标准协议的网络游戏设计者面临的挑战。

  TCP/IPUDP/IP是两层的通信协议系统。IP层负责网际数据包的传输。UDP或者TCP层将大的数据包传给IPIP将数据包分割为小的子数据包,为每个数据包加上一个信封,计算出目的地的IP地址,应该如何到达那里,然后将数据包发送到你的ISP,或者不管怎样你连接到网络。 这实在象是在一张明信片上写下你要发送的,贴上邮票,写上地址,塞进一个邮箱,它就送走了。

  UDPTCP是从你编码者或者游戏接收数据包的高层协议,并决定该如何处理这些数据包。UDPTCP的区别在于TCP保证数据包的传送和有序,而UDP不保证。UDP是一条直接和IP对话的小路,而TCP是在你和IP之间的一个接口。它像是在你和你的邮件之间有一个管理员助手。使用UDP你会自己为你的信打字,把它们放进一个信封等等。使用TCP你会仅仅向你的管理员口授信稿,管理员会做全部的工作并追踪确认信件送到了。

  然而,所有这些令人惊奇的为你完成的工作伴随着代价。为了确定数据包通过Internet完好无损地送到了目的方,TCP期待从目的方为它发送的每个数据包发回一个应答包(网络用语是ACK)。如果它在一定时间内没有收到ACK,它就停止发送任何新的数据包,重新发送丢失的数据包,并且将继续这样做直到收到目的方的回应。当你访问一个网页时,我们都已经看到了这种情形,在半途中下载停止了一会然后又重新开始了。可能是一个数据包在什么地方丢失了(假定不时ISP的问题),在任何更多的数据包被发送以前TCP要求重新发送它。

  这一切的问题是,在认识到出了差错的发送者和实际上正在送达的数据包之间出现了延迟。有时这能花上数秒钟,如果你仅仅只是下载一个文件或一个网页,这不是什么大碍,但如果这是一个游戏数据包而且每秒至少有十次,那么你真的是遇到麻烦了,尤其是因为它停止了其他一切事情。实际上就是这个问题所以几乎没有游戏选择使用TCP作为它们主要的Internet协议,除非它不是一个实时动作游戏。大多数游戏使用 UDP--他们不能保证有序或可靠送达,但它确实很快或者结果是至少通常比TCP/IP更快。现在我们了解这些了,接下来呢?


客户端预测
  因为 UDP 明显的是快速响应游戏的方式,我们将必须自己处理数据包的丢失和乱序。边而且这是技巧所在。不用说出太多的代码秘密,我就能说有方法。作为开始,有客户端预言,一个被谈论得相当多的词语。当你作为一个客户端连接到一个大的服务器,但是不能连贯地看见来自服务器的更新,客户端预言开始起作用了。正在你的电脑上运行的游戏部分看着你正给它的输入,并在缺乏来自服务器的任何弃绝信息的情况下,对它认为将继续进行的事情作出最好的猜测。它将会显示被猜测的数据,然后当它得到来自服务器的世界的最新状态时,改正它自己,如果需要。你可能会对这个方法的效力感到惊讶。大体而言,大部分时间数据包不容易丢失大多数时候是一秒的几十分之一,这种情况下游戏没有太多的时间偏离服务器实际上认为正在发生的事情。偏离确实会随着时间变的比较大,大多数游戏里面有一个超时功能,当出现很长时间没有来自服务器的联络时就停止游戏。

  你正在创造的游戏类型在这里有关系 -- 第一人称射击游戏不需要这样有效的客户端预言,因为它多数情况下仅仅处理我在哪儿,我是否要射击?。在第三人称游戏中,你必须更加精确,因此你能够正确地预测你的角色正在播放的动画,并且动作流畅。在这种情形中流畅的动画是完全必要的。Heretic II在这方面有很大的问题,并且是当它开始网络编码时Raven一直不得不处理的最困难的事情之一。

  当然如果你有一个很不错的网络连接,比如宽带连接,那么这个问题就远没有那么重要。对比较大的数据包有一个更宽的管道,对你的网络连通时间更快速。事实上,宽带对于游戏的主要优点不比较胖的管道多,但大大减少了延迟,特别是你到ISP的第一跳上。对于56K 调制解调器,第一跳典型的延迟是100ms,这已经严重地增加了你到网络上任意一台游戏服务器的潜在连通时间。对于宽带连接比如像DSL,第一跳的延迟时间多半是20ms。使用Windows中一个叫做TraceRouteTRACERT.EXE)的命令行程序并指定一个目标IP地址或者域名,你能够找出你的第一跳的连通时间。仔细观察第一跳,因为这几乎总是你到你的ISP的网络连通时间。并且观察你在你的ISP的网络内部用了多少跳直到你看见在一个给定跳上列出的一个不同的域名。

  请注意,宽带并不总是能解决延迟问题。你仍然受最慢的路由器/服务器和数据包从服务器穿越网络到达你的跳数(反之亦然)的支配。有一个宽带连接确实容易缓和这些,但不可能它们最后就消失了。当然,如果你打算要运行某种服务器,你将会需要一个具有足够快速的向上游的数据速率的带宽,因为仅仅一个调制解调器不能够处理一个服务器产生的负荷。

  值得一提的是,如果你想要在PS2或者Xbox上面玩网络游戏,你将需要一个宽带连接,因为它们两者都不支持调制解调器。


包大小,智能数据传输,和反作弊
  别的必须被处理的事情是数据包的大小。如果你在一个游戏里面64个人都在跑来跑去相互攻击,从一台机器发送到另外一台机器的数据包能变得相当大,达到了一些调制解调器没有带宽处理这些数据的程度。这正在变得特别和那些有着很大的地表系统的游戏有关。这里增加的问题是,因为你有这个很好的地表系统,你能够看得很远,因此能够看见许多其他游戏玩家,使得你为了精确渲染所需要的来自服务器的数据数量以很快的速率增长。我们能做什么呢?

  好吧,首先必要的是只发送绝对必须的东西给任何给定的客户端,因此他仅仅得到从他的角度观察游戏所需要的东西。发送在他视野以外的人们的数据没有一点意义他将看不见这些。同时,你最好确保只发送那些每幀之间实际上发生改变的数据。如果一个家伙仍然在播放相同的动画,重新发送数据没有意义。当然,如果数据包丢失时这确实带来一些问题,但这就是为什么好的网络程序员被支付很多金钱,来处理类似这样的东西。

  还有一些其他的事情也要处理。最近已经有大量的令人苦恼的连线作弊正在发生。这是某些人修改游戏以给他们不正当利益的地方。尽管严格意义上这不是网络的一部分,但它确实发生了。有时人们会创作一些模块,允许他们立即瞄准进入视野的任何人,或者简单地允许他们看穿墙壁,或者让其他游戏玩家看不见他们自己。大部份时间这些事情可以在网络层内部或者在服务器上被处理。任何有100%命中率的人被简单地踢出游戏,因为在人力所及的范围内那是不可能的。

  游戏开发者必须尽一切可能制止作弊行为,但很不幸,人做的东西可以被人突破。所有你能做的就是让作弊变得困难,当确实发生时去尝试发现它。

  好吧,现在就到这里了。在第8部分中,我们将会看看游戏脚本系统的趣味世界,根据游戏过程中出现的事件来渲染或使能预先定义的场景和行为,协助故事叙述。



梦在天涯 2007-12-04 13:24 发表评论

posted @ 2009-04-10 10:44 不会飞的鸟 阅读(135) | 评论 (0)编辑 收藏

仅列出标题
共9页: 1 2 3 4 5 6 7 8 9