headParticle

Particle ที่เราเห็นชาวบ้านเค้าเขียนกัน แสดงผลด้วย bitmapData จริงๆแล้วมันไม่ได้ยากอย่างที่คิดซักเท่าไหร่


นี่คือตัวที่ผมเขียนขึ้นมาทดสอบ แต่สำหรับเบื้องต้นแล้ว เราจะมาลองทำแบบง่ายๆกันก่อน

ก่อนจะเริ่มดูโค้ดกัน ผมขอสรุปก่อนเลยละกันว่าการทำงานของมันเป็นยังไงกันแน่

แนวทางการคิด หลักๆแล้วมีอยูี่่ไม่กี่ส่วนคือ

1.สร้าง คลาส พาร์ติเคิล ขึ้นมาแบบง่ายๆ หรือจะซับซ้อนก็แล้วแต่ใครจะออกแบบ
ภายในตัวมันจะเก็บข้อมูลง่ายๆไม่กี่อย่าง เช่น ขนาด ตำแหน่ง x y แรง หรือความเร่ง ที่จะให้มันวิ่งไป

2.สร้าง instance ของพาร์ติเคิล ออกมาให้มากพอจะทดสอบมัน

3.เก็บ instance พวกนั้นไว้ในอาเรย์ หรือถ้าจะให้เร็วมากขึ้น สำหรับ flash player10 ก็ลองใช้ Vector ดู

4.คำนวนให้มันเคลื่อนไหว โดยย้ายตำแหน่งของมันไปเรื่อยๆ  ซึ่งการย้ายตำแหน่ง ทำได้ตั้งแต่ง่ายๆ คือเคลื่อนไหวเป็นเส้นตรง หรือแม้กระทั่ง ใช้ physic เข้ามาเกี่ยวข้อง มีแรงโน้วถ่วง มีแรงดึงดูด มีความเฉื่อย

5. จากนั้นก็ สร้างเม็ดสี ลงบน bitmapData ตามข้อมูลตำแหน่งของคลาสพาร์ติเคิลที่เราสร้างขึ้นมา

6.ถ้าจะให้สวยงาม ก็ใส่พวก blur ลงไป อาจจะ colorTransform ลงไปง่ายๆ

สำหรับขั้นตอนแรก เรามาเริ่มที่ คลาส พาร์ติเคิลง่ายๆกันก่อนแล้วกัน

สร้างไฟล์คลาสชื่อว่า  Particle.as  แล้วใส่สคริปลงไปว่า

Particle.as

package
{
	/**
	 * ...
	 * @author heart
	 */
	public class Particle
	{
		public var x:Number;
        public var y:Number;
        public var speed:Number;
		public function Particle()
		{
		}
 
	}
 
}

เราจะสังเกตุได้ว่า คลาส Particle ไม่ได้มีอะไร ซับซ้อน มีเพียงข้อมูล property 3 ตัว นั่นก็คือ

x , y ไว้เก็บตำแหน่งพิกัด
speed ตั้งไว้เก็บค่า speed ของมัน

เมื่อเราได้ คลาส particle เราก็ลองเอามาใช้งานง่ายๆ บนคลาส Main กันดีกว่า

ผมจะใช้ FlashDevelop และคอมไพล์บน Flex SDK นะครับ

Main.as

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Rectangle;
 
	/**
	 * ...
	 * @author heart
	 */
	[SWF(width="300",height="300")]
	public class Main extends Sprite
	{
		private var pars:Array;
		private var canvas:Bitmap;
		private var bData:BitmapData;
		public function Main():void
		{
			//สร้าง Particle 50 ตัว
			pars = new Array();
			for (var i:Number = 0; i < 50 ; i++ ) {
				var p:Particle = new Particle();
				p.x = Math.random() * stage.stageWidth;
				p.y = Math.random() * stage.stageHeight;
				p.speed = 1 + (Math.random() * 10);
				pars.push(p);
			}
			//สร้าง Bitmap และ BitmapData ไว้แสดงผล
			bData = new BitmapData(stage.stageWidth, stage.stageHeight, false , 0x000000 );
			canvas = new Bitmap(bData);
			addChild(canvas);
			//เริ่ม Render กัน
			this.addEventListener(Event.ENTER_FRAME , render);
		}
		private function render(e:Event):void {
			//สั่งลบจุดสีทั้งหมด ก่อนจะวาดใหม่ เพื่อไม่ให้จำภาพเดิม จะได้เกิดภาพเคลื่อนไหว
			bData.fillRect(new Rectangle(0, 0, bData.width , bData.height), 0x000000);
			//วน LOOP เซ็ทจุดสี ให้ตรงกับตำแหน่งของ particle ทุกตัว
			for (var i:Number = 0; i < 50 ; i++ ) {
				var p:Particle = pars[i] as Particle;
				bData.setPixel( p.x , p.y , 0x00CCCC );
				//สั่งให้ Particle เซ็ทตำแหน่งแกน x ใหม่ เหมือนกับ วิ่งไปด้านข้าง ตามความเร็ว speed ของตัวเอง
				p.x += p.speed;
				//ถ้าชนขอบ ให้เด้งกลับไป กลับมา
				if (p.x <  0 ) { 					p.speed *= -1; 				}else if (p.x > stage.width) {
					p.speed += -1;
				}
			}
		}
	}
 
}

ลองอ่านสคริปตามนะครับ ไม่ยาก สำหรับตอน render เราจะใช้สีดำทับลงไปหมดเลย เพื่อให้มันกลายเป็นสีดำ ก่อนจะวาดจุดสีลงไปใหม่
ไม่เช่นนั้น มันจะจำจุดสีเดิมด้วย

ผลออกมาก็เป็น

DOWNLOAD โค้ดได้ที่นี่ครับ

ถ้าหากว่า จะให้แสดงผลได้สวยเหมือนตัวอย่างด้านบนสุด ตัวนั้นผมใส่ BlurFilter ลงไปด้วย เลยเห็นว่า มันมีหางวิ่งตามด้วย
ถ้าเพิ่มลงไป เราก็จะเพิ่มในส่วนของ Render ไปแทนที่ การเติมเต็มพื้นผิว ด้วยพื้นสีดำ

เราจะได้คลาส Main ตามนี้ สังเกตุในส่วน render ที่เพิ่ม BlurFilter ลงไปด้วย

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.filters.BlurFilter;
	import flash.geom.Point;
	import flash.geom.Rectangle;
 
	/**
	 * ...
	 * @author heart
	 */
	[SWF(width="300",height="300")]
	public class Main extends Sprite
	{
		private var pars:Array;
		private var canvas:Bitmap;
		private var bData:BitmapData;
		public function Main():void
		{
			//สร้าง Particle 50 ตัว
			pars = new Array();
			for (var i:Number = 0; i < 50 ; i++ ) {
				var p:Particle = new Particle();
				p.x = Math.random() * stage.stageWidth;
				p.y = Math.random() * stage.stageHeight;
				p.speed = 1 + (Math.random() * 10);
				pars.push(p);
			}
			//สร้าง Bitmap และ BitmapData ไว้แสดงผล
			bData = new BitmapData(stage.stageWidth, stage.stageHeight, false , 0x000000 );
			canvas = new Bitmap(bData);
			addChild(canvas);
			//เริ่ม Render กัน
			this.addEventListener(Event.ENTER_FRAME , render);
		}
		private function render(e:Event):void {
			//สั่งลบจุดสีทั้งหมด ก่อนจะวาดใหม่ เพื่อไม่ให้จำภาพเดิม จะได้เกิดภาพเคลื่อนไหว
			//bData.fillRect(new Rectangle(0, 0, bData.width , bData.height), 0x000000);
			//เพิ่มการ Blur
			var blur:BlurFilter = new BlurFilter(2, 2);
			bData.applyFilter(bData , new Rectangle(0, 0, bData.width , bData.height) , new Point(0,0),blur)
			//วน LOOP เซ็ทจุดสี ให้ตรงกับตำแหน่งของ particle ทุกตัว
			for (var i:Number = 0; i < 50 ; i++ ) {
				var p:Particle = pars[i] as Particle;
				bData.setPixel( p.x , p.y , 0x00CCCC );
				//สั่งให้ Particle เซ็ทตำแหน่งแกน x ใหม่ เหมือนกับ วิ่งไปด้านข้าง ตามความเร็ว speed ของตัวเอง
				p.x += p.speed;
				//ถ้าชนขอบ ให้เด้งกลับไป กลับมา
				if (p.x < 0 ) { 					
    p.speed *= -1; 				
}else if (p.x > stage.width) {
					p.speed += -1;
				}
			}
		}
	}
 
}

ผลของการเปลี่ยนแปลง

Download ไฟล์โค้ด ที่นี่