Hole クラスの作成
ボールのゴールとなるホールを作成します。要するに玉を転がして穴に落とそうというゲームです。Hole クラスは前回作成した Ball クラスとほぼ同じです。画面内での位置と半径を示すメンバ変数を持っています。
- "Hole.java"
public class Hole extends View{
int x, y, r;
Paint p;
public Hole(Context context){
super(context);
x = y = 0;
r = 40;
p = new Paint();
p.setColor(Color.BLACK);
p.setStyle(Paint.Style.FILL);
p.setAntiAlias(true);
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(x, y, r, p);
}
}
横画面固定にする
前回は縦画面固定に設定していましたが、実機に表示されるボールを操作しようとするとスマートフォンを両手に持つほうが操作しやすかったので、横画面固定に変更します。
- "AndroidManifest.xml"
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android= "http://schemas.android.com/apk/res/android" package="com.example.ball" > <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" android:screenOrientation="landscape" > <intent-filter> <action android:name= "android.intent.action.MAIN" /> <category android:name= "android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
画面内にボールを落とす穴を設置
次に MainActivity で Hole を実体化します。穴の位置は乱数を使ってランダムに発生させます。このとき画面の端っこに穴を設置してしまうとゲームの難易度が下がってしまうため、画面の端っこからは離して設置します。 下記のように乱数の発生範囲を限定しています。
Hole hole = new Hole(this); Random rnd = new Random(); hole.x = rnd.nextInt(width - (2 * (hole.r + ball.radius))) + hole.r + ball.radius; hole.y = rnd.nextInt(height - (2 * (hole.r + ball.radius))) + hole.r + ball.radius;
画面内に複数の図形を表示するため FrameLayout を使っています。FrameLayout は後に追加した View が上に重なるので Hole → Ball の順に addView() を実行します。
FrameLayout framelayout = new FrameLayout(this); framelayout.setBackgroundColor(Color.GREEN); setContentView(framelayout); … framelayout.addView(hole); framelayout.addView(ball);
run() メソッド内に、ボールが穴の上を通ったらボールを穴の中心に固定するコードを記述します。
if ((hole.x - hole.r < ball.x && ball.x < hole.x + hole.r) &&
(hole.y - hole.r < ball.y && ball.y < hole.y + hole.r)) {
ball.x = hole.x;
ball.y = hole.y;
ball.vx = ball.vy = 0;
ball.invalidate();
}
上記を踏まえ "MainActivity.java" を下記の通り書き換えます。
- "MainActivity.java"
public class MainActivity extends Activity implements Runnable, SensorEventListener {
SensorManager manager;
Ball ball;
Hole hole;
Handler handler;
int width, height, time;
float gx, gy, dpi;
FrameLayout framelayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
framelayout = new FrameLayout(this);
framelayout.setBackgroundColor(Color.GREEN);
setContentView(framelayout);
time = 10;
handler = new Handler();
handler.postDelayed(this, 3000);
WindowManager windowManager =
(WindowManager) getSystemService(WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
width = display.getWidth();
height = display.getHeight();
dpi = getResources().getDisplayMetrics().densityDpi;
ball = new Ball(this);
ball.x = width / 2;
ball.y = height / 2;
hole = new Hole(this);
Random rnd = new Random();
hole.x = rnd.nextInt(
width - (2 * (hole.r + ball.radius)))
+ hole.r + ball.radius;
hole.y = rnd.nextInt(
height - (2 * (hole.r + ball.radius)))
+ hole.r + ball.radius;
framelayout.addView(hole);
framelayout.addView(ball);
}
@Override
public void run() {
ball.vx += (float) (gx * time / 1000);
ball.vy += (float) (gy * time / 1000);
ball.x += dpi * ball.vx * time / 25.4;
ball.y += dpi * ball.vy * time / 25.4;
if (ball.x <= ball.radius) {
ball.x = ball.radius;
ball.vx = -ball.vx / 3;
} else if (ball.x >= width - ball.radius) {
ball.x = width - ball.radius;
ball.vx = -ball.vx / 3;
}
if (ball.y <= ball.radius) {
ball.y = ball.radius;
ball.vy = -ball.vy / 3;
} else if (ball.y >= height - ball.radius) {
ball.y = height - ball.radius;
ball.vy = -ball.vy / 3;
}
if ((hole.x - hole.r < ball.x &&
ball.x < hole.x + hole.r) &&
(hole.y - hole.r < ball.y &&
ball.y < hole.y + hole.r)) {
ball.x = hole.x;
ball.y = hole.y;
ball.vx = ball.vy = 0;
ball.invalidate();
} else {
ball.invalidate();
handler.postDelayed(this, time);
}
}
public void onDestroy() {
super.onDestroy();
handler.removeCallbacks(this);
}
@Override
protected void onResume() {
super.onResume();
manager = (SensorManager)getSystemService(
SENSOR_SERVICE);
List<Sensor> sensors =
manager.getSensorList(
Sensor.TYPE_ACCELEROMETER);
if (0 < sensors.size()) {
manager.registerListener(
this, sensors.get(0),
SensorManager.SENSOR_DELAY_NORMAL);
}
}
@Override
protected void onPause() {
super.onPause();
manager.unregisterListener(this);
}
@Override
public void onSensorChanged(SensorEvent event) {
gy = event.values[0];
gx = event.values[1];
}
@Override
public void onAccuracyChanged(
Sensor sensor, int accuracy) {
}
}
実機にインストールして実行すると、ボールが穴の上を通るとボールが穴の中心に固定されてゲームが終了する様子が確認できます。
以上でなんとか『ユーザが操作をして目標を達成する』というゲームとしての体裁が整いました。次は ActionBar のメニューを使ってゲームのリトライ処理を実装します。
HoleもBallと同じように傾けたら動くようにしたいのですが、Runメソッドの中身をHole内の変数にあうように複製したりしてもBallだけが動きます。 両方動くようにするためにはどうすればいいでしょうか?
返信削除