XNA и веб-сервисы

XNA и веб-сервисы

В этой статье я опишу возможности по работе с веб-сервисами в XNA Framework. Сразу хочу уточнить, что описанные методы проверялись только для Windows приложений. Для Xbox 360 они, скорее всего, работать не будут, а для Windows Phone 7, теоретически, должны работать.


Зачем могут понадобиться веб-сервисы в XNA играх? На самом деле, можно найти различные применения для сервисов в играх. Приведу несколько наиболее очевидных:

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

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

Создадим новый проект Windows Game 3.1 (я назвал его XNA Service).


Легко заметить, что в проекте отсутствует папка Service References. Это какая-то странная проблема Visual Studio, описанная по ссылке (http://msdn.microsoft.com/library/dd282473.aspx). Видимо, дело в том, что XNA для Xbox не поддерживает работу с веб-сервисами, однако такое ограничение для Windows игр лично мне кажется нелогичным.

Так или иначе проблема эта решаемая, но к ней мы вернемся позже. Пока нужно создать веб-сервис. Для этого в меню Visual Studio выбираем File->Add->New Project. Там во вкладке Web находим WCF Service Application (ASP.NET Web Service Application считается устаревшим).


Получим новый проект, в котором есть только один интересный класс Service1. Название можно изменить, но тогда нужно будет менять и Web.config.


public
class
Service1 : IService1

{


public
string GetData(int value)

{


return
string.Format(«You entered: {0}», value);

}


public
CompositeType GetDataUsingDataContract(CompositeType composite)

{


if (composite.BoolValue)

{

composite.StringValue += «Suffix»;

}


return composite;

}

}

В классе сервиса есть два метода. GetDataUsingDataContract сейчас не очень интересен, а вот GetData мы будем использовать в дальнейших примерах. Метод принимает на вход целочисленное значение и возвращает строку, содержащее это значение.

Сейчас мы никакими стандартными средствами Visual Studio не сможем связать эти два проекта так как нам нужно (то есть так, чтобы MyService действительно использовался как сервис).

Создадим еще одно приложение, на этот раз консольное приложение для Windows.

В контекстном меню нового проекта выбираем Add Service Reference.


Правда перед этим нужно обязательно скомпилировать проект с WCF сервисом, иначе ничего не должно получиться.

В появившемся окне жмем Discover для того, чтобы найти сервис.


После этого жмем ОК. В проекте появится папка Service Reference, с кучей страшных файлов (в Solution Explorer их не видно по умолчанию, но они есть).

Теперь можно использовать сервис в консольном проекте. В основном классе программы напишем:


class
Program

{


static
void Main(string[] args)

{


using (var service = new ServiceReference1.Service1Client())

{


Console.WriteLine(service.GetData(10));

}

}

}

Запускаем и видим надпись «You entered: 10». Все хорошо, сервис работает.

Теперь пришло время подключать наш работающий сервис к XNA приложению.

Пробовать просто подключать консольное приложение к XNA приложению не имеет смысла, при запуске мы просто увидим непонятную ошибку.

Теперь собственно решение проблемы, может оно и не слишком красиво, но лично я ничего лучше найти не смог:

Перехом к папке с консольным приложением и копируем оттуда папку Service References в папку с проектом XNA игры. Для этого я создаю аналогичную структуру папок в проекте с XNA приложением (обязательно из Visual Studio командом Add -> New Folder).


После этого копируем все файлы в новую папку и добавляем в Visual Studio через Add -> Existing Item. Для файла Reference.cs нужно выставить Build Action равным Compile, для остальных файлов – None.

Теперь нужно добавить в проект ссылки на некоторые сборки, это как минимум System.Runtime.Serialization и System.ServiceModel.

Теперь все должно успешно скомпилироваться.

Воспользуемся сервисом, например, так:


protected
override
void Initialize()

{


// TODO: Add your initialization logic here


using (var service = new ConsoleApplication1.ServiceReference1.Service1Client())

{

Window.Title = service.GetData(10);

}


base.Initialize();

}

Сейчас мы должны увидеть такую ошибку:

Could not find default endpoint element that references contract ‘ServiceReference1.IService1’ in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element.

Нам сообщили о том, что в конфигурационном файле приложения не указана ссылка на сервис, а точнее такого файла просто нет. Создадим его (Add->New Item->Application Configuration File). В новый файл просто скопируем код из кофигурационного файла нашего консольного приложения.

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

<configuration>

<system.serviceModel>

<bindings>

<wsHttpBinding>

<binding
name=«WSHttpBinding_IService1«
closeTimeout=«00:01:00»


openTimeout=«00:01:00«
receiveTimeout=«00:10:00«
sendTimeout=«00:01:00»


bypassProxyOnLocal=«false«
transactionFlow=«false«
hostNameComparisonMode=«StrongWildcard»


maxBufferPoolSize=«524288«
maxReceivedMessageSize=«65536»


messageEncoding=«Text«
textEncoding=«utf-8«
useDefaultWebProxy=«true»


allowCookies=«false«>

<readerQuotas
maxDepth=«32«
maxStringContentLength=«8192«
maxArrayLength=«16384»


maxBytesPerRead=«4096«
maxNameTableCharCount=«16384« />

<reliableSession
ordered=«true«
inactivityTimeout=«00:10:00»


enabled=«false« />

<security
mode=«Message«>

<transport
clientCredentialType=«Windows«
proxyCredentialType=«None»


realm=«» />

<message
clientCredentialType=«Windows«
negotiateServiceCredential=«true»


algorithmSuite=«Default«
establishSecurityContext=«true« />

</security>

</binding>

</wsHttpBinding>

</bindings>

<client>

<endpoint
address=«http://localhost:37408/Service1.svc«
binding=«wsHttpBinding»


bindingConfiguration=«WSHttpBinding_IService1«
contract=«ServiceReference1.IService1»


name=«WSHttpBinding_IService1«>

<identity>

<dns
value=«localhost« />

</identity>

</endpoint>

</client>

</system.serviceModel>

</configuration>

В моем случае он выглядит так, у Вас он будем немного другим, но суть от этого не изменится.

Снова запускаем игру, видим такую картину:

Ну и еще: я в ходе всей статьи использовал конструкцию using для вызова методов сервиса. На самом деле, можно и не использовать ее, если нет необходимости. Например, мы можем создать переменную для сервиса в методе Initialize, а использовать ее где угодно дальше.


Service1Client service;


protected
override
void Initialize()

{


// TODO: Add your initialization logic here

service = new
Service1Client();


base.Initialize();

}


protected
override
void LoadContent()

{


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

spriteBatch = new
SpriteBatch(GraphicsDevice);

Window.Title = service.GetData(10);


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

}

Результат будет точно таким же. Или более интересный вариант, будем вызывать метод из Update, передавая туда случайное число:


Service1Client service;


Random random = new
Random();


protected
override
void Initialize()

{


// TODO: Add your initialization logic here

service = new
Service1Client();


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

Window.Title = service.GetData(random.Next(255));


base.Update(gameTime);

}

Как обычно нужно учитывать, что методы Update и Draw вызываются очень часто и в них не стоит делать таким долгих операций как обращение к сервису, бузе данных и т.д. По крайней мере не каждый раз. Но сейчас все работает локально и это не слишком тормозит игру.

Весь код Game1 сейчас выглядит следующим образом:

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 ConsoleApplication1.ServiceReference1;

namespace XNAService

{


///
<summary>


/// This is the main type for your game


///
</summary>


public
class
Game1 : Microsoft.Xna.Framework.Game

{


GraphicsDeviceManager graphics;


SpriteBatch spriteBatch;


Service1Client service;


Random random = new
Random();


public Game1()

{

graphics = new
GraphicsDeviceManager(this);

Content.RootDirectory = «Content»;

}


///
<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

service = new
Service1Client();


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

}


///
<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

Window.Title = service.GetData(random.Next(255));


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.CornflowerBlue);


// TODO: Add your drawing code here


base.Draw(gameTime);

}

}

}

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

2 комментария на «XNA и веб-сервисы»

  1. Уведомление: Дайджест технических материалов #1 (для разработчиков) - Alex Krakovetskiy blog - Microsoft User Group Винница

  2. Уведомление: Дайджест технических материалов #5 (Windows Phone 7) - Oleksandr Krakovetskiy blog - Microsoft User Group Винница

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

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

Логотип WordPress.com

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

Фотография Twitter

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

Фотография Facebook

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

Google+ photo

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

Connecting to %s