2012-05-22 5 views
5

Ho cercato di cercare una risposta e non riesco a trovarne uno (è un approccio abbastanza non convenzionale alla programmazione di un gioco per Android, quindi è prevedibile, almeno penso).Ridimensionamento di SurfaceView fisso per il riempimento verticale e mantenimento delle proporzioni

Sto provando a creare un gioco RPG in stile GBA su Android (per la nostalgia). OpenGL non era abbastanza buono per me perché non era abbastanza personalizzabile nel ciclo di gioco (voglio solo disegnare per essere chiamato quando c'è qualcosa che deve essere disegnato, dal momento che voglio che questo gioco sia super efficiente nel modo in cui usa la batteria, il che significa renderlo più simile a un'app di polling che a un gioco). Ho creato una vista di superficie personalizzata con il proprio thread che viene chiamato ogni volta che si deve eseguire il disegno (l'ho modellato dopo il gioco Android di esempio LunarLander nell'SDK).

Il fatto è che voglio che il gioco stesso abbia una dimensione fissa (208x160) e quindi riduca le dimensioni dello schermo. Il problema che sto riscontrando è che non sembra esserci comunque lo stesso all'interno dei normali parametri del SurfaceView esteso in XML. Il gioco nel suo stato attuale è illustrato di seguito. Sto provando a farlo allungare all'altezza dello schermo e mantenere il rapporto sulla larghezza, pur non aggiungendo al gioco visibile (mantenendolo fisso, indipendentemente dalle dimensioni dello schermo).

http://i.stack.imgur.com/EWIdE.png (stavo per pubblicare l'immagine reale in linea. Prevenzione spam mi può mordere>. <)

Attualmente mi sto tela del SurfaceView e disegnando direttamente ad esso, e fornendo la mia taglia come una dimensione pixel hard-coded (208px, 160px).

Ecco come si presenta attualmente il thread per ottenere il disegno e il disegno su di esso. Quale sarebbe un modo migliore per disegnare su una tela senza cambiare le dimensioni che voglio che il gioco prenda praticamente?

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

Il metodo draw è questo (StringBuilder è il mio proprio oggetto codificato nel motore):

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

Tutte le idee?

+0

Hai considerato di ridimensionare la tua tela per adattarla a 'SurfaceView'? –

+0

@ K-ballo La tela proviene dallo stesso SurfaceView .... chiamando run() (che è il metodo di esecuzione del thread) crea una tela e quindi imposta la tela su SurfaceHolder, che quindi blocca la tela, quindi disegno ad esso e poi sbloccare e postare alla vista quando fatto. A meno che non legga il codice originale dal campione LunarLander sbagliato ... – TheAssailant

+0

Quindi ...? Non puoi semplicemente chiamare 'scale' su di esso? –

risposta

4

Si supponga invece che la funzione draw sia stata rinominata in drawBase. Ecco come si fa:.

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

Il problema è che sto ottenendo una tela da SurfaceView e SurfaceView ha già una dimensione predeterminata dal layout. Anche se mi piace questo codice, non crea ancora una visualizzazione di gioco che ha un rapporto fisso di 1,3 con le scatole del pilastro come imbottitura sui lati. C'è ancora un gioco extra sul lato destro e sarà una distrazione per i giocatori. – TheAssailant

+0

@ TheAssailant: Lì, ha regolato il codice per il riempimento e il clipping. –

+0

Grazie! Questo funziona (quasi) perfettamente. Per quello di cui ho bisogno ho semplicemente impostato il topMargin su 0 e ho usato l'altezza originale fornita per il ritaglio. Ha funzionato come un fascino. Ma sì, questo è perfetto, grazie mille! – TheAssailant

1

(La risposta accettata da K-ballo va bene, ma nei 2,5 anni da quando è stato scritto schermi hanno ottenuto un più denso molto, il che significa che il rendering software è sempre più costoso E ' importante per ottenere l'hardware per aiutare a fare il lavoro.)

Per eseguire il rendering a una risoluzione specifica, è possibile utilizzare SurfaceHolder#setFixedSize(). Questo imposta la dimensione della superficie in modo che corrisponda alle dimensioni esatte specificate. La superficie verrà ridimensionata per adattarsi alla dimensione della vista dal ridimensionatore hardware, che sarà più efficiente del ridimensionamento del software. La funzione è descritta in questo blog post e puoi vederla in azione nell'attività "attivatore dello scaler hardware" Grafika's (demo video).

È possibile mantenere le proporzioni corrette per i display di dimensioni diverse regolando la dimensione della superficie (che è ciò che Grafika fa per quella demo), o modificando il layout per visualizzare la lettera o il pilastro. È possibile trovare un esempio di quest'ultimo nella classe AspectFrameLayout di Grafika, che viene utilizzata per varie demo di videocamere e video.

A questo proposito:

OpenGL non era abbastanza buono per me perché non era abbastanza personalizzabile nel ciclo di gioco (voglio unico inconveniente di essere chiamato quando c'è qualcosa che deve essere disegnato ...

Hai due opzioni. in primo luogo, è possibile utilizzare GLSurfaceView#setRenderMode() per disabilitare fusione continua. in secondo luogo, non è necessario utilizzare GLSurfaceView di utilizzare OpenGL. la maggior parte del codice di GLES in Grafika opera su SurfaceView o TextureView.