当前位置:在线查询网 > 在线百科全书查询 > 平滑卷轴效果

平滑卷轴效果_在线百科全书查询


请输入要查询的词条内容:

平滑卷轴效果


对于J2ME框架下的手机游戏程序的开发,其地图滚动的重绘有多种算法,由于手机性能的限制和开发周期等其他非技术条件,需要根据情况灵活选择所需的技术。但在及其苛刻条件下,如系统CPU资源不足,地图块尺寸较小等,会造成屏幕闪耀,帧数过低等情况,严重影响到游戏体验。在开发中如此类问题无法绕过以及避免(指通过修改策划方案,以及程序使用的技术框架),则需要考虑使用地图缓冲绘制技术,卡马克卷轴就是一种最经典的地图缓冲绘制技术。可有效的改善在地图绘制中的屏幕闪耀,帧数过低等情况。

平滑卷轴效果

点子是卡马克提出来的。他正在尝试一项编程上的突破:让游戏的世界不再局限于屏幕的边界——所谓的“卷轴效果”。街机是这项技术的样板,早期街机游戏的移动也是局限在一个静态的屏幕内,譬如《乒乓》,用于把球击来击去的球拍只能在屏幕的底部和顶部之间移动,再譬如《吃豆子》里的迷宫,也是局限于屏幕那么大,还有《太空入侵者》,玩家控制的飞船只能在屏幕下方左右移动,外星飞船们则是从屏幕顶部涌现。所有这些游戏都把玩家局限在那一小方天地里,缺乏一种宽广的可延伸的感觉,这种状况一直持续到1980年,在那一年,威廉斯公司推出了一款名叫《防御者》(Defender)的街机游戏,这是第一个流行的卷轴游戏,在这个科幻射击游戏里,玩家不再受屏幕大小的限制,他操纵飞船在行星的表面水平移动,一路上击落敌机营救人质,屏幕上的一幅小地图显示着玩家在整个世界里的当前位置。如果把地图扩展为正常尺寸,这个世界大概有三个半屏幕那么大。与其他街机比起来,《防御者》显得宏大得多,玩家就像进入了一个更为广阔的虚拟空间。它很快变得和《太空入侵者》一样流行,还胜过《吃豆子》成为了业界的年度游戏。无数的卷轴游戏随之出现,到1989年的时候,卷轴技术已经是新游戏的一项必不可少的标准,这其中最成功的莫过于任天堂红白机上的《超级马里奥兄弟3》(Super Mario Brothers 3)。

但在那时,1990年9月,还没有人研究出如何在PC上实现卷轴效果,大家都用一些蹩脚的技巧来让玩家觉得游戏的世界比屏幕要大,譬如当玩家移动到屏幕最右边的时候,游戏会停顿一会,然后右边的场景出现在屏幕上。部分原因是PC的性能还很差,无论是街机还是苹果机,或是任天堂那样的家用机都比PC强不少。而卡马克下定决心要找出一种办法来在PC上实现像《防御者》或《超级马里奥》那样的平滑卷轴效果。

《玩家之刃》的下一个游戏就要朝着这个方向走。当大伙聚在一起讨论的时候,卡马克给他们演示了他最新的成果,他已经可以让屏幕上的内容平滑地往下方移动,和那些成熟的卷轴游戏比起来,这项技术还很粗糙,它就像是一条传送带,图像按照固定的速度和路线落下,玩家还不可能随心所欲地在里面畅游,那就像是拖动演员背后的舞台布景。

罗梅洛这个博览过几乎每一款PC游戏的玩家,没有见过这种效果。这对他们而言是一个成为先行者的好机会。他们给游戏命名为《搜捕》(Slordax),一个简单的飞船射击游戏,就像《太空入侵者》或《小蜜蜂》(Galaga)一样。

(此为一种实现方式的具体介绍)

操作贴片引擎时,大型的地图需要卷轴以便玩家可以看到整个地图,具体说,在绘制地图时尝试去改变坐标,贴片引擎就会产生一个急促的运动。为了提高引擎的视觉质量,需要使用一种称之为平滑卷轴(smooth scrolling)的技术来使运动平滑地进行。

为了实现平滑卷轴,将贴片绘制的地图想象成一个很大的位图。在位图中的每个像素都有它自己的一对坐标,即所谓的地图的精细坐标,代表贴片像素的每个分组被赋予它自己的地图坐标集,如下图所示:

举个例子,如果贴片是16 x 16像素大小,同时地图数组为10 x 10,当完全渲染时,地图将会为160 x 160像素大小(这就意味着地图有一个分辨率为160 x 160 的精细坐标)。

创建一个地图类

为了使游戏保持运行的平滑,首先需要对每一帧所绘制的自由浮动贴片的数量(子画面)进行限制,一个宏定义将出色地完成这个工作,它会通知地图类在每一帧中绘制了多少子画面:

#define MAX_OBJECTS 1024

每个地图类的实例可以存储大量的层次(甚至超过一百万个),将每个层次的贴片数据存储到一个数组_map_info里。因为地图的尺寸大小一旦被创建,将是固定不变的,可以通过计算在_map_info数组里的当前位移,并利用一个指针对每个层次的贴片数据进行读取或写入。

来看看MAP类的定义:

#define MAX_OBJECTS 1024

typedef struct OBJECT_INFO

{

long x_pos, y_pos;

char tile_index;

} *OBJECT_INFO_PTR;

//=========================================================================================

// This class encapsulate 2D map draw.

//=========================================================================================

typedef class MAP

{

public:

MAP();

~MAP();

// function to create and free a map class

BOOL create(long num_layers, long map_column, long map_row);

void free();

// function to set a map''s layer data

BOOL set_map_layer_data(long layer_index, char* layer_data);

// function to clear and add an object to list

void clear_object_list();

BOOL add_object(long x_pos, long y_pos, char tile_index);

char* get_ptr(long layer_index); // get pointer to map array

long get_map_column(); // get column of map

long get_map_row(); // get row of map

// assign TILE object to use for drawing map tiles

BOOL use_tile(TILE_PTR tile);

// Render map using specified top-left map coordinates,

// as well as number of columns and rows to draw, plus layer used to draw objects.

BOOL render(long pos_x, long pos_y,

long num_rows, long num_columns,

long object_layer,

D3DCOLOR color = 0xFFFFFFFF,

float scale_x = 1.0f, float scale_y = 1.0f);

private:

long _map_column; // column of map

long _map_row; // row of map

long _per_layer_size; // size of per map

long _num_layers; // number of layers

char* _map_info; // array for tile informarion

TILE_PTR _tile; // pointer to TILE object

long _num_objects_to_draw; // number of object need to be drawed

OBJECT_INFO _objects_info[MAX_OBJECTS]; // object information array

} *MAP_PTR;

实现:

/*************************************************************************

PURPOSE:

Implement for 2D map.

*************************************************************************/

#include "core_global.h"

#include "tile.h"

#include "map.h"

//----------------------------------------------------------------------------------

// Constructor, zero member data.

//----------------------------------------------------------------------------------

MAP::MAP()

{

memset(this, 0, sizeof(*this));

}

//----------------------------------------------------------------------------------

// Destructor, release allocated resources.

//----------------------------------------------------------------------------------

MAP::~MAP()

{

free();

}

//----------------------------------------------------------------------------------

// Release allocated resources.

//----------------------------------------------------------------------------------

void MAP::free()

{

// free map information array

delete[] _map_info;

_map_info = NULL;

_map_column = _map_row = 0;

_num_layers = 0;

}

//----------------------------------------------------------------------------------

// Create map object.

//----------------------------------------------------------------------------------

BOOL MAP::create(long num_layers, long map_column, long map_row)

{

// free a prior map

free();

// save number of layers, map column and row.

_num_layers = num_layers;

_map_column = map_column;

_map_row = map_row;

_per_layer_size = map_column * map_row;

long total_map_size = num_layers * _per_layer_size;

// allocate map data memory

if((_map_info = new char[total_map_size]) == NULL)

return FALSE;

// clear it out

ZeroMemory(_map_info, total_map_size);

// reset number of objexts to draw

_num_objects_to_draw = 0;

return TRUE;

}

//----------------------------------------------------------------------------------

// Set map data.

//----------------------------------------------------------------------------------

BOOL MAP::set_map_layer_data(long layer_index, char* layer_data)

{

// error checking

if(layer_index >= _num_layers)

return FALSE;

// copy over data

memcpy(&_map_info[layer_index * _per_layer_size], layer_data, _per_layer_size);

return TRUE;

}

//----------------------------------------------------------------------------------

// Clear object list which need to be drawed.

//----------------------------------------------------------------------------------

void MAP::clear_object_list()

{

_num_objects_to_draw = 0;

}

//----------------------------------------------------------------------------------

// Add object to object list.

//----------------------------------------------------------------------------------

BOOL MAP::add_object(long x_pos, long y_pos, char tile_index)

{

if(_num_objects_to_draw < MAX_OBJECTS)

{

_objects_info[_num_objects_to_draw].x_pos = x_pos;

_objects_info[_num_objects_to_draw].y_pos = y_pos;

_objects_info[_num_objects_to_draw].tile_index = tile_index;

_num_objects_to_draw++;

return TRUE;

}

return FALSE;

}

//----------------------------------------------------------------------------------

// Return pointer to specfied layer map data.

//----------------------------------------------------------------------------------

char* MAP::get_ptr(long layer_index)

{

if(layer_index >= _num_layers)

return NULL;

return &_map_info[layer_index * _per_layer_size];

}

//----------------------------------------------------------------------------------

// Return map columns.

//----------------------------------------------------------------------------------

long MAP::get_map_column()

{

return _map_column;

}

//----------------------------------------------------------------------------------

// Return map rows.

//----------------------------------------------------------------------------------

long MAP::get_map_row()

{

return _map_row;

}

//----------------------------------------------------------------------------------

// Set tile to map.

//----------------------------------------------------------------------------------

BOOL MAP::use_tile(TILE_PTR tile)

{

if((_tile = tile) == NULL)

return FALSE;

return TRUE;

}

//----------------------------------------------------------------------------------

// Render map.

//----------------------------------------------------------------------------------

BOOL MAP::render(long pos_x, long pos_y,

long num_rows, long num_columns,

long object_layer,

D3DCOLOR color,

float scale_x, float scale_y)

{

// error checking

if(_map_info == NULL || _tile == NULL)

return FALSE;

long tile_width = _tile->get_tile_width(0);

long tile_height = _tile->get_tile_height(0);

// calculate smooth scrolling variables

long map_x = pos_x / tile_width;

long map_y = pos_y / tile_height;

long off_x = pos_x % tile_width;

long off_y = pos_y % tile_height;

// loop through each layer

for(long layer = 0; layer < _num_layers; layer++)

{

// get a pointer to the map data

char* map_ptr = &_map_info[layer * _per_layer_size];

// loop for each row and column

for(long row = 0; row < num_rows+1; row++)

{

for(long column = 0; column < num_columns+1; column++)

{

// get the tile index to draw

char tile_index = map_ptr[(row + map_y) * _map_column + column + map_x];

long screen_x = column * tile_width - off_x;

long screen_y = row * tile_height - off_y;

// draw tile

_tile->draw_tile(0, tile_index, (DWORD)screen_x, (DWORD)screen_y, color, scale_x, scale_y);

}

}

// draw objects if on object layer

if(layer == object_layer)

{

for(long i = 0; i < _num_objects_to_draw; i++)

{

_tile->draw_tile(0, _objects_info[i].tile_index,

_objects_info[i].x_pos - off_x, _objects_info[i].y_pos - off_y,

color, scale_x, scale_y);

}

}

}

return TRUE;

}

我们接着编写两个例子来测试,第一个例子演示了基本贴片技术的使用,第二个例子演示了平滑卷轴的使用。 来看看第一个例子:

下载源码和工程

/*****************************************************************************

PURPOSE:

Test for class TILE and MAP.

*****************************************************************************/

#include "Core_Global.h"

#include "tile.h"

#include "map.h"

#pragma warning(disable : 4996)

class APP : public APPLICATION

{

public:

APP()

{

_width = 384;

_height = 384;

_style = WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU;

strcpy(_class_name, "scale_tile_class");

strcpy(_caption, "scale tile demo");

}

BOOL init()

{

// initialize the graphics device and set display mode

if(! _graphics.init())

return FALSE;

if(! _graphics.set_mode(get_hwnd() , TRUE, FALSE))

return FALSE;

// create and load the tile set

if(! _tile.create(&_graphics, 1))

return FALSE;

if(! _tile.load_texture(0, "tiles.bmp", 64, 64))

{

err_msg_box("load texture failed.");

return FALSE;

}

// create and set the map

char map_data[3][3] = {

{ 0, 1, 0 },

{ 2, 2, 2 },

{ 1, 2, 3 }

};

_map.create(1, 3, 3);

_map.set_map_layer_data(0, (char*) &map_data);

_map.use_tile(&_tile);

return TRUE;

}

BOOL APP::frame()

{

// calculate elapsed time

static DWORD s_last_time = timeGetTime();

DWORD now_time = timeGetTime();

DWORD elapsed_time = now_time - s_last_time;

// frame lock to 30ms per frame

if(elapsed_time < 30)

return TRUE;

s_last_time = now_time;

if(_graphics.begin_scene())

{

if(_graphics.begin_sprite())

{

D3DCOLOR color;

static uchar s_red = 0, s_green = 0, s_blue = 0;

static BOOL s_increment_color = TRUE;

if(s_increment_color)

{

color = D3DCOLOR_RGBA(s_red++, s_green++, s_blue++, 255);

if(s_red >= 255)

s_increment_color = FALSE;

}

else

{

color = D3DCOLOR_RGBA(s_red--, s_green--, s_blue--, 255);

if(s_red <= 0)

s_increment_color = TRUE;

}

// draw the map

_map.render(0, 0, 3, 3, 0, color, 2.0f, 2.0f);

_graphics.end_sprite();

}

_graphics.end_scene();

_graphics.display();

}

return TRUE;

}

BOOL shutdown()

{

return TRUE;

}

private:

GRAPHICS _graphics;

TILE _tile;

MAP _map;

};

int PASCAL WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)

{

APP app;

return app.run();

}

该程序淡入淡出地改变贴图的颜色,截图如下:

接着来看第二个例子:

下载源码和工程

/*****************************************************************************

PURPOSE:

Test for class TILE and MAP.

*****************************************************************************/

#include "Core_Global.h"

#include "tile.h"

#include "map.h"

#pragma warning(disable : 4996)

#define TILE_WIDTH 64

#define TILE_HEIGHT 64

#define MAP_COLUMNS 16

#define MAP_ROWS 16

#define TOTAL_MAP_SIZE 1024

class APP : public APPLICATION

{

public:

APP()

{

_width = 640;

_height = 480;

_num_columns_to_draw = _width / TILE_WIDTH;

_num_rows_to_draw = _height / TILE_HEIGHT;

_max_move_width = TOTAL_MAP_SIZE - _width;

_max_move_height = TOTAL_MAP_SIZE - _height;

_style = WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU;

strcpy(_class_name, "map class");

strcpy(_caption, "map demo");

}

BOOL init()

{

// initialize the graphics device and set display mode

if(! _graphics.init())

return FALSE;

if(! _graphics.set_mode(get_hwnd(), TRUE, FALSE))

return FALSE;

// create and load the tile set

if(! _tile.create(&_graphics, 1))

return FALSE;

if(! _tile.load_texture(0, "tiles.bmp", TILE_WIDTH, TILE_HEIGHT))

{

err_msg_box("load texture failed.");

return FALSE;

}

// create and set the map

char map_data[MAP_ROWS][MAP_COLUMNS] = {

{ 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },

{ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0 },

{ 1, 2, 2, 1, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0 },

{ 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 0, 2, 0 },

{ 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 0, 0, 0, 2, 0 },

{ 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0 },

{ 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0 },

{ 3, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0 },

{ 3, 0, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0 },

{ 0, 0, 2, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0 },

{ 0, 2, 2, 1, 1, 0, 0, 2, 2, 0, 0, 2, 2, 2, 2, 0 },

{ 0, 1, 2, 2, 2, 0, 0, 2, 2, 0, 0, 2, 1, 1, 2, 0 },

{ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0 },

{ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0 },

{ 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },

{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }

};

_map.create(1, MAP_COLUMNS, MAP_ROWS);

_map.set_map_layer_data(0, (char*) &map_data);

_map.use_tile(&_tile);

return TRUE;

}

BOOL APP::frame()

{

static long s_x_pos = 0, s_y_pos = 0;

// calculate elapsed time

static DWORD s_last_time = timeGetTime();

DWORD now_time = timeGetTime();

DWORD elapsed_time = now_time - s_last_time;

// frame lock to 33ms per frame

if(elapsed_time < 33)

return TRUE;

s_last_time = now_time;

if(_graphics.begin_scene())

{

if(_graphics.begin_sprite())

{

// draw the map

_map.render(s_x_pos, s_y_pos, _num_rows_to_draw, _num_columns_to_draw, 0, 0xFFFFFFFF, 1.0f, 1.0f);

// press arrows to scroll map around

if(GetAsyncKeyState(VK_LEFT)) s_x_pos -= 8;

if(GetAsyncKeyState(VK_RIGHT)) s_x_pos += 8;

if(GetAsyncKeyState(VK_UP)) s_y_pos -= 8;

if(GetAsyncKeyState(VK_DOWN)) s_y_pos += 8;

// bounds check map coordinates

if(s_x_pos < 0)

s_x_pos = 0;

if(s_x_pos > _max_move_width)

s_x_pos = _max_move_width;

if(s_y_pos < 0)

s_y_pos = 0;

if(s_y_pos > _max_move_height)

s_y_pos = _max_move_height;

_graphics.end_sprite();

}

_graphics.end_scene();

_graphics.display();

}

return TRUE;

}

BOOL shutdown()

{

return TRUE;

}

private:

GRAPHICS _graphics;

TILE _tile;

MAP _map;

long _num_columns_to_draw;

long _num_rows_to_draw;

long _max_move_width;

long _max_move_height;

};

int PASCAL WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)

{

APP app;

return app.run();

}

该程序展示了平滑卷轴技术的使用,用上下左右键进行控制,截图如下:

相关分词: 平滑 卷轴 效果