王学岗小球碰撞

公司要求我写小球碰撞,并有增加小球的效果

package com.meng.ballcollision;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.Log;

/**
 * User: mengqingdong
 * Date: 2018/5/21
 * Time: 14:30
 * Description:
 */

public class Ball {
    public int id;
    /**
     * x轴速度 向右为正,向左为负
     */
    public float vx = 1.0f;
    /**
     * y轴速度  向右为正,向左为负
     */
    public float vy = 1.0f;
    /**
     * 圆心x坐标
     */
    public float centerX;
    /**
     * 圆心y坐标
     */
    public float centerY;
    /**
     * 圆半径
     */
    public float radius = 150.0f;
    /**
     * 2个球碰撞的弹性
     */
    public float spring = 1.0f;
    /**
     * 球与边界碰撞的弹性
     */
    public float bounce = -1.0f;
    /**
     * 球的重力
     */
    public float gravity = 0.00f;

    /**
     * paint
     */
    public Paint paint;
    /**
     * 图片bitmap
     */
    public Bitmap bitmap;
    public RectF bitmapRectF;

    public boolean isStatic;

    public boolean isEnter;

    public boolean isFirstRun;

    public Ball(float radius, float centerX, float centerY) {
        this.radius = radius;
        this.centerX = centerX;
        this.centerY = centerY;
    }

    public Ball(float radius, float centerX, float centerY, Paint paint) {
        this.radius = radius;
        this.centerX = centerX;
        this.centerY = centerY;
        this.paint = paint;
    }

    public Ball(float radius, float centerX, float centerY, Bitmap bitmap) {
        this.radius = radius;
        this.centerX = centerX;
        this.centerY = centerY;
        this.bitmap = bitmap;
    }

    public Ball(float radius, float centerX, float centerY, Paint paint, boolean isStatic, boolean isEnter, int id) {
        this.radius = radius;
        this.centerX = centerX;
        this.centerY = centerY;
        this.isStatic = isStatic;
        this.isEnter = isEnter;
        this.paint = paint;
        this.id = id;
        this.isFirstRun = true;
    }

    public Paint getPaint() {
        return paint;
    }

    public void setPaint(Paint paint) {
        this.paint = paint;
    }

    /**
     * 优先使用bitmap绘制
     */
    public void draw(Canvas canvas) {
        if (null == bitmap) {
            canvas.drawCircle(centerX, centerY, radius, paint);
        } else {
            bitmapRectF = new RectF(centerX - radius, centerY - radius, centerX + radius, centerY + radius);
            canvas.drawBitmap(bitmap, null, bitmapRectF, null);
        }
    }


    /**
     * 计算小球是否越界,超出边界调整其位置并改变运动方向
     *
     * @param width  边界的宽
     * @param height 边界的高
     */
    public void calculateBallIsOutOfBounds(float width, float height) {
        if (isStatic) {
            float edgeDistance = centerX + radius - width * 0.7f;
            if (edgeDistance > 0) {
                //超出右边界
                centerX = width * 0.7f - radius;
                //向左移动
                vx *= bounce;
            }
            edgeDistance = this.centerX - radius;
            if (edgeDistance < width * 0.3f) {
                //超出左边界
                centerX = radius + width * 0.3f;
                //向右移动
                vx *= bounce;
            }
            edgeDistance = centerY + radius - height * 0.6f;
            if (edgeDistance > 0) {
                //超出下边界
                centerY = height * 0.6f - radius;
                vy *= bounce;
            }
            edgeDistance = centerY - radius;
            if (edgeDistance < height * 0.4f) {
                //超出上边界
                centerY = radius + height * 0.4f;
                vy *= bounce;
            }
        } else {
            float edgeDistance = centerX + radius - width;
            if (edgeDistance > 0) {

                //超出右边界
                centerX = width - radius;
                //向左移动
                vx *= bounce;
            }
            edgeDistance = this.centerX - radius;
            if (edgeDistance < 0) {
//                    this.isEnter = false;
                //超出左边界
                centerX = radius;
                //向右移动
                vx *= bounce;
            }
            edgeDistance = centerY + radius - height;
            if (edgeDistance > 0) {
//                    this.isEnter = false;
                //超出下边界
                centerY = height - radius;
                vy *= bounce;
            }
            edgeDistance = centerY - radius;
            if (edgeDistance < 0) {
//                    this.isEnter = false;
                //超出上边界
                centerY = radius;
                vy *= bounce;
            }
        }
        float edgeDistance = centerX + radius - width;
        if (edgeDistance > 0) {
            //超出右边界
            centerX = width - radius;
            //向左移动
            vx *= bounce;
        }
        edgeDistance = this.centerX - radius;
        if (edgeDistance < 0) {
            //超出左边界
            centerX = radius;
            //向右移动
            vx *= bounce;
        }
        edgeDistance = centerY + radius - height;
        if (edgeDistance > 0) {
            //超出下边界
            centerY = height - radius;
            vy *= bounce;
        }
        edgeDistance = centerY - radius;
        if (edgeDistance < 0) {
            //超出上边界
            centerY = radius;
            vy *= bounce;
        }
//        }
    }

    /**
     * 计算小球下一步的位置
     */
    public void calculateNextPositionOfBall() {
//        this.vy += gravity;
//        this.centerX += vx;
//        this.centerY += vy;
        if (isEnter) {
            if (this.isFirstRun) {
                vx = 10;
                vy = 10;
                this.isFirstRun = false;
            }
            this.centerX += vx;
            this.centerY += vy;
        } else {
            // 限制最大速度
            float maxSpeed = 1.5f;  // 设置一个最大速度值
            float speed = (float) Math.sqrt(vx * vx + vy * vy);
            if (speed > maxSpeed) {
                this.vx = (vx / speed) * maxSpeed;
                this.vy = (vy / speed) * maxSpeed;
            }
            this.centerX += vx;
            this.centerY += vy;
            Log.i("赵云" + this.id, this.id + "  vx:" + vx + " vy:" + vy + " isEnter:" + isEnter);
        }
    }
}

package com.meng.ballcollision;

import android.animation.Animator;
import android.animation.ValueAnimator;
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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * User: mengqingdong
 * Date: 2018/5/21
 * Time: 14:42
 * Description: 小球碰撞
 */

public class BallsCollisionView extends View {
    /**
     * 小球半径 px
     */
    private float radius = 150.0f;
    /**
     * 小球数量
     */
    private int ballCount = 1;
    /**
     * 小球集合
     */
    List<Ball> ballList = new ArrayList<>();

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

    public BallsCollisionView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BallsCollisionView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        try {
            createBalls(getMeasuredWidth(), getMeasuredHeight());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 初始化碰撞小球,默认随机位置,初始化时小球不可以重叠
     * 如果想生成不同样式的小球需要重写此方法
     *
     * @param width  view width
     * @param height view height
     */
    public List<Ball> createBalls(float width, float height) throws Exception {
        float minX = radius;
        float maxX = width - radius;
        float minY = radius;
        float maxY = height - radius;
        Paint paint = createBallPaint();
        int i = 0;
        while (ballList.size() < ballCount) {
            float centerX = getRandomIntByRange((int) minX, (int) maxX);
            float centerY = getRandomIntByRange((int) minY, (int) maxY);
            if (!ballIntersectList(radius, centerX, centerY, ballList)) {
                Ball ball = new Ball(i == 0 ? radius : radius / 2, centerX, centerY, paint, i == 0 ? true : false, false, i);
                ballList.add(ball);
            }
            i++;
        }
        return ballList;
    }

    public void addBall() {
        float centerX = 0f;
        float centerY = 0f;
        try {
            int randomNumber = (int) (Math.random() * 4);
            int randomX = (int) (Math.random() * getWidth());
            int randomY = (int) (Math.random() * getHeight());
            switch (randomNumber) {
                case 0:
                    //从左边进入
                    centerX = -radius / 2;
                    centerY = randomY;
                    break;
                case 1:
                    //从上边进入
                    centerX = randomX;
                    centerY = -radius / 2;
                    break;
                case 2:
                    //从右边进入
                    centerX = getWidth() + radius / 2;
                    centerY = randomY;
                    break;
                case 3:
                    //从下边进入
                    centerX = randomX;
                    centerY = getHeight() + radius / 2;
                    break;
                default:
                    break;
            }
            Paint paint = createBallPaint();
            if (!ballIntersectList(radius, centerX, centerY, ballList)) {
                Ball ball = new Ball(ballList.size() == 0 ? radius : radius / 2, centerX, centerY, paint, false, true, ballList.size() + 1);
                ballList.add(ball);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        invalidate();
    }

    private Paint createBallPaint() {
        Paint paint = new Paint();
        paint.setColor(Color.parseColor("#55943a21"));
        return paint;
    }

    /**
     * 判断当前要生成的小球是否与其他球相交
     *
     * @param radius   要生成小球的半径
     * @param centerX  要生成小球圆心 x 坐标
     * @param centerY  要生成小球圆心 y 坐标
     * @param ballList 被比较小球集合
     * @return 有一个相交返回true
     */
    private boolean ballIntersectList(float radius, float centerX, float centerY, List<Ball> ballList) {
        for (Ball temp : ballList) {
            if (ballIntersect(radius, centerX, centerY, temp)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取指定范围内整数 rangeStart要小于rangeEnd
     *
     * @param rangeStart 整数开始值
     * @param rangeEnd   整数结束值
     */
    private int getRandomIntByRange(int rangeStart, int rangeEnd) throws Exception {
        if (rangeStart >= rangeEnd) {
            throw new Exception("rangeStart should less then rangeEnd");
        }
        Random random = new Random();
        return random.nextInt(rangeEnd) % (rangeEnd - rangeStart + 1) + rangeStart;
    }

    /**
     * 判断2个小球是否相交
     *
     * @param ball    第一个小球
     * @param radius  第二个小球的半径
     * @param centerX 第二个小球圆心 x 坐标
     * @param centerY 第二个小球圆心 y 坐标
     * @return 相交返回true
     */
    private boolean ballIntersect(float radius, float centerX, float centerY, Ball ball) {
        double dx = centerX - ball.centerX;
        double dy = centerY - ball.centerY;
        double dist = Math.sqrt(dx * dx + dy * dy);
        return ball.radius + radius > dist;
    }

    /**
     * 判断2个小球是否相交
     *
     * @param ball1 第一个小球
     * @param ball2 第二个小球
     * @return 相交返回true
     */
    private boolean ballIntersect(Ball ball1, Ball ball2) {
        double dx = ball2.centerX - ball1.centerX;
        double dy = ball2.centerY - ball1.centerY;
        double dist = Math.sqrt(dx * dx + dy * dy);
        return ball1.radius + ball2.radius > dist;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (ballList.size() == 0) {
            return;
//            try {
//                initBall(getWidth(), getHeight());
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
        }
        hitBall();
        for (Ball ball : ballList) {
            ball.calculateNextPositionOfBall();
            ball.calculateBallIsOutOfBounds(getWidth(), getHeight());
        }

        for (Ball ball : ballList) {
            ball.draw(canvas);
        }
        postInvalidate();
    }

    /**
     * 一个球与当前球碰撞
     */
    public void hitBall() {
        int num = ballList.size();
        for (int i = 0; i < num; i++) {
            Ball ball1 = ballList.get(i);
            for (int j = i + 1; j < num; j++) {
                Ball ball2 = ballList.get(j);
                double dx = ball2.centerX - ball1.centerX;
                double dy = ball2.centerY - ball1.centerY;
                double dist = Math.sqrt(dx * dx + dy * dy);
                double misDist = ball1.radius + ball2.radius;
                if (dist < misDist) {
                    double angle = Math.atan2(dy, dx);
                    double tx = ball1.centerX + Math.cos(angle) * misDist;
                    double ty = ball1.centerY + Math.sin(angle) * misDist;
                    double ax = (tx - ball2.centerX) * ball1.spring;
                    double ay = (ty - ball2.centerY) * ball1.spring;

                    ball1.vx -= ax;
                    ball1.vy -= ay;
                    ball2.vx += ax;
                    ball2.vy += ay;
                    ball1.isEnter = false;
                    ball2.isEnter = false;
                }
            }
        }
    }
}

package com.meng.ballcollision;


import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.View;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        @SuppressLint({"MissingInflatedId", "LocalSuppress"}) BallsCollisionView ballsView = findViewById(R.id.ballsView);

        ballsView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ballsView.addBall();
            }
        });
    }
}

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   >

    <com.meng.ballcollision.BallsCollisionView
        android:id="@+id/ballsView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>

大球在中间固定范围内动。其它小球随意移动

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值