チュートリアル:Android向けの最もシンプルな2Dゲームの作成

このチュートリアルは、主にAndroidの開発の初心者を対象としていますが、より経験豊富な開発者には役立つかもしれません。 ゲームエンジンを使用せずに、Android上で最も単純な2Dゲームを作成する方法を説明します。 このために、Android Studioを使用しましたが、他の適切なカスタマイズされた開発環境を使用できます。



ステップ1.ゲームのアイデアを思いつく

たとえば、かなり単純なアイデアを考えてみましょう。



画面の下部には宇宙船があります。 対応するボタンを押すと、左右に移動できます。 小惑星は垂直に下に移動します。 画面の幅全体に表示され、異なる速度で移動します。 船はmet石をできるだけ避けなければなりません。 met石がヒットした場合、ゲームオーバーです。







ステップ2.プロジェクトを作成する

Android Studioのトップメニューで、[ファイル]→[新規]→[新しいプロジェクト]を選択します。







ここで、アプリケーションの名前、ドメイン、およびパスを入力します。 次へをクリックします。







ここで、Androidのバージョンを入力できます。 Android時計とテレビを選択することもできます。 しかし、私たちのアプリケーションがこれらすべてで機能するかどうかはわかりません。 そのため、スクリーンショットのようにすべてを入力することをお勧めします。 次へをクリックします。







ここでは、必ず「空のアクティビティ」を選択してください。 次へをクリックします。







ここではすべてをそのままにして、[完了]をクリックします。 これでプロジェクトが作成されました。 3番目のステップに進みます。



ステップ3.写真を追加する



写真付きのアーカイブをダウンロードして解凍します。



描画可能なフォルダを見つけて、そこに画像をコピーします。







後で必要になります。



ステップ4.レイアウトを作成する



activity_main.xmlを見つけ、[テキスト]タブを開いて、これに貼り付けます。



<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.spaceavoider.spaceavoider.MainActivity"> <LinearLayout android:id="@+id/gameLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:layout_weight="100"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/leftButton" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="50" android:text="Left" /> <Button android:id="@+id/rightButton" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="50" android:text="Right" /> </LinearLayout> </LinearLayout>
      
      





[デザイン]タブには、レイアウトの外観が表示されます。







上部にはゲーム自体が配置されるフィールドがあり、下部には左右のコントロールボタンがあります。 レイアウトについては、1つの記事ではなく別の記事を書くことができます。 これについては詳しく説明しません。 あなたはここでそれについて読むことができます



ステップ5. MainActivityクラスの編集



まず、実装のView.OnTouchListenerをクラス定義に追加します。 クラス定義は次のようになります。



 public class MainActivity extends AppCompatActivity implements View.OnTouchListener {
      
      





クラスに必要な静的変数(クラス変数)を追加します。



 public static boolean isLeftPressed = false; //    public static boolean isRightPressed = false; //   
      
      





プロシージャにprotected void onCreate(Bundle savedInstanceState){

行を追加します。



 GameView gameView = new GameView(this); //  gameView LinearLayout gameLayout = (LinearLayout) findViewById(R.id.gameLayout); //  gameLayout gameLayout.addView(gameView); //     gameView Button leftButton = (Button) findViewById(R.id.leftButton); //   Button rightButton = (Button) findViewById(R.id.rightButton); leftButton.setOnTouchListener(this); //       (   onTouch) rightButton.setOnTouchListener(this);
      
      





クラスLinearLayout、Buttonなど。 まだインポートに追加されていないため、赤で強調表示されています。

インポートに追加して赤いハイライトを削除するには、Alt + Enterキーを押すたびに。

このクラスはまだ存在しないため、GameViewは赤で強調表示されます。 後で作成します。



次に手順を追加します。



 public boolean onTouch(View button, MotionEvent motion) { switch(button.getId()) { //    case R.id.leftButton: switch (motion.getAction()) { //     case MotionEvent.ACTION_DOWN: isLeftPressed = true; break; case MotionEvent.ACTION_UP: isLeftPressed = false; break; } break; case R.id.rightButton: switch (motion.getAction()) { //     case MotionEvent.ACTION_DOWN: isRightPressed = true; break; case MotionEvent.ACTION_UP: isRightPressed = false; break; } break; } return true; }
      
      





誰かが混乱している場合、MainActivityクラスは次のようになります。



 package com.spaceavoider.spaceavoider; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; public class MainActivity extends AppCompatActivity implements View.OnTouchListener { public static boolean isLeftPressed = false; //    public static boolean isRightPressed = false; //    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); GameView gameView = new GameView(this); //  gameView LinearLayout gameLayout = (LinearLayout) findViewById(R.id.gameLayout); //  gameLayout gameLayout.addView(gameView); //     gameView Button leftButton = (Button) findViewById(R.id.leftButton); //   Button rightButton = (Button) findViewById(R.id.rightButton); leftButton.setOnTouchListener(this); //       (   onTouch) rightButton.setOnTouchListener(this); } public boolean onTouch(View button, MotionEvent motion) { switch(button.getId()) { //    case R.id.leftButton: switch (motion.getAction()) { //     case MotionEvent.ACTION_DOWN: isLeftPressed = true; break; case MotionEvent.ACTION_UP: isLeftPressed = false; break; } break; case R.id.rightButton: switch (motion.getAction()) { //     case MotionEvent.ACTION_DOWN: isRightPressed = true; break; case MotionEvent.ACTION_UP: isRightPressed = false; break; } break; } return true; } }
      
      





これで、MainActivityクラスの準備ができました! まだ作成されていないGameViewクラスをまだ作成していません。 そして、左ボタンが押されると、静的変数isLeftPressed = trueになり、右ボタンがisRightPressed = trueになると。 基本的に彼が行うことはこれだけです。



まず、画面に宇宙船を表示し、コントロールボタンを押して移動します。 小惑星は後で出発します。



ステップ6. GameViewクラスを作成する



最後に、不足しているGameViewクラスを作成します。 それでは始めましょう。 SurfaceViewのRunnableを実装する拡張機能をクラス定義に追加します。 モバイルデバイスの画面解像度は異なります。 解像度が480x800の古い小型電話でも、1800x2560の大型タブレットでもかまいません。 ゲームがすべてのデバイスで同じように見えるように、画面を水平方向に20の部分と垂直方向に28の部分に分割しました。 結果の測定単位を単位と呼びました。 他の番号を選択できます。 主なものは、それらの間の関係がほぼ維持されることです。そうでない場合、画像は伸長または圧縮されます。



 public static int maxX = 20; //    public static int maxY = 28; //    public static float unitW = 0; //      public static float unitH = 0; //     
      
      





unitWとunitWは後で計算します。 他の変数も必要になります。



 private boolean firstTime = true; private boolean gameRunning = true; private Ship ship; private Thread gameThread = null; private Paint paint; private Canvas canvas; private SurfaceHolder surfaceHolder;
      
      





コンストラクタは次のようになります。



 public GameView(Context context) { super(context); //    surfaceHolder = getHolder(); paint = new Paint(); //   gameThread = new Thread(this); gameThread.start(); }
      
      





run()メソッドには無限ループが含まれます。 ループの開始時に、update()メソッドが実行されます。

船の新しい座標を計算します。 次に、draw()メソッドが船を画面に描画します。 最後に、control()メソッドは17ミリ秒間停止します。 17ミリ秒後、run()が再び開始されます。 そして、変数gameRunning == trueになるまで続きます。 これらの方法は次のとおりです。



 @Override public void run() { while (gameRunning) { update(); draw(); control(); } } private void update() { if(!firstTime) { ship.update(); } } private void draw() { if (surfaceHolder.getSurface().isValid()) { //   surface if(firstTime){ //     firstTime = false; unitW = surfaceHolder.getSurfaceFrame().width()/maxX; //      unitH = surfaceHolder.getSurfaceFrame().height()/maxY; ship = new Ship(getContext()); //   } canvas = surfaceHolder.lockCanvas(); //  canvas canvas.drawColor(Color.BLACK); //    ship.drow(paint, canvas); //   surfaceHolder.unlockCanvasAndPost(canvas); //  canvas } } private void control() { //   17  try { gameThread.sleep(17); } catch (InterruptedException e) { e.printStackTrace(); } }
      
      





最初の起動時に初期化に注意してください。 そこで、ユニット内のピクセル数を計算し、船を追加します。 まだ船を作成していません。 しかし、最初に、その親クラスを作成します。



ステップ7. SpaceBodyクラスを作成する



クラスShip(宇宙船)およびAsteroid(小惑星)の親になります。 これらの2つのクラスに共通するすべての変数とメソッドが含まれます。 変数を追加します。



 protected float x; //  protected float y; protected float size; //  protected float speed; //  protected int bitmapId; // id  protected Bitmap bitmap; // 
      
      





および方法



 void init(Context context) { //      Bitmap cBitmap = BitmapFactory.decodeResource(context.getResources(), bitmapId); bitmap = Bitmap.createScaledBitmap( cBitmap, (int)(size * GameView.unitW), (int)(size * GameView.unitH), false); cBitmap.recycle(); } void update(){ //      } void drow(Paint paint, Canvas canvas){ //   canvas.drawBitmap(bitmap, x*GameView.unitW, y*GameView.unitH, paint); }
      
      





ステップ8. Shipクラスを作成する



次に、クラスShip(宇宙船)を作成します。 SpaceBodyクラスを継承しているため、addはSpaceBodyをクラス定義に拡張します。



コンストラクタを書きましょう:



 public Ship(Context context) { bitmapId = R.drawable.ship; //    size = 5; x=7; y=GameView.maxY - size - 1; speed = (float) 0.2; init(context); //   }
      
      





そしてupdate()メソッドを再定義します



 @Override public void update() { //        if(MainActivity.isLeftPressed && x >= 0){ x -= speed; } if(MainActivity.isRightPressed && x <= GameView.maxX - 5){ x += speed; } }
      
      





この宇宙船で準備ができています! すべてをコンパイルして実行します。 宇宙船が画面に表示されます。 ボタンをクリックすると、左右に移動します。 次に、上からストリーミングする小惑星を追加します。 船と衝突すると、ゲームは終了します。



ステップ9.小惑星クラスを作成する



小惑星クラス(小惑星)を追加します。 また、SpaceBodyクラスを継承するため、addはSpaceBodyをクラス定義に拡張します。



必要な変数を追加します。



 private int radius = 2; //  private float minSpeed = (float) 0.1; //   private float maxSpeed = (float) 0.5; //  
      
      





小惑星は、画面上部のランダムなポイントに表示され、ランダムな速度で飛行します。 これを行うには、コンストラクターで乱数ジェネレーターを使用してxと速度を設定します。



 public Asteroid(Context context) { Random random = new Random(); bitmapId = R.drawable.asteroid; y=0; x = random.nextInt(GameView.maxX) - radius; size = radius*2; speed = minSpeed + (maxSpeed - minSpeed) * random.nextFloat(); init(context); }
      
      





小惑星は垂直方向に一定の速度で移動する必要があります。 したがって、update()メソッドでは、x座標に速度を追加します。



 @Override public void update() { y += speed; }
      
      





また、小惑星が船に衝突したかどうかを判断する方法も必要です。



 public boolean isCollision(float shipX, float shipY, float shipSize) { return !(((x+size) < shipX)||(x > (shipX+shipSize))||((y+size) < shipY)||(y > (shipY+shipSize))); }
      
      





もっと詳しく考えてみましょう。 簡単にするために、船と小惑星を正方形と見なします。 それから私は反対から行きました。 つまり、正方形が交差しないときを決定します。



((x +サイズ)<shipX)-小惑星の左側に出荷します。

(x>(shipX + shipSize))-小惑星の右側の船。

((y +サイズ)<shipY)-小惑星の上にある船。

(y>(shipY + shipSize))は小惑星の下の船です。



これら4つの式の間に立つ|| (または)。 つまり、少なくとも1つの式が真の場合(つまり、正方形が交差しないことを意味します)-結果の式も真になります。



式全体を記号!で反転します。 その結果、メソッドは、正方形が交差するときにtrueを返します。 必要なもの。



ここで、より複雑な形状の交点の決定について読むことができます



ステップ10.小惑星をGameViewに追加する



GameViewで、変数を追加します。



 private ArrayList<Asteroid> asteroids = new ArrayList<>(); //     private final int ASTEROID_INTERVAL = 50; //      ( ) private int currentTime = 0;
      
      





また、2つのメソッドを追加します。



 private void checkCollision(){ //             for (Asteroid asteroid : asteroids) { if(asteroid.isCollision(ship.x, ship.y, ship.size)){ //   gameRunning = false; //   // TODO    } } } private void checkIfNewAsteroid(){ //  50     if(currentTime >= ASTEROID_INTERVAL){ Asteroid asteroid = new Asteroid(getContext()); asteroids.add(asteroid); currentTime = 0; }else{ currentTime ++; } }
      
      





また、run()メソッドでは、control()呼び出しの前にこれらのメソッドへの呼び出しを追加します。



 @Override public void run() { while (gameRunning) { update(); draw(); checkCollision(); checkIfNewAsteroid(); control(); } }
      
      





次に、update()メソッドで、すべての小惑星を反復処理し、それらの小惑星でupdate()メソッドを呼び出すループを追加します。



 private void update() { if(!firstTime) { ship.update(); for (Asteroid asteroid : asteroids) { asteroid.update(); } } }
      
      





同じループをdraw()メソッドに追加します。



 private void draw() { if (surfaceHolder.getSurface().isValid()) { //   surface if(firstTime){ //     firstTime = false; unitW = surfaceHolder.getSurfaceFrame().width()/maxX; //      unitH = surfaceHolder.getSurfaceFrame().height()/maxY; ship = new Ship(getContext()); //   } canvas = surfaceHolder.lockCanvas(); //  canvas canvas.drawColor(Color.BLACK); //    ship.drow(paint, canvas); //   for(Asteroid asteroid: asteroids){ //   asteroid.drow(paint, canvas); } surfaceHolder.unlockCanvasAndPost(canvas); //  canvas } }
      
      





以上です! 最も簡単な2Dゲームが用意されています。 コンパイルして実行し、何が起こったかを確認します!

誰かが混乱したり、何かが機能しない場合は、 ソースをダウンロードできます。



もちろん、ゲームは原始的です。 ただし、新しい機能を追加することで改善できます。 まず、画面から飛び出す小惑星の除去を実装する必要があります。 船に小惑星を発射させると、ゲームが徐々に加速し、タイマー、ハイスコアテーブルなどが追加されます。 それがあなたにとって興味深い場合-私はこれをすべて説明する続編を書きます。



それだけです 続行するには、レビュー、質問、興味のあるトピックを書いてください。



All Articles