Androidアプリ開発2回目は、前回とりあえず画面を表示したところからの続きです。
今回は横スクロールの描画を実装します。
- 使用言語 java
- 開発ツール AndroidStudio
まずは描画用の情報を保持するMapクラスを新規作成します。
package com.example.dbd.daybyday;
package com.example.dbd.daybyday;
public class Map {
private final int ROWCOUNT = 20; //縦表示ドット数
private final int COLUMNCOUNT = 10; //横表示ドット数
private float dotX; //1ドットの横幅
private float dotY; //1ドットの縦幅
//表示マップの2次元配列
private int[][] map = {
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};
//コンストラクタ
//画面サイズを引数で受け取り、ドットサイズを設定
public Map(float viewX, float viewY){
dotX = viewX / COLUMNCOUNT;
dotY = viewY / ROWCOUNT;
}
//以下はゲッター
public int getROWCOUNT() {
return ROWCOUNT;
}
public int getCOLUMNCOUNT() {
return COLUMNCOUNT;
}
public float getDotX() {
return dotX;
}
public float getDotY() {
return dotY;
}
public int[][] getMap() {
return map;
}
}
次にStageクラスに、Mapクラスを参照して描画を行う処理を追記していこうと思います。
前回お試しで書いてみたsurfaceCreatedメソッドの処理は一新します。
package com.example.dbd.daybyday;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class Stage extends SurfaceView implements SurfaceHolder.Callback {
Map map; //Mapクラスの参照保持変数
Paint paint; //Color保持用
SurfaceHolder holder; //ディスプレイ編集・監視用インターフェース
int[][] mapArray; //表示マップ保持用
//画面変更時の処理
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){}
//画面破棄時の処理
@Override
public void surfaceDestroyed(SurfaceHolder holder){}
//画面作成時の処理
@Override
public void surfaceCreated(SurfaceHolder holder){
//Mapクラスのインスタンス生成
//画面幅,画面高さを取得 コンストラクタの処理の後なら取得可能
map = new Map(super.getWidth(), super.getHeight());
mapArray = map.getMap(); //map配列取得
paint = new Paint();
paint.setColor(Color.GREEN);
draw(); //描画用自クラスメソッド
/*
Paint paint = new Paint();
paint.setColor(Color.GREEN);
Canvas canvas = holder.lockCanvas(); //画面をロックして
//画面幅いっぱいに高さ150の緑の部分を描画
canvas.drawRect(0,super.getHeight() - 150, super.getWidth(), super.getHeight(),paint);
//球も描画
paint.setColor(Color.RED);
canvas.drawCircle(super.getWidth() / 2, super.getHeight() - 180, 30,paint);
holder.unlockCanvasAndPost(canvas); //画面に変更反映
*/
}
//コンストラクタ
public Stage(Context context){
super(context);
//この処理でsurfaceCreatedメソッドが動きます
holder = super.getHolder();
holder.addCallback(this);
}
//描画用メソッド
private void draw(){
Canvas canvas = holder.lockCanvas(); //画面をロックして
//描画ドット数繰り返す
for(int y = 0; y < map.getROWCOUNT(); y++){
for(int x = 0; x < map.getCOLUMNCOUNT(); x++){
switch (mapArray[y][x]){
case 1: //配列の値が1の場合は
float drawX = x * map.getDotX(); //描画開始位置のX座標を設定
float drawY = y * map.getDotY(); //描画開始位置の座標を設定
canvas.drawRect(drawX,drawY,drawX + map.getDotX(), drawY + map.getDotY(),paint);
break;
}
}
}
holder.unlockCanvasAndPost(canvas); //画面に変更反映
}
}

最初の画面の表示はこれで出来ました。
次はこれを横に移動できるように処理を追記します。
画面をタッチすると横にマップが動くようにしたいので、onTouchEventメソッドを追加するのと、
メンバ変数に画面のタッチによって増減する変数を追加。
drawメソッドにも最初に画面をクリアにする処理を追加します。
package com.example.dbd.daybyday;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class Stage extends SurfaceView implements SurfaceHolder.Callback {
Map map; //Mapクラスの参照保持変数
Paint paint; //Color保持用
SurfaceHolder holder; //ディスプレイ編集・監視用インターフェース
int[][] mapArray; //表示マップ保持用
int xx = 0; //表示マップ横位置保持
//画面変更時の処理
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){}
//画面破棄時の処理
@Override
public void surfaceDestroyed(SurfaceHolder holder){}
//画面作成時の処理
@Override
public void surfaceCreated(SurfaceHolder holder){
//Mapクラスのインスタンス生成
//画面幅,画面高さを取得 コンストラクタの処理の後なら取得可能
map = new Map(super.getWidth(), super.getHeight());
mapArray = map.getMap(); //map配列取得
paint = new Paint();
paint.setColor(Color.GREEN);
draw(); //描画用自クラスメソッド
/*
Paint paint = new Paint();
paint.setColor(Color.GREEN);
Canvas canvas = holder.lockCanvas(); //画面をロックして
//画面幅いっぱいに高さ150の緑の部分を描画
canvas.drawRect(0,super.getHeight() - 150, super.getWidth(), super.getHeight(),paint);
//球も描画
paint.setColor(Color.RED);
canvas.drawCircle(super.getWidth() / 2, super.getHeight() - 180, 30,paint);
holder.unlockCanvasAndPost(canvas); //画面に変更反映
*/
}
//画面にタッチすると動くメソッド
@Override
public boolean onTouchEvent(MotionEvent event){
float touchX = event.getX(); //タッチ位置取得
//画面に指を置いたタイミングで条件分岐処理
switch (event.getAction()){
case MotionEvent.ACTION_DOWN :
//タッチ位置が画面の右側ならxxをインクリメント
if(touchX > super.getWidth() / 2){
xx++;
}else{ //それ以外はデクリメント
xx--;
}
break;
}
draw(); //画面再描画
return true;
}
//コンストラクタ
public Stage(Context context){
super(context);
//この処理でsurfaceCreatedメソッドが動きます
holder = super.getHolder();
holder.addCallback(this);
}
//描画用メソッド
private void draw(){
Canvas canvas = holder.lockCanvas(); //画面をロックして
canvas.drawColor(Color.WHITE); //画面をクリア
//描画ドット数繰り返す
for(int y = 0; y < map.getROWCOUNT(); y++){
for(int x = 0; x < map.getCOLUMNCOUNT(); x++){
switch (mapArray[y][x + xx]){ //xxが描画開始インデックスを調整
case 1: //配列の値が1の場合は
float drawX = x * map.getDotX(); //描画開始位置のX座標を設定
float drawY = y * map.getDotY(); //描画開始位置の座標を設定
canvas.drawRect(drawX,drawY,drawX + map.getDotX(), drawY + map.getDotY(),paint);
break;
}
}
}
holder.unlockCanvasAndPost(canvas); //画面に変更反映
}
}
今回はここまでなのですが、最後に画面のタイトルバーを消した方がゲーム画面が広くなるので、
MainActivityの継承クラスをAppCompatActivityからActivityにします。
さらに横スクロールゲームは横幅が広い方が今後やりやすそうなので、画面の向きを横向きに固定します。
AndroidManifest.xmlに少し追記します。
activity android:name=”.MainActivity”という所を
activity android:name=”.MainActivity” android:screenOrientation=”landscape”
にします。
ここで動作確認。

画面の右側をタップすると配列のインデックスが増減して、マップの横移動が起こるといった仕組みです。
現段階ではマップの端まで移動した時の分岐処理の実装がないので、端まで移動すると異常終了します。
次回はその実装と、画面に触れている間移動し続けるように改良していきます。



コメント