我的两台笔记本电脑被偷,其中就包括我用来开发iPhone程序的Macbook Pro。一想起来就有些气愤,实在不想再掏钱去买一台。好在我还有一台闲置的Dell 640m,于是决定装雪豹和Windows 7双系统。
先看看我的配置:
CPU: Intel 酷睿双核 T2300 1.66GHZ
芯片组: 945GM
内存: DDR2 1.5G
集成显卡: GMA950 (id 27a2)
硬盘: SATA 5400rmp
我的两台笔记本电脑被偷,其中就包括我用来开发iPhone程序的Macbook Pro。一想起来就有些气愤,实在不想再掏钱去买一台。好在我还有一台闲置的Dell 640m,于是决定装雪豹和Windows 7双系统。
先看看我的配置:
CPU: Intel 酷睿双核 T2300 1.66GHZ
芯片组: 945GM
内存: DDR2 1.5G
集成显卡: GMA950 (id 27a2)
硬盘: SATA 5400rmp
我目前正在开发一个跨平台游戏引擎,我准备将整个开发过程通过博客记录下来。欢迎各位朋友提出宝贵意见。
下面是我的设计文档初稿。
Portable Game Engine (PGE )
开发按阶段进行。第一阶段开发Windows下的OpenGL 2D引擎,然后移植到iPhone下。
(注:本文与第一篇文章的观点相反。不过我有些疑惑,我的游戏也持续几周排在二十五名前,可是赚的钱远远少过此数。)
App Store的空前成功毫无疑问地造就了一些诸如iShoot和Trism之类的神话故事(这两个游戏我都喜欢)。同时,最近Apple宣布App Store中有逾25,000个程序而且有超过50,000付费的SDK开发者。看到这组数字,许多人可能想知道是否日益增加的竞争会导致成功的障碍。首先,我们定义一下“成功”的概念。对“名”而言,获得许多人的关注和认可就是成功。但是我在这里聚焦在“利”上。我们会问:是否大部分App Store中的程序都是消遣,或者在其背后都有一个生意实体存在?这些人们关注的问题我们都有答案。根据我们的数据,还有大量的机会通过iPhone程序,特别是游戏获取利润。然而像大部分视频游戏,电影和音乐产业一样,iPhone程序市场是“由命中率驱动的产业“,它意味着整个市场的利润集中在少数几个大赢家手里。也就是说,App Store中存在着更多的中产阶级;或者说许多公司还是能够获取可观的利润。通过iPhone程序与传统电信公司像Verizon 和 Sprint之类提供的游戏和程序的利润发布比较,这显得尤为可信。这是由于Apple在他们的App store中提供了大量的免费试用,更好的页面导航,社区评分以及更高级的搜索技术。对于开发者这意味着如果他们的软件具有很新颖的概念和实在的产品价值 – 即使没有很响亮的商标 – 只有做好宣传,他们就能获得足够的命中率而赚钱。但是,多少钱?一次点击价值多少?三个星期$750,000怎么样?它还不足以达到U2期望的新专辑(No Line on the Horizon – 消失的地平线)的利润,但已经接近了。我们用一个发布了免费和付费版本的解题游戏作为演示。两个版本都在它们各自的分类中排在前25名。
三个星期之内,这个游戏获得了超过两百万下载,产生了大约 $750,000销售额。然而存在一个更大的疑惑,为什么这个程序可以赚怎么多钱,而其他25,000个程序没有?通过研究,答案就在:出色的概念,很好的用户体验,严密的市场分析以及聪明的发布计划。这些因素帮助它成为了畅销软件之一。然后订单就源源不断的到来。我们都知道App Store成长很快,点击率不断增加。要获得成功,开发者需要认真考虑定价以及怎样更有效地进行市场宣传。好消息就是,还是有利可图的,现在正是发布你的游戏的时候了。
原文见:Can Developers Still Make Money in the iPhone App Store?
我在从零开始学习OpenGL ES之四 – 光效 一文中使用了一个普通GLfloat数组。由于它没有使用任何非OpenGL定义的数据结构,所以是最为普通和方便的方式。
但在此我使用在第一部分中定义的Vertex3D, Vector3D和 Color3D数据结构重写了 setupView:方法。并不是这种方法“更好”,但是它是一种不同的方式。当我第一次学习OpenGL时,我发现使用顶点,颜色和三角形的术语比可变长度浮点数组更容易理解。如果你和我一样,那么你会发现这个版本更容易理解。
除了使用自定义数据结构外,我还减少了环境光元素的数量并将光源向右移动了一点。然后使用Vector3DMakeWithStartAndEndPoints()将移动的光源指向二十面体。这样做使得光效更为生动一点。
-(void)setupView:(GLView*)view
{
const GLfloat zNear = 0.01, zFar = 1000.0, fieldOfView = 45.0;
GLfloat size;
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0);
CGRect rect = view.bounds;
glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size /
(rect.size.width / rect.size.height), zNear, zFar);
glViewport(0, 0, rect.size.width, rect.size.height);
glMatrixMode(GL_MODELVIEW);
// Enable lighting
glEnable(GL_LIGHTING);
// Turn the first light on
glEnable(GL_LIGHT0);
// Define the ambient component of the first light
static const Color3D light0Ambient[] = {{0.05, 0.05, 0.05, 1.0}};
glLightfv(GL_LIGHT0, GL_AMBIENT, (const GLfloat *)light0Ambient);
// Define the diffuse component of the first light
static const Color3D light0Diffuse[] = {{0.4, 0.4, 0.4, 1.0}};
glLightfv(GL_LIGHT0, GL_DIFFUSE, (const GLfloat *)light0Diffuse);
// Define the specular component and shininess of the first light
static const Color3D light0Specular[] = {{0.7, 0.7, 0.7, 1.0}};
glLightfv(GL_LIGHT0, GL_SPECULAR, (const GLfloat *)light0Specular);
glLightf(GL_LIGHT0, GL_SHININESS, 0.4);
// Define the position of the first light
// const GLfloat light0Position[] = {10.0, 10.0, 10.0};
static const Vertex3D light0Position[] = {{10.0, 10.0, 10.0}};
glLightfv(GL_LIGHT0, GL_POSITION, (const GLfloat *)light0Position);
// Calculate light vector so it points at the object
static const Vertex3D objectPoint[] = {{0.0, 0.0, -3.0}};
const Vertex3D lightVector = Vector3DMakeWithStartAndEndPoints(light0Position[0], objectPoint[0]);
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, (GLfloat *)&lightVector);
// Define a cutoff angle. This defines a 50° field of vision, since the cutoff
// is number of degrees to each side of an imaginary line drawn from the light's
// position along the vector supplied in GL_SPOT_DIRECTION above
glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 25.0);
glLoadIdentity();
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
你可以随意调整光的属性,增加额外的光源或二十面体,体验一下这些调整会为场景带来什么样的变化。这些东西是很难体验出来的,所以不要指望一晚上就理解了所有东西。
在OpenGL ES中另一种为多边形定义颜色创建材质的方法是将纹理映射到多边形。这是一种很实用的方法,它可以产生很漂亮的外观并节省大量的处理器时间。比如说,你想在游戏中造一个砖墙。你当然可以创建一个具有几千个顶点的复杂物体来定义每块砖以及砖之间的泥灰。
或者你可以创建一个由两个三角形构成的方块(四个顶点),然后将砖的照片映射上去。简单的几何体通过纹理映射的方法比使用材质的复杂几何体的渲染快得多。
在 上一篇文章,我们讨论了光效的设定以及光效的各种属性。我们还讨论了光的三要素:散射光, 环境光 和 高光。如果你还不是完全清楚,那么我们来复习一下,在定义材质时大量的用到这些要素。
作为本文的起点,我们使用了此文中球体绘制 的项目文件。我们不再使用二十面体而是转向球体是因为球体是展示光和材质不同要素之间相互作用的最佳形状。
(注:本文是改写的)
在写第四部分文章时,当我使用了 glLightfv() 和 glLightf()两种版本时,我突然意识到我还从来没有解释过OpenGL的命名规则。这部分应该属于第一部分,随OpenGL数据类型一起介绍。
在OpenGL中,没有使用任何参数并在函数尾不具有数据类型代号的函数只有一个:
GL_ENUM error = glGetError();
另外,只具有一个参数(GL_ENUM)而且不具有函数尾数据类型代号的函数有:
glEnable(GL_COLOR_ARRAY);
大部分OpenGL函数都是适用于不止一种数据类型。这在普通版OpenGL中尤为明显,大部分函数都有至少半打变种,允许你传递诸如 GLshort, GLbyte, GLint, GLfloat, GLDouble,或 GLfixed值或参考。在OpenGL ES中,函数调用的变种和数据类型少得多。然而,OpenGL ES仍然遵循同样的命名规则,所以最好还是要理解这些数据类型代号的含义。
函数名后的第一或者第二个字母代表了数据类型。下面是说明:
所以 glFoof()需要传递一个glFloat,而 glFoos()则需要一个 GLshort。
注意: 在常规 OpenGL中,后缀中可能还包括数字。这些数字代表需要的数据类型的数量。例如,函数glVertex3f()使用三个GLfloat。由于此命名模式大部分用于直接模式,而OpenGL ES并不支持,所以在OpenGL ES中你不会常见到这种命名规则,然而有些函数还是遵循这种规则的,例如, glColor3f()。保持后缀中的数字可以使这两类API最大限度地兼容。
再回头看看第四部分,有时函数提供更通用的功能,如glLightf()。你可以调用此函数设置指定光的不同属性。例如,你可以设定点光源的截止角或者光源在3D空间的位置。点光源截止角需要一个值,但光源位置需要三个值。
对于这类函数,通过在函数尾加上一个 v 来表示。如果你想要传递常规数据类型给OpenGL,那么你不要使用此后缀,而是直接传递数据类型值。但是,如果你想要传递不止一个OpenGL原生数据类型( 即第一部分列表中的数据类型),那么你需要将这些值放入数组中并将数组开始的指针传递给OpenGL。当你像这样传递参考值给 OpenGL 时,你想要在函数后加上v后缀。
所以,回到第四部分的例子,当我们设置点光源的截止角时,我们使用了没有 -v的版本:
glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 45.0);
而当我们传递光源位置时,它需要三个GLfloats值,所以我们使用有v的版本:
const GLfloat light0Position[] = {0.0, 10.0, 10.0, 0.0};
glLightfv(GL_LIGHT0, GL_POSITION, light0Position);
想要你的iPhone/iPod Touch振动吗?实在很简单,只需一行代码:
1 | AudioServicesPlaySystemSound (kSystemSoundID_Vibrate); |
当然要记得在你的项目中加入AudioToolbox framework。不过振动的模式和长度无法控制。目前我在研究看是否有其他方法。
继续我们的iPhone OpenGL ES之旅,我们将讨论光效。目前,我们没有加入任何光效。幸运的是,OpenGL在没有设置光效的情况下仍然可以看见东西。 它只是提供一种十分单调的整体光让我们看到物体。但是如果不定义光效,物体看上去都很单调,就像你在第二部分程序中看到的那样。