『HTML5梦幻之旅』-缤纷多姿的烟花效果

天花无数月中开,五采祥云绕绛台。堕地忽惊星彩散,飞空旋作雨声来。怒撞玉斗翻晴雪,勇踏金轮起疾雷。更漏已深人渐散,闹竿挑得彩灯回。

——明·瞿佑·《烟火戏》

记得每年过春节的那段时间,除了欣赏隆冬的景色,剩下的就是欣赏天空中美丽的烟花了。

成都的冬天,天空中总是灰蒙蒙的,像是织了一层薄薄的轻纱,把阳光挡走了一部分。路边的枫树上,没有了夏日整天“知了”,“知了”的小家伙,是否有鸟儿,我却也忘了。树上的枫叶寥寥无几,可能是在某一时候,悄无声息地飘落了;有时路过一棵年岁已高的树,也不知是地球引力还是什么的原应,一片叶子会自然地落下来,不留神的话就会掉到你头顶上,顶着它走几里路了,还没发现。

大冷的天,躲在屋里,有时大风挂起,看着不停散落的枫叶,都开始害怕他们会冻成冰。有时敲代码不知不觉就到了傍晚,天更灰了,回身看看窗外的枫树,只见一片片的红云飘逸在眼前,随着风起而凋零。

除夕夜里,一般是在亲戚家过的,吃完了饭,一家人便一边欣赏提前放飞的烟花,一边聊天。屋里暖暖的,想到还在外面劳作的人们,不觉得会为他们打个寒战。

有时我和父母还有哥哥会提前回家,告别了总亲戚,便往屋里赶,希望还能赶上春节联欢晚会的开始(虽然现在很多人讨厌看春节联欢晚会,但我想这是一种传统,搞得不好可以提意见,但是好的传统应该继续传承下去,就像类的继承一样)。父亲是个不爱花冤枉钱的人,去某个地方从来不打车,要不自己走,要不就自己骑车,或者自己开车。因此在走回去的路上,我又可以观赏者隆冬的夜景了。

回家后,第一件事就是把年货拿出来,不停地吃,父亲一向不许我吃零食,但是过节的时候从来不阻止。记得有一年,父亲叫我和哥哥把所有吃剩的瓜子壳,橘子皮都扔在地上,我说难道不难收拾吗?父亲则说到时候扫一下就可以了,仍在地上才有节日的气氛。到现在来想想,过节不就是要气氛吗?没了气氛,也就没了温馨,没了温馨,这节日还有什么好过的呢?

当钟上的时针和分针都指在12点的位置上的时候,就是该观看烟花的时候了。各种各样的烟花被撒如天幕,绽放出各种五颜六色,奇形怪状的烟花粒子。有的升入云霄,忽地不见了,过一眨眼的功夫,便突然呈现为一点金色的小花,像夜幕中的星星一般。有的就给子弹一样,不停地连发出去,飞到一定高度也消失了,有人说这就是传说中的“冲天炮”,我觉得与其叫“冲天炮”,还不如叫火箭“喀秋莎”算了。烟花的声音可不是一般的混乱,轰隆隆的声响里,总少不了嗖嗖地烟花升天的声音。这些声音倒也吵人,倘若靠近声源处,旁边的人说话是听不见的。

可惜最近几年迷上了编程序,烟花也懒得看了,几年没欣赏烟花了,心里也惦记起来了。最近用html5做了一个拖尾效果,想到用拖尾可以做一个烟花效果,也就尝试做了一下,没想到不做不知道,原来实现起来这么简单。上面瞎扯了一大堆,大家见谅一下,接下来就给大家介绍一下是如何实现的。



先看一下游戏截图:




测试地址:http://www.cnblogs.com/yorhom/articles/3244140.html

用支持html5的浏览器打开就可以。


本次开发和上次一样,用到了开源引擎lufylegend,详细信息如下

lufylegend官方网站:http://lufylegend.com/lufylegend

lufylegend API文档:http://lufylegend.com/lufylegend/api


※注意:在了解了引擎lufylegend的前提下阅读本文方可没有障碍。


接下来是实现过程。


一,改进拖尾类


在上一节《『HTML5梦幻之旅』-炫丽的流星雨效果》中,我们讲解了Smearing这个类,这个类主要用于现实拖尾等效果。上一节中,我们讲到了Smearing.to()方法里的时候,谈到如果对象移动完毕时,自动将自己的mode设置为"complete"。实现这个方面的时候,我们直接将缓动的数据的onComplete进行更改,以达到效果。后来发现这样做不好,因此改进了一下:想将原数据的onComplete保存到一个变量中,再更改onComplete里的数据,更改为先调用先前保存的onComplete,然后再将mode改为"complete",这样的话,用户就可以自己设定onComplete里的内容了。Smearing.to()方法里的代码改为如下:

 

Smearing.prototype.to = function($duration,$vars){	
	var self = this;
	
	var customFunc = $vars.onComplete || function(){};
	
	$vars.onComplete = function(){
		customFunc();
		self.mode = "complete";
	}
	LTweenLite.to(self.originalSprite,$duration,$vars);
};

 

代码清单1

 

 

二,烟花类Fireworks


为了实现烟花效果,我们封装一个叫Fireworks的类,类的构造器如下:

 

function Fireworks(x,y,color){
	var self = this;
	base(self,LSprite,[]);
	
	self.fireworksX = x;
	self.fireworksY = y;
	
	self.angle = 20;
	self.count = 18;
	
	self.smearingColor = color;
	
	self._showFireworks();
}

 

代码清单2

这段代码其实不难理解,首先和拖尾类一样,继承自LSprite,然后把前三个参数保存进自身属性里面,中间有一段代码,如下:

 

 

self.angle = 20;
self.count = 18;

 

代码清单3

 

这两行代码看似平凡,却十分重要。由于我们的烟花是一圈一圈的,所以,我们在现实烟花喷出来的粒子的时候,要计算出每个烟花的位置。要确定位置,就要确定每个烟花的角度,以及所有烟花粒子的个数,为了实现这个,我们设置这两个属性。angle属性是当前烟花粒子要到达的位置与圆心的连线和上一个烟花粒子要到达的位置与圆心的连线的夹角。count属性是所有烟花粒子的个数。可以看到,如果angle和count想乘,积是360,也就是一圈的度数。

我们每个粒子要到达的位置图示如下:


这一点想通了就不难了,最后我们调用成员函数_showFireworks(),通过掉用这个函数实现显示烟花。具体代码如代码清单4:

 

Fireworks.prototype._showFireworks = function(){
	var self= this;
	var kaku;
	
	for(var i=0; i<self.count; i++){
		kaku = i*self.angle;
		var toX = 100*Math.sin(kaku * Math.PI / 180);
		var toY = 100*Math.cos(kaku * Math.PI / 180);
		
		var smearingLayer = new LGraphics();
		smearingLayer.drawArc(0,"",[0,0,5,0,2*Math.PI],true,self.smearingColor);
		
		var spreadingSmearing = new Smearing(smearingLayer);
		spreadingSmearing.x = self.fireworksX;
		spreadingSmearing.y = self.fireworksY;
		spreadingSmearing.to(1,{
			x: toX,
			y: toY,
			onComplete:function(){
				self.mode = "complete";
			}
		});
		self.addChild(spreadingSmearing);
	}
};

 

代码清单4

前两行代码跳过,因为js程序员都应该知道。然后我们跳到最重要的循环环节,整理后代码如下:

 

 

for(var i=0; i<self.count; i++){
	kaku = i*self.angle;
	var toX = 100*Math.sin(kaku * Math.PI / 180);
	var toY = 100*Math.cos(kaku * Math.PI / 180);
	
	var smearingLayer = new LGraphics();
	smearingLayer.drawArc(0,"",[0,0,5,0,2*Math.PI],true,self.smearingColor);
	
	var spreadingSmearing = new Smearing(smearingLayer);
	spreadingSmearing.x = self.fireworksX;
	spreadingSmearing.y = self.fireworksY;
	spreadingSmearing.to(1,{
		x: toX,
		y: toY,
		onComplete:function(){
			self.mode = "complete";
		}
	});
	self.addChild(spreadingSmearing);
}

 

代码清单5

首先我们为了达到粒子环形围绕效果,我们通过循环,取出每个点的角度,并且通过Math.sin()和Math.cos()两个函数确定该粒子的位置。然后我们画出一个圆形,颜色是我们实例化时给的color参数的值,上面我们已经把它保存起来了,所以这里直接用。画好之后,我们给这个粒子建立拖尾效果。该粒子拖尾的位置是我们保存好的x,y参数。接着通过to方法让改拖尾移动到我们指定的位置。当移动到位后,我们就通过onComplete加入完成时的函数,将该烟花类的mode设置为"complete",因为我们在上面更改了to方法,所以可以自定义onComplete里的东西了。

 

烟花类就Over了,接下来是有了这些封装之后,如何实现的方法。


三,溅入空中的烟花


先把所有代码放在下面:

 

init(10,"mylegend",500,500,main);
var backLayer,fireworksLayer;
var back;
//烟花颜色集
var colorArray = new Array(
	"yellow",
	"orangered",
	"red",
	"pink"
);
//加入烟花最大数量
var maxFrame = 4;
//当前加入烟花数量
var frameIndex = 0;
var sound;
function main(){
	LStage.setDebug(true);
	
	//加入音乐
	sound = new LSound("http://stream20.qqmusic.qq.com/34962638.mp3");
	
	//加入底板层
	backLayer = new LSprite();
	addChild(backLayer);
	//加入烟花层
	fireworksLayer = new LSprite();
	addChild(fireworksLayer);
	
	//画一个黑色矩形作为背景
	back = new LGraphics();
	back.drawRect(0,"",[0,0,LStage.width,LStage.height],true,"black");
	backLayer.addChild(back);
	
	//加入时间轴事件
	backLayer.addEventListener(LEvent.ENTER_FRAME,onframe)
}
function addFireworks(){
	var toY = Math.floor(Math.random() * (-350 + 250) - 250);
	
	var colorIndex = Math.floor(Math.random() * 4)
	//画一个黄色矩形作为一颗升天的烟花
	var fireworks = new LSprite();
	fireworks.x = Math.floor(Math.random() * (480 - 20) + 20);
	fireworks.y = 500;
	fireworks.graphics.drawRect(0,"",[0,0,10,10],true,colorArray[colorIndex]);
	
	//为升起的烟花添加一个拖尾
	var smearing = new Smearing(fireworks,50);
	//移动烟花
	smearing.to(1,{
		x: 0,
		y: toY,//-300
		onComplete:function(){
			//添加扩散开的烟花
			var spreading = new Fireworks(fireworks.x,fireworks.y+toY,colorArray[colorIndex]);
			fireworksLayer.addChild(spreading);
		}
	});
	fireworksLayer.addChild(smearing);
}
function onframe(){
	//加入烟花
	if(frameIndex < maxFrame){
		frameIndex ++;
		addFireworks();
	}
	//播放音乐
	if(sound.playing == false){
		sound.play();
	}
	//移除烟花
	for(var key in fireworksLayer.childList){
		if(fireworksLayer.childList[key].mode == "complete"){
			//通过缓动更改烟花透明度
			LTweenLite.to(fireworksLayer.childList[key],0.3,{
				alpha:0,
				onComplete:function(o){
					//移除对象
					fireworksLayer.removeChild(o);
					//如果界面上没有烟花,将已经加入数量设为0
					if(fireworksLayer.childList.length == 0){
						frameIndex = 0;
					}
				}
			});
		}
	}
}

 

代码清单6

代码都加了注释,我只讲一下设计方案:因为我们的烟花是五颜六色的,所以我们将要有的颜色全部放入colorArray中,然后我们在main中初始化层,并加入背景,音乐,时间轴事件。在时间轴事件触发的onframe函数中,我们通过addFireworks来加烟花。在addFireworks中,我们首先取出一个烟花的颜色,并以这种颜色画出相应的烟花,然后通过拖尾效果中的to方法送这个烟花上天,然后在上天动作完成的时候,我们加入散开的粒子效果,如果我们反复调用addFireworks就会反复出现烟花。由于我们的界面上不能出现太多的烟花,因为一方面会显得拥挤看不清楚效果,另一方面就是会导致界面很卡。为了解决这个问题,我们通过,maxFrame和frameIndex两个变量来控制。当onframe每触发一次,我们就将frameIndex加一,然后判断是否小于maxFrame,如果是,就调用addFireworks(),因为我们在上面定义的maxFrame为4,所以最多加4个烟花到界面上。但是如果我们不把frameIndex重新设置为0的话,且不移除烟花的话,那4个烟花就会一直在屏幕上,一不会消失,而不会重新加入。为了实现这些,我们加入循环,在循环里通过判断mode是否为"complete"来移除对象,通过判断fireworksLayer里的对象是否被移除完了来判断是否把frameIndex重新设置为0。通过这一个系统,就会在画面上不断地出现烟花并消失。这个效果就搞定了~~~~

 


最后运行出来,连我都感叹道:“这不是久违不见的烟花君吗?”

lufy长者也曰到:“效果很漂亮啊。”

嘿嘿,既然专家都说漂亮了,大家不赶快试试?


最后奉上源代码:https://files.cnblogs.com/yorhom/fireworks.rar

 

话说这烟花效果效率不高,先截了一次放20个烟花的截图:


最后我冒着卡死的风险搞了一次放1000个烟花的效果,居然还可以运行,截图如下:


如果大家要测试效率的话,用把变量maxFrame调大些就可以了。


本篇文章就到此结束了。文章如有什么地方写得不妥,欢迎提出。另外,如果有任何不解的地方,可以在博客下方留言或者新浪微博@Yorhom,当然也可以发邮件,邮箱:wangyuehao1999@gmail.com,我会尽我所能帮你解决。

支持就是最大的鼓励!


 

----------------------------------------------------------------

欢迎大家转载我的文章。

转载请注明:转自Yorhom's Game Box

http://blog.csdn.net/yorhomwang

欢迎继续关注我的博客

 

posted on 2013-08-09 23:50  you Richer  阅读(459)  评论(0编辑  收藏  举报