android随机小球碰撞(一)边界检测

1. 通过自定义View绘制小球

创建Ball类,封装与小球有关的属性元素
在构造方法中随机生成0-360的方向值

package com.pcf.randomball.bean;

import java.util.HashMap;
import java.util.Random;

public class Ball {

    int radius;//半径大小  单位px
    int color;//色值
    int alpha;//透明度
    int x;//圆心x坐标
    int y;//圆心y坐标
    int degree;//方向
    int speed//速度 1-3
    int axisX;//X轴矢量方向值
    int axisY;//Y轴矢量方向值

    public Ball() {
        //生成一个在0-360范围内的随机数 为小球的方向
        this.degree = new Random().nextInt(360);
        //生成一个在1-3范围内的随机数 为小球的速度
        this.speed = new Random().nextInt(3)+1;
    }

    public Ball(int radius, int color, int alpha, int x, int y) {
        this.radius = radius;
        this.color = color;
        this.alpha = alpha;
        this.x = x;
        this.y = y;
        //生成一个在0-360范围内的随机数 为小球的方向
        this.degree = new Random().nextInt(360);
        //生成一个在1-3范围内的随机数 为小球的速度
        this.speed = new Random().nextInt(3)+1;
        if (degree == 0) {
            axisX = 0;
            axisY = 1;
        } else if (degree == 90) {
            axisX = 1;
            axisY = 0;
        } else if (degree == 180) {
            axisX = 0;
            axisY = -1;
        } else if (degree == 270) {
            axisX = -1;
            axisY = 0;
        } else if (degree > 0 && degree < 90) {
            axisX = 1;
            axisY = 1;
        } else if (degree > 90 && degree < 180) {
            axisX = 1;
            axisY = -1;
        } else if (degree > 180 && degree < 270) {
            axisX = -1;
            axisY = -1;
        } else if (degree > 270 && degree < 360) {
            axisX = -1;
            axisY = 1;
        }
    }

    public int getRadius() {
        return radius;
    }

    public void setRadius(int radius) {
        this.radius = radius;
    }

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }

    public int getAlpha() {
        return alpha;
    }

    public void setAlpha(int alpha) {
        this.alpha = alpha;
    }
    
    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getDegree() {
        return degree;
    }

    public void setDegree(int degree) {
        this.degree = degree;
    }
    
    public long getSpeed() {
        return speed;
    }

    public void setSpeed(long speed) {
        this.speed = speed;
    }

    public int getAxisX() {
        return axisX;
    }

    public void setAxisX(int axisX) {
        this.axisX = axisX;
    }

    public int getAxisY() {
        return axisY;
    }

    public void setAxisY(int axisY) {
        this.axisY = axisY;
    }
}

创建自定义RandomView类继承自View类
实现构造方法与OnDraw OnLayout OnMeasure方法
在OnLayout方法中获取控件实际宽高
在OnDraw方法里绘制圆形小球

package com.pcf.randomball.bean;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
/**
 * 随机小球View
 * */
public class RandomView extends View {
	
    public RandomView(Context context) {
        super(context);
    }

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

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Ball ball = new Ball(50, Color.BLUE, 600, width / 2, height / 2)
        //实例化画笔对象
        Paint paint = new Paint();
        //给画笔设置颜色
        paint.setColor(ball.getColor());
        paint.setAlpha(ball.getAlpha());
        //设置画笔属性
        paint.setStyle(Paint.Style.FILL);//画笔属性是实心
        paint.setStrokeWidth(1);//设置画笔粗细
        /**四个参数:
         * 参数一:圆心的x坐标
         * 参数二:圆心的y坐标
         * 参数三:圆的半径
         * 参数四:定义好的画笔
         **/
        canvas.drawCircle(ball.getX(), ball.getY(), ball.getRadius(), paint);
    }
}

2. 定时更新小球位置,让小球运动

在Ball类的构造方法里面,通过生成随机数0-360赋值运动方向

45度角,第一象限
在直角坐标系中把0-360°分成四等份,均分在四个象限中
通过角度判断小球的运动方向,每个角限都是90°
无法直接对View进行斜线的移动
把斜线的移动分解成竖向与横向的移动
沿X轴的,Y轴的进行分解
把90°分成了10等份,并分解到X轴与Y轴
开启线程定时更新小球位置并重新绘制小球位置

PS:后面会把小球运动的方案改为以速度为基础

package com.pcf.randomball.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import com.pcf.randomball.bean.Ball;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class RandomView extends View {

    private int height;
    private int width;
    private long maxBallNumber = -1;
    private List<Ball> ballList = new ArrayList();
    private String TAG = "RandomLayout";
    private MyThread thread;

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

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

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.d(TAG, "onMeasure ");
        width = getMeasuredWidth();
        height = getMeasuredHeight();
        Log.d(TAG, "width " + width);
        Log.d(TAG, "height " + height);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Log.d(TAG, "onLayout ");
        ballList.add(new Ball(50, Color.BLUE, 600, width / 2, height / 2));
        //避免重复创建线程
        if (thread == null) {
            thread = new MyThread();
        }
        thread.start();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.d(TAG, "onDraw ");
        for (int i = 0; i < ballList.size(); i++) {
            Ball ball = ballList.get(i);
            //实例化画笔对象
            Paint paint = new Paint();
            //给画笔设置颜色
            paint.setColor(ball.getColor());
            paint.setAlpha(ball.getAlpha());
            //设置画笔属性
            paint.setStyle(Paint.Style.FILL);//画笔属性是实心
            paint.setStrokeWidth(1);//设置画笔粗细
            /*四个参数:
                参数一:圆心的x坐标
                参数二:圆心的y坐标
                参数三:圆的半径
                参数四:定义好的画笔*/
            canvas.drawCircle(ball.getX(), ball.getY(), ball.getRadius(), paint);
        }
    }

    public long getMaxBallNumber() {
        return maxBallNumber;
    }

    public void setMaxBallNumber(long maxBallNumber) {
        this.maxBallNumber = maxBallNumber;
    }

    /**
     * 往布局添加小球
     */
    public void addBall(Ball ball) {
        if (ball != null && ballList.size()<maxBallNumber) {
            ballList.add(ball);
        }
    }

    /**
     * 从布局移除小球
     */
    public void removeBall(Ball ball) {
        if (ball != null)
            ballList.remove(ball);
    }

    public void actionBall() {
        for (int i = 0; i < ballList.size(); i++) {
            Ball ball1 = ballList.get(i);
            int x1 = ball1.getX();
            int y1 = ball1.getY();
            int degree = ball1.getDegree();

            //按照0-360度划分四个象限
            if (degree >= 0 && degree < 90) {
                //按10等份去分配X轴与Y轴的位移值
                int xL = degree / 9;
                int yL = 10 - degree / 9;
                //第一象限
                ball1.setX(x1 + xL);
                ball1.setY(y1 + yL);
            } else if (degree >= 90 && degree < 180) {
                //第二象限 碰撞到布局边界
                int xL = 10 - (degree / 9 - 10);
                int yL = (degree / 9 - 10);
                ball1.setX(x1 + xL);
                ball1.setY(y1 + yL);
            } else if (degree >= 180 && degree < 270) {
                //第三象限 碰撞到布局边界
                int xL = (degree / 9 - 20);
                int yL = 10 - (degree / 9 - 20);
                ball1.setX(x1 - xL);
                ball1.setY(y1 + yL);
            } else if (degree >= 270 && degree < 360) {
                //第四象限  碰撞到布局边界
                int xL = 10 - (degree / 9 - 30);
                int yL = (degree / 9 - 30);
                ball1.setX(x1 - xL);
                ball1.setY(y1 - yL);
            }
        }
    }

    private class MyThread extends Thread {
        @Override
        public void run() {
            while (true) {
                actionBall();
                //通知更新界面,会重新调用onDraw()函数
                postInvalidate(); 
                try {
                    sleep(10);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if(thread!=null)
        thread.stop();
    }
}

3. 边界检测,小球碰撞到布局边界时更改小球运动方向

根据入射角与反射角与平面夹角大小相等的原理
可以计算出在不同象限与边界碰撞后的运动方向
入射角与反射角

package com.pcf.randomball.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import com.pcf.randomball.bean.Ball;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class RandomView extends View {

    private int height;
    private int width;
    private long maxBallNumber = -1;
    private List<Ball> ballList = new ArrayList();
    private String TAG = "RandomLayout";
    private MyThread thread;

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

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

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.d(TAG, "onMeasure ");
        width = getMeasuredWidth();
        height = getMeasuredHeight();
        Log.d(TAG, "width " + width);
        Log.d(TAG, "height " + height);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Log.d(TAG, "onLayout ");
        ballList.add(new Ball(50, Color.BLUE, 600, width / 2, height / 2));
        //避免重复创建线程
        if (thread == null) {
            thread = new MyThread();
        }
        thread.start();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.d(TAG, "onDraw ");
        for (int i = 0; i < ballList.size(); i++) {
            Ball ball = ballList.get(i);
            //实例化画笔对象
            Paint paint = new Paint();
            //给画笔设置颜色
            paint.setColor(ball.getColor());
            paint.setAlpha(ball.getAlpha());
            //设置画笔属性
            paint.setStyle(Paint.Style.FILL);//画笔属性是实心
            paint.setStrokeWidth(1);//设置画笔粗细
            /*四个参数:
                参数一:圆心的x坐标
                参数二:圆心的y坐标
                参数三:圆的半径
                参数四:定义好的画笔*/
            canvas.drawCircle(ball.getX(), ball.getY(), ball.getRadius(), paint);
        }
    }

    public long getMaxBallNumber() {
        return maxBallNumber;
    }

    public void setMaxBallNumber(long maxBallNumber) {
        this.maxBallNumber = maxBallNumber;
    }

    /**
     * 往布局添加小球
     */
    public void addBall(Ball ball) {
        if (ball != null && ballList.size()<maxBallNumber) {
            ballList.add(ball);
        }
    }

    /**
     * 从布局移除小球
     */
    public void removeBall(Ball ball) {
        if (ball != null)
            ballList.remove(ball);
    }

    public void actionBall() {
        for (int i = 0; i < ballList.size(); i++) {
            Ball ball1 = ballList.get(i);
            int x1 = ball1.getX();
            int y1 = ball1.getY();
            int degree = ball1.getDegree();

            //按照0-360度划分四个象限
            if (degree >= 0 && degree < 90) {
                //按10等份去分配X轴与Y轴的位移值
                int xL = degree / 9;
                int yL = 10 - degree / 9;
                //第一象限 碰撞到布局右边界
                if (ball1.getX() + ball1.getRadius() >= width) {
                    ball1.setDegree(360 - degree);
                    ball1.setX(x1 - ball1.getRadius());
                } else {
                    if (x1 + ball1.getRadius() + 1 + xL >= width) {
                        ball1.setX(width - ball1.getRadius());
                    } else {
                        ball1.setX(x1 + 1 + xL);
                    }
                }
                //碰撞到布局上边界
                if (ball1.getY() - ball1.getRadius() <= 0) {
                    ball1.setDegree(180 - degree);
                    ball1.setY(y1 + 1 + yL);
                } else {
                    if (y1 - ball1.getRadius() - 1 - yL <= 0) {
                        ball1.setY(ball1.getRadius());
                    } else {
                        ball1.setY(y1 - 1 - yL);
                    }
                }
            } else if (degree >= 90 && degree < 180) {
                //第二象限 碰撞到布局边界
                int xL = 10 - (degree / 9 - 10);
                int yL = (degree / 9 - 10);
                if (ball1.getX() + ball1.getRadius() >= width) {
                    ball1.setDegree(180 - degree + 180);
                    ball1.setX(x1 - ball1.getRadius());
                } else {
                    if (x1 + ball1.getRadius() + 1 + xL >= width) {
                        ball1.setX(width - ball1.getRadius());
                    } else {
                        ball1.setX(x1 + 1 + xL);
                    }
                }
                //碰撞到布局边界
                if (ball1.getY() + ball1.getRadius() >= height) {
                    ball1.setDegree(90 - degree + 90);
                    ball1.setY(y1 - 1 - yL);
                } else {
                    if (y1 + ball1.getRadius() + 1 + yL >= height) {
                        ball1.setY(height - ball1.getRadius());
                    } else {
                        ball1.setY(y1 + 1 + yL);
                    }
                }
            } else if (degree >= 180 && degree < 270) {
                //第三象限 碰撞到布局边界
                int xL = (degree / 9 - 20);
                int yL = 10 - (degree / 9 - 20);
                if (ball1.getX() - ball1.getRadius() <= 0) {
                    ball1.setDegree(360 - degree);
                    ball1.setX(x1 + 1 + xL);
                } else {
                    if (x1 - ball1.getRadius() - 1 - xL <= 0) {
                        ball1.setX(ball1.getRadius());
                    } else {
                        ball1.setX(x1 - 1 - xL);
                    }
                }
                //碰撞到布局边界
                if (ball1.getY() + ball1.getRadius() >= height) {
                    ball1.setDegree(270 + 270 - degree);
                    ball1.setY(y1 - 1 - yL);
                } else {
                    if (x1 + ball1.getRadius() + 1 + yL >= height) {
                        ball1.setY(height - ball1.getRadius());
                    } else {
                        ball1.setY(y1 + 1 + yL);
                    }
                }
            } else if (degree >= 270 && degree < 360) {
                //第四象限  碰撞到布局边界
                int xL = 10 - (degree / 9 - 30);
                int yL = (degree / 9 - 30);
                if (ball1.getX() - ball1.getRadius() <= 0) {
                    ball1.setDegree(360 - degree);
                    ball1.setX(x1 + 1 + xL);
                } else {
                    if (x1 - ball1.getRadius() - 1 - xL <= 0) {
                        ball1.setX(ball1.getRadius());
                    } else {
                        ball1.setX(x1 - 1 - xL);
                    }
                }
                //碰撞到布局边界
                if (ball1.getY() - ball1.getRadius() <= 0) {
                    ball1.setDegree(360 - degree + 180);
                    ball1.setY(y1 + 1 + yL);
                } else {
                    if (y1 - ball1.getRadius() - 1 - yL <= 0) {
                        ball1.setY(ball1.getRadius());
                    } else {
                        ball1.setY(y1 - 1 - yL);
                    }
                }
            }
        }
    }

    private class MyThread extends Thread {
        @Override
        public void run() {
            while (true) {
                actionBall();
                //通知更新界面,会重新调用onDraw()函数
                postInvalidate(); 
                try {
                    sleep(10);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if(thread!=null)
        thread.stop();
    }
}

4. GitHubDemo

GitHubDemo
https://github.com/pengchengfuGit/randomball

5. 相关链接

https://www.cnblogs.com/xieyuan/archive/2012/11/27/3787450.html

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值