Skip to content

SurfaceViewとCanvasで画面サイズに合わせて描画する方法 [Android]

SurfaceViewを使ってゲームを作った時に使った画面サイズに合わせて描画する方法をメモしておきます。

Androidでは様々な画面サイズの端末が存在するため、ゲームなどでの描画処理に一工夫が必要です。

まず、Canvas.scaleを使って画面サイズに合わせて全体を拡大縮小します。

これだけでは画面の左上に偏るので、画面の中心に描画されるようにcanvasを移動させます。

移動にはCanvas.translateを使います。

使用する関数
Canvas.scale(x方向の倍率, y方向の倍率)
Canvas.translate(x方向の移動量, y方向の移動量);

コード例

/** 最初にゲーム内の画面サイズを決めておく **/
final float VIEW_WIDTH = 600
final float VIEW_HEIGHT = 900;

決めた画面サイズをもとに倍率を計算します。縦横比が異なる場合は小さい方に合わせます。

/** SurfaceCreatedに記述 **/
float scaleX = getWidth() / VIEW_WIDTH;
float scaleY = getHeight() /  VIEW_HEIGHT;
scale = scaleX > scaleY ? scaleY : scaleX;
// onCreate内ではgetWidth()が0を返すので上手くいかない。

この倍率から、拡大・縮小して描画します。

/** 描画部分で記述 **/
Canvas c = holder.lockCanvas();
c.translate(getWidth() - VIEW_WIDTH)/2*scale, (getHeight() - VIEW_HEIGHT)/2*scale); // 画面の中央になるように移動させる
c.scale(scale, scale); // 端末の画面に合わせて拡大・縮小する

// 描画処理

holder.unlockCanvasAndPost(c);

また、ゲームを作る場合は、これだけではゲームのタッチ入力を取得するときに問題が起きます。

タッチ入力の座標は拡大・縮小されないため、ここにズレが生じるからです。

そこで、タッチ入力の座標に関しても倍率を使って調整する必要があります。

/** (例) イベント部分で記述 **/
public boolean onTouch(View v, MotionEvent event) {
		touchedX = event.getX() / scale;
		touchedY = event.getY() / scale;
}
広告

AndroidのMediaPlayerでのseekTo()がずれる問題 [未解決]

MediaPlayerを使って動画プレイヤーを作っていたのですが、seekTo()が上手く動いていない様子でした。

seekTo()を使うと指定した位置からずれた位置に移動してしまいます。

まだ解決はしていませんが、原因や試したことをメモしておきます。解決策をご存知ならば是非教えて下さい。

色々と調べた結果、次のような書き込みを見つけました。

Seek to Key frame – The video when encoded will usually have something called as the I frame or the Key frame, it means that this frame has lot of information and can be used to decode a frame in its entirety. To reduce the amount of space all the frames are not encoded as key frames instead they are encoded as P (Predicted) frames or predicted frames, meaning you can decode a P frame with the help from the key frame.

(http://stackoverflow.com/questions/6845161/accuracy-of-mediaplayer-seektoint-msecsより)

MediaPlayerのseekTo()では、指定した時間の近くのキーフレームにしか移動できないようです。

0.1秒ごとにseekTo()してみると、一定の間は同じ場所に飛んで、急に少し先の場所に飛ぶ、というのが繰り返されていたのですが、このためだと思われます。

そこで、seekTo()で移動した位置から実際に指定した位置まで再生して移動させようと試したのですが、これも上手くいきませんでした。

こうしたかった

mediaPlayer.seekTo(seekPosition);

int diff = seekPosition - mediaPlayer.getCurrentPosition();

mediaPlayer.start();

try {

Thread.sleep(diff);

} catch (InterruptedException e) {

e.printStackTrace();

}

mediaPlayer.pause();

しかし、getCurrentPosition()からはseekPositionが返ってきてしまって失敗。

もう少し試してみて何か分かったら追記します。