VS85 Team 的个人资料XNALearners日志列表留言簿 工具 帮助

日志


2008/3/20

Testo e primitive 2D in XNA

English Version

 

by Giuseppe Maggiore

 

Introduzione

XNA supporta in modo molto ordinato il disegno di oggetti bidimensionali su schermo. Tipicamente queste operazioni di disegno verranno eseguite durante la chiamata a Game.Draw, tramite un oggetto helper, lo sprite batch:

SpriteBatch spriteBatch; 

Lo sprite batch, come suggerisce il nome, e’ un sistema di raggruppamento ottimizzato di chiamate di disegno di sprites. Gli sprites sono piccoli rettangoli che vogliamo prendere da una texture e mostrare su schermo. Tale sistema richiede che le chiamate di disegno avvengano in mezzo tra apertura (Begin()) e chiusura (End()), in modo che lo sprite batch le possa ottimizzare o ordinare come preferisce:

spriteBatch.Begin();

/*...*/

spriteBatch.End();

La funzione Begin prende opzionalmente tre parametri:

· SpriteBlendMode che specifica se vogliamo usare alpha blending, alpha blending additivo o nessun blending

· SpriteSortMode che indica in che ordine lo sprite batch deve disegnare: per z crescente, per z decrescente, immediato, deferito alla chiamata di End()

· SaveStateMode per far si che la chiamata a End() ripristini tutte le flags della scheda video ai valori che avevano prima della chiamata a Begin(): utile per rendere l’uso dello SpriteBatch del tutto trasparente agli altri moduli di disegno

SpriteBatch.Draw

Quando arriva il momento di disegnare ecco che entra in gioco la prima delle due primitive di disegno: SpriteBatch.Draw. Tale funzione prende come parametri di base una texture, un rettangolo in pixel su schermo in cui la texture va messa, ed un colore per cui moltiplicare quello della texture. Quindi per inserire un’immagine come sfondo dovremo scrivere:

spriteBatch.Draw(Content.Load<Texture2D>("background"),
    new Rectangle(0, 0, 800, 600), Color.White);

Questa chiamata a draw sta indicando di disegnare la texture “background” con il suo colore originale (moltiplicare per Color.White lascia inalterato il colore) nell’area di schermo che inizia al pixel (0,0) ed ha dimensioni (800,600) pixel:

clip_image002

Ipotizziamo ora di voler disegnare un oggetto di qualche tipo che si muova ruotando sullo schermo. Diciamo anche che tale oggetto ha posizione “Position” ed e’ ruotato di “Rotation” radianti rispetto al suo centro. Allora dovremo invocare nuovamente draw, ma prima sara’ necessario introdurre un nuovo parametro di draw che definisce il centro di rotazione. Di default infatti uno sprite viene fatto ruotare intorno al suo angolo in alto a sinistra, dove effettivamente l’oggetto viene posizionato:

clip_image004 clip_image006clip_image008

Questo pero’ non e’ affatto cio’ che desideriamo: vorremmo che lo sprite ruotasse attorno al suo centro, non attorno ad un suo angolo. Esiste una versione della funzione Draw che accetta anche il parametro origin. Tale parametro serve per indicare quale e’ l’origine della texture, rispetto a cui Position e’ definita. Nel disegno qui accanto vediamo che l’origine e’ stata messa al centro della texture (25,25) posto che la texture fosse grande (50,50). Qualsiasi rotazione adesso sara’ relativa al centro e non agli angoli, proprio come desideriamo. La chiamata risultante per ottenere questo effetto sara’:

spriteBatch.Draw(Content.Load<Texture2D>("rotating_particle"),
    new Rectangle((int)Position.X, (int)Position.Y, (int)Size.X, (int)Size.Y),
    null, Color.White, Rotation, Size / 2, SpriteEffects.None, 0.0f);

in cui possiamo notare come la texture sia posizionata in Position, abbia dimensione Size, e sia centrata sul suo punto medio Size/2, affinche’ le rotazioni usino quello come riferimento e non l’angolo (0,0).

SpriteBatch.DrawString

Per disegnare testo la situazione non e’ molto piu’ complicata. Prima pero’ ci serve un file di contenuto di tipo spritebatch, per descrivere il tipo di font da usare, i suoi parametri (dimensione, bold, etc.) e il set di caratteri da caricare (di default si usa Basic Latin, che va dallo spazio ASCII sino alla tilde ~). Creiamo il file di contenuto “Arial.SpriteFont”:

clip_image009clip_image010clip_image012clip_image014

clip_image016

In questo semplice file xml sono specificati il tipo del font di sistema da usare per il disegno (arial), la sua dimensione in punti (14), spaziatura, uso di kerning, stile (bold, regular, italic) e i range di caratteri da usare (qui vediamo che e’ stato richiesto di caricare i caratteri dal 32 al 126, ossia il set Basic Latin menzionato prima).

Ipotizziamo di voler disegnare la doverosa stringa “Hello World!” su schermo. La chiamata opportuna risulta essere:

spriteBatch.DrawString(Content.Load<SpriteFont>("arial"), "Hello World!",
    new Vector2(10, 70), Color.LightBlue);

Vediamo che la funzione DrawString richiede lo SpriteFont da usare per costruire la scritta, il testo da scrivere, la posizione su schermo (dell’angolo in alto a sinistra della scritta) ed infine il colore del testo. Effettivamente questa invocazione produce la scritta desiderata in azzurro a partire dal pixel (10,70):

clip_image018

Se pero’ volessimo far ruotare la scritta intorno al suo centro? Avremmo bisogno di specificare l’Origin della nostra DrawString a meta’ della dimensione della stringa disegnata, ma per fare cio’ e’ necessario sapere quanto grande risultera’ la stringa su schermo. SpriteFont fortunatamente e’ in grado di fornirci questa misura:

SpriteFont Arial = Content.Load<SpriteFont>("arial");
Vector2 stringSize = Arial.MeasureString("Hello World!");

Tramite cui possiamo disegnare la nostra stringa di testo roteante intorno al proprio centro:

float Rotation = (float)gameTime.TotalGameTime.TotalSeconds;
SpriteFont arialBig = Content.Load<SpriteFont>("arialBig");
Vector2 stringSize = arialBig.MeasureString("Hello World!");
spriteBatch.DrawString(arialBig, "Hello World!", new Vector2(400, 300),
    Color.Yellow, Rotation, stringSize / 2, 1.0f, SpriteEffects.None, 0.0f);

clip_image002[4]

Font disegnati a mano

Tutto cio’ che abbiamo visto finora e’ relativo a fonts true type che siano installati nel sistema. Immaginiamo pero’ di avere un font disegnato a mano da un artista, come quello riportato in figura:

clip_image004

Sara’ possibile usare questo font non come texture (ossia importandolo direttamente), ma elaborarlo con un content processor appropriato e caricarlo in quanto sprite font. Il primo passo e’ quello di importare il file tra i contenuti del nostro gioco:

clip_image006

A questo punto sappiamo che il file dovra’ essere importato con un caricatore di texture, ma vogliamo che sia processato con un processor in grado di leggere i singoli caratteri e metterceli a disposizione. Tale processor e’ chiamato Sprite Font Texture - XNA Framework, e puo’ essere attivato dalle proprieta’ del file “customfont.png”:

clip_image002[6]

Da tenere a mente che il font, per funzionare correttamente, dovra’ rispettare due punti fondamentali:

· Il canale alfa dovra’ rispecchiare quali parti del testo sono caratteri e quali sono lo sfondo dei caratteri

· In mezzo tra i vari caratteri dovra’ esserci un colore magenta con alfa impostato ad opaco

Ed e’ fatta, a questo punto e’ possibile caricare ed usare il nostro font come vedete qui sotto:

spriteBatch.DrawString(Content.Load<SpriteFont>("customfont"), "test-text",
    new Vector2(10, 10), Color.LightBlue);

Caratteri Unicode speciali

Ipotizziamo adesso di tornare al caso iniziale, ossia di caricare un font true type installato nel sistema. Vogliamo pero’ poter inserire dei simboli speciali, nel caso specifico vogliamo vedere come stampare tramite sprite font i caratteri elencati di seguito: “€™∞”. Se proviamo direttamente a stampare una stringa del genere pero’ otterremo un’errore runtime che causera’ il crash del nostro gioco! Ricordiamoci che alla creazione dello sprite font abbiamo visto che alla fine del file generato automaticamente ci sono una serie di “character regions”, che denotano gli insiemi di caratteri che intendiamo usare. Di default il solo insieme caricato e’ il cosiddetto Basic Latin, ma possiamo estenderlo in due modi. Il primo e’ naturalmente quello di trovare una lista di caratteri unicode ed aggiungere a mano i codici dei caratteri desiderati nella definizione dello sprite font. La seconda strada e’ apparentemente piu’ complessa, ma in realta’ ci da’ molto piu’ controllo. Per prima cosa creiamo una custom pipeline nella soluzione che contiene il nostro gioco:

clip_image004[4]

La sola classe che deve essere contenuta nel nostro processor sara’ una che aggiunge i caratteri desiderati al set di caratteri da caricare, e quindi lancera’ il processor originale con il set di caratteri desiderato:

namespace FontProcessor
{
    [ContentProcessor(DisplayName = "ExtendedCharacterSetFontProcessor")]
    public class ContentProcessor1 : FontDescriptionProcessor
    {
        public override SpriteFontContent Process(FontDescription input,
            ContentProcessorContext context)
        {
            input.Characters.Add('€');
            input.Characters.Add('™');
            input.Characters.Add('∞');            

            // should load this list of characters from a file!

            return base.Process(input, context);
        }
    }
}

A questo punto aggiungiamo questo nuovo progetto tra le references del progetto content:

clip_image006[4]

Ed infine definiamo come font processor dello sprite font da estendere il processor appena creato:

clip_image008

Il font arial adesso sara’ identico a prima, ma in aggiunta supportera’ i caratteri che il nuovo processor aggiunge, rendendo una chiamata come

spriteBatch.DrawString(Content.Load<SpriteFont>("arial"), "€™∞",
    new Vector2(10, 70), Color.LightBlue);

perfettamente valida!

Conclusione

Trovate il sorgente online qui, mentre il webcast in cui viene scritto e spiegato nel dettaglio tutto il sorgente puo’ essere scaricato da questo link, o guardato direttamente nella sua versione low-res embedded:

 

评论 (7)

请稍候...
很抱歉,您输入的评论太长。请缩短您的评论。
您没有输入任何内容,请重试。
很抱歉,我们当前无法添加您的评论。请稍后重试。
若要添加评论,需要您的家长授予您相应权限。请求权限
您的家长禁用了评论功能。
很抱歉,我们当前无法删除您的评论。请稍后重试。
您已超过了一天之内允许提供的评论数上限。请在 24 小时后重试。
因为我们的系统表明您可能在向其他用户提供垃圾评论,您的帐户已禁用了评论功能。如果您认为我们错误地禁用了您的帐户,请联系 Windows Live 支持部门
完成下面的安全检查,您提供评论的过程才能完成。
您在安全检查中键入的字符必须与图片或音频中的字符一致。

若要添加评论,请使用您的 Windows Live ID 登录(如果您使用过 Hotmail、Messenger 或 Xbox LIVE,您就拥有 Windows Live ID)。登录


还没有 Windows Live ID 吗?请注册

VS85 Team发表:
Ciao Zaroc, grazie dei complimenti!!
Come ti ho scritto anche via mail, il file particle.dds non c'e'
nel sorgente scaricabile. Nel sorgente trovi il file borg_capture0.dds,
che e' del tutto equivalente a particle.
 
Giulia Costantini
7 月 20 日
匿名 的图片
Zaroc 发表:
Ciao, tutorial chiari ed esaustivi, complimenti!!!
Ma dov'è il file Particle.dds? non lo trovo...

Grazie, Zaroc
7 月 16 日
VS85 Team发表:
Ciao, prima di mettermi a leggere il codice volevo assicurarmi di una cosa che potrebbe avere causato il problema: oltre a inserire la reference della custom pipeline nel progetto del gioco, l'hai inserita anche nelle references del progetto "Content" (che sta dentro al progetto principale del gioco)? Va inserita anche li', altrimenti non funzionera'! Facci sapere questa cosa e se anche cosi' non dovesse andare, allora guardero' attentamente il codice che hai postato!
 
Giulia Costantini
4 月 13 日
匿名 的图片
:D 发表:


Scusate, mi sono perso sui caratteri unicode..
Perchè il mio Content Processor non appare nella lista da cui scegliere come processare il file?

Ho generato una nuovo progetto custom pipeline nella soluzione e l'ho messo tra le reference del progetto del gioco ma "ExtendedCharacterSetFontProcessor" non viene elencato tra i processor diponibili sulle proprietà del file...

Dove posso aver sbagliato?

Il codice del file ContentProcessor1.cs è questo:

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;

// TODO: replace these with the processor input and output types.
using TInput = System.String;
using TOutput = System.String;

namespace SPACE_RUNNER
{
    [ContentProcessor(DisplayName = "ExtendedCharacterSetFontProcessor")]
    public class ContentProcessor1 : FontDescriptionProcessor
    {
        public override SpriteFontContent Process(FontDescription input,
            ContentProcessorContext context)
        {
            input.Characters.Add('€');
            input.Characters.Add('™');
            input.Characters.Add('∞');

            // should load this list of characters from a file!

            return base.Process(input, context);
        }
    }
}

Che mi sono perso?
4 月 12 日
VS85 Team发表:
Grazie a entrambi, avere del feedback sul proprio lavoro e' molto importante, specie se questo feedback e' positivo ;) Continuate a darci le vostre opinioni e scrivete per qualsiasi domanda/dubbio!
 
Giulia Costantini
3 月 27 日
Alex发表:
ancora complimenti!

Sta diventando davvero un operazione corposa e rilevante... e sopratutto di ottima qualità.
3 月 21 日
Ottimo! Non vedo l'ora che si arrivi ai tutorial sulle collisioni! Sono la mia dannazione..
Intanto ho implementato l'aternanza notte/giorno su Space Running, mio primo esperimento in XNA (un video sull'effetto notte/giorno è su youtube a questo link anche se il video in bassa risoluzione omette tutti i dettagli..)

Volevo presentarmi per bene, non so se potrà essere d'aiuto a qualcuno, ma uso 3DSMAX da più di dieci anni e se dovessero servire modelli o informazioni, io sono disponibile.
Questo è il mio sito: http://www.wilez.it/ dove in realtà non elenco i miei lavori in 3D ma basta guardare il sito..

Se può servire a qualcuno, molto tempo fa scrissi un tutorial sulle mappature UVW per 3DSMAX che potete trovare a questo link.

Lavorai anche con le normal map con cui feci dei livelli e dei modelli per un gioco che non vide mai la luce (shot livello)
Ho lavorato anche in Hi-poly ma molte cose sono andate perse (folletto di ceramica) (pallone) (team biliardo).

Se qualcuno usa il Max per i propri lavori in XNA ed ha domande sulla modellazione o problemi vari (come l'exporting in .X o .FBX) io sono felice di aiutare.

Ciao e continuate così!

3 月 20 日

引用通告

此日志的引用通告 URL 是:
http://vs85team.spaces.live.com/blog/cns!B49FFA0EB319A219!353.trak
引用此项的网络日志