jbox2d贴纸动画
来源:互联网 发布:网络贷款 编辑:程序博客网 时间:2024/04/28 13:07
使用jbox2d物理引擎打造摩拜单车贴纸动画效果
接下来我们在看下摩拜单车安卓客户端贴纸动画,是什么样子呢?先上一副动态图:
效果很炫,也很漂亮,至少看起来实现的难度要比ofo那个大的多。那么该怎么实现呢?(这里插下题外话,ios端比较容易实现,因为系统内置了某些api,可以很方便的实现这样的效果),可我们是安卓程序员啊,怎么办?自定义view?嗯,好像可以,但是这坐标怎么计算?并且还持续运动着,还有弹力,摩擦力,碰撞力…..
大脑一片茫然,如果产品经理坚决的确定需要这个效果,咋整?先不慌,我们可以先了解下摩拜单车是如何实现这样的效果的,很不幸,经过反编译摩拜单车apk后,安装包进行了加固,连混淆后的代码都看不到.
于是就上网各种搜索酷炫动画,不经意间发现了jbox2d物理引擎,然后回头在看下摩拜单车的apk,发现了其用到了libgdx-box2d,所以说摩拜是使用libgdx-box2d物理引擎来实现的这个效果:
jbox2d和libgdx-box2d:
jbox2d和libgdx-box2d有什么区别,他们之间的关系是什么?
jbox2d:
jbox2d百度百科这样描述的: jbox2D物理引擎原版 Box2D 是采用C++编写的,后来扩展到java,as等多种版本。著名手机游戏愤怒的小鸟便是采用jbox2D物理引擎。不过java版的jbox2D引擎性能不如C++环境下运行的性能好。在性能配置比较好的手机上面,jbox2D效果也是不错的。
libgdx:
libgdx百度百科这样描述的:libGdx是一个跨平台的2D/3D的游戏开发框架,它由Java/C/C++语言编写而成。
可能看完百度百科,还是比较模糊,我们只知道了他俩都是物理引擎,并且知道了愤怒的小鸟,就是用jbox2d引擎开发的。
后来经过搜索了解得出这样一句话:Libgdx使用jni封装了box2d的c++版本,使得其运行效率比其他同级的物理引擎如jbox2d快不少。所以最后的结论是jbox2d是面向java的,运行效率要慢。而libgdx是面向c/c++的,运行效率要快。
这次效果的实现是基于jbox2d上完成的,最终效果图如下:
APK 下载体验:
Github地址: https://github.com/andmizi/MobikeTags 欢迎star
jbox2d快速上手,推荐参考百度文库 https://wenku.baidu.com/view/c584cbfaf90f76c661371a5f.html
网上的文档都是比较过时的,新版中有些地方有变化,不过不影响快速入门。
jbox2d Github: https://github.com/jbox2d/jbox2d
简单描述下jbox2d物理引擎在安卓中的用法,jbox2d物理引擎并不负责view的绘制,只负物理数据的计算和分析,如物体的密度,质量,摩擦力,速度,碰撞力,恢复力……
通俗的讲,在安卓中一个view代表了一个物体,也就是刚体,通过view和刚体的绑定并设置初始参数,最后不停的draw,再从绑定的刚体中取出参数绘制到界面上,如此循环。而物体的所有物理参数都由jbox2d物理引擎帮助我们完成。
在MobikeLibrary中,自己只写了两个类,就实现了如上效果
Mobike和MobikeView是自己写的,org.jbox2d是官方的源代码,MobikeLibrary的具体使用请查看 https://github.com/andmizi/MobikeTags
MobikeView.java:
package com.mobike.library;import android.content.Context;import android.graphics.Canvas;import android.support.annotation.NonNull;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.widget.FrameLayout;/** * Created by kimi on 2017/7/8 0008. * Email: 24750@163.com */public class MobikeView extends FrameLayout { private Mobike mMobike; public MobikeView(@NonNull Context context) { this(context,null); } public MobikeView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); setWillNotDraw(false); mMobike = new Mobike(this); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mMobike.onSizeChanged(w,h); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); mMobike.onLayout(changed); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mMobike.onDraw(canvas); } public Mobike getmMobike(){ return this.mMobike; }}
MobikeView继承自Framelayout,比较简单,这里略过
Mobike.java
package com.mobike.library;import android.graphics.Canvas;import android.util.Log;import android.view.View;import android.view.ViewGroup;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;/** * Created by kimi on 2017/7/8 0008. * Email: 24750@163.com */public class Mobike { public static final String TAG = Mobike.class.getSimpleName(); private World world; private float dt = 1f / 60f; private int velocityIterations = 3; private int positionIterations = 10; private float friction = 0.3f,density = 0.5f,restitution = 0.3f,ratio = 50; private int width,height; private boolean enable = true; private final Random random = new Random(); private ViewGroup mViewgroup; public Mobike(ViewGroup viewgroup) { this.mViewgroup = viewgroup; density = viewgroup.getContext().getResources().getDisplayMetrics().density; } public void onSizeChanged(int width,int height){ this.width = width; this.height = height; //sizeChanged的时候获取到viewgroup的宽和高 } public void onDraw(Canvas canvas) { if(!enable){ //设置标记,在界面可见的时候开始draw,在界面不可见的时候停止draw return; } //dt 更新引擎的间隔时间 //velocityIterations 计算速度 //positionIterations 迭代的次数 world.step(dt,velocityIterations,positionIterations); int childCount = mViewgroup.getChildCount(); for(int i = 0; i < childCount; i++){ View view = mViewgroup.getChildAt(i); Body body = (Body) view.getTag(R.id.mobike_body_tag); if(body != null){ //从view中获取绑定的刚体,取出参数,开始更新view view.setX(metersToPixels(body.getPosition().x) - view.getWidth() / 2); view.setY(metersToPixels(body.getPosition().y) - view.getHeight() / 2); view.setRotation(radiansToDegrees(body.getAngle() % 360)); } } //手动调用,反复执行draw方法 mViewgroup.invalidate(); } public void onLayout(boolean changed) { createWorld(changed); } public void onStart(){ setEnable(true); } public void onStop(){ setEnable(false); } public void update(){ world = null; onLayout(true); } private void createWorld(boolean changed) { //jbox2d中world称为世界,这里创建一个世界 if(world == null){ world = new World(new Vec2(0, 10.0f)); //创建边界,注意边界为static静态的,当物体触碰到边界,停止模拟该物体 createTopAndBottomBounds(); createLeftAndRightBounds(); } int childCount = mViewgroup.getChildCount(); for(int i = 0; i < childCount; i++){ View view = mViewgroup.getChildAt(i); Body body = (Body) view.getTag(R.id.mobike_body_tag); if(body == null || changed){ createBody(world,view); } } } private void createBody(World world, View view) { //创建刚体描述,因为刚体需要随重力运动,这里type设置为DYNAMIC BodyDef bodyDef = new BodyDef(); bodyDef.setType(BodyType.DYNAMIC); //设置初始参数,为view的中心点 bodyDef.position.set(pixelsToMeters(view.getX() + view.getWidth() / 2) , pixelsToMeters(view.getY() + view.getHeight() / 2)); Shape shape = null; Boolean isCircle = (Boolean) view.getTag(R.id.mobike_view_circle_tag); if(isCircle != null && isCircle){ //创建圆体形状 shape = createCircleShape(view); }else{ //创建多边形形状 shape = createPolygonShape(view); } //初始化物体信息 //friction 物体摩擦力 //restitution 物体恢复系数 //density 物体密度 FixtureDef fixture = new FixtureDef(); fixture.setShape(shape); fixture.friction = friction; fixture.restitution = restitution; fixture.density = density; //用世界创建出刚体 Body body = world.createBody(bodyDef); body.createFixture(fixture); view.setTag(R.id.mobike_body_tag,body); //初始化物体的运动行为 body.setLinearVelocity(new Vec2(random.nextFloat(),random.nextFloat())); } private Shape createCircleShape(View view){ CircleShape circleShape = new CircleShape(); circleShape.setRadius(pixelsToMeters(view.getWidth() / 2)); return circleShape; } private Shape createPolygonShape(View view){ PolygonShape polygonShape = new PolygonShape(); polygonShape.setAsBox(pixelsToMeters(view.getWidth() / 2),pixelsToMeters(view.getHeight() / 2)); return polygonShape; } private void createTopAndBottomBounds() { BodyDef bodyDef = new BodyDef(); bodyDef.type = BodyType.STATIC; PolygonShape box = new PolygonShape(); float boxWidth = pixelsToMeters(width); float boxHeight = pixelsToMeters(ratio); box.setAsBox(boxWidth, boxHeight); FixtureDef fixtureDef = new FixtureDef(); fixtureDef.shape = box; fixtureDef.density = 0.5f; fixtureDef.friction = 0.3f; fixtureDef.restitution = 0.5f; bodyDef.position.set(0, -boxHeight); Body topBody = world.createBody(bodyDef); topBody.createFixture(fixtureDef); bodyDef.position.set(0, pixelsToMeters(height)+boxHeight); Body bottomBody = world.createBody(bodyDef); bottomBody.createFixture(fixtureDef); } private void createLeftAndRightBounds() { float boxWidth = pixelsToMeters(ratio); float boxHeight = pixelsToMeters(height); BodyDef bodyDef = new BodyDef(); bodyDef.type = BodyType.STATIC; PolygonShape box = new PolygonShape(); box.setAsBox(boxWidth, boxHeight); FixtureDef fixtureDef = new FixtureDef(); fixtureDef.shape = box; fixtureDef.density = 0.5f; fixtureDef.friction = 0.3f; fixtureDef.restitution = 0.5f; bodyDef.position.set(-boxWidth, boxHeight); Body leftBody = world.createBody(bodyDef); leftBody.createFixture(fixtureDef); bodyDef.position.set(pixelsToMeters(width) + boxWidth, 0); Body rightBody = world.createBody(bodyDef); rightBody.createFixture(fixtureDef); } private float radiansToDegrees(float radians) { return radians / 3.14f * 180f; } private float degreesToRadians(float degrees){ return (degrees / 180f) * 3.14f; } public float metersToPixels(float meters) { return meters * ratio; } public float pixelsToMeters(float pixels) { return pixels / ratio; } public void random() { //弹一下,模拟运动 int childCount = mViewgroup.getChildCount(); for (int i = 0; i < childCount; i++) { Vec2 impulse = new Vec2(random.nextInt(1000) - 1000, random.nextInt(1000) - 1000); View view = mViewgroup.getChildAt(i); Body body = (Body) view.getTag(R.id.mobike_body_tag); if(body != null){ body.applyLinearImpulse(impulse, body.getPosition(),true); } } } public void onSensorChanged(float x,float y) { //传感器模拟运动 int childCount = mViewgroup.getChildCount(); for (int i = 0; i < childCount; i++) { Vec2 impulse = new Vec2(x, y); View view = mViewgroup.getChildAt(i); Body body = (Body) view.getTag(R.id.mobike_body_tag); if(body != null){ body.applyLinearImpulse(impulse, body.getPosition(),true); } } } public float getFriction() { return friction; } public void setFriction(float friction) { if(friction >= 0){ this.friction = friction; } } public float getDensity() { return density; } public void setDensity(float density) { if(density >= 0){ this.density = density; } } public float getRestitution() { return restitution; } public void setRestitution(float restitution) { if(restitution >= 0){ this.restitution = restitution; } } public float getRatio() { return ratio; } public void setRatio(float ratio) { if(ratio >= 0){ this.ratio = ratio; } } public boolean getEnable() { return enable; } public void setEnable(boolean enable) { this.enable = enable; mViewgroup.invalidate(); }}
阅读顺序,onlayout—ondraw,如果你仔细阅读过上面的百度文库,应该都可以看的懂代码。
- jbox2d贴纸动画
- 使用jbox2d物理引擎打造摩拜单车贴纸动画效果
- Android 仿摩拜贴纸的动画
- jbox2d
- ImageProcessing贴纸效果
- 贴纸效果优惠券
- JBox2D学习 - 小球碰撞
- JBox2d入门学习一
- android加入jbox2d
- jbox2d开发流程
- jBox2D v2.0.1 用户手册
- JBox2D物理引擎
- android JBox2D 框架
- IE网页贴纸打印程序
- Vue 版sticker 贴纸实现
- android的jbox2d的使用!
- JBox2D For Android - hello box2d
- JBox2D学习 - 小球碰撞实例 .
- C++中的.操作符与->操作符
- 公众号第三方平台开发,获取授权公众号用户信息
- UGUI_Text的显示
- OSB操作数据库表
- js url上添加随机数防止缓存
- jbox2d贴纸动画
- Mysql 之DDL
- 223. Rectangle Area
- 自定view--小清新加载进度动画特效
- js中setTimeout和clearTimeout的使用
- Android多进程通信
- AtCoder ARC088C Multiple Gift
- Java8之Stream
- 使用 vue 构建 video 组件