游戏邦在:
杂志专栏:
gamerboom.com订阅到鲜果订阅到抓虾google reader订阅到有道订阅到QQ邮箱订阅到帮看

分享《愤怒的小鸟》视差图层制作方法(1)

发布时间:2012-01-17 15:02:39 Tags:,,

作者:Paul Firth

现在网络上有许多关于游戏开发技巧和诀窍的各种教程,但是我们却甚少看到有哪些教程涵括了从最初的游戏开发到完成游戏这一整个完整的过程。

我希望通过这些文章让人们明白创造一款可行的游戏需要具备那些要素,以及如何才能将其组合在一起开发出优秀的游戏。

本文以读者均以掌握基本游戏开发技巧为前提,侧重于说明图像和编程两个内容。

angry birds(from wildbunny)

angry birds(from wildbunny)

我将以Rovio旗下大受欢迎的《愤怒的小鸟》(据称开发成本为14万美元)为例。因为只有我一个人完成这整个演示过程,所以在此我只会摘取其中一些核心要点跟各位分享(本教程纯属教育目的,不支持商业用途)。

分析

让我们先分析一款游戏基础版本所需要的内容:

图像

*背景,中景和前景

*小鸟角色

*小猪角色

*弹弓

*刚体——材料:木头,石头,矩形玻璃,正方块和三角形

代码

摄像镜头

*移镜头

*视差

*变焦

*目标追踪

碰撞检测

*可能需要高级的碰撞检测法

*各种静态和动态形状——矩形,三角形,圆形

*物体碰撞回调系统

物理元素

*非常稳定的物理引擎

*综合的目标睡眠系统

编辑器

*用于创建关卡布局和图像的编辑器,我使用的是Flash CS4。

开始

首先要做的是创建游戏世界,形成外表好看的环境并将并明确游戏项目的外观。

我将先创造小鸟发射时所处的矮墙;在物理系统中,这是一个静态物,并且为了简化过程,我打算以一些较原始的结构去创建这个矮墙,所以我选择了正方形和三角形进行构造。

矮墙(from wildbunny)

矮墙(from wildbunny)

正如你所看到的,矮墙上将会覆盖着重复的泥土纹理,所以我们需要做的第一件事便是创造这种纹理。这很容易:

变化过程(from wildbunny)

变化过程(from wildbunny)

第一部分很简单,我们只需要挑选两个接近于泥土的颜色,在每个颜色中涂上一半的纹理图案,而此时两个颜色中间便会出现一条自然的重叠线。

然后通过压缩一半的宽度和高度以偏置图像(我使用的是photoshop的位移滤镜),而让图像位于正中央。你便可以像第一张图像那样塑造你的中间边缘了。如此你便能够创造出非常好看的无限重复纹理。

为了让这种纹理适用于Flash中的任何抽象物体中,你必须导入图像,点击右键并选择“Break apart”。然后使用吸管工具选择需要填色的纹理,并使用油漆桶为任何形状的矮墙上色。

重复样本(from wildbunny)

重复样本(from wildbunny)

因为我们都是使用最原始的形状去构造碰撞物体,所以我们需要判断需要使用哪些基本的形状。以下是我所设想的矮墙形状:

矮墙形状(from wildbunny)

矮墙形状(from wildbunny)

我们可以翻转这一形状,复制并铺设成不同形状的矮墙,如下图:

组合形状(from wildbunny)

组合形状(from wildbunny)

所以,当我们真正创造出矮墙的可视部分内容时,我们就必须牢牢遵循这一形状——只使用最原始的结构并通过不同配置组合成的形状。

这个步骤对于后来我们在整合物理系统与图像时非常有帮助。

前景

前景中包含了另外一种形状的铺设,还有土地。这是利用Flash将一个矩形和许多椭圆形反复构造并安置而成。在形状铺设过程中必须确保边缘的缝隙也能够被填满,所以我们便使用了“栅格捕捉”方法。

前景(from wildbunny)

前景(from wildbunny)

中景

在这里也出现了不一样的铺设形状,即连绵起伏的群山以及遥远的植物;都是一些普通的事物,我们只要使用不同形状将其创造而成即可。同时还需要确保你在工具上所创造的图像必须比屏幕上宽——像我所制作图像就比屏幕宽了2倍。

中景(from wildbunny)

中景(from wildbunny)

背景

在这里你仍需要铺设不同形状——而在这个阶段你可以依照屏幕大小进行创造。

背景(from wildbunny)

背景(from wildbunny)

输出

你所创造的所有图层将被输出到Flash IDE的动作脚本中;我在源文件上都标注了名称,以便我能够从代码中清晰地看出哪些是来自于Falsh的内容,哪些是我自己所创造的。

所有内容都应该以.sws格式输出,你可以通过“File->Publish Settings->Flash->Export SWC”这一过程进行操作。

然后就可以使用你所选择的flash代码编辑器将这些SWC文件输入你的项目中;因为我非常依赖Visual Studio,所以我选择使用Amythyst。

整合

当然了,关于所有分离图层的理念便是,代码将会以一种分离的形式创造这些图层的动态表象,从而达到摄像镜头捕捉世界画面的视差效果。

规格

我选择的屏幕大小是640×360(宽屏),而游戏世界的规格是2560×720 (足足有4个屏幕宽两个屏幕高)。而我将关注于0,0角度的世界。

摄像镜头

对于任何游戏来说,摄像镜头都是不可或缺的重要角色,那些要求大量移动镜头和变焦场景的游戏更是如此。

至少要保证摄像镜头能够转换世界空间以及屏幕空间之间的坐标轴。

因为同时有3个视差图层在运转,所以摄像镜头应该能够在其中一个图层转移时快速进行定位,其构造参数如下:

public function Camera(background:MovieClip, midground:MovieClip, foreground:MovieClip, bird:Bird)
{
...
}

而在这个阶段小鸟的参数还只是虚拟的,只是用于表示摄像镜头的定位焦点——小鸟的参数中包含了访问函数,以便摄像镜头能够捕捉到它所在的具体位置。

为了让摄像镜头能够准确捕捉到小鸟于屏幕中的具体方位,我们需要涵括一些数学元素;也就是构成所谓的“屏幕世界”矩阵图,如此称法是因为它能够将世界空间转变成屏幕空间。

public function Update( dt:Number ) : void
{
	m_worldToScreen = new Matrix();

	m_worldToScreen.translate( -m_bird.m_Pos.x, -m_bird.m_Pos.m_y );
	m_worldToScreen.scale( m_scale.m_x, m_scale.m_y );
	m_worldToScreen.translate( Constants.kScreenDimensions.m_x/2, Constants.kScreenDimensions.m_y/2 );

	m_foreground.transform.matrix = m_worldToScreen;

	// for screen->world matrix
	m_screenToWorld = m_worldToScreen.clone();
	m_screenToWorld.invert();
}

上述函数意思是:首先摄像镜头集中于小鸟身上,然后在屏幕中间使用变焦方法,以确保小鸟能够出现在屏幕中间(游戏邦注:但需要注意的是,0,0角度是位于屏幕的左上方而不是中间)。

我们使用了这一矩阵图,并将其用于转变前景几何体,所以事实上我们从摄像镜头看到的是一些集合体的移动,所以一开始我们总是很难理解透彻这些抽象的内容。

如此我们便能够较容易地理解上述摘要中的一个内容,即0,0是世界几何体的目标,并且始于从小鸟的发射位置,而0-m_bird.m_Pos是能够帮助我们达到这一目标的矢量。

然后我便复制了一份相同的矩阵图,以便我能够在将屏幕空间转变为世界空间时尝试相反的转换角度。

为了在背景和中景图层中达到视差效果,我也为每个图层(每个图层拥有属于自己的世界->屏幕矩阵)进行了相同的函数运算,唯一不同的是我用图层“z-depth”划分了“x translation”,即让它们能够以不同速度进行移动。

所有的这些设置都能够合理地运行,但是它们却不能够避免摄像镜头偏离世界的界限。所以我们必须理解摄像镜头与世界之间的关系。

让我们看看游戏世界,摄像镜头以及它们之间的关系:

图 1(from wildbunny)

图 1(from wildbunny)

图1呈现的是整个世界的范围,以及能够用于安置摄像镜头的位置。同时我门也需注意摄像镜头在屏幕上也有大小限制。

在这种配置下,摄像镜头所呈现的画面将会出现部分的外部世界,而这也是我们所不希望看到的。为了纠正这一问题,我们首先需要明确摄像镜头所偏离出的范围是多少。

图 2(from wildbunny)

图 2(from wildbunny)

图2展示出了我们需要纠正的范围(也就是红色箭头所指出的)以及改正后摄像镜头所覆盖的正确范围,即绿色框架内。

public function Update( dt:Number ) : void
{
	//
	// clamp camera to only show map
	//

	var translate:Vector2 = m_bird.m_Pos.m_Neg;

	var screenHalfExtents:Vector2 = new Vector2(Constants.kScreenDimensions.m_x/2, Constants.kScreenDimensions.m_y/2).Div(new Vector2(m_scale.m_x, m_scale.m_y));
	var mapExtents:Vector2 = Constants.kWorldAabb.m_HalfExtents.MulScalar( 2 );

	var topLeft:Vector2 = m_bird.m_Pos.Sub(screenHalfExtents);
	var bottomRight:Vector2 = m_bird.m_Pos.Add(screenHalfExtents);

	var correctLeft:Number = Math.min(topLeft.m_x+Constants.kWorldAabb.m_HalfExtents.m_x, 0);
	var correctTop:Number = Math.min(topLeft.m_y+Constants.kWorldAabb.m_HalfExtents.m_y, 0);

	var correctRight:Number = Math.min(Constants.kWorldAabb.m_HalfExtents.m_x-bottomRight.m_x, 0);
	var correctBottom:Number = Math.min(Constants.kWorldAabb.m_HalfExtents.m_y-bottomRight.m_y, 0);

	translate.m_x += correctLeft - correctRight;
	translate.m_y += correctTop - correctBottom;

        ...
}

上述代码是用于纠正图2所标出的红色箭头区域,并应用了最初摄像镜头转换所需要的纠正方法。

这里有一个需要注意的问题:前景图层是构成游戏的MovieClip的产物——这点很重要,因为如果未明确这一内容,摄像镜头便只是在变换着前景形状而无关世界中的其它内容。尽管摄像镜头能够转换MovieClip内容,但是中景和背景图层却不属于这些内容。相反地,它们应该是舞台效果的产物,从而让它们能够独立发生转变并产生合理的效果。

创造一个铺设带

这是一个相对简单的过程,你只需要通过铺设形状的宽度去划分世界的宽度,从而明确所需重复的数量,确定起始点然后列出正确的MovieClip即可:

private function CreateTileStrip( start:Vector2, type:Class ):MovieClip
{
	var obj:* = new type();
	var mc:MovieClip = MovieClip( obj );
	var tileRoot:MovieClip = new MovieClip( );

	var worldWidth:Number = Constants.kWorldAabb.m_HalfExtents.m_x*2;
	var numTiles:int = (worldWidth/mc.width) + 1;

	for ( var i:int = 0; i<numTiles; i++)
	{
		var x:Number = i*(mc.width-1);

		mc.x = x + start.m_x;
		mc.y = start.m_y-mc.height;

		tileRoot.addChild( mc );

		obj = new type();
		mc = MovieClip( obj );
	}

	return tileRoot;
}

你可以用以下方法调用它:

m_backgroundLayer = CreateTileStrip( bottomLeft, BackgroundTileFla );
m_midgroundLayer = CreateTileStrip( bottomLeft, MidgroundTileFla );
m_foregroundLayer = CreateTileStrip( bottomLeft, ForegroundTileFla );

我发现在动作脚本中传输类竟然比在c#或者c++简单多了,并且它也能够帮助我们更轻松地铺设任何从Flash IDE输出的形状。

演示内容

以下便是我们目前努力所创造出的演示图:

演示样本(from wildbunny)

演示样本(from wildbunny)

它所呈现的是一个连续循环的变焦和移景画面,三个带有视差效果的图层,并且摄像镜头牢牢锁定于游戏世界中。

游戏邦注:原文发表于2011年5月12日,所涉事件和数据均以当时为准。(本文为游戏邦/gamerboom.com编译,拒绝任何不保留版权的转载,如需转载请联系:游戏邦

How to make Angry Birds – part 1

by Paul Firth

This time I’m going to try something new. There are many tutorials for various techniques and tricks on the web, but what you don’t often see is one that takes you through the development of a game, step by step from start to finish.

This is what I’m going to attempt here. I hope that it will provide some missing insight into the components that actually make up a working game and how to go about developing them.

I’m going to assume the reader is familiar with the basics of game development and I’m going to concentrate on the art and programming. At the end of this tutorial you will be able create a demo similar to this:

Angry Birds

So, the game I’m going to be making is to be based on the extremely popular Angry Birds by Rovio, a AAA title which cost some $140k USD to make.

Angry Birds

Obviously, since its just me making this I will have to take a few short-cuts and will be concentrating on the core part of the game.

Cloning

Before I start I should mention that I do not condone the cloning of games in any way; what I’m doing in these tutorials is purely for educational purposes and I have no plans to release the game at the end of this.

Analysis

Ok, lets have a look at the requirements for a bare-bones version of the game:

Graphics

Background, mid-ground and foreground layers

Bird characters,

Pig characters,

Slingshot,

Rigid body pieces – materials: wood, stone, glass in rectangle, square and triangle shapes

Code

Camera

Panning

Parallax

Zoom

Object tracking

Collision detection

Possible requirement for advanced broad-phase

Various static and dynamic shapes – rectangles, triangles,
circles

Object colliding call-back system

Physics

Very stable physics engine

Integrated object sleeping system

Editor

Some kind of editor to allow layout of levels and creation of graphics. I’m using Flash CS4 for all this.

The Beginning

The first thing I’m going to tackle from that list is to get the world set up so that there is a nice looking environment and to root the project in something solid looking.

I started with the podium that the birds gets launched from; its going to be a static object in the physics system and to keep things simple I’m going to compose all objects from primitive parts, so collision wise at least the podium will be made from some squares and triangles.

Podium

As you can see, it’s covered with a repeating earth texture, so the first thing is to make this. Its actually quite easy to do:

Process

The first part is easy, just pick two vaguely earthy colours and paint one half of the texture in each colour, with a nice rough overlap in the midddle.

Then, offset the image by half the width and height (I used photoshop’s offset filter) to get the image in the middle. Then you can rough up the middle edge as you did in the first image. Once you’re done you’ll have a nice infinitely repeatable texture.

In order to actually apply this texture to an arbitrary object in Flash, you import the image onto the stage, then right click and and choose ‘Break apart’. Then you can use the eye-dropper tool to pick that texture as a fill texture which you can then apply to any shape with the paint bucket.

Infinite repeats

Because we’re making all collision objects from primitive shapes, we need to decide what basic shapes we’re going to be using. I chose these for the podium:

Podium shapes

These can be rotated, duplicated and placed to form the podium shape:

Podium composed

So, when actually creating the shape for the visible part of the podium, its important to follow the exact same shape that you could compose only using the primitive collision shapes in various different configurations.

This will start to become very useful later when we try to integrate the physics system with the graphics.

Foreground

The foreground consists of another tiling shape; the soil. This was made from a rectangle, and lots of ovals in Flash, just repeated and placed. Its important to make sure it tiles so pay attention at the edges and use grid snap.

Foreground

Mid-ground

Yet another tiling shape, this time rolling hills with a few far off plants; nothing out of the ordinary here, just make sure it tiles. You’ll want to make this one wider than the screen – I made mine roughly twice as wide:

Mid-ground

Background

Yes, you guessed it, tiling shapes again – this one is roughly one screen in size:

Background

Exporting

All these layers have been set to export for action-script in the Flash IDE; I’ve appended every name with Fla so that I can tell in code which classes are from Flash and which are my own.

Everything should now be exported as a .swc which you can enable in File->Publish Settings->Flash->Export SWC.

Then you can import this SWC into your project in your favourite flash code compiler; I’m using Amythyst because I cannot live without Visual Studio.

Putting it together

Of course, the idea with all these separate layers is that the code will place and animate them separately to give a parallax effect as the camera pans/zooms around the world.

Dimensions

I picked a screen size of 640×360 (wide-screen), and a world size of 2560×720 (four screens wide by two screens high). And I’ve centred the world at 0,0.

The Camera

Having a good camera class is fundamental to any game, particularly this one which requires lots of smooth pans and zooms.

At the very least it should provide functions for converting coordinates between world space and screen space and visa versa.

Because there are three layers of parallax at work, the camera needs to be able to position each one as it pans around the scene, so its constructor takes them as parameters:

The bird parameter is just a dummy at the moment which represents the focal point for the camera – it contains accessors for position so the camera can know where in the world it is.

In order to get the camera to centre on the bird no matter where the bird is on screen we need to do a little bit of maths; forming what is known as the world to screen matrix, so called because it transforms points in world space into screen space.

What’s going on in the above function is: first the camera is centred on the bird, then any zoom is applied and finally we add on the centre of the screen to make sure the bird is in the centre (remember, 0,0 is the top left of the screen, not the centre).

We then take this matrix and apply it as the transform for the foreground geometry, so in actual fact the geometry moves around the camera, not the other way around – which is a bit difficult to get your head around at first.

The first translate in the above snippet is easier to understand with this in mind – 0,0 is the destination for the world geometry, coming from the position of the bird and 0-m_bird.m_Pos is the vector which achieves that.

Then I take a copy of this matrix and invert it so that I can do the opposite transform whenever I need to convert a point in screen-space to world-space.

To get the parallax effect on the back and mid-ground layers, I do a similar piece of maths for each layer (each layer getting its own world->screen matrix), the only change is that I divide the x translation by the layer’s z-depth, which causes them to move at different speeds.

All this will work fine, but it won’t prevent the camera from leaving the bounds of the world. In order to do that we need to understand the camera’s relationship with the world.

So, lets take a look at the world, the camera and their relationship:

Figure 1

Figure 1 shows the entire extents of the world and also one possible location for the camera. Note that the camera has the dimensions of the screen.

In this configuration, the camera is actually showing a view which is partially outside the world, which should not be allowed to happen. In order to fix this problem we need to know exactly how far outside the camera is.

Figure 2

Figure 2 shows the measurements we need to correct this problem (the red arrows) and also shows in green, the camera’s position after it has been corrected.

The above is the code which calculates the red arrowed regions shown in Figure 2, and applies any corrections needed to the initial translation of the camera.

There is one caveat to watch out for: the foreground layer is a child of the main MovieClip which makes up the game – this is essential because otherwise the camera would only be translating the foreground shape and not everything in the world, which would be all bad. However, because the camera is translating the main MovieClip, the mid and background layers cannot be children of it. Instead, they must be children of the stage; this allows them to be translated independently to give the correct effect.

Create a tile strip

This is relatively simple, you just divide the width of the world by the width of a tile to get the number of repetitions, set the starting point and then just instance the correct MovieClip:

And you call it like this:

I was actually pleasantly surprised how easy it was to pass a class as a type in actionscript, much easier than in c# or c++ and it really makes it simple to tile any shape that you export from the Flash IDE.

The demo

Ok, so here is the demo so far:

It demonstrates a continuous loop of zooming and panning, three layers of parallax and will not allow the camera to move outside of the world.

The source

As ever, please if you like this article buy the source code (and in this case assets as well) so that I can produce more of this series; your contribution makes a real difference!

It will include all the code which produced the demo above and in addition, all the artwork as well, which you will need Flash CS4 to edit. You are free to use this however you like, even in commercial applications – the only thing I ask is that you don’t give it wholesale to anyone else.

After purchasing, you will be redirected to a page where you can download the source immediately.(source:wildbunny


上一篇:

下一篇: