function gametimerexample()
%% Purpose of this game is to avoid beeing shot by bullets. It is possible to
% set speed of the bullets and its amount. User position can se set using
% arrow keys.

%% Initial state of the game and allocation
gameSpeed = 50;
nBullets = 50;
userPos = [];
bulletsPos = [];
nHits = [];
XMax = 2;
YMax = 5;
stepSize = 0.1;

%% GUI building
hFig = uifigure('Name', 'Game');
hGridMain = uigridlayout(hFig, 'ColumnWidth', {150, '1x'}, ...
   'RowHeight', {'1x'});
hGridLeft = uigridlayout(hGridMain, 'ColumnWidth', {'1x'}, ...
   'RowHeight', {20, '1x', 20, '1x', 20, 20}, 'Padding', [0 0 0 0]);
uilabel(hGridLeft, 'Text', 'Speed:');
hKnobSpeed = uiknob(hGridLeft, 'Value', gameSpeed);
uilabel(hGridLeft, 'Text', 'N. Bullets:');
hKnobNBullets = uiknob(hGridLeft, 'Limits', [10, 110], 'Value', nBullets);
hButtStartStop = uibutton(hGridLeft, 'Text', 'Start');
hLabelNHits = uilabel(hGridLeft);
hAx = uiaxes(hGridMain, 'XLim', XMax*[-1, 1], 'YLim', [0, YMax], 'Color', 'k', ...
   'DataAspectRatio', [1 1 1], 'Interactions', []);
hAx.Toolbar.Visible = false;
hLineBullets = line(hAx, 'Color', 'w', 'Marker', '.', 'LineStyle', 'none', 'LineWidth', 3);
hLineUser = line(hAx, 'Color', 'w', 'Marker', 'o', 'MarkerSize', 15, ...
   'MarkerFaceColor', 'w');
hTimer = timer('ExecutionMode', 'fixedRate', 'Period', 1/10);
%% Callbacks
hButtStartStop.ButtonPushedFcn = @(a, b)startStopGame();
hKnobSpeed.ValueChangingFcn = @(a, event)updateSpeed(event);
hKnobNBullets.ValueChangingFcn = @(a, event)updateNBullets(event);
hFig.CloseRequestFcn = @(a, b)closeFigure();
hTimer.TimerFcn = @(a, b)updateShots();
%% Initialization
resetGame();

   function resetGame()
      userPos = [0, 0.5];
      redrawUser();
      bulletsPos = generateNewBulletPositions(nBullets);
      redrawBullets();
      nHits = 0;
      redrawNHits();
   end

   function redrawBullets()
      set(hLineBullets, 'XData', bulletsPos(:, 1), 'YData', bulletsPos(:, 2));
   end

   function updateShots()
      bulletsPos = bulletsPos + [0, -gameSpeed/1e3];
      checkGameState();
      redrawBullets();
   end

   function checkGameState()
      actualNOfBullets = size(bulletsPos, 1);
      if actualNOfBullets ~= nBullets
         if actualNOfBullets < nBullets
            nNewBullets = nBullets - actualNOfBullets;
            bulletsPos = [bulletsPos; generateNewBulletPositions(nNewBullets)];
         else
            bulletsPos = bulletsPos(1:nBullets, :);
         end
      end
      % check bullets outside playground
      logIndBulOutside = bulletsPos(:, 2) < 0;
      bulletsPos(logIndBulOutside, :) = generateNewBulletPositions(sum(logIndBulOutside));
      % check when a bullet hit user
      rangesBullUser = hypot(bulletsPos(:, 1) - userPos(1), bulletsPos(:, 2) - userPos(2));
      logIndHit = rangesBullUser < 0.15;
      thisNHits = sum(logIndHit);
      bulletsPos(logIndHit, :) = generateNewBulletPositions(thisNHits);
      % change color of user in case of hit
      if thisNHits
         nHits = nHits + thisNHits;
         redrawNHits();
         hLineUser.Color = 'r';
         hLineUser.MarkerFaceColor = 'r';
      else
         hLineUser.Color = 'w';
         hLineUser.MarkerFaceColor = 'w';
      end
   end

   function newPos = generateNewBulletPositions(nPoints)
      newPos = [rand(nPoints, 1)*XMax^2-XMax, YMax*rand(nPoints, 1)+YMax];
   end

   function updateSpeed(event)
      gameSpeed = event.Value;
   end

   function updateNBullets(event)
      nBullets = ceil(event.Value);
   end

   function keyListener(event)
      switch event.Key
         case 'leftarrow'
            userPos = userPos - [stepSize, 0];
         case 'rightarrow'
            userPos = userPos + [stepSize, 0];
         case 'uparrow'
            userPos = userPos + [0, stepSize];
         case 'downarrow'
            userPos = userPos - [0, stepSize];
      end
      redrawUser();
   end

   function redrawUser()
      set(hLineUser, 'XData', userPos(1), 'YData', userPos(2));
   end

   function redrawNHits()
      hLabelNHits.Text = sprintf('Number of shots: %d', nHits);
   end

   function startStopGame()
      if strcmp(hButtStartStop.Text, 'Start')
         resetGame();
         hFig.WindowKeyPressFcn = @(src, event)keyListener(event);
         hButtStartStop.Text = 'Stop';
         hTimer.start();
      elseif strcmp(hButtStartStop.Text, 'Stop')
         hTimer.stop();
         hFig.WindowKeyPressFcn = '';
         hButtStartStop.Text = 'Start';
      end
   end

   function closeFigure()
      hTimer.stop();
      hFig.delete();
   end
end