效果如下:录制的时候有点卡,实际没有卡顿哈。(基于JBox2d 2.3开发的)

Android--碰撞效果--JBox2d实现


代码结构:

Android--碰撞效果--JBox2d实现


代码:


package com.bmob.im.demo.mobaianimation.widget;


import android.view.View;

import com.bmob.im.demo.mobaianimation.R;

import org.jbox2d.collision.shapes.CircleShape;
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.BodyType;
import org.jbox2d.dynamics.FixtureDef;
import org.jbox2d.dynamics.World;

import java.util.Random;

/**jbox2d 2.3.1
 * Created by Administrator--沈斌 on 2017/10/23.
 */
public class JboxImp {
    private static final String TAG = JboxImp.class.getSimpleName();
    private World mWorld;
    private float dt = 1f/ 30f;  //运动的频率
    private int mVelocityIterations = 5;  //每一帧迭代次数
    private int mPosiontionIterations = 20;  //每一帧计算的位置点

    private  int mWidth,mHeight;   //刚体的边界,宽高
    private float mDesity = 0.5f;  //刚体密度
    private float mRatio = 20;   //坐标比率,模拟和真实的映射比率,碰撞的速度以及幅度

    private final Random mRandom = new Random(); //随机数

    /**
     * 设置密度
     * @param desity
     */
    public JboxImp(float desity){
        this.mDesity = desity;
    }

    /**
     * 设置边界(弹力墙的边界)
     * @param width
     * @param height
     */
    public void setWorldSize(int width,int height){
        this.mWidth = width;
        this.mHeight = height;
    }

    public void createWorld(){
        if (mWorld == null){
            mWorld = new World(new Vec2(0,10.0f));

            updateVerticalBounds();
            updateHorzontalBounds();
        }
    }

    /**
     * 竖直方向的弹力墙(上和下)
     */
    private void updateVerticalBounds(){

        //创建静态刚体
        BodyDef bodyDef = new BodyDef();
        bodyDef.type = BodyType.STATIC;  //静态

        PolygonShape box = new PolygonShape();
        float boxWidth = switchPositionToBody(mWidth);
        float boxHeight = 1;  //因为垂直,所以高度为1
        box.setAsBox(boxWidth,boxHeight);

        FixtureDef fixtureDef = new FixtureDef();
        fixtureDef.shape = box;
        fixtureDef.density = mDesity;
        fixtureDef.friction = 0.8f;  //摩擦系数
        fixtureDef.restitution = 0.5f;  //补偿系数

        //底部body
        bodyDef.position.set(0,-boxHeight);  //底部弹力墙
        Body bottomBody = mWorld.createBody(bodyDef);
        bottomBody.createFixture(fixtureDef);

        //顶部body
        bodyDef.position.set(0,switchPositionToBody(mHeight)+boxHeight);  //顶部弹力墙
        Body topBody = mWorld.createBody(bodyDef);
        topBody.createFixture(fixtureDef);


    }

    /**
     * 水平方向的弹力墙(左边和右边)
     */
    private void updateHorzontalBounds(){

        //创建静态刚体
        BodyDef bodyDef = new BodyDef();
        bodyDef.type = BodyType.STATIC;  //静态

        PolygonShape box = new PolygonShape();
        float boxWidth = 1;
        float boxHeight = switchPositionToBody(mHeight);  //因为垂直,所以高度为1
        box.setAsBox(boxWidth,boxHeight);


        FixtureDef fixtureDef = new FixtureDef();
        fixtureDef.shape = box;
        fixtureDef.density = mDesity;
        fixtureDef.friction = 0.8f;  //摩擦系数
        fixtureDef.restitution = 0.5f;  //补偿系数

        //左边body
        bodyDef.position.set(-boxWidth,0);  //左边弹力墙
        Body leftBody = mWorld.createBody(bodyDef);
        leftBody.createFixture(fixtureDef);

        //右边body
        bodyDef.position.set(switchPositionToBody(mWidth)+boxWidth,0);  //右边弹力墙
        Body rightBody = mWorld.createBody(bodyDef);
        rightBody.createFixture(fixtureDef);


    }

    /**
     * view坐标映射为物理的坐标
     * @param viewPosition
     * @return
     */
    public float switchPositionToBody(float viewPosition){
        return viewPosition / mRatio;
    }

    /**
     * 物理坐标映射为view的坐标
     * @param bodyPosition
     * @return
     */
    public float switchPositionToView(float bodyPosition){
        return bodyPosition * mRatio;
    }



    public float radiansToDegrees(float radians) {
        return radians / 3.14f * 180f;
    }

    public boolean isBodyView(View view){
        Body body = (Body) view.getTag(R.id.shenbin_body_view_tag);
        return body != null;
    }

    /**
     * 创建模拟事件的刚体(运动的小圆球)
     * @param view
     */
    public void createBody(View view){
        //创建动态刚体
        BodyDef bodyDef = new BodyDef();
        bodyDef.type = BodyType.DYNAMIC;  //动态

        Shape shape = null;
        boolean isCircle = (boolean) view.getTag(R.id.shenbin_circle_view_tag);
        bodyDef.position.set(switchPositionToBody(view.getX() + view.getWidth() / 2), switchPositionToBody(view.getY() + view.getHeight() / 2));

        if(isCircle){
            shape = createCircleShape(switchPositionToBody(view.getWidth()/2));
        }

        FixtureDef fixtureDef = new FixtureDef();
        fixtureDef.shape = shape;
        fixtureDef.density = mDesity;
        fixtureDef.friction = 0.8f;  //摩擦系数
        fixtureDef.restitution = 0.5f;  //补偿系数

        Body body = mWorld.createBody(bodyDef);
        body.createFixture(fixtureDef);

        view.setTag(R.id.shenbin_body_view_tag,body);

        //线性加速约束
        body.setLinearVelocity(new Vec2(mRandom.nextFloat(),mRandom.nextFloat()));

    }

    /**
     * 得到圆形刚体
     * @param radius
     * @return
     */
    private Shape createCircleShape(float radius){
        CircleShape circleShape = new CircleShape();
        circleShape.setRadius(radius);
        return circleShape;
    }

    public void startWorld(){
        if(mWorld != null){
            mWorld.step(dt,mVelocityIterations,mPosiontionIterations);
        }
    }

    /**
     * 通过调用这个方法,使刚体运动起来
     * @param x
     * @param y
     * @param view
     */
    public void applyLinearImpulse(float x ,float y,View view){

        Body body = (Body) view.getTag(R.id.shenbin_body_view_tag);

        Vec2 impluse = new Vec2(x,y);   //这两个参数,可以控制刚体碰撞的范围

        body.applyLinearImpulse(impluse,body.getPosition(),true);


    }
}



package com.bmob.im.demo.mobaianimation.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;

import com.bmob.im.demo.mobaianimation.R;

import org.jbox2d.dynamics.Body;

/**
 * Created by Administrator--沈斌 on 2017/10/23.
 */
public class CollisionView extends FrameLayout {

    private JboxImp jboxImp;

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

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

    public CollisionView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setWillNotDraw(false);
        initView();

    }

    private void initView(){
        jboxImp = new JboxImp(getResources().getDisplayMetrics().density);
    }

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

        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View view = getChildAt(i);
            Body body = (Body) view.getTag(R.id.shenbin_body_view_tag);
            if(body == null || changed){
                jboxImp.createBody(view);
            }

        }


    }

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

        jboxImp.startWorld();//也可以放到onLayout里面

        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View view = getChildAt(i);

            Body body = (Body) view.getTag(R.id.shenbin_body_view_tag);

            if(view != null && jboxImp.isBodyView(view)){
                view.setX(jboxImp.switchPositionToView(body.getPosition().x) - view.getWidth() / 2);
                view.setY(jboxImp.switchPositionToView(body.getPosition().y) - view.getHeight() / 2);
                view.setRotation(jboxImp.radiansToDegrees(body.getAngle() % 360));
            }

        }

        invalidate();

    }


    public void onSensorChanged(float x,float y){
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View view = getChildAt(i);
            if(view != null && jboxImp.isBodyView(view)){
                jboxImp.applyLinearImpulse(x,y,view);
            }
        }
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        jboxImp.setWorldSize(w,h);
    }
}



package com.bmob.im.demo.mobaianimation;

import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.Gravity;
import android.widget.FrameLayout;
import android.widget.ImageView;

import com.bmob.im.demo.mobaianimation.widget.CollisionView;

import java.util.Random;

public class MainActivity extends Activity implements SensorEventListener{

    CollisionView myView;
    private SensorManager sensorManager;

    private  int[] imgs = {
            R.drawable.share_fb,
            R.drawable.share_kongjian,
            R.drawable.share_pyq,
            R.drawable.share_qq,
            R.drawable.share_tw,
            R.drawable.share_wechat,
            R.drawable.share_weibo,
            R.mipmap.ic_launcher
    };
    private Sensor defaultSensor;

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

    }

    @Override
    public void onContentChanged() {
        super.onContentChanged();

        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        defaultSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

        myView = (CollisionView) findViewById(R.id.myView);

        initViews();

    }

    @Override
    protected void onResume() {
        super.onResume();
        sensorManager.registerListener( this,defaultSensor,SensorManager.SENSOR_DELAY_UI);

    }

    @Override
    protected void onPause() {
        super.onPause();
        sensorManager.unregisterListener(this);
    }

    private void initViews() {
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
        layoutParams.gravity = Gravity.CENTER;
        for(int i = 0; i < imgs.length  ; i ++){
            ImageView imageView = new ImageView(this);
            imageView.setImageResource(imgs[i]);

            imageView.setTag(R.id.shenbin_circle_view_tag,true);

            myView.addView(imageView,layoutParams);
        }
    }


    private final Random mRandom = new Random();

    /**
     * 使用传感器,因为这个会一直回调,一直运行,所以可以使刚体不停的碰撞。
     * @param sensorEvent
     */
    @Override
    public void onSensorChanged(SensorEvent sensorEvent) {
        if(sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER){
            float x = sensorEvent.values[0];
            float y = sensorEvent.values[1] * 2.0f;

            myView.onSensorChanged(mRandom.nextInt(200) - 100, mRandom.nextInt(1000) - 500);  //这两个参数传递下去,可以控制刚体碰撞的范围
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int i) {

    }
}


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorAccent"
    tools:context="com.bmob.im.demo.mobaianimation.MainActivity">

    <com.bmob.im.demo.mobaianimation.widget.CollisionView
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:id="@+id/myView"
        android:background="#fff"/>
</RelativeLayout>



相关文章:

  • 2021-09-10
  • 2021-06-28
  • 2022-12-23
  • 2022-12-23
  • 2021-08-31
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-12-14
  • 2021-07-05
  • 2022-12-23
  • 2021-05-22
相关资源
相似解决方案