Время задержки и JInput - PullRequest
       87

Время задержки и JInput

2 голосов
/ 31 января 2010

ОК, я не знаю, как сформулировать этот вопрос, но, возможно, мой код разобьет проблему:

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" (i.e. 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 < 1) || (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

Ответы [ 3 ]

3 голосов
/ 31 января 2010

Я думаю, что вы используете неправильный шаблон дизайна здесь. Вы должны использовать шаблон наблюдателя для этого типа вещей.

Цикл опроса не очень эффективен, и, как вы заметили, на самом деле не дает желаемых результатов.

Я не уверен, что вы используете внутри своих объектов для обнаружения нажатия клавиши, но если это архитектура GUI, такая как Swing или AWT , она будет основана на шаблоне наблюдателя с помощью EventListener и т. д.

1 голос
/ 02 февраля 2010

Вот (слегка упрощенный) шаблон 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");
    }
  }
0 голосов
/ 31 января 2010

Пожалуйста, опубликуйте дополнительную информацию о классе 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);
}
...