Clarius333
Clarius333

Reputation: 93

Why does my while-loop sometimes not respond to a keypress in Matlab?

I have created a game for a new study I will be running using Psychtoolbox (3.0.13, flavor beta) in Matlab R2018a, and the condition to exit my while-loop in a trial, which is a spacebar press, sometimes works immediately but sometimes needs several key presses in order to exit.

I have tried making my code more efficient, by removing any unnecessary code and putting in wait times before showing the stimuli in case this would solve the issue. I have a while loop that needs input from mouse clicks alongside a spacebar press to satisfy the exit conditions. The code does run in the end, but I just do not understand why sometimes a single spacebar press is enough, and other times I need to press it 3 times to exit. I have the same problem in another task where I also use mouse clicks and a spacebar press (and no sound, so I don't think the sound is causing it) as conditions to exit a while loop. I wonder if this has anything to do with the way I use the RECS structure, and if I could maybe use a more efficient way. It would be amazing if anyone has any idea as to how to improve this in the task.

% Start game: They play a total of 5 games. A game finishes when
% they have selected at least 1 card and pressed spacebar.

for Game = 1:5
% If ESC is pressed, quit game.
if OM.ESC == 0

% Draw wooden background
Screen('DrawTexture', OM.wid, OM.greywoodTexture);

% Show score and game counter
Screen('TextSize', OM.wid, OM.textSize);
DrawFormattedText(OM.wid, ['Score: ', num2str(OM.Score)], ...
    OM.origin(1)+750, OM.origin(2)-400);
DrawFormattedText(OM.wid, ['Game: ', num2str(Game)], ...
    OM.origin(1), OM.origin(2)-425);

% create sequence for where Randy wil be and randomise it
sequence = [1 0 0 0 0 0 0 0 0 0];
sequence = sequence(randperm(length(sequence)));

% Draw the cards upside down
for i = 1:length(RECS)
    Screen('DrawTexture', OM.wid, OM.cardsTexture, [], RECS(i).rectangles);
end

% Show the cards and get the time. If it's the first game, use 0.5
% seconds before showing to make sure the game is up to speed. (Thought
% this might help with speed later on, not sure if it does.)
if Game == 1
    WaitSecs(0.5);
end
timage = GetSecs;
Screen('Flip', OM.wid);

% set screenpress and loss to 0
Screenpress = 0;
loss = 0;

% wait for screenpress (space bar press)
while Screenpress == 0 && OM.ESC == 0 % checks for completion
    [keyIsDown, keyTime, keyCode] = KbCheck;
        if keyIsDown
            keyStroke = KbName(keyCode);
            % check if space was pressed and atleast one card was
            % selected
            if any(strcmpi(keyStroke,OM.screenKey)) && sum([RECS(:).pressed]) ~= 0
                Screenpress = 1;
                RT = round((keyTime - timage)*1000);
            elseif any(strcmpi(keyStroke,OM.exitKey))
                OM.ESC = 1;
                RT = 0;
                break;
            end
        end  

        % check if the mouse was pressed inside any of the rectangles
        % (cards)
        [mx,my,buttons] = GetMouse();  %waits for a key-press
        for i = 1:length(RECS)
            if IsInRect(mx, my, RECS(i).rectangles) == 1 && sum(buttons) > 0 && RECS(i).pressed == 0
                RECS(i).pressed = 1;
            elseif IsInRect(mx, my, RECS(i).rectangles) == 1 && sum(buttons) > 0 && RECS(i).pressed == 1
                % This is to enable de-selecting a card by double
                % clicking on it.
                RECS(i).pressed = 0;
            end
        end

        % Draw wooden background
        Screen('DrawTexture', OM.wid, OM.greywoodTexture);

        % Draw score counter
        Screen('TextSize', OM.wid, OM.textSize);
        DrawFormattedText(OM.wid, ['Score: ', num2str(OM.Score)], ...
        OM.origin(1)+750, OM.origin(2)-400);
        DrawFormattedText(OM.wid, ['Game: ', num2str(Game)], ...
        OM.origin(1), OM.origin(2)-425);

    % Draw cards with a frame around it if selected.
        for i = 1:length(RECS)
            Screen('DrawTexture', OM.wid, OM.cardsTexture, [], RECS(i).rectangles);
            if RECS(i).pressed == 1
                Screen('FrameRect', OM.wid, OM.selRecColour, RECS(i).rectangles, OM.selRecSize);
            end
        end

        % Show the selected card borders and delay against flicker
        WaitSecs(0.2); % need this delay to stop flicker
        Screen('Flip', OM.wid);

end % space was pressed, after the cards were selected. Now we need to flip the cards and show what they chose.

% if space was pressed,
% Draw wooden background
Screen('DrawTexture', OM.wid, OM.greywoodTexture);

% Draw score counter
Screen('TextSize', OM.wid, OM.textSize);
DrawFormattedText(OM.wid, ['Score: ', num2str(OM.Score)], ...
OM.origin(1)+750, OM.origin(2)-400);
DrawFormattedText(OM.wid, ['Game: ', num2str(Game)], ...
    OM.origin(1), OM.origin(2)-425);

% randomly select the 9 animals to show. Replace is set to false so
% each animal can only be shown once in one game. It needs to be 10,
% else the game won't run if they select 10 cards (just in case they
% do). 
animals = datasample(1:26, 10, 'Replace', false);

% Set card count to 0
card_count = 0;

% display animations
for i = 1:length(RECS)
    if RECS(i).pressed == 1
        % add to card_count if a card was selected
        card_count = card_count + 1;
        Screen('FrameRect', OM.wid, OM.selRecColour, RECS(i).rectangles, OM.selRecSize);

        if sequence(i) == 0 % normal animal
            t = animals(i);
            Screen('FillRect', OM.wid, OM.turnedCardColour, RECS(i).rectangles)
            Screen('DrawTexture', OM.wid, ANIMAL(t).AniTexture, [], RECS(i).animals);

        elseif sequence(i) == 1 % troll face
            Screen('FillRect', OM.wid, OM.redColour, RECS(i).rectangles)
            Screen('DrawTexture', OM.wid, OM.trollTexture, [], RECS(i).animals);
            % play giggle if Randy was selected
            PsychPortAudio('Start', OM.giggle, 1);

            loss = 1;
        end

    else
        % Show the cards upside down for the ones that weren't
        % selected.
        Screen('DrawTexture', OM.wid, OM.cardsTexture, [], RECS(i).rectangles);

    end
end


    Screen('Flip', OM.wid);
    WaitSecs(2);

    % Draw wooden background
    Screen('DrawTexture', OM.wid, OM.greywoodTexture);
    Screen('TextSize', OM.wid, OM.feedbackSize);

    % Show text after game round, and determine Score for game
    if loss == 1
        message = 'Oh no! You lost all your cards for this round...';
        DrawFormattedText(OM.wid, message, 'center', 'center', OM.textColor);
        Score = 0;

    elseif loss == 0
        message = sprintf('Well done! You''ve won %d cards this round!', card_count);
        DrawFormattedText(OM.wid, message, 'center', 'center', OM.textColor);
        Score = card_count;
    end

    % calculate the total score in the game
    OM.Score = OM.Score + Score ;

    % Draw score counter 
    Screen('TextSize', OM.wid, OM.textSize);
    DrawFormattedText(OM.wid, ['Score: ', num2str(OM.Score)], ...
    OM.origin(1)+750, OM.origin(2)-400);
    DrawFormattedText(OM.wid, ['Game: ', num2str(Game)], ...
        OM.origin(1), OM.origin(2)-425);

        Screen('Flip',OM.wid);
        WaitSecs(2); %change back to 1 second. % put to 2 seconds because is delay of showing text?

    %% Save the data file
    OM.dataFile = fopen(OM.dataFileName, 'a');
    fprintf(OM.dataFile, OM.formatString, OM.SJNB, OM.Test_session, OM.Age, OM.Date, Game, RT, card_count, loss, OM.Score);
    fclose(OM.dataFile);

    % Cleaning this.
    for i = 1:10
        RECS(i).pressed = 0;
        RECS(i).pressedCount = 0;
    end


end
end

Upvotes: 0

Views: 349

Answers (1)

Clarius333
Clarius333

Reputation: 93

Just wanted to post the answer below here, which was given to me by a commenter. The problem was actually just in the delay period of 200 ms I put in, which also meant that KbCheck did not always manage to register the spacebar press in time.. so it was a simple problem but this answer made me fix this annoyance in my task as well as in another one, so thank you! Comment with answer copied below:

"I don't know this Screen class, and it looks like this is all specific to Psychtoolbox, there is little native MATLAB that you use here, so I can't be of help. But a quick look in the docs reveals that KbCheck checks for the key to be down at that instant. So you need to have the key down when that bit of code is being executed. Since you have an 0.2 s wait in there too, it'll become challenging to press the key at the right time. Try to keep the key down until the program stops. I don't have suggestions for how to improve this using this toolbox, sorry. – Cris Luengo"

Upvotes: 1

Related Questions