
Androidの勉強中です。
まずは、描画の方法&加速度センサーの利用方法の確認。
クリックで丸が追加され、それぞれが加速度センサーによって重力を与えられ動きます。
Androidで描画する場合はSurfaceViewを使うのが基本ってことでいいんだろうか。
以下、ソースです。
Main.javaのソース。
package jp.sakef;
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
public class Main extends Activity implements SensorEventListener
{
private SensorManager sensorManager;
private Sensor accelerometer;
private MainView view;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// 画面を縦表示で固定
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
view = new MainView(this);
setContentView(view);
//センサーマネージャの取得
sensorManager=(SensorManager)getSystemService(Context.SENSOR_SERVICE);
//センサーの取得
List<Sensor> list;
list=sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
if (list.size()>0) accelerometer=list.get(0);
}
//アプリの開始
protected void onResume()
{
//アプリの開始
super.onResume();
//センサーの処理の開始
if (accelerometer!=null) sensorManager.registerListener(this,accelerometer,SensorManager.SENSOR_DELAY_FASTEST);
}
//アプリの停止
protected void onStop()
{
//センサーの処理の停止
sensorManager.unregisterListener(this);
//アプリの停止
super.onStop();
}
//センサーリスナーの処理
public void onSensorChanged(SensorEvent event)
{
//加速度の取得
if (event.sensor==accelerometer) view.setAcce(-event.values[0]*0.2f, event.values[1]*0.2f);
}
//精度変更イベントの処理
public void onAccuracyChanged(Sensor sensor,int accuracy){}
// 破棄の際に実行
public void onDestroy()
{
super.onDestroy();
}
}
MainView.javaのソース。
package jp.sakef;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MainView extends SurfaceView implements SurfaceHolder.Callback, Runnable
{
private static double e = 0.8;
private static double d = 0.99;
private float gx;
private float gy;
private Paint paint;
private SurfaceHolder holder;
private Thread thread;
private ArrayList<Circle> container;
private int width;
private int height;
public MainView(Context context)
{
super(context);
holder = null;
thread = null;
container = new ArrayList<Circle>();
paint = new Paint();
paint.setAntiAlias(true);
gx = gy = 0;
// getHolder()メソッドでSurfaceHolderを取得。さらにコールバックを登録
getHolder().addCallback(this);
}
//SurfaceView生成時に呼び出される
public void surfaceCreated(SurfaceHolder holder)
{
this.holder = holder;
thread = new Thread(this);
}
//SurfaceView変更時に呼び出される
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
if(thread != null )
{
this.width = width;
this.height = height;
thread.start();
}
}
//SurfaceView破棄時に呼び出される
public void surfaceDestroyed(SurfaceHolder holder)
{
thread = null;
}
//スレッドによるSurfaceView更新処理
public void run()
{
while (thread != null)
{
//描画処理
Canvas canvas = holder.lockCanvas();
canvas.drawColor(Color.argb(255, 0, 0, 0));
int size = container.size();
// 簡易衝突計算
for(int i=0 ; i<size ; i++)
{
for(int j=0 ; j<size ; j++)
{
if(j<=i) continue;
Circle a = container.get(i);
Circle b = container.get(j);
float ab_x = b.x - a.x;
float ab_y = b.y - a.y;
float tr = a.radius + b.radius;
if(ab_x*ab_x + ab_y*ab_y < tr*tr)
{
float len = (float)Math.sqrt(ab_x*ab_x + ab_y*ab_y);
float distance = (a.radius + b.radius) - len;
if(len>0) len = 1 / len;
ab_x *= len;
ab_y *= len;
distance /= 2.0;
a.x -= ab_x * distance;
a.y -= ab_y * distance;
b.x += ab_x * distance;
b.y += ab_y * distance;
// 衝突後の速度を計算
float ma = (float) ((b.m / (a.m + b.m))*(1 + e)* ((b.dx - a.dx)*ab_x + (b.dy - a.dy)*ab_y));
float mb = (float) ((a.m / (a.m + b.m))*(1 + e)* ((a.dx - b.dx)*ab_x + (a.dy - b.dy)*ab_y));
a.dx += ma*ab_x;
a.dy += ma*ab_y;
b.dx += mb*ab_x;
b.dy += mb*ab_y;
}
}
}
// 計算をボールに反映
for(int i = 0 ; i < size ; i++)
{
Circle a = container.get(i);
a.dx *= d;
a.dy *= d;
a.dx += gx;
a.dy += gy;
a.x += a.dx;
a.y += a.dy;
if(a.x < a.radius)
{
a.x = a.radius;
a.dx *= -1;
}
if(a.y < a.radius)
{
a.y = a.radius;
a.dy *= -1;
}
if(a.x > width-a.radius)
{
a.x = width-a.radius;
a.dx *= -1;
}
if(a.y > height-a.radius)
{
a.y = height-a.radius;
a.dy *= -1;
}
paint.setColor(Color.argb(255, a.cr, a.cg, a.cb));
canvas.drawCircle(a.x, a.y, a.radius, paint);
}
holder.unlockCanvasAndPost(canvas);
}
}
// クリック時のイベント
public boolean onTouchEvent(MotionEvent event)
{
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
float x = event.getX();
float y = event.getY();
float dx = (float)(Math.random()*10-5);
float dy = (float)(Math.random()*10-5);
float ran = (float)Math.random();
float r = ran*20+5;
float m = ran*10+10;
int cr = 255;
int cb = (int)(255 * Math.random());
int cg = (int)(255 * Math.random());
container.add(new Circle(r, x, y, dx, dy, m, cr, cg, cb));
}
return true;
}
// 加速度の更新
public void setAcce(float gx, float gy)
{
this.gx = gx;
this.gy = gy;
}
}
Circle.javaのソース。
package jp.sakef;
public class Circle
{
public float radius;
public float x;
public float y;
public float dx;
public float dy;
public float m;
public int cr;
public int cg;
public int cb;
public Circle(float radius, float x, float y, float dx, float dy, float m, int cr, int cb, int cg)
{
this.radius = radius;
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.m = m;
this.cr = cr;
this.cg = cg;
this.cb = cb;
}
}
One Comment
Great tutorial!
Thank you.
Could you tell me how can I define a custom area instead of whole screen for the surfaceview?