在没有控制台输出或使用新功能的情况下使用MATLAB'KeyPressFcn'

时间:2014-12-29 08:32:18

标签: matlab character keypress

我在Matlab(一种太空入侵者射手)制作游戏 图形都在一个图上,只是用多个图生成,我知道matlab并不适合这样的事情,这使得向同学展示它特别有趣。 对于我目前使用的关键输入:

f = figure(1)
set(f, 'KeyPressFcn', @(x,y)disp(get(f,'CurrentCharacter')));
a = get(f,'CurrentCharacter');
if a=='w' %move up..
if a=='d' %move right..
if a=='2' %switch to weapon 2..

这很有效,但每按一次键,它就会显示在控制台上,造成不必要的延迟。

我试过了:

f = figure(1)
set(f, 'KeyPressFcn');
a = get(f,'CurrentCharacter');

但每次按下一个键时都会显示“string -or- function handle -or- cell array”。

如何识别按键并将其存储到变量中而控制台上没有弹出任何内容?我也不想做第二个功能,因为目前所有内容都保存在一个.m文件中。

谢谢!

3 个答案:

答案 0 :(得分:4)

作为MATLAB Central上一些热门游戏的作者,我可以让你了解如何在MATLAB中正确地做到这一点。我不能保证我的方式不是最优的,但这是我多年来思考这个问题后想出的最好的解决方案。首先,我在编写游戏时通常遵循一些原则:

  1. 使用' CurrentKey'赞成' CurrentCharacter' ,因为前者识别更多未被归类为'字符的键。

  2. 你也可能需要一个' KeyReleaseFcn' ,因为这是一个拍摄对象。通常你希望飞机在按住钥匙时继续移动,并在你松开钥匙时停下来;你不想反复按下和释放钥匙,以便飞机继续前进。它是如何工作的:当玩家按下“w”时,我们调用KeyPressedFcn一次,我们在其中设置一个标志变量' w_status'为真;当玩家发布' w'时,调用KeyReleasedFcn一次,并将标志设置为flase。在游戏的主循环中,反复检查是否' w_status'是真的。如果是这样,那么将飞机向上移动一步,否则不要更新位置。

  3. 如果要将所有内容保存在一个文件中,请尝试将KeyPressFcn和KeyReleaseFcn实现为嵌套函数。这比将所有代码压缩到单行内容更好。

  4. 在if-else子句中避免使用硬编码的密钥名称。您以后可能希望允许用户重新分配密钥,因此最好将密钥名称保留在数组中,您可以修改它。

  5. 因此整个游戏将如下所示:

        function MainGame()
    
        KeyStatus = false(1,6);    % Suppose you are using 6 keys in the game
        KeyNames = {'w', 'a','s', 'd', 'j', 'k'};
        KEY.UP = 1;
        KEY.DOWN = 2;
        KEY.LEFT = 3;
        KEY.RIGHT = 4;
        KEY.BULLET = 5;
        KEY.BOMB = 6;
        ...
            gameWin = figure(..., 'KeyPressFcn', @MyKeyDown, 'KeyReleaseFcn', @MyKeyUp)
            ...
        % Main game loop
        while GameNotOver
            if KeyStatus(KEY.UP)  % If left key is pressed
                player.y = player.y - ystep;
            end
            if KeyStatus(KEY.LEFT)  % If left key is pressed
                player.x = player.x - xstep;
            end
            if KeyStatus(KEY.RIGHT)  % If left key is pressed
                %..
            end
            %...
        end
    
        % Nested callbacks...
            function MyKeyDown(hObject, event, handles)
                key = get(hObject,'CurrentKey');
                % e.g., If 'd' and 'j' are already held down, and key == 's'is
                % pressed now
                % then KeyStatus == [0, 0, 0, 1, 1, 0] initially
                % strcmp(key, KeyNames) -> [0, 0, 1, 0, 0, 0, 0]
                % strcmp(key, KeyNames) | KeyStatus -> [0, 0, 1, 1, 1, 0]
                KeyStatus = (strcmp(key, KeyNames) | KeyStatus);
            end
            function MyKeyUp(hObject, event, handles)
                key = get(hObject,'CurrentKey');
                % e.g., If 'd', 'j' and 's' are already held down, and key == 's'is
                % released now
                % then KeyStatus == [0, 0, 1, 1, 1, 0] initially
                % strcmp(key, KeyNames) -> [0, 0, 1, 0, 0, 0]
                % ~strcmp(key, KeyNames) -> [1, 1, 0, 1, 1, 1]
                % ~strcmp(key, KeyNames) & KeyStatus -> [0, 0, 0, 1, 1, 0]
                KeyStatus = (~strcmp(key, KeyNames) & KeyStatus);
            end
    
        end
    

    请注意,回调会使用'字典' KeyNames消除了任何if-else子句的需要。这样,无论使用的密钥数量和实际使用的密钥数量,这两个功能都可以插入任何游戏而无需任何修改。

    要了解这个想法在现实生活中是如何运作的,您可以在MATLAB Central上查看我的游戏:

    http://www.mathworks.com/matlabcentral/fileexchange/authors/111235

    有一个太空射击游戏&Stellaria'那里。但是,它是在我不知道嵌套函数的那天写的,因此代码被拆分成许多小函数文件,可能很难阅读。请谨慎使用。

答案 1 :(得分:1)

您在没有第三个参数的情况下调用set。由于您没有使用KeyPressFcn回调,因此无需进行任何设置。试试吧

f = figure(1);
a = get(f,'CurrentCharacter');

这很可能会给你a = ''作为输出。这是因为你还没有按任何按钮。如果单击该图,请按任意按钮并调用

b = get(f,'CurrentCharacter');

您将获得按下的按钮。因此,您可以在循环中调用它并执行相应的操作。请注意,在按下新按钮之前,您将获得相同的按钮。

为了防止每次按下按钮时MATLAB跳转到控制台,您可以将回调设置为NOP function

处理此问题的最佳方法当然是使用KeyPressFcn来控制您的软件而不是覆盖/忽略它。

答案 2 :(得分:0)

为此目的,编写一个处理击键的完整函数并将该函数的句柄分配给图形回调会更容易。

以下最小示例对图形处于活动状态时按下的任何键作出反应,并且只是为了示例的目的,显示在gui 中检测到的字符,但没有控制台输出。

function h = guigame
   h.fig = figure(1) ;
   h.txtDebug = uicontrol('Style','text','String','init') ;
   set(h.fig, 'KeyPressFcn', @processinput);
   guidata(h.fig,h)

function processinput(hobj,evt)
   h = guidata(hobj) ;
   a = get(h.fig,'CurrentCharacter');

   %// just to show that you captured the right key
   %// note the absence of console output
   set(h.txtDebug,'String',a)

   %// now do what you want with your captured key
   %// if a=='w' %move up..
   %// if a=='d' %move right..
   %// if a=='2' %switch to weapon 2..

请注意,轮询图CurrentCharacter是最接近初始代码的方式,但如果用户开始使用特殊键(ctrl / alt,则不是万无一失的/ shift等等。)

如果您希望能够处理这些情况,请在processinput函数中使用evt数据。如果我在开头设置调试点并查看evt我得到:

K>> evt
evt = 
    Character: 'f'
     Modifier: {1x0 cell}
          Key: 'f'

所以你的函数已经拥有它需要知道的一切而不需要调用a = get(f,'CurrentCharacter');,你也可以处理特殊情况。

虽然对于小型游戏,您可能不需要处理每个单独的组合键,但最好知道如何操作;)