Creating a moving line to depict progress

17 views (last 30 days)
I want to create a line at the top of a figure that will move like a progress bar that completely fills up. My idea is to make it so that it's a function of time. I have a code for a Whack-A-Mole game and I have it set up so that instead of having a limited amount of time to "Whack" the moles, each whack gets progressively faster. I want some sort of a progress bar that crosses the screen at the same rate (indicating that when the bar has crossed the screen there is no more time left to whack the mole). I haven't seen anything that does this. Would appreciate any help.

Accepted Answer

Tommy
Tommy on 19 Apr 2020
Simple and crude but possibly relevant to what you are hoping for?
The actual progress bar is a set of axes. A patch shows how 'filled' it is. Here, it fills at a speed of 1 bar per 15 seconds. Make it faster by decreasing secAllowed.
playGame = true;
f = figure;
a = axes(f,...
'Position', [0.1 0.95 0.8 0.05],...
'YColor', 'none',...
'XColor', 'none',...
'NextPlot', 'add',...
'XLim', [0 1],...
'XLimMode', 'manual');
t = a.XLim(1);
p = patch([0 t t 0], repelem(a.YLim,1,2), 'r',...
'EdgeColor', 'none');
secAllowed = 15;
t0 = tic;
while playGame
tn = toc(t0);
p.XData = [0 tn/secAllowed tn/secAllowed 0];
drawnow;
if tn>=secAllowed
playGame=false;
end
end
  2 Comments
Luke Fox
Luke Fox on 19 Apr 2020
So thank you because this was exactly what I was looking for, but is there a way to run this and a waitforbuttonpress at the same time. I tried integrating it into my code and the way that it overlays is exactly how I want it, but it waits for the bar to completely fill before I can press. If there's no clear or easy fix don't worry about it.
clear all
CorrectX = 0;
CorrectY = 0;
DistanceToCenter = 0;
t = linspace(0,2*pi);
a = 20;
b = a;
x = a*cos(t) + a;
y = b*sin(t) + b;
xpositions = [a*cos(t) + 300;a*cos(t) + 600;a*cos(t) + 900;a*cos(t) + 1200];
ypositions = [b*sin(t) + 250;b*sin(t) + 550];
Time1 = 0;
T1 = 10;
WhackAMoleRound = 1;
while Time1 < T1 && DistanceToCenter <= a
T1 = T1*(9/10);
C1 = 0;
fig1 = figure('Visible','on','Units','normalized','Position',[0 0.047 1 .863],'pointer','crosshair');
axes('Units', 'normalized', 'Position', [0 0 1 1]);
set(gca,'Color','k');
hold on
xlim([1 1536]);
ylim([42 786]);
Hole1 = plot(xpositions(1,1:100),ypositions(1,1:100),'w','Visible','off');
Hole2 = plot(xpositions(1,1:100),ypositions(2,1:100),'w','Visible','off');
Hole3 = plot(xpositions(2,1:100),ypositions(1,1:100),'w','Visible','off');
Hole4 = plot(xpositions(2,1:100),ypositions(2,1:100),'w','Visible','off');
Hole5 = plot(xpositions(3,1:100),ypositions(1,1:100),'w','Visible','off');
Hole6 = plot(xpositions(3,1:100),ypositions(2,1:100),'w','Visible','off');
Hole7 = plot(xpositions(4,1:100),ypositions(1,1:100),'w','Visible','off');
Hole8 = plot(xpositions(4,1:100),ypositions(2,1:100),'w','Visible','off');
HoleVector = [Hole1,Hole2,Hole3,Hole4,Hole5,Hole6,Hole7,Hole8];
CPX1 = [300];
CPX2 = [300];
CPX3 = [600];
CPX4 = [600];
CPX5 = [900];
CPX6 = [900];
CPX7 = [1200];
CPX8 = [1200];
CPXVector = [CPX1,CPX2,CPX3,CPX4,CPX5,CPX6,CPX7,CPX8];
CPY1 = [250];
CPY2 = [550];
CPY3 = [250];
CPY4 = [550];
CPY5 = [250];
CPY6 = [550];
CPY7 = [250];
CPY8 = [550];
CPYVector = [CPY1,CPY2,CPY3,CPY4,CPY5,CPY6,CPY7,CPY8];
if WhackAMoleRound == 1
pause(5)
end
t1 = tic;
Mole = randi(8);
set(HoleVector(Mole),'Visible','on');
playGame = true; %%%%%%%%%%%%%%%%%%%%% Input your code starting here
a = axes(fig1,...
'Position', [0.1 0.95 0.8 0.05],...
'YColor', 'none',...
'XColor', 'none',...
'NextPlot', 'add',...
'XLim', [0 1],...
'XLimMode', 'manual');
t = a.XLim(1);
p = patch([0 t t 0], repelem(a.YLim,1,2), 'r',...
'EdgeColor', 'none');
t0 = tic;
while playGame
tn = toc(t0);
p.XData = [0 tn/T1 tn/T1 0];
drawnow;
if tn>=T1
playGame=false;
end
end
waitforbuttonpress
Press = get(0,'PointerLocation');
DistanceToCenter = sqrt((Press(1) - CPXVector(Mole))^2 + (Press(2) - CPYVector(Mole))^2);
Time1 = toc(t1);
if Time1 <= 10 && DistanceToCenter <= a
WhackAMoleRound = WhackAMoleRound + 1;
else
close all
end
reset(HoleVector(Mole));
close all
end
disp('Rounds Total')
disp(WhackAMoleRound)
Tommy
Tommy on 19 Apr 2020
The call to waitforbuttonpress won't happen until after my while loop exits. Detection of the mouse click needs to happen during the loop. One option is to use the figure's button down callback (i.e. mouse click callback) rather than waitforbuttonpress. I would also suggest moving a lot of your code outside of your outer loop. Draw the figure and each hole once at the beginning, and then just switch holes' visibilities on and off as needed. Don't close all on every iteration and then redraw everything.
clear
CorrectX = 0;
CorrectY = 0;
DistanceToCenter = 0;
t = linspace(0,2*pi);
a = 20;
b = a;
x = a*cos(t) + a;
y = b*sin(t) + b;
xpositions = [a*cos(t) + 300;a*cos(t) + 600;a*cos(t) + 900;a*cos(t) + 1200];
ypositions = [b*sin(t) + 250;b*sin(t) + 550];
T1 = 10;
WhackAMoleRound = 1;
fig1 = figure('Visible', 'on',...
'Units', 'normalized',...
'Position', [0 0.047 1 .863],...
'pointer', 'crosshair',...
'UserData', false);
ax = axes('Units', 'normalized',...
'Position', [0 0 1 1],...
'Color', 'k',...
'XLim', [1 1536],...
'YLim', [42 786]);
hold(ax, 'on')
Hole1 = plot(ax, xpositions(1,1:100),ypositions(1,1:100),'w','Visible','off');
Hole2 = plot(ax, xpositions(1,1:100),ypositions(2,1:100),'w','Visible','off');
Hole3 = plot(ax, xpositions(2,1:100),ypositions(1,1:100),'w','Visible','off');
Hole4 = plot(ax, xpositions(2,1:100),ypositions(2,1:100),'w','Visible','off');
Hole5 = plot(ax, xpositions(3,1:100),ypositions(1,1:100),'w','Visible','off');
Hole6 = plot(ax, xpositions(3,1:100),ypositions(2,1:100),'w','Visible','off');
Hole7 = plot(ax, xpositions(4,1:100),ypositions(1,1:100),'w','Visible','off');
Hole8 = plot(ax, xpositions(4,1:100),ypositions(2,1:100),'w','Visible','off');
HoleVector = [Hole1,Hole2,Hole3,Hole4,Hole5,Hole6,Hole7,Hole8];
CPX1 = [300];
CPX2 = [300];
CPX3 = [600];
CPX4 = [600];
CPX5 = [900];
CPX6 = [900];
CPX7 = [1200];
CPX8 = [1200];
CPXVector = [CPX1,CPX2,CPX3,CPX4,CPX5,CPX6,CPX7,CPX8];
CPY1 = [250];
CPY2 = [550];
CPY3 = [250];
CPY4 = [550];
CPY5 = [250];
CPY6 = [550];
CPY7 = [250];
CPY8 = [550];
CPYVector = [CPY1,CPY2,CPY3,CPY4,CPY5,CPY6,CPY7,CPY8];
ax2 = axes(fig1,...
'Position', [0.1 0.95 0.8 0.05],...
'YColor', 'none',...
'XColor', 'none',...
'NextPlot', 'add',...
'XLim', [0 1],...
'XLimMode', 'manual');
t = ax2.XLim(1);
p = patch([0 t t 0], repelem(ax2.YLim,1,2), 'r',...
'EdgeColor', 'none');
while true % go until player loses, or replace with something like
% while turnNum < totalTurns
% if you want a limited number of turns
fig1.UserData = false;
T1 = T1*(9/10);
C1 = 0;
if WhackAMoleRound == 1
pause(5)
end
Mole = randi(8);
fig1.WindowButtonDownFcn = {@clickCallback CPXVector(Mole) CPYVector(Mole) a ax};
set(HoleVector(Mole),'Visible','on');
t0 = tic;
tn = toc(t0);
while tn<T1 && ~fig1.UserData
p.XData = [0 tn/T1 tn/T1 0];
drawnow;
tn = toc(t0);
end
if fig1.UserData
WhackAMoleRound = WhackAMoleRound + 1;
set(HoleVector(Mole),'Visible','off');
else
break
end
end
close all
disp('Rounds Total')
disp(WhackAMoleRound)
function clickCallback(src,~,x,y,a,ax)
Press = ax.CurrentPoint;
DistanceToCenter = sqrt((Press(1,1) - x)^2 + (Press(1,2) - y)^2);
if DistanceToCenter <= a
src.UserData = true;
end
end
This requires passing many parameters into clickCallback (x, y, a, ax) and somehow communicating the result back to the main while loop (here, by using the figure's UserData property). You may consider creating your own class to do this. Short example:
classdef whackamole < handle
properties
fig % figure
ax1 % background
ax2 % progress bar
p % patch
r = 20 % radius of holes
HoleVector % holes
CPXVector % x positions
CPYVector % y positions
Mole % current mole #
b % button to start
onToNextRound % if user clicked hole in time
end
methods
function obj = whackamole
obj.fig = figure('Visible', 'on',...
'Units', 'normalized',...
'Position', [0 0.047 1 .863],...
'pointer', 'crosshair',...
'UserData', false);
obj.ax1 = axes('Units', 'normalized',...
'Position', [0 0 1 1],...
'Color', 'k',...
'XLim', [1 1536],...
'YLim', [42 786],...
'NextPlot', 'add');
t = linspace(0,2*pi);
xpositions = [obj.r*cos(t) + 300;obj.r*cos(t) + 600;obj.r*cos(t) + 900;obj.r*cos(t) + 1200];
ypositions = [obj.r*sin(t) + 250;obj.r*sin(t) + 550];
Hole1 = plot(obj.ax1, xpositions(1,1:100),ypositions(1,1:100),'w','Visible','off');
Hole2 = plot(obj.ax1, xpositions(1,1:100),ypositions(2,1:100),'w','Visible','off');
Hole3 = plot(obj.ax1, xpositions(2,1:100),ypositions(1,1:100),'w','Visible','off');
Hole4 = plot(obj.ax1, xpositions(2,1:100),ypositions(2,1:100),'w','Visible','off');
Hole5 = plot(obj.ax1, xpositions(3,1:100),ypositions(1,1:100),'w','Visible','off');
Hole6 = plot(obj.ax1, xpositions(3,1:100),ypositions(2,1:100),'w','Visible','off');
Hole7 = plot(obj.ax1, xpositions(4,1:100),ypositions(1,1:100),'w','Visible','off');
Hole8 = plot(obj.ax1, xpositions(4,1:100),ypositions(2,1:100),'w','Visible','off');
obj.HoleVector = [Hole1,Hole2,Hole3,Hole4,Hole5,Hole6,Hole7,Hole8];
obj.CPXVector = [300,300,600,600,900,900,1200,1200];
obj.CPYVector = [250,550,250,550,250,550,250,550];
obj.ax2 = axes(obj.fig,...
'Position', [0.1 0.95 0.8 0.05],...
'YColor', 'none',...
'XColor', 'none',...
'NextPlot', 'add',...
'XLim', [0 1],...
'XLimMode', 'manual',...
'Visible', 'off');
obj.p = patch([0 0 0 0], repelem(obj.ax2.YLim,1,2), 'r',...
'EdgeColor', 'none');
obj.b = uicontrol(obj.fig,...
'Style', 'pushbutton',...
'String', 'Start Game',...
'Units', 'normalized',...
'Position', [0.45 0.45 0.1 0.1],...
'Callback', @obj.startGame);
if nargout==0
clear obj
end
end
function startGame(obj, ~, ~)
obj.ax2.Visible = 'on';
obj.b.Visible = 'off';
WhackAMoleRound = 1;
gameOver = false;
T1 = 10;
while ~gameOver
obj.onToNextRound = false;
obj.Mole = randi(8);
obj.fig.WindowButtonDownFcn = @obj.clickCallback;
set(obj.HoleVector(obj.Mole), 'Visible', 'on');
t0 = tic;
tn = toc(t0);
while tn<T1 && ~obj.onToNextRound
obj.p.XData = [0 tn/T1 tn/T1 0];
drawnow;
tn = toc(t0);
end
if ~obj.onToNextRound % time ran out
gameOver = true;
else % on to next round
WhackAMoleRound = WhackAMoleRound + 1;
set(obj.HoleVector(obj.Mole), 'Visible', 'off');
T1 = 0.9*T1;
end
end
close(obj.fig)
disp('Rounds Total')
disp(WhackAMoleRound)
end
function clickCallback(obj, ~, ~)
Press = obj.ax1.CurrentPoint;
DistanceToCenter = sqrt((Press(1,1) - obj.CPXVector(obj.Mole))^2 + (Press(1,2) - obj.CPYVector(obj.Mole))^2);
if DistanceToCenter <= obj.r
obj.onToNextRound = true;
end
end
end
end

Sign in to comment.

More Answers (0)

Categories

Find more on Graphics Object Properties in Help Center and File Exchange

Products

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!