# Improve the performance of jquery/php generated map

We are building a interactive tile-based (32x32 px) (game) map where the user can move around. However we experience lag (some sort of a delay on the movement) and we need to work around this problem. The "hack/lag" also happen on a local server so it's not because of the traffic yet. Any suggestions how we can make the rendering of the map and performance of the map faster?

DISCLAIMER: The code is fast-written, we are beware of the security issues, please do not point them out

map.php

<?php
session_start();

$_SESSION['angle'] = 'up';$conn = mysql_connect('localhost', 'root', '') or die(mysql_error());
mysql_select_db('hol', $conn) or die(mysql_error());$query = mysql_query("SELECT x, y FROM hero WHERE id = 1");
$result = mysql_fetch_assoc($query);

$startX =$result['x'];
$startY =$result['y'];

$fieldHeight = 10;$fieldWidth = 10;

//x = 0 = 4
//y = 0 = 4

$sql = "SELECT id, x, y, terrain FROM map WHERE x BETWEEN ".($startX-$fieldWidth)." AND ".($startX+$fieldWidth). " AND y BETWEEN ".($startY-$fieldWidth)." AND ".($startY+$fieldHeight);$result = mysql_query($sql);$map = array();
while($row = mysql_fetch_assoc($result)) {
$map[$row['x']][$row['y']] = array('terrain' =>$row['terrain']);
}

ob_start();

for ($y=$startY-$fieldHeight;$y<$startY+$fieldHeight;$y++) { echo '<tr>'; for ($x=$startX-$fieldWidth;$x<$startX+$fieldWidth;$x++) {

if ($x ==$startX && $y ==$startY) {
echo '<td style="width:32px; height:32px; background-image:url(\'tiles/' . (isset($map[$x][$y]['terrain']) ?$map[$x][$y]['terrain'] : 'water') . '\');"><img src="char/medic_' . $_SESSION['angle'] . '.png" alt="" /></td>'; } else { //echo '(' .$x . ',' . $y . ')'; echo '<td style="width:32px; height:32px; background-image:url(\'tiles/' . (isset($map[$x][$y]['terrain']) ? $map[$x][$y]['terrain'] : 'water') . '\');"></td>'; } } echo '</tr>'; } echo '</table>';$content = ob_get_contents();

ob_end_clean();

?>
<!DOCTYPE html>
<html>
<title>Map</title>
<meta charset="utf-8">
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript">

$(document).ready(function() {$(document).keyup(function(e){
if (e.keyCode == 37) {
move("West");
return false;
}
if (e.keyCode == 38) {
move("North");
return false;
}
if (e.keyCode == 39) {
move("East");
return false;
}
if (e.keyCode == 40) {
move("South");
return false;
}
});

$(".direction").click(function() { move($(this).text());
});

function move(newDirection)
{
var direction = newDirection;

$.ajax({ type: "POST", url: "ajax/map.php", data: { direction: direction }, success: function(data) {$('#content').html(data);
}
});
}

/*
$("#content").click(function() { var x = 3; var y = 3;$.ajax({
type: "POST",
url: "ajax.php",
data: { x: x, y: y },
success: function(data) {
$('#content').html(data); } }); }); */ }); </script> <style type="text/css"> td {margin: 0; border: none; padding: 0;} img{ display:block; margin:0; } . </style> </head> <body> <div id="content"><?php echo$content; ?></div>

<div class="result"></div>

<button class="direction">South</button>
<button class="direction">North</button>
<button class="direction">West</button>
<button class="direction">East</button>
</body>
</html>


ajax/map.php

<?php
session_start();

$conn = mysql_connect('localhost', 'root', '') or die(mysql_error()); mysql_select_db('hol',$conn) or die(mysql_error());

//Get Player's current position

$query = mysql_query("SELECT x, y FROM hero WHERE id = 1");$result = mysql_fetch_array($query);$current_x = $result['x'];$current_y = $result['y']; switch ($_POST['direction']) {
case 'North':
if ($current_y - 1 < 0) { echo 'Invalid path'; } //Next tile$x = $current_x;$y = $current_y - 1;$_SESSION['angle'] = 'up';
break;
case 'South':
if ($current_y + 1 > 500) { echo 'Invalid path'; }$x = $current_x;$y = $current_y + 1;$_SESSION['angle'] = 'down';
break;
case 'West':

$x =$current_x - 1;
$y =$current_y;

$_SESSION['angle'] = 'left'; break; case 'East':$x = $current_x + 1;$y = $current_y;$_SESSION['angle'] = 'right';

break;
}

$result = mysql_query("SELECT walkable FROM map WHERE x =$x AND y = $y");$row = mysql_fetch_array($result); //Is the next tile walkable? if ($row['walkable'] == 1) {
//Update Player's position
mysql_query("UPDATE hero SET x=$x, y=$y WHERE id = 1");

$startX =$x;
$startY =$y;
} else {

$startX =$current_x;
$startY =$current_y;

}

$fieldHeight = 10;$fieldWidth = 10;

//x = 0 = 4
//y = 0 = 4

$sql = "SELECT id, x, y, terrain FROM map WHERE x BETWEEN ".($startX-$fieldWidth)." AND ".($startX+$fieldWidth). " AND y BETWEEN ".($startY-$fieldWidth)." AND ".($startY+$fieldHeight);$result = mysql_query($sql);$map = array();
while($row = mysql_fetch_assoc($result)) {
$map[$row['x']][$row['y']] = array('terrain' =>$row['terrain']);
}

ob_start();

for ($y=$startY-$fieldHeight;$y<$startY+$fieldHeight;$y++) { echo '<tr>'; for ($x=$startX-$fieldWidth;$x<$startX+$fieldWidth;$x++) {

if ($x ==$startX && $y ==$startY) {
echo '<td style="width:32px; height:32px; background-image:url(\'tiles/' . (isset($map[$x][$y]['terrain']) ?$map[$x][$y]['terrain'] : 'water') . '\');"><img src="char/medic_' . $_SESSION['angle'] . '.png" alt="" /></td>'; } else { //echo '(' .$x . ',' . $y . ')'; echo '<td style="width:32px; height:32px; background-image:url(\'tiles/' . (isset($map[$x][$y]['terrain']) ? $map[$x][$y]['terrain'] : 'water') . '\');"></td>'; } } echo '</tr>'; } echo '</table>';$content = ob_get_contents();

ob_end_clean();

echo $content;  • I'll take another look at it in a bit, but maybe this will get you started. Maybe its ob_start(). I don't use it myself, mostly because I do not like the way it lets you have HTML in your PHP. But just the way it works makes me think it adds a lot of overhead. Jun 13 '12 at 17:01 • Not according to: stackoverflow.com/questions/6934762/… Jun 13 '12 at 17:29 • Yes, but string concatenation is different from PHP injection. Which is more or less what you have. Remove the echoes, close the PHP and start directly dumping the HTML, then only go back into PHP for the loops and specific variables you need to dump Jun 13 '12 at 18:00 • I'm taking another look at it now. Hopefully something pops up Jun 13 '12 at 18:01 • Yes unless you come up with something, you are very welcome to develop your answer above. Jun 13 '12 at 18:15 ## 1 Answer Besides what I already mentioned about output buffering, I don't really see anything wrong. And I can't prove output buffering is the culprit. You could try escaping the HTML, what I was calling "PHP injection" (sorry I had the wrong term) to see if that fixes it, but that would require some rewrite. Here's what I mean in case clarification is still needed: ?> <table border="0" cellpadding="0" cellspacing="0"> <? for ($y=$startY-$fieldHeight;$y<$startY+$fieldHeight;$y++) : ?>
<tr>
<? //etc... ?>
</tr>
<? endfor; ?>
</table>


Only other thing I can think of is that maybe its your MySQL queries that are bogging you down. Try wrapping those in microtime()'s and see how long they are taking. If they aren't the culprit, try sprinkling microtime()'s liberally throughout your code to see if you can pinpoint it.

That being said, this is test code as you pointed out. There are bound to be issues like lag and such with test code. Now that it works, even laggy as it is, you should see about making it more permanent. Who knows, while you are solidifying your code maybe something will occur to you. Or miracle of miracles, maybe it will fix itself. Not that we could ever be so lucky. Once its "finalized" if it is still laggy or has bugs, try reposting your question then. Maybe something will be more apparent with cleaner code. Sorry I can't be of more help.

Trivialities

These are some trivialities I would address. Not saying they are your problem, but they definitely contribute. Also not saying you didn't know about them, you'd probably get them while finalizing. Just pointing them out for completeness.

These, and similar, are used quite frequently and could be saved as variables to reduce the need to retype and the minor overhead they cause.

$corrd =$map[$x][$y];
$terrain = isset($coord['terrain']) ? $coord['terrain'] : 'water';$Ymin = $startY-$fieldHeight;


Maybe beyond scope of your project at the moment, but getting away from HTML tables and using external stylized spans and divs might also help. Example using terrain block.

//BEGINCSS
.terrain { height: 35px; width: 35px; }
.water { background-image: url('tiles/water'); }
//ENDCSS

<?php
$coord =$map[$x][$y];
$terrain = isset($coord['terrain']) ? $coord['terrain'] : 'water'; ?> <div class="row"><!-- Replaces TR's --> <span class="terrain <? echo$terrain; ?>"><img src="char/medic_<? echo \$_SESSION['angle']; ?>.png" alt="" /></span><!-- Replaces TD's -->
//more spans
</div>

• Do you think I would gain performance by reconstructing the rendering, let's say I don't re render the tiles unless it's actually needed. And just move the character? Jun 14 '12 at 10:55
• @josmith: I'm not sure, it was just a suggestion. I'm honestly stumped as to why this is running slow. My last suggestion about using microtime() is probably your best bet for pinpointing it. All my suggestions so far are more for fine tuning and probably wont make a "huge" impact. But you never know. Its the development stage, sometimes you try something out and it doesn't work, sometimes it does. I'm not sure what you mean by "Just move the character". BTW: Researched ob_start() last night and you were right, this should not be your problem. Have you tried cleaning up the code yet? Jun 14 '12 at 13:22