Actionscript 3.0のピクセル効果

プレビュー






そのような単純な効果。 カットソースの下で、いくつかの場所でコメントと説明。



これまたはその特殊効果がどのように機能するかを考えると、多くの考えが頭に浮かびますが、特殊効果を使用した経験が少ないことを考えると、常に実装に来るとは限りません。 状況を修正する時が来たと思いました。 少し考えてみると、そのようなことはどうなるかという簡単なアイデアが浮かびました。 彼は、まるでピクセル溶接によって印刷されたかのように、テキストの外観の効果を作成するタスクを設定し、溶接点から散在する粒子。 そして同時に、それを記事の形で説明してください。 筆者は本当に役に立たないが、この記事が誰かの利益になることを願っている。



アルゴリズムの説明


効果に基づいて、これはテキストで指定された輪郭に従って切り取られたパーリンノイズ( doc )です。 テキストの上にはマスクがあり、軸OXに沿って右に移動し、テキストを開きます。 メインループでは、マスクの右端がどこにあるかを判断し、Yごとに、テキストを含むビットマップの高さによって、不透明ピクセルを取得し、アニメーションを開始します。 さらに、ここで「溶接」ポイントがどこにあるかを示します。



実装


テキストはBlendMode.ERASEを使用してカットされます。これは、混合オブジェクトのアルファ値に基づいて背景オブジェクトのピクセルを消去します。 つまり、アルファが0xFFに等しい場合、背景のアルファチャネルの値は0x00になります。



ノイズのあるテキスト

テキスト付きのビットマップを準備する方法。

コード
private function GetMaskedText(text:String):BitmapData { var tf:TextField = new TextField(); var format:TextFormat = new TextFormat("Arial", 60, 0xFFFFFF, true); tf.defaultTextFormat = format; tf.text = text; tf.width = tf.textWidth + 4.0; tf.height = tf.textHeight + 4.0; tf.filters = [new GlowFilter(0xFFFFFF, 1.0, 2, 2, 4, 3)]; var w:int = tf.width; var h:int = tf.height; var noiseBdata:BitmapData = new BitmapData(w, h, true, 0xFFFFFFFF); //         var channels:int = BitmapDataChannel.GREEN | BitmapDataChannel.RED; //    noiseBdata.perlinNoise( w / 6 , h / 4 , 6 , int(Math.random() * 1000) , false , false , channels , false); //    noiseBdata.colorTransform(noiseBdata.rect, new ColorTransform(1.4, 1.4, 1.4)); var noiseBmp:Bitmap = new Bitmap(noiseBdata); //    var textBdata:BitmapData = new BitmapData(w, h, true, 0x00000000); textBdata.draw(tf); var textBmp:Bitmap = new Bitmap(textBdata); //          var eraseTextBdata:BitmapData = noiseBdata.clone(); eraseTextBdata.draw(noiseBmp); eraseTextBdata.draw(textBmp, null, null, BlendMode.ERASE); var eraseTextBmp:Bitmap = new Bitmap(eraseTextBdata); //  ,      var eraseBackByTextBdata:BitmapData = noiseBdata.clone(); eraseBackByTextBdata.draw(eraseTextBmp, null, null, BlendMode.ERASE); eraseBackByTextBdata.applyFilter( eraseBackByTextBdata , eraseBackByTextBdata.rect , new Point() , new GlowFilter(0xFFFFFF, 1.0, 3, 3) ); noiseBdata.dispose(); textBdata.dispose(); eraseTextBdata.dispose(); head = new BitmapData(6, 6, true, 0xffFFFFFF); return eraseBackByTextBdata; }
      
      









次に、テキストをシーンに追加し、マスクを上に配置する必要があります。

コード
 private var textBdata:BitmapData; private var textBmp:Bitmap; private var txtMask:Shape; private var btnRestart:MiniButton; private function InitalizeLayout():void { screen = new BitmapData(W, H, true, 0x00000000); addChild(new Bitmap(screen)); textBdata = GetMaskedText("..Hello world.."); textBmp = new Bitmap(textBdata); textBmp.x = (W - textBmp.width) * 0.5; textBmp.y = (H - textBmp.height) * 0.5; addChild(textBmp); txtMask = new Shape(); txtMask.graphics.beginFill(0xFFFFFF); txtMask.graphics.drawRect(0, 0, 4, textBmp.height); txtMask.graphics.endFill(); txtMask.x = textBmp.x; txtMask.y = textBmp.y; textBmp.mask = txtMask; addChild(txtMask); btnRestart = new MiniButton("restart"); btnRestart.x = (W - MiniButton.W) * 0.5; btnRestart.y = 10.0; addChild(btnRestart); }
      
      







画面変数は、パーティクルが描画される大きなBitmapDataです。 すぐに小さな警告があります。パーティクルを保存するために、 Vectorの代わりに単純にリンクされたリストを使用することにしました。パーティクルを頻繁に削除してvector.spliceを作成する必要があるためです。 また、リストから要素を削除する方が簡単です。要素を除外し、近隣からのリンクを変更するだけです。

クラス粒子

コード
 class Particle { private static const GRAVITY:Point = new Point(0, 0.2); private var speed:Point; public var position:Point; public var color:int; public var next:Particle; public function Particle(x:Number, y:Number, color:int) { position= new Point(x, y); //    speed = new Point(); speed.x = Math.random() * 10 - 2; speed.y = Math.random() * 1 - 4; this.color = color; } public function Update():void { speed = speed.add(GRAVITY); pos = pos.add(speed); } }
      
      







アニメーションを再開するには、再起動ボタンが必要です。 ボタンには2つの状態があります-ホバー中とハイライト中に強調表示されます。

コード
 class MiniButton extends Sprite { private var tf:TextField; public static const W:int = 100; public static const H:int = 24; public function MiniButton(text:String) { tf = new TextField(); var format:TextFormat = new TextFormat( "Arial" , 16 , 0x676767 , true , null, null, null, null , TextFormatAlign.CENTER); tf.defaultTextFormat = format; tf.mouseEnabled = false; tf.text = text; tf.width = W; tf.height = H; Redraw(0xB3F7B6); this.filters = [new GlowFilter(0xFFFFFF, 0.5, 14, 14, 3, 3)]; mouseChildren = false; buttonMode = true; addEventListener(MouseEvent.ROLL_OVER, HandleRollOver); addEventListener(MouseEvent.ROLL_OUT, HandleRollOut); } private function Redraw(color:int):void { graphics.clear(); graphics.beginFill(0xB3F7B6); graphics.drawRoundRect(0, 0, W, H, 16); graphics.endFill(); } private function HandleRollOut(e:MouseEvent):void { Redraw(0xB3F7B6); } private function HandleRollOver(e:MouseEvent):void { Redraw(0x37EC41); } }
      
      







アニメーションを再開および停止する方法。

コード
 private function Reset():void { btnRestart.visible = false; txtMask.width = 4; isRunning = true; addEventListener(Event.ENTER_FRAME, HandleEnterFrame); } private function Stop():void { btnRestart.visible = true; isRunning = false; removeEventListener(Event.ENTER_FRAME, HandleEnterFrame); }
      
      







必要な機能が準備できました。 コンストラクターで、 InitializeLayoutを呼び出してアニメーションを開始します。

コード
 public function Main() { InitalizeLayout(); Reset(); btnRestart.addEventListener(MouseEvent.CLICK, HandleResetClick); }
      
      







これがメインサイクルですが、やや面倒なことがわかりました。

コード
 private var firstParticle:Particle; private function HandleEnterFrame(e:Event):void { if (!isRunning) return; //    screen.lock(); screen.fillRect(screen.rect, 0x00000000); // X    var currentX:int = (txtMask.x + txtMask.width) - textBmp.x; if (currentX >= 0 && currentX < textBmp.width) { //      var color:int; var alpha:int; while (currentY < txtMask.height) { //    color = textBdata.getPixel32(currentX, currentY); alpha = (color >> 24) & 0xFF; //    if (alpha > 0x7f) { //          alpha /= 1.4; color = alpha << 24 | (color & 0xFFFFFF); for (var i:int = 0; i < 8; ++i) { var pp:Particle = new Particle(txtMask.x + txtMask.width, txtMask.y + currentY + i, color); if (firstParticle == null) { firstParticle = pp; } else { pp.next = firstParticle; firstParticle = pp; } } currentY += 6; screen.copyPixels(head, head.rect, new Point( txtMask.x + txtMask.width , txtMask.y + currentY - head.height / 2 ) ); screen.applyFilter( screen , screen.rect , new Point() , new BlurFilter(2, 2) ); break; } currentY += 2; } } var p:Particle = firstParticle; var prev:Particle; while (p != null) { p.Update(); //         if (p.pos.x < 0 || p.pos.y < 0 || p.pos.x > W || p.pos.y > H) { //     if (prev == null) { p = p.next; firstParticle = p; continue; } else { prev.next = p.next; } } //    var clr:int = pc; screen.setPixel32(p.pos.x, p.pos.y, clr); screen.setPixel32(p.pos.x-1, p.pos.y, clr); screen.setPixel32(p.pos.x+1, p.pos.y, clr); screen.setPixel32(p.pos.x, p.pos.y-1, clr); screen.setPixel32(p.pos.x, p.pos.y + 1, clr); prev = p; p = p.next; } //   ""  screen.applyFilter(screen, screen.rect, new Point(), new GlowFilter(0xFFFF00, 0.8, 10, 10)); screen.applyFilter(screen, screen.rect, new Point(), new BlurFilter(2, 2)); screen.unlock(); if (currentY >= txtMask.height) { currentY = 0; txtMask.width += 2; } if (txtMask.width >= textBmp.width) { if (firstParticle == null) { Stop(); } } }
      
      









PS。 念のため、完全なソース

コード
 package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.BitmapDataChannel; import flash.display.BlendMode; import flash.display.Shape; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.MouseEvent; import flash.filters.BlurFilter; import flash.filters.GlowFilter; import flash.geom.ColorTransform; import flash.geom.Point; import flash.text.TextField; import flash.text.TextFormat; /** * ... * @author KeeReal */ public class Main extends Sprite { //- PRIVATE & PROTECTED VARIABLES ------------------------------------------------------------------------- private var textBdata:BitmapData; private var textBmp:Bitmap; private var screen:BitmapData; private var head:BitmapData; private var txtMask:Shape; private var firstParticle:Particle; private var btnRestart:MiniButton; private var isRunning:Boolean; private var currentY:int = 0; //- PUBLIC & INTERNAL VARIABLES --------------------------------------------------------------------------- public static const W:int = 460; public static const H:int = 240; //- CONSTRUCTOR ------------------------------------------------------------------------------------------- public function Main() { stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP; InitalizeLayout(); Reset(); btnRestart.addEventListener(MouseEvent.CLICK, HandleResetClick); } //- PRIVATE & PROTECTED METHODS --------------------------------------------------------------------------- private function Reset():void { btnRestart.visible = false; txtMask.width = 4; isRunning = true; addEventListener(Event.ENTER_FRAME, HandleEnterFrame); } private function Stop():void { btnRestart.visible = true; isRunning = false; removeEventListener(Event.ENTER_FRAME, HandleEnterFrame); } private function InitalizeLayout():void { screen = new BitmapData(W, H, true, 0x00000000); addChild(new Bitmap(screen)); textBdata = GetMaskedText("..Hello habr.."); textBmp = new Bitmap(textBdata); textBmp.x = (W - textBmp.width) * 0.5; textBmp.y = (H - textBmp.height) * 0.5; addChild(textBmp); txtMask = new Shape(); txtMask.graphics.beginFill(0xFFFFFF); txtMask.graphics.drawRect(0, 0, 4, textBmp.height); txtMask.graphics.endFill(); txtMask.x = textBmp.x; txtMask.y = textBmp.y; textBmp.mask = txtMask; addChild(txtMask); btnRestart = new MiniButton("restart"); btnRestart.x = (W - MiniButton.W) * 0.5; btnRestart.y = 10.0; addChild(btnRestart); } private function GetMaskedText(text:String):BitmapData { var tf:TextField = new TextField(); var format:TextFormat = new TextFormat("Arial", 60, 0xFFFFFF, true); tf.defaultTextFormat = format; tf.text = text; tf.width = tf.textWidth + 4.0; tf.height = tf.textHeight + 4.0; tf.filters = [new GlowFilter(0xFFFFFF, 1.0, 2, 2, 4, 3)]; var w:int = tf.width; var h:int = tf.height; var noiseBdata:BitmapData = new BitmapData(w, h, true, 0xFFFFFFFF); var channels:int = BitmapDataChannel.GREEN | BitmapDataChannel.RED; noiseBdata.perlinNoise(w / 6, h / 4, 6, int(Math.random() * 1000), false, false, channels, false); noiseBdata.colorTransform(noiseBdata.rect, new ColorTransform(1.4, 1.4, 1.4)); var noiseBmp:Bitmap = new Bitmap(noiseBdata); var textBdata:BitmapData = new BitmapData(w, h, true, 0x00000000); textBdata.draw(tf); var textBmp:Bitmap = new Bitmap(textBdata); var eraseTextBdata:BitmapData = noiseBdata.clone(); eraseTextBdata.draw(noiseBmp); eraseTextBdata.draw(textBmp, null, null, BlendMode.ERASE); var eraseTextBmp:Bitmap = new Bitmap(eraseTextBdata); var eraseBackByTextBdata:BitmapData = noiseBdata.clone(); eraseBackByTextBdata.draw(eraseTextBmp, null, null, BlendMode.ERASE); eraseBackByTextBdata.applyFilter( eraseBackByTextBdata , eraseBackByTextBdata.rect , new Point() , new GlowFilter(0xFFFFFF, 1.0, 3, 3) ); noiseBdata.dispose(); textBdata.dispose(); eraseTextBdata.dispose(); head = new BitmapData(6, 6, true, 0xffFFFFFF); return eraseBackByTextBdata; } //- PUBLIC & INTERNAL METHODS ----------------------------------------------------------------------------- //- EVENT HANDLERS ---------------------------------------------------------------------------------------- private function HandleResetClick(e:MouseEvent):void { Reset(); } private function HandleEnterFrame(e:Event):void { if (!isRunning) return; screen.lock(); screen.fillRect(screen.rect, 0x00000000); var currentX:int = (txtMask.x + txtMask.width) - textBmp.x; if (currentX >= 0 && currentX < textBmp.width) { var color:int; var alpha:int; while (currentY < txtMask.height) { color = textBdata.getPixel32(currentX, currentY); alpha = (color >> 24) & 0xFF; if (alpha > 0x7f) { alpha /= 1.4; color = alpha << 24 | (color & 0xFFFFFF); for (var i:int = 0; i < 8; ++i) { var pp:Particle = new Particle(txtMask.x + txtMask.width, txtMask.y + currentY + i, color); if (firstParticle == null) { firstParticle = pp; } else { pp.next = firstParticle; firstParticle = pp; } } currentY += 6; screen.copyPixels(head, head.rect, new Point( txtMask.x + txtMask.width , txtMask.y + currentY - head.height / 2 ) ); screen.applyFilter( screen , screen.rect , new Point() , new BlurFilter(2, 2) ); break; } currentY += 2; } } var p:Particle = firstParticle; var prev:Particle; while (p != null) { p.Update(); if (p.position.x < 0 || p.position.y < 0 || p.position.x > W || p.position.y > Main.H) { if (prev == null) { p = p.next; firstParticle = p; continue; } else { prev.next = p.next; } } var clr:int = p.color; screen.setPixel32(p.position.x, p.position.y, clr); screen.setPixel32(p.position.x - 1, p.position.y, clr); screen.setPixel32(p.position.x + 1, p.position.y, clr); screen.setPixel32(p.position.x, p.position.y - 1, clr); screen.setPixel32(p.position.x, p.position.y + 1, clr); prev = p; p = p.next; } screen.applyFilter(screen, screen.rect, new Point(), new GlowFilter(0xFFFF00, 0.8, 10, 10)); screen.applyFilter(screen, screen.rect, new Point(), new BlurFilter(2, 2)); screen.unlock(); if (currentY >= txtMask.height) { currentY = 0; txtMask.width += 2; } if (txtMask.width >= textBmp.width) { if (firstParticle == null) { Stop(); } } } //- GETTERS & SETTERS ------------------------------------------------------------------------------------- //- HELPERS ----------------------------------------------------------------------------------------------- } } import flash.display.Sprite; import flash.events.MouseEvent; import flash.filters.GlowFilter; import flash.geom.Point; import flash.text.TextField; import flash.text.TextFormat; import flash.text.TextFormatAlign; class Particle { private static const GRAVITY:Point = new Point(0.0, 0.2); private var speed:Point; public var position:Point; public var color:int; public var next:Particle; public function Particle(x:Number, y:Number, color:int) { position = new Point(x, y); speed = new Point(); speed.x = Math.random() * 10 - 2; speed.y = Math.random() * 1 - 4; this.color = color; } public function Update():void { speed = speed.add(GRAVITY); position = position.add(speed); } } class MiniButton extends Sprite { private var tf:TextField; public static const W:int = 100; public static const H:int = 24; public function MiniButton(text:String) { tf = new TextField(); var format:TextFormat = new TextFormat( "Arial" , 16 , 0x676767 , true , null, null, null, null , TextFormatAlign.CENTER); tf.defaultTextFormat = format; tf.mouseEnabled = false; tf.text = text; tf.width = W; tf.height = H; Redraw(0xB3F7B6); this.filters = [new GlowFilter(0xFFFFFF, 0.5, 14, 14, 3, 3)]; mouseChildren = false; buttonMode = true; addEventListener(MouseEvent.ROLL_OVER, HandleRollOver); addEventListener(MouseEvent.ROLL_OUT, HandleRollOut); } private function Redraw(color:int):void { graphics.clear(); graphics.beginFill(0xB3F7B6); graphics.drawRoundRect(0, 0, W, H, 16); graphics.endFill(); } private function HandleRollOut(e:MouseEvent):void { Redraw(0xB3F7B6); } private function HandleRollOver(e:MouseEvent):void { Redraw(0x37EC41); } }
      
      








All Articles