Google、さまざまな Android ブログ、さまざまなゲーム開発ブログ、および Android の SurfaceView に関するその他のチュートリアル サイトを検索した後、SurfaceView を完全に理解しようとしています。Android ビューと Surface ビューに関する Safaribooks の本をいくつか読んだことがありますが、提供する情報が少なすぎるか、AndEngine などの他の SDK を使用しています。厳密に表面図を学びたいと思っていました。Lunar Lander のサンプル プロジェクトや、見つけた他のプロジェクトで遊んで、スケルトン サーフェス ビューのコードをいくつか作成しました。スケルトン専用の 3 つのクラスで構成されます。
MainActivity クラス:
package com.learning.svlearning;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.Window;
import android.view.WindowManager;
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Set FullScreen Mode - No title bars!!
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
// Screen created with pure java - Say no to xml (atleast for this demo)
setContentView(new MainGamePanel(this));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.layout_game_window, menu);
return true;
}
}
このクラスはかなり簡単です。タイトル バーのない全画面表示を要求する、メインのゲーム アクティビティ ウィンドウ。実際のゲームのあり方 :) このクラスは、「この」(MainActivity) クラスのコンテキストを渡すことによって、ビューの次のクラスを呼び出します。
MainGamePanel クラス:
package com.learning.svlearning;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MainGamePanel extends SurfaceView {
final static public String tag = "Tracer";
private GameThread gameThread; // For our thread needed to do logical processing without holding up the UI thread
private SurfaceHolder holder; // For our CallBacks.. (One of the areas I don't understand!)
public MainGamePanel(Context context) {
super(context);
Log.d(tag, "Inside MainGamePanel");
gameThread = new GameThread(this); //Create the GameThread instance for our logical processing
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
// Since we are using the SurfaceView, we need to use, at very least, the surfaceDestroyed and surfaceCreated methods.
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
Log.d(tag, "Inside SurfaceHolder Callback - surfaceDestroyed");
gameThread.setRunning(false); // Stop the Thread from running because the surface was destroyed. Can't play a game with no surface!!
while (retry) {
try {
Log.d(tag, "Inside SurfaceHolder Callback - surfaceDestroyed - while statement");
gameThread.join();
retry = false; //Loop until game thread is done, making sure the thread is taken care of.
} catch (InterruptedException e) {
// In case of catastrophic failure catch error!!!
}
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// let there be Surface!
Log.d(tag, "Inside SurfaceHolder Callback - surfaceCreated");
gameThread.setRunning(true); // Now we start the thread
gameThread.start(); // and begin our game's logical processing
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
// The code to resize the screen ratio when it flips from landscape to portrait and vice versa
}
});
}
@Override
protected void onDraw(Canvas canvas) {
//This is where we draw stuff.. since this is just a skeleton demo, we only draw the color Dark Grey so we can visibly see that we actually accomplished something with the surfaceview drawing
Log.d(tag, "Inside onDraw");
canvas.drawColor(Color.DKGRAY); // You can change the Color to whatever color you want, for this demo I just used Color.DKGRAY
}
}
このクラスは主に onDraw メソッドを使用してリソース/画像の描画を処理し、サーフェスが作成および破棄されたときに何が起こるかを処理します (画面が変更されたときも同様ですが、今のところそれを処理するコードを書きませんでした)。ゲーム ロジックの処理を処理する GameThread クラス。
GameThread クラス:
package com.learning.svlearning;
import android.graphics.Canvas;
import android.util.Log;
public class GameThread extends Thread{
final static public String tag = "Tracer";
private MainGamePanel view;
private boolean running = false;
static final long FPS = 30; // To help limit the FPS when we draw, otherwise we would kill the CPU and increase the Battery Consumption.
public GameThread(MainGamePanel view){
Log.d(tag, "inside GameThread");
this.view = view;
}
public void setRunning(boolean run){
Log.d(tag, "inside GameThread - setRunning");
running = run; // For starting / stoping our game thread
}
@Override
public void run() {
long ticksPS = 1000 / FPS; // Limit the frames per second
long startTime;
long sleepTime;
Log.d(tag, "inside GameThread - run");
while(running){ // Our Main Game Loop is right here
Canvas c = null; // build our canvas to draw on
Log.d(tag, "inside GameThread - run - while loop");
startTime = System.currentTimeMillis(); //get the current time in milliseconds - this is for helping us limit the FPS
try{
c = view.getHolder().lockCanvas(); //Before we can draw, we always have to lock the canvas, otherwise goblins will invade your app and destroy everything!
synchronized (view.getHolder()){ // we have to synchronize this because we need to make sure that the method runs when at the proper time.
view.onDraw(c); // this is where we pass our drawing information. The canvas gets passed to the onDraw method in our MainGamePanel class.
}
}finally{
if(c != null) {
view.getHolder().unlockCanvasAndPost(c); // Once we are done drawing, we unlock our canvas and post. which means we drew on the canvas, and now the devices screen will display our drawing.
}
}
sleepTime = ticksPS-(System.currentTimeMillis() - startTime); // this is where we calculace how long we need this thread to sleep (again with the FPS) we want it limited to 30 FPS as defined in our FPS variable.
try {
if (sleepTime > 0){
sleep(sleepTime); // night night, sleep to limit the fps and save our batteries!
}
else{
sleep(10); // Incase something goes crazy, we still want to sleep the thread to save the battery.
}
}catch(Exception e){
}
}
}
}
このクラスは、ゲーム ロジックを処理し、描画可能な情報を MainGamePanel クラスの draw メソッドに送信します。たとえば、キャラクターが動いている場合、x 座標と y 座標を draw メソッドに送信して、キャラクターを別の位置に描画することができます。
これらのクラスのいくつかの部分は、完全には理解できません。コールバックのように、googles android 開発者ページの情報を読むと、それが何であり、なぜそれを使用するのかについて、さらに混乱したままです。また、誰かがこれに追加するものがある場合、または私が誤解している可能性のあるものを見つけた場合は、遠慮なく修正してください。私はアンドロイドを使って仕事をするのが好きです。かなり難しいですが、物事を理解し始めると、とてもやりがいがあります!