コンテンツ
Android用のゲームを作成する方法はたくさんありますが、重要な方法の1つは、Javaを使用してAndroid Studioでゼロから作成することです。これにより、ゲームの外観と動作を最大限に制御できます。また、プロセスは、アプリのスプラッシュスクリーンを作成する場合でも、単に他のシナリオでも使用できるスキルを教えてくれますいくつかのアニメーションを追加します。このことを念頭に置いて、このチュートリアルでは、Android StudioとJavaを使用して簡単な2Dゲームを作成する方法を示します。追跡したい場合は、Githubですべてのコードとリソースを見つけることができます。
設定中
ゲームを作成するには、ゲームループ、スレッド、キャンバスなど、いくつかの特定の概念に対処する必要があります。まず、Android Studioを起動します。インストールしていない場合は、Android Studioの完全な紹介をご覧ください。インストールプロセスについて説明しています。次に、新しいプロジェクトを開始し、「空のアクティビティ」テンプレートを選択してください。これはゲームなので、もちろん、問題を複雑にするFABボタンなどの要素は必要ありません。
最初にしたいことは、変更することです AppCompatActivity に アクティビティ。これは、アクションバー機能を使用しないことを意味します。
同様に、ゲームをフルスクリーンにしたいと考えています。 setContentView()を呼び出す前に、onCreate()に次のコードを追加します。
getWindow()。setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN、WindowManager.LayoutParams.FLAG_FULLSCREEN); this.requestWindowFeature(Window.FEATURE_NO_TITLE);
いくつかのコードを記述し、赤で下線が引かれた場合、おそらくクラスをインポートする必要があることに注意してください。言い換えると、特定のステートメントを使用してそれらを使用可能にすることをAndroid Studioに伝える必要があります。下線付きの単語のどこかをクリックしてからAlt + Enterを押すだけで、自動的に実行されます!
ゲームビューを作成する
XMLスクリプトを使用して、ボタン、画像、ラベルなどのビューのレイアウトを定義するアプリに慣れているかもしれません。これがライン setContentView 私たちのためにやっています。
繰り返しになりますが、これはブラウザウィンドウやスクロール表示のリサイクラビューを必要としないゲームです。その代わりに、代わりにキャンバスを表示します。 Android Studioのキャンバスは、アートの場合とまったく同じです。これは、描くことができる媒体です。
そのため、その行を次のように変更します。
setContentView(new GameView(this))
これが再び下線が引かれていることがわかります。しかし 今 Alt + Enterを押すと、クラスをインポートするオプションがありません。代わりに、次のオプションがあります 作成する クラス。つまり、キャンバスで何が行われるかを定義する独自のクラスを作成しようとしています。これにより、既成のビューを表示するだけでなく、画面に描画することができます。
そのため、左側の階層でパッケージ名を右クリックして選択します 新規>クラス。クラスを作成するためのウィンドウが表示されますので、それを呼び出します GameView。 SuperClassの下で、次のように記述します。 android.view.SurfaceView つまり、クラスはSurfaceViewからメソッド(その機能)を継承します。
[インターフェイス]ボックスに、 android.view.SurfaceHolder.Callback。他のクラスと同様に、コンストラクタを作成する必要があります。このコードを使用してください:
プライベートMainThreadスレッド。 public GameView(Context context){super(context); getHolder()。addCallback(this); }
新しいオブジェクト(この場合はサーフェス)を作成するためにクラスが呼び出されるたびに、コンストラクターが実行され、新しいサーフェスが作成されます。 「スーパー」という行はスーパークラスを呼び出します。この場合は、SurfaceViewです。
コールバックを追加することで、イベントをインターセプトできます。
次に、いくつかのメソッドをオーバーライドします。
@Override public void surfaceChanged(SurfaceHolderホルダー、int形式、int幅、int高さ){} @Override public void surfaceCreated(SurfaceHolderホルダー){} @Override public void surfaceDestroyed(SurfaceHolderホルダー){}
これらにより、基本的にはスーパークラス(SurfaceView)のメソッドをオーバーライド(名前を変更)できます。これで、コードに赤い下線がなくなるはずです。いいね
新しいクラスを作成したばかりであり、それを参照するたびに、ゲームにペイントするためのキャンバスが構築されます。クラス 作成する オブジェクトが必要です。
スレッドを作成する
新しいクラスが呼び出されます メインスレッド。そして、その仕事はスレッドを作成することです。スレッドは基本的に、並行して実行できるコードの並列フォークのようなものです メイン コードの一部。多数のスレッドを一度に実行できるため、厳密なシーケンスに固執するのではなく、同時に発生することができます。これはゲームにとって重要です。なぜなら、たくさんのことが起こっているときでも、スムーズに実行し続けることを確認する必要があるからです。
以前と同じように新しいクラスを作成し、今回は拡張します 糸。コンストラクターで呼び出すだけです スーパー()。忘れないでください、それはスレッドであり、私たちのためにすべての面倒な作業を行うことができるスーパークラスです。これは、電話をかけるだけで皿を洗うプログラムを作成するようなものです 洗濯機().
このクラスが呼び出されると、主なものの派生物として実行される別のスレッドが作成されます。そしてそれは ここに GameViewを作成します。つまり、GameViewクラスも参照する必要があり、キャンバスを含むSurfaceHolderも使用します。したがって、キャンバスが表面である場合、SurfaceHolderはイーゼルです。そして、GameViewはそれをすべてまとめるものです。
完全なものは次のようになります。
パブリッククラスMainThreadはThread {private SurfaceHolder surfaceHolder;を拡張します。プライベートGameView gameView; public MainThread(SurfaceHolder surfaceHolder、GameView gameView){super(); this.surfaceHolder = surfaceHolder; this.gameView = gameView; }}
シュウィート。これでGameViewとスレッドができました!
ゲームループの作成
これでゲームを作成するために必要な原材料は手に入りましたが、何も起きていません。これがゲームループの出番です。基本的に、これは画面を描画する前に入力と変数を何度もチェックしてチェックするコードのループです。私たちの目的は、これを可能な限り一貫させて、フレームレートにthis音やしゃっくりがないようにすることです。
今のところ、私たちはまだ メインスレッド クラスを作成し、スーパークラスのメソッドをオーバーライドします。これは 走る.
そして、それは次のような小さなものになります:
@Override public void run(){while(running){canvas = null; try {canvas = this.surfaceHolder.lockCanvas(); synchronized(surfaceHolder){this.gameView.update(); this.gameView.draw(canvas); }} catch(例外e){}最終的に{if(canvas!= null){try {surfaceHolder.unlockCanvasAndPost(canvas); } catch(例外e){e.printStackTrace(); }}}}}
多くの下線が表示されるので、変数と参照をさらに追加する必要があります。先頭に戻り、以下を追加します。
プライベートSurfaceHolder surfaceHolder;プライベートGameView gameView;実行中のプライベートブール値。パブリックスタティックキャンバスキャンバス。
Canvasをインポートすることを忘れないでください。キャンバスは、実際に描画するものです。 「lockCanvas」に関しては、キャンバスをフリーズして描画できるようにするため、これは重要です。そうでないと、複数のスレッドが同時に描画しようとする可能性があるため、これは重要です。キャンバスを編集するには、まず最初に ロック キャンバス。
更新は私たちが作成しようとしている方法であり、これは後で楽しいことが起こる場所です。
の 試してみる そして キャッチ 一方、キャンバスの準備が整っていない場合に発生する可能性のある例外(エラー)を処理しようとすることを示すJavaの要件のみです。
最後に、必要なときにスレッドを開始できるようにします。これを行うには、ここで動きを設定できる別の方法が必要です。それは何ですか ランニング variableはforです(ブールは、trueまたはfalseのみである変数のタイプであることに注意してください)。このメソッドを メインスレッド クラス:
public void setRunning(boolean isRunning){running = isRunning; }
しかし、この時点では、1つのことを強調する必要があります。 更新。これは、更新メソッドをまだ作成していないためです。に戻ってポップ GameView そして、メソッドを追加します。
public void update(){}
また、する必要があります 開始 スレッド!私たちはこれを surfaceCreated 方法:
@Override public void surfaceCreated(SurfaceHolder holder){thread.setRunning(true); thread.start(); }
また、表面が破壊されたときにスレッドを停止する必要があります。ご想像のとおり、この処理は surfaceDestroyed 方法。しかし、実際にはスレッドを停止するために複数の試行が必要になる可能性があるので、これをループに入れて使用します 試してみる そして キャッチ 再び。そのようです:
@Override public void surfaceDestroyed(SurfaceHolder holder){boolean retry = true; while(retry){try {thread.setRunning(false); thread.join(); } catch(InterruptedException e){e.printStackTrace(); } retry = false; }}
最後に、コンストラクターに移動して、スレッドの新しいインスタンスを作成してください。そうしないと、恐ろしいnullポインター例外が発生します。それから、GameViewをフォーカス可能にします。つまり、イベントを処理できるようにします。
thread = new MainThread(getHolder()、this); setFocusable(true);
今、あなたはできる 最後に 実際にこのことをテストしてください!そうです、実行をクリックして すべき 実際にエラーなしで実行されます。吹き飛ばされる準備を!
それは...それは...空白の画面です!すべてのコード。空白の画面の場合。しかし、これは空白の画面です 機会。イベントを処理するためのゲームループを使用して、表面を稼働させます。残っているのは、物事を実現することだけです。ここまでチュートリアルのすべてを実行していなくても問題ありません。重要なのは、このコードを単純にリサイクルして、すばらしいゲームの作成を開始できるということです!
グラフィックスを行う
今、描画する空白の画面があります。描画するだけです。幸いなことに、それは簡単な部分です。あなたがする必要があるのは、私たちの描画メソッドをオーバーライドすることです GameView クラスといくつかのきれいな写真を追加します。
@override public void draw(Canvas canvas){super.draw(canvas); if(canvas!= null){canvas.drawColor(Color.WHITE);ペイントpaint = new Paint(); paint.setColor(Color.rgb(250、0、0)); canvas.drawRect(100、100、200、200、paint); }}
これを実行すると、白い画面の左上にかなり赤い正方形が表示されます。これは確かに改善です。
理論的には、このメソッド内にゲームを貼り付けることで、ゲーム全体を作成できます(オーバーライドします onTouchEvent 入力を処理します)が、それは物事を進めるためのひどく良い方法ではないでしょう。ループ内に新しいPaintを配置すると、処理速度が大幅に低下します。これを他の場所に配置しても、 ドロー 方法はくて従うのが難しくなります。
代わりに、ゲームオブジェクトを独自のクラスで処理する方がはるかに理にかなっています。キャラクターを示すものから始めて、このクラスを呼び出します キャラクタースプライト。さあ、それを作りましょう。
このクラスはキャンバスにスプライトを描画し、次のようになります
パブリッククラスCharacterSprite {プライベートビットマップイメージ。 public CharacterSprite(ビットマップbmp){image = bmp; } public void draw(Canvas canvas){canvas.drawBitmap(image、100、100、null); }}
これを使用するには、最初にビットマップをロードしてからクラスを呼び出す必要があります GameView。参照を追加する プライベートCharacterSprite characterSprite そして、 surfaceCreated メソッド、次の行を追加します。
characterSprite = new CharacterSprite(BitmapFactory.decodeResource(getResources()、R.drawable.avdgreen));
ご覧のとおり、読み込んでいるビットマップはリソースに保存されており、avdgreenと呼ばれています(以前のゲームのもの)。あとは、そのビットマップを新しいクラスに渡すだけです ドロー 方法:
characterSprite.draw(canvas);
実行をクリックすると、画面にグラフィックが表示されます。これはBeeBooです。私は学校の教科書に彼を描いていました。
この小さな男を動かしたい場合はどうしますか?シンプル:彼の位置にxとyの変数を作成し、これらの値を 更新 方法。
参照を追加します キャラクタースプライト 次に、ビットマップをで描画します x、y。ここで更新メソッドを作成します。今のところは次のことを試してみましょう。
y ++;
ゲームループが実行されるたびに、キャラクターを画面の下に移動します。覚えておいて、 y 座標は上から測定されるので 0 画面の上部です。もちろん、呼び出す必要があります 更新 方法 キャラクタースプライト から 更新 方法 GameView.
もう一度[再生]を押すと、画像がゆっくりと画面をたどっていくことがわかります。まだゲーム賞を受賞していませんが、スタートです!
さて、ものを作るために わずかに さらに興味深いのは、「弾むボール」コードをここにドロップするだけです。これにより、古いWindowsスクリーンセーバーのように、グラフィックが画面の端から跳ね返ります。奇妙に催眠術をかけられたものです。
public void update(){x + = xVelocity; y + = yVelocity; if((x> screenWidth-image.getWidth())||(x< 0)){xVelocity = xVelocity * -1; } if((y> screenHeight-image.getHeight())||(y< 0)){yVelocity = yVelocity * -1; }}
また、これらの変数を定義する必要があります。
private int xVelocity = 10; private int yVelocity = 5; private int screenWidth = Resources.getSystem()。getDisplayMetrics()。widthPixels; private int screenHeight = Resources.getSystem()。getDisplayMetrics()。heightPixels;
最適化
がある たっぷり プレーヤーの入力の処理から画像のスケーリング、多くのキャラクターがすべて画面内を一度に移動することの管理まで、ここで詳しく説明します。現在、キャラクターは跳ね返っていますが、非常によく見ると、わずかにslight音があります。それはひどいものではありませんが、肉眼で見ることができるという事実は警告サインのようなものです。速度は、物理デバイスと比較してエミュレーターでも大きく異なります。今あなたが持っているときに何が起こるか想像してください トン すぐに画面に表示されます!
この問題にはいくつかの解決策があります。私が始めたいことは、プライベート整数を作成することです メインスレッド そしてそれを呼ぶ targetFPS。この値は60になります。この速度でゲームを実行してみてください。その間、それが確実に行われるようにチェックします。そのために、プライベートダブルと呼ばれる averageFPS.
私も更新します 走る 各ゲームループの所要時間を測定する方法と 一時停止 それがtargetFPSの先にある場合、そのゲームは一時的にループします。それからどれくらいの時間を計算します 今 それを取得して印刷し、ログで確認できるようにします。
@Override public void run(){long startTime;長い時間長いwaitTime; long totalTime = 0; int frameCount = 0; long targetTime = 1000 / targetFPS; while(running){startTime = System.nanoTime(); canvas = null; try {canvas = this.surfaceHolder.lockCanvas(); synchronized(surfaceHolder){this.gameView.update(); this.gameView.draw(canvas); }} catch(例外e){}最終的に{if(canvas!= null){try {surfaceHolder.unlockCanvasAndPost(canvas); } catch(例外e){e.printStackTrace(); }}} timeMillis =(System.nanoTime()-startTime)/ 1000000; waitTime = targetTime-timeMillis; try {this.sleep(waitTime); } catch(例外e){} totalTime + = System.nanoTime()-startTime; frameCount ++; if(frameCount == targetFPS){averageFPS = 1000 /((totalTime / frameCount)/ 1000000); frameCount = 0; totalTime = 0; System.out.println(averageFPS); }}}
現在、私たちのゲームはFPSを60にロックしようとしていますが、最近のデバイスでは一般的にかなり安定した58-62 FPSを測定していることがわかります。エミュレーターでは、別の結果が得られる場合があります。
60から30に変更して、何が起こるかを確認してください。ゲームが遅くなり、それ すべき logcatで30を読み取ります。
おわりに
パフォーマンスを最適化するためにできることは他にもいくつかあります。このテーマに関する素晴らしいブログ投稿があります。ループ内でペイントやビットマップの新しいインスタンスを作成することを控え、すべての初期化を行うようにしてください 外側 ゲームが始まる前。
次のヒットAndroidゲームの作成を計画している場合は、 確かに 最近では、より簡単で効率的な方法で対処できます。しかし、キャンバスに描画できるユースケースシナリオは間違いなく存在し、レパートリーに追加するのに非常に便利なスキルです。このガイドがお役に立てば幸いであり、今後のコーディングベンチャーでの幸運を祈ります!
次 – Javaの初心者向けガイド