未显示需要 JavaScript 的文档选项





Sam Lantinga, 首席程序员, Loki Entertainment Software

1999 年 9 月 01 日

Sam Lantinga 是 Simple DirectMedia Layer (SDL) 库的作者和 Loki Entertainment 的首席开发人员,他将向您介绍一种将游戏移植到 Linux上的优秀工具。SDL 是一个跨平台代码移植的理想工具,它支持许多平台,如Linux、Solaris、IRIX、FreeBSD 和 MacOS,这对于那些认为可以在 Linux上开发商业软件的 Linux 开发者来说是一大进步。他向社区的前辈之一讨教SDL 如何使 Linux 用户享受任何平台上最好的游戏,SDL如何帮助开发者跟上下一代计算机游戏迷的要求。
Sam Lantinga 是 Simple DirectMedia Layer (SDL) 库的作者和 Loki Entertainment 的首席开发人员,他将向您介绍一种将游戏移植到 Linux 上的优秀工具。SDL 是一个跨平台代码移植的理想工具,它支持许多平台,如 Linux、Solaris、IRIX、FreeBSD 和 MacOS,这对于那些认为可以在 Linux 上开发商业软件的 Linux 开发者来说是一大进步。他向社区的前辈之一讨教 SDL 如何使 Linux 用户享受任何平台上最好的游戏,SDL 如何帮助开发者跟上下一代计算机游戏迷的要求。

自从 Linus 首先开发出 Linux 时开始,到现在 Linux 成为所有黑客的梦想并且遍及全世界,Linux 开发最重要的元素之一就是 OS 上游戏的质量和可用性。游戏是我们用来娱乐和休闲的。它们可以提高创造力并拓展思路。游戏还可以用来测量操作系统的性能。由于游戏越来越复杂,它们迫使每 个子系统逼近极限。每当我装配一个系统时,首先要做的就是装入一个游戏并试玩,以“测试”每一项的性能。

Linux 上的游戏已经存在了很长时间。从早期的 NetTrek,到受高度赞扬的 DOOM!雷神 (Quake),人们已经可以在 Linux 上玩游戏了。但问题是没有足够的游戏。没有哪家大公司为 Linux 创作能产生轰动效应的游戏。但是,由于该操作系统变得日益流行,这种情况正开始改善。

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

早期 X11 游戏, 争霸 (Craft)的图片。 神话 2 (Myth 2)的图片,Loki 出品
单击以放大图片 单击以放大图片

今天,游戏开发者有了更多的选择。仍然可以使用 X 工具箱或全屏 API,如 SVGAlib 或 fbcon,来编写游戏,但他们现在还有许多游戏库可以使用。Simple DirectMedia Layer 库是 Linux 上最好的低层游戏开发 API 之一。

SDL 是什么?
Simple DirectMedia Layer 库,简称 SDL,是为数不多的商业游戏开发公司使用的免费软件库之一。它提供跨平台的二维帧缓冲区图形和音频服务,它支持 Linux、Win32 和 BeOS。也不同程度地支持其它平台,包括 Solaris、IRIX、FreeBSD 和 MacOS。除了大量的服务,包括线程、独立于字节存储次序的宏和 CD 音频,SDL 还提供了一个简单的 API,它允许您尽可能接近本机硬件。使用 SDL 有三重优点:稳定、简单和灵活。

  • 稳定。如果 SDL 不向 API 提供可靠的支持,那么那些爱好者和商业公司就不能使用它。因为使用了 SDL,就添加了错误修正并增强了性能,也就加强了 API 的强健性。就像内核开发是分步进行的,SDL 的开发也是分步进行的,其中一部分是可靠稳定的 API,其它部分是新功能和构思的沙箱。
  • 简单。SDL 被设计成一个简单的 API,以最少的代码实现您的构思。比如,我最近从 Linux 演示组 Optimum中移植了一些演示程序,我将它们的 X11 代码替换成 SDL 代码(请参见下面的列表)。您可以看到,SDL 代码非常易于编写和理解。

    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);
    }

  • 灵活。 返回到上面的 Optimum 演示代码示例,只要移植到 SDL,并确定一些数据假设,那么根本不必改动代码,演示就可以在 Win32、BeOS 和 Linux 控制台上运行了。灵活性的另一方面体现在尽管代码完全是跨平台的,但不会把您和底层实现隔开。SDL 提供了函数 SDL_GetWMInfo(),该函数可以让您访问底层驱动程序的专用窗口信息。Loki Entertainment Software 广泛使用这一技术为它们的游戏智能窗口管理器交互。

    Optimum 演示版的屏幕快照

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

文明:力量的呼唤 (Civilization: Call To Power)的图片 天旋地转 2 (Descent 2)的图片
单击以放大图片 单击以放大图片