2015-03-01 6 views
-4

Я работаю в игре с видом на корабль первого лица. У меня есть джойстик, и когда я перемещаю джойстик, я могу перемещать все объекты (астероиды) экрана, имитируя, что космический корабль перемещается с помощью джойстика.Проблема с моей игрой Джойстик

Игра работает отлично, но теперь у меня есть проблема. Если вы нажимаете джойстик в макс левом положении, а затем вы снова активируете ACTION_UP, а затем мгновенно ACTION_DOWN в джойстике, но в максимальном правильном положении, космический корабль начинает двигаться вправо с максимальной скоростью. Трудно это объяснить. Например, если вы нажмете джойстик в макс левом положении, космический корабль движется -20px за кадр влево, и если вы нажмете джойстик в макс. Правильном положении, космический корабль движется вправо + 20 пикселей на кадр.

Итак, теперь, если я сделать быстрый максимум левый и правый максимум касания на джойстике, космический корабль делает это движение: -20 .... + 20

Это не reallistic движение.

Я хочу получить это движение: -20 -17 -14 -9 -5 0 +5 +9 +14 +17 +20 .... Я имею в виду более реалистичное движение космического корабля. Но проблема в том, что я не специалист по математике или физике, и я не знаю, как получить такую ​​функциональность в этом джойстике ... любая помощь будет очень благодарна.

Здесь вы можете найти демонстрационный проект с помощью джойстика:https://mega.co.nz/#!cp5FhYIT!dM88qx_xQdyhED9fX_4xeJ9ciQYJirUlNzEi-KOzU2k

Это код джойстик, я нашел его в Google и работает очень хорошо для не реалистического движения, которое я описал раньше, за исключением:

public class Joystick extends View { 
    public static final int INVALID_POINTER = -1; 

    private JoystickMovedListener moveListener; 

    //# of pixels movement required between reporting to the listener 
    private float moveResolution; 

    //Max range of movement in user coordinate system 
    private float movementRange; 

    //Last touch point in view coordinates 
    private int pointerId = INVALID_POINTER; 
    private float touchX; 
    private float touchY; 
    private float touchXDelayedMovement; 
    private float touchYDelayedMovement; 

    //Handle center in view coordinates 
    private float handleX; 
    private float handleY; 

    //Last reported position in view coordinates (allows different reporting sensitivities) 
    private float reportX; 
    private float reportY; 

    //Center of the view in view coordinates 
    private int cX; 
    private int cY; 

    //Size of the view in view coordinates 
    private int dimX; 
    private int dimY; 

    private int innerPadding; 
    private int bgRadius; 
    private int handleRadius; 
    private int movementRadius; 
    private int handleInnerBoundaries; 

    //Cartesian coordinates of last touch point - joystick center is (0,0) 
    private int cartX; 
    private int cartY; 

    //User coordinates of last touch point 
    private int userX; 
    private int userY; 

    //Offset co-ordinates (used when touch events are received from parent's coordinate origin) 
    private int offsetX; 
    private int offsetY; 

    private Paint bgPaint; 
    private Paint handlePaint; 

    boolean disabled; 

    Handler handler; 
    Handler handlerDelayedMovement; 

    public Joystick(Context context) { 
     super(context); 
     initJoystickView(); 
    } 

    private void initJoystickView() { 
     setFocusable(true); 

     handlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
     handlePaint.setColor(Color.RED); 
     handlePaint.setStrokeWidth(1); 
     handlePaint.setStyle(Paint.Style.FILL_AND_STROKE); 

     bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
     bgPaint.setColor(Color.DKGRAY); 
     bgPaint.setStrokeWidth(1); 
     bgPaint.setStyle(Paint.Style.FILL_AND_STROKE);  

     this.moveResolution = 1.0f; 

     handler = new Handler(); 
     handlerDelayedMovement = new Handler(); 
    } 

    public void setMovementRange(float movementRange) { 
     this.movementRange = movementRange; 
    } 

    public void setOnJostickMovedListener(JoystickMovedListener listener) { 
     this.moveListener = listener; 
    } 

    @Override 
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 
     super.onLayout(changed, left, top, right, bottom); 

     int d = Math.min(getMeasuredWidth(), getMeasuredHeight()); 

     dimX = d; 
     dimY = d; 

     cX = d/2; 
     cY = d/2; 

     bgRadius = dimX/2 - innerPadding; 
     handleRadius = (int)(d * 0.2); 
     handleInnerBoundaries = handleRadius; 
     movementRadius = Math.min(cX, cY) - handleInnerBoundaries; 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
     // Here we make sure that we have a perfect circle 
     int measuredWidth = measure(widthMeasureSpec); 
     int measuredHeight = measure(heightMeasureSpec); 
     setMeasuredDimension(measuredWidth, measuredHeight); 
    } 

    private int measure(int measureSpec) { 
     int result = 0; 
     // Decode the measurement specifications. 
     int specMode = MeasureSpec.getMode(measureSpec); 
     int specSize = MeasureSpec.getSize(measureSpec); 
     if (specMode == MeasureSpec.UNSPECIFIED) {   
      result = 200; // Return a default size of 200 if no bounds are specified. 
     } else {    
      result = specSize; // As you want to fill the available space always return the full available bounds. 
     } 
     return result; 
    } 

    @Override 
    protected void onDraw(Canvas canvas) { 
     canvas.save(); 
     // Draw the background 
     canvas.drawCircle(cX, cY, bgRadius, bgPaint); 

     // Draw the handle 
     handleX = touchX + cX; 
     handleY = touchY + cY; 
     canvas.drawCircle(handleX, handleY, handleRadius, handlePaint); 

     canvas.restore(); 
    } 

    public void setPointerId(int id) { 
     this.pointerId = id; 
    } 

    public int getPointerId() { 
     return pointerId; 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
     final int action = ev.getAction(); 
     switch (action & MotionEvent.ACTION_MASK) { 
      case MotionEvent.ACTION_MOVE: { 
       if (disabled==true) 
        break; 
       return processMoveEvent(ev); 
      }  
      case MotionEvent.ACTION_CANCEL: 
      case MotionEvent.ACTION_UP: { 
       if (pointerId != INVALID_POINTER) { 
        returnHandleToCenter(); 
        returnHandleToCenterDelayedMovement(); 
        setPointerId(INVALID_POINTER); 
       } 
       break; 
      } 
      case MotionEvent.ACTION_POINTER_UP: { 
       if (pointerId != INVALID_POINTER) { 
        final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 
        final int pointerId = ev.getPointerId(pointerIndex); 
        if (pointerId == this.pointerId) { 
         returnHandleToCenter(); 
         returnHandleToCenterDelayedMovement(); 
         setPointerId(INVALID_POINTER); 
         return true; 
        } 
       } 
       break; 
      } 
      case MotionEvent.ACTION_DOWN: { 
       handlerDelayedMovement.removeCallbacksAndMessages(null); 
       if (pointerId == INVALID_POINTER) { 
        int x = (int) ev.getX(); 
        if (x >= offsetX && x < offsetX + dimX) { 
         setPointerId(ev.getPointerId(0)); 
         if (disabled==true){ 
          return true; 
         }      
         return processMoveEvent(ev); 
        } 
       } 
       break; 
      } 
      case MotionEvent.ACTION_POINTER_DOWN: { 
       if (pointerId == INVALID_POINTER) { 
        final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 
        final int pointerId = ev.getPointerId(pointerIndex); 
        int x = (int) ev.getX(pointerId); 
        if (x >= offsetX && x < offsetX + dimX) { 
         setPointerId(pointerId); 
         return true; 
        } 
       } 
       break; 
      } 
     } 
     return false; 
    } 

    private boolean processMoveEvent(MotionEvent ev) { 
     if (pointerId != INVALID_POINTER) { 
      final int pointerIndex = ev.findPointerIndex(pointerId); 

      // Translate touch position to center of view 
      float x = ev.getX(pointerIndex); 
      touchX = x - cX - offsetX; 
      float y = ev.getY(pointerIndex); 
      touchY = y - cY - offsetY; 

      reportOnMoved(); 
      invalidate(); 

      return true; 
     } 
     return false; 
    } 

    private void reportOnMoved() { 
     //constraint circle 
     float diffX = touchX; 
     float diffY = touchY; 
     double radial = Math.sqrt((diffX*diffX) + (diffY*diffY)); 
     if (radial > movementRadius) { 
      touchX = (int)((diffX/radial) * movementRadius); 
      touchY = (int)((diffY/radial) * movementRadius); 
     } 

     //We calc user coordinates  
     //First convert to cartesian coordinates 
     cartX = (int)(touchX/movementRadius * movementRange); 
     cartY = (int)(touchY/movementRadius * movementRange); 

     //Cartesian Coordinates 
     userX = cartX; 
     userY = cartY;  

     if (moveListener != null) { 
      boolean rx = Math.abs(touchX - reportX) >= moveResolution; 
      boolean ry = Math.abs(touchY - reportY) >= moveResolution; 
      if (rx || ry) { 
       this.reportX = touchX; 
       this.reportY = touchY; 

       moveListener.OnMoved(userX, userY); 
      } 
     } 
    } 

    private void reportOnMovedDelayedMovement() { 
     //constraint circle 
     float diffX = touchXDelayedMovement; 
     float diffY = touchYDelayedMovement; 
     double radial = Math.sqrt((diffX*diffX) + (diffY*diffY)); 
     if (radial > movementRadius) { 
      touchXDelayedMovement = (int)((diffX/radial) * movementRadius); 
      touchYDelayedMovement = (int)((diffY/radial) * movementRadius); 
     } 

     //We calc user coordinates  
     //First convert to cartesian coordinates 
     cartX = (int)(touchXDelayedMovement/movementRadius * movementRange); 
     cartY = (int)(touchYDelayedMovement/movementRadius * movementRange); 

     //Cartesian Coordinates 
     userX = cartX; 
     userY = cartY;  

     if (moveListener != null) { 
      boolean rx = Math.abs(touchXDelayedMovement - reportX) >= moveResolution; 
      boolean ry = Math.abs(touchYDelayedMovement - reportY) >= moveResolution; 
      if (rx || ry) { 
       this.reportX = touchXDelayedMovement; 
       this.reportY = touchYDelayedMovement; 

       moveListener.OnMoved(userX, userY); 
      } 
     } 
    } 

    private void returnHandleToCenter() { 
     final int numberOfFrames = 5; 
     final double intervalsX = (0 - touchX)/numberOfFrames; 
     final double intervalsY = (0 - touchY)/numberOfFrames; 

     handler.removeCallbacksAndMessages(null); 
     for (int i = 0; i < numberOfFrames; i++) { 
      final int j = i; 
      handler.postDelayed(new Runnable() { 
       @Override 
       public void run() { 
        touchX += intervalsX; 
        touchY += intervalsY; 

        //reportOnMoved(); 
        invalidate(); 

        if (moveListener != null && j == numberOfFrames - 1) { 
         moveListener.OnReturnedToCenter(); 
        } 
       } 
      }, i * 10); 
     } 

     if (moveListener != null) { 
      moveListener.OnReleased(); 
     } 
    } 

    private void returnHandleToCenterDelayedMovement() { 
     final int numberOfFrames = 25; 
     touchXDelayedMovement=touchX; 
     touchYDelayedMovement=touchY; 
     final double intervalsX = (0 - touchXDelayedMovement)/numberOfFrames; 
     final double intervalsY = (0 - touchYDelayedMovement)/numberOfFrames; 

     handlerDelayedMovement.removeCallbacksAndMessages(null); 
     for (int i = 0; i < numberOfFrames; i++) { 
      handlerDelayedMovement.postDelayed(new Runnable() { 
       @Override 
       public void run() { 
        touchXDelayedMovement += intervalsX; 
        touchYDelayedMovement += intervalsY; 

        reportOnMovedDelayedMovement(); 
       } 
      }, i * 50); 
     } 
    } 

    public void setInnerPadding(int innerPadding){ 
     this.innerPadding=innerPadding; 
    } 

    public void disable(){ 
     disabled=true; 
    } 

    public void enable(){ 
     disabled=false; 
    } 

    public interface JoystickMovedListener { 
     public void OnMoved(int pan, int tilt); 
     public void OnReleased(); 
     public void OnReturnedToCenter(); 
    } 
} 

Вы должны сделать это в классе, который будет использовать джойстик:

private JoystickMovedListener joystickListener = new JoystickMovedListener() { 
    @Override 
    public void OnMoved(int pan, int tilt) {  
     //here i move the objects in the game 
     } 
    } 

    @Override 
    public void OnReleased() {} 

    public void OnReturnedToCenter() {}; 
}; 

joystickOnScreen = new Joystick(this); 
     joystickOnScreen.setMovementRange(screenHeight/50); 
     joystickOnScreen.setInnerPadding(screenHeight/30); 
     joystickOnScreen.setOnJostickMovedListener(joystickListener); 
     RelativeLayout.LayoutParams joystickParams = new RelativeLayout.LayoutParams(sh/3, sh/3); 
     joystickParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); 
     joystickParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT); 
     joystickParams.setMargins(sh/100, 0, 0, sh/100); 
     joystickOnScreen.setLayoutParams(joystickParams); 
     joystickOnScreen.setAlpha(0.3f); 
+0

Возможный дубликат [Проблема с моей игрой Джойстик] (http://stackoverflow.com/questions/28792914/issue-with-my-game-joystick) – Clive

+1

, что сообщение не существует, оно удалено – NullPointerException

+2

Нет необходимости задайте один и тот же вопрос снова и снова, если он существует. Дубликаты будут удалены или закрыты. Просто подождите, пока у кого-нибудь есть ответ/сделайте какое-нибудь исследование/откройте щедрость существующего вопроса (желательно все три). – fge

ответ

1

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

С вашей текущей реализацией вы обновляете позицию объекта (x, y) каждого кадра. Чтобы получить более реалистичную физику, которую вы хотите, вам нужно также сохранить и обновить скорость (vx, vy).

Добавьте две новые переменные, vx и vy (с начальными значениями нуля) в объекты, которые вы сейчас обновляете для позиции. Джойстик должен управлять изменением скорости вместо положения. Измените код, который обновляет позиции x и y, чтобы обновить скорости vx и vy. Когда джойстик остается макс, вы можете, например, установить vx = vx - 3.

После того, как скорость обновлена, вам необходимо обновить позицию, используя переменные скорости. Например, установите позицию x = x + vx. В идеале вы хотите, чтобы это происходило в другом методе, который выполняется, даже если вы не перемещаете джойстик, но чтобы это было просто, вы можете сделать это обновление сразу после обновления переменных скорости.

С этой реализацией вы получите более реалистичную физику игры. В качестве следующего шага вам может потребоваться добавить ограничения скорости, чтобы они не двигались слишком быстро. Это можно сделать с помощью if-statement, где вы проверяете, что значение не слишком велико, прежде чем добавлять к нему больше, или слишком замалчивать перед вычитанием из него. Удачи!

+0

Я пытаюсь понять, как реализовать это, но я не могу. Не могли бы вы объяснить это еще немного? – NullPointerException

+0

также в коде, который я отправил вам zipped. Я делаю что-то вроде x = x + vx, но vx не называется vx, вместо этого называется joyX. И это приводит к плохому поведению, которое я описал в вопросе – NullPointerException

+0

В zip-файле нет 'joyX'. Где именно вы используете joyX? – Niemi

 Смежные вопросы

  • Нет связанных вопросов^_^