Related
I see scale with only 0 written! As in the picture and two strange zeros out of the schema: (GALAXY NEXUS 4.2.1)
As in the example at http://mindtherobot.com/blog/272/android-custom-ui-making-a-vintage-thermometer/comment-page-1/#comment-106122%29 this is my custom view:
package com.wikibuyers.barometro;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LightingColorFilter;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RadialGradient;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
public class Barometro extends View implements SensorEventListener{
private static final String TAG = Barometro.class.getSimpleName();
private Handler handler;
// drawing tools
private RectF rimRect;
private Paint rimPaint;
private Paint rimCirclePaint;
private RectF faceRect;
private Bitmap faceTexture;
private Paint facePaint;
private Paint rimShadowPaint;
private Paint scalePaint;
private RectF scaleRect;
private Paint titlePaint;
private Path titlePath;
private Paint logoPaint;
private Bitmap logo;
private Matrix logoMatrix;
private float logoScale;
private Paint handPaint;
private Path handPath;
private Paint handScrewPaint;
private Paint backgroundPaint;
// end drawing tools
private Bitmap background; // holds the cached static part
// scale configuration
private static final int totalNicks = 100;
private static final float degreesPerNick = 360.0f / totalNicks;
private static final int centerDegree = 40; // the one in the top center (12 o'clock)
private static final int minDegrees = -30;
private static final int maxDegrees = 110;
// hand dynamics -- all are angular expressed in F degrees
private boolean handInitialized = false;
private float handPosition = centerDegree;
private float handTarget = centerDegree;
private float handVelocity = 0.0f;
private float handAcceleration = 0.0f;
private long lastHandMoveTime = -1L;
public Barometro(Context context) {
super(context);
init();
}
public Barometro(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public Barometro(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
attachToSensor();
}
#Override
protected void onDetachedFromWindow() {
detachFromSensor();
super.onDetachedFromWindow();
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
Bundle bundle = (Bundle) state;
Parcelable superState = bundle.getParcelable("superState");
super.onRestoreInstanceState(superState);
handInitialized = bundle.getBoolean("handInitialized");
handPosition = bundle.getFloat("handPosition");
handTarget = bundle.getFloat("handTarget");
handVelocity = bundle.getFloat("handVelocity");
handAcceleration = bundle.getFloat("handAcceleration");
lastHandMoveTime = bundle.getLong("lastHandMoveTime");
}
#Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
Bundle state = new Bundle();
state.putParcelable("superState", superState);
state.putBoolean("handInitialized", handInitialized);
state.putFloat("handPosition", handPosition);
state.putFloat("handTarget", handTarget);
state.putFloat("handVelocity", handVelocity);
state.putFloat("handAcceleration", handAcceleration);
state.putLong("lastHandMoveTime", lastHandMoveTime);
return state;
}
#SuppressLint({ "NewApi", "InlinedApi" })
private void init() {
handler = new Handler();
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
initDrawingTools();
}
private String getTitle() {
return "wikibuyers.com";
}
private SensorManager getSensorManager() {
return (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
}
private void attachToSensor() {
SensorManager sensorManager = getSensorManager();
List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_PRESSURE);
if (sensors.size() > 0) {
Sensor sensor = sensors.get(0);
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL, handler);
} else {
Log.e(TAG, "No pressure sensor found");
}
}
private void detachFromSensor() {
SensorManager sensorManager = getSensorManager();
sensorManager.unregisterListener(this);
}
private void initDrawingTools() {
rimRect = new RectF(0.1f, 0.1f, 0.9f, 0.9f);
// the linear gradient is a bit skewed for realism
rimPaint = new Paint();
rimPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
rimPaint.setShader(new LinearGradient(0.40f, 0.0f, 0.60f, 1.0f,
Color.rgb(0xf0, 0xf5, 0xf0),
Color.rgb(0x30, 0x31, 0x30),
Shader.TileMode.CLAMP));
rimCirclePaint = new Paint();
rimCirclePaint.setAntiAlias(true);
rimCirclePaint.setStyle(Paint.Style.STROKE);
rimCirclePaint.setColor(Color.argb(0x4f, 0x33, 0x36, 0x33));
rimCirclePaint.setStrokeWidth(0.005f);
float rimSize = 0.02f;
faceRect = new RectF();
faceRect.set(rimRect.left + rimSize, rimRect.top + rimSize,
rimRect.right - rimSize, rimRect.bottom - rimSize);
faceTexture = BitmapFactory.decodeResource(getContext().getResources(),
R.drawable.plastic);
BitmapShader paperShader = new BitmapShader(faceTexture,
Shader.TileMode.MIRROR,
Shader.TileMode.MIRROR);
Matrix paperMatrix = new Matrix();
facePaint = new Paint();
facePaint.setFilterBitmap(true);
paperMatrix.setScale(1.0f / faceTexture.getWidth(),
1.0f / faceTexture.getHeight());
paperShader.setLocalMatrix(paperMatrix);
facePaint.setStyle(Paint.Style.FILL);
facePaint.setShader(paperShader);
rimShadowPaint = new Paint();
rimShadowPaint.setShader(new RadialGradient(0.5f, 0.5f, faceRect.width() / 2.0f,
new int[] { 0x00000000, 0x00000500, 0x50000500 },
new float[] { 0.96f, 0.96f, 0.99f },
Shader.TileMode.MIRROR));
rimShadowPaint.setStyle(Paint.Style.FILL);
scalePaint = new Paint();
scalePaint.setStyle(Paint.Style.STROKE);
scalePaint.setColor(0x9f004d0f);
scalePaint.setStrokeWidth(0.005f);
scalePaint.setAntiAlias(true);
scalePaint.setTextSize(0.045f);
scalePaint.setTypeface(Typeface.SANS_SERIF);
scalePaint.setTextScaleX(0.8f);
scalePaint.setTextAlign(Paint.Align.CENTER);
float scalePosition = 0.10f;
scaleRect = new RectF();
scaleRect.set(faceRect.left + scalePosition, faceRect.top + scalePosition,
faceRect.right - scalePosition, faceRect.bottom - scalePosition);
titlePaint = new Paint();
titlePaint.setColor(0xaf946109);
titlePaint.setAntiAlias(true);
titlePaint.setTypeface(Typeface.DEFAULT_BOLD);
titlePaint.setTextAlign(Paint.Align.CENTER);
titlePaint.setTextSize(0.05f);
titlePaint.setTextScaleX(0.8f);
titlePath = new Path();
titlePath.addArc(new RectF(0.24f, 0.24f, 0.76f, 0.76f), -180.0f, -180.0f);
logoPaint = new Paint();
logoPaint.setFilterBitmap(true);
logo = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.logo);
logoMatrix = new Matrix();
logoScale = (1.0f / logo.getWidth()) * 0.3f;;
logoMatrix.setScale(logoScale, logoScale);
handPaint = new Paint();
handPaint.setAntiAlias(true);
handPaint.setColor(0xff392f2c);
handPaint.setShadowLayer(0.01f, -0.005f, -0.005f, 0x7f000000);
handPaint.setStyle(Paint.Style.FILL);
handPath = new Path();
handPath.moveTo(0.5f, 0.5f + 0.2f);
handPath.lineTo(0.5f - 0.010f, 0.5f + 0.2f - 0.007f);
handPath.lineTo(0.5f - 0.002f, 0.5f - 0.32f);
handPath.lineTo(0.5f + 0.002f, 0.5f - 0.32f);
handPath.lineTo(0.5f + 0.010f, 0.5f + 0.2f - 0.007f);
handPath.lineTo(0.5f, 0.5f + 0.2f);
handPath.addCircle(0.5f, 0.5f, 0.025f, Path.Direction.CW);
handScrewPaint = new Paint();
handScrewPaint.setAntiAlias(true);
handScrewPaint.setColor(0xff493f3c);
handScrewPaint.setStyle(Paint.Style.FILL);
backgroundPaint = new Paint();
backgroundPaint.setFilterBitmap(true);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d(TAG, "Width spec: " + MeasureSpec.toString(widthMeasureSpec));
Log.d(TAG, "Height spec: " + MeasureSpec.toString(heightMeasureSpec));
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int chosenWidth = chooseDimension(widthMode, widthSize);
int chosenHeight = chooseDimension(heightMode, heightSize);
int chosenDimension = Math.min(chosenWidth, chosenHeight);
setMeasuredDimension(chosenDimension, chosenDimension);
}
private int chooseDimension(int mode, int size) {
if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) {
return size;
} else { // (mode == MeasureSpec.UNSPECIFIED)
return getPreferredSize();
}
}
// in case there is no size specified
private int getPreferredSize() {
return 300;
}
private void drawRim(Canvas canvas) {
// first, draw the metallic body
canvas.drawOval(rimRect, rimPaint);
// now the outer rim circle
canvas.drawOval(rimRect, rimCirclePaint);
}
private void drawFace(Canvas canvas) {
canvas.drawOval(faceRect, facePaint);
// draw the inner rim circle
canvas.drawOval(faceRect, rimCirclePaint);
// draw the rim shadow inside the face
canvas.drawOval(faceRect, rimShadowPaint);
}
private void drawScale(Canvas canvas) {
canvas.drawOval(scaleRect, scalePaint);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
for (int i = 0; i < totalNicks; ++i) {
float y1 = scaleRect.top;
float y2 = y1 - 0.020f;
canvas.drawLine(0.5f, y1, 0.5f, y2, scalePaint);
if ((i % 5) == 0) {
int value = nickToDegree(i);
if ((value >= minDegrees) && (value <= maxDegrees)) {
String valueString = Integer.toString(value);
canvas.drawText(valueString, 0.5f, y2 - 0.015f, scalePaint);
}
}
canvas.rotate(degreesPerNick, 0.5f, 0.5f);
}
canvas.restore();
}
private int nickToDegree(int nick) {
int rawDegree = ((nick < totalNicks / 2) ? nick : (nick - totalNicks)) * 2;
int shiftedDegree = rawDegree + centerDegree;
return shiftedDegree;
}
private float degreeToAngle(float degree) {
return (degree - centerDegree) / 2.0f * degreesPerNick;
}
private void drawTitle(Canvas canvas) {
String title = getTitle();
canvas.drawTextOnPath(title, titlePath, 0.0f,0.0f, titlePaint);
}
private void drawLogo(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(0.5f - logo.getWidth() * logoScale / 2.0f,
0.5f - logo.getHeight() * logoScale / 2.0f);
int color = 0x00000000;
float position = getRelativePressurePosition();
if (position < 0) {
color |= (int) ((0xf0) * -position); // blue
} else {
color |= ((int) ((0xf0) * position)) << 16; // red
}
//Log.d(TAG, "*** " + Integer.toHexString(color));
LightingColorFilter logoFilter = new LightingColorFilter(0xff338822, color);
logoPaint.setColorFilter(logoFilter);
canvas.drawBitmap(logo, logoMatrix, logoPaint);
canvas.restore();
}
private void drawHand(Canvas canvas) {
if (handInitialized) {
float handAngle = degreeToAngle(handPosition);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.rotate(handAngle, 0.5f, 0.5f);
canvas.drawPath(handPath, handPaint);
canvas.restore();
canvas.drawCircle(0.5f, 0.5f, 0.01f, handScrewPaint);
}
}
private void drawBackground(Canvas canvas) {
if (background == null) {
Log.w(TAG, "Background not created");
} else {
canvas.drawBitmap(background, 0, 0, backgroundPaint);
}
}
#Override
protected void onDraw(Canvas canvas) {
drawBackground(canvas);
float scale = (float) getWidth();
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(scale, scale);
drawLogo(canvas);
drawHand(canvas);
canvas.restore();
if (handNeedsToMove()) {
moveHand();
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
Log.d(TAG, "Size changed to " + w + "x" + h);
regenerateBackground();
}
private void regenerateBackground() {
// free the old bitmap
if (background != null) {
background.recycle();
}
background = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas backgroundCanvas = new Canvas(background);
float scale = (float) getWidth();
backgroundCanvas.scale(scale, scale);
drawRim(backgroundCanvas);
drawFace(backgroundCanvas);
drawScale(backgroundCanvas);
drawTitle(backgroundCanvas);
}
private boolean handNeedsToMove() {
return Math.abs(handPosition - handTarget) > 0.001f;
}
private void moveHand() {
if (! handNeedsToMove()) {
return;
}
if (lastHandMoveTime != -1L) {
long currentTime = System.currentTimeMillis();
float delta = (currentTime - lastHandMoveTime) / 1000.0f;
float direction = Math.signum(handVelocity);
if (Math.abs(handVelocity) < 90.0f) {
handAcceleration = 5.0f * (handTarget - handPosition);
} else {
handAcceleration = 0.0f;
}
handPosition += handVelocity * delta;
handVelocity += handAcceleration * delta;
if ((handTarget - handPosition) * direction < 0.01f * direction) {
handPosition = handTarget;
handVelocity = 0.0f;
handAcceleration = 0.0f;
lastHandMoveTime = -1L;
} else {
lastHandMoveTime = System.currentTimeMillis();
}
invalidate();
} else {
lastHandMoveTime = System.currentTimeMillis();
moveHand();
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
if (sensorEvent.values.length > 0) {
float pressureAtm = sensorEvent.values[0]/20;
//Log.i(TAG, "*** Temperature: " + temperatureC);
float pressureMmg = (9.0f / 5.0f) * pressureAtm + 32.0f;
setHandTarget(pressureAtm);
} else {
Log.w(TAG, "Empty sensor event received");
}
}
private float getRelativePressurePosition() {
if (handPosition < centerDegree) {
return - (centerDegree - handPosition) / (float) (centerDegree - minDegrees);
} else {
return (handPosition - centerDegree) / (float) (maxDegrees - centerDegree);
}
}
private void setHandTarget(float pressure) {
if (pressure < minDegrees) {
pressure = minDegrees;
} else if (pressure > maxDegrees) {
pressure = maxDegrees;
}
handTarget = pressure;
handInitialized = true;
invalidate();
}
}
This is focus on drawScale() method:
private void drawScale(Canvas canvas) {
canvas.drawOval(scaleRect, scalePaint);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
for (int i = 0; i < totalNicks; ++i) {
float y1 = scaleRect.top;
float y2 = y1 - 0.020f;
canvas.drawLine(0.5f, y1, 0.5f, y2, scalePaint);
if ((i % 5) == 0) {
int value = nickToDegree(i);
if ((value >= minDegrees) && (value <= maxDegrees)) {
String valueString = Integer.toString(value);
canvas.drawText(valueString, 0.5f, y2 - 0.015f, scalePaint);
}
}
canvas.rotate(degreesPerNick, 0.5f, 0.5f);
}
canvas.restore();
}
I have been on this for days without finding a good solution (at least to me).
Basically what I need to do is draw an animated dotted curve, that would represent a missile trajectory for a basic game. This trajectory would accept starting, middle and final x,y. I read a lot of Q/A about animating curves or even straight lines just to begin, but they look really complex compared to what I have to do.
I have a button with an onclick listener which triggers the missile launch, but until now it just draws static lines without any animation.
Do you remember this? That's what i would like to achieve:
http://www.youtube.com/watch?v=UDc3ZEKl-Wc
Banana trajectory is not painted but, you got the idea.
Here again, starting from your example i tried to draw simultaneous curves using arrays and for loops, but what i got is that sometimes, not always, app crashes with a nullpointer exception, don't know why. What am i doing wrong? Here's my code:
public class DrawDottedCurve extends View {
Path[] path = new Path[8];
Path[] drawingPath = new Path[8];
PathMeasure[] measure = new PathMeasure[8];
Path[] segmentPath = new Path[8];
float[] length = new float[8];
float[] start = new float[8];
float[] percent = new float[8];
Paint paint = new Paint();
float x1;
float y1;
float x3;
float y3;
long k = 0;
Canvas canvas;
Random r = new Random();
public DrawDottedCurve(Context context, int a, int b, int c, int d) {
super(context);
x1 = a;
y1 = b;
x3 = c;
y3 = d;
paint.setAlpha(255);
paint.setStrokeWidth(2);
paint.setColor(Color.RED);
paint.setStyle(Style.STROKE);
paint.setPathEffect(new DashPathEffect(new float[] { 2, 4 }, 50));
for (int i = 0; i < 8; i++) {
k = r.nextInt(100 - 30) + 30;
path[i] = new Path();
path[i].moveTo(x1 + k, y1 + k); //
path[i].quadTo((x3 + k - x1 + k) / 2, (y3 + k - y1 + k) / 2,
x3 + k, y3 + k); // Calculate Bezier Curves
}
final long DRAW_TIME = 10000;
CountDownTimer timer = new CountDownTimer(DRAW_TIME, 100) {
#Override
public void onTick(long millisUntilFinished) {
Log.d("Timer", "Inizio");
for (int i = 0; i < 8; i++) {
start[i] = 0;
measure[i] = new PathMeasure();
measure[i].setPath(path[i], false);
percent[i] = ((float) (DRAW_TIME - millisUntilFinished))
/ (float) DRAW_TIME;
segmentPath[i] = new Path();
drawingPath[i] = new Path();
length[i] = measure[i].getLength() * percent[i];
measure[i].getSegment(start[i], length[i], segmentPath[i],
true);
start[i] = length[i];
drawingPath[i].addPath(segmentPath[i]);
}
invalidate();
;
}
#Override
public void onFinish() {
for (int i = 0; i < 8; i++) {
measure[i].getSegment(start[i], measure[i].getLength(),
segmentPath[i], true);
drawingPath[i].addPath(segmentPath[i]);
}
invalidate();
Log.d("Timer", "Fine");
}
};
timer.start();
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < 8; i++) {
canvas.drawPath(drawingPath[i], paint);
invalidate();
}
}
}
Assuming that you have a Canvas to draw on and a path you want to trace this code should do the work for animating it.
private void init() {
missilePath = new Path();
missileDrawPath = new Path();
mPaint = new Paint();
mPaint.setStrokeWidth(3);
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Style.STROKE);
mPaint.setAntiAlias(true);
PathEffect dashEffect = new DashPathEffect(new float[] {2, 4}, 0);
mPaint.setPathEffect(dashEffect);
final long DRAW_TIME = 5000;
timer = new CountDownTimer(DRAW_TIME, 100) {
PathMeasure measure = new PathMeasure();
Path segmentPath = new Path();
float start = 0;
#Override
public void onTick(long millisUntilFinished) {
measure.setPath(missilePath, false);
float percent = ((float) (DRAW_TIME - millisUntilFinished)) / (float) DRAW_TIME;
float length = measure.getLength() * percent;
measure.getSegment(start, length, segmentPath, true);
start = length;
missileDrawPath.addPath(segmentPath);
invalidate();
}
#Override
public void onFinish() {
measure.getSegment(start, measure.getLength(), segmentPath, true);
missileDrawPath.addPath(segmentPath);
invalidate();
}
};
timer.start();
}
#Override
public void onDraw(Canvas c) {
super.onDraw(c);
c.drawPath(missileDrawPath, mPaint);
}
Basically a timer gets started and depending on the elapsed time a segment is retrieved from missilePath and added to the path that should be currently drawn.
Thank you SceLus that worked really well, here's the full code for people eventually stuck on same problem.
Class constructor takes Bezier Curve starting and final x's and y's as input and simply calculate mid control point, then draws everything according to SceLus code.
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.os.CountDownTimer;
import android.view.View;
public class DrawDottedCurve extends View {
Path path = new Path();
Path drawingPath = new Path();
Paint paint = new Paint();
float x1;
float y1;
float x3;
float y3;
public DrawDottedCurve(Context context, int a, int b, int c, int d) {
super(context);
x1 = a;
y1 = b;
x3 = c;
y3 = d;
paint.setAlpha(255);
paint.setStrokeWidth(2);
paint.setColor(Color.RED);
paint.setStyle(Style.STROKE);
paint.setPathEffect(new DashPathEffect(new float[] { 2, 4 }, 50));
path.moveTo(x1, y1); //
path.quadTo((x3 - x1) / 2, (y3 - y1) / 2, x3, y3); // Calculate Bezier Curve
final long DRAW_TIME = 10000;
CountDownTimer timer = new CountDownTimer(DRAW_TIME, 100) {
PathMeasure measure = new PathMeasure();
Path segmentPath = new Path();
float start = 0;
#Override
public void onTick(long millisUntilFinished) {
measure.setPath(path, false);
float percent = ((float) (DRAW_TIME - millisUntilFinished))
/ (float) DRAW_TIME;
float length = measure.getLength() * percent;
measure.getSegment(start, length, segmentPath, true);
start = length;
drawingPath.addPath(segmentPath);
invalidate();
}
#Override
public void onFinish() {
measure.getSegment(start, measure.getLength(), segmentPath,
true);
drawingPath.addPath(segmentPath);
invalidate();
}
};
timer.start();
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(drawingPath, paint);
}
}
I am setting a linear impulse on an object, it moves fine but when I press power button and then again come to game I noticed that my object rotation speed is lessened.
package com.algo;
import org.anddev.andengine.engine.Engine;
import org.anddev.andengine.engine.camera.Camera;
import org.anddev.andengine.engine.options.EngineOptions;
import org.anddev.andengine.engine.options.EngineOptions.ScreenOrientation;
import org.anddev.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
import org.anddev.andengine.entity.primitive.Rectangle;
import org.anddev.andengine.entity.scene.Scene;
import org.anddev.andengine.entity.scene.Scene.IOnSceneTouchListener;
import org.anddev.andengine.entity.scene.background.ColorBackground;
import org.anddev.andengine.entity.shape.Shape;
import org.anddev.andengine.entity.sprite.AnimatedSprite;
import org.anddev.andengine.extension.physics.box2d.PhysicsConnector;
import org.anddev.andengine.extension.physics.box2d.PhysicsFactory;
import org.anddev.andengine.extension.physics.box2d.PhysicsWorld;
import org.anddev.andengine.extension.physics.box2d.util.constants.PhysicsConstants;
import org.anddev.andengine.input.touch.TouchEvent;
import org.anddev.andengine.opengl.texture.TextureOptions;
import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory;
import org.anddev.andengine.opengl.texture.region.TiledTextureRegion;
import org.anddev.andengine.sensor.accelerometer.AccelerometerData;
import org.anddev.andengine.sensor.accelerometer.IAccelerometerListener;
import org.anddev.andengine.ui.activity.BaseGameActivity;
import org.anddev.andengine.util.Debug;
import org.anddev.andengine.util.MathUtils;
import org.anddev.andengine.util.constants.TimeConstants;
import android.hardware.SensorManager;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
public class PhysicsTest1 extends BaseGameActivity implements IAccelerometerListener, IOnSceneTouchListener{
private final float CAMERA_WIDTH = 800;
private final float CAMERA_HEIGHT = 480;
private final float MAX_DISTANCE_FLING = 80f;
private final float MAX_VELOCITY_CONST = 250f;
private final float DEFAULT_VELOCITY = 50f;
private BitmapTextureAtlas mBitmapTextureAtlas;
private Scene mScene;
protected TiledTextureRegion mBoxFaceTextureRegion;
protected TiledTextureRegion mCircleFaceTextureRegion;
protected PhysicsWorld mPhysicsWorld;
private int mFaceCount = 0;
private float X = 0;
private float Y = 0;
private Body body = null;
#Override
public void onLoadComplete() {
}
#Override
public Engine onLoadEngine() {
final Camera camera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
final EngineOptions engineOptions = new EngineOptions(true, ScreenOrientation.LANDSCAPE, new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), camera);
engineOptions.getTouchOptions().setRunOnUpdateThread(true);
return new Engine(engineOptions);
}
#Override
public void onLoadResources() {
this.mBitmapTextureAtlas = new BitmapTextureAtlas(64, 64, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
this.mBoxFaceTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mBitmapTextureAtlas, this, "face_box_tiled.png", 0, 0, 2, 1); // 64x32
this.mCircleFaceTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mBitmapTextureAtlas, this, "face_circle_tiled.png", 0, 32, 2, 1); // 64x32
this.mEngine.getTextureManager().loadTexture(this.mBitmapTextureAtlas);
}
#Override
public Scene onLoadScene() {
this.mScene = new Scene();
this.mScene.setBackground(new ColorBackground(0, 0, 0));
this.mScene.setOnSceneTouchListener(this);
this.mScene.setOnSceneTouchListenerBindingEnabled(true);
this.mPhysicsWorld = new PhysicsWorld(new Vector2(0, SensorManager.GRAVITY_EARTH), false);
final Shape ground = new Rectangle(0, CAMERA_HEIGHT - 2, CAMERA_WIDTH, 2);
final Shape roof = new Rectangle(0, 0, CAMERA_WIDTH, 2);
final Shape left = new Rectangle(0, 0, 2, CAMERA_HEIGHT);
final Shape right = new Rectangle(CAMERA_WIDTH - 2, 0, 2, CAMERA_HEIGHT);
final FixtureDef wallFixtureDef = PhysicsFactory.createFixtureDef(0, 0.5f, 0.3f);
PhysicsFactory.createBoxBody(this.mPhysicsWorld, ground, BodyType.StaticBody, wallFixtureDef);
PhysicsFactory.createBoxBody(this.mPhysicsWorld, roof, BodyType.StaticBody, wallFixtureDef);
PhysicsFactory.createBoxBody(this.mPhysicsWorld, left, BodyType.StaticBody, wallFixtureDef);
PhysicsFactory.createBoxBody(this.mPhysicsWorld, right, BodyType.StaticBody, wallFixtureDef);
this.mScene.attachChild(ground);
this.mScene.attachChild(roof);
this.mScene.attachChild(left);
this.mScene.attachChild(right);
this.mScene.registerUpdateHandler(this.mPhysicsWorld);
addFace(0,0);
return this.mScene;
}
#Override
public void onResumeGame() {
super.onResumeGame();
this.enableAccelerometerSensor(this);
}
#Override
public void onPauseGame() {
super.onPauseGame();
this.disableAccelerometerSensor();
}
#Override
public void onAccelerometerChanged(AccelerometerData pAccelerometerData) {
// final Vector2 gravity = Vector2Pool.obtain(pAccelerometerData.getX(), pAccelerometerData.getY());
// this.mPhysicsWorld.setGravity(gravity);
// Vector2Pool.recycle(gravity);
}
private float Xlocal = 0.0f;
private float Ylocal = 0.0f;
private long timeDown = 0;
#Override
public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
// if(this.mPhysicsWorld != null) {
// if(pSceneTouchEvent.isActionDown()) {
// this.addFace(pSceneTouchEvent.getX(), pSceneTouchEvent.getY());
// return true;
// }
if (pSceneTouchEvent.isActionDown()) {
timeDown = System.currentTimeMillis();
System.out.println("Time down is "+timeDown);
Xlocal = pSceneTouchEvent.getX();
Ylocal = pSceneTouchEvent.getY();
} else if (pSceneTouchEvent.isActionUp()) {
X = (pSceneTouchEvent.getX() - Xlocal);
Y = (pSceneTouchEvent.getY() - Ylocal);
float distance = getDistance(Xlocal, Ylocal, pSceneTouchEvent.getX(), pSceneTouchEvent.getY());
if( distance >= MAX_DISTANCE_FLING){
long timeUp = System.currentTimeMillis();
System.out.println("Time up is "+timeUp);
shootBall(X, Y, distance, (float) (timeUp - timeDown) / TimeConstants.MILLISECONDSPERSECOND);
}
}
// }
return true;
}
private void addFace(final float pX, final float pY) {
this.mFaceCount++;
Debug.d("Faces: " + this.mFaceCount);
final AnimatedSprite face;
final FixtureDef objectFixtureDef = PhysicsFactory.createFixtureDef(0.3f, 0.2f, 0.3f);
final FixtureDef objectFixtureDefKinetic = PhysicsFactory.createFixtureDef(0.5f, 0.4f, 0f);
// if(this.mFaceCount % 2 == 0) {
face = new AnimatedSprite(pX, pY, this.mCircleFaceTextureRegion){
#Override
public boolean onAreaTouched(TouchEvent pSceneTouchEvent, float pTouchAreaLocalX, float pTouchAreaLocalY) {
// if(pSceneTouchEvent.getAction() == TouchEvent.ACTION_DOWN){
// Xlocal = pSceneTouchEvent.getX();
// Ylocal = pSceneTouchEvent.getY();
//
// }else if(pSceneTouchEvent.getAction() == TouchEvent.ACTION_UP){
// float x = pSceneTouchEvent.getX();
// float y = pSceneTouchEvent.getY();
//
// x = Math.abs(x - Xlocal);
// y = Math.abs(y - Ylocal);
//
// shootBall(x, y);
// }
//
// return mVisible;
return false;
}
};
face.setScale(2);
AnimatedSprite face2 = new AnimatedSprite((CAMERA_WIDTH * 0.5f) - mBoxFaceTextureRegion.getWidth(), (CAMERA_HEIGHT * 0.5f) - mBoxFaceTextureRegion.getHeight(), this.mBoxFaceTextureRegion.clone());
Body bodyKinetic = PhysicsFactory.createBoxBody(this.mPhysicsWorld, face2, BodyType.KinematicBody, objectFixtureDefKinetic);
bodyKinetic.setAngularVelocity(15f);
AnimatedSprite face3 = new AnimatedSprite(400f, 100f, this.mBoxFaceTextureRegion.clone());
Body body2 = PhysicsFactory.createBoxBody(this.mPhysicsWorld, face3, BodyType.DynamicBody, objectFixtureDef);
body2.setLinearDamping(2f);
body2.setAngularDamping(10f);
// } else {
// face = new AnimatedSprite(pX, pY, this.mBoxFaceTextureRegion);
// face.setScale(MathUtils.random(0.5f, 1.25f));
// body = PhysicsFactory.createBoxBody(this.mPhysicsWorld, face, BodyType.DynamicBody, objectFixtureDef);
// }
body = PhysicsFactory.createCircleBody(this.mPhysicsWorld, face, BodyType.DynamicBody, objectFixtureDef);
body.setBullet(true);
this.mScene.attachChild(face);
this.mScene.registerTouchArea(face);
this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(face, body, true, true));
this.mScene.attachChild(face2);
this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(face2, bodyKinetic));
this.mScene.attachChild(face3);
this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(face3, body2));
}
private void shootBall(final float pX, final float pY, final float pDistance, final float pTime) {
System.out.println("Time Final seconds "+pTime);
float angleRad =(float)Math.atan2(pY, pX);
float velocity = this.getVelocity(pTime);//(pDistance * 12.5f) / 100f;
if(body != null){
float Vx = velocity * (float)Math.cos(angleRad);
float Vy = velocity * (float)Math.sin(angleRad);
body.applyLinearImpulse(new Vector2(Vx,Vy), body.getWorldCenter());
body.setAngularDamping(0.8f); //to decrease velocity slowly. no linear no floaty :)
body.setLinearDamping(0.5f);
body.applyTorque(100f);
}
}
private float getDistance(float x1, float y1, float x2, float y2){
float X2_ = (float)Math.pow(x2 - x1, 2);
float Y2_ = (float)Math.pow(y2 - y1, 2);
float distance = (float)Math.sqrt(X2_ + Y2_);
return distance;
}
private float getVelocity(float pTime) {
float velocity = MAX_VELOCITY_CONST - (pTime * 100f);
if (velocity < DEFAULT_VELOCITY) {
velocity = DEFAULT_VELOCITY;
}
System.out.println("velocity "+velocity);
return velocity;
}
}
please see bodyKinetic.setAngularVelocity(15f);
Update
#Override
public void onResumeGame() {
super.onResumeGame();
if(bodyKinetic != null){
System.out.println("set angular veclocity resume ");
bodyKinetic.setAngularVelocity(25f);
}
this.enableAccelerometerSensor(this);
}
Its strange that when I run it in dubug mode it runs ok and without debug it has no effect
Update
I have also unreg and re-reg body but still of no use :(
#Override
public void onResumeGame() {
super.onResumeGame();
if(bodyKinetic != null){
System.out.println("OnResumeGame Angular Velocity "+bodyKinetic.getAngularVelocity());
this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector((IShape) bodyKinetic.getUserData(), bodyKinetic));
bodyKinetic.setAngularVelocity(25f);
}
this.enableAccelerometerSensor(this);
}
#Override
public void onPauseGame() {
super.onPauseGame();
if(bodyKinetic != null){
System.out.println("OnPauseGame Angular Velocity "+bodyKinetic.getAngularVelocity());
bodyKinetic.setAngularVelocity(0f);
this.mPhysicsWorld.unregisterPhysicsConnector(this.mPhysicsWorld.getPhysicsConnectorManager().findPhysicsConnectorByShape((IShape) bodyKinetic.getUserData()));
}
this.disableAccelerometerSensor();
}
You can try saving the bodies' velocities in onPauseGame() and setting them back in onResume(). You can also try setting linear and angular damping to 0 when pausing the game and reseting the values when resuming. The damping sometimes behaved weirdly in the project I work on.
The fact that you're experiencing a heisenbug suggests that it might be a bug in the engine.
I am doing simple application that should put circle on Canvas when the user taps the screen and then put that circle in the PhysicalWorld I have defined.
I draw the circle in "onDraw" method, but when it is created its position does not change (seems like the gravity and staff are not applied) and the circle is static (stays on the position where it's created).
can you check this code and tell me if I am doing anything wrong:
[update]
public class PhysicsWorld extends SurfaceView implements SurfaceHolder.Callback {
private AABB worldAABB;
private World world;
private PolygonDef groundShapeDef;
public int W_width, W_height;
private static final String TAG = PhysicsWorld.class.getSimpleName();
protected static final int GUIUPDATEIDENTIFIER = 0x231;
public int targetFPS = 40;
public float timeStep = (10.0f/targetFPS);
public int iterations = 5 ;
private int count=0;
private Body[] theBodies ;
private Paint paint;
private float radius = 20;
private MyThread mMyThread;
public PhysicsWorld(Context context) {
super(context);
}
public PhysicsWorld(Context context, AttributeSet set) {
super(context, set);
getHolder().addCallback(this);
W_width = 500;
W_height = 700;
worldAABB = new AABB();
Vec2 min = new Vec2(-50, -50);
Vec2 max = new Vec2(W_width+50, W_height+50);
worldAABB.lowerBound.set(min);
worldAABB.upperBound.set(max);
Vec2 gravity = new Vec2((float) 10.0, (float) 9.8);
boolean doSleep = false;
world = new World(worldAABB, gravity, doSleep);
BodyDef groundBodyDef = new BodyDef();
groundBodyDef.position.set(new Vec2((float) 0.0, (float) -10.0));
Body groundBody = world.createBody(groundBodyDef);
groundShapeDef = new PolygonDef();
groundShapeDef.setAsBox(W_width, 10);
groundBody.createShape(groundShapeDef);
// up :
groundBodyDef = new BodyDef();
groundBodyDef.position.set(new Vec2((float) 0.0, (float) (W_height + 10.0)));
groundBody = world.createBody(groundBodyDef);
groundShapeDef = new PolygonDef();
groundShapeDef.setAsBox(W_width, 10);
groundBody.createShape(groundShapeDef);
// left :
groundBodyDef = new BodyDef();
groundBodyDef.position.set(new Vec2(-10, (float) 0.0));
groundBody = world.createBody(groundBodyDef);
groundShapeDef = new PolygonDef();
groundShapeDef.setAsBox(10, W_height);
groundBody.createShape(groundShapeDef);
// right :
groundBodyDef = new BodyDef();
groundBodyDef.position.set(new Vec2((float) W_width + 10, (float) 0.0));
groundBody = world.createBody(groundBodyDef);
groundShapeDef = new PolygonDef();
groundShapeDef.setAsBox(10, W_height);
groundBody.createShape(groundShapeDef);
theBodies = new Body[50];
paint = new Paint();
paint.setStyle(Style.FILL);
paint.setColor(Color.RED);
paint.setAntiAlias(true);
// setWillNotDraw(false);
}
public void addBall(int x, int y) {
BodyDef bodyDef = new BodyDef();
bodyDef.position.set(x, y);
Log.d(TAG,"Ball Created At: " + Integer.toString(x) + "," + Integer.toString(y));
theBodies[count] = world.createBody(bodyDef);
CircleDef circle = new CircleDef();
circle.radius = radius;
circle.density = (float) 1.0;
circle.restitution = 0.5f;
theBodies[count].createShape(circle);
theBodies[count].setMassFromShapes();
count+=1;
}
public void update() {
world.step(timeStep, iterations);
postInvalidate();
}
#Override
protected void onDraw(Canvas canvas) {
paint = new Paint();
paint.setStyle(Style.FILL);
paint.setColor(Color.RED);
for (int j = 0; j < count; j++) {
canvas.drawCircle(theBodies[j].getPosition().x, W_height - theBodies[j].getPosition().y, radius, paint);
Log.v(TAG + " x: ", String.valueOf(theBodies[j].getPosition().x));
Log.v(TAG + " y:", String.valueOf(W_height - theBodies[j].getPosition().y));
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
addBall((int)event.getX(),(int)event.getY());
}
return true;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
mMyThread = new MyThread(holder, this);
mMyThread.setFlag(true);
mMyThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
}
My experience with box2D is in C++ and iPhone, but it seems your forgetting to make the Ball dynamic.
I've kinda guessed at the solution as I don't know the exact syntax for JBox2D (Please edit if anyone knows actual code)
bodyDef.type = BodyDef.dynamicBody;
I've had a look at the documentation on google code and I think you should use world.createDynamicBody(bodyDef);
I started off with the tutorial here and added drawing code for a view (covering the entire screen). The scene appears static and not dynamic even though I'm calling world.step(,). How does one make the scene dynamic and is my drawing the right implementation or is there a better way (using jBox2d functions)?
private class PhysicsView extends View {
Paint mPaint;
public PhysicsView(Context context) {
super(context);
mPaint = new Paint();
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Style.FILL_AND_STROKE);
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
synchronized (canvas) {
Body[] b = mWorld.bodies;
for (int i = 0; i < b.length; i++) {
if (b[i] != null) {
Float mass = b[i].getMass();
Vec2 v = b[i].getPosition();
canvas.drawCircle(v.x, v.y, 15, mPaint);
}
}
Paint p2 = new Paint(mPaint);
p2.setColor(Color.GREEN);
Vec2 base = mWorld.groundBody.getPosition();
canvas.drawRect(new RectF((float) base.x - 25.0f,
(float) base.y - 10.0f, (float) base.x + 25.0f,
(float) base.y + 10.0f), p2);
canvas.drawLine(0.0f, 0.0f,
mWorld.world.getWorldAABB().upperBound.x, mWorld.world
.getWorldAABB().upperBound.y, p2);
}
}
}
public class PhysicsWorld {
public int targetFPS = 40;
public int timeStep = (1000 / targetFPS);
public int iterations = 5;
public Body[] bodies = new Body[50];
public Body groundBody;
private int count = 0;
private AABB worldAABB;
public World world;
private BodyDef groundBodyDef;
private PolygonDef groundShapeDef;
private Vec2 screenDimensions;
public void create(Vec2 dimens) {
screenDimensions = dimens;
worldAABB = new AABB();
worldAABB.lowerBound.set(new Vec2((float) -1*dimens.x, (float) -1*dimens.y));
worldAABB.upperBound.set(new Vec2((float) dimens.x, (float) dimens.y));
Vec2 gravity = new Vec2((float) 0.0, (float) 0.0);
boolean doSleep = true;
world = new World(worldAABB, gravity, doSleep);
groundBodyDef = new BodyDef();
groundBodyDef.position.set(new Vec2((float) dimens.x/2, (float) dimens.y - 10.0f));
groundBody = world.createBody(groundBodyDef);
groundBody.m_mass = 200.0f;
groundShapeDef = new PolygonDef();
groundShapeDef.setAsBox((float) 50.0, (float) 10.0);
groundShapeDef.density = 1.0f;
groundShapeDef.friction = 0.3f;
groundBody.createShape(groundShapeDef);
}
public void addBall() {
float x = (float) Math.random() * screenDimensions.x;
float y = (float) Math.random() * screenDimensions.y;
BodyDef bodyDef = new BodyDef();
bodyDef.position.set(new Vec2((float) x, (float) y));
bodyDef.massData.mass = 20.0f;
bodies[count] = world.createBody(bodyDef);
bodies[count].m_type = Body.e_dynamicType;
bodies[count].m_linearVelocity = new Vec2(1.0f, 2.0f);
CircleDef circle = new CircleDef();
circle.radius = (float) 1.8;
circle.density = (float) 1.0;
circle.type = ShapeType.CIRCLE_SHAPE;
bodies[count].createShape(circle);
count++;
}
public void update() {
world.step(timeStep, iterations);
}
public void changeGravity(Vec2 gravity) {
world.setGravity(gravity);
}
}
There are a number of issues with your code....but this answer is probably way too late to be of any help anyways.
Firstly, world.createBody() seems to have been deprecated. Now you have to explicitly call world.createDynamicBody() in the latest version of the Android Box2D jar that I have.
Anyways...my version goes something like this...not perfect but at least its dynamic.
package com.box2D;
import java.util.ArrayList;
import org.jbox2d.collision.AABB;
import org.jbox2d.collision.CircleDef;
import org.jbox2d.collision.PolygonDef;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.World;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.Log;
public class PhysicsWorld
{
public float timeStep = 1;
public int iterations = 1;
private ArrayList<Body> bodies;
private int count = 0;
private AABB worldAABB;
private World world;
private BodyDef groundBodyDef;
private PolygonDef boxShapeDef;
//a paint
private Paint paint;
public void create()
{
//create the paint
paint = new Paint();
paint.setColor(0xFFFFFF);
// Step 1: Create Physics World Boundaries
worldAABB = new AABB();
worldAABB.lowerBound.set(new Vec2((float) -100.0, (float) -100.0));
worldAABB.upperBound.set(new Vec2((float) 100.0, (float) 300.0));
// Step 2: Create Physics World with Gravity
Vec2 gravity = new Vec2((float) 0.0, (float)10.0);
boolean doSleep = true;
world = new World(worldAABB, gravity, doSleep);
bodies = new ArrayList<Body>();
// Step 3: Create Ground Box
groundBodyDef = new BodyDef();
groundBodyDef.position.set(new Vec2((float) 0.0, (float) 300.0));
Body groundBody = world.createDynamicBody(groundBodyDef);
boxShapeDef = new PolygonDef();
boxShapeDef.setAsBox((float) 50.0, (float) 10.0);
groundBody.createShape(boxShapeDef);
}
public void addBall()
{
// Create Dynamic Body
BodyDef bodyDef = new BodyDef();
bodyDef.position.set((float)16.0+(count * 10), (float)24.0);
Body newBody = world.createDynamicBody(bodyDef);
// Create Shape with Properties
CircleDef circle = new CircleDef();
circle.radius = (float)5;
circle.density = (float)1.0;
// Assign shape to Body
newBody.createShape(circle);
newBody.setMassFromShapes();
newBody.m_userData = "Circle";
bodies.add(newBody);
// Increase Counter
count += 1;
}
public void addBox()
{
// Create Dynamic Body
BodyDef bodyDef = new BodyDef();
bodyDef.position.set((float)36.0+(count * 10), (float)24.0);
Body newBody = world.createDynamicBody(bodyDef);
// Create Shape with Properties
boxShapeDef = new PolygonDef();
boxShapeDef.setAsBox((float) 50.0, (float) 10.0);
// Assign shape to Body
newBody.createShape(boxShapeDef);
newBody.setMassFromShapes();
newBody.m_userData = "Box";
bodies.add(newBody);
// Increase Counter
count += 1;
}
/*
*
*
* Physics Update
*/
private Vec2 position;
private float angle;
public void Update(Canvas canvas)
{
// Update Physics World
world.step(timeStep/5, 1);
// Print info of latest body
for (Body i : bodies)
{
position = i.getPosition();
angle = i.getAngle();
if(i.m_userData == "Circle")
canvas.drawCircle(position.x, position.y, 5,paint );
if(i.m_userData == "Box")
canvas.drawRect((float)(position.x -5), (float)(position.y - 5), (float)(position.x + 5), (float)(position.y + 5), paint );
Log.v("Physics Test", "Pos: (" + position.x + ", " + position.y + "), Angle: " + angle);
}
}
}
In response to this part of your question:
"is my drawing the right implementation or is there a better way"
This is from the Box2D website:
What units does Box2D use?
Box2D is tuned for meters-kilograms-seconds (MKS). Your moving objects should be between 0.1 - 10 meters. Do not use pixels as units! You will get a jittery simulation.
How do I convert pixels to meters?
Suppose you have a sprite for a character that is 100x100 pixels. You decide to use a scaling factor that is 0.01. This will make the character physics box 1m x 1m. So go make a physics box that is 1x1. Now suppose the character starts out at pixel coordinate (345,679). So position the physics box at (3.45,6.79). Now simulate the physics world. Suppose the character physics box moves to (2.31,4.98), so move your character sprite to pixel coordinates (231,498). Now the only tricky part is choosing a scaling factor. This really depends on your game. You should try to get your moving objects in the range 0.1 - 10 meters, with 1 meter being the sweet spot.
...
What are the biggest mistakes made by new users?
Using pixels for length instead of meters.
...
Source: http://code.google.com/p/box2d/wiki/FAQ
public class PhysicsWorld extends View{
protected static final int GUIUPDATEIDENTIFIER = 0x231;
public int targetFPS = 40;
public float timeStep = 10.0f / targetFPS;
public int iterations = 5;
private Body[] bodies;
private int count = 0;
private AABB worldAABB;
public World world;
private PolygonDef groundShapeDef;
public int World_W,World_H;
private Paint paint;
private float radius=10;
public PhysicsWorld(Context context,int W,int H) {
super(context);
World_W=W;
World_H=H;
// Step 1: Create Physics World Boundaries
worldAABB = new AABB();
Vec2 min = new Vec2(-50, -50);
Vec2 max = new Vec2(World_W + 50, World_H + 50);
worldAABB.lowerBound.set(min);
worldAABB.upperBound.set(max);
// Step 2: Create Physics World with Gravity
Vec2 gravity = new Vec2((float) 0.0, (float) -10.0);
boolean doSleep = true;
world = new World(worldAABB, gravity, doSleep);
// Step 3:
//Create Ground Box :
BodyDef bodyDef = new BodyDef();
bodyDef.position.set(new Vec2((float) 0.0, (float) -10.0));
Body groundBody = world.createBody(bodyDef);
groundShapeDef = new PolygonDef();
groundShapeDef.setAsBox((float) World_W, (float) 10);
groundBody.createShape(groundShapeDef);
// up :
bodyDef = new BodyDef();
bodyDef.position.set(new Vec2((float) 0.0, (float) (World_H+10.0) ));
groundBody = world.createBody(bodyDef);
groundShapeDef = new PolygonDef();
groundShapeDef.setAsBox((float) World_W, (float) 10);
groundBody.createShape(groundShapeDef);
// left :
bodyDef = new BodyDef();
bodyDef.position.set(new Vec2((float) -10, (float) 0.0 ));
groundBody = world.createBody(bodyDef);
groundShapeDef = new PolygonDef();
groundShapeDef.setAsBox((float)10, (float) World_H);
groundBody.createShape(groundShapeDef);
// right :
bodyDef = new BodyDef();
bodyDef.position.set(new Vec2((float) World_W+10, (float) 0.0 ));
groundBody = world.createBody(bodyDef);
groundShapeDef = new PolygonDef();
groundShapeDef.setAsBox((float)10, (float) World_H);
groundBody.createShape(groundShapeDef);
//
// step 4: initialize
bodies=new Body[50];
//
paint=new Paint();
paint.setStyle(Style.FILL_AND_STROKE);
paint.setColor(Color.CYAN);
}
public void addBall() {
// Create Dynamic Body
BodyDef bodyDef = new BodyDef();
Random rnd = new Random();
bodyDef.position.set((float) radius*2+rnd.nextInt( (int)(World_W-radius*4) ), (float)2*radius+ rnd.nextInt( (int)(World_H-radius*4) ));
bodies[count] = world.createBody(bodyDef);
// Create Shape with Properties
CircleDef circle = new CircleDef();
circle.radius = (float) radius;
circle.density = (float) 1.0;
circle.friction = 0.1f;
circle.restitution=0.5f;
// Assign shape to Body
bodies[count].createShape(circle);
bodies[count].setMassFromShapes();
// Increase Counter
count += 1;
}
public void addBox() {
BodyDef bodyDef = new BodyDef();
Random rnd = new Random();
bodyDef.position.set((float)2*count+rnd.nextInt( (int)(World_W-4*count) ), (float)2*count+rnd.nextInt( (int)(World_H-count*4) ));
bodies[count] = world.createBody(bodyDef);
// Create Shape with Properties
PolygonDef boxShapeDef = new PolygonDef();
boxShapeDef.setAsBox((float) 10.0, (float) 10.0);
boxShapeDef.density = 1.0f; // A density of 0 will create a fixed Body that doesn't move
boxShapeDef.friction = 0.1f; // How much friction
boxShapeDef.restitution = 0.5f; // How bouncy is this Shape?
// Assign shape to Body
bodies[count].createShape(boxShapeDef);
bodies[count].setMassFromShapes();
bodies[count].m_userData = "Box";
count += 1;
}
public void update() {
world.step(timeStep/5, 1);
postInvalidate();
}
protected void onDraw(Canvas canvas) {
for(int j = 0;j<count;j++)
{
canvas.drawRect((float)(bodies[j].getPosition().x -10), (float)(bodies[j].getPosition().y - 10), (float)(bodies[j].getPosition().x+ 10), (float)(bodies[j].getPosition().y + 10), paint );
canvas.drawCircle(bodies[j].getPosition().x,World_H- bodies[j].getPosition().y, radius, paint);
}
}