• 跨平台代码分享之七 – 一个简单的音效引擎

    游戏都需要一个声音引擎,一般来说,有三种类型:

    1. 播放音效。音效一般是比较简短的声音文件。
    2. 播放背景音乐。
    3. 乐器,音效模拟。

     

    本文介绍一个简单跨平台的音效引擎,主要针对第一种类型。实际上,也可以适用于第二种类型,但由于背景音乐文件较大,占用内存较大,所以本文介绍的方法并不是最为有效率的(特别是对于像iPhone之类内存较为紧张的平台,但仍然可用)。可以在本引擎的基础上加以改进,比如采用“流”(streaming)技术。而第三种类型,可能涉及到DSP(数字处理技术),比较复杂,另外在不同平台有一些不同的技术,如Windows下的VST/VSTi,Mac OSX下的AU等,不在本文的讨论范围。

     

    本文介绍的音效引擎是基于OpenAL的,并且是我在许多项目中使用的引擎的简化部分(去除错误处理及乐器模拟部分)。

     

    有关OpenAL的使用,网上有许多教程,比如:OpenAL编程教程等。iPhone上的OpenAL使用,本网站也有几篇文章。所以这里不再赘述。

     

    本引擎的目的,是简化声音播放接口。最简单的情况下可以仅仅使用两条语句,就播放一段音效。而繁复的OpenAL初始化功能等都由引擎自动完成。下例展示了怎样播放一段音效的最为典型的调用:

     

    1
    2
    3
    audio::CSound sound("sound.wav");

    sound.play();

     

    本引擎由三个类和一个公共函数构成。audio::CEngine是引擎的核心部分,它是一个单例类(Singleton),负责直接与OpenAL接口,用户无需与其打交道。audio::CBuffer类对应于OpenAL中的缓存,通过audio::openAudioData函数打开音效文件,创建内部缓存,用户也无需直接使用。audio::CSound是用户直接使用的类,它提供了诸如play(),stop(),setVolume()和setPosition()几个接口函数。如需扩展功能,添加一些其他功能,可以直接加在此类中,并在audio::CEngine中实现。

     

    以上三个类可以直接使用在不同平台中。与平台相关的代码,都在audio::openAudioData()中。它的作用是打开不同的声音文件。对于Windows,我使用了一个开源项目库libsndfile,用来打开诸如wav,caf,aiff,flac,ogg等常用音效文件(注意:mp3并未包括其中。由于使用mp3的软件需要支付一定许可证费用,本人也不喜欢使用)。而对于Mac OSX(iPhone),则使用了core audio。这里大家可能会有一个疑问,既然libsndfile也是跨平台的,为什么在iPhone上不使用它呢?确实,libsndfile支持Windows,Linux,Mac OSX,Sun Solaris等平台,而iPhone并不支持ogg等格式,使用libsndfile不正好填补这个空白?是的,我也是这样想的,但是,有两点原因初始我暂时没有使用它:

    1. 没有现成的iPhone库。由于libsndfile需要使用makefile由GCC编译(我曾试过使用Xcode编译,没有成功),我也不是这方面的专家。但是,libsndfile在Mac OSX下编译没有问题,所以只需稍作修改,在为iPhone进行编译也不是没有可能的事情。
    2. 由于其许可证的限制。libsndfile是基于LGPL的,它要求使用者采用动态连接库,而众所周知,iPhone(官方)是不支持动态链接的。如果使用静态链接,则需要随软件发布源代码或目标代码。其实,这也不是无法克服的,LGPL只要求发布与libsnd相关的目标代码,所以对你的软件并无影响。对吗?

     

    本文给出了Windows下和iPhone下的应用。在Windows下使用需要下载OpenAL SDKlibsndfile。为方便编译好的libsndfile随示例程序给出给出。(你需要修改一下VS 项目文件中的有关OpenAL的头文件和库文件路径,另外,在OpenAL include目录中创建一个OpenAL目录,并将.h复制到其下,这样我就不需要修改#include语句了。)

     

    示例程序audiolib下载。(项目在test目录下)

     
  • 从零开始学习OpenGL ES之三 – 透视

    现在你已经知道OpenGL是怎样绘图的了,让我们回头谈谈一个很重要的概念:OpenGL视口(viewport)。 许多人对3D编程还很陌生,那些使用过像Maya, Blender, 或 Lightwave之类3D图形程序的人都试图在OpenGL虚拟世界中找到“摄像机”。但OpenGL并不存在这样的东西。它所有的是在3D空间中定义可见的物体。虚拟世界是没有边界的,但计算机不可能处理无限的空间,所以OpenGL需要我们定义一个可以被观察者看到的空间。

    如果我们从大部分3D程序具有的摄像机对象的角度出发来考虑,视口端点的中心就是摄像机。也就是观察者站的位置。它是一个观察虚拟世界的虚拟窗口。观察者可见的空间有一定限制。她看不见她身后的东西。她也看不见视角之外的东西。而且她还不能看见太远的东西。可以认为视口是通过“观察者可见”参数所确定的形状。很简单,对吗?

    不幸的是,并非如此。要解释原因,我们首先需要讨论的是在OpenGL ES中具有的两种不同的视口类型:正交和透视。

    读文章 »

     
  • OpenAL系列之二 – 为什么要避免使用alBuferDataStatic

    我收到Ken一封Email,这位仁兄浏览了我博客的中有关OpenAL的文章,遇到一些问题请求我的帮助。他在加载缓存后重复使用时遇到了一些OpenAL神秘的错误。

     

    所以我阅读了他寄给我的代码片段,我注意到:

     

    1
    2
    3
    // use the static buffer data API

    alBufferDataStaticProc(buffer, format, data, size, freq);

     

    啊哈! 这可能就是你问题的根源!除非你真正需要,alBufferDataStatic应该完全被避免。你怎样才知道你需要使用它?有一条金桂玉律:如果你不知道这个问题的答案,那么你就不需要它。

     

    读文章 »

     
  • 从零开始学习OpenGL ES之二 – 简单绘图概述

    还有许多理论知识需要讨论,但与其花许多时间在复杂的数学公式或难以理解的概念上,还不如让我们开始熟悉OpenGL ES的基本绘图功能。

    读文章 »

     
  • 从零开始学习OpenGL ES之一 – 基本概念

    (注:由于Simon Maurice的iPhone OpenGL ES的网站已经不存在了,所以我将翻译另一系列的OpenGL ES教程。我是通过Proxy访问的此教程,所以无法给出原文链接。另外所有的图片和源代码我放在本地,以防不测。原文的名称是“OpenGL ES from the Ground Up”。)

    我曾写过一些文章介绍iPhone OpenGL ES编程,但大部分针对的是已经至少懂得一些3D编程知识的人。

    作为起点,请下载我的OpenGL Xcode项目模板,而不要使用Apple提供的模板。你可以解压到下面位置安装此模板:

    /Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Project Templates/Application/

    已经有大量有关OpenGL的好教程和书籍。但是,却没有多少是关于OpenGL ES,而且没有(至少在我撰写此文时)是专门针对学习iPhone上3D编程的。因为大部分有关学习OpenGL的材料是从所谓“直接模式(direct mode)”开始的,而OpenGL ES并不支持此模式,对于没有3D背景知识的iPhone开发者而言,使用现有的书籍和教程是十分困难的。为满足一些开发者的要求,我决定撰写一个针对3D初学者的博文系列。这是此系列的第一篇文章。

    读文章 »

     
  • 存储OpenGL ES内容到相片簿中

    (注:本文改编自iPhone – saving OpenGL ES content to the Photo Album,懒得全文翻译了,只挑一下重点加上自己的语言描述一下)

     

    有许多程序都有将图片存入相片簿的功能。如果你使用了OpenGL ES,那么本文的方法适合你使用。简单步骤:

    1. 使用glReadPixels读取GL数据到字节数组中
    2. 使用UIImageWriteToSavedPhotosAlbum将UIImage存储到相片簿中

     

    但怎样将字节数组转换成UIImage比较困难。由于 [UIImage imageFromData:data]需要UIImage支持的文件格式,所以并不适用,因为从glReadPixels读取的字节数组中的数据是原始像素数据。可以使用CGImageCreate来创建一个CGImageRef,然后用[UIImage imageWithCGImage:imageRef]转换成UIImage。CGImageCreate要求CGDataProviderRef,可以通过CGDataProviderCreateWithData使用glReadPixels的数据来创建。整个过程的流程如下:

     

    glReadPixels -> CGDataProviderCreateWithData -> CGImageCreate -> [UIImage imageWithCGImage:] -> UIImageWriteToSavedPhotosAlbum

     

    但是还存在一个问题,OpenGL 使用标准笛卡尔坐标,即+Y朝上, -Y朝下。所以用glReadPixels获得的数组是上下颠倒的。可以使用位旋转技术进行修正。下面是代码(用在具有CAEAGLLayer的UIView中) :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
          -(UIImage *) glToUIImage {
       
              NSInteger myDataLength = 320 * 480 * 4;
               
              // allocate array and read pixels into it.
              GLubyte *buffer = (GLubyte *) malloc(myDataLength);
              glReadPixels(0, 0, 320, 480, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
       
             // gl renders "upside down" so swap top to bottom into new array.
             // there's gotta be a better way, but this works.
             GLubyte *buffer2 = (GLubyte *) malloc(myDataLength);
     
             for(int y = 0; y <480; y++)
             {
                   for(int x = 0; x <320 * 4; x++)
                   {
                        buffer2[(479 - y) * 320 * 4 + x] = buffer[y * 4 * 320 + x];
                   }
             }
             
            // make data provider with data.
            CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, myDataLength, NULL);
             
            // prep the ingredients
            int bitsPerComponent = 8;
            int bitsPerPixel = 32;
            int bytesPerRow = 4 * 320;
     
            CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
            CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
            CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
             
            // make the cgimage
            CGImageRef imageRef = CGImageCreate(320, 480, bitsPerComponent, bitsPerPixel,
                 bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
           
            // then make the uiimage from that
            UIImage *myImage = [UIImage imageWithCGImage:imageRef];
            return myImage;
       }
     
       -(void)captureToPhotoAlbum {
                UIImage *image = [self glToUIImage];
                UIImageWriteToSavedPhotosAlbum(image, self, nil, nil);
        }
     
  • 不使用Objective-C进行iPhone编程?

    (注:为方便,我为本文提到的库和工具添加了链接。)

     

    是的,有可能。尽管Objective-C我最喜欢的编程语言之一,在任何情况下都值得提到这点。但随着官方iPhone SDK宣布两年以来,iPhone开发发展十分迅速,现在有许多的选择。本文提供一些我在网上找到的选项,我可以肯定有更多的替换方法。

     

    这里是我的发现:

     

    • * 首先,记住你总是可以编写web程序。(值得提醒你可以因此完全避免App Store,当然这完全取决于你)。这为你使用服务器端技术+Javascript提供了可能,而且有各种库和工具帮助你创建一个“杀手级”web程序:Joe Hewitt优秀的iUI (是的,他就是Three20项目后的同一个家伙),Tank Engine Rails插件和Rails iUI插件 (不幸的是Tank Engine工作得不是很好), iPhoney Eclipse(译者注:可能是指使用Eclipse开发iPhone程序吧,所以我的链接是IBM的一个教程)等其他选择。
    • * 如果你喜欢C++,你可以在官方SDK (是的,你可以仅使用 C 或 C++来编写iPhone程序),或其它选项如nui, POCOBoosthaXe中选择。而且很明显,很快你就可能使用Symbian C++ toolkit(谁知道,甚至有一天我们可能在iPhone上使用Juce工具)。
    • * 如果你是个Flash和ActionScript的开发者,很可能你已经知道通过使用Adobe Flash CS5,你也可以编写iPhone程序。
    • * 对于 C# 和 .NET 开发者,有MonoTouch,最近也频频曝光。
    • * 如果你希望使用Lua,你可以使用Wax framework
    • * 如果你的设想是“编写一次,运行在任何地方”,“任何地方”是指Android, iPhone, BlackBerry和其他移动平台的话,你可以试一下rhomobileCoronaPhoneGap 或者 QuickConnect。 由于XMLVM项目包括交叉编译的工具链,可以用于Ruby, .NET, Java并立即产生许多平台的代码,你也可能对它有兴趣。
    • * 最后,如果你进行游戏开发,可以用于创建iPhone游戏的库的数量之多,令人惊奇,它们支持各种编程语言:SDL, Unity, SIO2, Torque2D,cocos2d 以及 Game Haxe
    • *当然,还有使用Objective-C和Cocoa Touch的官方SDK。(注:这显然不符合本文主题)。

       

      你怎么想?我漏掉了一些库或编程语言?请在评论区留下库的名称和URL。

       

      2009-11-05更新: 刚发现Swebapps服务,它允许不用代码建立iPhone(非常基本的)程序。

       

      2009-11-24更新: Chillingo的Crystal SDK

       

      2009-11-25更新: 另一个C++ 的跨平台库:Airplay SDK

       

      2009-11-28更新: 三个C++跨平台库,有限地支持创建iPhone应用程序:wxWidgetsJuceQt

       

      2009-11-29更新: 我朋友Bertrand Dufresne刚告诉我两个很棒的web app frameworks: jQTouch (jQuery插件,据Joe Hewitt自己说,它已经在很多方面超过iUI)和WebApp.net。在你的iPhone上试一下jQTouch 和 WebApp.net的展示程序,你一定会印象深刻(特别是jQTouch)。

       

      2009-11-30更新: Max Penet指出下列选项可以允许开发者创建iPhone程序:Yahoo! Blueprint(一个用于设计iPhone程序方案(包括调试支持))和Appcelerator

       

      2009-12-8更新: NimbleKit iWebKit.

       

      2009-12-15更新: Bork3D C++ 游戏引擎。

     
  • Poco C++ for iPhone之一 – 编译

    Poco C++是一个强大的跨平台C++库,你可以在iPhone程序中使用它,如果:

    1. 你打算使你的程序跨平台。
    2. 你喜欢使用C++或希望使用Poco C++中的功能如:XML,Zip等。

     

    目前,最新Poco C++版本为1.3.6P1,根据文档,它可以编译为iPhone静态库。但是我试用了一下,无法使用在iPhone Xcode项目中链接,不知何故。所以我自己创建了下列Xcode项目:

    • Poco Foundation
    • Poco Util
    • Poco Net
    • Poco XML
    • Poco Zip

     

    另外,由于Crypto,Data,NetSSL_OpenSSL库需要外部库而且目前我不需要使用,所以没有包括。另外我增加了一个目录iPhoneSamples,它目前包括了一个示例程序HelloPoco,演示了怎样在iPhone项目中使用Poco C++库(此演示程序十分简单,它调用Poco中的UUID功能,产生一个UUID,显示与屏幕上)。下面是包括了此展示程序以及各Xcode文件的Poco包的下载:

     

    poco-1.3.6p1

     
  • 抢钱系列之一 – 令人难以置信的App Store炒作

    (译者注:开发iPhone程序到底能不能赚钱,可谓是众说纷纭啊。从今天起,我将翻译一些有关App Store赚钱的文章。我虽然没有本文作者这么悲观,但基本认同其观点。App Store基本上就是一个只有一个大赢家加上少许小赢家的彩票发行商。它一下将软件的价格贬得一钱不值,或准确地说,只值一钱:0.99美元。但是,生活要继续,梦也照样作!)

     

    iphonehype.jpg

     

    有关iPhone App Store的炒作一直持续着。它被称为淘金热,一夜暴富的故事常有耳闻。但是如果你也想置身于这场游戏,这些对你而言意味着什么?让我们看看现实吧。

     

    读文章 »

     
  • 跨平台代码分享之六 – 使用minizip进行解压

    压缩和解压技术应用很广泛,比如游戏程序中的资源管理器可以使用压缩技术将许多文件打包,便于管理,也节约了空间。本文介绍使用minizip进行解压。由于可以使用zip工具比如winzip,betterzip等进行压缩,所以本文并未涉及压缩。

     

    读文章 »