Падает производительность приложения?

Добрый вечер, уважаемые хабравчане!


Взялся за изучение js: решил реализовать игру «Жизнь» с использованием canvas. За основу игры взят самый простой алгоритм с циклами и двумя двумерными массивами.


Столкнулся с такой проблемой, что после определенного времени работы приложения (примерно после 250 шага игры) оно начинает тормозить. От загруженности поля это не зависит: в начале игры могут стабильно без задержек отрисовываться 100+ объектов, а после определенного момента даже при 3 живых клетках будут задержки.


Подскажите, пожалуйста: в чем может быть дело?


Заранее благодарю.

Игра
Исходный код
var gridColor = "#c9c9c9";
var pieceColor = "green";
var backgroundColor = "#ffffff"; //c0cd02

var gDrawingContext, gCanvasElement, gStopwatchElement;
var gameSpeed = 100;
var dGameSpeed = 50;
var kBoardWidth = 80; 
var kBoardHeight= 40;
var kMinBoardSize=4;
var kPieceWidth = 10;
var kPieceHeight= 10;
var kPixelWidth = 1 + (kBoardWidth * kPieceWidth);
var kPixelHeight= 1 + (kBoardHeight * kPieceHeight);
var gridOn = true;
var gameInProgress = false;
var k = 0; // how many pieces exist now - population
var step = 0;
var piece = [];
var h = 0, s = 0, min = 0;
var gameTimer;
var timer; //stopwatch
var firstStart = 1;

function Cell(column, row){
	this.y = row;
	this.x = column;
}
function matrix(a,b) {
	var arr = [];
	for(var i=0;i<=a+1;i++) {arr[i] = [];}
	for(var i=0;i<=a+1;i++){
		for(var k=0;k<=b+1;k++) {arr[i][k] = 0;}
	}
	return arr;
}
function initGame(canvasElement, scoreCountElement, stopwatchElement) {
    if (!canvasElement) {
        canvasElement = document.createElement("canvas");
		canvasElement.id = "life_canvas";
		canvasElement.style.margin = "0 0 0 -"+kPixelWidth/2+"px";
		document.body.appendChild(canvasElement);
    }
    gCanvasElement = canvasElement;
	gCanvasElement.addEventListener("click", lifeOnClick, false);
    gCanvasElement.width = kPixelWidth;
    gCanvasElement.height = kPixelHeight;
    gScoreCountElem = scoreCountElement;
	gStopwatchElement = stopwatchElement;
    gDrawingContext = gCanvasElement.getContext("2d");
	newGame();
}

function newGame() {
	firstStart = 1;
	step = 1;
	arrayA = matrix(kBoardWidth,kBoardHeight);
	arrayB = matrix(kBoardWidth,kBoardHeight);
	arrayA[23][1] = 1;
	arrayA[24][14] = 1;
	arrayA[25][14] = 1;
	arrayB[23][14] = 1;
	arrayB[24][14] = 1;
	arrayB[25][14] = 1;
	k = 3;
	document.getElementById('step').innerHTML = step;
	document.getElementById('population').innerHTML = k;
	gameInProgress = true;
    drawBoard();
	gStopwatchElement.innerHTML = "00:00:00";
	clearInterval(timer);
	clearInterval(gameTimer);
	stopwatch();
	message(false,"");
	gameClock();
	if (firstStart == 1) {pauseGame(); message(true,'.</br> Backspace to pause or resume game.</br>');firstStart = 2;}
	}

function gameOver(way){
	clearInterval(gameTimer);
	clearInterval(timer);
	gameInProgress = false;
}
function gameClock(){
	gameTimer = setInterval(
		function(){
			if (!gameInProgress) {return;}
			evolution();
			drawBoard();
			step++;
			document.getElementById('step').innerHTML = step;
			document.getElementById('population').innerHTML = k;
		},gameSpeed);
}
function makePiece(cell){
	k++;
	arrayA[cell.x][cell.y] = 1;
	arrayB[cell.x][cell.y] = 1;
	gDrawingContext.fillRect((cell.x-1)*kPieceWidth+1, (cell.y-1)*kPieceHeight+1, kPieceWidth-1, kPieceHeight-1);
	document.getElementById('population').innerHTML = k;
}

function evolution(){
	var nearPieces = 0;
	for (var i=1;i<=kBoardWidth;i++){
		for (var j=1;j<=kBoardHeight;j++){
			nearPieces = arrayA[i+1][j] + arrayA[i-1][j] + arrayA[i+1][j+1] + arrayA[i-1][j-1] + arrayA[i+1][j-1] + arrayA[i-1][j+1] + arrayA[i][j+1] + arrayA[i][j-1];
			if (nearPieces == 3 && arrayA[i][j] == 0) {arrayB[i][j] = 1; k++;}
			if (arrayA[i][j] == 1){	
				if (nearPieces == 2 || nearPieces == 3){arrayB[i][j] = 1;}
				else {arrayB[i][j] = 0; k--;}
			}
		}
	}	
}

function drawBoard() {	
    gDrawingContext.clearRect(0, 0, kPixelWidth, kPixelHeight);
	gDrawingContext.fillStyle = backgroundColor;
	gDrawingContext.fillRect(0, 0, kPixelWidth, kPixelHeight);
	if (gridOn) {
	    for (var x = 0; x <= kPixelWidth; x += kPieceWidth) {
		gDrawingContext.moveTo(0.5 + x, 0);
		gDrawingContext.lineTo(0.5 + x, kPixelHeight);
		}
		for (var y = 0; y <= kPixelHeight; y += kPieceHeight) {
			gDrawingContext.moveTo(0, 0.5 + y);
			gDrawingContext.lineTo(kPixelWidth, 0.5 +  y);
		}
	}
	else {
			gDrawingContext.moveTo(0.5, 0);
			gDrawingContext.lineTo(0.5, kPixelHeight);
			gDrawingContext.moveTo(0, 0.5);
			gDrawingContext.lineTo(kPixelWidth, 0.5);
			gDrawingContext.moveTo(kPixelWidth - 0.5, 0);
			gDrawingContext.lineTo(kPixelWidth - 0.5, kPixelHeight);
			gDrawingContext.moveTo(0, kPixelHeight - 0.5);
			gDrawingContext.lineTo(kPixelWidth, kPixelHeight - 0.5);
	}
    gDrawingContext.strokeStyle = gridColor;
    gDrawingContext.stroke();
	drawPieces();
}
function drawPieces() {
	gDrawingContext.fillStyle = pieceColor;
	for (var i=1;i<=kBoardWidth;i++){
		for (var j=1;j<=kBoardHeight;j++){
			if (arrayB[i][j] == 1){
				gDrawingContext.fillRect((i-1)*kPieceWidth+1, (j-1)*kPieceHeight+1, kPieceWidth-1, kPieceHeight-1);
				arrayA[i][j] = 1;
			} else {arrayA[i][j] = 0;}
		}
	}

}

function getCursorPosition(e) {
    var x;
    var y;
    if (e.pageX != undefined && e.pageY != undefined) {
		x = e.pageX;
		y = e.pageY;
    }
    else {
		x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
		y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
    }
    x -= gCanvasElement.offsetLeft;
    y -= gCanvasElement.offsetTop;
    x = Math.min(x, kBoardWidth * kPieceWidth);
    y = Math.min(y, kBoardHeight * kPieceHeight);
    var cell = new Cell(Math.floor(x/kPieceWidth)+1, Math.floor(y/kPieceHeight)+1);
    return cell;
}

function lifeOnClick(e) {
    var cell = getCursorPosition(e);
	makePiece(cell);
}
/****Main stopwatch****/
function stopwatch(){
	var sh="00", ss="00", smin="00";
	h = 0, s = 0, min = 0;
		timer = setInterval(
			function () {
				if (!gameInProgress) {return;}
				s++;
				if (s==60) {min++; s = 0; if (min<10) {smin="0"+min} else {smin = min}}
				if (min==60) {h++; min = 0; if (h<10) {sh="0"+h} else {sh = h}}
				if (s>=10) {ss = s} else {ss="0"+s};
				gStopwatchElement.innerHTML = sh+":"+smin+":"+ss;},
			1000);
}



П.С. Буду безмерно рад комментариям по коду о том, как не надо писать на js.
  • Вопрос задан
  • 3288 просмотров
Решения вопроса 1
sheknitrtch
@sheknitrtch
Я вроде бы нашёл в чем причина.
Во-первых, методом тыка было определено, что если закомментировать строку 97:

drawBoard();

То никаких тормозов в Opera не наблюдается.

Во-вторых, если в глобальную переменную gridOn записать false, то тормоза тоже исчезают.

Ну и в-третьих, оказалось, что достаточно добавить после 127-ой строки

gDrawingContext.beginPath();

И тормоза исчезают в Opera навсегда.

Мне кажется, что если не вызывать beginPath, то Opera запоминает все созданные с помощью lineTo линии при каждом вызове drawBoard. И когда вызывается stroke() то рисуется сетка от всех предыдущих кадров вместе взятых. Эту гипотезу нужно ещё проверить. Но судя то тому, что вызов beginPath исправляет проблему производительности, Я думаю, дело именно в этом.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
sdevalex
@sdevalex
Никаких задержек не увидел. Вы профайлер пробовали запускать?
Ответ написан
Ваш ответ на вопрос

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

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