时间延迟和JInput

好的,我不知道怎么说这个问题,但也许我的代码会说明问题:

public class ControllerTest { public static void main(String [] args) { GamePadController rockbandDrum = new GamePadController(); DrumMachine drum = new DrumMachine(); while(true) { try{ rockbandDrum.poll(); if(rockbandDrum.isButtonPressed(1)) //BLUE PAD HhiHat) { drum.playSound("hiHat.wav"); Thread.sleep(50); } if(rockbandDrum.isButtonPressed(2)) //GREEN PAD (Crash) { //Todo: Change to Crash drum.playSound("hiHat.wav"); Thread.sleep(50); } //Etc.... } } } public class DrumMachine { InputStream soundPlayer = null; AudioStream audio = null; static boolean running = true; public void playSound(String soundFile) { //Tak a sound file as a paramater and then //play that sound file try{ soundPlayer = new FileInputStream(soundFile); audio = new AudioStream(soundPlayer); } catch(FileNotFoundException e){ e.printStackTrace(); } catch(IOException e){ e.printStackTrace(); } AudioPlayer.player.start(audio); } //Etc... Methods for multiple audio clip playing } 

现在的问题是,如果我降低了延迟

 Thread.sleep(50) 

然后声音每秒播放多次,但如果我保持在这个级别或更高,我可能会错过正在播放的声音……

这是一个奇怪的问题,如果延迟太低,声音会循环。 但是如果它太高则会错过播放声音。 这只是我需要调整设置的问题,还是有其他方法来轮询控制器而没有循环声音?

编辑:如果我需要发布用于轮询控制器的代码,我会……

 import java.io.*; import net.java.games.input.*; import net.java.games.input.Component.POV; public class GamePadController { public static final int NUM_BUTTONS = 13; // public stick and hat compass positions public static final int NUM_COMPASS_DIRS = 9; public static final int NW = 0; public static final int NORTH = 1; public static final int NE = 2; public static final int WEST = 3; public static final int NONE = 4; // default value public static final int EAST = 5; public static final int SW = 6; public static final int SOUTH = 7; public static final int SE = 8; private Controller controller; private Component[] comps; // holds the components // comps[] indices for specific components private int xAxisIdx, yAxisIdx, zAxisIdx, rzAxisIdx; // indices for the analog sticks axes private int povIdx; // index for the POV hat private int buttonsIdx[]; // indices for the buttons private Rumbler[] rumblers; private int rumblerIdx; // index for the rumbler being used private boolean rumblerOn = false; // whether rumbler is on or off public GamePadController() { // get the controllers ControllerEnvironment ce = ControllerEnvironment.getDefaultEnvironment(); Controller[] cs = ce.getControllers(); if (cs.length == 0) { System.out.println("No controllers found"); System.exit(0); } else System.out.println("Num. controllers: " + cs.length); // get the game pad controller controller = findGamePad(cs); System.out.println("Game controller: " + controller.getName() + ", " + controller.getType()); // collect indices for the required game pad components findCompIndices(controller); findRumblers(controller); } // end of GamePadController() private Controller findGamePad(Controller[] cs) /* Search the array of controllers until a suitable game pad controller is found (eith of type GAMEPAD or STICK). */ { Controller.Type type; int i = 0; while(i < cs.length) { type = cs[i].getType(); if ((type == Controller.Type.GAMEPAD) || (type == Controller.Type.STICK)) break; i++; } if (i == cs.length) { System.out.println("No game pad found"); System.exit(0); } else System.out.println("Game pad index: " + i); return cs[i]; } // end of findGamePad() private void findCompIndices(Controller controller) /* Store the indices for the analog sticks axes (x,y) and (z,rz), POV hat, and button components of the controller. */ { comps = controller.getComponents(); if (comps.length == 0) { System.out.println("No Components found"); System.exit(0); } else System.out.println("Num. Components: " + comps.length); // get the indices for the axes of the analog sticks: (x,y) and (z,rz) xAxisIdx = findCompIndex(comps, Component.Identifier.Axis.X, "x-axis"); yAxisIdx = findCompIndex(comps, Component.Identifier.Axis.Y, "y-axis"); zAxisIdx = findCompIndex(comps, Component.Identifier.Axis.Z, "z-axis"); rzAxisIdx = findCompIndex(comps, Component.Identifier.Axis.RZ, "rz-axis"); // get POV hat index povIdx = findCompIndex(comps, Component.Identifier.Axis.POV, "POV hat"); findButtons(comps); } // end of findCompIndices() private int findCompIndex(Component[] comps, Component.Identifier id, String nm) /* Search through comps[] for id, returning the corresponding array index, or -1 */ { Component c; for(int i=0; i < comps.length; i++) { c = comps[i]; if ((c.getIdentifier() == id) && !c.isRelative()) { System.out.println("Found " + c.getName() + "; index: " + i); return i; } } System.out.println("No " + nm + " component found"); return -1; } // end of findCompIndex() private void findButtons(Component[] comps) /* Search through comps[] for NUM_BUTTONS buttons, storing their indices in buttonsIdx[]. Ignore excessive buttons. If there aren't enough buttons, then fill the empty spots in buttonsIdx[] with -1's. */ { buttonsIdx = new int[NUM_BUTTONS]; int numButtons = 0; Component c; for(int i=0; i < comps.length; i++) { c = comps[i]; if (isButton(c)) { // deal with a button if (numButtons == NUM_BUTTONS) // already enough buttons System.out.println("Found an extra button; index: " + i + ". Ignoring it"); else { buttonsIdx[numButtons] = i; // store button index System.out.println("Found " + c.getName() + "; index: " + i); numButtons++; } } } // fill empty spots in buttonsIdx[] with -1's if (numButtons < NUM_BUTTONS) { System.out.println("Too few buttons (" + numButtons + "); expecting " + NUM_BUTTONS); while (numButtons < NUM_BUTTONS) { buttonsIdx[numButtons] = -1; numButtons++; } } } // end of findButtons() private boolean isButton(Component c) /* Return true if the component is a digital/absolute button, and its identifier name ends with "Button" (ie the identifier class is Component.Identifier.Button). */ { if (!c.isAnalog() && !c.isRelative()) { // digital and absolute String className = c.getIdentifier().getClass().getName(); // System.out.println(c.getName() + " identifier: " + className); if (className.endsWith("Button")) return true; } return false; } // end of isButton() private void findRumblers(Controller controller) /* Find the rumblers. Use the last rumbler for making vibrations, an arbitrary decision. */ { // get the game pad's rumblers rumblers = controller.getRumblers(); if (rumblers.length == 0) { System.out.println("No Rumblers found"); rumblerIdx = -1; } else { System.out.println("Rumblers found: " + rumblers.length); rumblerIdx = rumblers.length-1; // use last rumbler } } // end of findRumblers() // ----------------- polling and getting data ------------------ public void poll() // update the component values in the controller { controller.poll(); } public int getXYStickDir() // return the (x,y) analog stick compass direction { if ((xAxisIdx == -1) || (yAxisIdx == -1)) { System.out.println("(x,y) axis data unavailable"); return NONE; } else return getCompassDir(xAxisIdx, yAxisIdx); } // end of getXYStickDir() public int getZRZStickDir() // return the (z,rz) analog stick compass direction { if ((zAxisIdx == -1) || (rzAxisIdx == -1)) { System.out.println("(z,rz) axis data unavailable"); return NONE; } else return getCompassDir(zAxisIdx, rzAxisIdx); } // end of getXYStickDir() private int getCompassDir(int xA, int yA) // Return the axes as a single compass value { float xCoord = comps[ xA ].getPollData(); float yCoord = comps[ yA ].getPollData(); // System.out.println("(x,y): (" + xCoord + "," + yCoord + ")"); int xc = Math.round(xCoord); int yc = Math.round(yCoord); // System.out.println("Rounded (x,y): (" + xc + "," + yc + ")"); if ((yc == -1) && (xc == -1)) // (y,x) return NW; else if ((yc == -1) && (xc == 0)) return NORTH; else if ((yc == -1) && (xc == 1)) return NE; else if ((yc == 0) && (xc == -1)) return WEST; else if ((yc == 0) && (xc == 0)) return NONE; else if ((yc == 0) && (xc == 1)) return EAST; else if ((yc == 1) && (xc == -1)) return SW; else if ((yc == 1) && (xc == 0)) return SOUTH; else if ((yc == 1) && (xc == 1)) return SE; else { System.out.println("Unknown (x,y): (" + xc + "," + yc + ")"); return NONE; } } // end of getCompassDir() public int getHatDir() // Return the POV hat's direction as a compass direction { if (povIdx == -1) { System.out.println("POV hat data unavailable"); return NONE; } else { float povDir = comps[povIdx].getPollData(); if (povDir == POV.CENTER) // 0.0f return NONE; else if (povDir == POV.DOWN) // 0.75f return SOUTH; else if (povDir == POV.DOWN_LEFT) // 0.875f return SW; else if (povDir == POV.DOWN_RIGHT) // 0.625f return SE; else if (povDir == POV.LEFT) // 1.0f return WEST; else if (povDir == POV.RIGHT) // 0.5f return EAST; else if (povDir == POV.UP) // 0.25f return NORTH; else if (povDir == POV.UP_LEFT) // 0.125f return NW; else if (povDir == POV.UP_RIGHT) // 0.375f return NE; else { // assume center System.out.println("POV hat value out of range: " + povDir); return NONE; } } } // end of getHatDir() public boolean[] getButtons() /* Return all the buttons in a single array. Each button value is a boolean. */ { boolean[] buttons = new boolean[NUM_BUTTONS]; float value; for(int i=0; i < NUM_BUTTONS; i++) { value = comps[ buttonsIdx[i] ].getPollData(); buttons[i] = ((value == 0.0f) ? false : true); } return buttons; } // end of getButtons() public boolean isButtonPressed(int pos) /* Return the button value (a boolean) for button number 'pos'. pos is in the range 1-NUM_BUTTONS to match the game pad button labels. */ { if ((pos  NUM_BUTTONS)) { System.out.println("Button position out of range (1-" + NUM_BUTTONS + "): " + pos); return false; } if (buttonsIdx[pos-1] == -1) // no button found at that pos return false; float value = comps[ buttonsIdx[pos-1] ].getPollData(); // array range is 0-NUM_BUTTONS-1 return ((value == 0.0f) ? false : true); } // end of isButtonPressed() // ------------------- Trigger a rumbler ------------------- public void setRumbler(boolean switchOn) // turn the rumbler on or off { if (rumblerIdx != -1) { if (switchOn) rumblers[rumblerIdx].rumble(0.8f); // almost full on for last rumbler else // switch off rumblers[rumblerIdx].rumble(0.0f); rumblerOn = switchOn; // record rumbler's new status } } // end of setRumbler() public boolean isRumblerOn() { return rumblerOn; } } // end of GamePadController class 

请发布有关您正在使用的GamePadController类的更多信息。

更有可能的是,同一个库将提供一个“事件”API,一旦用户按下按钮,就会调用您向游戏手柄对象注册的“回调”。 通过这种设置,“轮询”循环在框架中,而不是您的应用程序,并且它可以更高效,因为它使用来自硬件的信号而不是忙等待轮询循环。


好的,我查看了JInput API,它实际上不是事件驱动的; 你必须像你一样轮询它。 释放按钮时声音是否会停止循环? 如果是这样,你的目标是让声音只播放一次,而不是再次释放并按下按钮之前? 在这种情况下,您需要在每次循环时跟踪上一个按钮状态。

人类的响应时间大约是250毫秒(对于像我这样的老家伙,无论如何)。 如果你每50毫秒进行一次轮询,我希望控制器报告按下按钮几次迭代循环。 你能尝试这样的事吗:

 boolean played = false; while (true) { String sound = null; if (controller.isButtonPressed(1)) sound = "hiHat.wav"; if (controller.isButtonPressed(2)) sound = "crash.wav"; if (sound != null) { if (!played) { drum.playSound(sound); played = true; } } else { played = false; } Thread.sleep(50); } 

我认为你在这里使用了错误的设计模式。 您应该将观察者模式用于此类事物。

轮询循环效率不高,正如您所注意到的那样,并没有真正产生预期的结果。

我不确定你在对象中使用什么来检测是否按下了一个键,但如果它是一个GUI架构,如Swing或AWT ,它将基于观察者模式,通过使用EventListeners等。

这是一个(略微简化的)Observer模式,适用于您的情况。

这种设计的优点是当按下按钮时,方法’buttonChanged’仍然只会被调用一次,而不是每50毫秒开始’重复’。

  public static final int BUTTON_01 = 0x00000001; public static final int BUTTON_02 = 0x00000002; public static final int BUTTON_03 = 0x00000004; public static final int BUTTON_04 = 0x00000008; // hex 8 == dec 8 public static final int BUTTON_05 = 0x00000010; // hex 10 == dec 16 public static final int BUTTON_06 = 0x00000020; // hex 20 == dec 32 public static final int BUTTON_07 = 0x00000040; // hex 40 == dec 64 public static final int BUTTON_08 = 0x00000080; // etc. public static final int BUTTON_09 = 0x00000100; public static final int BUTTON_10 = 0x00000200; public static final int BUTTON_11 = 0x00000400; public static final int BUTTON_12 = 0x00000800; private int previousButtons = 0; void poll() { rockbandDrum.poll(); handleButtons(); } private void handleButtons() { boolean[] buttons = getButtons(); int pressedButtons = getPressedButtons(buttons); if (pressedButtons != previousButtons) { buttonChanged(pressedButtons); // Notify 'listener'. previousButtons = pressedButtons; } } public boolean[] getButtons() { // Return all the buttons in a single array. Each button-value is a boolean. boolean[] buttons = new boolean[MAX_NUMBER_OF_BUTTONS]; float value; for (int i = 0; i < MAX_NUMBER_OF_BUTTONS-1; i++) { int index = buttonsIndex[i]; if (index < 0) { continue; } value = comps[index].getPollData(); buttons[i] = ((value == 0.0f) ? false : true); } return buttons; } private int getPressedButtons(boolean[] array) { // Mold all pressed buttons into a single number by OR-ing their values. int pressedButtons = 0; int i = 1; for (boolean isBbuttonPressed : array) { if (isBbuttonPressed) { pressedButtons |= getOrValue(i); } i++; } return pressedButtons; } private int getOrValue(int btnNumber) // Get a value to 'OR' with. { int btnValue = 0; switch (btnNumber) { case 1 : btnValue = BUTTON_01; break; case 2 : btnValue = BUTTON_02; break; case 3 : btnValue = BUTTON_03; break; case 4 : btnValue = BUTTON_04; break; case 5 : btnValue = BUTTON_05; break; case 6 : btnValue = BUTTON_06; break; case 7 : btnValue = BUTTON_07; break; case 8 : btnValue = BUTTON_08; break; case 9 : btnValue = BUTTON_09; break; case 10 : btnValue = BUTTON_10; break; case 11 : btnValue = BUTTON_11; break; case 12 : btnValue = BUTTON_12; break; default : assert false : "Invalid button-number"; } return btnValue; } public static boolean checkButton(int pressedButtons, int buttonToCheckFor) { return (pressedButtons & buttonToCheckFor) == buttonToCheckFor; } public void buttonChanged(int buttons) { if (checkButton(buttons, BUTTON_01) { drum.playSound("hiHat.wav"); } if (checkButton(buttons, BUTTON_02) { drum.playSound("crash.wav"); } }