Androidプログラミング日記 (仮)  

虫?潰し!改

あなたは

人目のプログラマーだよ。

Androidプログラミング日記 (仮).

 

 

 

 

虫?潰し!改!!

前回、最後に色々書いた事をさっそく勉強してみました。まず画像を移動角度に合わせて回転させる方法。そして初期位置のランダム配置。おまけでオプションメニューの改造。同じようなプログラムでも全然違う仕様になっています。でもやはりちょっとでもいい感じにしようとするとコードは長くなりますし、壁がでてきたりします。しかし諦めずに考えていると、力技的なコードの書き方をひらめいたりします。「とりあえず趣味ですし動けばいいのだ。」と自分にいいきかせつつ完成させた逸品です。もっといいコードの書き方などあるはずなのですが今の私にはこれが精一杯です。前回などと比べるとゲームアプリでの骨格ぽいものはできてきたので、だいぶ時間の節約になりました。ここまで来るとやはりしっかりした内容や、効率的なコードや計算ができないと考える時間がいっぱいですね・・。

今回は

今回ファイルは4つ。Stringファイルも変更しました。画像も追加変更です。

   
 

ファイル名「String.xml」

<?xml version="1.0" encoding="utf-8"?>
<resources>

<string name="hello">Hello World, Box!</string>
<string name="app_name">Box</string>
<string name="menu_item0">終了</string>

</resources>
 
 

ファイル名「MushiTataki.java」


					
    						
 
 

ファイル名「MainView.java」

package and.roid.mushi;

import android.app.Activity;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.view.Display;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.Window;
import android.view.WindowManager;

public class MushiTataki extends Activity {
//登録するオプションメニューの種類3種
private static final int MENU_ITEM0=0;
private static final int COLOR_MENU_GROUP = Menu.FIRST + 1;
private static final int COLOR_WHITE_ID = Menu.FIRST + 1;
private static final int COLOR_BLACK_ID = Menu.FIRST + 2;
private static final int COLOR_GREY_ID = Menu.FIRST + 3;
private static final int COLOR_BLUE_ID = Menu.FIRST + 4;
private static final int COLOR_RED_ID = Menu.FIRST + 5;
private static final int COLOR_YELLOW_ID = Menu.FIRST + 6;

private int selected_color_id = COLOR_WHITE_ID;
public float disp_w,disp_h;//端末の画面の大きさを取得するための変数
private int col = Color.WHITE;
private boolean reset = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFormat(PixelFormat.TRANSLUCENT);
//端末の画面の大きさを取得するための処理
Window window = getWindow();
WindowManager manager = window.getWindowManager();
Display disp = manager.getDefaultDisplay();
disp_w = disp.getWidth();//端末の画面の横幅取得
disp_h = disp.getHeight();//端末の画面の縦幅取得

//BoxViewを直接レイアウトに指定
setContentView(new MainView(this));
}
//メニューボタンを押すと、セットされたメニューを表示
public boolean onCreateOptionsMenu(Menu menu){
super.onCreateOptionsMenu(menu);

//add(0,MENU_ITEM0,0,R.string.menu_item0)
//引数はグループID、アイテムID、優先順位、アイテムタイトルです
MenuItem item0 = menu.add(0,MENU_ITEM0,0,R.string.menu_item0);
//アイコン設定
item0.setIcon(R.drawable.ic_end);

SubMenu subMenuColor = menu.addSubMenu(Menu.NONE, COLOR_MENU_GROUP, 1,"背景色変更").setIcon(R.drawable.ic_haikei);
subMenuColor.add(COLOR_MENU_GROUP, COLOR_WHITE_ID, 0, "WHITE");
subMenuColor.add(COLOR_MENU_GROUP, COLOR_BLACK_ID, 0, "BLACK");
subMenuColor.add(COLOR_MENU_GROUP, COLOR_GREY_ID, 0, "GREY");
subMenuColor.add(COLOR_MENU_GROUP, COLOR_BLUE_ID, 0, "BLUE");
subMenuColor.add(COLOR_MENU_GROUP, COLOR_RED_ID, 0, "RED");
subMenuColor.add(COLOR_MENU_GROUP, COLOR_YELLOW_ID, 0, "YELLOW");
subMenuColor.setGroupCheckable(COLOR_MENU_GROUP, true, true);
subMenuColor.findItem(selected_color_id).setChecked(true);

 

return true;
}
//オプションメニューを選択した時の処理
public boolean onOptionsItemSelected(MenuItem item){
switch (item.getItemId()){
case MENU_ITEM0:
finish();//このメソッドでアプリ終了
return true;
case COLOR_WHITE_ID:
selected_color_id = COLOR_WHITE_ID;
setcolor(Color.WHITE);
return true;
case COLOR_BLACK_ID:
selected_color_id = COLOR_BLACK_ID;
setcolor(Color.BLACK);
return true;
case COLOR_GREY_ID:
selected_color_id = COLOR_GREY_ID;
setcolor(Color.GRAY);
return true;
case COLOR_BLUE_ID:
selected_color_id = COLOR_BLUE_ID;
setcolor(Color.BLUE);
return true;
case COLOR_RED_ID:
selected_color_id = COLOR_RED_ID;
setcolor(Color.RED);
return true;
case COLOR_YELLOW_ID:
selected_color_id = COLOR_YELLOW_ID;
setcolor(Color.YELLOW);
return true;
}
return true;
}

public void setcolor(int co){col = co;}
public int getcolor(){return col;}
public boolean getreset(){return reset;}
public void setreset(){reset = false;}
}
package and.roid.mushi;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
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 SurfaceHolder holder;
private Thread thread;
//ゲーム状態
private static final int GAME_START = 0;
private static final int GAME_PLAY = 1;
private static final int GAME_END = 2;
private static final int GAME_CLEAR = 3;
private int gamestate = 0;//ゲーム状態を表す変数
private String massage="";//画面に表示させるメッセージ
private float disp_w,disp_h;//画面の幅高さ
private MushiTataki mushitataki;
private GameGamen gamen;
private static final int//画面の飾りのための座標
gamen_x = 0,
gamen_y = 0;
private Bitmap img;

//コンストラクタ
public MainView(Context context) {
super(context);
holder = getHolder();
holder.addCallback(this);
holder.setFixedSize(getWidth(), getHeight());
init(context);
}
//初期化処理
public void init(Context context){
Resources resources = context.getResources();//画像登録準備
img = BitmapFactory.decodeResource(resources,R.drawable.mushirobo);//imgに画像登録
mushitataki = (MushiTataki)context;
disp_w = mushitataki.disp_w;//画面幅取得
disp_h = mushitataki.disp_h;//画面高さ取得
//BoxGamenクラスを作成
gamen = new GameGamen(gamen_x,gamen_y,(int)disp_w,(int)disp_h-30,img,gamestate);
}

//ループ:SurfaceViewのimprements Runnableを使用すると自動的に作成されるメソッド
public void run() {
Canvas c;
Paint p = new Paint();
p.setAntiAlias(true);

while(thread != null){
c = holder.lockCanvas();
c.drawColor(mushitataki.getcolor());
//ゲーム状態によってスイッチ処理
switch(gamestate){
case GAME_START:
massage = "TOUCH GAME START";
break;
case GAME_PLAY://プレイ中にゲームオーバーかクリアかの判定
massage = "";
if(gamen.getEnd()==true) {gamestate=2;gamen.setGameState(gamestate);}
if(gamen.getCla()==true) {gamestate=3;gamen.setGameState(gamestate);}
break;
case GAME_END:
massage = "虫の勝ち(´;ω;`)";
break;
case GAME_CLEAR:
massage = "GAMECLEAR!(*´∀`*)";
break;
}

gamen.draw(c, p,massage);

holder.unlockCanvasAndPost(c);

try {
Thread.sleep(50);
} catch (Exception e){}
}
}
//タッチ処理
public boolean onTouchEvent(MotionEvent event){
int action = event.getAction();
int x = (int)event.getX();
int y = (int)event.getY();
switch(action){
case MotionEvent.ACTION_DOWN:
switch(gamestate){
case GAME_START:
gamestate = 1;
gamen.setGameState(gamestate);
gamen.init(disp_w,img);
break;
case GAME_PLAY:
gamen.RectRect(x, y);
break;
case GAME_END:
gamestate = 0;
gamen.setGameState(gamestate);
gamen.setEnd();
gamen.GameOver();
break;
case GAME_CLEAR:
gamestate = 0;
gamen.setGameState(gamestate);
gamen.setCla();
break;
}
break;
case MotionEvent.ACTION_UP:
switch(gamestate){
case GAME_PLAY:
gamen.RectRect(x, y);
break;
}
break;
}
return true;
}

public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {}

public void surfaceCreated(SurfaceHolder arg0) {thread = new Thread(this);thread.start();}

public void surfaceDestroyed(SurfaceHolder arg0) {thread = null;}

}
 
 

ファイル名「GameGamen.java」

package and.roid.mushi;

import java.util.ArrayList;
import java.util.Date;
import java.util.Random;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;

public class GameGamen {
//ゲーム状態
private static final int GAME_START = 0;
private static final int GAME_PLAY = 1;
private static final int GAME_END = 2;
private static final int GAME_CLEAR = 3;
private int gamestate = 0;
private boolean endflg = false;//ゲームオーバーフラグ
private boolean claflg = false;//クリアフラグ
private static final double PIE = 3.1415926;
private int gamen_x,gamen_y,gamen_w,gamen_h;//画面の大きさ取得用
private ArrayList mushi = new ArrayList();
private Bitmap[] imgs = new Bitmap[4];
private int score = 0;//スコア用
private int kazu;//虫の数用
private int x,y;
private double angle;
private int vxy;

GameGamen(int x,int y,int w,int h,Bitmap img,int state){
gamestate = state;
gamen_x = x;
gamen_y = y;
gamen_w = w;
gamen_h = h;
//一旦ここで画像を4つに分ける
for(int i=0;i<imgs.length;i++){
imgs[i] = Bitmap.createBitmap(img, i*(img.getWidth()/4), 0, img.getWidth()/4, img.getHeight());
}
}
//初期化
public void init(float w,Bitmap img){
//ここから虫の初期位置決め
Random r = new Random(new Date().getTime());
kazu = r.nextInt(20)+5;//虫の数決め
//kazu分だけ虫を作成
int wh = r.nextInt(80)+50;//虫大きさ
for(int i=0;i<kazu;i++){
r = new Random(new Date().getTime());
//0から100のランダムを作成
int random = r.nextInt(100);
//画面上下左右4箇所の場所をランダムで出た数25%づつで決定
if(random <= 100){ x = gamen_x - wh; y = r.nextInt(gamen_h);}
if(random <= 75){ x = gamen_w + wh; y = r.nextInt(gamen_h);}
if(random <= 50){ x = r.nextInt(gamen_w); y = gamen_y - wh;}
if(random <= 25){ x = r.nextInt(gamen_w); y = gamen_h + wh;}
//スタート時の虫の出発角度、初期は中心に向かう
angle = Math.atan2(gamen_h / 2 - y, gamen_w / 2 - x);
vxy = r.nextInt(10)+5;//補正虫移動量用
//int d = r.nextInt(360);//虫移動方向
//double rad = toRadian((d+360)%360);//虫移動角度をラジアンに変換
double xspeed = (int) (Math.cos(angle)*vxy);//移動了確定
double yspeed = (int) (Math.sin(angle)*vxy);//移動量確定
int count = r.nextInt(500);//虫出発用カウント
//虫作成
mushi.add(new Mushi(count,x,y,wh,angle,vxy,xspeed,yspeed,imgs));
score = 0;
}
}
//表示
public void draw(Canvas c, Paint p,String massage){
switch(gamestate){
case GAME_START:
p.setTextSize(30);
p.setColor(Color.GREEN);
c.drawText(massage, gamen_x/2, gamen_h/2, p);
break;
case GAME_PLAY:
for(int i=0;i<mushi.size();i++){
c.save();//画像回転用にここまでの表示を保存
mushi.get(i).draw(c, p);
c.restore();//ここで保存解除
mushi.get(i).setAnime();
mushi.get(i).move();
if(mushi.get(i).getDead() == 0) mushi.get(i).Henkan(vxy);
mushi.get(i).SotoNaka(gamen_x, gamen_y, gamen_w, gamen_h);
if(mushi.get(i).getEnd() == true) endflg=true;
if(mushi.get(i).getDead() > 20) ++score;
if(mushi.get(i).getDead() > 20 || endflg==true) Reset(i);
if(score == kazu) claflg=true;
}
break;
case GAME_END:
p.setTextSize(30);
p.setColor(Color.RED);
c.drawText(massage, gamen_x, gamen_h/2, p);
break;
case GAME_CLEAR:
p.setTextSize(30);
p.setColor(Color.RED);
c.drawText(massage, gamen_x, gamen_h/2, p);
break;
}

p.setTextSize(30);
p.setColor(Color.BLACK);
c.drawText("SCORE:"+score, 10, 30, p);

}
public void GameOver(){
mushi.clear();
}
//虫消去用
public void Reset(int index){
mushi.remove(index);
}
//タッチ座標と虫座標を比べて潰したか否か
public void RectRect(int x, int y){
for(int i=0;i<mushi.size();i++){
if(mushi.get(i).Hanni(x, y) == true) mushi.get(i).setSpeed();
}
}
//ラジアン変換
public double toRadian(double deg){return (deg * PIE / 180);}

//ゲーム状態セット用
public void setGameState(int state){gamestate = state;}

//ゲームオーバー確認用
public boolean getEnd(){return endflg;}

//ゲームクリア確認用
public boolean getCla(){return claflg;}

//ゲームオーバー確定用
public void setEnd(){endflg=false;}

//ゲームクリア確定用
public void setCla(){claflg=false;}
}
 
 

ファイル名「Mushi.java」

package and.roid.mushi;
import java.util.Date;
import java.util.Random;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;

public class Mushi {
private static final double PIE = 3.1415926;
private double obj_x,obj_y;//オブジェクト座標
private int obj_wh;//オブジェクト大きさ
private double obj_xspeed,obj_yspeed;//移動量
private Drawable[] image2;
private int anime;//アニメーション用
private int deadcount;//虫死亡用カウント
private boolean dead;//虫死亡フラグ
private int count;//虫出発用カウンタ
private float rad;//移動角度

private int obj_owh;//キャラ中心座標補正用
private boolean start,end;//画面内外の判定用
private boolean tenkan;//方向転換フラグ

//コンストラクタ
Mushi(int cou,double x,double y, int wh, double d, int vxy,double xspeed,double yspeed,Bitmap[] img){
count = cou;
dead = false;
anime = 0;
rad = (float) d;
obj_x = x;
obj_y = y;
obj_xspeed = xspeed;
obj_yspeed = yspeed;
deadcount = 0;
obj_wh = wh;
obj_owh = wh/2;
start = false;
end = false;
tenkan = false;
image2 = new Drawable[4];
//画像を受け取り振り分ける
for(int i=0;i<image2.length;i++){
image2[i] = new BitmapDrawable(img[i]);
}
}
//表示
public void draw(Canvas c, Paint p){
//Drawableでセットされた画像を、座標補正(画像の中心をxy座標にする)をして表示。
image2[anime].setBounds((int)obj_x-obj_owh,(int)obj_y-obj_owh,(int)obj_x+obj_wh-obj_owh,(int)obj_y+obj_wh-obj_owh);
c.rotate(toDeg(rad,90), (int)obj_x, (int)obj_y);//角度によって画像を回転。移動方向に向くように
image2[anime].draw(c);
}
//移動
public void move(){
if(count <= 0){
obj_x += obj_xspeed;
obj_y += obj_yspeed;
if(dead==true) ++deadcount;
}
--count;//出発用、0になったら移動開始
}
//虫生き残り用ゲームオーバー用
public boolean getEnd(){return end;}

//虫死亡用
public boolean getDeadtrue(){return dead;}

//虫死亡用
public int getDead(){return deadcount;}

//虫アニメーション用
public void setAnime(){if(dead==false){anime=(++anime)%3;}else{anime=3;}}

//虫座標と渡された座標の比較
public boolean Hanni(int x1, int y1){return (x1>obj_x && x1obj_y && y1<obj_y+obj_wh);}

//虫死亡時の処理用
public void setSpeed(){obj_xspeed=0;obj_yspeed=0;dead=true;}

//ラジアン変換
public double toRadian(double deg){return (deg * PIE / 180);}

//角度度数変換
public float toDeg(float rad,int hosei){
//移動方向のラジアン値を度に変換
//移動方向は右が0度、画像は上が0度なので+90で補正し合わせる
float r = (float) (rad * 180 / PIE) + hosei;
return (float) ((r+360)%360);//360度の範囲を超えないように補正
}
//無理やり外出た条件文
//画面と虫画像を箱とみなして箱の当たり判定
//虫初期位置が画面外なので、画面内(画面と虫の衝突)にはいったらまずstartをtrueに
//start==trueなら次は外に出た判定だが、すでに中に入っている状態(衝突状態)なので
//衝突していればendはfalseのまま。衝突していなければendはtrueで、外に出たと判定。
public void SotoNaka(int gx, int gy, int gw, int gh){
//if(obj_x+obj_wh >gx || obj_x>gw || obj_y+obj_whɘ || obj_y>gh) start = true;
if(obj_x < gw && gx < obj_x+obj_wh && obj_y < gh && gy < obj_y+obj_wh) start = true;
if(start == true){
if(obj_x < gw && gx < obj_x+obj_wh && obj_y < gh && gy < obj_y+obj_wh) {end = false;}else{end = true;}
}
}
//虫の方向転換は1回だけ。画面内に虫がいて方向転換をまだしてないなら
//テキトーな短形範囲内に入ったらとりあえずランダムで0から360を出す
//もともとの虫の移動角度とランダム値の差が10度以上あるなら10度に設定
//これを元の移動角度に加えて、もう一度ラジアン値に戻し、移動量を再計算
public void Henkan(int vxy){
Random r = new Random(new Date().getTime());
if(tenkan == false && start == true){
if(obj_x < 300 && 200 < obj_x+obj_wh && obj_y < 630 && 200 < obj_y+obj_wh){
tenkan = true;
float d = r.nextInt(360);
rad = (float) (rad * 180 / PIE);
if(Math.abs(rad-d) > 10) rad += 10;
rad = (float) toRadian((rad+360)%360);
obj_xspeed = (int) (Math.cos(rad)*vxy);//移動了確定
obj_yspeed = (int) (Math.sin(rad)*vxy);//移動量確定
}
}
}

}
 
   
     

う〜む

操作やルールは前回と同じです。

画像の回転ですが、基本的にわたしの解釈ではCanvasを使用するとCanvasごと回転する感じですね。指定がなければCanvasの0、0の所から回転しているように見えたからです。c.rotate(toDeg(rad,90), (int)obj_x, (int)obj_y)で引数が角度、回転の中心座標、ということで画像の中心座標を普段の画像にすることで簡単に画像だけが回転してくれました。きをつけないといけないのが、このやり方だけだと画面全体が回転してしまうので、このクラスをDrawする上位クラスの所でsave()メソッドとrestore()メソッドでDrawを囲っています。これはsave()の時点でそれまでの画面状態を保存してrestore()で保存解除をしています。詳しくは別ページに画像色々ページを儲けようと思います。

あと今回はまず虫をランダムに表示させる試みをしてみました。画面上下左右4箇所なので四分の一の確率で算出して、座標が決まると画面の中心に虫を向かわせるようにしました。虫が出発するとある一定の短形範囲に虫がくると、最高10度ほどの角度の変更をします。今回はこんな感じです。

オプションメニューはサブメニューというものをやってみました。

まだまだ行き当たりばったりのプログラミングをしています。今回も本当はまだ問題があります。ランダムで出発時間を遅延しているのですが、座標も遅延も合ってしまって虫が重なったり。やはりランダムだけに頼るとなってほしくない現象が多い気がします。まだまだ勉強がたりないわたくしでした。

Androidプログラミング日記 (仮) | サイトマップ | 個人情報保護方針 | 応援メールテヘペロ | ©2012 Japan  相互リンク大募集中です