Моделирование нивелира (XNA)
Дополнительные материалы к занятию можно скачать здесь.
При производстве различных строительных работ часто используется инструмент ватерпас, или нивелир, в котором по положению пузырька воздуха в жидкости можно судить о горизонтальности поверхности. В книге Чарльза Петзолда [25] описывается приложение XNA, моделирующее поведение пузырька воздуха в жидкости в зависимости от положения устройства. Предлагается воспроизвести этот проект.
Итак, вначале создаем новое приложение Practice_18 XNA Windows Phone 7.
Далее, необходимо добавить к проекту папку Images.
Затем вставляем в эту папку картинку, имитирующую пузырек Bubble.bmp, после чего подключаем эту картинку к проекту:
Затем подключаем библиотеку Microsoft.Devices.Sensors
Далее, требуется изменить файл 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.Input.Touch; using Microsoft.Xna.Framework.Media; using Microsoft.Devices.Sensors; namespace Practice_18 { public class Game1 : Microsoft.Xna.Framework.Game { const float BUBBLE_RADIUS_MAX = 25; const float BUBBLE_RADIUS_MIN = 12; GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Vector2 screenCenter; float screenRadius; Texture2D bubbleTexture; Vector2 bubbleCenter; Vector2 bubblePosition; float bubbleScale; Vector3 accelerometerVector; object accelerometerVectorLock = new object(); public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; TargetElapsedTime = TimeSpan.FromTicks(333333); } protected override void Initialize() { Accelerometer accelerometer = new Accelerometer(); accelerometer.ReadingChanged += OnAccelerometerReadingChanged; try { accelerometer.Start(); } catch { } base.Initialize(); } protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); Viewport viewport = this.GraphicsDevice.Viewport; screenCenter = new Vector2(viewport.Width / 2, viewport.Height / 2); screenRadius = Math.Min(screenCenter.X, screenCenter.Y) - BUBBLE_RADIUS_MAX; bubbleTexture = this.Content.Load<Texture2D>("Images/Bubble"); bubbleCenter = new Vector2(bubbleTexture.Width / 2, bubbleTexture.Height / 2); } void OnAccelerometerReadingChanged(object sender, AccelerometerReadingEventArgs args) { lock (accelerometerVectorLock) { accelerometerVector = new Vector3((float)args.X, (float)args.Y, (float)args.Z); } } protected override void UnloadContent() { } protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); Vector3 accVector; lock (accelerometerVectorLock) { accVector = accelerometerVector; } bubblePosition = new Vector2(screenCenter.X + screenRadius * accVector.Y, screenCenter.Y + screenRadius * accVector.X); float bubbleRadius = BUBBLE_RADIUS_MIN + (1 - accVector.Z) / 2 * (BUBBLE_RADIUS_MAX - BUBBLE_RADIUS_MIN); bubbleScale = bubbleRadius / (bubbleTexture.Width / 2); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.Navy); spriteBatch.Begin(); spriteBatch.Draw(bubbleTexture, bubblePosition, null, Color.White, 0, bubbleCenter, bubbleScale, SpriteEffects.None, 0); spriteBatch.End(); base.Draw(gameTime); } } }Листинг .
Метод LoadContent загружает растровое изображение, используемое для моделирования нивелира. Метод Update осуществляет доступ к вектору акселерометра и вычисляет положение "пузырька" на основании компонентов X и Y. На основании величины Z вычисляется масштаб "пузырька", имитируя отображение реального пузырька при различном положении резервуара с жидкостью.