ActionScript 3 Tutorial: Atomen animeren

Posted at January 12, 2011

You’ve found a dinosaur. This is one of my first posts about programming and the code quality is very poor, it is only here for archiving purposes.

in deze tutorial ga ik uitleggen hoe ik de atomen uit mijn laatste opdracht voor het vak programmeren (in AS3) heb gemaakt. Het niveau van deze tutorial is niet heel hoog zodat het voor een stuk meer mensen toegankelijk is.

Eerst ga ik een class Bal maken met nogal simpele code, ik richt de class zo in dat wanneer ik een bolletje nodig heb ik deze zo kan importeren. Ik werk met hierbij met zogeheten arguments/parameters.

Arguments/parameters De makkelijkste manier om dit uit te leggen is om een situatie te schetsen. Stel je voor dat je een huisrobot programmeert om boodschappen voor je te doen: Je programmeert dat hij naar de supermarkt moet lopen, hoe hij met betalen en hoe hij terug moet lopen. Nu zijn deze dingen altijd hetzelfde, alleen moet je elke keer iets anders hebben van de supermarkt. Het makkelijkste zou zijn om hem naar de supermarkt te sturen met een boodschappenlijstje, dit is precies wat een argument is. De syntax ziet er als volgt uit:

voerFunctieUit(argument1, argument2, etc);

(dit is dus in principe je booschappenlijstje)

Hier is de code van de class Bal:

/** Bal.as -- Jan 6, 2011
 *
 * Beschrijving: [beschrijving van dit bestand]
 * 
 * @author Mike van Rossum
 *
 * Copyleft 2011, all wrongs reversed.
 */
package
{
    import flash.display.Sprite;

// Importeer benodigde classes
public class Bal
{
    public var cirkel:Sprite;   
    // Constructor functie
    public function Bal(kleur:uint, grootte:int)
    {
        cirkel = new Sprite;
        cirkel.graphics.beginFill(kleur);
        cirkel.graphics.drawCircle(0,0,grootte);
        cirkel.graphics.endFill();
    }
    // Eigen functies
}

Zoals je ziet zijn er twee argumenten, kleur en grootte. Elke keer als je een Bal wilt maken kan je meegeven welke kleur en hoe groot hij moet zijn. Op deze manier hoef je niet elke keer alle code te kopiëren en de kleur en grootte aan te passen.

En dan nu de Classe voor de atomen, ik begin met de basis variant en maak hem steeds moeilijker. Maar eerst een klein beetje theorie om te snappen hoe hij werkt. Hier is een stukje code van Justus dat een Sprite rond laat draaien. Dit stukje code hoef je niet te begrijpen, je moet alleen weten dat die 2 regels code iets in een cirkel kunnen laten bewegen. De beweging zit in Angle, dit staat voor een getal dat elk frame veranderd. Hij begint als 0 en elke keer komt er 0,1 bij.

Hierin zijn 2 waardes belangrijk als je er mee wilt gaan spelen: angle: als je deze kleiner maakt gaat de bal langzamer. En de waarde die nu 100 is. Deze staat gelijk aan de grootte van de cirkel.

Hieronder staat de code om een rode bal (half doorzichtig) te maken en hierin kleinere blauwe balletjes die volgens Justus zijn formule allemaal een rondje draaien.

/** Atoms.as -- Jan 10, 2011
 *
 * Beschrijving: [beschrijving van dit bestand]
 * 
 * @author Mike van Rossum
 *
 * Copyleft 2011, all wrongs reversed.
 */
package
{
    // Importeer benodigde classes
    import Bal;

import flash.display.Sprite;
import flash.events.Event;

public class Atoms extends Sprite
{
    // Variabele declaratie
    private var array:Array;
    private var angle:Number;
    private var rodeBal:Bal;
    private var defCircling:int;

    // Constructor functie
    public function Atoms()
    {
        addEventListener(Event.ENTER_FRAME, MoveAtoms);     

        array = new Array;

        angle = new Number;
        angle = 0;

        for (var i:int=0; i<15; i++)
        {
            var bal:Bal;
            bal = new Bal(0x0000ff,10);
            addChild(bal.cirkel);
            array[i] = bal.cirkel;
        }

        defCircling = new int;
        defCircling = 100;

        rodeBal = new Bal(0xff0000,160);
        rodeBal.cirkel.y = stage.stageHeight/2;
        rodeBal.cirkel.x = stage.stageWidth/2
        rodeBal.cirkel.alpha = .5;
        addChild(rodeBal.cirkel);   
    }

    // Eigen functies
    public function MoveAtoms(event:Event):void 
    {
        angle += .1;
        for (var i:int=0; i<15; i++)
        {
            array[i].x = stage.stageWidth/2 + Math.sin(angle) * 100;
            array[i].y = stage.stageHeight/2 + Math.cos(angle) * 100;
        }
    }
}

Hier is het resultaat gecompileerd

Ik maak eerst een rode bal en een Array aan. in de Array ga ik zometeen een aantal kleine balletjes stoppen. Dat doet ik met een foor loop om mijn werk te besparen. Het principe werkt als volgt: Alles wat in een for loop staat wordt niet 1 maar meerdere keren uitgevoerd. het nummertje I wat je overal in de foor loop ziet staan is iets wat gelijk is aan een nummer, hoeveel dit nummer is is afhankelijk van hoe vaak de for loop de code al heeft uitgevoerd. In mijn geval voert hij hem 15 keer uit. In het begin is i 0, de volgende keer 1, enzovoorts. Er worden dus gewoon 15 ballen aangemaakt.

Als je nu nog iets met een bal uit een Array wil doen kan je hem niet aanspreken als bal (daar zijn er namelijk meer van) maar als Array[nummer uit array]. Als je ze allemaal aan wilt spreken is het makkelijk om gewoon dezelfde for loop te gebruiken waarmee je ze hebt gemaakt. In ons geval is dat precies wat we willen. dus daarom zie je in de functie MoveAtoms dezelfde for loop als in de constructor.

Hoe komt het dat je maar 1 balletje ziet bewegen? We hebben 15 ballen en die bewegen we precies hetzelfde. Ze zitten dus elk frame precies op elkaar. Om te zorgen dat ze allemaal zichtbaar zijn kan je een aantal dingen doen:

  • Je kan de de draaicirkel van de een kleiner maken dan de ander
  • Je kan de een sneller laten gaan dan de ander
  • Je kan de cirkels op een ander punt laten starten

Laten we beginnen met optie 1 Met een for loop pas je de beweging van de balletjes aan, deze is voor elke bal hetzelfde dus het lijkt heel moeilijk. Maar dat is het niet want je hebt namelijk het getal i (een variabele die gelijk staat aan een getal, zelfs elke keer een ander getal) meenemen om de grootte van de draaicirkel te bepalen.

In dit geval is dat 100 maar we kunnen daar ook een rekensommetje inzetten: Laten we doen (i * 25). Dit betekend dat de for loop elke keer dezelfde code uitvoert en dan 1 bij i optelt. i begint bij 0 en wordt elke keer groter. Op deze manier wordt de grootte van de cirkel afhankelijk van de i.

Zie hier het resultaat.

Laten we de een sneller laten draaien dan de ander We kunnen weer met de i werken om elke bal een andere snelheid te geven. Ik zou al dat het met angle te maken had dus we maken hier een sommetje van om de i hier ook in te verwerken. Na wat spelen met verschillende getallen vond ik deze combinatie:

(angle *(i+7)/10)

Ik doe angle (wat elke keer veranderd) keer i + 7 en dat deel ik door 10. Al je dat doet krijg je dit resultaat (ik heb de balletjes ondertussen iets kleiner gemaakt).

Dit is de basis voor atomen, om het resultaat te krijgen wat lijkt op een atoom passen we alles dubbel toe wat we nu hebben. Om dit uit te leggen gebruik ik een voorbeeld van de aarde. De aarde draait net als een van onze blauwe balletjes om de zon (de rode bal). Om de aarde draait weer een maan. Probeer je dit te visualiseren en maak dan de aarde onzichtbaar. hier kan je zien wat ik bedoel. De half doorzichtige blauwe cirkels draaien rond en op basis van die positie draaien de kleine balletjes erin ook rond.

Zolang de binnenste draaibeweging heel klein blijft vergeleken met de buitenste draaibeweging blijf je het planeet met maan idee krijgen. Maar wanneer je de binnenste draaibeweging een stuk groter maakt krijg je andere vormen.

Hoe doe je dit in code?

Eerst hadden we een som en daarmee rekende we rechtstreeks de x en y positie uit van de bal per frame. Nu moeten we eerst de positie van de “aarde” opslaan (ook al is de aarde zelf niet zichtbaar) en vanuit dit punt een nieuwe berekening doen voor de maan.

voor het opslaan van getallen (namelijk een x en y positie van de “aarde”) gebruiken we variabelen. Net als angle. Het voordeel van een variabele ten opzichte van een direct getal is dat we de variabele kunnen aanpassen en op deze manier de uitkomst van de som veranderen zonder de som zelf te veranderen. We gaan nu twee variabelen maken waarin we elk frame de locatie van “aarde” opslaan.

private var cirkely:int = new int;
        private var cirkelx:int = new int;

Als je dit voor je constructor zet dan kan je ze later gebruiken.

En de code voor atomen ziet er zo uit:

cirkely = positieXCirkel + Math.sin(angle(i+7)/10) * 100;
            cirkelx = positieYCirkel + Math.cos(angle(i+7)/10) * 100           

        array[i].y = cirkely + Math.sin(-angle*(i+7)/20) * 50;
        array[i].x = cirkelx + Math.cos(-angle*(i+7)/20) * 50;

Zoals je ziet zie je twee keer de formule om iets in een cirkelbeweging te laten gaan. Alleen bij de eerste slaan we alleen de positie op in twee variabelen (de aarde: cirkely en cirkels) en vanuit die relatieve positie beginnen we de formule opnieuw. Door te spelen met de getallen kom je tot verschillende resultaten.

Het resultaat ziet er dan uit zoals in het filmpje (de atoom die links begint).

Na wat spelen met de getallen kom ik uit op <twee andere=”” bewegingen=”” die=”” je=”” <a=”” href=”http://mikevanrossum.nl/wp-content/uploads/2011/01/Atoms4.swf">hier kan zien.

Hier is de uiteindelijke broncode van het bovenstaande voorbeeld.

/** Atoms.as -- Jan 10, 2011
 *
 * Beschrijving: [beschrijving van dit bestand]
 * 
 * @author Mike van Rossum
 *
 * Copyleft 2011, all wrongs reversed.
 */
package
{
    // Importeer benodigde classes
    import Bal;

import flash.display.Sprite;
import flash.events.Event;

public class Atoms extends Sprite
{
    // Variabele declaratie
    private var arrayLinks:Array;
    private var arrayRechts:Array;

    private var grootteBal:int;
    private var angle:Number;

    private var linkerCirkel:Bal;
    private var rechterCirkel:Bal;

    private var LinkerCirkely:int;
    private var LinkerCirkelx:int;

    private var rechterCirkely:int;
    private var RechterCirkelx:int;

    private var vis:Number;

    // Constructor functie
    public function Atoms()
    {
        addEventListener(Event.ENTER_FRAME, MoveAtoms);     

        LinkerCirkely = new int;
        LinkerCirkelx = new int;

        rechterCirkely = new int;
        RechterCirkelx = new int;

        arrayRechts = new Array;
        arrayLinks = new Array;

        vis= new Number;
        vis = .035

        angle = new Number;
        angle = 0;

        for (var i:int=0; i&lt;15; i++)
        {
            var bal:Bal;
            bal = new Bal(0x0000ff,6);
            addChild(bal.cirkel);
            arrayLinks[i] = bal.cirkel;
        }

        for (var j:int=0; j&lt;15; j++)
        {
            var bal:Bal;
            bal = new Bal(0x0000ff,6);
            addChild(bal.cirkel);
            arrayRechts[j] = bal.cirkel;
        }

        linkerCirkel = new Bal(0xff0000,117);
        linkerCirkel.cirkel.y = stage.stageHeight/2; 
        linkerCirkel.cirkel.x = stage.stageWidth/20;
        linkerCirkel.cirkel.alpha = .5;
        addChild(linkerCirkel.cirkel);  

        rechterCirkel = new Bal(0xff0000,30);
        rechterCirkel.cirkel.y = stage.stageHeight/2;
        rechterCirkel.cirkel.x = stage.stageWidth/20*18; 
        rechterCirkel.cirkel.alpha = .5;
        addChild(rechterCirkel.cirkel); 
    }

    // Eigen functies
    public function MoveAtoms(event:Event):void 
    {

        //dit moeten we elk frame verhogen om te zorgen dat er een draaibeweging is
        angle += .1;

        //laat de rechterCirkel in en uit faden.
        rechterCirkel.cirkel.alpha -= vis;
        if (rechterCirkel.cirkel.alpha &gt;= 1 || rechterCirkel.cirkel.alpha &lt;= 0 )
        {
            vis *= -1;
        }

        //beweeg linkerballetjes
        for (var i:int=0; i&lt;15; i++)
        {   
            LinkerCirkely = stage.stageHeight/2 + Math.sin(angle*(i+7)/8) * 50;
            LinkerCirkelx = stage.stageWidth/20  + Math.cos(angle*(i+7)/8) * 50;

            arrayLinks[i].y = LinkerCirkely + Math.sin(-angle*(i+7)/100) * 60;
            arrayLinks[i].x = LinkerCirkelx + Math.cos(-angle*(i+7)/100) * 60;
        }

        //beweeg rechterballetjes
        for (var j:int=0; j&lt;15; j++)
        {
            rechterCirkely = stage.stageHeight/2 + Math.sin(angle*(j+7)/8) *  60;
            RechterCirkelx = stage.stageWidth/20*18 + Math.cos(angle*(j+7)/8) * 60;

            arrayRechts[j].y = rechterCirkely + Math.sin(-angle*(j+7)/20) * 25;
            arrayRechts[j].x = RechterCirkelx + Math.cos(-angle*(j+7)/20) * 25;
        }
    }
}

Posted at January 12, 2011, under AS3.