2012-05-22 5 views
5

Ich habe versucht, nach einer Antwort zu suchen und finde sie nicht (es ist ein ziemlich unkonventioneller Ansatz, ein Android-Spiel zu programmieren, also ist zu erwarten, zumindest denke ich).Skalierung eines fixierten SurfaceView, um vertikal zu füllen und das Seitenverhältnis beizubehalten

Ich versuche, ein GBA-Stil RPG-Spiel in Android (für die Nostalgie) zu erstellen. OpenGL war nicht gut genug für mich, weil es in der Spielschleife nicht anpassbar genug war (ich möchte nur, dass etwas aufgerufen wird, wenn etwas gezeichnet werden muss, da ich möchte, dass dieses Spiel in der Art und Weise wie es verwendet wird sehr effizient ist) die Batterie, was bedeutet, dass es eher wie eine Poll-App als ein Spiel zu handeln). Ich habe eine eigene Oberflächenansicht mit einem eigenen Thread erstellt, der immer aufgerufen wird, wenn eine Zeichnung erstellt werden muss (ich habe sie nach dem Beispiel-LunarLander android-Spiel im SDK modelliert).

Die Sache ist, ich möchte das Spiel selbst eine feste Größe (208x160) und dann auf die Größe des Bildschirms skalieren. Das Problem, das ich habe, scheint es nicht zu sein, dies aus den normalen Parametern des erweiterten SurfaceView in XML heraus zu tun. Das Spiel in seinem aktuellen Zustand ist unten dargestellt. Ich versuche, es auf die Höhe des Bildschirms zu strecken und das Verhältnis auf der Breite beizubehalten, während es auch nicht zum sichtbaren Spiel hinzugefügt wird (es wird unabhängig von der Bildschirmgröße beibehalten).

http://i.stack.imgur.com/EWIdE.png (ich würde das eigentliche Bild in-line posten. Spam Prävention kann mich beißen>. <)

derzeit der Surface der Leinwand Ich bin immer und direkt mit ihm zeichnen, und meine Größe liefert als eine hartcodierte Pixeldimension (208px, 160px).

So sieht der Thread derzeit aus, wenn Sie die Zeichenfläche und die Zeichnung dorthin ziehen. Was wäre eine bessere Möglichkeit, auf eine Leinwand zu zeichnen, ohne die Größe zu verändern, die das Spiel virtuell aufnehmen soll?

@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); 
     } 
    } 

Das Draw-Verfahren ist dies (String mein eigenes codiertes Objekt im Motor ist):

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?"); 
} 

Irgendwelche Ideen?

+0

Haben Sie darüber nachgedacht, Ihre Arbeitsfläche so zu skalieren, dass sie der "SurfaceView" entspricht? –

+0

@ K-ballo Der Canvas kommt von der SurfaceView selbst .... Aufruf von run() (was die Laufmethode des Threads ist) erstellt einen Canvas und setzt dann den Canvas auf den SurfaceHolder, der dann den Canvas sperrt, den ich dann zeichne dazu und dann entsperren und post auf die Ansicht, wenn Sie fertig sind. Es sei denn, ich lese den Originalcode aus dem LunarLander Probe falsch ... – TheAssailant

+0

Also ...? Kann man nicht einfach 'scale' drauf ansprechen? –

Antwort

4

Angenommen, Ihre draw-Funktion wird stattdessen in drawBase umbenannt. Hier ist, wie Sie es tun.

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); 
    } 
} 
+0

Das Problem ist, dass ich eine Leinwand von der SurfaceView bekomme und die SurfaceView bereits eine vorgegebene Größe vom Layout hat. Obwohl ich diesen Code mag, erstellt er immer noch keine Spielansicht, die ein festes 1,3-Verhältnis mit Pillar-Boxen als Polsterung an den Seiten hat. Auf der rechten Seite gibt es noch ein zusätzliches Spiel, und es wird eine Ablenkung für die Spieler sein. – TheAssailant

+0

@TheAssialogant: Dort wurde der Code für Padding und Clipping angepasst. –

+0

Vielen Dank! Das funktioniert (fast) perfekt. Für das, was ich brauche, setze ich topMargin einfach auf 0 und benutze die ursprünglich mitgelieferte Höhe für das Clipping. Lief wie am Schnürchen. Aber ja, das ist perfekt, vielen Dank! – TheAssailant

1

(Die akzeptierte Antwort von K-ballo in Ordnung ist, aber in den 2,5 Jahre, da es zeigt haben viel dichter geworden geschrieben wurde, was bedeutet, dass Software-Rendering immer teurer ist es schon wichtig, um die Hardware zu bekommen, um die Arbeit zu erledigen.)

Um mit einer bestimmten Auflösung zu rendern, können Sie SurfaceHolder#setFixedSize() verwenden. Dadurch wird die Größe der Oberfläche auf die von Ihnen angegebenen genauen Abmessungen festgelegt. Die Oberfläche wird so skaliert, dass sie der Größe der Ansicht des Hardware-Scalers entspricht, die effizienter ist als die Software-Skalierung. Die Funktion ist in dieser blog post beschrieben, und Sie können es in Aktion in Grafika's "Hardware Scaler Exerciser" Aktivität (demo video) sehen.

Sie können das richtige Seitenverhältnis für Displays unterschiedlicher Größe beibehalten, indem Sie die Größe der Oberfläche anpassen (was Grafika für diese Demo tut) oder indem Sie das Layout auf Brief- oder Pillar-Box anpassen. Ein Beispiel dafür finden Sie in der AspectFrameLayout-Klasse von Grafika, die für verschiedene Kamera- und Videodemos verwendet wird.

Im Hinblick auf diese:

OpenGL für mich nicht gut genug war, weil es nicht anpassbar genug im Spiel Schleife (ich nur gezeichnet werden soll aufgerufen werden, wenn etwas ist da, die gezogen werden muss ...

Sie haben zwei Möglichkeiten. Erstens, Sie GLSurfaceView#setRenderMode() kontinuierliche Rendering deaktivieren können. Zweitens, Sie brauchen GLSurfaceView nicht verwenden OpenGL zu verwenden. die meisten der GLES Code in Grafika arbeitet auf Surface oder TextureView

Verwandte Themen