Windows Phone 7 Quick Tip: How to use Bing Maps in XNA games

2 篇文章 0 订阅
1 篇文章 0 订阅

Windows Phone 7 Quick Tip: How to use Bing Maps in XNA games

October 22, 2010

A few days ago, a colleague of mine pointed out to this site which shows a Bing Maps usage within XNA game.


I was intrigued how to achieve it… I know that Silverlight applications for Windows Phone 7 could use Bing Maps control (see great usage tutorial here), but XNA games cannot render Silverlight controls…


To solve it, I decided to use a different approach for XNA – download image tiles and present them in XNA game.


Let’s get started. First, in order to use Bing Maps API you need a Bing Maps Account and Application Private Key.
The Bing Maps Account Center allows you to create keys to use the Bing Maps Control, Bing Maps SOAP Services, Bing Maps REST (representational state transfer) Services and Bing Spatial Data Services.


If you already have a key for using Bing, you may skip next few linesSmile.


Without a valid key, you won’t be able to see retrieve Bing Maps content from the web. To create the account use the following steps (shamelessly taken from here)



  1. Open your web browser and go to the following address: http://www.bingmapsportal.com.
  2. Click Create to create a new Bing Maps account using your Windows Live ID.

    alt


  3. On the next page, fill in your details.

    alt


  4. Once you’ve agreed the terms and registered and/or signed in, you may choose to create a new key for your application. Click the “Create or view keys” link on the left.

    alt


  5. On the next page fill in your application details and click Create Key.

    alt


  6. Below, you’ll see your new private key. Save this key, you will use it later.

    alt


Now let’s do some coding! I decided to create very simple XNA game, which uses Windows Phone 7 location services (on the device) or hardcoded location (on emulator). The game will query Bing maps services and display the received map on screen.


In addition I added my XboxXBOX Live avatar to the center of the map – this will simulate a game process.


Lastly I decided to add simple Zoom In/Out controls too.


Disclaimer: this sample is not an XNA tutorial. To learn about 2D game development with XNA game studio 4.0 for Windows Phone 7 please refer to great tutorial (which I had a great honor to be an author Winking smile) at new MSDN Apps Hub site (http://create.msdn.com/en-US/education/catalog/lab/catapult_wars).


My game will start with loading some assets at LoadContent function – it will load a font to draw text on screen, default background (to fill the screen while first map tile still downloading) and ZoomIn/ZoomOut images.


First – the references I will need in order to make whole thing work – please make sure all of the selected assemblies referenced in the project (some of them automatically referenced with new Windows Phone 7 Game project, some of them need to be referenced manually):


image


Make sure or add the following using statements at the top of the class:

using Microsoft.Xna.Framework.Input;

using Microsoft.Xna.Framework.Input.Touch;

using System.Device.Location;

using System.Net;

using Microsoft.Devices;

Let’s see the LoadContent function:

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 font = Content.Load<SpriteFont>(“Font”); mapImage = Content.Load<Texture2D>(“Nowhere”); zoominImage = Content.Load<Texture2D>(“zoomin”); zoomoutImage = Content.Load<Texture2D>(“zoomout”);

}


In addition I created a number of class level variables which will be used during the game:

GeoCoordinateWatcher watcher;

Texture2D mapImage;

Texture2D playerImage;

Texture2D zoominImage;

Texture2D zoomoutImage;

SpriteFont font;

GeoCoordinate co;

bool mapLoaded = false;

bool mapUpdated = false;

float baseScale = 0.04545454545454545454545454545455f; float scale = 1f; Vector2 zoomInPosition = new Vector2(20, 20); Vector2 zoomOutPosition = new Vector2(20, 100);

const string bingAppKey = “MY_BING_MAPS_KEY”;

The GeoCoordinateWatcher will be used to observe location changes on the device; once location changes it will be retrieved to the GeoCoordinate variable.


Since Bing Maps uses scale between 1 and 22, I have to scale my textures accordingly.


baseScale variable is 1/22 in order to enable easy scale calculation.


bingAppKey is a private key received at step 6 during registration process.


Now game will draw a mapImage on screen and will display status text according to boolean variables introduced above:

protected override void Draw(GameTime gameTime)

{

    GraphicsDevice.Clear(Color.CornflowerBlue);

// TODO: Add your drawing code here spriteBatch.Begin();

spriteBatch.Draw(mapImage, Vector2.Zero, Color.White);

if (mapLoaded) DrawZoomControls();

if (null != playerImage) { Vector2 position = new Vector2(GraphicsDevice.Viewport.Width / 2 – (playerImage.Width * baseScale * scale) / 2, GraphicsDevice.Viewport.Height / 2 – (playerImage.Height * baseScale * scale) / 2);

spriteBatch.Draw(playerImage, position, null, Color.White, 0f, Vector2.Zero, baseScale * scale, SpriteEffects.None, 0); }

if (!mapLoaded || mapUpdated) { string text = !mapLoaded ? “Locating position…” : “Refining map…”; Color color = !mapLoaded ? Color.White : Color.Red; Vector2 size = font.MeasureString(text); Vector2 position = new Vector2(10, GraphicsDevice.Viewport.Height – size.Y – 10); spriteBatch.DrawString(font, text, position, color); }

spriteBatch.End(); base.Draw(gameTime); }

private void DrawZoomControls()

{

    spriteBatch.Draw(zoominImage, zoomInPosition, Color.White);

    spriteBatch.Draw(zoomoutImage, zoomOutPosition, Color.White);

}

The code snippet is pretty simple, yet requires some explanations. In XNA the on-screen object location and its Z-Index depends on draw order during the Draw function. In order to start drawing the function should Begin a sprite batch rendering process and End it after all objects are drawn. First, the function above draws a background texture which currently will be a “Nowhere” image and will be replaced by the map once it arrives from server. Next, if map already downloaded it will call to helper method to draw zoom controls at known fixed positions. This assures, that zoom texture will be paced above the background (Z-Index is higher). After then, if the player’s image (remember  – it will be my XBOX Live Avatar) arrived, it will display it at the center of the screen scaled accordingly to the map scale. Lastly, it will display “Loading” text if the map is not loaded yet or zoom level changed and map image being updated.


Running application at this stage produces the following result (both at emulator and the device):


image


To get map images I will use a WebClient class (same one which used also in Silverlight applications on Windows Phone 7).


Now it is a time to look at Bing Maps API; from MSDN documentation (http://msdn.microsoft.com/en-us/library/ff701716.aspx, http://msdn.microsoft.com/en-us/library/ff701724.aspx) I see, that the request URI should be build as follows:


http://dev.virtualearth.net/REST/version/Imagery/Metadata/imagerySet?key=BingMapsKey


For more info about version, Imagery, Metadata, etc. please refer to the Bing Maps APIs documentation.


In my sample I will use static image search by query on the emulator:

http://dev.virtualearth.net/REST/V1/Imagery/Map/AerialWithLabels/eiffel tower?zoomLevel=SCALE&mapSize=800,480&key=MY_BING_MAPS_KEY
and location centers at specified geographical coordinate (longitude and latitude) on the device:
http://dev.virtualearth.net/REST/V1/Imagery/Map/Aerial/LATITUDE,LONGTITUDE/SCALE?mapSize=800,480&key=MY_BING_MAPS_KEY

SCALE will be replaced by current scale and MY_BING_MAPS_KEY by my private key. On the device also LATITUDE and LONGTITUDE will be replaced by values reported by Windows Phone 7 location services.


In this quick-and-dirty sample I put the initialization code in Initialize function of XNA game class:

protected override void Initialize()

{

    string requestUri = “”;

    WebClient wc = null;

float currentScale = baseScale * scale * 22;

// TODO: Add your initialization logic here if (Microsoft.Devices.Environment.DeviceType == DeviceType.Device) { watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High); // Use high accuracy. watcher.MovementThreshold = 20; // Use MovementThreshold to ignore noise in the signal. watcher.StatusChanged += (sender, e) => { if (e.Status == GeoPositionStatus.Ready) { mapUpdated = true; co = watcher.Position.Location; //watcher.Stop(); //Stop to conserve the battery – will disable the movement updates

requestUri = string.Format(“http://dev.virtualearth.net/REST/V1/Imagery/Map/Aerial/{0},{1}/{3}?mapSize=800,480&key={2}”, co.Latitude, co.Longitude, bingAppKey, currentScale); wc = new WebClient(); wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted); wc.OpenReadAsync(new Uri(requestUri, UriKind.Absolute), “MAP”); } }; watcher.Start(); } else { //Get static location (also by lat/long) on emu requestUri = “http://dev.virtualearth.net/REST/V1/Imagery/Map/AerialWithLabels/eiffel tower?zoomLevel=” + currentScale.ToString() + “&mapSize=800,480&key=” + bingAppKey; wc = new WebClient(); wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted); wc.OpenReadAsync(new Uri(requestUri, UriKind.Absolute), “MAP”); }

//Download avatar anyway requestUri = “https://i-blog.csdnimg.cn/blog_migrate/d3537ee89d8918e5a07ea640e591bfaf.png”; wc = new WebClient(); wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted); wc.OpenReadAsync(new Uri(requestUri, UriKind.Absolute), “IMG”);

base.Initialize(); }


The code snippet above check the Environment variable in order to understand where it is being executed.


Note: this is not System.Environment available for all .NET applications, but Microsoft.Device.Environment specific to Windows Phone apps.


If the device is actual device (Windows Phone 7 hardware) the code snippet initializes GeoCoordinateWatcher and subscribes to StatusChanged events. Once the status turns to “Ready”, the code snippet gets current reported location and requests a map image using WebClient. It uses reported Longitude, Latitude and current zoom level; in addition it provides Bing Maps private key.


If the device is emulator (and GeoCoordinateWatcher will never return meaningful value), the code snippet uses WebClient to request a map centered at some landmark – in my case Eiffel Tower.


In both request WebClient passes desired image size 800×480 to match Windows Phone 7 landscape resolution.


Lastly, it uses another instance of WebClient to download my XBOX Live avatar.


Last parameter in all OpenReadAsync calls is a state object I’m using to identify which image is arrived.


Let’s see the wc_OpenReadCompleted method:

void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)

{

    if (e.Error == null)

    {

        if (e.UserState.ToString() == “MAP”)

        {

            mapImage = Texture2D.FromStream(graphics.GraphicsDevice, e.Result);

            mapLoaded = true;

            mapUpdated = false;

        }

        else

            playerImage = Texture2D.FromStream(graphics.GraphicsDevice, e.Result);

    }

}

Running the application at the current state produces the following output on the emulator:


image


and the following at the device:


image


Note small red rectangle I put on both images – there is a very small scaled avatar inside Smile


Now let’s enable zoom controls. First, I enabled a Tap gesture at the Game constructor:

TouchPanel.EnabledGestures = GestureType.Tap;

Next, at the Update function of the game I checked Available gestures and if available, their positions. I limited the scale between 1 and 22 (supported by Bing Maps) and if scale change detected used WebClient to get new map at desired zoom level:

protected override void Update(GameTime gameTime)

{

    float prevScale = scale;

// Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit();

// TODO: Add your update logic here while (TouchPanel.IsGestureAvailable && mapLoaded) { GestureSample sample = TouchPanel.ReadGesture();

//Prepare a rectangle around touch position to allow more comfortable touch Rectangle touchPosition = new Rectangle((int)sample.Position.X – 5, (int)sample.Position.Y – 5, 10, 10);

//Prepare a rectange from ZoomIn texture and on-screen position Rectangle zoomInRect = new Rectangle((int)zoomInPosition.X, (int)zoomInPosition.Y, zoominImage.Width, zoominImage.Height);

//Prepare a rectange from ZoomOut texture and on-screen position Rectangle zoomOutRect = new Rectangle((int)zoomOutPosition.X, (int)zoomOutPosition.Y, zoomoutImage.Width, zoomoutImage.Height);

//Check Touch and zoom rectangles intersection if (touchPosition.Intersects(zoomInRect)) scale += 1; else if (touchPosition.Intersects(zoomOutRect)) scale -= 1;

//Limit the result between 1 and 22 (scale values supported by Bing Maps scale = MathHelper.Clamp(scale, 1, 22); }

if (prevScale != scale) { string requestUri = “”; WebClient wc = null;

float currentScale = baseScale * scale * 22;

if (Microsoft.Devices.Environment.DeviceType == DeviceType.Device) requestUri = string.Format(“http://dev.virtualearth.net/REST/V1/Imagery/Map/Aerial/{0},{1}/{3}?mapSize=800,480&key={2}”, co.Latitude, co.Longitude, bingAppKey, currentScale); else //Get static location (also by lat/long) on emu requestUri = “http://dev.virtualearth.net/REST/V1/Imagery/Map/AerialWithLabels/eiffel tower?zoomLevel=” + currentScale.ToString() + “&mapSize=800,480&key=” + bingAppKey;

wc = new WebClient(); wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted); wc.OpenReadAsync(new Uri(requestUri, UriKind.Absolute), “MAP”); mapUpdated = true; }

base.Update(gameTime); }


Now when running the application on the device touching Zoom In/Out buttons produces the following results:


image


Note: in this image the next map image is still downloading…


image


and on the emulator:


image


From here the process is simple: add some real game logic, animations, some Bing Maps pushpins (using sample Bing Maps APIs), respond to drag/pinch gestures and voila – the XNA game for Windows Phone 7 with Bing Maps integrated is ready!


Completed sample hosted here. Note: you will need a Bing Maps Application Key to run it.


To see more of XNA 4.0 with Windows Phone 7, PC and Xbox 360 visit my session “XNA Studio 4.0: Code Once, Play Everywhere! PC, Xbox, Phone” at TechEd 2010 Israel/Development Tools & Technologies track. In this session you will see how to build the real world computer games running on all platforms together!


Stay tuned for more come!


Enjoy,


Alex

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值