Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pixel Perfect Collision detection between a custom view and an ImageView

I have a CustomView and an Image view. The CustomView is a ball that moves around the screen and bounces off the walls. The Image is a quarter circle that you can rotate in a circle on touch. I am trying to make my game so that when the filled pixels from the CustomView cross paths with the Filled pixels from the ImageView a collision is detected. The problem that I am having is I do not know how to retrieve where the filled pixels are on each view.

Here is my XML code

<com.leytontaylor.bouncyballz.AnimatedView
    android:id="@+id/anim_view"
    android:layout_height="fill_parent"
    android:layout_width="fill_parent"
    />

<ImageView
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:id="@+id/quartCircle"
    android:layout_gravity="center_horizontal"
    android:src="@drawable/quartercircle"
    android:scaleType="matrix"/>

My MainActivity

public class MainActivity extends AppCompatActivity {

private static Bitmap imageOriginal, imageScaled;
private static Matrix matrix;

private ImageView dialer;
private int dialerHeight, dialerWidth;

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

    // load the image only once
    if (imageOriginal == null) {
        imageOriginal = BitmapFactory.decodeResource(getResources(), R.drawable.quartercircle);
    }

    // initialize the matrix only once
    if (matrix == null) {
        matrix = new Matrix();
    } else {
        // not needed, you can also post the matrix immediately to restore the old state
        matrix.reset();
    }

    dialer = (ImageView) findViewById(R.id.quartCircle);
    dialer.setOnTouchListener(new MyOnTouchListener());
    dialer.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        @Override
        public void onGlobalLayout() {
            // method called more than once, but the values only need to be initialized one time
            if (dialerHeight == 0 || dialerWidth == 0) {
                dialerHeight = dialer.getHeight();
                dialerWidth = dialer.getWidth();

                // resize
                Matrix resize = new Matrix();
                resize.postScale((float) Math.min(dialerWidth, dialerHeight) / (float) imageOriginal.getWidth(), (float) Math.min(dialerWidth, dialerHeight) / (float) imageOriginal.getHeight());
                imageScaled = Bitmap.createBitmap(imageOriginal, 0, 0, imageOriginal.getWidth(), imageOriginal.getHeight(), resize, false);

                // translate to the image view's center
                float translateX = dialerWidth / 2 - imageScaled.getWidth() / 2;
                float translateY = dialerHeight / 2 - imageScaled.getHeight() / 2;
                matrix.postTranslate(translateX, translateY);

                dialer.setImageBitmap(imageScaled);
                dialer.setImageMatrix(matrix);

            }
        }
    });
 }

MyOnTouchListener class:

private class MyOnTouchListener implements View.OnTouchListener {

    private double startAngle;

    @Override
    public boolean onTouch(View v, MotionEvent event) {

        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:
                startAngle = getAngle(event.getX(), event.getY());
                break;

            case MotionEvent.ACTION_MOVE:
                double currentAngle = getAngle(event.getX(), event.getY());
                rotateDialer((float) (startAngle - currentAngle));
                startAngle = currentAngle;
                break;

            case MotionEvent.ACTION_UP:
                break;
        }
        return true;
    }
}

private double getAngle(double xTouch, double yTouch) {
    double x = xTouch - (dialerWidth / 2d);
    double y = dialerHeight - yTouch - (dialerHeight / 2d);

    switch (getQuadrant(x, y)) {
        case 1:
            return Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
        case 2:
            return 180 - Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
        case 3:
            return 180 + (-1 * Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);
        case 4:
            return 360 + Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
        default:
            return 0;
    }
}

/**
 * @return The selected quadrant.
 */
private static int getQuadrant(double x, double y) {
    if (x >= 0) {
        return y >= 0 ? 1 : 4;
    } else {
        return y >= 0 ? 2 : 3;
    }
}

/**
 * Rotate the dialer.
 *
 * @param degrees The degrees, the dialer should get rotated.
 */
private void rotateDialer(float degrees) {
    matrix.postRotate(degrees, dialerWidth / 2, dialerHeight / 2);

    dialer.setImageMatrix(matrix);
}

And my AnimatedView

public class AnimatedView extends ImageView {

private Context mContext;
int x = -1;
int y = -1;
private int xVelocity = 10;
private int yVelocity = 5;
private Handler h;
private final int FRAME_RATE = 60;

public AnimatedView(Context context, AttributeSet attrs) {
    super(context, attrs);
    mContext = context;
    h = new Handler();
}

private Runnable r = new Runnable() {
    @Override
    public void run() {
        invalidate();
    }
};

protected void onDraw(Canvas c) {
    BitmapDrawable ball = (BitmapDrawable) mContext.getResources().getDrawable(R.drawable.smallerball);

    if (x<0 && y <0) {
        x = this.getWidth()/2;
        y = this.getHeight()/2;
    } else {
        x += xVelocity;
        y += yVelocity;
        if ((x > this.getWidth() - ball.getBitmap().getWidth()) || (x < 0)) {
            xVelocity = xVelocity*-1;
        }
        if ((y > this.getHeight() - ball.getBitmap().getHeight()) || (y < 0)) {
            yVelocity = yVelocity*-1;
        }
    }

    c.drawBitmap(ball.getBitmap(), x, y, null);
    h.postDelayed(r, FRAME_RATE);
}

public float getX() {
    return x;
}

public float getY() {
    return y;
}

}

My question is: How can I retrieve the filled pixels from both of these views, and pass them through a function that detects a collision.

Thanks in advance for the help!:)

like image 652
Leyton Taylor Avatar asked Jun 25 '17 01:06

Leyton Taylor


1 Answers

You could encapsulate your images with Array of points marking your border coordinates(and update them on move/calculate them based on origin) then you'll be able to decide whether oposing object are in touch or not(if any of oposing arrays share the same point)

like image 158
PanBrambor Avatar answered Oct 23 '22 06:10

PanBrambor