Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scaling a fixed SurfaceView to fill vertically and maintain aspect ratio

I've been trying to look for an answer and can't seem to find one (it's a fairly unconventional approach to programming an android game, so it's to be expected, at least I think).

I'm trying to create a GBA-style RPG game in Android (for the nostalgia). OpenGL wasn't good enough for me because it wasn't customizable enough in the game loop (I only want draw to be called when there's something that needs to be drawn, since I want this game to be uber-efficient in how it uses the battery, which means making it act more like a polling app than a game). I created a custom surface view with its own thread that gets called whenever drawing needs to be done (I modeled it after the sample LunarLander android game in the SDK).

The thing is, I want the game itself to be a fixed size (208x160) and then scale up to the size of the screen. The problem I'm having is there doesn't seem to be anyway to do so from within the normal parameters of the extended SurfaceView in XML. The game in its current state is illustrated below. I'm trying to make it stretch to the height of the screen and maintain ratio on the width, while also not adding to the viewable game (keeping it fixed, regardless of screen size).

https://i.stack.imgur.com/EWIdE.png (I was going to post the actual image in-line. Spam prevention can bite me >.<)

Currently I'm getting the SurfaceView's canvas and drawing directly to it, and supplying my size as a hard-coded pixel dimension (208px, 160px).

This is what the thread currently looks like in getting the canvas and drawing to it. What would be a better way to draw to a canvas without changing the size I want the game to virtually take up?

@Override
    public void run()
    {
        Canvas c = null;
        try
        {
            c = surfaceHolder.lockCanvas(null);
            synchronized (surfaceHolder)
            {
                draw(c);
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (c != null)
                surfaceHolder.unlockCanvasAndPost(c);
        }
    }

The draw method is this (StringBuilder is my own coded Object in the engine):

public void draw(Canvas canvas)
{       
    canvas.drawColor(Color.GRAY);
    StringBuilder stringBuilder = new StringBuilder(canvas, Color.BLACK, letters);
    stringBuilder.drawString("Oh, hello there!");
    stringBuilder.setLocation(10, 20);
    stringBuilder.drawString("Why am I so small?");
}

Any ideas?

like image 458
TheAssailant Avatar asked May 22 '12 18:05

TheAssailant


1 Answers

Assume your draw function is renamed to drawBase instead. Here is how you do it:

public void draw(Canvas canvas)
{
    final float scaleFactor = Math.min( getWidth() / 208.f, getHeight() / 160.f );
    final float finalWidth = 208.f * scaleFactor;
    final float finalHeight = 160.f * scaleFactor;
    final float leftPadding = ( getWidth() - finalWidth ) / 2;
    final float topPadding =  ( getHeight() - finalHeight ) / 2;

    final int savedState = canvas.save();
    try {
        canvas.clipRect(leftPadding, topPadding, leftPadding + finalWidth, topPadding + finalHeight);

        canvas.translate(leftPadding, topPadding);
        canvas.scale(scaleFactor, scaleFactor);

        drawBase(canvas);
    } finally {
        canvas.restoreToCount(savedState);
    }
}
like image 63
K-ballo Avatar answered Oct 06 '22 16:10

K-ballo