@Sk1talec
Фанат Java, Android и компьютерного зрения :)

В чём причина небольших подлагиваний в игре?

Решил в свободное от учебы время попробовать написать свой проект — небольшую игру на flash.

В качестве метода вывода изображений я решил использовать BitmapData, который выполняет роль камеры. На него каждый update с помощью метода draw отрисовываются все игровые объекты которые попадают в поле зрения камеры. Однако в процессе реализации появилась одна проблема, с которой я воюю уже 3ий день.

При перемещений камеры, происходит «подлагивание» игрового фона. Проблема в том, что это только визуальный эффект. На деле же, один update занимает время <1 мс поэтому дело совсем не в производительности.

Собственно вопрос. В чем причина этих «подлагиваний»?


Архитектура программы:

1) Создается таймер который отвечает за обновление элементов игры.

_timer = new Timer(1000 / FPS);//FPS == 60;

_timer.addEventListener(TimerEvent.TIMER, gameLoop);

_timer.start();

2)Главный игровой цикл
private function gameLoop(e:Event):void 
		{
			//Считаем время, которое прошло с момента последнего апдейта
			_date = new Date();
			var deltaTime:Number = (isNaN(_lastUpdate))?0:(_date.getTime() - _lastUpdate) / 1000;
			
			//НАЧАЛО ОБРАБОТКИ ИГРОВОГО ЦИКЛА
			//Обновляем команды полученные от игрока.
			_playerController.update();
			
			//Обновляем игровую модель.
			_gameController.update(deltaTime);
			
			//Обновляем позицию камеры.
			updateCameraPosition();
			
			//Обновляем отображение игры.
			_gameView.update();
			//КОНЕЦ ОБРАБОТКИ ИГРОВОГО ЦИКЛА
			//Засекаем время, на котором закончился апдейт.
			_lastUpdate = _date.getTime();
		}


3) PlayerController.update() — обновляет вектор движения игрока и нормализует его.

4) _gameController.update(deltaTime);

Идет проверка на столкновения.

В случае успешного прохождения всех проверок в классе игрока вызывается метод

move(deltaTime):void

{

this.x += movingDirection.x * speed * deltaTime;

this.y += movingDirection.y * speed * deltaTime;

}

5) Обновляем позицию камеры
private function updateCameraPosition():void 
		{
			var levelWidth:int = _gameLevel.maxWidth / 2 - 1;
			var leveHeight:int = _gameLevel.maxHeight / 2 - 1;
			
			if ((levelWidth + 1) * LevelView.CELL_WIDTH > _gameController.player.x + GAME_WIDTH / 2
				&& ( -levelWidth - 1) * LevelView.CELL_WIDTH < _gameController.player.x - GAME_WIDTH / 2)
			{
				_gameView.camX = _gameController.player.x;
			}
			
			if ((leveHeight + 1)  * LevelView.CELL_HEIGHT > _gameController.player.y + GAME_HEIGHT / 2
				&& ( -leveHeight - 1) * LevelView.CELL_HEIGHT < _gameController.player.y - GAME_HEIGHT / 2)
			{
				_gameView.camY = _gameController.player.y;
			}
		}


6) Обновляем view.
public function update():void
		{
			camera.lock();
			camera.fillRect(new Rectangle(0, 0, _camWidth, _camHeight), 0);
			drawLevelAndStuff();
			drawUnits();
			
			camera.unlock();
			
		}


7) Метод drawLevelAndStuff()
private function drawLevelAndStuff():void 
		{
			var minColumn:int = (camX - _camWidth / 2) / LevelView.CELL_WIDTH - 1;
			var minRow:int = (camY - _camHeight / 2) / LevelView.CELL_HEIGHT - 1;
			
			var maxColumn:int = minColumn + _camWidth / LevelView.CELL_WIDTH + 1;
			var maxRow:int = minRow + _camHeight / LevelView.CELL_HEIGHT + 1;
			
			var matrix:Matrix = new Matrix();
			
			for (var i:int = minRow; i <= maxRow; i++)
			{
				for (var j:int = minColumn; j <= maxColumn; j++)
				{
					//Отрисовываем покрытие клетки
					matrix.translate(getLocalX(j * LevelView.CELL_WIDTH), getLocalY(i * LevelView.CELL_HEIGHT));
					camera.draw(_level.getCell(i, j), matrix);
		
					//Отрисовываем статические объекты
					var stuff:IBitmapDrawable = _level.getStuff(i, j);
					if (stuff)
					{
						camera.draw(stuff,matrix);
					}
					matrix.identity();
				}
			}
		}



Пример флешки.
belk.su/


Управление:

W — вперед

S — назад

Мышкой поворот. Для старта нужно нажать на черный круг :)

UPD

P.S. В www.realmofthemadgod.com/ наблюдается тот же эффект. Если сфокусировать взгляд на каком-то элементе покрытия, то при перемещении видно, что оно движется не плавно. Я уже начал думать, что проблема в моих глазах, однако друзья, в том числе и со своих компьютеров, видят то же самое.
  • Вопрос задан
  • 3056 просмотров
Решения вопроса 1
@ashcraft
Это так называемый Screen tearing, эффект возникает, как выше писали, из-за того, что частота обновления монитора не кратна частоте флешевого рендера. При скроллинге область перерисовки большая, поэтому артефакты заметны сильнее. Для наглядности можно запилить тест, где просто каждый кадр поочередно делать заливку битмапы, то в белый, то в черный цвета. Из-за особенностей однобуферного софтверного флешевого рендера, это нельзя устранить при CPU-рендере, в общем случае. Решают эту проблему только 11-й плеер + stage3d.
Ответ написан
Пригласить эксперта
Ответы на вопрос 4
Iliapan
@Iliapan
может быть не стоит обновлять так часто экран… выберите для себя приемлемый fps и работайте с ним.
Ответ написан
skyboy
@skyboy
Может быть причина в том, что у вас стоит дебаг версия флеш плеера. У меня стоит обычная, никаких лагов нет.
Ответ написан
@egorinsk
Вы уверены, что таймер не округляет интервал, до кратности какому-то числу мс? Также, гарантирует ли флеш, что таймер будет вызван ровно 60 раз и через равные промежутки?

Также, синхронизируется ли перерисовка картинки флешем с разверткой монитора?

Попробуйте в начале каждого кадра получать показания часов и проверьте, равномерно ли вызвыается ваш код. Я подозреваю, что неравномерно. Также, почему вы используете таймер, а не событие входа в кадр? Может, флеш перерисовывает объекты не сразу, а когда начинается следующий кадр?

И вообще, это обязательно, каждый кадр передвигать и перерисовывать все объекты? Особенно те, которые невидимы? Может, можно сделать их классическими объектами, привязать к прямоугольнику и двигать только его, этот прямоугольник? Вы точно флешер?
Ответ написан
Ogra
@Ogra
А это не может быть накапливание ошибки округления?
Например, если персонаж проходит по 1.1 пикселя за кадр, то в одном из 10 кадров он будет проходить 2 пикселя, а в 9 из 10 — только 1. Вот и «подергивания».
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы