I made a couple of changes to you function and tested.
- "Output argument "rueda1" (and maybe others) not assigned during call"   the reason is that the function executes to the end before you hit any key. One way to avoid that error is to add [rueda1,rueda2,rueda3] = deal( nan ); % <<<<<<<<< edit
- prinfig is a nested function. It shares the variables rueda1,rueda2,rueda3 with its parent function. Thus function printfig(~,event) % <<<<<<<<< edit without output arguments
Test:
>> [rueda1,rueda2,rueda3]=testkeyboard;
w
rueda1= 1 9
rueda2= 0 9
rueda3= 0 9
k
rueda1= 0 9
rueda2= 0 9
rueda3= -1 10
>> [rueda1,rueda2,rueda3]
ans =
NaN NaN NaN
>>
where
function [rueda1,rueda2,rueda3]=testkeyboard
figure('KeyPressFcn',@printfig);
[rueda1,rueda2,rueda3] = deal( nan );
function printfig(~,event)
if event.Character == 'w'
rueda2=0;
rueda1=1;
rueda3=0;
elseif event.Character == 's'
...
end
disp( event.Character )
disp(fprintf('rueda1= %d', rueda1));
 
Regarding comment 1
"but they are always in NaN"   Yes that is the expected behavior. The function, testkeyboard, is run once and returns [nan,nan,nan] to the base (/caller) workspace. In response to pressing a key the nested function printfig is executed. It updates the values in the workspace of testkeyboard, but that's all. Adding the lines
assignin( 'caller', 'rueda1', rueda1 )
assignin( 'caller', 'rueda2', rueda2 )
assignin( 'caller', 'rueda3', rueda3 )
at the end of printfig will update the variable values in the base workspace. However, I don't think that helps.
"create a figure of a "robot", and move it with testkeyboard"
IMO: You need to step back and do some basic design. There is hardly a way to achieve the goal by small changes to your code.
Model-View-Controller is an ambitious approach. (There exists variants and different meaning of the term.) See
A nested-function-approach
function my_robot
rueda = nan(3,1);
create_view
function create_view
figure('KeyPressFcn',@catch_keystroke);
end
function update_view
end
function catch_keystroke
update_view
end
end