我的游戏操纵杆问题

时间:2015-03-01 12:30:17

标签: java android game-engine game-physics joystick

我在宇宙飞船第一人称视角游戏中工作。我有一个操纵杆,当我移动操纵杆时,我可以移动屏幕上的所有物体(小行星),模拟用操纵杆移动宇宙飞船。

游戏运行正常,但现在我遇到了问题。如果您在最大左侧位置按下操纵杆,然后执行ACTION_UP,然后立即在操纵杆中立即执行ACTION_DOWN,但在最右侧位置,飞船开始以最大速度向右移动。 很难解释。例如,如果您在最左侧位置按下操纵杆,则太空船每帧向左移动-20px,如果您在最右侧位置按下操纵杆,则飞船向右移动+ 20px每帧。

所以,现在,如果我在操纵杆上进行快速最大左右最大触摸,那么宇宙飞船会做这个动作:-20 .... + 20

这不是真正的运动。

我想得到这个动作:-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);

1 个答案:

答案 0 :(得分:1)

我不会为您实施这些更改,但希望这个答案可以帮助您自己实现这些更改。

使用当前实现,您将更新每帧的对象位置(x,y)。为了获得您想要的更逼真的物理效果,您还需要存储和更新速度(vx,vy)。

在当前正在更新位置的对象中添加两个新变量vxvy(初始值为零)。操纵杆应控制速度的变化而不是位置。更改更新位置x和y的代码,以更新速度vx和vy。当操纵杆最大化时,您可以设置vx = vx - 3

更新速度后,您需要使用速度变量更新位置。例如,设置位置x = x + vx。理想情况下,即使您不移动操纵杆,您也希望以不同的方式运行,但为了保持简单,您可以在更新速度变量后立即执行此更新。

通过这种实现,您将获得更逼真的游戏物理。作为下一步,您可能希望增加速度限制,以避免移动太快。这可以通过if语句来完成,在if语句中,在向其添加更多值之前检查值是否太大,或者在从中减去之前检查该值是否太小。祝你好运!