Toon posts:

[js] animatie, function queue oid

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik ben bezig met het animeren van een object in js. Dit object heeft verschillende methods, bijvoorbeeld om een sprite door te schuiven, een stapje naar rechts te doen etc. Alle animatie moet gebeuren aan de hand van een timer in het object (er komt ook maar 1 object, maar dat terzijde).

Nu moet het object verschillende acties uit gaan voeren die bestaan uit deel acties, die moeten worden uitgevoerd tot aan een bepaalde voorwaarde is voldaan (ene bepaald aantal clockticks of een bepaalde positie bereikt ofzo). De opzet is globaal als volgt

JavaScript:
1
2
3
4
5
6
7
8
9
10
Sprite.prototype.run = function() {
    var self = this;
    this.running = setInterval(function(){
        self.step();
    },self.speed);
}

Sprite.prototype.step = function() {
   // doe vanalles;
}

in die step function moet ie dus de deelfuncties gaan uitvoeren die op dat moment gedaan moeten worden en als ie daar mee klaar is overspringen op andere deel functies.

Nu zit ik me een beetje af te vragen hoe ik dit het beste kan vormgeven, zelf zat ik te denken aan iets wat dus functies in een queue zet (dat moet nog wel lukken) en dan ook een check functie meegeeft ofzo, iets als:

JavaScript:
1
2
3
4
5
6
7
8
9
10
Sprite.prototype.een_actie = function() {
  this.addToQueue(
    function(){this.moveBy(10,0); this.fadeBy(0.5);}, 
    function(){return this.x >= 100}
  )
  this.addToQueue(
    function(){this.moveBy(-10,0);}, 
    function(){return this.elapsed = 4}
  )
}

hier voeg ik dus functies toe in een queue, vergezeld van functies die moeten checken of de boel klaar is met de deelactie. Voor verschillende acties heb ik dus verschillende van deze methods.

Is dit een handige aanpak? of is het nodeloos ingewikkeld? misschien dat mensen die hier al eens mee gespeeld hebben (clay, crisp? :P) eens een visie over geven?

Verwijderd

Ik zou de functies die in de queue komen voorzien van een interface, dus allemaal voldoen ze aan minimaal een aantal methods en properties. Je laat vervolgens aan die functies over wat er moet gebeuren, het enige wat je queue doet is ze delegeren en op een response van die actie weer een andere actie delegeren :)

function Queue(){
this.state;
this.items = [];
}
Queue.prototype.start = function(){}
Queue.prototype.stop = function(){}
Queue.prototype.pauze = function(){}
Queue.prototype.goto = function(item){}
Queue.prototype.abort = function(){}
Queue.prototype.add = function(){}
Queue.prototype.remove = function(){}
Queue.prototype.listener = function(){}


Waarbij een functie voldoet aan een interface:

function actie(){}
actie.prototype.start = function(){}
actie.prototype.stop = function(){}
actie.prototype.run = function(){}
actie.prototype.evaluate = function(){ // is de actie klaar?}
actie.prototype.Queue = sub process?

De actie zorgt zelf voor zijn eigen staat, of er aan een bepaalde eigenschap is voldaan, en geeft aan de Queue door dat die klaar is. Dus in een actie ga je controleren of de actie klaar is of niet. De voordelen zijn dat je zo een groot aantal acties goed onderhoudbaar maakt zonder aanpassingen aan de queue code.

Je zou zelf een actie kunnen overerven van een abstract actie klasse zodat je alleen de evaluatie en run functies hoeft te maken. Verder voordeel is dat je acties multithreaded kunt laten lopen, omdat ze zichzelf controleren.

Op zich ook een grappig voorbeeld van IoC.

[ Voor 26% gewijzigd door Verwijderd op 23-08-2005 19:14 ]


Verwijderd

Dan krijg je dus uiteindelijk

var myQueue = new Queue();
myQueue.add(geefGas(veel));
myQueue.add(remmen(hard));
myQueue.run();

function Actie(){
this.Queue;
this.running;
}
Actie.prototype.start = function(){
this.running = true;
this.Queue.notify(this);
}
Actie.prototype.stop = function(){
this.running = false;
this.Queue.notify(this);
}

function GeefGas(amount){
Actie.apply(this,argument);
this.amount = amount;
}
GeefGas.prototype = Actie.prototype;
GeefGas.prototype.evaluate = function(){
if(hasWallNearby){
this.Queue.goto(remmen(hard));
}
}
GeefGas.prototype.run = function(){
this.rijden();
}
GeefGas.prototype.rijden = function(){
/etc...
}

  • André
  • Registratie: Maart 2002
  • Laatst online: 14:48

André

Analytics dude

Het nadeel van een queue systeem is denk ik de performance. Als er 1 actie heel veel tijd in beslag neemt (grafische update) zal dat de rest vertragen. En ik denk dat de snelheid van een setInterval wat theoretisch niet lager is dan 20ms wel eens te weinig zou kunnen zijn om alles soepel te laten lopen.

Mij lijkt het eenvoudiger om voor elke actie een aparte thread te maken met zijn eigen interval. Zo kunnen ze elkaar ook niet direct vertragen.

[ Voor 3% gewijzigd door André op 23-08-2005 23:19 ]


Verwijderd

Dat valt reuze mee, ik heb zelfs wat proof of concept liggen qua multithreading en hierbij worden de berekeningen in een thread gedaan, en de andere zorgt voor de animaties. Zo krijg je per tick van setInterval dus niet berekeningen+animatie maar om en om berekenen en animatie. De cpu hit gaat omlaag, en de snelheid gaat omhoog. :)

  • André
  • Registratie: Maart 2002
  • Laatst online: 14:48

André

Analytics dude

Sowieso moeten idd berekeningen en animatie gesplitst worden :)

  • djluc
  • Registratie: Oktober 2002
  • Laatst online: 15:03
Hoe kan je dan multi-threaded met JavaScript? Doe je dan gewoon 2 tiks van 1 seconde in plaats van 1 grote tik van 2 seconden?

Verwijderd

Wat je normaal hebt is bijvoorbeeld dit

window.setInterval("actie()",30);
function actie(){
// stukje berekeningen
// stukje animatie
}

Dat veroorzaakt om de 30ms een klap op je cpu. Ineens heel veel werk verzetten.

Als we dit nu onderverdelen in:

window.setInterval("berekening()",30);
window.setInterval("actie()",20);

function berekening(){
// stukje berekeningen
}
function actie(){
// stukje actie
}

Dan wordt om en om de cpu belast, overall een lagere belasting, en een vloeiende animatie. Je kunt zelfs meer threads aanmaken, maar zoals met multithreading moet je de middenweg zoeken mbt voordelen en nadelen.

Even een voorbeeld van wat het resultaat ong. is. Wat je wel krijgt is dat er vaker wordt geanimeerd, maar dit levert niet zoveel load op. Het kan dus zijn dat een animatie 2x wordt uitgevoerd op dezelfde berekening (de berekening loopt trager), maar hiermee weet de browser wel raad. Als je het andersom zou doen, dus trager animeren dan de berekening loopt, dan loop je het risico dat je geen vloeiende animatie krijgt omdat deze ineens vreemde stappen gaat nemen.


Afbeeldingslocatie: http://www.mschopman.demon.nl/multithreading.gif

[ Voor 24% gewijzigd door Verwijderd op 24-08-2005 09:54 ]


Verwijderd

Topicstarter
in mijn geval zijn de berekeingne echt peanuts, ik werk de hele tijd met 1 sprite en de hele berekening is dat ie z'n positie met een dx moet incrementen en dat tekenen. Ik denk niet dat dit performance problemen op gaat leveren. Als ik nou een raar pad zou moeten volgen met veel sinussen en cosinussen enzo (en het is niet goed mogelijk vooraf een sinustabel aan te leggen), dan lijkt me een bovenstaande aanpak wel makkelijk, maar nu lijkt het me een beetje overdone.

Ik hoef alleen maar een paar acties in een wachtrij te plaatsen die worden uitgevoerd als de ander klaar is. Binnenkort maar eens gaan stoeien met deze ideeen.
Het nadeel van een queue systeem is denk ik de performance. Als er 1 actie heel veel tijd in beslag neemt (grafische update) zal dat de rest vertragen.
de bedoeling is dus dat de acties na elkaar moeten worden uitgevoerd, dus als er 1 traag is, so be it, maar de volgende mag toch niet beginnen totdat de eerste klaar is.

[ Voor 22% gewijzigd door Verwijderd op 24-08-2005 10:33 ]


  • djluc
  • Registratie: Oktober 2002
  • Laatst online: 15:03
Bedankt voor de uitleg Gordijnstok, dat bedoelde ik inderdaad. Ook voor zaken als Ajax e.d. kan dit handig zijn.

Verwijderd

Topicstarter
wat ik nu heb gedaan is het volgende

een Task class
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
function Task(owner, action, canceller) {
    this.owner = owner;
    this.action = action;
    this.canceller = canceller;
}

Task.prototype.run = function() {
    this.action.apply(this.owner);
}

Task.prototype.finished = function() {
    return this.canceller.apply(this.owner);
}


en m'n sprite een queue gegeven en methods om Task objects in die queue te proppen:
JavaScript:
1
2
3
4
5
6
7
8
Sprite.prototype.addToQueue = function(action, canceller) {                     // add an action to the queue
    var sTask = new Task(this, action, canceller);
    this.queue[this.queue.length] = sTask;
}

Sprite.prototype.clearQueue = function() {                                      // clear the queue
    this.queue.length = 0;
}


tijdens de animatie roep ik elk interval een step() method aan:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
Sprite.prototype.step = function() {                                            // perform one step in animation
    if (this.currentAction == this.queue.length) {
        this.stop();
        this.currentAction = 0;
    } else {
        this.ticksElapsed++;
        this.queue[this.currentAction].run();
        if (this.queue[this.currentAction].finished()) {
            this.ticksElapsed = 0;
            this.currentAction++;
        }
    }
}

die doet dus de action en kijkt of aan de stopvoorwaarde is voldaan. Ondertussen telt ie nog het eea.

een concrete actie ziet er dus als volgt uit:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Sprite.prototype.hopTo = function(x,y) {
    this.clearQueue();
    this.addToQueue(
        function() {this.toStrip(2); this.incFrame(); this.moveBy(5,10);},
        function() {return (this.ticksElapsed == 5)}
    )
    this.addToQueue(
        function() {this.toStrip(1); this.incFrame(); this.moveBy(10,0);},
        function() {return (this.ticksElapsed == 10)}
    )
    this.addToQueue(
        function() {this.toStrip(0); this.incFrame(); this.moveBy(5,-10);},
        function() {return (this.ticksElapsed == 5)}
    )
}
Pagina: 1