canvas en tile-images

Status
Niet open voor verdere reacties.

That Guy

Meubilair
Lid geworden
28 nov 2006
Berichten
5.010
Yo,

ik was even gaan *****n met de (html5-tag) canvas en javascript. Heel leuk allemaal.

Nu is het geval dit: ik was wat testjes aan het draaien met tiles (shocker :p), en het viel me op dat na een bepaald aantal [keer|seconde] het steeds langzamer gaat...



{WAARSCHUWING: het kan je pc's cpu opzuigen, hier gaat ie tot iets van 50%, heel erg trage pc's, klik niet}
Kijk eens hier: [ http://void-studios.com/projects/bin/glory/ ]. Je kan bewegen met de pijltjes-toetsen.

Page-refresh eens (F5), en beweeg dan eens 3 of 4 keer naar rechts of onderen, meteen als je de map ziet. Als het goed is, zie je het beeld (flikkerend, dat wel) bewegen.

Als je echter een paar seconde (7 ofzo) wacht nadat de pagina geladen is, zal je zien dat het bewegen steeds langzamer gaat, en je de tiles daadwerkelijk 'ziet' opbouwen.


De map die je ziet is een json-opgebouwd bestand. Plaatjes staan als base64 opgeslagen in de 'images' rij. Zie [ http://void-studios.com/projects/bin/glory/games/littleroot.json ]
Deze wordt geladen, geparsed door een json-parser, en dan wordt het gehele object opgeslagen in een array:
PHP:
jtile_maps[jtile_maps.length] = json_parse(include(path));
Dit werkt correct. Als alles geladen is, wordt er een 'setmap(id)' functie aangeroepen om de map te laten zien:
PHP:
setmap(map1);   //map1 is het id van de jtile_maps[] array, in dit test-geval 0.
De setmap(id) functie ziet er zo uit:
PHP:
   var mapdata = jtile_maps[id];
   for(j=0;j<SCREEN_WIDTH/16;j++){
      for(i=0;i<SCREEN_WIDTH/16;i++){
         drawBase64Image(mapdata.images[mapdata.map[j+playerY][i+playerX].image],i*16,j*16);
      }
   }
Met wat testjes ben ik erachter gekomen dat dit allemaal correct werkt.


De drawBase64 functie ziet er ongeveer zo uit:
PHP:
//... stuff
   c = document.getElementById('mijn_canvas').canvas.getContext('2d');

//... stuff
drawBase64 = function(data,x,y)
{
   var img = new Image();
   img.src = "data:image/png;base64," + data;
   img.onload = function(){
      c.drawImage(img,x,y);      //c is hier de canvas-handle.
   }
}
Om het probleem wat duidelijker te maken wordt VOOR elke drawmap(id)-aanroep, eerst de clearRect() aangeroepen, wat het scherm dus weer leeg (wit) maakt.




Mijn vraag dus (eindelijk): wie weet er waarom het steeds trager wordt? Komt dit door de grote hoeveelheid 'bla = new Image();' calls die het script maakt? Of is het iets anders? Iemand een oplossing?


:thumb:
 
Laatst bewerkt:
De link die er nu staat werkt bij mij zonder langzamer te gaan draaien. Maar het klinkt als een memory leak.

Dit maakt elke keer een anonieme functie aan:
Code:
drawBase64 = function(data,x,y)
{
   var img = new Image();
   img.src = "data:image/png;base64," + data;
[COLOR="Red"]   img.onload = function()
   {
      c.drawImage(img,x,y);      //c is hier de canvas-handle.
   }[/COLOR]
}

De anonieme functie heeft toegang tot alle variabele die al gedefinieerd waren, inclusief img. Vanwege de anonieme functie moet een refrence naar img blijven bestaan en img heeft een refrence naar de anonieme functie. Een refrence count voor beide objecten zal minimaal 1 zijn, dus kunnen ze beide niet opgeruimd worden.

Je kunt één img object aanmaken en de .src steeds verversen of je kunt een niet anonieme functie gebruiken. Nadeel is dan dat je gebruik moet gaan maken van element.target / element.currentTarget / element.srcElement. Met één img object kun je gerust anonieme functies aan blijven maken omdat je de refrence naar de anonieme functie opheft wanneer je een nieuwe anonieme functie aanmaakt.

Tenminste, ik denk dit dit nog wel eens het probleem zou kunnen zijn. Memory leaks zijn lastig op te sporen.
:thumb:
 
Laatst bewerkt:
yo Glest,

De link die er nu staat werkt bij mij zonder langzamer te gaan draaien.
dat komt omdat het nu weer 16x10 <img>'s zijn, geen canvas dus.


Het probleem is dat ik de img.onload = function... moet gebruiken, omdat het image eerst moet laden, voordat het op het canvas 'geblit' kan worden. Als ik maar één groot image gebruik en die steeds laat zien gaat het wel met normale snelheid, dus dat zegt genoeg.

Hoe bedoel je dit:
Je kunt één img object aanmaken en de .src steeds verversen
? Want, dat image-element moet het eerst nog laden, toch, zodat het overschreven wordt voordat het wat kan, en dan krijg je dus een map met 1 tile steeds herhaald (de laaste uit de map[][] array). Denk ik. Ik ga even wat proberen.

Met één img object kun je gerust anonieme functies aan blijven maken omdat je de refrence naar de anonieme functie opheft wanneer je een nieuwe anonieme functie aanmaakt.
Dat is dan toch niet zo - zie bovenstaande alina.


Verder ben ik niet echt op de hoogte wat je hiermee bedoelt:
Nadeel is dan dat je gebruik moet gaan maken van element.target / element.currentTarget / element.srcElement.
.



alvast bedankt.
 
Laatst bewerkt:
{Pas op, extreem traag! Eet CPU als een idioot.}
ohja, met canvas staat hier: http://void-studios.com/projects/bin/glory/index2.php

Als je op 'pause/break' op je toetsenbord drukt, komt er een console tevoorschijn. Als je dan met je pijltjes-toetsen beweegt, krijg je de drawBase64-coors te zien. Met 'pause/break' gaat ie ook weer weg.


De functie ziet er nu zo uit:
PHP:
//c = handle


drawBase64 = function(x,y,data)
{

   var img = new Image();
   img.src = data;
   img.onload = function()
   {
      c.drawImage(img,x*16,y*16);
      output('loaded image ' + x + ',' + y);
   }
}
 
Laatst bewerkt:
Hmm, ik denk dat dit niet zozeer door een memory leak komt maar door de inefficiëntie van de browser met óf het maken van de image bestanden óf het tekenen op de canvas.

Ik zou eens proberen om je array van data aan het begin één keer te vertalen in een array van plaatjes (image objecten) en vervolgens de nieuwe array gebruiken. Daarna kun je, om geheugen te sparen, de oude array legen. Waarschijnlijk is het het steeds opnieuw maken van de plaatjes wat CPU eet.

Dan hoef je ook meteen niet meer het image.onload event te gebruiken, dus ook geen anonyme functie en geen mogelijkheid tot een memory leak (daar niet iig :p ). En het elimineert de overhead van asynchroon programmeren.

:)
 
Laatst bewerkt:
ik heb wat interesante resultaten...

Heb even, als test, dit gedaan:
PHP:
var preMap = [];
var all_maps_array = [];


//loadMap(), wordt 1x aangeroepen.
loadmap(file)
{
   var data = parseFile(loadFile(file));
   //doe wat dingen
   all_maps_array[all_maps_array.length] = data;

   //maak een array aan van plaatjes. Ofwel, ALLE plaatjes die op de map te vinden zijn
   //hebben in het mapfile een id.
   loop(aantalPlaatjes times @ i){
      preMap[i] = new Image();
      preMap[i].src = data; //data is de base64-data-string, plaatje dus.
   }

   //extra: i+1 = een zwarte tile.
   preMap[preMap.length] = new Image(); src = "BLACK_DATA";
}


//setMap() wordt elke keer aangeroepen als je een 'frame' laat zien (dus hebt bewogen)
setmap(id)
{
   var data = all_maps_array[id];

   loop(screen_width times @ i){
      loop(screen_height times @ j){
         //check of het plaatje bestaat, anders maak je m zwart (void)
         if(not_exist_tile(i,j){ id=preMap.length; }

         id = data[j+playerY][i+playerX].image; //het ID van het plaatje, komt overeen met de
         //id's uit de preMap lijst

         output('at ' + i + ',' + j + ' the imageID is ' + id);
         c.drawImage(preMap[id],i*16,j*16);
       }
   }
}
Dit werkt, maar... er zit een ongelovelijke delay tussen. De browser (ff3 bij mij) loopt vast, kan ook geen toolbar-knoppies meer drukken.
Je moet even wachten tot de mapfile is geladen. Het scherm blijft wit, totdat je beweegt (pijltjestoetsen).

Kijk zelf maar eens hier: [ http://bin.void-studios.com/glorycanvas/ ]. {WARNING: weer erg traag, CPU-etend}
Zoals je ziet duurt het eerst een paar seconde (tenminste, hier), en daarna wordt alles in 1x geblit. Het is dus een verbetering, maar deels.

Je kan het console weer toggle-n met 'pause/break'. Het jsbestand waar het gebeurt is map.js, hier: [ http://bin.void-studios.com/glorycanvas/engine/map.js ] zonder pseudocode. Ohja, let niet op de vreselijk lelijke code :p.



enig idee hoe dit kan?
 
Laatst bewerkt:
hmm het is vreemd dat browsers zo traag worden van een paar 16*16 png plaatjes. Je zou nog kunnen experimenteren met verschillende bestandstypes maar dat zal wel niet de echte boosdoener zijn.

Is het overigens geen optie om de plaatjes te pre-loaden van internet? Wie weet is het data: scheme wel erg slecht ge-implementeert in veel browsers. Je kunt de plaatjes van internet laden en in een array opslaan in plaats van genereren uit een string. Veel minder interessant natuurlijk, maar wie weet scheelt het behoorlijk wat CPU.

Ik zal zelf ook nog wat testjes draaien (ben zelf ook geïnteresseerd :p ) als ik de tijd heb. Je vragen hier zijn een fijne afwisseling van de gebruikelijke beginnersvragen ;)
 
Je vragen hier zijn een fijne afwisseling van de gebruikelijke beginnersvragen
hehe, tsja. Vind experimenteren met js altijd wel leuk.

Overgens, ik had ook al wat gerunt. Check bv. eens [ http://bin.void-studios.com/glorycanvas/res.other/tests/tilespeed2.html ]. Dat's best snel, en het is via de anonieme onload, maar het zijn natuurlijk maar 2 verschillende plaatjes. Maargoed.

Het ligt dus niet aan het canvas-element, tenminste, blijkbaar niet (?). Mischien is het zoals je zei het data-uri-scheme. Overgens zijn er best wel wat grappige dingen met dat canvas gedaan, zoals een '3d' shooter (raycast meen ik) [bv deze [ http://www.nihilogic.dk/labs/wolf/ ]] - vandaar dat ik het zo raar vind dat wat images blitten zo lang duurt :p


:thumb:


[edit]
chromefirefoxtilespeed.png
[/edit]


[edit]
cpujtile41.png

Nu moet ik wel zeggen dat mijn CPU niet meer het nieuwste van het nieuwste is (IP4) maargoed, het gaat om de verschillen.
Heeft het iets te maken met de hoeveelheid tile-images? Ik ga vanavond even de testjes runnen op een snellere pc (quadcore bla bla geval, jeweetwel).[/edit]​



damn
 
Laatst bewerkt:
Hehe, oh my.

Ik ben er achter waarom het zo traag is. En eerlijkgezegd is het nogal triest.

Dit was de setmap functie:
PHP:
//js
jTileEngine.prototype.setmap = function(id)
{

   output("<span style='color: #f00;'>Call: setMap(" + id + "); </span><br />");

   mapdata = jTileMaps[id];


   for(var x=0;x<15;x++){
      for(var y=0;y<10;y++){
         if(!mapdata.map[y+playerY] || !mapdata.map[x+playerX] || !mapdata.map[y+playerY][x+playerX]){
            var id = preMap.length;
         }else{
            var id = mapdata.map[y+playerY][x+playerX].image;
         }
            output('at ' + x + ',' + y + ' the imageID is ' + id);
            c.drawImage(preMap[id],x*16,y*16);
      }
    }

}
(let niet op de baggercode)

Nu, ik zat dus even te klooien. Vanmiddag bedacht ik me, dat ik maar eens de output() ging weghalen, en ergens anders neerzetten. Dus, ik haal deze regel weg (regel 16 hierrboven):
[PHP//jk
output('at ' + x + ',' + y + ' the imageID is ' + id);[/PHP]
en voila, een snelle engine. Zie op http://bin.void-studios.com/glorycanvas/


Droogheid. Heb nogsteeds geen idee waarom dat allemaal zo lang duurt. Output doet simpelweg dit:
PHP:
//js
output = function(str)
{
   document.getElementById('console_out').innerHTML = str + '<br />' + document.getElementById('console_out').innerHTML;
}
trouwens. Maargoed, dit is opgelost.


:thumb:
 
Laatst bewerkt:
Hmm, dat is vreemd. Het zou wel sneller zijn om één keer document.getElementById('console_out') te roepen en de refrence te bewaren, maar dat kan het niet zijn. De browser moet wel steeds opnieuw de HTML van output parsen maar zo veel werk is dat nou ook weer niet...

Maargoed, anders de output tijdelijk in een string opslaan en aan het einde van de loops in één keer output()en.

Hoe dan ook, vreemd, maar mooi dat het niet aan de canvas ligt :)
 
Status
Niet open voor verdere reacties.
Steun Ons

Nieuwste berichten

Terug
Bovenaan Onderaan