【问题标题】:How to check for a sequence of keystrokes如何检查击键序列
【发布时间】:2011-11-05 05:34:57
【问题描述】:

我正在制作一款游戏,并希望实现像 Konami 代码这样的作弊代码。

但是如何检查击键顺序呢?

我希望它能够正常工作,以便玩家只要输入代码就会触发。

提前致谢!

【问题讨论】:

    标签: java keyboard io


    【解决方案1】:

    下面是一个检查Konami代码的类,包括“UP, UP, UP, DOWN等”的情况

    这应该适用于任何给定的序列。

    import java.util.Map;
    import java.util.TreeMap;
    
    public class Konami {
    
        static private int[] code = 
            {UP, UP, DOWN, DOWN, LEFT, RIGHT, LEFT, RIGHT, B};
        static private Map<Integer, Integer>[] graph;
        static private int currentNode = 0;
    
        public static void main(String args[]) {
            //Create graph
            graph = generateSequenceMap(code);
    
            //Call checkKonami(key) whenever a key is pressed
        }
    
    
        static public boolean checkKonami(int keyPressed) {
            Integer nextNode = graph[currentNode].get(keyPressed);
    
            //Set currentNode to nextNode or to 0 if no matching sub-sequence exists
            currentNode = (nextNode==null ? 0 : nextNode);
    
            return currentNode == code.length-1;
        }
    
    
        static private Map<Integer, Integer>[] generateSequenceMap(int[] sequence) {
    
            //Create map
            Map<Integer, Integer>[] graph = new Map[sequence.length];
            for(int i=0 ; i<sequence.length ; i++) {
                graph[i] = new TreeMap<Integer,Integer>();
            }
    
            //i is delta
            for(int i=0 ; i<sequence.length ; i++) {
                loop: for(int j=i ; j<sequence.length-1 ; j++) {
                if(sequence[j-i] == sequence[j]) {
                    System.out.println("If at Node "+j+" you give me seq["+(j-i+1) 
                            + "] OR " + (sequence[j-i+1]) + " , goto Node " + (j-i+1));
    
                    //Ensure that the longest possible sub-sequence is recognized
                    Integer value = graph[j].get(sequence[j-i+1]);
                    if(value == null || value < j-i+1)
                        graph[j].put(sequence[j-i+1], j-i+1);
                }
                else
                    break loop;
                }
            }
            return graph;
        }
    }
    

    【讨论】:

    • 也是一个非常棒的解决方案,并且稍作修改,现在允许我检查作弊的保留范围。 :) 另一个谢谢你送你的方式。
    【解决方案2】:

    编辑: 有关始终有效的代码,请参阅我的另一篇文章。如果代码与自身重叠,则以下代码不会检测到(例如:“UP, UP, UP, DOWN, DOWN, LEFT, RIGHT, LEFT, RIGHT, B”不起作用)

    感谢 Gevorg 指出这一点。


    如果它只是如何识别您关心的序列(我假设您知道如何从键盘获取输入),那么您可以使用以下内容。

    int[] sequence = {UP, UP, DOWN, DOWN, LEFT, RIGHT, LEFT, RIGHT, B};
    int currentButton = 0;
    
    boolean checkKonami(int keyPressed) {
        //Key sequence pressed is correct thus far
        if(keyPressed == sequence[currentButton]) {
            currentButton++;
    
            //return true when last button is pressed
            if(currentButton == sequence.length) {
    
                //Important! Next call to checkKonami()
                //would result in ArrayIndexOutOfBoundsException otherwise
                currentButton = 0;
    
                return true;
            }
        }
        else {
            //Reset currentButton
            currentButton = 0;
        }
    
        return false;
    }
    

    每当注册按键时调用此函数,并传递已按下的键。当然在适当的地方修改类型。

    【讨论】:

    • UP,UP,UP,DOWN,DOWN,LEFT,RIGHT,LEFT,RIGHT,B 破坏了你的算法,不是吗?第三个 U 将 currentButton 带回 0,并且无法识别模式。注意,如果你丢弃第一个UP,则顺序有效!
    • 好发现!后一部分应该在 else 块中。
    • 已修复。 刘海撞墙
    • 是的,'else' 有帮助!但是对于我上面给定的序列,该算法仍然中断。到第三次击键时,您的算法已经接受了前两个(UP,UP),它期待“DOWN”,但在收到另一个“UP”后,currentButton 回到 0。不过,我的击键序列是有效的,应该被接受。
    • 必须在第一个 if 中添加一个 = more,并将 -1 删除到 sequence.length。但现在工作正常,非常感谢
    【解决方案3】:

    我确定您现在已经完成了这个项目,但我刚刚将它应用到我的一项任务中,并希望将它留给其他人查找。此解决方案将最后 n 次击键(此处定义为 10)记录到一个循环数组中,并在它们与我们的代码匹配时返回 true。作为方法的一部分,您可以轻松地传递不同的长度和代码(但此实现不需要它)。我用过 ^ ^ v v b a.

    public class Code {
    private static int[] history = new int[10];
    private static int front = 0;
    private static int size = 0;
    
    // Here is the Code they must enter (ascii vals for konami).
    private static int[] code = {38, 38, 40, 40, 37, 39, 37, 39, 66, 65};
    
    // Static class. No constructor.
    private Code(){}
    
    // Adds key-press into history buffer. If code is matched, return true.
    public static boolean add(int e){
    
        // Write the value into our key history.
        history[(front + size) % 10] = e;
    
        // Stop growing at length 10 and overwrite the oldest value instead.
        if (size < 10){
            size++;
        } else {
            front = front + 1 % 10;
        }
    
        // Compare our history (from the current front) to the code (from 0)
        for(int i = front; i < front + size; i++){
            if (history[i % 10] != code[i-front]){
                // Any 1 mismatch will abort
                return false;
            }
        }
        // Make sure we've logged enough keystrokes so it doesn't fire off
        // if your first key press matches the code.
        if (size < 10){
            return false;
        }
        return true;
    }
    

    享受吧! :D

    【讨论】:

      【解决方案4】:

      我不知道你的需求是什么。您是在尝试使用 System.inSystem.out 创建游戏,还是尝试制作完整的可视化 GUI?

      同时,请参阅接口java.awt.event.KeyListener。 (Oracle Documentation) 另见Oracle's Tutorial

      根据个人经验,下面的代码与您需要的差不多。

      import java.awt.event.*; //Specifically KeyListener and KeyEvent
      import java.util.ArrayList;
      
      public class Test implements KeyListener {
      
          private final int[] cheatCode = {38, 38, 40, 40, 37, 39, 37, 39, 66, 65, 83, 84, 65, 82, 84} //assuming user types out "start"
          private final ArrayList<Integer> KONAMI_CODE = createCheatCode(cheatCode);
          private ArrayList<Integer> typedKeys = new ArrayList<Integer>();
          public Test() {
              //constructor goes here, if necessary
          }
      
          public /*static*/ ArrayList<Integer> createCheatCode(int[] code) { //uses Key Codes
              ArrayList<Integer> temp = new ArrayList<Integer>();
              for (int i = 0; i < code.length; i++)
                  temp.add(new Integer(code[i]));
              return temp;
          }
      
      // Warning: MUST implement ALL KeyListener methods, or compiler will complain
      
          public /*static*/ void keyPressed(KeyEvent e) {}
      
          public /*static*/ void keyReleased(KeyEvent e) {
              typedKeys.add(new Integer(e.getKeyCode()));
          }
      
          public /*static*/ void keyTyped(KeyEvent e) {}
      
          public /*static*/ boolean cheatEntered() {
              int cheatLen = KONAMI_CODE.size(); // or length, depending on what you use
              int index = typedKeys.size() - cheatLen;
              if (index < 0)
                  return false;
              return typedKeys.get(index, typedKeys.size()).equals(KONAMI_CODE);
          }
      }
      

      当使用 runner 方法时,只需指定

      if (test.cheatEntered()) {
          // do something
      }
      

      如果你想要面向对象的编程,你可以删除/*static*/;否则,如果您想使用静态方法运行它,请去掉 /**/ 对。

      【讨论】:

      • 您所说的“要遵循的实施”是什么意思?
      • @BalusC 我的意思是我会充实我的答案。只是需要一些时间。
      • 这将如何工作?线程/助手会在后台运行吗?只是好奇。
      • @James 过去,我只是通过将侦听器附加到对象(例如,Pong 中的桨或 GUI 中的按钮)来做到这一点。我的猜测是它是一个线程,但我对 shell 脚本的了解还不够,无法 100% 确定。
      • 嗯,好吧,所以我假设这是在 Swing 中。如果出现错误,应该有一些方法可以累积输入的键并重置。这可以在keyTyped 中完成。 typedKeys.add(keyChar) 之类的东西,对 KONAMI_CODE 常量进行索引比较检查。如果其中一个字符不对应,则typedKeys.clear() 否则会触发作弊或设置标志。知道是否可以一次输入多个密钥会很有趣,在这种情况下应该添加一些额外的检查。
      【解决方案5】:

      查看状态模式可能会很有趣,但您可以尝试以下技巧,因为这是一个简单的案例:

      1. 将要识别的序列放入String secretCode
      2. 创建一个StringBuilder userInput 来保存用户按下的键
      3. 每次用户按下一个键时,将其附加到userInput
      4. userInput 中附加的每个键之后,检查更新后的userInput 是否包含secretCode 以及以下内容:userInput.indexOf(secretCode)&gt;-1

      您可能希望从现在开始清空userInput,然后根据时间或在识别出序列之后。

      【讨论】:

        猜你喜欢
        • 2011-08-12
        • 2018-11-10
        • 1970-01-01
        • 2011-07-09
        • 1970-01-01
        • 2014-01-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多