Ritaglia un video in tempo reale con AS3

Ciao, code freaks! Questo tutorial ti mostrerà come dividere un video in esecuzione in blocchi come se fosse esploso. E tutto questo usando solo ActionScript. Per questo tutorial utilizzeremo la fotocamera come sorgente video, in modo che tu possa vedere le modifiche dal vivo.


Anteprima del risultato finale

Diamo un'occhiata al risultato finale su cui lavoreremo:

Clicca e trascina un blocco per muoverlo sullo schermo! (È richiesto l'accesso alla videocamera.)


Passaggio 1: installazione - IDE

Per questo tutorial useremo l'IDE di FlashDevelop (sebbene tu possa usare qualsiasi editor AS3). Se non lo hai e vuoi provare, puoi prenderlo da qui. Un tutorial di base sulla configurazione di FlashDevelop sul tuo computer può essere trovato qui.

Anche se hai Flash Professional installato al tuo fianco, funzionerà anche tu. Tutto ciò che devi fare è creare un file di classe esterno come indicato di seguito e collegarlo al tuo progetto Flash come classe Document.

Questo crea il nostro ambiente di lavoro.


Passaggio 2: installazione - Nuovo progetto

Crea un nuovo progetto AS3 in FlashDevelop.

Quando è fatto, avrai un Principale classe creata nella cartella src come si vede nel pannello di destra:


Passaggio 3: Impostazione: la classe principale

Quindi dobbiamo fare il Main.as file un po 'più pulito, eliminando un po' di codice. Inizialmente quando apri il Main.as file, avrebbe codice qualcosa come questo:

 pacchetto import flash.display.Sprite; import flash.events.Event; public class Main extends Sprite public function Main (): void if (stage) init (); else addEventListener (Event.ADDED_TO_STAGE, init);  funzione privata init (e: Event = null): void removeEventListener (Event.ADDED_TO_STAGE, init); // punto d'entrata   

Cancelleremo parte del codice per renderlo più pulito. Quindi dovresti avere questo:

 pacchetto import flash.display.Sprite; import flash.events.Event; public class Main extends Sprite public function Main (): void 

Ora, tutte le impostazioni sono terminate ed è ora di tuffarsi in qualche codice.


Passaggio 4: dichiarazione delle variabili video e videocamera

Il nostro primo obiettivo è disegnare il video sul palco usando la fotocamera; per questo abbiamo bisogno di dichiarare alcune variabili. Metti queste dichiarazioni appena sopra il Principale costruttore di classe.

 // variabili video private var camW: int = 300; private var camH: int = 300; video var privato: video;

camW - La larghezza della videocamera / video.

CAMH - L'altezza della videocamera / del video.

video - Nostro video istanza di classe.


Passaggio 5: preparare la fotocamera del dispositivo

Come accennato in precedenza, utilizzeremo l'output della videocamera nel video. Quindi prima dobbiamo rendere pronta la fotocamera del dispositivo. Il seguente codice dovrebbe andare nel costruttore della classe.

 var camera: Camera = Camera.getCamera ();

Qui, istanziamo a telecamera istanza e ottenere la fotocamera del dispositivo disponibile utilizzando il metodo statico getCamera () del telecamera classe.

 camera.setMode (camW, camH, 30);

Forniamo alcune impostazioni della fotocamera: larghezza, altezza e fps.

Nota che non abbiamo reso globale la variabile della videocamera perché non è necessario accedervi da nessuna parte al di fuori di questa funzione, come vedrai successivamente. Nel prossimo passo inizializziamo il video variabile.


Passaggio 6: Creare effettivamente il video!

 video = nuovo video (camW, camH); Video.attachCamera (telecamera);

Abbiamo ora istanziato il video variabile e usato il attachCamera () metodo per collegare la fotocamera ad esso. Ciò significa che ora il video utilizza l'uscita della telecamera come fonte.

Tutto fatto con il materiale video e fotografico. Ricorda, devi importare classi appropriate nel tuo codice. Il tuo codice classe completo dovrebbe apparire come questo al momento:

 pacchetto import flash.display.Sprite; import flash.events.Event; import flash.media.Camera; import flash.media.Video; public class Main estende Sprite // video variables private var camW: int = 300; private var camH: int = 300; video var privato: video; public function Main (): void var camera: Camera = Camera.getCamera (); camera.setMode (camW, camH, 30); video = nuovo video (camW, camH); Video.attachCamera (telecamera); 

Se si esegue (F5 o CTRL + Invio) il progetto in questo momento, si tratterà di una fase vuota, ma molto probabilmente si otterrà una richiesta di accesso alla telecamera mentre l'applicazione sta tentando di accedere alla telecamera del dispositivo. Permettilo.

Il motivo per cui non vedi nulla è perché non vogliamo mostrare il video e quindi non lo abbiamo aggiunto allo stage (elenco di visualizzazione). Sarà usato solo come fonte per i nostri blocchi separati. Se vuoi verificare che tutto funzioni correttamente, aggiungi la seguente riga alla fine nel file Principale costruttore:

 addChild (video); // Rimuovere questa riga dopo il test

Passaggio 7: dichiarazione delle variabili del blocco

Ora creiamo i blocchi, i pezzetti separati del video. E il primo passo è dichiarare alcune variabili richieste per questo. Quindi vai avanti e aggiungi le seguenti dichiarazioni di variabili appena sotto le variabili video:

 // block variables private var rows: 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 ();

righe - Numero di righe per dividere il video.

Cols - Sì. Hai capito. Numero di colonne per dividere il video.

blockW - La larghezza di ogni blocco Questa è una variabile derivata in quanto è semplicemente calcolata dividendo la larghezza totale (camW) per numero di colonne (Cols).

blockH - Altezza di ogni blocco, ad es. CAMH diviso per righe.

PointCollection - Variabile finale ma più importante. È un array associativo che useremo per memorizzare il punto corrispondente di ciascun blocco. Ad esempio, se un blocco ha un nome block12, allora dovremmo memorizzare il punto corrispondente p12 come questo :

 pointCollection ["block12"] = p12; // i punti sono istanze di classe Point qui

Passaggio 8: iniziare a creare i blocchi

Ora che abbiamo definito le variabili richieste, iniziamo effettivamente a creare i blocchi. Manterremo tutto il codice di creazione del blocco in una funzione chiamata initBlocks (). Questa funzione sarà chiamata dal Principale costruttore dopo aver impostato il video.

Quindi, prima dichiariamo una funzione chiamata initBlocks () subito dopo il Principale costruttore.

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

Notate i due per loop che abbiamo inserito all'interno, il che ci aiuterà a creare i blocchi in una griglia 2D, in termini di righe. E quindi aggiungere una chiamata a questa funzione alla fine di Principale():

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

Passaggio 9: Componenti di un blocco

Prima di creare i blocchi, capiamo cos'è un singolo blocco. Ogni blocco è in realtà:

  • UN folletto
  • con un Bitmap dentro
  • che a sua volta ha bisogno di a BitmapData

Pensa a folletto come il contenitore più esterno. Completamente vuoto.

Per disegnare il blocco, abbiamo bisogno di a Bitmap che mostrerà l'uscita video corrispondente.

E infine, ogni Bitmap ha bisogno di alcuni dati per disegnare al suo interno. Questo è dato sotto forma di BitmapData.


Passo 10: Crea la base del blocco - Sprite

Il primo componente di un blocco, come abbiamo discusso, è uno Sprite. Quindi lasciamolo creare. Tutto il codice che scriviamo per creare il blocco deve essere scritto all'interno di per loop.

 var newBlock: Sprite = new Sprite ();

Creiamo una nuova istanza di folletto classe.

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

Quindi chiamiamo lo sprite in modo che possiamo fare riferimento più avanti nel codice. La convenzione di denominazione è semplice: un blocco alla riga r e la colonna c è nominato blocco + r + c (+ significa concatenazione). Quindi un blocco alla riga 2 e alla colonna 1 è denominato block21.


Step 11: Posizionandolo

Dopo averlo creato, dobbiamo posizionarlo sullo stage in base alla sua riga e colonna. Quindi aggiungiamo il seguente codice.

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

Noi usiamo a Punto oggetto di classe per memorizzare le coordinate di qualsiasi punto qui. E così creiamo una nuova istanza di Punto e passare c * blockW come il valore x e r * blockH come il valore y. Ora è possibile accedere alle coordinate semplicemente come p.x e p.y e viene utilizzato in seguito per recuperare la regione tagliata di ogni blocco da un fotogramma video completo. Ricorda che il punto di ogni blocco è in realtà le coordinate del punto in alto a sinistra nella griglia.

Se hai dei dubbi su come viene calcolata la posizione qui, la seguente figura lo renderà chiaro.

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

Successivamente, posizioniamo lo sprite. Le cordinate sono più o meno le stesse aspettiamo ora aggiungiamo 20 per dare un offset. Inoltre aggiungiamo 1 al blockW e blockH per separare i blocchi di 1 pixel, come è visibile nella demo sopra.

 pointCollection [newBlock.name] = p;

Infine, salviamo il punto che abbiamo calcolato in precedenza nel file PointCollection.


Passaggio 12: aggiunta della bitmap al blocco

Ora, arrivando al 2 ° e 3 ° componente del blocco.

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

Per prima cosa creiamo a BitmapData istanza e passare la larghezza e l'altezza del blocco richieste precedentemente memorizzate. Come discusso in precedenza, a BitmapData l'istanza è richiesta per creare un Bitmap istanza che viene passata nel costruttore.

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

Ora creiamo a Bitmap istanza e passare il precedentemente creato bmpd nel costruttore. Inoltre, chiamiamo la bitmap myBmp in modo che possiamo fare riferimento più tardi.

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

Alla fine aggiungiamo la bitmap bmp come un bambino di newBlock e newBlock stesso come il figlio del palcoscenico.

Per essere sicuro di essere sulla buona strada, la tua Main.as il codice dovrebbe assomigliare a questo:

 package 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 Main estende Sprite // video variables private var camW: int = 300; private var camH: int = 300; video var privato: video; // block variables private var rows: 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 = nuovo video (camW, camH); Video.attachCamera (telecamera); initBlocks ();  funzione privata initBlocks (): void per (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);     

Passaggio 13: Aggiornamento dei blocchi - Concetto

Anche se abbiamo i blocchi posizionati nelle posizioni giuste, non vediamo ancora nulla sulla gestione del progetto. Questo perché non abbiamo ancora disegnato nulla all'interno dei bitmap del blocco.

Il nostro prossimo passo è eseguire un ciclo costantemente in esecuzione che esegua le seguenti operazioni:

  1. Ottieni il fotogramma video corrente.
  2. Passa attraverso tutti i blocchi.
  3. Recupera figlio punto e bitmap di ogni blocco.
  4. Disegna la parte corrispondente del fotogramma video sulla bitmap del blocco.

Così? Facciamolo!

Prima di implementare il codice loop, ciò di cui abbiamo bisogno è una FUNZIONE LOOP (un po 'come un loop di gioco). Aggiungere la seguente dichiarazione di funzione sotto il initBlocks () funzione :

 funzione privata updateBlocks (e: Event): void 

Come è visibile dal parametro function, sembra un listener di eventi e sì lo è. Questa è una funzione di ascolto che assegneremo al ENTER_FRAME evento del palcoscenico. Per collegare l'ascoltatore, aggiungi questa linea alla fine del Principale() costruttore.

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

Passaggio 14: Cattura fotogramma

Questa è la prima operazione che eseguiamo nel nostro ciclo: il updateBlocks () funzione che viene chiamata su ogni frame. Inserisci il seguente codice all'interno updateBlocks () funzione.

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

I dati di ogni bitmap in Actionscript 3.0 devono essere contenuti in a BitmapData istanza e così ne creiamo una. Successivamente popoleremo questa istanza con i dati del fotogramma video corrente.

 srcBmpd.draw (video);

Qui abbiamo usato il disegnare() funzione del BitmapData classe. Richiede un oggetto di qualsiasi classe che implementa IBitmapDrawable interfaccia. Per es. Sprite, MovieClip, BitmapData ecc. Quello che fa è semplicemente prendere i dati visivi dell'oggetto passato e memorizzarlo nel BitmapData esempio.

Così ora abbiamo il fotogramma video corrente (o, si potrebbe dire, uno screenshot) nella variabile srcBmpd.

Step 15: Let's Loop

Dato che abbiamo bisogno di aggiornare ogni blocco, creiamo un doppio per-loop, simile a quello che abbiamo scritto per creare i blocchi. Quindi vai avanti e aggiungilo.

La funzione dovrebbe essere simile a questa ora:

 funzione privata updateBlocks (e: Event): void var srcBmpd: BitmapData = new BitmapData (camW, camH); srcBmpd.draw (video); per (var r: int = 0; r < rows; r++)  for (var c:int = 0; c < cols; c++)  // update code here   

Passaggio 16: recuperare la bitmap e il punto del blocco

Ricorda che abbiamo chiamato i blocchi in un certo modo durante la loro creazione in modo da poter fare riferimento a qualsiasi blocco usando il suo numero di riga e colonna. Questo è ciò che useremo ora per ottenere il riferimento di ogni blocco.

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

Noi usiamo il getChildByName funzione del palcoscenico (Questo) che restituiscono un riferimento a un oggetto il cui nome corrisponde alla stringa passata. Inoltre lo abbiamo tipizzato a folletto classe solo per essere sicuro che l'oggetto restituito sia a folletto. Ora il riferimento del blocco è nella variabile b_mc.

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

Più o meno allo stesso modo, recuperiamo il riferimento alla bitmap che è stata aggiunta come figlio del blocco sprite.

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

Infine, otteniamo il blocco corrente (b_mc) coordinate dalla matrice in cui l'abbiamo memorizzata in precedenza utilizzando il nome del blocco.

Passaggio 17: Disegnalo!

Ora che abbiamo tutte le informazioni necessarie su cosa disegnare dove, possiamo in realtà disegnarlo. Il nostro motivo qui è quello di ottenere la regione rettangolare del fotogramma video (ad es. srcBmpd) con il punto in alto a sinistra come punto recuperato p, larghezza come blockW e altezza come blockH.

Per questo scopo usiamo il copyPixels () metodo del BitmapData classe. In realtà copia la regione di un'altra fonte BitmapData specificato passando a Rettangolo oggetto.

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

Il disegnare() la funzione è attiva bmp'S bitmapData proprietà. I parametri passati sono:

  1. La fonte BitmapData obeject. Lo screenshot del video in questo caso (srcBmpd).
  2. UN Rettangolo oggetto che specifica il punto in alto a sinistra, la larghezza e la colonna della regione nella sorgente da copiare.
  3. Il punto nella destinazione in cui la porzione ritagliata deve essere copiata. (0,0) in questo caso. Quindi passiamo semplicemente un nuovo Punto oggetto.

Tutto fatto! Ora è il momento di eseguire il tuo progetto e vedere l'effetto fantastico.

Passaggio 18: aggiunta della funzionalità di trascinamento della selezione

Per aggiungere la funzionalità di trascinamento della selezione come mostrato nella demo, abbiamo solo bisogno di collegare due listener del mouse a ciascun blocco - uno per il MOUSE_DOWN evento e un altro per il MOUSE_UP evento. Quindi vai avanti e definisci due funzioni del gestore del mouse alla fine della classe:

 funzione privata onMouseDown (e: MouseEvent): void Sprite (e.currentTarget) .startDrag ();  funzione privata onMouseUp (e: MouseEvent): void Sprite (e.currentTarget) .stopDrag (); 

Tutto ciò che facciamo all'interno di queste funzioni di ascolto è ottenere il riferimento al blocco di invio di eventi usando il currentTarget proprietà del Evento oggetto, convertitelo in a folletto (come questo è quello che sono i nostri blocchi) e chiamare il startDrag () e stopDrag () gestire il drag-and-drop.

Non è ancora tutto. Abbiamo ancora bisogno di collegare questi ascoltatori ai loro eventi corrispondenti. Quindi aggiungi queste due linee al initBlocks () funzione.

 funzione privata initBlocks (): void per (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);   

Passo 19: tocco finale

Un'ultima cosa solo per renderla più interattiva. Potresti aver notato come i blocchi si dissolvono in entrata e in uscita quando vengono premuti e rilasciati. Questa è una manipolazione alfa che facciamo all'interno degli ascoltatori. Modifica i tuoi ascoltatori a qualcosa di simile:

 funzione privata onMouseDown (e: MouseEvent): void Sprite (e.currentTarget) .alpha = 0.4; Sprite (e.currentTarget) .startDrag ();  funzione privata onMouseUp (e: MouseEvent): void Sprite (e.currentTarget) .alpha = 1; Sprite (e.currentTarget) .stopDrag (); 

E lì hai l'effetto di cambiamento alfa.

Conclusione

L'effetto ha un grande potenziale da utilizzare in varie applicazioni. Recentemente sto usando un puzzle game che lo usa.

Oltre a questo, potrebbe essere usato per creare effetti di transizione per i lettori video, o in combinazione con il 3D per tessere una superficie con un video.

Spero di vedere alcune cose interessanti che vengono in mente usando questo effetto!