PDA

View Full Version : projector performance varies between platforms


sparky_hd
10-13-2006, 09:39 PM
i have a projector that loads a swf tile-based game. The game is time-based so it's important that the game has consistant performance across platforms.

If i use the speed of the game played through Flash 8 Player in the authoring environment as a reference, i find that the game played through the PC projector is about 1.5 times faster, and the game through the Mac projector is almost 2 times slower.

What could be causing that and how could i address it?

i'm using a modification of the Game.as class from http://www.flashierflash.com/

class Game {
var grid:Array, columns:Number, rows:Number, grid_clip:MovieClip, spacing:Number;
var defaultFrame:Number, startingPositions:Array, myCharacter:MovieClip, speed:Number;
var downY:Number, upY:Number, leftX:Number, rightX:Number;
var mapNum:Number, setNum:Number, doorTile:Tile, doorTileCol:Number, doorTileRow:Number;
var gameXML:XML;
function Game() {
}
function setGridClip(tmp_clip:MovieClip) {
grid_clip = tmp_clip;
}
function getGridClip():MovieClip {
return grid_clip;
}
function setSpacing(num:Number) {
spacing = num;
}
function getSpacing():Number {
return spacing;
}
function fromXML(info:XML) {
//store the game XML data the first time game is run FOR DIFFICULTY LEVEL ONLY
if (gameXML == undefined){
gameXML = info;
//gameXML = info.firstChild.childNodes[getSetNumber()];
//trace(gameXML);
}
//var rootNodes:Array = info.firstChild.childNodes;
var rootNodes:Array = info.firstChild.childNodes[getSetNumber()].childNodes[getMapNumber()].childNodes;
var spNode:XMLNode;
var epNode:XMLNode;
for (var i = 0; i<rootNodes.length; ++i) {
var tmp_node:XMLNode = rootNodes[i];
if (tmp_node.nodeName == "Header") {
parseHeader(tmp_node);
} else if (tmp_node.nodeName == "Map") {
createGrid(tmp_node);
} else if (tmp_node.nodeName == "StartingPositions") {
spNode = tmp_node;
} else if (tmp_node.nodeName == "EndingPosition") {
epNode = tmp_node;
}
}
parseStartingPositions(spNode);
parseEndingPosition(epNode);
}
function parseStartingPositions(info:XMLNode) {
var children:Array = info.childNodes;
for (var i = 0; i<children.length; ++i) {
var child:XMLNode = children[i];
var atts:Object = child.attributes;
var x:Number = Number(atts.x);
var y:Number = Number(atts.y);
speed = Number (atts.speed);
var tmp_tile:Tile = getTile(x, y);
//trace("tmp_tile: "+tmp_tile); // tmp_tile: [object Object]
addStartingPosition(tmp_tile);
}
}
function parseEndingPosition(info:XMLNode) {
var atts:Object = info.attributes;
var x:Number = Number(atts.x);
var y:Number = Number(atts.y);
var tmp_tile:Tile = getTile(x, y);
doorTileCol = x;
doorTileRow = y;
addEndingPosition(tmp_tile);
/*var children:Array = info.childNodes;
for (var i = 0; i<children.length; ++i) {
var child:XMLNode = children[i];
var atts:Object = child.attributes;
var x:Number = Number(atts.x);
var y:Number = Number(atts.y);
speed = Number (atts.speed);
var tmp_tile:Tile = getTile(x, y);
addStartingPosition(tmp_tile);
}*/
}
function moveCharacter(x_move, y_move){
var door_x = Math.floor(getCharacter()._x/getSpacing());
var door_y = Math.floor(getCharacter()._y/getSpacing());
var Char_x = myCharacter._x+x_move*speed;
var Char_y = myCharacter._y+y_move*speed;
//var Char_h = (myCharacter._height+1)/2;
var Char_h = (myCharacter.hit_mc._height+1)/2;
setMyCorners(Char_x, Char_y);
if (y_move == -1) {
if (getTile(leftX, upY).getIsWalkable().toString() == "true" && getTile(rightX, upY).getIsWalkable().toString() == "true") {
myCharacter._y += y_move*speed;
} else {
myCharacter._y = ((upY+1)*spacing)+Char_h;
}
}
if (y_move == 1) {
if (getTile(leftX, downY).getIsWalkable().toString() == "true" && getTile(rightX, downY).getIsWalkable().toString() == "true") {
myCharacter._y += y_move*speed;
} else {
myCharacter._y = (downY*spacing)-Char_h;
}
}
if (x_move == -1) {
if (getTile(leftX, upY).getIsWalkable().toString() == "true" && getTile(leftX, downY).getIsWalkable().toString() == "true") {
myCharacter._x += x_move*speed;
} else {
myCharacter._x = ((leftX+1)*spacing)+Char_h;
}
}
if (x_move == 1) {
if (getTile(rightX, upY).getIsWalkable().toString() == "true" && getTile(rightX, downY).getIsWalkable().toString() == "true") {
myCharacter._x += x_move*speed;
} else {
myCharacter._x = (rightX*spacing)-Char_h;
}
}
//
//if(getTile(door_y,door_x).getFrame() == "Doors"){
if(door_y == doorTileRow && door_x == doorTileCol){
trace("door detected");
changeMap();
}
//
setCharacter(myCharacter);
myCharacter.gotoAndStop(x_move+y_move*2+3);
}
function setMyCorners(x, y) {
/*downY = Math.floor((y+(myCharacter._height/2))/spacing);
upY = Math.floor((y-(myCharacter._height/2))/spacing);
leftX = Math.floor((x-(myCharacter._width/2))/spacing);
rightX = Math.floor((x+(myCharacter._width/2))/spacing);*/
downY = Math.floor((y+(myCharacter.hit_mc._height/2))/spacing);
upY = Math.floor((y-(myCharacter.hit_mc._height/2))/spacing);
leftX = Math.floor((x-(myCharacter.hit_mc._width/2))/spacing);
rightX = Math.floor((x+(myCharacter.hit_mc._width/2))/spacing);
}
function getMyCorners():Number {
return downY, upY, leftX, rightX;
}
function changeMap(){
trace("changeMap");
if(getMapNumber() < 2){
// set new map
setMapNumber(getMapNumber()+1);
fromXML(gameXML);
getGridClip()._parent.skylarFeedback(getMapNumber( ));
//getCharacter()._x = getSpacing()*1+5;
}else{
// game is over
//trace("you won ... "+_parent); // you won ... undefined
//trace("you won ... "+getGridClip()); // you won ... _level0.grid_clip
getGridClip()._parent.gotoAndPlay("endAnim");
//setMapNumber(1);
//getCharacter()._x = getSpacing()*7-5;//200;
}
}
function setMapNumber(tmp:Number){
if (tmp == undefined) {//|| tmp == undefined){
mapNum = 0;
//trace("mapNum "+mapNum);
}else{
mapNum = tmp;
//trace("mapNum "+mapNum);
}
}
function getMapNumber():Number{
return mapNum;
}
function setSetNumber(tmp:Number){
if (tmp == undefined) {
setNum = 0;
//trace("setNum "+setNum);
}else{
setNum = tmp;
//trace("setNum "+setNum);
}
}
function getSetNumber():Number{
return setNum;
}
function addStartingPosition(tmp_tile:Tile) {
if (getGridClip().char_holder.name == undefined){
trace("create character");
var depth:Number = getGridClip().char_holder.getNextHighestDepth();
var clip:MovieClip = getGridClip().char_holder.attachMovie("character", "name", depth);
clip._x = tmp_tile.getClip()._x+getSpacing()/2;
clip._y = tmp_tile.getClip()._y+getSpacing()/2;
setCharacter(clip);
} else{
trace("reposition character instead");
getCharacter()._x = tmp_tile.getClip()._x+getSpacing()/2;
getCharacter()._y = tmp_tile.getClip()._y+getSpacing()/2;
}
}
function addEndingPosition(tmp_tile:Tile) {
//trace("addEndingPosition: "+tmp_tile); // addEndingPosition: [object Object]
//trace(getGridClip().char_holder);
//trace(getGridClip().test_holder);
if (getGridClip().char_holder.end == undefined){
trace("create doorway");
var depth:Number = getGridClip().char_holder.getNextHighestDepth();
//trace(getGridClip().char_holder);
var clip:MovieClip = getGridClip().char_holder.attachMovie("doorway", "end", depth);
clip._x = tmp_tile.getClip()._x+getSpacing()/2;
//trace("clip._x: "+clip._x); //162
clip._y = tmp_tile.getClip()._y+getSpacing()/2;
//trace("clip._y: "+clip._y); //18
//setDoor(tmp_tile);
//setCharacter(clip);
} else {
trace("reposition doorway");
getGridClip().char_holder.end._x = tmp_tile.getClip()._x+getSpacing()/2;
getGridClip().char_holder.end._y = tmp_tile.getClip()._y+getSpacing()/2;
}
}
function getDefaultFrame():Number {
return defaultFrame;
}
function setDefaultFrame(frame:Number) {
defaultFrame = frame;
for (var i = 0; i<getColumns(); ++i) {
for (var j = 0; j<getRows(); ++j) {
getTile(i, j).changeDefaultFrame(frame);
}
}
}
function parseHeader(info:XMLNode) {
var atts:Object = info.attributes;
setColumns(Number(atts.columns));
setRows(Number(atts.rows));
}
function createGrid(info:XMLNode) {
clearGrid();
setDefaultFrame(info.attributes.defaultFrame);
var column_nodes:Array = info.childNodes;
var th:MovieClip = getGridClip().tile_holder;
for (var i = 0; i<column_nodes.length; ++i) {
var tiles:Array = column_nodes[i].childNodes;
for (var j = 0; j<tiles.length; ++j) {
var depth:Number = th.getNextHighestDepth();
var tmp_tile_clip:MovieClip = th.attachMovie("tile", "tile"+depth.toString(), depth);
tmp_tile_clip._x = i*getSpacing();
tmp_tile_clip._y = j*getSpacing();
var tmp_tile:Tile = getTile(i, j);
tmp_tile.setClip(tmp_tile_clip);
tmp_tile.setDefaultFrame(getDefaultFrame());
tmp_tile.fromXML(tiles[j]);
}
}
}
function getTile(column:Number, row:Number):Tile {
return grid[column][row];
}
function clearGrid() {
grid = new Array();
for (var i = 0; i<getColumns(); ++i) {
for (var j = 0; j<getRows(); ++j) {
if (j == 0) {
grid[i] = new Array();
}
var tmp_tile:Tile = new Tile();
grid[i][j] = tmp_tile;
}
}
}
function setColumns(num:Number) {
columns = num;
}
function getColumns():Number {
return columns;
}
function setRows(num:Number) {
rows = num;
}
function getRows():Number {
return rows;
}
function setCharacter(clip:MovieClip) {
myCharacter = clip;
}
function getCharacter():MovieClip {
return myCharacter;
}
}

sparky_hd
10-16-2006, 05:57 PM
well here's a new clue: fullscreen is involved.

i had the app running on the Mac, hit ESC to go to window-view, and performance was the same. But when i reloaded the game swf in the projector (by hitting a menu button) suddenly the game was working just fine!

So is there something that can be done regarding performance on a mac with fullscreen (with noscale) projectors?

blockage
10-16-2006, 06:17 PM
Yup flash on a Mac is slower - have you though about old PCs? they will be slow too - nothing you can do about any of it.

The thing to do is make sure the timer in your game isn't just a frame counter (that would be to easy wouldn't it?) - to make your game robust you need to use getTimer() or setInterval() for anything that is time critical.

flashead
10-16-2006, 06:24 PM
Yup flash on a Mac is slower - have you though about old PCs? they will be slow too - nothing you can do about any of it.

The thing to do is make sure the timer in your game isn't just a frame counter (that would be to easy wouldn't it?) - to make your game robust you need to use getTimer() or setInterval() for anything that is time critical.

although, if you DID use a frame-loop timer, at least the timer would slow down proportionatly to the rest of the game. so users on slow machines wouldn't run out of time before they had a chance to do anything. i built a Memory game a few months back that used a getTimer timer and had a lot of timeline animation in it. on my old Mac all the timline animation slowed things down so much that user input was either delayed or not even recognized, meanwhile the clock counted down in realtime. so it essentially gave the users on slower computers less of chance to win.

so i guess all i'm saying is don't make the same mistake i did. i had code that was dependant on the completion of a timeline animation. i guess as long as all your code runs independantly of the framerate (not dependant on timeline animations, all code is run through setIntervals etc.) you'll be ok. like anything else in programming it just comes down to consistancy.

k.

sparky_hd
10-17-2006, 04:18 PM
You know, when i first started making this game i used a setInterval() but because of the performance differences i switched it to a frame counter, following a similar train of through to flashead.

//set up "timer" - simulated seconds but based on system performance
counter_mc.count = 0;
counter_mc.onEnterFrame = function(){
this.count++;
//trace("count: "+this.count);
this.counter_txt.text = Math.floor(this.count/24); //24 fps for framerate of movie
}

But that's the weird part- on the Mac, it seems like the counter is still going at second intervals while the character is moving soooo slooowly. If the whole game was chugging, wouldn't the timer be slow as well? And why is the fullscreen related somehow? i'm baffled.

Here's the keylistener code if it sheds any light:

var keyListener:Object = new Object();
keyListener.onKeyDown = function() {
trace("keyListener.onKeyDown");
//car_idle.wav
if (truckIsPlaying==false){
truck_sound.start();
truckIsPlaying=true;
}
if (Key.isDown(Key.RIGHT) || Key.isDown(68)) {
myGame.moveCharacter(1, 0);
} else if (Key.isDown(Key.LEFT) || Key.isDown(65)) {
myGame.moveCharacter(-1, 0);
} else if (Key.isDown(Key.UP) || Key.isDown(87)) {
myGame.moveCharacter(0, -1);
} else if (Key.isDown(Key.DOWN) || Key.isDown(83)) {
myGame.moveCharacter(0, 1);
}
};
keyListener.onKeyUp = function(){
trace("keyListener.onKeyUp");
truck_sound.stop();
truckIsPlaying=false;
}

blockage
10-17-2006, 04:26 PM
Could be the magic framerate bug. Mac have trouble with soem framerates - try setting your frame rate to 31 as see if it helps.

More info about this one here...
http://www.brajeshwar.com/archives/2004/08/magic_framerate/

sparky_hd
10-17-2006, 06:16 PM
still toying around, but i tried 21 and 31 fps - didn't seem to have an effect. To add to my results- i ran some tests and it looks like the Mac version is almost exactly twice as slow. it takes an average of 60 seconds to play game on PC, and 120 seconds on Mac.

This is weird, though- remember how hitting ESC in the original projector (which cancelled fullscreen) would make the game play faster the next time it was loaded? Well i tried exporting the game itself as a projector, at three different framerates and in both fullscreen and window modes. This time there seemed to be no difference in performance between fullscreen and window mode. Why such a marked difference when the game is being loaded by an app and quitting fullscreen, when there is no improvement at all when it's just the game itself as a projector in wondow mode compared to fullscreen? Is it possible that ESC is doing something in addition to cancelling fullscreen?

sparky_hd
10-17-2006, 10:53 PM
OK- i's fixed. When i realized that keyListener.onKeyDown was triggering on the Mac about half as frequently as on the PC, i realized that is the issue. i assumed it would happen every frame, but no. i moved the code to a function and called it from an onEnterFrame instead. Tt all works now, and consistantly across platforms! Hope this helps someone, and thanks to blockage and flashead for their suggestions.

function detectKeys(){
//trace("detectKeys");
var keyPressed:Boolean = false;
if (Key.isDown(Key.RIGHT)) {
keyPressed=true;
myGame.moveCharacter(1, 0);
} else if (Key.isDown(Key.LEFT)) {
keyPressed=true;
myGame.moveCharacter(-1, 0);
} else if (Key.isDown(Key.UP)) {
keyPressed=true;
myGame.moveCharacter(0, -1);
} else if (Key.isDown(Key.DOWN)) {
keyPressed=true;
myGame.moveCharacter(0, 1);
}
if (!keyPressed && truckIsPlaying) {
// if key is up and truck sound is going, turn it off
truck_sound.stop();
truckIsPlaying=false;
} else if (keyPressed && !truckIsPlaying) {
//if key is down and truck sound is not going, turn it on
truck_sound.start();
truckIsPlaying=true;
}
}