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.
Diamo un'occhiata al risultato finale su cui lavoreremo:
Clicca e trascina un blocco per muoverlo sullo schermo! (È richiesto l'accesso alla videocamera.)
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.
Crea un nuovo progetto AS3 in FlashDevelop.
Quando è fatto, avrai un Principale
classe creata nella cartella src come si vede nel pannello di destra:
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.
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.
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.
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
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
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 ();
Prima di creare i blocchi, capiamo cos'è un singolo blocco. Ogni blocco è in realtà:
folletto
Bitmap
dentroBitmapData
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
.
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
.
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
.
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);
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:
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);
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
.
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
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.
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:
BitmapData
obeject. Lo screenshot del video in questo caso (srcBmpd
).Rettangolo
oggetto che specifica il punto in alto a sinistra, la larghezza e la colonna della regione nella sorgente da copiare. Punto
oggetto. Tutto fatto! Ora è il momento di eseguire il tuo progetto e vedere l'effetto fantastico.
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);
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.
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!