среда, 9 февраля 2011 г.

Скроллинг плиточного мира.

Представляю вашему вниманию перевод серии статей «Tutorial: N-way tile-based blit fine scrolling in AS3».

Мы обсудим теорию и практику скроллинга блитированной битмапдаты в любом направлении. Что это значит? Скроллинг это средство, с помощью которого вы можете передвинуть свой игровой экран в любом направлении, которое определяется углом поворота вашего персонажа. Здесь, в качестве примера мы рассмотрим двухмерную симуляцию автогонки с видом сверху, но техника, которую мы здесь рассмотрим, может применяться и в других играх. В нашем примере автомобиль всегда остается в центре экрана, а фон под ним прокручивается. Наш пример может быть легко сведен к скроллингу в 4-х и 8-ми направлениях, и хотя движение в таких играх не основано на направлении поворота, но способ скроллинга идентичен. Примеры скроллинга в 4-х и 8-ми направлениях мы также рассмотрим.

Что собой представляет скроллинг блитированного мира?



Скроллинг – это метод, с помощью которого мы показываем и обновляем только ту часть тайловой карты, которая видна на экране. Это не то же самое, что и хранение всего мира как одного большого объекта BitmapData и отображение только той части, которая нам нужна. Конечно, такой метод имеет право на жизнь, но он ограничивает физический размер мира, который мы можем создать. С помощью скроллинга тайлового мира, мы можем создать безграничный игровой мир. Мы можем представить его, например, как двухмерный массив (считываемый из xml) размером 100х100. Каждый элемент нашего массива представляет собой целое число, которое соответствует определенному тайлу на тайловом листе (можно посмотреть этот перевод). Мы показываем только то, что помещается на экране, но обновляем в буфере скроллинга область большую на один тайл в каждом направлении (влево, вправо, вверх, вниз), чем размер видимой области.

Мир, окно и буфер



Мир ссылается на всю карту тайлов, которую игрок может прокрутить. Окно - это текущий набор тайлов, который видит игрок. Буфер – набор тайлов больше размера окна на один тайл в каждом направлении. Сначала мы отрисовываем буфер, затем выясняем, какая граница будет видна игроку и отрисовываем область, ограниченную этой границей в окно. Пока мы здесь не говорим о коде, но когда я говорю «отрисовываем» и «прокрутить» я имею ввиду метод copyPixel() класса BitmapData и экземпляры класса Point и Rectangle необходимых для выполнения этого метода.

Точный скроллинг



Точный скроллинг дает нам возможность прокрутить экран на 1 пиксель за единицу времени в любом направлении. Это противоположность «тайловому скроллингу», когда экран прокручивается вверх, вниз, влево или вправо на целый тайл за один раз. Тайловый скроллинг (или грубый скроллинг) это более простой метод, для выполнения которого вам нужно знать только какой тайл находится в верхнем левом углу и в каком направлении осуществлять скроллинг. Когда экран прокручен, мы начинаем «раскрашивать» тайлы в соответствием со значениями смещения по осям х и у. Если смещение по оси х равно единице, тогда экран прокручен на один тайл по оси х вправо, если смещение по оси у равно двум, то экран прокручен на два тайла по оси у вниз (расположение осей соответствует их расположению в плеере). Значения 0х и 0у соответствуют левому верхнему углу экрана и значения смещений не могут быть меньше нуля. Когда пользователь нажимает на кнопку «стрелка ВВЕРХ» то значение смещения по оси у уменьшается на единицу, когда пользователь нажимает кнопку «стрелка ВНИЗ», то значение смещения по оси у увеличивается на единицу. То же самое справедливо и для оси х. «Стрелка ВЛЕВО» - уменьшаем смещение по оси х на единицу, «стрелка ВПРАВО» - увеличиваем значение смещения на единицу по оси х. Максимальное значение, на которое мы можем прокрутить экран вправо – ширина самого мира и, соответственно, максимальное значение, на которое мы можем прокрутить экран вниз – высота мира.
Ниже пример тайлового скроллинга. Используйте клавиши со стрелками для перемещения по миру. Здесь прокрутка происходит на один тайл за один раз. (Убедитесь, что ролик в фокусе).

[SWF]http://coolisee.com/wordpress/wp-content/uploads/2011/02/09/1.swf, 240, 320[/SWF]



Как видите, тайлы прокручиваются очень быстро и это годится для многих типов игр (Boulder Dash, Phantasie). В симуляторе гонок такой тип скроллинга не подходит, поэтому мы будем использовать точный скроллинг.
В точном скроллинге мы используем ту же самую концепцию, но прокручивать будем на один пиксель за один раз (а не на один тайл). Простейший метод точного скроллинга – скроллинг в четырех направлениях. В этом методе, когда мы нажимаем вверх, вниз, влево, вправо, значения смещений по осям х и у изменяются на один пиксель. Следующая вещь, которую мы должны сделать – выяснить, какие тайлы должны находиться в окне и буфере. Также как и тайловом скроллинге, мы будем использовать простую арифметику для того, чтобы выяснить какие тайлы должны быть помещены в буфер. Но немного усложним задачу и сделаем прокрутку экрана зависимой от ускорения и поворота автомобиля. В конечном итоге это позволит прокручивать экран в любых направлениях, для этого нам нужно знать только угол поворота автомобиля и некоторые простые тригонометрические функции. Если вы интересуетесь только скроллингом в четырех и восьми направлениях, то наш метод применим и для них. В конце мы разберем модификации метода, которые позволят получить скроллинг в четырех и восьми направлениях для того же самого мира.

Как это сделать?



Прежде всего, у нас должен быть этот спрайт в библиотеке и он должен иметь имя для экспорта sprites16x16_png

Тайловый лист


Рисунок 1. – Тайловый лист

У нас есть окно размером 16х16 тайлов, но мы будем использовать для этого примера не все.
Также у нас есть данные xml, в которых определен мир размером 100х100 тайлов. Это выглядит просто как набор цифр:

00000


Данные мира представляют собой строки и столбцы по сто элементов. Значения элементов могут быть от 0 до 5 и соответствуют определенному тайлу на тайловом листе.
Эти данные мы будем парсить в двухмерный массив, который назовем aWorld. Для начала давайте займемся настройками мира, которые позволят нам отображать на экране.
Здесь стартовые данные для нашего мира. Этот код расположен в методе init() моей игры.

mapTileHeight=16;
mapTileWidth=16;
// the output window width and height in pixels
viewWidth=240;
viewHeight=320;
//our scrollable world in tile height and width
worldCols=100;
worldRows=100;
worldWidth=worldCols*mapTileWidth; //world width: 16*100=1600 pixels
worldHeight=worldRows*mapTileHeight; //world height: 16*100=1600 pixels
viewCols=viewWidth/mapTileWidth; // columns in view:240/16=15
viewRows=viewHeight/mapTileHeight; //rows in view: // 320/16 = 20


Итак, у нас есть мир размером 1600х1600 и окно размером 240х320 пикселей. Нам понадобится холст (наше окно вывода) для блитирования и буфер размером 240+(ширина тайла) на 320+(высота тайла). Нам нужно записать в буфер только по одному тайлу в каждом направлении. Нужно заметить, что скорость автомобиля не должна быть выше, чем ширина или высота тайла.

В нашем случае буфер будет размером 256х336. Нам нужен будет один экземпляр класса Bitmap для хранения нашего холста.

canvasBD=new BitmapData(viewWidth,viewHeight,false,0x000000);
bufferBD=new BitmapData(viewWidth+2*mapTileWidth,viewHeight+2*mapTileHeight,false,0x000000);
canvasBitmap=new Bitmap(canvasBD);
addChild(canvasBitmap);


Я решил поместить автомобиль в начало дорожки, которая находиться внизу и середине мира. Начальные значения смещений по осям х и у следующие:

viewXOffset=787;
viewYOffset=1249;


Итак, когда мы запустим приложение, нам нужно будет сначала определить тайл, который представляет стартовую позицию, затем заполнить буфер и наконец заполнить окно вывода.

Следующий код находится в методе drawView(), который вызывается в каждом игровом интервале.

//calculate starting tile position
var tilex:int=int(viewXOffset/mapTileWidth);
var tiley:int=int(viewYOffset/mapTileHeight);
//tilex=int(787/16). tilex=49;
//tiley=int(1249/16). tiley=78;
//set up loop counters
var rowCtr:int=0;
var colCtr:int=0;
//tile num is used to hold the index of the current tile on the tile sheet
var tileNum:int;


Теперь пройдем в цикле по всем тайлам чтобы заполнить экран и буфер.

for (rowCtr=0;rowCtr<=viewRows; rowCtr++) {
for (colCtr=0;colCtr<=viewCols; colCtr++) {
tileNum=aWorld[rowCtr+tiley][colCtr+tilex];
tilePoint.x=colCtr*mapTileWidth;
tilePoint.y=rowCtr*mapTileHeight;
tileRect.x = int((tileNum % sprites16x16_perRow))*mapTileWidth;
tileRect.y = int((tileNum / sprites16x16_perRow))*mapTileHeight;
bufferBD.copyPixels(sprites16x16,tileRect,tilePoint);
}
}


Во внешнем цикле проходим строки, во внутреннем столбцы. Мы используем обычную математику, чтобы определить, какой тайл выбрать из тайлового листа. Затем мы перемещаем точку tilePoint и прямоугольник tileRect в соответствующие координаты.

Буфер bufferBD сейчас состоит из 21 строки и 16 столбцов, но нам нужно блитировать область только 15х20. Следующая вещь, которую мы сделаем – определим часть буфера, которую нужно блитировать в холст.

bufferRect.x=viewXOffset % mapTileWidth;
bufferRect.y=viewYOffset % mapTileHeight;
canvasBD.copyPixels(bufferBD,bufferRect,bufferPoint);


Здесь мы просто определили начальные координаты прямоугольника, которым будет ограничена область, которую мы будем блитировать в холст. В нашем случае это составит

787 % 16 = 3х;
1249 % 16 = 1у;


Итак, как мы будем перемещать наш автомобиль?



Наша машинка изображена ниже и ее имя для экспорта car_png.

Машинка


Рисунок 2. – Наш автомобиль

Существует много путей для добавления объекта на экран. В большинстве случаев, если у меня более ста объектов в игре, то я буду блитировать каждый, чтобы гарантировать постоянную частоту кадров. Это не нужно в этой демонстрации или даже реальной игре основанной на этой демонстрации, т.к. перемещающихся объектов не слишком много. В этом случае, почему бы не использовать класс, наследующий от класса Sprite? Это самый быстрый способ для прототипирования движка. Если игра начнет тормозить, то содержимое этого спрайта может быть спокойно блитировано в холст.

В методе init() я делаю настройки машинки, не создавая объект для хранения всех свойств машинки. В конечном варианте игры я, возможно, создам класс Car, который будет наследовать от Sprite. Но это не нужно в нашем примере.

//car
carPNG=new car_png(31,21)
carSprite=new Sprite();
carBitmap=new Bitmap(carPNG);
carBitmap.x=-15.5;
carBitmap.y=-10.5;
carSprite.addChild(carBitmap);
carSprite.x=110;
carSprite.y=145;
carRotation=-90;
carTurnSpeed=.5;
carMaxSpeed=5;
carAcceleration=.02;
carDeceleration=.03;
addChild(carSprite);
addChild(carSprite);


Машинка представляет собой просто рисунок размером 31х21 в формате png и она повернута направо. Мы сделали так, потому-что этот угол соответствует углу в ноль градусов для отображаемых объектов во Flash. Также нам нужно указать точку регистрации для этого объекта в точке с координатами (0,5*ширины ; 0,5*высоты). Затем мы добавляем битмап в спрайт carSprite, а carSprite добавляем в центр нашего окна размером 240х320 (т.е. carSprite.x = 110, carSprite.y = 145). Мы устанавливаем угол поворота машинки carRotation равным -90, чтобы в начале игры она смотрела передней частью вверх. Переменная carTurnSpeed определяет значение, на которое будет повернута машинка при нажатии на клавиши влево или вправо. Максимальное значение для скорости машинки мы устанавливаем равным 5. Скорость машинки carSpeed может увеличиваться с ускорением carAcceleration (когда нажата кнопка вверх) и уменьшаться с замедлением carDeceleration (когда нажата кнопка вниз).
Наконец, мы добавляем carSprite в список отображения (это происходит после добавления битмапдаты холста).

Проверка нажатия клавиш



В каждом игровом интервале нам нужно проверять, какие из клавиш со стрелками нажаты (если они нажаты).
В методе init() нам нужно добавить следующие слушатели:

//key listeners
stage.addEventListener(KeyboardEvent.KEY_DOWN,keyDownListener);
stage.addEventListener(KeyboardEvent.KEY_UP,keyUpListener);

private function keyDownListener(e:KeyboardEvent) {
//trace("down e.keyCode=" + e.keyCode);
aKeyPress[e.keyCode]=true;
}
private function keyUpListener(e:KeyboardEvent) {
//trace("up e.keyCode=" + e.keyCode);
aKeyPress[e.keyCode]=false;
}


В каждом игровом интервале мы будем вызывать метод checkKeys():

if (aKeyPress[38]){
trace("up pressed");
carSpeed+=carAcceleration;
if (carSpeed >carMaxSpeed) carSpeed=carMaxSpeed;

}
if (aKeyPress[40]){
trace("down pressed");
carSpeed-=carDeceleration;
if (carSpeed <-carMaxSpeed) carSpeed=-carMaxSpeed;

}
if (aKeyPress[37]){
trace("left pressed");

carRotation-=(carSpeed+.1)*carTurnSpeed;

}
if (aKeyPress[39]){
trace("right pressed");

carRotation+=(carSpeed+.1)*carTurnSpeed;

}


Если нажата клавиша «стрелка ВВЕРХ» то мы будем добавлять ускорение к скорости, при этом будем следить, чтобы скорость не превысила максимальное значение maxSpeed. То же самое мы сделаем с замедлением при нажатии на клавишу «стрелка ВНИЗ». Если нажаты клавиши «стрелка ВЛЕВО» и «стрелка ВПРАВО», то мы будем обновлять carRotation, учитывая при этом скорость машинки.

Последний метод, который нам нужен это метод updatePlayer(). Он будет также вызываться в каждом временном интервале и служит для определения величины скроллинга экрана и поворота игрока. Также он проверяет столкновения с краями мира.

carSprite.rotation=carRotation;
var carRadians:Number=(carRotation/360)*(2.0*Math.PI);
carDX=Math.cos(carRadians)*carSpeed;
carDY=Math.sin(carRadians)*carSpeed;

viewXOffset+=carDX;
viewYOffset+=carDY;

if (viewXOffset <0) {
viewXOffset=0;
}else if (viewXOffset > (worldWidth-viewWidth)-1) {
viewXOffset = (worldWidth - viewWidth)-1;
trace("hit end of world width");
}

if (viewYOffset <0) {
viewYOffset=0;
}else if (viewYOffset > (worldHeight-viewHeight)-1) {
viewYOffset = (worldHeight-viewHeight)-1;
trace("hit end of world height");
}


Итак работающий пример:

[SWF]http://coolisee.com/wordpress/wp-content/uploads/2011/02/09/2.swf, 240, 320[/SWF]



Как сделать скроллинг в четырех направлениях



Единственная вещь, которую для этого нужно поменять это как мы будем обрабатывать нажатия клавиш и преобразовывать их в значения carDX и carDY. Сначала нам нужно изменить поведения при нажатия на клавиши влево, вправо, вниз, вверх и затем изменить обновление значений viewXOffset и viewYOfsset. В основном это все, что нам нужно.

private function checkKeys():void {

if (aKeyPress[38]){
trace("up pressed");
carRotation=-90;
carSpeed+=carAcceleration;
if (carSpeed >carMaxSpeed) carSpeed=carMaxSpeed;
}
if (aKeyPress[40]){
trace("down pressed");
carRotation=90;
carSpeed+=carAcceleration;
if (carSpeed >carMaxSpeed) carSpeed=carMaxSpeed;
}
if (aKeyPress[37]){
trace("left pressed");
carRotation=-180
carSpeed+=carAcceleration;
if (carSpeed >carMaxSpeed) carSpeed=carMaxSpeed;
}
if (aKeyPress[39]){
trace("right pressed");
carRotation=0;
carSpeed+=carAcceleration;
if (carSpeed >carMaxSpeed) carSpeed=carMaxSpeed;
}
}


В этом методе мы жестко задаем значения carRotation. Это не совсем точная модель ускорения т.к. скорость сразу равна максимальному значению, но т.к. это пример, то оставим как есть. В игре нужно будет запоминать последнюю нажатую клавишу и если она отличается от предыдущего нажатия, то присваивать скорости машинки значение 0.
Ниже пример скроллинга в четрырех направлениях:

[SWF]http://coolisee.com/wordpress/wp-content/uploads/2011/02/09/3.swf, 240, 320[/SWF]




Скроллинг в восьми направлениях



Этот метод почти не отличается от предыдущего, только кнопок нам надо использовать больше:

QWE
ASD
ZXC

Кнопку S мы будем использовать для полной остановки машинки.

Измененный метод checkKeys():

private function checkKeys():void {

if (aKeyPress[81]){
trace("Q pressed");

carRotation=-135;
carSpeed+=carAcceleration;
if (carSpeed >carMaxSpeed) carSpeed=carMaxSpeed;

}
if (aKeyPress[87]){
trace("W pressed");

carRotation=-90;
carSpeed+=carAcceleration;
if (carSpeed >carMaxSpeed) carSpeed=carMaxSpeed;
}
if (aKeyPress[69]){
trace("E pressed");
carRotation=-45
carSpeed+=carAcceleration;
if (carSpeed >carMaxSpeed) carSpeed=carMaxSpeed;
}
if (aKeyPress[65]){
trace("A pressed");
carRotation=-180;
carSpeed+=carAcceleration;
if (carSpeed >carMaxSpeed) carSpeed=carMaxSpeed;
}

if (aKeyPress[83]){
trace("S pressed");
carSpeed=0;
}
if (aKeyPress[68]){
trace("D pressed");
carRotation=-0;
carSpeed+=carAcceleration;
if (carSpeed >carMaxSpeed) carSpeed=carMaxSpeed;
}

if (aKeyPress[90]){
trace("Z pressed");
carRotation=135;
carSpeed+=carAcceleration;
if (carSpeed >carMaxSpeed) carSpeed=carMaxSpeed;
}

if (aKeyPress[88]){
trace("X pressed");
carRotation=90;
carSpeed+=carAcceleration;
if (carSpeed >carMaxSpeed) carSpeed=carMaxSpeed;
}

if (aKeyPress[67]){
trace("C pressed");
carRotation=45;
carSpeed+=carAcceleration;
if (carSpeed >carMaxSpeed) carSpeed=carMaxSpeed;
}
}


Может это некрасиво, но для демонстрации сойдет. Работающий пример:

[SWF]http://coolisee.com/wordpress/wp-content/uploads/2011/02/09/4.swf, 240, 320[/SWF]




Остальная часть кода



Эта часть кода не обязательна для применения, а только для демонстрации.

runGame()

Этот метод – обработчик события TimerEvent.TIMER. В этом методе выполняются все действия по изменению игрового мира (подробнее можно почитать здесь).

private function runGame(e:TimerEvent) {
//trace("running game");
_beforeTime = getTimer();
_overSleepTime = (_beforeTime - _afterTime) - _sleepTime;


checkKeys();
updatePlayer();
//checkCollisions();
canvasBD.lock();
drawBackground();
drawView();
drawPlayer();
canvasBD.unlock();

_afterTime = getTimer();
_timeDiff = _afterTime - _beforeTime;
_sleepTime = (_period - _timeDiff) - _overSleepTime;
if (_sleepTime <= 0) {
_excess -= _sleepTime
_sleepTime = 2;
}
gameTimer.reset();
gameTimer.delay = _sleepTime;
gameTimer.start();

while (_excess > _period) {
checkKeys();
updatePlayer();
checkCollisions();
_excess -= _period;
}

//frameTimer.countFrames();
//frameTimer.render();
e.updateAfterEvent();
}


setUpWorld()

Этот метод берет карту в формате xml и парсит ее в двухмерный массив aWorld.

private function setupWorld():void {
//parse xmlMapData and create aWorld array
for (var rowCtr:int=0; rowCtr < worldRows; rowCtr++) {
var tempArray:Array=new Array();
for (var colCtr:int=0; colCtr < worldCols; colCtr++) {
tempArray.push(XMLLevelData.tilerow[rowCtr].tilecol[colCtr])
}
aWorld.push(tempArray);
}

//trace("row0,col0=" + aWorld[0][0]);
}


Метод init()

В этом методе просто делаются все предварительные настройки мира.

private function init():void {

viewWidth=240;
viewHeight=320;
mapTileWidth=16;
mapTileHeight=16;
worldCols=100;
worldRows=100;
worldWidth=worldCols*mapTileWidth;
worldHeight=worldRows*mapTileHeight;
viewCols=viewWidth/mapTileWidth;
viewRows=viewHeight/mapTileHeight;
viewXOffset=787;
viewYOffset=1249;


tileRect=new Rectangle(0,0,mapTileWidth,mapTileHeight);
tilePoint=new Point(0,0);

trace("viewCols=" + viewCols);
trace("viewRows=" + viewRows);


trace("setup map data");
setupMapData();
setupWorld();

trace("init and canvas");
canvasBD=new BitmapData(viewWidth,viewHeight,false,0x000000);
//canvasBD=new BitmapData(viewWidth+2*mapTileWidth,viewHeight+2*mapTileHeight,false,0x000000);
trace("init background");
backgroundBD = new BitmapData(viewWidth,viewHeight,false,0x000000);
backgroundRect=new Rectangle(0,0,viewWidth,viewHeight);
backgroundPoint = new Point(0,0);
trace("init buffer");
//bufferBD=new BitmapData(viewWidth+2*mapTileWidth,viewHeight+2*mapTileHeight,false,0x000000);
bufferBD=new BitmapData(viewWidth+mapTileWidth,viewHeight+mapTileHeight,false,0x000000);
bufferRect=new Rectangle(0,0,viewWidth,viewHeight);
bufferPoint=new Point(0,0);
trace("init 16x16 sprites");
sprites16x16_width=160;
sprites16x16_height=160;
sprites16x16=new sprites16x16_png(sprites16x16_width,sprites16x16_height);
sprites16x16_perRow=sprites16x16_width/mapTileWidth;

//car
carPNG=new car_png(31,21)
carSprite=new Sprite();
carBitmap=new Bitmap(carPNG);
carBitmap.x=-15.5;
carBitmap.y=-10.5;
carSprite.addChild(carBitmap);
carSprite.x=110;
carSprite.y=145;
carRotation=-90;
carTurnSpeed=.5;
carMaxSpeed=5;
carAcceleration=.02;
carDeceleration=.03;

//canvasBitmap
canvasBitmap=new Bitmap(canvasBD);
addChild(canvasBitmap);
addChild(carSprite);

//message panel
mPanel=new messagePanel();
mPanel.x=50;
mPanel.y=50;
addChild(mPanel);


gameTimer=new Timer(_period,1); //changed in part 3 from 50
gameTimer.addEventListener(TimerEvent.TIMER, runGame);
gameTimer.start();

//key listeners
stage.addEventListener(KeyboardEvent.KEY_DOWN,keyDownListener);
stage.addEventListener(KeyboardEvent.KEY_UP,keyUpListener);
}


Блок объявления переменных.

    var mapTileWidth:int;
var mapTileHeight:int;

var playerObj:Object=new Object();
var canvasBD:BitmapData;
var backgroundBD:BitmapData;
var backgroundRect:Rectangle;
var backgroundPoint:Point;

//level xml
var XMLLevelData:XML;

//world
var aWorld:Array=new Array();
var worldCols:int;
var worldRows:int;
var worldTileWidth:int;
var worldTileHeight:int;
var worldWidth:int;
var worldHeight:int;

//the buffer is 2 tiles longer and higher than he view
//well first copy all of the tiles to the buffer
//then copy just the portion we need to the view
var bufferBD:BitmapData;
var bufferWidth:int;
var bufferHeight:int;
var bufferRect:Rectangle;
var bufferPoint:Point;

//view
var viewWidth:int;
var viewHeight:int;
var viewCols:int;
var viewRows:int;
var viewXOffset:Number;
var viewYOffset:Number;
//for drawing viewAreaTiles
var tileRect:Rectangle;
var tilePoint:Point;

//sprites16x16
var sprites16x16_width:int;
var sprites16x16_height:int;
var sprites16x16:BitmapData;
var sprites16x16_perRow:int;

//car stuff
var carPNG:BitmapData;
var carBitmap:Bitmap;
var carSprite:Sprite;
var carSpeed:Number=0;
var carAcceleration:Number=0;
var carDeceleration:Number=0;
var carMaxSpeed:Number=0;
var carRotation:Number=0;
var carDX:Number=0;
var carDY:Number=0;
var carTurnSpeed:Number=0;



//canvasBitmap
var canvasBitmap:Bitmap;

//message panel
var mPanel:MovieClip;

//Game Timer
public static const FRAME_RATE:int = 40;
private var _period:Number = 1000 / FRAME_RATE;
private var _beforeTime:int = 0;
private var _afterTime:int = 0;
private var _timeDiff:int = 0;
private var _sleepTime:int = 0;
private var _overSleepTime:int = 0;
private var _excess:int = 0;
private var gameTimer:Timer;

//keyboard input
var aKeyPress:Array=[];


Если у вас есть этот код, то вы имеете 99% того, что понадобится для создания скроллинга с нуля. Единственное, что я здесь пропустил – метод setUpMapData(), т.к. он просто внедряет гигантский файл уровня в формате xml и присваивает его переменной XMLLevelData.

На этом у меня все.

Комментариев нет:

Отправить комментарий