2012-01-07 62 views
3

我已經拿SensorManager加速器的例子,其中畫布(球)得到更新其位置爲每個設備加速器旋轉。下面是圖像:彈回球android

enter image description here

作爲圖像中示出有一個球和一個線。球的位置經常更新,線的位置是靜態的。

我想讓球在接觸線時反彈回來。我從3天起就嘗試過,但不明白我該如何做到這一點。

這裏是我的代碼:

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); 
        } 
       } 
      } 
     } 
    } 
} 
+0

讓它彈跳就像檢測碰撞一樣簡單,球和線非常簡單(垂直線的x位置大於球的中心+其半徑)。然後你會在那一刻抓住速度並扭轉它。那很簡單。 – blindstuff 2012-01-12 17:52:02

+0

對不起,但你應該只反轉速度的水平分量。垂直的一個保持不變。 – Gangnus 2012-01-14 22:10:46

回答

13

而不是試圖通過開發有兩個組件的軟件架構,以解決您的代碼中,工作在設計層面:物理模型和顯示。關鍵是將問題的物理學與顯示分開。與顯示器分開進行物理建模變得更容易。同樣,顯示也變得更容易。有兩個獨立的包 - 一個用於物理和一個用於顯示。

從一個簡單的問題開始,物理世界只有一個點和一條線。模擬反射線的點。你有一些代碼可以做到這一點。只需將其從當前代碼中刪除即可。確保物理過程符合您的期望,而不必擔心顯示器。

設計一個球類。球具有速度和位置屬性。它有一種移動方法,根據一次點擊的速度更新位置。移動方法檢查它是否與牆壁發生了交互(碰撞),並根據您希望您的世界的物理量改變速度。碰撞檢測通過詢問牆壁是否完成來完成。物理學可以是入射角等於反射角,或者您可以在球上具有旋轉屬性以改變球的反彈方式。關鍵是所有的物理建模都是與顯示器分開進行的。同樣,你爲牆壁創建一個類。最初牆是固定的,但你可以增加運動。好的是,如果您設計的球類正確地更換了牆,使其移動並不影響球類的設計。此外,這些都不會改變物理效果如何完成顯示。

製作一個簡單的物理轉換成屏幕上的演示文稿的顯示。

從那裏你可以增加你的模型的複雜性。讓這個觀點成爲一個圓圈。重做物理學,使其適應這種新的複雜性。顯示屏不會有太大變化,但要保持獨立。

我有我的CS1類做同樣的問題的版本。兩年前,我讓他們做了一場乒乓球比賽。去年一個版本的蜈蚣。這個即將到來的學期,他們將有Breakout作爲一個項目。當他們與顯示器分開建立物理模型時,他們就可以正常工作。當他們不這樣做時,通常是一團糟。

1

Physics modyle應在單獨的線程中運行,並使用最佳可用時間分辨率來更新位置。 (毫秒應該是足夠了)這是我的設計gameloop:

lastFrameTime = System.currentTimeMillis(); 

    // as long as we run we move 
    while (state == GameState.RUNNING) { 
     currentFrame++; 
     timeNow = System.currentTimeMillis(); 

     // sleep until this frame is scheduled 
     long l = lastFrameTime + FRAME_DELAY - timeNow; 
     updatePositions(); 
     redraw();  
     if (l > 0L) { 
      try { 
       Thread.sleep(l); 
      } 
      catch (Exception exception) { 
      } 
     } else { 
      // something long kept us from updating, reset delays 
      lastFrameTime = timeNow; 
      l = FRAME_DELAY; 
     } 

     lastFrameTime = timeNow + l; 
     // be polite, let others play 
     Thread.yield(); 
    } 

它放棄線程的控制權,將爲其處理事件和蜂巢命令你phyiscs引擎用戶界面的任務是很重要的。

至於碰撞檢測 - 這是非常簡單的數學。你的線是垂直的,並且zou必須檢查線和中心的x座標中的差值是否小於半徑 - 然後是速度的反向x部分

+0

這很好,但...我如何在我的編碼中實現這一點? +1 – 2012-01-09 07:43:14

+0

這實際上是完整的遊戲循環;)你必須實現updatePositions()和redraw() – 2012-01-09 10:18:59

0

你可以使用Rect.intersects(Rect,Rect)來檢測碰撞。使用您的位圖寬度和高度來設置新的Rects。

這裏是一個骯髒的例子:

import java.util.Timer; 

import android.app.Activity; 
import android.content.Context; 
import android.content.pm.ActivityInfo; 
import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
import android.graphics.Canvas; 
import android.graphics.Paint; 
import android.graphics.Rect; 
import android.graphics.RectF; 
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.view.Display; 
import android.view.SurfaceHolder; 
import android.view.SurfaceView; 
import android.view.WindowManager; 

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 final boolean isTimerStarted = false; 
private long mStart; 

@Override 
public void onCreate(final 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 
    final 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(final Sensor sensor, final int accuracy) { 

} 

@Override 
public void onSensorChanged(final SensorEvent event) { 
    // obtain the three accelerations from sensors 
    mAx = event.values[0]; 
    mAy = event.values[1]; 

    final 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 final RectF mRectF; 
    private final Paint mPaint; 
    private final ShapeThread mThread; 

    private float mVx; 
    private float mVy; 
    private final Rect lineRect = new Rect(); 
    private final Rect ballRect = new Rect(); 

    public ShapeView(final 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(final int x, final 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; 
     } 

     if(Rect.intersects(lineRect, ballRect)){ 
      mVx = -mVx * FACTOR_BOUNCEBACK; 
      mVy = -mVy * FACTOR_BOUNCEBACK; 
      mXCenter += (int) (mDeltaT * (mVx + 0.6 * mAx * mDeltaT)) * 5; 
      mYCenter += (int) (mDeltaT * (mVy + 0.6 * mAy * mDeltaT)) * 5; 
     } 

     return true; 
    } 

    // update the canvas. 
    @Override 
    protected void onDraw(final Canvas canvas) { 
     if (mRectF != null) { 
      mRectF.set(mXCenter - RADIUS, mYCenter - RADIUS, mXCenter 
        + RADIUS, mYCenter + RADIUS); 
      canvas.drawColor(0XFF000000); 
      // canvas.drawOval(mRectF, mPaint); 

      final Bitmap kangoo = BitmapFactory.decodeResource(getResources(), 
        R.drawable.blankcard); 
      lineRect.set(130, 10, 130 + kangoo.getWidth(), 10 + kangoo.getHeight()); 

      final Bitmap ball = BitmapFactory.decodeResource(getResources(), 
        R.drawable.blankcard); 
      ballRect.set(mXCenter - RADIUS, mYCenter - RADIUS, mXCenter - RADIUS + ball.getWidth(), mYCenter - RADIUS + ball.getHeight()); 

      canvas.drawBitmap(ball, mXCenter - RADIUS, mYCenter - RADIUS, 
        mPaint); 
      canvas.drawBitmap(kangoo, 130, 10, null); 

     } 
    } 

    @Override 
    public void surfaceChanged(final SurfaceHolder holder, final int format, final int width, 
      final int height) { 
    } 

    @Override 
    public void surfaceCreated(final SurfaceHolder holder) { 
     mThread.setRunning(true); 
     mThread.start(); 
    } 

    @Override 
    public void surfaceDestroyed(final SurfaceHolder holder) { 
     boolean retry = true; 
     mThread.setRunning(false); 
     while (retry) { 
      try { 
       mThread.join(); 
       retry = false; 
      } catch (final InterruptedException e) { 

      } 
     } 
    } 
} 

class ShapeThread extends Thread { 
    private final SurfaceHolder mSurfaceHolder; 
    private final ShapeView mShapeView; 
    private boolean mRun = false; 

    public ShapeThread(final SurfaceHolder surfaceHolder, final ShapeView shapeView) { 
     mSurfaceHolder = surfaceHolder; 
     mShapeView = shapeView; 
    } 

    public void setRunning(final 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); 
       } 
      } 
     } 
    } 
} 
} 

需要改進,但可能讓你在正確的軌道上。