Вывод отладочной информации

Вывод отладочной информации

Часто во время разработки приложения может быть полезным вывод какой-либо дополнительной информации на экран.

В рамках этой статьи мы создадим простой игровой компонент, который поможет нам легко справляться с этой задачей.


Пока ограничимся следующей идеей: нам нужно выводить некоторый набор пар ключ-значение. Скорее всего, мы будем выводить значения некоторых переменных (параметры шейдера, позиция камеры и т.д.).

Создадим новый класс DebugInfo, который будет унаследован от DrawableGameComponent.


class
DebugInfo : DrawableGameComponent

{


private
static
Dictionary<string, string> info = new
Dictionary<string,string>();


public DebugInfo(Game game) : base(game)

{

}


public
static
void SetVariable(string key, string value)

{


if (info.ContainsKey(key))

{

info[key] = value;

}


else

{

info.Add(key, value);

}

}

}

DebugInfo содержит словарь info, в котором и будут храниться значения переменных в стороковой форме. Стоит обратить внимание на то, что словарь является статическим, это сделано для того, чтобы можно было легко обратиться к нему их любого места приложения.

Метод SetVariable устанавливает новое значение переменной или добавляет новую пару в словарь.

Теперь добавим новый шрифт DebugFont.spritefont в папку Content, загрузим его и воспользуемся им для вывода отладочной информации на экран.

DebugFont.spritefont (в файле пока ничего не изменено, это то, что создается в XNA Game Studio по умолчанию)

<?xml
version=«1.0«
encoding=«utf-8«?>

<!—

This file contains an xml description of a font, and will be read by the XNA

Framework Content Pipeline. Follow the comments to customize the appearance

of the font in your game, and to change the characters which are available to draw

with.

—>

<XnaContent
xmlns:Graphics=«Microsoft.Xna.Framework.Content.Pipeline.Graphics«>

<Asset
Type=«Graphics:FontDescription«>

<!—

Modify this string to change the font that will be imported.


—>

<FontName>Kootenay</FontName>

<!—

Size is a float value, measured in points. Modify this value to change

the size of the font.


—>

<Size>14</Size>

<!—

Spacing is a float value, measured in pixels. Modify this value to change

the amount of spacing in between characters.


—>

<Spacing>0</Spacing>

<!—

UseKerning controls the layout of the font. If this value is true, kerning information

will be used when placing characters.


—>

<UseKerning>true</UseKerning>

<!—

Style controls the style of the font. Valid entries are «Regular», «Bold», «Italic»,

and «Bold, Italic», and are case sensitive.


—>

<Style>Regular</Style>

<!—

If you uncomment this line, the default character will be substituted if you draw

or measure text that contains characters which were not included in the font.


—>

<!— <DefaultCharacter>*</DefaultCharacter> —>

<!—

CharacterRegions control what letters are available in the font. Every

character from Start to End will be built and made available for drawing. The

default range is from 32, (ASCII space), to 126, (‘~’), covering the basic Latin

character set. The characters are ordered according to the Unicode standard.

See the documentation for more information.


—>

<CharacterRegions>

<CharacterRegion>

<Start> </Start>

<End>~</End>

</CharacterRegion>

</Asset>

</XnaContent>


class
DebugInfo : DrawableGameComponent

{


SpriteFont font;


SpriteBatch spriteBatch;


private
static
Dictionary<string, string> info = new
Dictionary<string,string>();


public DebugInfo(Game game) : base(game)

{

}


public
static
void SetVariable(string key, string value)

{


if (info.ContainsKey(key))

{

info[key] = value;

}


else

{

info.Add(key, value);

}

}


protected
override
void LoadContent()

{

font = Game.Content.Load<SpriteFont>(«DebugFont»);

spriteBatch = new
SpriteBatch(Game.GraphicsDevice);


base.LoadContent();

}


public
override
void Draw(GameTime gameTime)

{


StringBuilder sb = new
StringBuilder();


foreach (var key in info.Keys)

{

sb.AppendLine(string.Format(«{0}={1}», key, info[key]));

}

spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);

spriteBatch.DrawString(font, sb, Vector2.Zero, Color.White);

spriteBatch.End();


base.Draw(gameTime);

}

}

Ничего особого в этом коде нет, в методе Draw при помощи StringBuilder создается строка, содержащая всю отладочную информацию, которая выводится на экран при помощи SpriteBatch.DrawString.

Вернемся к нашему прошлому примеру и будем выводить значение f (смещение для модели освещения Ламберта) при помощи нового игрового компонента.

DebugInfo debugInfo;


protected
override
void Initialize()

{


// TODO: Add your initialization logic here

Components.Add(new
FPSCounter(this));

light = new
Light(this);

light.DefaultPosition = new
Vector3(1, 1, 1);

Components.Add(light);

camera = new
FreeCamera(this, new
Vector3(0, 0, 2), Vector3.Zero);

Components.Add(camera);

debugInfo = new
DebugInfo(this);

Components.Add(debugInfo);

teapot = new
TeapotPrimitive(GraphicsDevice);


base.Initialize();

}


protected
override
void Update(GameTime gameTime)

{


// Allows the game to exit


if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)


this.Exit();


// TODO: Add your update logic here


KeyboardState state = Keyboard.GetState();


if (state.IsKeyDown(Keys.Z) && oldState.IsKeyUp(Keys.Z))

{

f = f — 0.1f;

}


if (state.IsKeyDown(Keys.X) && oldState.IsKeyUp(Keys.X))

{

f = f + 0.1f;

}


DebugInfo.SetVariable(«f», f.ToString());

oldState = state;


base.Update(gameTime);

}


Если мы сейчас попробуем использовать русские символы в названии или значении переменной (например, так DebugInfo.SetVariable(«тест», f.ToString())), то мы получим исключение следующего вида:

The character ‘т’ (0x0442) is not available in this SpriteFont. If applicable, adjust the font’s start and end CharacterRegions to include this character.

Имя параметра: character

В исключении нам сообщают о том, что русский набор символов не поддерживается. Чтобы добавить поддержку русских символов, вернемся в файлу со шрифтом и добавим в его описание следующее:

<CharacterRegions>

<CharacterRegion>

<Start> </Start>

<End>~</End>

</CharacterRegion>

<CharacterRegion>

<Start>А</Start>

<End>я</End>

</CharacterRegion>

</CharacterRegions>

Таким образом, мы исправили ошибку:


Исходный код:

Game1.cs

using System;

using System.Collections.Generic;

using System.Linq;

using Microsoft.Xna.Framework;

using Microsoft.Xna.Framework.Audio;

using Microsoft.Xna.Framework.Content;

using Microsoft.Xna.Framework.GamerServices;

using Microsoft.Xna.Framework.Graphics;

using Microsoft.Xna.Framework.Input;

using Microsoft.Xna.Framework.Media;

using Microsoft.Xna.Framework.Net;

using Microsoft.Xna.Framework.Storage;

using Primitives3D;

namespace MyGame

{


///
<summary>


/// This is the main type for your game


///
</summary>


public
class
Game1 : Microsoft.Xna.Framework.Game

{


GraphicsDeviceManager graphics;


SpriteBatch spriteBatch;


Light light;


DebugInfo debugInfo;


Effect effect;


TeapotPrimitive teapot;


FreeCamera camera;


KeyboardState oldState;


float f;


public Game1()

{

graphics = new
GraphicsDeviceManager(this);

Content.RootDirectory = «Content»;

graphics.SynchronizeWithVerticalRetrace = false;

IsFixedTimeStep = false;

}


///
<summary>


/// Allows the game to perform any initialization it needs to before starting to run.


/// This is where it can query for any required services and load any non-graphic


/// related content. Calling base.Initialize will enumerate through any components


/// and initialize them as well.


///
</summary>


protected
override
void Initialize()

{


// TODO: Add your initialization logic here

Components.Add(new
FPSCounter(this));

light = new
Light(this);

light.DefaultPosition = new
Vector3(1, 1, 1);

Components.Add(light);

camera = new
FreeCamera(this, new
Vector3(0, 0, 2), Vector3.Zero);

Components.Add(camera);

debugInfo = new
DebugInfo(this);

Components.Add(debugInfo);

teapot = new
TeapotPrimitive(GraphicsDevice);


base.Initialize();

}


///
<summary>


/// LoadContent will be called once per game and is the place to load


/// all of your content.


///
</summary>


protected
override
void LoadContent()

{


// Create a new SpriteBatch, which can be used to draw textures.

spriteBatch = new
SpriteBatch(GraphicsDevice);


// TODO: use this.Content to load your game content here

effect = Content.Load<Effect>(«light»);

}


///
<summary>


/// UnloadContent will be called once per game and is the place to unload


/// all content.


///
</summary>


protected
override
void UnloadContent()

{


// TODO: Unload any non ContentManager content here

}


///
<summary>


/// Allows the game to run logic such as updating the world,


/// checking for collisions, gathering input, and playing audio.


///
</summary>


///
<param name=»gameTime»>Provides a snapshot of timing values.</param>


protected
override
void Update(GameTime gameTime)

{


// Allows the game to exit


if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)


this.Exit();


// TODO: Add your update logic here


KeyboardState state = Keyboard.GetState();


if (state.IsKeyDown(Keys.Z) && oldState.IsKeyUp(Keys.Z))

{

f = f — 0.1f;

}


if (state.IsKeyDown(Keys.X) && oldState.IsKeyUp(Keys.X))

{

f = f + 0.1f;

}


DebugInfo.SetVariable(«f», f.ToString());


DebugInfo.SetVariable(«тест», f.ToString());

oldState = state;


base.Update(gameTime);

}


///
<summary>


/// This is called when the game should draw itself.


///
</summary>


///
<param name=»gameTime»>Provides a snapshot of timing values.</param>


protected
override
void Draw(GameTime gameTime)

{

GraphicsDevice.Clear(Color.Black);


Matrix view = camera.View;


Matrix proj = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), GraphicsDevice.Viewport.AspectRatio, 0.1f, 10f);


// TODO: Add your drawing code here

effect.Parameters[«World»].SetValue(Matrix.CreateRotationY((float)gameTime.TotalGameTime.TotalSeconds));

effect.Parameters[«View»].SetValue(view);

effect.Parameters[«Projection»].SetValue(proj);

effect.Parameters[«LightPosition»].SetValue(light.Position);

effect.Parameters[«f»].SetValue(f);

teapot.Draw(effect);

light.Draw(view, proj);


base.Draw(gameTime);

}

}

}

DebugInfo.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.Xna.Framework;

using Microsoft.Xna.Framework.Graphics;

namespace MyGame

{


class
DebugInfo : DrawableGameComponent

{


SpriteFont font;


SpriteBatch spriteBatch;


private
static
Dictionary<string, string> info = new
Dictionary<string,string>();


public DebugInfo(Game game) : base(game)

{

}


public
static
void SetVariable(string key, string value)

{


if (info.ContainsKey(key))

{

info[key] = value;

}


else

{

info.Add(key, value);

}

}


protected
override
void LoadContent()

{

font = Game.Content.Load<SpriteFont>(«DebugFont»);

spriteBatch = new
SpriteBatch(Game.GraphicsDevice);


base.LoadContent();

}


public
override
void Draw(GameTime gameTime)

{


StringBuilder sb = new
StringBuilder();


foreach (var key in info.Keys)

{

sb.AppendLine(string.Format(«{0}={1}», key, info[key]));

}

spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);

spriteBatch.DrawString(font, sb, Vector2.Zero, Color.White);

spriteBatch.End();


base.Draw(gameTime);

}

}

}

DebugFont.spritefont

<?xml
version=«1.0«
encoding=«utf-8«?>

<!—

This file contains an xml description of a font, and will be read by the XNA

Framework Content Pipeline. Follow the comments to customize the appearance

of the font in your game, and to change the characters which are available to draw

with.

—>

<XnaContent
xmlns:Graphics=«Microsoft.Xna.Framework.Content.Pipeline.Graphics«>

<Asset
Type=«Graphics:FontDescription«>

<!—

Modify this string to change the font that will be imported.


—>

<FontName>Kootenay</FontName>

<!—

Size is a float value, measured in points. Modify this value to change

the size of the font.


—>

<Size>14</Size>

<!—

Spacing is a float value, measured in pixels. Modify this value to change

the amount of spacing in between characters.


—>

<Spacing>0</Spacing>

<!—

UseKerning controls the layout of the font. If this value is true, kerning information

will be used when placing characters.


—>

<UseKerning>true</UseKerning>

<!—

Style controls the style of the font. Valid entries are «Regular», «Bold», «Italic»,

and «Bold, Italic», and are case sensitive.


—>

<Style>Regular</Style>

<!—

If you uncomment this line, the default character will be substituted if you draw

or measure text that contains characters which were not included in the font.


—>

<!— <DefaultCharacter>*</DefaultCharacter> —>

<!—

CharacterRegions control what letters are available in the font. Every

character from Start to End will be built and made available for drawing. The

default range is from 32, (ASCII space), to 126, (‘~’), covering the basic Latin

character set. The characters are ordered according to the Unicode standard.

See the documentation for more information.


—>

<CharacterRegions>

<CharacterRegion>

<Start> </Start>

<End>~</End>

</CharacterRegion>

<CharacterRegion>

<Start>А</Start>

<End>я</End>

</CharacterRegion>

</CharacterRegions>

</Asset>

</XnaContent>

Реклама
Запись опубликована в рубрике Uncategorized с метками , , , . Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s