[PR] この広告は3ヶ月以上更新がないため表示されています。
ホームページを更新後24時間以内に表示されなくなります。

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

シューティング作るぞ!> 第四回 弾の種類

あなたは

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

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

 

share
 

 

 

弾の種類

今回は弾の種類をふやしてみようと思いました。通常の1発だけでる弾、一度に2発出る弾、一度に360度10発出る弾の3種類ほど作ってみました。ちょっとだけ考えてしまうことが今回作成していてありました。弾自体を持つべきは本当は自機なのではないのかと。なので自機クラス内に弾クラスを作成させたほうがいいのかな?とか思ったのですが、敵機のことや、判定、なによりもややこしくなりそうなので「イヤ、ソンナコトハナイ」と言い聞かせてなっとくしました。

それと弾クラス内で弾の種類を作成したほうがいいのかとかも思いましたが、よく考えたら表示も移動も自機と一緒にやっています。当然敵機も敵機の弾も一気に表示することになるでしょう。そうすると同じ画面のオブジェクトとして弾を扱う方が、いろいろと判定とかが簡単になると思ったのでこういうやりかたにしました。

今回も結構色々と追加変更があります。

この回では、特別新しいコードなどは書いていません。

追加といっても弾の種類を作成させるメソッドくらいが目新しいメソッドです。それと弾の移動方向を変化させたいので、角度による移動の計算式に変更しました。

 

ファイル名「MainLoop.java」

001/*
002 * SurfaceViewをextendsさせたクラス
003 * メインループなどはここにあります
004 * 基本的にSurfaceViewを使用した時には決まった変数の使用
005 * などがあるので、そういう変数やメソッドの末尾に
006 * ”お決まり”と書いておきます
007 */
008package and.roid.shooting2;
009 
010import java.util.ArrayList;
011 
012import android.content.Context;
013import android.content.res.Resources;
014import android.graphics.Bitmap;
015import android.graphics.BitmapFactory;
016import android.graphics.Canvas;
017import android.graphics.Color;
018import android.graphics.Paint;
019import android.graphics.Rect;
020import android.graphics.drawable.Drawable;
021import android.util.AttributeSet;
022import android.view.MotionEvent;
023import android.view.SurfaceHolder;
024import android.view.SurfaceView;
025 
026public class MainLoop extends SurfaceView implements SurfaceHolder.Callback,Runnable{
027    private SurfaceHolder holder;//お決まり
028    private Thread thread;//お決まり
029 
030    //どのActivityを使用しているかのための変数
031    private Shooting2Activity s2a;
032    private Mesod ms;
033    private float disp_w,disp_h;
034    private Drawable jikiimg,tamaimg;
035    private Bitmap jikibit,tamabit;
036    //弾用、連続で重ならないようにの変数
037    private boolean tamaflg;
038    private int tamatime;
039    //弾変化ボタン用
040    private Rect tamabtn;
041 
042 
043    /*
044     * 今回画面に表示させるオブジェクトは自機と弾です
045     * 全然違うオブジェクトですがObjectクラスをextends
046     * することで同じような変数として扱うことができます。
047     * 自機は一つのオブジェクトだけですが、弾はたくさん
048     * 表示します。しかし数は決まっていません。なので
049     * ArrayListを使用することにしました。
050     * 普通の配列変数では、いくつ配列を使用するかを決めないと
051     * いけませんが、ArrayListの場合は使用したいときに配列的な
052     * ものをいくつでも作成することができます。
053     */
054    private ArrayList<Object> object = new ArrayList();
055 
056    //コンストラクタが二つあるけど気にしないように
057    //こちらのコンストラクタは、自前でViewを実装するときに
058    //呼ばれるコンストラクタっぽい
059    public MainLoop(Context context) {
060        super(context);
061        init(context);
062    }
063    //こちらはxml方式でViewを呼び出すときに呼ばれるぽい
064    public MainLoop(Context context, AttributeSet attrs) {
065        super(context, attrs);
066        init(context);
067    }
068 
069    public void init(Context context){
070        holder = getHolder();//お決まり
071        holder.addCallback(this);//お決まり
072        holder.setFixedSize(getWidth(), getHeight());//お決まり
073        /*
074         * s2a = (Shooting2Activity)context;
075         * よくわからないけども、私的には使用している(今現在
076         * 画面に表示させている)アプリの何か色々なものをcontext
077         * として受け取り、それがいまどのActivityのやつかとか
078         * そんな感じの雰囲気と思います。(キニシテナイ)
079         */
080        s2a = (Shooting2Activity)context;
081        ms = new Mesod();
082        disp_w = s2a.disp_w;
083        disp_h = s2a.disp_h;
084 
085        Resources resources = context.getResources();//画像登録準備
086        //ビットマップ方式で画像取り込み
087        //ビットマップで取り込む理由として、使用したい大きさなどに変換できるので
088        Bitmap img= BitmapFactory.decodeResource(resources,and.roid.shooting2.R.drawable.jiki);
089        //ここで画像分割
090        //わざわざ画像の大きさをgetWidthgetHeightを使用するのは
091        //確実に大きさを図って分割するため
092        jikibit = Bitmap.createBitmap(img,0,0,img.getWidth()/2,img.getHeight());
093        tamabit = Bitmap.createBitmap(img,img.getWidth()/2,0,img.getWidth()/2,img.getHeight());
094 
095        /*
096         * Onjectクラスではインスタンス(実装)できないので
097         * ObjectクラスをextendsさせたJikiクラスを実装
098         * ArrayListを使用しているため、addでインスタンスしています
099         * メソッドなどを使用する場合はget(インデックス).でメソッドなど
100         * 色々呼び出したりします。
101         * 今回はArrayListの0番目の要素に自機が入っています
102         */
103        object.add(new Jiki(disp_w,disp_h));
104        object.get(0).Oint(jikibit, 240, 425, 0, 0, jikibit.getWidth(), jikibit.getHeight(),0);
105 
106        tamaflg = true;
107        tamatime = 5;
108 
109        //弾ボタン用座標
110        tamabtn = new Rect(50,50,100,100);
111 
112    }
113 
114    //implements Runnableを実装するとこのメソッドが自動追加
115    //ここがメインループとなります
116    public void run() {//お決まり
117        Canvas c;
118        Paint p = new Paint();
119        p.setAntiAlias(true);
120 
121        while(thread != null){
122            c = holder.lockCanvas();//お決まり
123 
124            c.drawColor(Color.BLACK);
125 
126            //弾変化ボタン
127            p.setColor(Color.BLUE);
128            c.drawRect(tamabtn, p);
129            p.setTextSize(30);
130            c.drawText("tama:"+object.get(0).tamajoutai, 50, 150, p);
131 
132            /*
133             * 自機も弾も同じObjectの要素を持っているので
134             * インスタンス(実装)時に作成したいクラスを
135             * 指定しておけば、同じObjectクラスとして使用
136             * することができます。わざわざ各オブジェクトの
137             * メソッドを呼ぶのではなく、共通しているObjectの
138             * メソッドを呼ぶことで解決しています
139             */
140            for(int i=0;i<object.size();i++){
141                object.get(i).ODraw(c);
142                object.get(i).OMove();
143                /*
144                 * 弾が画面外に出たらオブジェクト要素を消去します
145                 */
146                if(object.get(i).Ogetdead()==true) object.remove(i);
147            }
148            /*
149             * 弾を1發打ったら連続で打てないようになり、タイマー
150             * が0になったら打てるようにしています。
151             */
152            if(tamaflg == false){
153                --tamatime;
154                if(tamatime < 0){
155                    tamatime = 5;
156                    tamaflg = true;
157                }
158            }
159 
160 
161            holder.unlockCanvasAndPost(c);//お決まり
162 
163            try {
164                Thread.sleep(50);//お決まり
165            } catch (Exception e){}
166        }
167    }
168 
169    public boolean onTouchEvent(MotionEvent event){
170        int action = event.getAction();
171        int x = (int)event.getX();
172        int y = (int)event.getY();
173        switch(action){
174        case MotionEvent.ACTION_DOWN:
175            /*
176             * 弾を打っていいよ状態であり自機画像範囲にタップしていれば
177             * Jitama(弾クラス)を作成する
178             */
179            if(tamaflg == true && ms.RectTap(
180                    x, y, object.get(0).OgetTapRect()) == true){
181                /*
182                 * 今回は弾を出すような操作をすると
183                 * どんな弾を作成するかのメソッドを
184                 * 呼び出します
185                 */
186                Tamajoutai();
187                tamaflg = false;
188            }
189            /*
190             * 弾状態を変化させるボタン
191             * 本来ならアイテムなどで弾の飛び方などの
192             * 変化をさせるものでしょうけども、今回は
193             * テストということで青短形タップで変化します
194             */
195            if(ms.RectTap(x, y, tamabtn)==true){
196                ++object.get(0).tamajoutai;
197                object.get(0).tamajoutai = (object.get(0).tamajoutai+3)%3;
198            }
199            break;
200        case MotionEvent.ACTION_UP:
201            break;
202        case MotionEvent.ACTION_MOVE:
203            /*
204             * 一応自機の移動もできますが、移動と弾発射は同時にはまだ
205             * できません
206             */
207            if(ms.RectTap(x, y, object.get(0).OgetTapRect()) == true) object.get(0).OMove(x, y);
208 
209            break;
210        }
211        return true;
212    }
213 
214    /*
215     * Objectクラスに弾の状態を表す変数を用意しています
216     * それを外部から操作して変化させることでここのif文
217     * の分岐に割り当てられます
218     */
219    public void Tamajoutai(){
220        //通常の1発だけ
221        if(object.get(0).tamajoutai == 0){
222            object.add(new JiTama(disp_w,disp_h));
223            object.get(object.size()-1).Oint(
224                    tamabit, object.get(0).cx, object.get(0).cy-jikibit.getHeight(),
225                    0, 30, tamabit.getWidth(), tamabit.getHeight(),0);
226        }
227        //2発並んで
228        if(object.get(0).tamajoutai == 1){
229 
230            object.add(new JiTama(disp_w,disp_h));
231            object.get(object.size()-1).Oint(
232                    tamabit, object.get(0).cx-20, object.get(0).cy-jikibit.getHeight(),
233                    0, 30, tamabit.getWidth(), tamabit.getHeight(),0);
234            object.add(new JiTama(disp_w,disp_h));
235            object.get(object.size()-1).Oint(
236                    tamabit, object.get(0).cx+20, object.get(0).cy-jikibit.getHeight(),
237                    0, 30, tamabit.getWidth(), tamabit.getHeight(),0);
238        }
239        /*
240         * 36度づつ自機の周りに10発一気に出ます
241         */
242        if(object.get(0).tamajoutai == 2){
243            for(int i=0;i<10;i++){
244            object.add(new JiTama(disp_w,disp_h));
245            object.get(object.size()-1).Oint(
246                    tamabit, object.get(0).cx, object.get(0).cy,
247                    30, 30, tamabit.getWidth(), tamabit.getHeight(),i*(360/10));
248            }
249        }
250    }
251 
252    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {}//お決まり
253 
254    public void surfaceCreated(SurfaceHolder arg0) {thread = new Thread(this);thread.start();}//お決まり
255 
256    public void surfaceDestroyed(SurfaceHolder arg0) {thread = null;}//お決まり
257 
258}
259 
260 
261 
262                        

ファイル名「Object.java」

01/*
02 * ほとんどのメソッドをabstractにしました
03 * これによって自機クラス弾クラスと違うオブジェクト
04 * によってメソッド名は同じでそれぞれ違う固有の命令
05 * を書くことができます
06 *
07 * このクラスは画面に表示させるオブジェクト
08 * を作成させるためのクラスです
09 * このクラス単体ではインスタンス(オブジェクト化、実体化)
10 * はできず、このクラスをextendsさせて使うようにしています
11 * 理由としてはこのゲームを作成していくうちにわかってきますが
12 * コード表記がラクチンになります
13 */
14package and.roid.shooting2;
15 
16import android.graphics.Bitmap;
17import android.graphics.Canvas;
18import android.graphics.Color;
19import android.graphics.Paint;
20import android.graphics.Rect;
21import android.graphics.drawable.BitmapDrawable;
22import android.graphics.drawable.Drawable;
23 
24public abstract class Object {
25    public Mesod ms = new Mesod();
26    public float disp_w,disp_h;
27    //オブジェクトの画像
28    public Drawable img;
29    //オブジェクトの中心座標
30    public float cx,cy;
31    //向かおうとしている座標
32    public float vx,vy;
33    //オブジェクトの移動スピード
34    public float spx,spy;
35    //オブジェクトの大きさ
36    public int imgw,imgh;
37    //オブジェクトを消去させるための変数
38    public boolean dead;
39    //弾の状態
40    public int tamajoutai;
41    //弾画像の角度
42    public int tamar;
43 
44    public Object(){}
45    public Object(float dw,float dh){
46        disp_w = dw;
47        disp_h = dh;
48    }
49    /*
50     * このクラスに各オブジェクトに必要なメソッドを書いても
51     * いいのですが、どのオブジェクトにどのメソッドを使用して
52     * いるかなどの管理も大変なので、こういうメソッドを持って
53     * いますよ的なことだけかいておきます(abstract)
54     * 作ろうとしている弾クラスには、タップ座標はいらないので
55     * メソッドのオーバーライドで登録
56     */
57    //画像表示
58    public abstract void ODraw(Canvas c);
59    //初期設定
60    public abstract void Oint(Bitmap imgb,float x,float y, float sx,float sy,int w,int h);
61    public abstract void Oint(Bitmap imgb,float x,float y, float sx,float sy,int w,int h,int tj);
62    public abstract void OMove();
63    public abstract void OMove(int x,int y);
64    public abstract Rect OgetTapRect();
65    /*
66     * オブジェクトによって調べたい範囲が違うので引数で指定して調べるように変更
67     */
68    public boolean OsotoX(int ww){return (cx-ww<0 || cx+ww>disp_w);}
69    public boolean OsotoY(int hh){return (cy-hh<0 || cy+hh>disp_h);}
70    public boolean Ogetdead(){return dead;}//表示していいかどうかを返す
71}
72 
73                        

ファイル名「Jiki.java」

01/*
02 * 自機クラス
03 * Objectクラスをextendsしています
04 */
05package and.roid.shooting2;
06 
07import android.graphics.Bitmap;
08import android.graphics.Canvas;
09import android.graphics.Rect;
10import android.graphics.drawable.BitmapDrawable;
11 
12public class Jiki extends Object{
13 
14    public Jiki(){}
15    public Jiki(float dw,float dh){
16        super(dw,dh);
17    }
18    //初期設定
19    public void Oint(Bitmap imgb,float x,float y, float sx,float sy,int w,int h,int tj){
20        img = new BitmapDrawable(imgb);
21        cx = ms.setSizeX(disp_w, x);
22        cy = ms.setSizeY(disp_h, y);
23        spx = sx;
24        spy = sy;
25        imgw = w;
26        imgh = h;
27        dead = false;
28        //弾の初期状態を受け取ります
29        tamajoutai = tj;
30    }
31    public void ODraw(Canvas c){
32        /*
33         * 画像を表示させていいかどうかdead状態を調べて表示
34         * cx,cyは中心座標のため、画像の中心にちゃんとcx,cyがくるように調整
35         */
36        if(dead == false){
37            img.setBounds((int)(cx-imgw/2),(int)(cy-imgh/2),
38                    (int)(cx+imgw/2),(int)(cy+imgh/2));
39            img.draw(c);
40        }
41    }
42    /*
43     * 今回はここは使用していません
44     */
45    public void OMove(int x, int y) {
46        float cxx = cx;
47        float cyy = cy;
48        cx = x;
49        cy = y;
50 
51        if(OsotoX(imgw/2)==true) cx = cxx;
52        if(OsotoY(imgh/2)==true) cy = cyy;
53    }
54    public void OMove() {}
55 
56    /*
57     * タップ範囲にオブジェクトがあると移動できるようにしてます。。
58     * しかしタップして動かしてみると、指の動きにオブジェクトが
59     * ついてこれない場合があります。そのためオブジェクトの大きさを
60     * タップ時のみ大きくしてある程度オブジェクトから離れても
61     * ついてこれるようにごまかしています。
62     */
63    public Rect OgetTapRect(){
64        Rect taprect = new Rect(
65                img.getBounds().left-50,img.getBounds().top-50,
66                img.getBounds().right+50,img.getBounds().bottom+50);
67        return taprect;
68        }
69    @Override
70    public void Oint(Bitmap imgb, float x, float y, float sx, float sy, int w,int h) {}
71}
72 
73 
74                        

ファイル名「JiTama.java」

01/*
02 * 弾の種類で後方や横など飛び方が異なる
03 * 飛び方もさせたいので、飛ぶ方向を角度
04 * で設定。それに合わせて画像の角度も変化
05 * させるようにしました
06 */
07package and.roid.shooting2;
08 
09import android.graphics.Bitmap;
10import android.graphics.Canvas;
11import android.graphics.Rect;
12import android.graphics.drawable.BitmapDrawable;
13 
14public class JiTama extends Object{
15    /*
16     * 弾と画像の角度
17     */
18    public int tamakakudo;
19 
20    public JiTama(){}
21    public JiTama(float dw,float dh){
22        super(dw,dh);
23    }
24    public void ODraw(Canvas c){
25        /*
26         * 画像を回転させるにあたり、rotateのみでやろうとすると
27         * 別の画像も一緒に回転してしまいおかしくなります。
28         * そこでsave()でいったん以前の画像を保存しておき、表示
29         * し終わったらrestore()で開放すると手法で、ちゃんと表示
30         * するようになります
31         *
32         * 画像を表示させていいかどうかdead状態を調べて表示
33         * cx,cyは中心座標のため、画像の中心にちゃんとcx,cyがくるように調整
34         */
35        if(dead == false){
36            c.save();
37            img.setBounds((int)(cx-imgw/2),(int)(cy-imgh/2),
38                    (int)(cx+imgw/2),(int)(cy+imgh/2));
39            c.rotate(tamar, cx, cy);
40            img.draw(c);
41            c.restore();
42        }
43    }
44    /*
45     * 角度で弾を飛ばすようにしています
46     * 画像角度と移動角度は-90度ほどの誤差があるので
47     * 調整
48     * 例えば角度0度でここもそのままの角度0度にすると
49     * 画像表示は正常だが飛ぶ方向が右方向に・・・
50     */
51    public void OMove() {
52        cx += (float) Math.cos(ms.toRadian(tamar-90)) * spx;
53        cy += (float) Math.sin(ms.toRadian(tamar-90)) * spy;
54        /*
55         * 範囲外にでたら弾消してくださいの合図
56         */
57        if(OsotoX(-imgw/2)==true) dead = true;
58        if(OsotoY(-imgh/2)==true) dead = true;
59    }
60 
61    public void OMove(int x, int y) {}
62    public Rect OgetTapRect() {return null;}
63    //初期設定
64    @Override
65    public void Oint(Bitmap imgb, float x, float y, float sx, float sy, int w,int h, int r) {
66        img = new BitmapDrawable(imgb);
67        cx = ms.setSizeX(disp_w, x);
68        cy = ms.setSizeY(disp_h, y);
69        spx = sx;
70        spy = sy;
71        imgw = w;
72        imgh = h;
73        dead = false;
74        tamar = r;
75    }
76    @Override
77    public void Oint(Bitmap imgb, float x, float y, float sx, float sy, int w,int h) {}
78 
79}
80 
81 
82                        

ファイル名「Mesod.java」

01/*
02 * 無理やり作った汎用メソッド群
03 * なんども使用するようなメソッドをかためて置いて
04 * おきたかったので無理やりつくったクラス
05 */
06 
07package and.roid.shooting2;
08 
09import android.graphics.Rect;
10 
11public class Mesod {
12    /*
13     * わたくしの環境がXPERIAでその画面幅でアプリを作成
14     * しているのでそれから各アプリの画面幅に合うように調整
15     * させるための変数
16     */
17 
18    static public final float XPERIA_W = 480f;
19    static public final float XPERIA_H = 854f;
20    //せっかくなので0も固定値に
21    static public final float ZERO = 0f;
22    private static final double PIE = 3.1415926;
23 
24 
25    /*
26     * sin,cosなどを使用するときに入れ込む数値は
27     * 3.14を半周とした数値を180で割ったラジアン値
28     * というものを使用しなければなりません。
29     * (1周3.14×2=6.28を360で割った数値)
30     * 角度設定などは度数で出したほうが簡単なので設定は
31     * 度数でして、使用するときにここのメソッドでラジアン値
32     * に変換しています
33     */
34    public double toRadian(double deg){return (deg * PIE / 180);}
35    /*
36     * 受け取ったxy座標と調べたい短形範囲が重なっているかいないか
37     */
38    public boolean RectTap(int x,int y,Rect gazou){
39        return gazou.left < x && gazou.top < y && gazou.right > x && gazou.bottom > y;
40    }
41    /*
42     * この2行で各座標を実装機種の画面比に合わせます
43     */
44    public int setSizeX(float disp_w,float zahyou){return (int) (zahyou * (disp_w / XPERIA_W));}
45    public int setSizeY(float disp_h,float zahyou){return (int) (zahyou * (disp_h / XPERIA_H));}
46}
47 
48                        

 

無事成功しました!

 

無事に打ち分けができました。

これの応用で、画像を変えたり、以前作成したちょっとだけポリモーフィズムの応用で誘導弾にしたりもできますね。

今回思ったことは、クラス作成てしっかりしていないとグダグダになるってことですね。もうすでにグダグダなんですが、自分で思っているクラス要素、何を持たせるかなどわけがわからなくなります。今回のことでいえば、弾の種類はどこで作成するかってとこで迷いました。普通に考えると弾の種類だから弾クラスに・・いやいや弾を持ってて操作するのは自機なので自機で・・とか考えて、たどりついたのが「色々と一番簡単に書ける場所に書こう」ということで今回のMainLoopに書く事になりました。

まぁあまだテスト段階ですし・・・あとで直せばいいですよね・・・;;

 

<戻る   次へ>

 

 

 

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