使用matlab制作漂亮的烟花,核心实现在于烟花图式的控制和关于如何控制多支烟花的同时释放。烟花样式的呈现需要经过复杂的数学表达式生成,升级到发射的角度和位移事变公式等,而多支烟花的同时释放难题源于matlab对多线程的不支持,幸运的是,matlab有Timer定时器,这样,我们可以实现单线程的非阻塞式烟花齐放。先上一张烟花爆炸的图形。
程序还支持在爆炸过程中即时随机改变烟花颜色,实现五彩缤纷的效果。
在程序中,我们可以通过滚动条调整RBG动态调整烟花颜色,可以改变烟花爆炸的速度和烟花爆炸后的颗粒数量,此外,还有下落的加速度。在上侧菜单中,我们可以选择不同的爆炸风格图案,背景图片,颜色等。这些都是通过回调函数的触发动态改变。
下面附上程序源码,大家有觉得写得不好的地方或不理解的地方都可以在下面评论讨论。
function yanhuamoban()
clear all;
global ah ;
global styleNum ;
global multiColor;
global color;
global v0;
global n;
global g;
multiColor = 0;
styleNum = 3;
color = [1 1 0];
v0 = 250;
n = 2000;
g=1000;
fig = figure('units','normalized','position',[0.1 0.1 0.6 0.8],...
'menubar','none','numberTitle','off','Name','烟花欣赏','WindowButtonDownFcn',@yanhua...
);
file_menu = uimenu(fig,'Label','文件(&f)');
sub_file_menu1 = uimenu(file_menu,'Label','退出(&q)','CallBack',@file_menu_callback);
style_menu = uimenu(fig,'Label','爆炸风格(&s)');
sub_style_menu1 = uimenu(style_menu,'Label','风格(&1)','CallBack',@style_menu_callback);
sub_style_menu2 = uimenu(style_menu,'Label','风格(&2)','CallBack',@style_menu_callback);
sub_style_menu3 = uimenu(style_menu,'Label','风格(&3)','CallBack',@style_menu_callback);
sub_style_menu4 = uimenu(style_menu,'Label','风格(&4)','CallBack',@style_menu_callback);
sub_style_menu5 = uimenu(style_menu,'Label','风格(&5)','CallBack',@style_menu_callback);
sub_style_menu6 = uimenu(style_menu,'Label','风格(&6)','CallBack',@style_menu_callback);
picture_menu = uimenu(fig,'Label','背景图片(&p)');
sub_picture_menu1 = uimenu(picture_menu,'Label','图片(&1)','CallBack',@picture_menu_callback);
sub_picture_menu2 = uimenu(picture_menu,'Label','图片(&2)','CallBack',@picture_menu_callback);
sub_picture_menu3 = uimenu(picture_menu,'Label','图片(&3)','CallBack',@picture_menu_callback);
sub_picture_menu4 = uimenu(picture_menu,'Label','图片(&4)','CallBack',@picture_menu_callback);
sub_picture_menu5 = uimenu(picture_menu,'Label','图片(&5)','CallBack',@picture_menu_callback);
color_menu = uimenu(fig,'Label','烟花颜色(&c)');
sub_color_menu1 = uimenu(color_menu,'Label','黄色(&1)','CallBack',@color_menu_callback);
sub_color_menu2 = uimenu(color_menu,'Label','紫红色(&2)','CallBack',@color_menu_callback);
sub_color_menu3 = uimenu(color_menu,'Label','青色(&3)','CallBack',@color_menu_callback);
sub_color_menu4 = uimenu(color_menu,'Label','红色(&4)','CallBack',@color_menu_callback);
sub_color_menu5 = uimenu(color_menu,'Label','绿色(&5)','CallBack',@color_menu_callback);
sub_color_menu6 = uimenu(color_menu,'Label','蓝色(&6)','CallBack',@color_menu_callback);
sub_color_menu7 = uimenu(color_menu,'Label','白色(&7)','CallBack',@color_menu_callback);
sub_color_menu8 = uimenu(color_menu,'Label','五彩变换(&8)','CallBack',@color_menu_callback);
color_button_group = uibuttongroup(fig,'Title','颜色调节面板','Position',[0.83,0.55,0.16,0.44]);
speed_button_group = uibuttongroup(fig,'Title',regexprep('速度调节面板(num)','num',num2str(v0)),'Position',[0.83,0.36,0.16,0.185],'Tag','speed_panel');
number_button_group = uibuttongroup(fig,'Title',regexprep('数量调节面板(num)','num',num2str(n)),'Position',[0.83,0.005,0.16,0.35],'Tag','ammount_panel');
red_slider = uicontrol(fig,'Style','slider','String','red','Position',[695,450,25,140],'Min',0,'Max',1,'Value',1,'backgroundColor',[1,0,0],'CallBack',@slider_callback);
green_slider = uicontrol(fig,'Style','slider','String','green','Position',[735,450,25,140],'Min',0,'Max',1,'Value',1,'backgroundColor',[0,0,1],'CallBack',@slider_callback);
blue_slider = uicontrol(fig,'Style','slider','String','blue','Position',[775,450,25,140],'Min',0,'Max',1,'Value',0,'backgroundColor',[0,1,0],'CallBack',@slider_callback);
color_text = uicontrol(fig,'Style','text','Position',[690,395,115,50],'Tag','color_text',...
'String',char('red=1','blue=1','green=0'),'backgroundColor',[1,0.95,0.87],'FontSize',10,'HorizontalAlignment','left');
color_panel = uipanel(fig,'Title','颜色演示面板','BackgroundColor',color,'Units','pixels','Position',[690,345,115,40]);
fast_button = uicontrol(fig,'Style','pushbutton','Position',[700,280,100,40],'FontSize',10,'String','加快','CallBack',@button_callback);
slow_button = uicontrol(fig,'Style','pushbutton','Position',[700,230,100,40],'FontSize',10,'String','减慢','CallBack',@button_callback);
increase_button = uicontrol(fig,'Style','pushbutton','Position',[700,160,100,40],'FontSize',10,'String','增多','CallBack',@button_callback);
decrease_button = uicontrol(fig,'Style','pushbutton','Position',[700,110,100,40],'FontSize',10,'String','减少','CallBack',@button_callback);
modNum_button = uicontrol(fig,'Style','pushbutton','Position',[700,60,100,40],'FontSize',10,'String','手动调整数量','CallBack',@button_callback);
modG_button = uicontrol(fig,'Style','pushbutton','Position',[700,10,100,40],'FontSize',10,'String','手动调整加速度','CallBack',@button_callback);
axes('DrawMode','fast','position',[0,0,0.8,1]);
II=imread('back4.jpg');
image(II)
ah = axes('DrawMode','fast','Color','none','position',[0,0,0.8,1]);
axis([-200 420 1 410])
grid off
hold on
function yanhua(src,event)
cp = get(ah,'CurrentPoint');
xt = cp(1,1);yt = cp(1,2);
if(xt < 420)
launch(xt,yt)
end
end
end
function launch(xt,yt)
x=xt;
y=0;
H=plot(x,y,'w.');
set(H,'EraseMode','xor','MarkerSize',25)
T = timer('TimerFcn',@timer_display1,'StopFcn',@stopFcn1,'TasksToExecute',50,'Period',0.005,'ExecutionMode','fixedSpacing');
T.userData = {H,xt,yt,1};
start(T);
end
function timer_display1(obj,event)
userData = obj.userData;
H = userData{1};
x = userData{2};
yt = userData{3};
i = userData{4};
y=i/50*yt;
set(H,'XData',x,'YData',y)
drawnow;
i=i+1;
obj.userData = {H,x,yt,i};
end
function stopFcn1(obj,event)%定时器1完成了它的50次调用使命,使烟花(点)到达爆炸处(鼠标位置),这个烟花(点)在这里被消除,然后开始了烟花爆炸的定时器初始化、启动工作
global n;
global color;
userData = obj.userData;
H = userData{1}
xt = userData{2};
yt = userData{3};
x=-20;y=-20;
set(H,'XData',x,'YData',y);
x=zeros(n,1);
y=zeros(n,1);
h=plot(x,y,'.');
set(h,'Color',color);
set(h,'erasemode','xor','MarkerSize',5)
T = timer('TimerFcn',@timer_display2,'StopFcn',@stopFcn2,'TasksToExecute',40,'Period',0.05,'ExecutionMode','fixedSpacing');
T.userData = {h,xt,yt,0};
start(T);
end
function timer_display2(obj,event)
global n v0 styleNum multiColor g;
userData = obj.userData;
h = userData{1};
x0 = userData{2};
y0 = userData{3};
t = userData{4};
theta=rand(n,1)*2*pi;
fy=rand(n,1)*2*pi;
theta1=round(12*rand(n,1))/6*pi;
for i=1:n
switch(styleNum)
case 1
x(i)=2*v0*cos(fy(i))*cos(theta(i)+t*pi)*t+x0;
y(i)=1.2*v0*cos(fy(i))*sin(theta(i)+i*pi)*t-g*t.^2+y0;
case 2
if(mod(theta(i),0.4*pi)<=0.1*pi)
x(i)=2*v0*cos(fy(i))*cos(theta(i)-4*t*pi)*t+x0;
y(i)=1.2*v0*cos(fy(i))*sin(theta(i)-4*t*pi)*t-g*t.^2+y0;
else
x(i)=(mod(theta(i),0.4*pi)*2.5/pi+0.4)*2*v0*cos(fy(i))*cos(theta(i)-4*t*pi)*t+x0;
y(i)=(mod(theta(i),0.4*pi)*2.5/pi+0.4)*1.2*v0*cos(fy(i))*sin(theta(i)-4*t*pi)*t-g*t.^2+y0;
end
case 3
x(i)=2*v0*cos(fy(i))*cos(theta(i)+i*t*pi)*t+x0;
y(i)=1.2*v0*cos(fy(i))*sin(theta(i)+i*t*pi)*t-g*t.^2+y0;
case 4
x(i)=0.8*fy(i)/pi*2*v0*cos(fy(i))*cos(theta(i)+i*pi)*t+x0;
y(i)=0.8*fy(i)/pi*(1.2*v0*cos(fy(i))*sin(theta(i)+i*pi)*t-g*t.^2)+y0;
case 5
x(i)=4*v0*cos(fy(i))*cos(theta1(i))*t+x0;
y(i)=(4*v0*cos(fy(i))*sin(theta1(i)-cos(fy(i))*cos(theta1(i))*0.3*t*pi)*t-4*t.^2)+y0;
case 6
x(i)=v0*cos(fy(i))*cos(theta1(i)+4*t*pi)*t+x0;
y(i)=v0*cos(fy(i))*sin(theta1(i))*t-g*t.^2+y0;
end
end
set(h,'xdata',x,'ydata',y);
if(multiColor == 1)
set(h,'Color',rand(1,3));
end
drawnow;
t = t + 0.01;
obj.userData = {h,x0,y0,t};
end
function stopFcn2(obj,event)
global n;
userData = obj.userData;
h = userData{1};
x=zeros(n,1);
y=zeros(n,1);
set(h,'xdata',x,'ydata',y);
end
function button_callback(object,data,handles)
global v0 n g;
switch(object.String)
case '加快'
v0 = v0 + 50;
speed_panel = findobj(gcf,'Tag','speed_panel');
speed_panel.Title = regexprep(speed_panel.Title,'\d*',num2str(v0));
case '减慢'
v0 = v0 - 50;
speed_panel = findobj(gcf,'Tag','speed_panel');
speed_panel.Title = regexprep(speed_panel.Title,'\d*',num2str(v0));
case '增多'
n = n + 100;
ammount_panel = findobj(gcf,'Tag','ammount_panel');
ammount_panel.Title = regexprep(ammount_panel.Title,'\d*',num2str(n));
case '减少'
n = n - 100;
ammount_panel = findobj(gcf,'Tag','ammount_panel');
ammount_panel.Title = regexprep(ammount_panel.Title,'\d*',num2str(n));
case '手动调整数量'
ammount_input = inputdlg('请输入爆炸后烟花颗粒的数量','修改烟花数量',1);
num=str2num(ammount_input{1});
if isempty(num)
errordlg('必须输入有效数字','错误');
else
n = num;
ammount_panel = findobj(gcf,'Tag','ammount_panel');
ammount_panel.Title = regexprep(ammount_panel.Title,'\d*',num2str(n));
end
case '手动调整加速度'
ammount_input = inputdlg(strcat('请输入爆炸后烟花加速度值,当前值为:',num2str(g)),'修改加速度',1);
num=str2num(ammount_input{1});
if isempty(num)
errordlg('必须输入有效数字','错误');
else
g = num;
end
end
end
function style_menu_callback(object,data,handles)
global styleNum;
number = regexp(object.Label,'\d','match');
styleNum = str2num(number{1});
end
function file_menu_callback(object,data,handles)
switch(object.Label)
case '退出(&q)'
close(gcf);
end
end
function picture_menu_callback(object,data,handles)
global ah;
number = regexp(object.Label,'\d','match');
imageName = strcat(strcat('back',number),'.jpg');
axes('DrawMode','fast','position',[0,0,0.8,1]);
II=imread(imageName{1});
image(II);
ah = axes('DrawMode','fast','Color','none','position',[0,0,0.8,1]);
axis([-200 420 1 410])
grid off
hold on;
end
function color_menu_callback(object,data,handles)
global color multiColor;
disp(object.Label);
multiColor = 0;
switch(object.Label)
case '黄色(&1)'
color = [1 1 0];
case '紫红色(&2)'
color = [1 0 1];
case '青色(&3)'
color = [0 1 1];
case '红色(&4)'
color = [1 0 0];
case '绿色(&5)'
color = [0 0 1];
case '蓝色(&6)'
color = [0 1 0];
case '白色(&7)'
color = [1 1 1];
case '五彩变换(&8)'
color = [0 0 0];
multiColor = 1;
end
parseColor(color);
end
function slider_callback(obj,event)
global color ;
red_slider = findobj(gcf,'String','red');
blue_slider = findobj(gcf,'String','blue');
green_slider = findobj(gcf,'String','green');
color = [red_slider.Value,blue_slider.Value,green_slider.Value];
parseColor(color);
end
function[rbgStr] = parseColor(color)
color_text = findobj(gcf,'Tag','color_text');
rbgStr = char(strcat('red=',num2str(color(1))),...
strcat('blue=',num2str(color(2))),...
strcat('green=',num2str(color(3))));
color_text.String = rbgStr;
color_panel = findobj(gcf,'Title','颜色演示面板');
color_panel.BackgroundColor = color;
end
可能大家还是不太明白,为什么有了定时器,即时没有多线程也能实现多支烟花同时爆炸呢?我们可以这样想,matlab永远只有一条主线程在跑,现在有3个烟花A,B,C要爆炸,只能让主线程在for循环中不断改变A的位移来控制A运动爆炸,然后再到B,但如果用定时器Ta,Tb来控制,我们可以让主线程控制Ta让烟花A移动一下,然后Ta睡眠0.0.5s,主线程控制Tb让烟花B移动,然后Tb睡眠,Ta调用…Ta,Tb调用间隔极短(短到人眼无法识别),看起来就是Ta,Tb一起让烟花A,B运动爆炸了。
|
请发表评论