ef230-2012-08/modules/misc/bounce.m
%|Bouncing ball game
%|A good example of animation and real-time user interaction
% University of Tennessee : EF 230 Fall, 2009 : Will Schleter
function main
clear all; close all; clc;
format compact;
% use a global structure to store all values that need to be
% used in several different functions
global g;
get_settings();
init_figure();
create_ball();
tic; % start timer
while g.playing
if g.moving==0, tic; drawnow; continue; end
if g.moving==1, dt=toc; % get time since last bounce
else, dt=.1; g.moving=0;
end
if dt<.05, continue; end % prevents updates of less than 1/20 of a second
tic; % reset timer
% new position and velocity
g.cx=g.cx+g.vx*dt+.5*g.ax*dt*dt;
g.cy=g.cy+g.vy*dt+.5*g.ay*dt*dt;
g.vx=round(g.vx+g.ax*dt);
g.vy=round(g.vy+g.ay*dt);
% check for wall hits
if (g.cx>g.xmax && g.vx>0) || (g.cx<0 && g.vx<0), g.vx=-g.cor*g.vx; beep; end
if (g.cy>g.ymax && g.vy>0) || (g.cy<0 && g.vy<0), g.vy=-g.cor*g.vy; beep; end
update_graphics
end
close(1)
% msgbox('Thanks for playing')
return
function buttonpush(src,ed)
global g
dv=10;
if length(ed.Modifier)>0, dv=1; end % shift, ctrl, alt all set dv=1
switch ed.Key
case 'q'
g.playing=0;
case 'space'
if g.moving, g.moving=0;
else, g.moving=1;
end
case 'r'
if length(ed.Modifier)>0
g.cor=max(.1,g.cor-.01);
else
g.cor=min(1,g.cor+.01);
end
case 'y'
if length(ed.Modifier)>0
g.ay=g.ay-10;
else
g.ay=g.ay+10;
end
case 'x'
if length(ed.Modifier)>0
g.ax=g.ax-10;
else
g.ax=g.ax+10;
end
case 'tab'
g.moving=2;
case 'leftarrow'
g.vx=g.vx-dv;
case 'rightarrow'
g.vx=g.vx+dv;
case 'uparrow'
g.vy=g.vy+dv;
case 'downarrow'
g.vy=g.vy-dv;
otherwise
ed % show key info
end
return
function get_settings
global g;
p={'X acceleration','Y acceleration','Coef of restitution'};
t='Bouncing Ball Game';
d={'0','-100','1'};
a=inputdlg(p,t,1,d);
if length(a)==0, error('Cancelled'); end % user pushed close or cancel
g.ax=str2double(a{1});
g.ay=str2double(a{2});
g.cor=str2double(a{3});
% hardcoded values
g.playing=1;
g.moving=1;
g.xmax=100;
g.ymax=100;
g.cx=g.xmax*rand(1); % starting position
g.cy=g.ymax*rand(1);
g.vx=0; % starting velocity
g.vy=0;
return
function create_ball
global g;
% create a 'ball'
t = linspace(0,2*pi,20);
r=2;
g.bx=r*cos(t);
g.by=r*sin(t);
g.ball=patch(g.bx,g.by,'r');
% trace (tail)
tlen=50;
g.trace.x=ones(1,tlen)*g.cx;
g.trace.y=ones(1,tlen)*g.cy;
g.trace.h=plot(g.trace.x,g.trace.y,'b:');
% velocity vector
x=[g.cx g.cx];
y=[g.cy g.cy];
g.velvec.h=plot(x,y,'r-');
return
function init_figure
% set up figure window
global g;
figure(1);
hold on;
axis equal;
title('Bouncy Ball');
axis([0 g.xmax 0 g.ymax]);
axis off;
rectangle('Position',[0,0,g.xmax,g.ymax])
% set the function that gets called when a key is pressed
set(gcf,'WindowKeyPressFcn',@buttonpush);
% instructions
text(102,90,{'Q: quit game',
'space: pause/resume',
'Tab: single step',
'R: COR +.01',
'sh-R: COR -.01',
'X: Ax+10',
'sh-X: Ax-10',
'Y: Ay+10',
'sh-Y: Ay-10',
'Arrow keys:',
' "push" the ball'}, 'verticalalign','top' );
% status text
g.stattext_h=text(-30,90,'','verticalalign','top');
return
function update_graphics
global g;
% move ball to new position
set(g.ball,'XData',g.bx+g.cx);
set(g.ball,'YData',g.by+g.cy);
% update status text
set(g.stattext_h,'String',...
sprintf('Vx=%.0f\nAx=%.0f\nVy=%.0f\nAy=%.0f\nCOR=%.2f',...
g.vx,g.ax,g.vy,g.ay,g.cor));
% update trace
g.trace.x=[g.cx g.trace.x(1:end-1)];
g.trace.y=[g.cy g.trace.y(1:end-1)];
set(g.trace.h,'XData',g.trace.x,'YData',g.trace.y);
% update velocity vector
x=[g.cx g.cx+g.vx/5];
y=[g.cy g.cy+g.vy/5];
set(g.velvec.h,'XData',x,'YData',y);
% update display
drawnow
return