安卓 关于《贪吃蛇大作战》蛇头方向的判断与实现

本人大二学生,自学安卓半年左右,第一次发博客,忘各位不喜勿喷,有什么问题欢迎指正~

前两天看了这两篇博客

Android自定义View系列之<贪吃蛇大作战>方向操作键效果实现http://www.jianshu.com/p/c8e4add4780a

AndroidMatrix详解http://blog.csdn.net/flash129/article/details/8234599

有不少感触,想继续把贪吃蛇大作战挖一挖。

建议大家先按照第一篇引文把handleView控件做出来,因为本文是在第一篇基础上继续往下做的。


先上效果图:



讲解部分:

在 handleView 实现后

1、TransformMatrixView 类:

  重写 imageview,基本按照第二篇引文写的,几乎没有变动


  head2原图:     (一会解释为什么是向右边)

public class TransformMatrixView extends  ImageView {
    private Bitmap bitmap;
    private Matrix matrix;
    public TransformMatrixView(Context context)
    {
        super(context);
            bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.head2);
        matrix = new Matrix();
    }
    @Override
     protected void onDraw(Canvas canvas)
    {
                    // 画出原图像
    //    canvas.drawBitmap(bitmap, 0, 0, null);
                    // 画出变换后的图像
        canvas.drawBitmap(bitmap, matrix, null);
        super.onDraw(canvas);
    }
                @Override
     public void setImageMatrix(Matrix matrix) {
                    this.matrix.set(matrix);
                    super.setImageMatrix(matrix);
    }
    public Bitmap getImageBitmap()
    {
        return bitmap;
    }
}

2、原理讲解:

  首先在 Handleview 里新建2个全局变量:

public float xMove;
public float yMove;  //用于记录,小圆和大圆中心的相对偏移 如下:

xMove = cx2 - cx;
yMove = cy2 - cy;

tanθ = xMove/yMove;    //所以图像要旋转的角度与θ有关


要明白一点,屏幕当中用的坐标系和我们现实的坐标系不同,左为屏幕中的坐标系,右为实际坐标系,如图:

 
 
  所以 yMove = -yMove',即 :tanθ =xMove/(-yMove);tanθ = xMove/(-yMove);

然后把θ做变换反应到matrix.setRotate(θ’width,height);

接下来引入旋转坐标系,黑色度数为旋转度数(现在流行这种左右180的分配,当然从0~360旋转也是可以的),红色为常规坐标系,如图:


按照我这种来算的话很容易发现:θ(旋转)+θ(实际)=90°

接下来讨论arctanθ通过java中math.atan函数,有:

angel = (float) (Math.atan(xMove / (-yMove)) / Math.PI * 180);
可知 arctan θ(angel)在1,3象限为正,2,4象限为负,但不代表它在3象限就是180°到270°之间

要通过一定加减90°、180°换算到我们熟悉的坐标,再被90°减去得到旋转度数,这里我直接给结果:

angel = (float) (Math.atan(xMove / (-yMove)) / Math.PI * 180);
if (xMove > 0 && yMove < 0) {  //第一象限
	//度数正确不改变
} else if (xMove > 0 && yMove > 0) {  //第四象限 
  angel += 180;
} else if (xMove < 0 && yMove < 0) { //第二象限     
      //度数正确不改变
} else if (xMove < 0 && yMove > 0) { //第三象限
  angel -= 180;
}

   但是!问题来了,如果 yMove=0呢?此时手指触碰的位置应该是横向!要用一个 matrix 保存 yMove为0的情况,这里方法有很多,我采用讲常规坐标顺时针旋转90°,如图:



可然后直接保存最初方向,在oncreate 方法中先将图片逆时针旋转90°,呈现正方向状态,每一次 ACTION_MOVE 都去通过读取 angel 改变图片方向,改变后的 angel 代码:

private void rotateRole(float xMove, float yMove) {
  //图片处理(蛇头旋转)
  if (yMove != 0) {
    angel = (float) (Math.atan(xMove / (-yMove)) / Math.PI * 180);
    if (xMove > 0 && yMove < 0) {  //第一象限
      angel -= 90;  //第二次
    }
    else if (xMove > 0 && yMove > 0) {  //第四象限
      angel += 90;  //第二次
    }
    else if (xMove < 0 && yMove < 0) { //第二象限
      angel -= 90;  //第二次
    }
    else if (xMove < 0 && yMove > 0) { //第三象限
      angel = angel - 270;  //第二次
    }
  } else {
    mRole.setImageMatrix(matrixPre);
  }
  matrixLast.setRotate(angel, mRole.getImageBitmap().getWidth() / 2f,
          mRole.getImageBitmap().getHeight() / 2f);

 mRole.setImageMatrix(matrixLast);
}

代码部分:

  HandleView类:

public class HandleView extends View {

  private Paint mPaintForCircle;
  private HandleReaction mHandleReaction;

  public float xMove;
  public float yMove;

  public HandleView(Context context) {
    this(context, null);
  }

  public HandleView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public HandleView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }

  private static int getDefaultSize2(int size, int measureSpec) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
      case MeasureSpec.UNSPECIFIED:
        result = size;
        break;
      case MeasureSpec.AT_MOST:
        result = Math.min(size, specSize);
        break;
      case MeasureSpec.EXACTLY:
        result = specSize;
        break;
    }
    return result;
  }

  public void setHandleReaction(HandleReaction handleReaction) {
    mHandleReaction = handleReaction;
  }

  @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize2(getSuggestedMinimumWidth(), widthMeasureSpec),
        getDefaultSize2(getSuggestedMinimumHeight(), heightMeasureSpec));
    int childWidthSize = getMeasuredWidth();
    widthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  }

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

  @Override protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    //背景透明
    canvas.drawColor(Color.TRANSPARENT);

    //外圆半径
    int radiusOuter = getWidth() / 2;  //120
    //内圆半径
    int radiusInner = getWidth() / 5;  //48

    //圆心坐标  getWidth()代表handleView这个控件的宽度  但这个不是真实的原点坐标
    float cx = getWidth() / 2;
    float cy = getHeight() / 2;

    if (null == mPaintForCircle) {
      mPaintForCircle = new Paint();
    }

    mPaintForCircle.setAntiAlias(true);
    mPaintForCircle.setStyle(Paint.Style.FILL);

    mPaintForCircle.setColor(Color.argb(0x7f, 0x11, 0x11, 0x11));  //外围圆环颜色
    canvas.drawCircle(cx, cy, radiusOuter, mPaintForCircle);

    //未点击的时候
    if (null == mHandleReaction || null == mHandleReaction.getTouchPosition()) {
  //    mPaintForCircle.setColor(Color.argb(0xff, 0x11, 0x11, 0x11));
      mPaintForCircle.setColor(Color.argb(190,255,255,255));   //0为透明 255为不透明
      canvas.drawCircle(cx, cy, radiusInner, mPaintForCircle);
      canvas.save();
      return;
    }

    float[] touchPosition = mHandleReaction.getTouchPosition();

    double ratio = (radiusOuter - radiusInner) / Math.sqrt(
        Math.pow(touchPosition[0] - cx - getLeft(), 2) + Math.pow(touchPosition[1] - cy - getTop(),
            2));
    float cx2 = (float) (ratio * (touchPosition[0] - cx - getLeft()) + cx);
    float cy2 = (float) (ratio * (touchPosition[1] - cy - getTop()) + cy);

    //点击时
    //  mPaintForCircle.setColor(Color.argb(0xff, 0x11, 0x11, 0x11));
    mPaintForCircle.setColor(Color.argb(150,255,255,255));
    canvas.drawCircle(cx2, cy2, radiusInner, mPaintForCircle);

    xMove = cx2 - cx;   //x偏移量   与实际xy坐标无关
    yMove = cy2 - cy;

    canvas.save();
  }

  /**
   * 获得用户触摸的坐标
   */
  public interface HandleReaction {

    float[] getTouchPosition();  //touchPostition[0][1]
  }
}

  TransformMatrixView类:

public class TransformMatrixView extends  ImageView {

    private Bitmap bitmap;
    private Matrix matrix;
    public TransformMatrixView(Context context)
    {
        super(context);
            bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.head2);
        matrix = new Matrix();
    }

    @Override
     protected void onDraw(Canvas canvas)
    {
        canvas.drawBitmap(bitmap, matrix, null);
        super.onDraw(canvas);
    }

                @Override
     public void setImageMatrix(Matrix matrix) {
                    this.matrix.set(matrix);
                    super.setImageMatrix(matrix);}

    public Bitmap getImageBitmap()
    {
        return bitmap;
    }
}

  BackgroundView类:

public class BackgroundView extends View {

    public float width = 0, height = 0;
    public int unit = 30;
    public int rCircle = 40;
    public int[][] cXY;
    public int[] colors = {Color.GREEN, Color.RED, Color.CYAN,
            Color.BLUE, Color.YELLOW, Color.GREEN};

    private Point[] points = new Point[colors.length];

    public BackgroundView(Context context) {
        super(context);
    }

    public BackgroundView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public BackgroundView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        Paint paint = new Paint();
        Paint paintC = new Paint();

        paint.setColor(getResources().getColor(R.color.colorBg));
        paint.setStrokeWidth(2);
        paint.setStyle(Paint.Style.FILL);
        paintC.setStyle(Paint.Style.FILL);

        for (int i = 0; i < width / unit; i++)
            for (int j = 0; j < height / unit; j++) {
                canvas.drawLine(0, (j + 1) * unit,
                        width, (j + 1) * unit, paint);
                canvas.drawLine((i + 1) * unit, 0, (i + 1) * unit,
                        height, paint);
            }
        cXY = getCircleXY();
        for (int i = 0; i < points.length; i++) {
            paintC.setColor(colors[i]);
            canvas.drawCircle(cXY[i][0], cXY[i][1], rCircle, paintC);  //圆
        }
    }

    private int[][] getCircleXY() {
        HashSet<Integer> integerHashSet = new HashSet<Integer>();
        Random random = new Random();
        int[][] circleXY = new int[colors.length][2];

        int randomX, randomY;
        for (int i = 0; i <colors.length; i++) {
            randomX = rCircle / 2 +
                    random.nextInt((int) width - rCircle);
            randomY = rCircle / 2 +
                    random.nextInt((int) height - rCircle);
            if (!integerHashSet.contains(randomX)) {  //确保x坐标唯一
                integerHashSet.add(randomX);
                circleXY[i][0] = randomX;
            }
            circleXY[i][1] = randomY;
        }
        return circleXY;
    }
}

  MainActivity类:

public class MainActivity extends AppCompatActivity
        implements HandleView.HandleReaction, View.OnTouchListener {

  private float[] mTouchPosition = null;
  private HandleView mHandleView;
  private BackgroundView backgroundView;
  private TransformMatrixView mRole;
  private Matrix matrixPre, matrixLast;

  float angel = 0f;
  int width, height;

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    WindowManager wm = this.getWindowManager();
    width = wm.getDefaultDisplay().getWidth();
    height = wm.getDefaultDisplay().getHeight();

    FrameLayout frameLayout = (FrameLayout) findViewById(R.id.frameLayout);
    frameLayout.setOnTouchListener(this);  //对整个界面这是touch监听  保证点所有位置都可以反应
    mHandleView = (HandleView) findViewById(R.id.handleView);
    mHandleView.setHandleReaction(this);

    backgroundView = (BackgroundView) findViewById(R.id.backgroundView);
    backgroundView.width = width;
    backgroundView.height = height;

    mRole = new TransformMatrixView(this);
    mRole.setX(width / 2 - mRole.getImageBitmap().getWidth() / 2f);
    mRole.setY(height / 2 - mRole.getImageBitmap().getHeight() / 2f);
    mRole.setScaleType(ImageView.ScaleType.MATRIX);
    matrixPre = new Matrix();
    matrixLast = new Matrix();
    matrixPre = mRole.getMatrix();
    matrixLast.setRotate(-90, mRole.getImageBitmap().getWidth() / 2f,
            mRole.getImageBitmap().getHeight() / 2f);
    mRole.setImageMatrix(matrixLast);
    frameLayout.addView(mRole);

    //   Log.d("test", String.valueOf(Math.atan(1.0)/Math.PI * 180));
  }

  @Override
  public boolean onTouch(View view, MotionEvent motionEvent) {

    switch (motionEvent.getAction()) {
      case MotionEvent.ACTION_DOWN: {
        mTouchPosition = new float[2];
        mTouchPosition[0] = motionEvent.getX();
        mTouchPosition[1] = motionEvent.getY();

        rotateRole(mHandleView.xMove, mHandleView.yMove);
        mHandleView.invalidate();
        return true;
      }
      case MotionEvent.ACTION_MOVE: {
        mTouchPosition[0] = motionEvent.getX();
        mTouchPosition[1] = motionEvent.getY();

        rotateRole(mHandleView.xMove, mHandleView.yMove);

        Log.d("roleX", String.valueOf(mRole.getX()));
        Log.d("roleY", String.valueOf(mRole.getY()));
        mHandleView.invalidate();
        return true;
      }
      case MotionEvent.ACTION_UP: {
        mTouchPosition = null;
        mHandleView.invalidate();
        return true;
      }
    }
    return true;
  }

  @Override
  public float[] getTouchPosition() {
    return mTouchPosition;
  }

  private void rotateRole(float xMove, float yMove) {
    //图片处理(蛇头旋转)
    if (yMove != 0) {
      angel = (float) (Math.atan(xMove / (-yMove)) / Math.PI * 180);
      if (xMove > 0 && yMove < 0) {  //第一象限
        angel -= 90; 
      } else if (xMove > 0 && yMove > 0) {  //第四象限
        //    angel += 180; 
        angel += 90; 
      } else if (xMove < 0 && yMove < 0) { //第二象限
        angel -= 90; 
      } else if (xMove < 0 && yMove > 0) { //第三象限
         //   angel = angel - 180;
        angel = angel - 270; 
      }
    } else {
      mRole.setImageMatrix(matrixPre);
    }
    matrixLast.setRotate(angel, mRole.getImageBitmap().getWidth() / 2f,
            mRole.getImageBitmap().getHeight() / 2f);
    mRole.setImageMatrix(matrixLast);
  }
}

  XML:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:background="#FCFCFC"
    android:id="@+id/relativeLayout"
    >
    <com.hayukleung.handle.BackgroundView
        android:id="@+id/backgroundView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentBottom="true" />

    <FrameLayout
        android:id="@+id/frameLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true">

<com.hayukleung.handle.HandleView
            android:id="@+id/handleView"
            android:layout_width="80dp"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|center"
            android:layout_marginBottom="100dp"
            />
    </FrameLayout>
</RelativeLayout>


发布了18 篇原创文章 · 获赞 21 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_35561857/article/details/54668312