Een real-time video maken met AS3

Hallo, code freaks! In deze zelfstudie leer je hoe je een lopende video in blokken splitst alsof deze is geëxplodeerd. En dit alles met alleen ActionScript. Voor deze zelfstudie gebruiken we de camera als videobron, zodat je de wijzigingen live kunt bekijken.


Eindresultaat voorbeeld

Laten we eens kijken naar het eindresultaat waar we naartoe zullen werken:

Klik en sleep een blok om het over het scherm te verplaatsen! (Toegang tot de camera vereist.)


Stap 1: Setup - IDE

Voor deze tutorial gebruiken we de FlashDevelop IDE (hoewel je elke AS3-editor zou kunnen gebruiken). Als je het niet hebt en het wilt proberen, kun je het vanaf hier pakken. Een eenvoudige tutorial over het instellen van FlashDevelop op uw machine vindt u hier.

Ook als u Flash Professional aan uw zijde hebt geïnstalleerd, werkt ook dat. Het enige wat u hoeft te doen is een extern klassenbestand maken zoals hieronder vermeld en het koppelen aan uw Flash-project als een klasse Document.

Dat schept onze werkomgeving.


Stap 2: Setup - Nieuw project

Maak een nieuw AS3-project in FlashDevelop.

Als het klaar is, heb je een Hoofd klasse gemaakt in de map src zoals te zien in het rechterdeelvenster:


Stap 3: Setup - De hoofdklasse

Vervolgens moeten we de Main.as bestand een beetje schoner door het elimineren van een code. Aanvankelijk wanneer u de Main.as bestand, zou het code iets als dit hebben:

 pakket import flash.display.Sprite; import flash.events.Event; public class Hoofd breidt uit openbare functie Main (): void if (stage) init (); else addEventListener (Event.ADDED_TO_STAGE, init);  private function init (e: Event = null): void removeEventListener (Event.ADDED_TO_STAGE, init); // ingangspunt   

We zullen een deel van de code verwijderen om het er schoner uit te laten zien. Dus je zou dit moeten hebben:

 pakket import flash.display.Sprite; import flash.events.Event; public class Hoofd breidt uit openbare functie Main (): void 

Nu is alle installatie voltooid en is het tijd om in een code te duiken.


Stap 4: De video- en camera-variabelen declareren

Ons eerste doel is om de video op het podium te tekenen met behulp van de camera; hiervoor moeten we enkele variabelen declareren. Zet deze verklaringen net boven de Hoofd klassenbouwer.

 // videovariabelen private var camW: int = 300; private var camH: int = 300; privé var video: video;

camW - De breedte van de camera / video.

CAMH - De hoogte van de camera / video.

video- - Onze Video klasse instantie.


Stap 5: bereid de camera van het apparaat voor

Zoals eerder vermeld, gebruiken we de camera-uitvoer in de video. Dus eerst moeten we de camera van het apparaat klaar maken. De volgende code zou in de klassenbouwer moeten gaan.

 var camera: Camera = Camera.getCamera ();

Hier nemen we een Camera bijvoorbeeld en verkrijg de beschikbare apparaatcamera met behulp van de statische methode getCamera () van de Camera klasse.

 camera.setMode (camW, camH, 30);

We bieden enkele camera-instellingen: breedte, hoogte en fps.

Merk op dat we de camera-variabele niet globaal hebben gemaakt, omdat we deze nergens anders nodig hebben buiten deze functie, zoals je hierna zult zien. In de volgende stap initialiseren we de video- veranderlijk.


Stap 6: Maak de video daadwerkelijk!

 video = nieuwe video (camW, camH); Video.attachCamera (camera);

We hebben nu het video- variabele en gebruikte de attachCamera () methode om de camera eraan te bevestigen. Dit betekent dat de video nu de camera-uitgang als bron gebruikt.

Alles gedaan met de video en camera dingen. Vergeet niet dat u de juiste klassen in uw code moet importeren. Uw volledige klassecode zou er op het moment zo uit moeten zien:

 pakket import flash.display.Sprite; import flash.events.Event; import flash.media.Camera; import flash.media.Video; public class Hoofd breidt Sprite uit // videovariabelen private var camW: int = 300; private var camH: int = 300; privé var video: video; public function Main (): void var camera: Camera = Camera.getCamera (); camera.setMode (camW, camH, 30); video = nieuwe video (camW, camH); Video.attachCamera (camera); 

Als je het project nu (F5 of CTRL + Enter) uitvoert, zou het een lege fase zijn, maar je zult waarschijnlijk een cameratoegangsverzoek ontvangen terwijl de applicatie probeert toegang te krijgen tot de camera van het apparaat. Sta het toe.

De reden waarom u niets ziet, is omdat we de video niet willen weergeven en daarom hebben we deze niet aan het podium toegevoegd (weergavelijst). Het zal alleen worden gebruikt als een bron voor onze afzonderlijke blokken. Als je wilt testen of alles goed werkt, voeg je gewoon de volgende regel toe aan het einde in de Hoofd bouwer:

 addChild (video); // Verwijder deze regel na het testen

Stap 7: De blokvariabelen declareren

Nu maken we de blokken - de afzonderlijke stukken van de video. En de eerste stap is om een ​​aantal variabelen te declareren die daarvoor vereist zijn. Dus ga je gang en tel de volgende variabelenverklaringen op net onder de videovariabelen:

 // blokvariabelen privé var-rijen: int = 3; private var cols: int = 3; private var blockW: int = camW / cols; private var blockH: int = camH / rows; private var pointCollection: Object = new Object ();

rijen - Aantal rijen om de video in te splitsen.

cols - Ja. Je hebt het. Aantal kolommen om de video in te splitsen.

blockW - De breedte van elk blok. Dit zijn afgeleide variabelen omdat het eenvoudigweg wordt berekend door de totale breedte te delen (camW) op aantal colums (cols).

blockH - De hoogte van elk blok, d.w.z.. CAMH gedeeld door rijen.

pointCollection - Laatste maar belangrijkste variabele. Het is een associatieve array die we zullen gebruiken om het corresponderende punt van elk blok op te slaan. Als een blok bijvoorbeeld een naam heeft block12, dan zouden we het bijbehorende punt opslaan p12 zoals dit :

 pointCollection ["block12"] = p12; // punten zijn hier Point-class instanties

Stap 8: Begin met het maken van de blokken

Nu we de vereiste variabelen hebben gedefinieerd, beginnen we met het maken van de blokken. We houden alle code voor het maken van blokken in een functie met de naam initBlocks (). Deze functie wordt aangeroepen vanuit de Hoofd constructor na het instellen van de video.

Laten we dus eerst een functie noemen initBlocks () net na de Hoofd bouwer.

 private functie initBlocks (): void for (var r: int = 0; r < rows; r++)  for (var c:int = 0; c < cols; c++)  // code to create each block   

Let op de twee voor loops die we erin hebben geplaatst, wat ons helpt om de blokken in een 2D-raster rijvormig te maken. En voeg vervolgens aan het eind van deze maand een oproep toe aan deze functie Hoofd():

 public function Main (): void var camera: Camera = Camera.getCamera (); camera.setMode (camW, camH, 30); video = nieuwe video (camW, camH); Video.attachCamera (camera); initBlocks (); 

Stap 9: Componenten van een blok

Voordat we de blokken maken, laten we begrijpen waaruit een enkel blok bestaat. Elk blok is eigenlijk:

  • EEN sprite
  • met een Bitmap in het
  • die op zijn beurt een BitmapData

Denken aan sprite als de buitenste container. Helemaal leeg.

Om het blok te tekenen, hebben we een Bitmap die de bijbehorende video-uitvoer zal tonen.

En tot slot, elke Bitmap heeft wat gegevens nodig om erin te tekenen. Dat wordt gegeven in de vorm van BitmapData.


Stap 10: Maak de Block's Base - Sprite

Het eerste onderdeel van een blok, zoals we hebben besproken, is een Sprite. Dus laten we er een maken. Alle code die we schrijven om het blok aan te maken, moet in de. Worden geschreven voor loops.

 var newBlock: Sprite = nieuwe Sprite ();

We maken een nieuw exemplaar van de sprite klasse.

 newBlock.name = "block" + r + c;

Vervolgens noemen we de sprite, zodat we er later in de code naar kunnen verwijzen. De naamgevingsconventie is eenvoudig: een blok op rij r en kolom c heeft de naam blok + r + c (+ betekent aaneenschakeling). Dus een blok in rij 2 en kolom 1 wordt genoemd block21.


Stap 11: Positionering

Nadat je het hebt gemaakt, moeten we het op het podium plaatsen volgens de rij en kolom. Dus laten we de volgende code toevoegen.

 var p: Point = new Point (c * blockW, r * blockH);

We gebruiken een Punt class object om de coördinaten van elk punt hier op te slaan. En dus creëren we een nieuw exemplaar van Punt en ga voorbij c * blockW als de x-waarde en r * blockH als de y-waarde. Nu zijn de coördinaten eenvoudig toegankelijk p.x en p.y en wordt later gebruikt om het clippped-gebied van elk blok op te halen uit een volledig videoframe. Onthoud dat het punt van elk blok eigenlijk de coördinaten is van het punt linksboven in het raster.

Als u twijfelt over hoe de positie hier wordt berekend, zal de volgende afbeelding duidelijk maken.

 newBlock.x = c * (blockW + 1) + 20; newBlock.y = r * (blockH + 1) + 20;

Vervolgens plaatsen we de sprite. De cordinaten zijn min of meer hetzelfde, dus we voegen nu 20 toe om een ​​offset te geven. We voegen ook 1 toe aan de blockW en blockH om de blokken met 1 pixel te scheiden, zoals zichtbaar is in de demo hierboven.

 pointCollection [newBlock.name] = p;

Ten slotte besparen we het punt dat we eerder in de pointCollection.


Stap 12: De bitmap toevoegen aan het blok

Nu komt het naar de 2e en 3e component van het blok.

 var bmpd: BitmapData = nieuwe BitmapData (blockW, blockH);

Eerst maken we een BitmapData bijvoorbeeld en geef de vereiste blokbreedte en hoogte door die we eerder hadden opgeslagen. Zoals eerder besproken, BitmapData instantie is vereist om een ​​te maken Bitmap instantie die wordt doorgegeven in de constructor.

 var bmp: Bitmap = nieuwe Bitmap (bmpd); bmp.name = "myBmp";

Nu maken we een Bitmap exemplaar en geef de eerder gemaakte door bmpd in de constructor. We noemen ook de bitmap myBmp zodat we er later naar kunnen verwijzen.

 newBlock.addChild (BMP); addChild (newBlock);

Uiteindelijk voegen we de bitmap toe bmp als een kind van newBlock en newBlock zichzelf als het kind van het podium.

Om er zeker van te zijn dat u op de goede weg bent, uw Main.as code zou er ongeveer zo uit moeten zien:

 pakket import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.geom.Point; import flash.media.Camera; import flash.media.Video; public class Hoofd breidt Sprite uit // videovariabelen private var camW: int = 300; private var camH: int = 300; privé var video: video; // blokvariabelen privé var-rijen: int = 3; private var cols: int = 3; private var blockW: int = camW / cols; private var blockH: int = camH / rows; private var pointCollection: Array = new Array (); public function Main (): void var camera: Camera = Camera.getCamera (); camera.setMode (camW, camH, 30); video = nieuwe video (camW, camH); Video.attachCamera (camera); initBlocks ();  private functie initBlocks (): void for (var r: int = 0; r < rows; r++)  for (var c:int = 0; c < cols; c++)  var newBlock:Sprite = new Sprite(); newBlock.name = "block" + r + c; var p:Point = new Point(c * blockW, r * blockH); newBlock.x = c * (blockW + 1) + 20; newBlock.y = r * (blockH + 1) + 20; pointCollection[newBlock.name] = p; var bmpd:BitmapData = new BitmapData(blockW, blockH); var bmp:Bitmap = new Bitmap(bmpd); bmp.name = "myBmp"; newBlock.addChild(bmp); addChild(newBlock);     

Stap 13: De blokken bijwerken - Concept

Hoewel we de blokken op de juiste posities hebben geplaatst, zien we nog steeds niets over het uitvoeren van het project. Dat komt omdat we nog steeds niets hebben getekend binnen de blokbitmaps.

Onze volgende stap is het uitvoeren van een constant lopende lus die de volgende bewerkingen uitvoert:

  1. Download het huidige videoframe.
  2. Doorloop alle blokken.
  3. Haal het punt en het bitmap-kind van elk blok op.
  4. Teken het overeenkomstige deel van het videoframe op de bitmap van het blok.

Zo? laten we het doen!

Voordat we de loop-code implementeren, hebben we een LOOP-FUNCTIE nodig (een beetje zoals een gamelus). Voeg de volgende functieverklaring toe onder de initBlocks () functie:

 persoonlijke functie updateBlocks (e: Event): void 

Zoals zichtbaar is vanuit de functieparameter, lijkt het een gebeurtenislistener en ja, dat is het. Dit is een luisterfunctie die we zullen koppelen aan de ENTER_FRAME evenement van de etappe. Om de luisteraar te bevestigen, voegt u deze regel toe aan het einde van de Hoofd() bouwer.

 public function Main (): void var camera: Camera = Camera.getCamera (); camera.setMode (camW, camH, 30); video = nieuwe video (camW, camH); Video.attachCamera (camera); initBlocks (); addEventListener (Event.ENTER_FRAME, updateBlocks); 

Stap 14: Capture Frame

Dit is de eerste bewerking die we uitvoeren in onze loop - de updateBlocks () functie die op elk frame wordt aangeroepen. Plaats de volgende code erin updateBlocks () functie.

 var srcBmpd: BitmapData = nieuwe BitmapData (camW, camH);

De gegevens van elke bitmap in Actionscript 3.0 moeten worden opgenomen in a BitmapData instantie en dus maken we er een. We vullen dit exemplaar vervolgens met de huidige videoframegegevens in.

 srcBmpd.draw (video);

Hier hebben we de trek() functie van de BitmapData klasse. Het vereist een object van de klasse die het implementeert IBitmapDrawable interface. Voor bijvoorbeeld. Sprite, MovieClip, BitmapData enz. Wat het doet is simpelweg de visuele gegevens van het object halen en opslaan in de BitmapData aanleg.

Dus nu hebben we het huidige videoframe (of, zou je kunnen zeggen, een screenshot) in de variabele srcBmpd.

Stap 15: Laten we herhalen

Omdat we elk blok moeten bijwerken, maken we een dubbel voor-loop, vergelijkbaar met degene die we hebben geschreven voor het maken van de blokken. Dus ga je gang en voeg het toe.

De functie zou er op dit moment hetzelfde uit moeten zien:

 private function updateBlocks (e: Event): void var srcBmpd: BitmapData = nieuwe BitmapData (camW, camH); srcBmpd.draw (video); voor (var r: int = 0; r < rows; r++)  for (var c:int = 0; c < cols; c++)  // update code here   

Stap 16: De bitmap en het punt van het blok ophalen

Vergeet niet dat we de blokken op een bepaalde manier hebben benoemd tijdens het maken ervan, zodat we naar elk blok kunnen verwijzen met behulp van het rij- en kolomnummer. Dat is wat we nu gebruiken om de referentie van elk blok te krijgen.

 var b_mc: Sprite = this.getChildByName ("block" + r + c) als Sprite;

Wij gebruiken de getChildByName functie van de stadium (deze) die een verwijzing retourneren naar een object waarvan de naam overeenkomt met de doorgegeven string. We typeren het ook sprite class om er zeker van te zijn dat het geretourneerde object een is sprite. Nu is de blokverwijzing in de variabele b_mc.

 var bmp: Bitmap = b_mc.getChildByName ("myBmp") als Bitmap;

Op dezelfde manier halen we de verwijzing naar de bitmap op die als een kind van de bloksprite is toegevoegd.

 var p: Point = pointCollection [b_mc.name];

Eindelijk krijgen we de huidige blok (b_mc) coördinaten van de array waarin we het eerder hebben opgeslagen met behulp van de naam van het blok.

Stap 17: Teken het!

Nu we alle benodigde informatie hebben over waar we naartoe moeten tekenen, kunnen we het eigenlijk TEKENEN. Ons motief hier is om het rechthoekige gebied van het videoframe (d.w.z.. srcBmpd) met het punt linksboven als het opgehaalde punt p, breedte als blockW en hoogte als blockH.

Voor dit doel gebruiken we de copyPixels () methode van de BitmapData klasse. Het kopieert eigenlijk de regio van een andere bron BitmapData gespecificeerd door het passeren van een Rechthoek voorwerp.

 bmp.bitmapData.copyPixels (srcBmpd, nieuwe Rectangle (p.x, p.y, blockW, blockH), new Point ());

De trek() functie wordt ingeschakeld bmp's bitmapData eigendom. De parameters die erin worden doorgegeven zijn:

  1. De bron BitmapData obeject. Het screenshot van de video in dit geval (srcBmpd).
  2. EEN Rechthoek object dat het punt, de breedte en de kolom linksboven in de regio in de bron die moet worden gekopieerd, opgeeft.
  3. Het punt in de destnatie waar het geknipte deel moet worden gekopieerd. (0,0) in dit geval. Dus we geven gewoon een nieuwe door Punt voorwerp.

Helemaal klaar! Nu is het tijd om je project uit te voeren en het geweldige effect te zien.

Stap 18: Drag-and-Drop-functionaliteit toevoegen

Om de drag-and-drop-functie toe te voegen zoals te zien is in de demo, hoeven we slechts twee muisluisteraars aan elk blok te hechten - een voor de MOUSE_DOWN evenement en een andere voor de MOUSE_UP evenement. Dus ga je gang en definieer twee muis-handler-functies aan het einde van de les:

 private function onMouseDown (e: MouseEvent): void Sprite (e.currentTarget) .startDrag ();  private function onMouseUp (e: MouseEvent): void Sprite (e.currentTarget) .stopDrag (); 

Alles wat we doen in deze listener-functies is om de verwijzing naar het blok voor gebeurtenisafhandeling te krijgen met behulp van de currentTarget eigendom van de Evenement object, typ het naar een sprite (zoals dat is wat onze blokken zijn) en noem het startDrag () en stopDrag () om het slepen-en-neerzetten af ​​te handelen.

Dat is nog niet alles. We moeten deze luisteraars nog steeds koppelen aan hun bijbehorende evenementen. Dus voeg deze twee regels toe aan de initBlocks () functie.

 private functie initBlocks (): void for (var r: int = 0; r < rows; r++)  for (var c:int = 0; c < cols; c++)  var newBlock:Sprite = new Sprite(); newBlock.name = "block" + r + c; var p:Point = new Point(c * blockW, r * blockH); newBlock.x = c * (blockW + 1) + 20; newBlock.y = r * (blockH + 1) + 20; pointCollection[newBlock.name] = p; var bmpd:BitmapData = new BitmapData(blockW, blockH); var bmp:Bitmap = new Bitmap(bmpd); bmp.name = "myBmp"; newBlock.addChild(bmp); addChild(newBlock); newBlock.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); newBlock.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);   

Stap 19: Final Touch

Een laatste ding om het alleen maar interactiever te laten lijken. Je hebt misschien gemerkt hoe de blokken vervagen in en uit wanneer ze worden ingedrukt en losgelaten. Dat is een alfamanipulatie die we binnen de luisteraars doen. Pas je luisteraars aan op iets als:

 private function onMouseDown (e: MouseEvent): void Sprite (e.currentTarget) alpha = 0,4; Sprite (e.currentTarget) .startDrag ();  private function onMouseUp (e: MouseEvent): void Sprite (e.currentTarget) .alpha = 1; Sprite (e.currentTarget) .stopDrag (); 

En daar heb je het alpha-veranderingseffect.

Conclusie

Het effect heeft veel potentieel dat in verschillende toepassingen kan worden gebruikt. Ik heb onlangs een puzzelspel ontwikkeld om er gebruik van te maken.

Afgezien daarvan kan het worden gebruikt om overgangseffecten voor videospelers te creëren, of in combinatie met 3D om een ​​oppervlak met een video te structureren.

Ik hoop dat ik een aantal leuke dingen zie die mensen bedenken om dit effect te gebruiken!