android 弹回球
我以使用 SensorManager 的加速器为例,其中画布(球)根据设备加速器的旋转来更新其位置。这是图像:
如图所示,有一个球和一条线。球的位置经常更新,而线的位置是静态的。
我想让球在触及线时反弹回来。我从三天开始就尝试过,但不明白我该怎么做。
这是我的代码:
public class ballsensor extends Activity implements SensorEventListener {
// sensor-related
private SensorManager mSensorManager;
private Sensor mAccelerometer;
// animated view
private ShapeView mShapeView;
// screen size
private int mWidthScreen;
private int mHeightScreen;
// motion parameters
private final float FACTOR_FRICTION = 0.5f; // imaginary friction on the
// screen
private final float GRAVITY = 9.8f; // acceleration of gravity
private float mAx; // acceleration along x axis
private float mAy; // acceleration along y axis
private final float mDeltaT = 0.5f; // imaginary time interval between each
// acceleration updates
// timer
private Timer mTimer;
private Handler mHandler;
private boolean isTimerStarted = false;
private long mStart;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set the screen always portait
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// initializing sensors
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mAccelerometer = mSensorManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// obtain screen width and height
Display display = ((WindowManager) this
.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
mWidthScreen = display.getWidth();
mHeightScreen = display.getHeight() - 35;
// initializing the view that renders the ball
mShapeView = new ShapeView(this);
mShapeView.setOvalCenter((int) (mWidthScreen * 0.6),
(int) (mHeightScreen * 0.6));
setContentView(mShapeView);
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
public void onSensorChanged(SensorEvent event) {
// obtain the three accelerations from sensors
mAx = event.values[0];
mAy = event.values[1];
float mAz = event.values[2];
// taking into account the frictions
mAx = Math.signum(mAx) * Math.abs(mAx)
* (1 - FACTOR_FRICTION * Math.abs(mAz) / GRAVITY);
mAy = Math.signum(mAy) * Math.abs(mAy)
* (1 - FACTOR_FRICTION * Math.abs(mAz) / GRAVITY);
}
@Override
protected void onResume() {
super.onResume();
// start sensor sensing
mSensorManager.registerListener(this, mAccelerometer,
SensorManager.SENSOR_DELAY_NORMAL);
}
@Override
protected void onPause() {
super.onPause();
// stop senser sensing
mSensorManager.unregisterListener(this);
}
// the view that renders the ball
private class ShapeView extends SurfaceView implements
SurfaceHolder.Callback {
private final int RADIUS = 30;
private final float FACTOR_BOUNCEBACK = 0.50f;
private int mXCenter;
private int mYCenter;
private RectF mRectF;
private final Paint mPaint;
private ShapeThread mThread;
private float mVx;
private float mVy;
public ShapeView(Context context) {
super(context);
getHolder().addCallback(this);
mThread = new ShapeThread(getHolder(), this);
setFocusable(true);
mPaint = new Paint();
mPaint.setColor(0xFFFFFFFF);
mPaint.setAlpha(192);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setAntiAlias(true);
mRectF = new RectF();
}
// set the position of the ball
public boolean setOvalCenter(int x, int y) {
mXCenter = x;
mYCenter = y;
return true;
}
// calculate and update the ball's position
public boolean updateOvalCenter() {
mVx -= mAx * mDeltaT;
mVy += mAy * mDeltaT;
System.out.println("mVx is ::" + mVx);
System.out.println("mVy is ::" + mVy);
mXCenter += (int) (mDeltaT * (mVx + 0.6 * mAx * mDeltaT));
mYCenter += (int) (mDeltaT * (mVy + 0.6 * mAy * mDeltaT));
if (mXCenter < RADIUS) {
mXCenter = RADIUS;
mVx = -mVx * FACTOR_BOUNCEBACK;
}
if (mYCenter < RADIUS) {
mYCenter = RADIUS;
mVy = -mVy * FACTOR_BOUNCEBACK;
}
if (mXCenter > mWidthScreen - RADIUS) {
mXCenter = mWidthScreen - RADIUS;
mVx = -mVx * FACTOR_BOUNCEBACK;
}
if (mYCenter > mHeightScreen - 2 * RADIUS) {
mYCenter = mHeightScreen - 2 * RADIUS;
mVy = -mVy * FACTOR_BOUNCEBACK;
}
return true;
}
// update the canvas.
@Override
protected void onDraw(Canvas canvas) {
if (mRectF != null) {
mRectF.set(mXCenter - RADIUS, mYCenter - RADIUS, mXCenter
+ RADIUS, mYCenter + RADIUS);
canvas.drawColor(0XFF000000);
// canvas.drawOval(mRectF, mPaint);
Bitmap kangoo = BitmapFactory.decodeResource(getResources(),
R.drawable.stripe1);
Bitmap ball = BitmapFactory.decodeResource(getResources(),
R.drawable.blackwhiteball);
canvas.drawBitmap(ball, mXCenter - RADIUS, mYCenter - RADIUS,
mPaint);
canvas.drawBitmap(kangoo, 130, 10, null);
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mThread.setRunning(true);
mThread.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
mThread.setRunning(false);
while (retry) {
try {
mThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
}
class ShapeThread extends Thread {
private SurfaceHolder mSurfaceHolder;
private ShapeView mShapeView;
private boolean mRun = false;
public ShapeThread(SurfaceHolder surfaceHolder, ShapeView shapeView) {
mSurfaceHolder = surfaceHolder;
mShapeView = shapeView;
}
public void setRunning(boolean run) {
mRun = run;
}
public SurfaceHolder getSurfaceHolder() {
return mSurfaceHolder;
}
@Override
public void run() {
Canvas c;
while (mRun) {
mShapeView.updateOvalCenter();
c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
mShapeView.onDraw(c);
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
}
I have take Example of Accelerator with SensorManager, in which canvas(ball) are get update its position as per device Accelerator are rotated. Here is image :
As shown in the image there is a ball and one line. The ball's position is frequently updated, while the line's position is static.
I would like to have the ball bounce back when it touches the line. I have tried since from 3 day, but don't understand how I can do this.
here is my code:
public class ballsensor extends Activity implements SensorEventListener {
// sensor-related
private SensorManager mSensorManager;
private Sensor mAccelerometer;
// animated view
private ShapeView mShapeView;
// screen size
private int mWidthScreen;
private int mHeightScreen;
// motion parameters
private final float FACTOR_FRICTION = 0.5f; // imaginary friction on the
// screen
private final float GRAVITY = 9.8f; // acceleration of gravity
private float mAx; // acceleration along x axis
private float mAy; // acceleration along y axis
private final float mDeltaT = 0.5f; // imaginary time interval between each
// acceleration updates
// timer
private Timer mTimer;
private Handler mHandler;
private boolean isTimerStarted = false;
private long mStart;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set the screen always portait
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// initializing sensors
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mAccelerometer = mSensorManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// obtain screen width and height
Display display = ((WindowManager) this
.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
mWidthScreen = display.getWidth();
mHeightScreen = display.getHeight() - 35;
// initializing the view that renders the ball
mShapeView = new ShapeView(this);
mShapeView.setOvalCenter((int) (mWidthScreen * 0.6),
(int) (mHeightScreen * 0.6));
setContentView(mShapeView);
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
public void onSensorChanged(SensorEvent event) {
// obtain the three accelerations from sensors
mAx = event.values[0];
mAy = event.values[1];
float mAz = event.values[2];
// taking into account the frictions
mAx = Math.signum(mAx) * Math.abs(mAx)
* (1 - FACTOR_FRICTION * Math.abs(mAz) / GRAVITY);
mAy = Math.signum(mAy) * Math.abs(mAy)
* (1 - FACTOR_FRICTION * Math.abs(mAz) / GRAVITY);
}
@Override
protected void onResume() {
super.onResume();
// start sensor sensing
mSensorManager.registerListener(this, mAccelerometer,
SensorManager.SENSOR_DELAY_NORMAL);
}
@Override
protected void onPause() {
super.onPause();
// stop senser sensing
mSensorManager.unregisterListener(this);
}
// the view that renders the ball
private class ShapeView extends SurfaceView implements
SurfaceHolder.Callback {
private final int RADIUS = 30;
private final float FACTOR_BOUNCEBACK = 0.50f;
private int mXCenter;
private int mYCenter;
private RectF mRectF;
private final Paint mPaint;
private ShapeThread mThread;
private float mVx;
private float mVy;
public ShapeView(Context context) {
super(context);
getHolder().addCallback(this);
mThread = new ShapeThread(getHolder(), this);
setFocusable(true);
mPaint = new Paint();
mPaint.setColor(0xFFFFFFFF);
mPaint.setAlpha(192);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setAntiAlias(true);
mRectF = new RectF();
}
// set the position of the ball
public boolean setOvalCenter(int x, int y) {
mXCenter = x;
mYCenter = y;
return true;
}
// calculate and update the ball's position
public boolean updateOvalCenter() {
mVx -= mAx * mDeltaT;
mVy += mAy * mDeltaT;
System.out.println("mVx is ::" + mVx);
System.out.println("mVy is ::" + mVy);
mXCenter += (int) (mDeltaT * (mVx + 0.6 * mAx * mDeltaT));
mYCenter += (int) (mDeltaT * (mVy + 0.6 * mAy * mDeltaT));
if (mXCenter < RADIUS) {
mXCenter = RADIUS;
mVx = -mVx * FACTOR_BOUNCEBACK;
}
if (mYCenter < RADIUS) {
mYCenter = RADIUS;
mVy = -mVy * FACTOR_BOUNCEBACK;
}
if (mXCenter > mWidthScreen - RADIUS) {
mXCenter = mWidthScreen - RADIUS;
mVx = -mVx * FACTOR_BOUNCEBACK;
}
if (mYCenter > mHeightScreen - 2 * RADIUS) {
mYCenter = mHeightScreen - 2 * RADIUS;
mVy = -mVy * FACTOR_BOUNCEBACK;
}
return true;
}
// update the canvas.
@Override
protected void onDraw(Canvas canvas) {
if (mRectF != null) {
mRectF.set(mXCenter - RADIUS, mYCenter - RADIUS, mXCenter
+ RADIUS, mYCenter + RADIUS);
canvas.drawColor(0XFF000000);
// canvas.drawOval(mRectF, mPaint);
Bitmap kangoo = BitmapFactory.decodeResource(getResources(),
R.drawable.stripe1);
Bitmap ball = BitmapFactory.decodeResource(getResources(),
R.drawable.blackwhiteball);
canvas.drawBitmap(ball, mXCenter - RADIUS, mYCenter - RADIUS,
mPaint);
canvas.drawBitmap(kangoo, 130, 10, null);
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mThread.setRunning(true);
mThread.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
mThread.setRunning(false);
while (retry) {
try {
mThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
}
class ShapeThread extends Thread {
private SurfaceHolder mSurfaceHolder;
private ShapeView mShapeView;
private boolean mRun = false;
public ShapeThread(SurfaceHolder surfaceHolder, ShapeView shapeView) {
mSurfaceHolder = surfaceHolder;
mShapeView = shapeView;
}
public void setRunning(boolean run) {
mRun = run;
}
public SurfaceHolder getSurfaceHolder() {
return mSurfaceHolder;
}
@Override
public void run() {
Canvas c;
while (mRun) {
mShapeView.updateOvalCenter();
c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
mShapeView.onDraw(c);
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
不要尝试修复代码,而是通过开发具有两个组件的软件架构来进行设计级别的工作:物理模型和显示。关键是将问题的物理原理与显示分开。当与显示器分开时,物理建模变得更加容易。同样,显示也变得更加容易。有两个单独的包 - 一个用于物理,一个用于显示。
从问题的更简单版本开始,其中物理世界只有一个点和一条线。对直线反射的点进行建模。您有一些代码可以执行此操作。只需将其从当前代码中删除即可。确保物理效果符合您的预期,而无需担心显示。
为球设计一个类。球具有速度和位置属性。它有一种移动方法,可以根据一次单击的速度更新位置。 move 方法检查它是否与墙壁相互作用(碰撞),并根据您希望世界具有的物理特性更改速度。碰撞检测是通过询问墙壁是否存在来完成的。物理原理可能是入射角等于反射角,或者您可以在球上设置旋转属性来改变球的弹跳方式。关键是所有物理建模都是与显示分开完成的。同样,您为墙创建一个类。最初,墙是固定的,但您可以为其添加运动。好处是,如果您正确设计了球类,则更改墙壁以使其移动不会影响球类的设计。此外,这些物理变化都不会影响显示的方式。
制作一个显示器,可以将物理现象简单地转换为屏幕上的演示。
从那里您可以增加模型的复杂性。将点设为圆。重做物理原理,使其能够适应这种新的复杂性。显示不会有太大变化,但将它们分开。
我让我的 CS1 班做了同样问题的版本。两年前,我让他们做了一场乒乓球比赛。去年有一个版本的蜈蚣。下个学期他们将把“突破”作为一个项目。当他们将物理模型与显示器分开时,他们就能让它发挥作用。如果他们不这样做,事情通常会变得一团糟。
Rather than try to fix your code, work at the design level by developing a software architecture that has two components: physics model and display. The key is to separate the physics of the problem from the display. Modelling the physics becomes much easier when done separately from the display. Likewise the display also becomes easier. Have two separate packages - one for the physics and one for the display.
Start with a simpler version of the problem where the physics world just has a point and a line. Model the point reflecting off the line. You have some code that does this. Just rip it out of the current code. Make sure the physics does what you expect it to without worrying about the display.
Design a class for the ball. The ball has velocity and position properties. It has a move method that updates the position based on the velocity for one time click. The move method checks to see if it has interacted (collided) with the wall and changes the velocity according the physics you want your world to have. The collision detection is done by asking the wall were it is. The physics could be angle of incidence equals angle of reflection, or you could have a spin property on the ball that changes how the ball bounces. The key is that all of the physics modelling is done separately from the display. Similarly, you create a class for the wall. Initially the wall is fixed, but you could add movement to it. The nice thing is that if you've designed the ball class correctly changing the wall to make it move doesn't effect the design of the ball class. Also, none of these changes to the physics effect how the display is done.
Make a display that simply translates the physics into a presentation on the screen.
From there you can add complexity to your model. Make the point a circle. Redo the physics to make it work with this new complexity. The display won't change much, but keep them separate.
I have my CS1 class do versions of this same problem. Two years ago, I had them make a pong game. Last year a version of Centipede. This coming semester they'll have Breakout as a project. When they model the physics separately from the display, they get it working. When they don't, it is usually a muddled mess.
物理模型应在单独的线程中运行,并使用最佳可用时间分辨率进行位置更新。 (毫秒应该足够了)这就是我设计游戏循环的方式:
对于将处理事件和发送给 phyiscs 引擎的 hive 命令的 UI 任务,放弃线程的控制非常重要。
至于碰撞检测——这是非常简单的数学。你的线是垂直的,zou必须检查线和中心的x坐标差是否小于半径 - 然后反转速度的x分量
Physics modyle shall run in separate thread, and use best available time resolution for position updates. ( milliseconds should be enough ) This is how I design gameloop:
It is important to give up control of the thread, for UI tasks which will be processing events and hive commands to your phyiscs engine.
As for collision detection - it is pretty simple math. Your line is vertical, and zou must just check whether difference in x-coord of line and center is got less than radius - then reverse x-componen of velocity
您可以使用 Rect.intersects(Rect, Rect) 来检测碰撞。使用位图宽度和高度来设置新的矩形。
这是一个肮脏的例子:
需要改进,但可能会让你走上正轨。
You can use Rect.intersects(Rect, Rect) to detect collisions. Use your bitmap width and height to set up the new Rects.
Here is a dirty example:
Needs improvement but might get you on the right track.