정보
요청한 주제가 아래에 표시됩니다. 그러나 이 주제는 이 라이브러리에 포함되지 않습니다.

방법: Windows Phone의 복합 동작 API 사용

2012-02-09

Motion API는 공간에서의 단말기 방향 및 동작을 입력 메커니즘으로 사용하는 Windows Phone 응용프로그램을 만드는 데 유용합니다. 단말기의 Compass, GyroscopeAccelerometer 센서에서 원시 센서 데이터를 가져올 때 사용하는 API가 있지만, 이러한 센서로부터 데이터를 결합하여 단말기의 자세와 동작에 대한 사용하기 쉬운 값으로 산출하는 데 필요한 복잡한 수식은 동작 API가 처리합니다.

이 항목에서는 동작 API를 사용하는 두 개의 다른 응용프로그램을 만드는 과정을 안내합니다. 첫 번째 응용프로그램은 매우 간단하며 단말기 회전의 변경에 대한 반응으로 화면의 삼각형을 회전시킵니다. 두 번째 응용프로그램은 단말기의 카메라와 동작 API를 사용하여 사용자가 단말기 주변 공간의 지점에 레이블을 표시할 수 있는 증강 현실 응용프로그램입니다.

이 예제에 사용되는 동작 API는 지원되는 모든 Windows Phone 센서를 필요로 하므로 이 예제 응용프로그램은 정상적으로 종료됩니다. 그러나, 필요한 센서 또는 단말기 에뮬레이터가 없으면 단말기에서는 제대로 작동하지 않습니다.

이 섹션에서 설명하는 응용프로그램은 Polygon 개체의 RenderTransform 속성을 사용하여 다각형을 회전시킵니다. RenderTransformAngle 속성은 AttitudeReading 클래스의 Yaw 속성 값을 사용하여 업데이트됩니다. 이 때문에 단말기가 회전하면 삼각형이 회전하게 됩니다.

간단한 동작 기반 응용프로그램을 만들려면

  1. Visual Studio 에서 새로운 Windows Phone 응용프로그램 프로젝트를 만듭니다. 이 템플릿은 Windows Phone용 Silverlight 카테고리에 있습니다.

  2. 이 응용프로그램에는 센서 API와 XNA Framework를 포함하는 어셈블리에 대한 참조가 필요합니다. 프로젝트 메뉴에서 참조 추가…를 클릭하고 Microsoft.Devices.SensorsMicrosoft.Xna.Framework를 선택한 다음 확인을 클릭합니다.

  3. MainPage.xaml 파일에서 "ContentPanel"이라는 Grid 요소에 다음 XAML 코드를 넣습니다.

    <StackPanel>
      <TextBlock Text="attitude" Style="{StaticResource PhoneTextLargeStyle}"/>
      <Grid Margin="12 0 12 0">
        <TextBlock Height="30" HorizontalAlignment="Left"  Name="yawTextBlock" Text="YAW: 000" VerticalAlignment="Top" Foreground="Red" FontSize="25" FontWeight="Bold"/>
        <TextBlock Height="30" HorizontalAlignment="Center"  Name="pitchTextBlock" Text="PITCH: 000" VerticalAlignment="Top" Foreground="Green" FontSize="25" FontWeight="Bold"/>
        <TextBlock Height="30" HorizontalAlignment="Right"   Name="rollTextBlock" Text="ROLL: 000" VerticalAlignment="Top"  Foreground="Blue" FontSize="25" FontWeight="Bold"/>
      </Grid>
      <Grid Height="200">
        <Polygon Name="yawtriangle"
          Points="205,135 240,50 275,135"
          Stroke="Red"
          StrokeThickness="2" >
          <Polygon.Fill>
            <SolidColorBrush Color="Red" Opacity="0.3"/>
          </Polygon.Fill>
          <Polygon.RenderTransform>
            <RotateTransform CenterX="240" CenterY="100"></RotateTransform>
          </Polygon.RenderTransform>
        </Polygon>
        <Polygon Name="pitchtriangle"
          Points="205,135 240,50 275,135"
          Stroke="Green"
          StrokeThickness="2" >
          <Polygon.Fill>
            <SolidColorBrush Color="Green" Opacity="0.3"/>
          </Polygon.Fill>
          <Polygon.RenderTransform>
            <RotateTransform CenterX="240" CenterY="100"></RotateTransform>
          </Polygon.RenderTransform>
        </Polygon>
        <Polygon Name="rolltriangle"
          Points="205,135 240,50 275,135"
          Stroke="Blue"
          StrokeThickness="2" >
          <Polygon.Fill>
            <SolidColorBrush Color="Blue" Opacity="0.3"/>
          </Polygon.Fill>
          <Polygon.RenderTransform>
            <RotateTransform CenterX="240" CenterY="100"></RotateTransform>
          </Polygon.RenderTransform>
        </Polygon>
      </Grid>
      <TextBlock Text="acceleration" Style="{StaticResource PhoneTextLargeStyle}"/>
      <Grid Margin="12 0 12 0">
        <TextBlock Height="30" HorizontalAlignment="Left"  Name="xTextBlock" Text="X: 000" VerticalAlignment="Top" Foreground="Red" FontSize="25" FontWeight="Bold"/>
        <TextBlock Height="30" HorizontalAlignment="Center"  Name="yTextBlock" Text="Y: 000" VerticalAlignment="Top" Foreground="Green" FontSize="25" FontWeight="Bold"/>
        <TextBlock Height="30" HorizontalAlignment="Right"   Name="zTextBlock" Text="Z: 000" VerticalAlignment="Top"  Foreground="Blue" FontSize="25" FontWeight="Bold"/>
      </Grid>
      <Grid Height="300">
        <Line x:Name="xLine" X1="240" Y1="150" X2="340" Y2="150" Stroke="Red" StrokeThickness="4"></Line>
        <Line x:Name="yLine" X1="240" Y1="150" X2="240" Y2="50" Stroke="Green" StrokeThickness="4"></Line>
        <Line x:Name="zLine" X1="240" Y1="150" X2="190" Y2="200" Stroke="Blue" StrokeThickness="4"></Line>
      </Grid>
    </StackPanel>
    
    
    

    이 코드는 단말기의 요, 피치 및 롤의 숫자 값(도)을 표시하기 위해 3개의 TextBlock 컨트롤을 만듭니다. 또한 그 값을 그래픽으로 나타내기 위해 3개의 삼각형이 만들어집니다. RotateTransform이 각 삼각형에 추가되고 회전 중심으로 사용할 점이 지정됩니다. RotateTransform의 각도가 C# 코드 숨김 페이지에서 설정되어 휴대폰의 방향에 따라 삼각형에 애니메이션 효과를 줍니다.

    그런 다음 3개의 TextBlock 컨트롤을 추가로 사용하면서 각 축에서의 단말기 가속을 숫자로 표시합니다. 그런 다음 3개의 선이 추가되어 가속을 그래픽으로 나타냅니다.

  4. 이제 MainPage.xaml.cs 코드 숨김 페이지를 열고 센서 및 XNA Framework 네임스페이스에 대한 using 지시문을 해당 페이지의 맨 위에 있는 다른 using 지시문에 추가합니다.

    using Microsoft.Devices.Sensors;
    using Microsoft.Xna.Framework;
    
    
  5. MainPage 클래스 정의의 맨 위에서 Motion 형식의 변수를 선언합니다.

    public partial class MainPage : PhoneApplicationPage
    {
      Motion motion;
    
    
  6. 다음으로 Page 클래스의 OnNavigatedTo(NavigationEventArgs) 메서드를 재정의합니다. 이 메서드는 사용자가 페이지로 이동할 때마다 호출됩니다. 이 메서드에서는 IsSupported 속성이 확인됩니다. 모든 단말기에 이 기능을 사용하는 데 필요한 센서가 있는 것은 아니므로 API를 사용하기 전에 이 값을 확인해야 합니다. 다음으로 Motion 개체가 초기화되고 이벤트 처리기가 CurrentValueChanged 이벤트에 연결됩니다. 이 이벤트는 TimeBetweenUpdates 매개 변수에 지정된 간격으로 발생합니다. 기본값은 2밀리초입니다. 마지막으로, 데이터 수집은 Start 메서드를 호출하여 시작됩니다. 이 메서드는 예외를 발생시킬 수 있으므로 try 블록 내에 넣습니다.

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
      // Check to see whether the Motion API is supported on the device.
      if (! Motion.IsSupported)
      {
        MessageBox.Show("the Motion API is not supported on this device.");
        return;
      }
    
      // If the Motion object is null, initialize it and add a CurrentValueChanged
      // event handler.
      if (motion == null)
      {
        motion = new Motion();
        motion.TimeBetweenUpdates = TimeSpan.FromMilliseconds(20);
        motion.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<MotionReading>>(motion_CurrentValueChanged);
      }
    
      // Try to start the Motion API.
      try
      {
        motion.Start();
      }
      catch (Exception ex)
      {
        MessageBox.Show("unable to start the Motion API.");
      }
    }
    
    
  7. 현재 값 변경 이벤트는 응용프로그램에 새로운 센서 데이터를 제공하기 위해 정기적으로 발생합니다. 이벤트 처리기는 응용프로그램 UI에 대한 액세스 권한이 없는 백그라운드 스레드에서 호출됩니다. BeginInvoke를 사용하여 UI 스레드에서 CurrentValueChanged를 호출합니다. MotionReading 개체를 매개 변수로 허용하는 이 메서드는 다음에 정의합니다.

    void motion_CurrentValueChanged(object sender, SensorReadingEventArgs<MotionReading> e)
    {
      // This event arrives on a background thread. Use BeginInvoke to call
      // CurrentValueChanged on the UI thread.
      Dispatcher.BeginInvoke(() => CurrentValueChanged(e.SensorReading));
    }
    
    
  8. 마지막으로 CurrentValueChanged 메서드를 만듭니다. 이 메서드는 TextBlock 개체의 Text 속성을 요, 피치 및 롤 특성 표시 값으로 설정합니다. 그런 다음 각 삼각형 RenderTransformAngle 매개 변수가 설정되어 해당 특성 값에 따라 각 삼각형을 회전시킵니다. XNA Framework의 MathHelper 클래스는 라디안을 도 단위로 변환하는 데 사용됩니다. 그리고 가속 TextBlock 개체가 업데이트되어 각 축에서의 현재 가속 값을 표시합니다. 마지막으로, 선이 업데이트되어 단말기의 가속을 그래픽으로 나타냅니다.

    private void CurrentValueChanged(MotionReading e)
    {
      // Check to see if the Motion data is valid.
      if (motion.IsDataValid)
      {
        // Show the numeric values for attitude.
        yawTextBlock.Text = "YAW: " + MathHelper.ToDegrees(e.Attitude.Yaw).ToString("0") + "°";
        pitchTextBlock.Text = "PITCH: " + MathHelper.ToDegrees(e.Attitude.Pitch).ToString("0") + "°";
        rollTextBlock.Text = "ROLL: " + MathHelper.ToDegrees(e.Attitude.Roll).ToString("0") + "°";
    
        // Set the Angle of the triangle RenderTransforms to the attitude of the device.
        ((RotateTransform)yawtriangle.RenderTransform).Angle = MathHelper.ToDegrees(e.Attitude.Yaw);
        ((RotateTransform)pitchtriangle.RenderTransform).Angle = MathHelper.ToDegrees(e.Attitude.Pitch);
        ((RotateTransform)rolltriangle.RenderTransform).Angle = MathHelper.ToDegrees(e.Attitude.Roll);
    
        // Show the numeric values for acceleration.
        xTextBlock.Text = "X: " + e.DeviceAcceleration.X.ToString("0.00");
        yTextBlock.Text = "Y: " + e.DeviceAcceleration.Y.ToString("0.00");
        zTextBlock.Text = "Z: " + e.DeviceAcceleration.Z.ToString("0.00");
    
        // Show the acceleration values graphically.
        xLine.X2 = xLine.X1 + e.DeviceAcceleration.X * 100;
        yLine.Y2 = yLine.Y1 - e.DeviceAcceleration.Y * 100;
        zLine.X2 = zLine.X1 - e.DeviceAcceleration.Z * 50;
        zLine.Y2 = zLine.Y1 + e.DeviceAcceleration.Z * 50;
      }
    }
    
    
  9. 단말기가 컴퓨터에 연결되어 있는지 확인하고 Visual Studio 에서 F5 키를 눌러 디버깅을 시작합니다. 다양한 위치로 단말기를 회전하면서 단말기의 방향에 따라 요, 피치 및 롤 값이 어떻게 바뀌는지 확인합니다. 단말기를 상하좌우로 흔들면서 가속 값이 어떻게 바뀌는지 확인합니다. 가속도계 API와 달리 값에서 중력 가속이 필터링되므로, 단말기가 정지 상태일 때 모든 축의 가속이 0입니다.

증강 현실은 사용자가 눈으로 보는 현실 세계에 그래픽이나 오디오 등의 부가 기능을 겹쳐서 보여 주는 응용프로그램을 나타내는 용어입니다. 이 예제 응용프로그램에서는 PhotoCamera API를 사용하여 단말기 카메라로부터 동영상 피드를 표시합니다. 텍스트 레이블은 공간의 특정 지점에 레이블을 표시하기 위해 동영상 피드 맨 위에 있습니다. 동작 API는 단말기 방향이 변하면 카메라 뷰파인더를 따라 텍스트 레이블이 이동할 수 있도록 텍스트 레이블을 동적으로 배치하는 데 사용합니다. 그러면 레이블이 단말기 주변 공간의 지점에 고정되는 효과가 발생합니다. 이 응용프로그램에서는 사용자가 카메라 뷰파인더의 한 지점을 클릭하면 이 지점이 화면 공간에서 현실 공간으로 변형되고, 여기에 텍스트를 입력하여 레이블을 표시할 수 있습니다.

다음 프로시저에서 설명할 이 응용프로그램은 화면의 지점을 3D 공간 및 뒤쪽으로 프로젝션하는 계산에 XNA Framework 라이브러리를 사용하지만 그래픽은 전적으로 Silverlight에서 만듭니다. 이러한 프레임워크를 함께 사용하는 것에 대한 자세한 내용은 방법: Windows Phone 응용프로그램에서 Silverlight 및 XNA Framework 결합을 참조하십시오.

Silverlight 기반 증강 현실 응용프로그램을 만들려면

  1. Visual Studio 에서 새로운 Windows Phone 응용프로그램 프로젝트를 만듭니다. 이 템플릿은 Windows Phone용 Silverlight 카테고리에 있습니다.

  2. 센서 API와 XNA Framework를 포함하는 어셈블리에 대한 참조를 추가합니다. 프로젝트 메뉴에서 참조 추가…를 클릭하고 Microsoft.Devices.Sensors, Microsoft.Xna.FrameworkMicrosoft.Xna.Framework.Graphics를 선택한 다음 확인을 클릭합니다.

  3. XAML로 사용자 인터페이스를 만듭니다. 이 응용프로그램은 Rectangle 개체와 VideoBrush를 함께 사용하여 단말기 카메라로부터 동영상 스트림을 표시합니다. 이는 방법: Windows Phone용 기본 카메라 응용프로그램 만들기에서 설명한 것과 동일한 기술입니다.

    또한 XAML 코드는 사용자가 선택한 공간의 지점에 표시될 레이블의 이름을 지정할 수 있도록 하는 TextBox 컨트롤을 만듭니다. TextBoxCanvas 컨트롤 내에 배치됩니다. Canvas 개체는 사용자가 화면 위의 어딘가를 탭할 때까지 숨겨져 있다가 사용자가 탭하면 해당 지점에 텍스트를 입력할 수 있도록 표시됩니다. 사용자가 Enter 키를 누르면 Canvas는 다시 숨겨집니다.

    MainPage.xaml 파일에서 "ContentPanel"이라는 Grid 요소에 다음 XAML 코드를 넣습니다.

    <Rectangle Width="640" Height="480" Canvas.ZIndex="1">
      <Rectangle.Fill>
        <VideoBrush x:Name="viewfinderBrush" />
      </Rectangle.Fill>
      <Rectangle.RenderTransform>
        <RotateTransform Angle="90" CenterX="240" CenterY="240"></RotateTransform>
      </Rectangle.RenderTransform>
    </Rectangle>
            
    <Canvas Name="TextBoxCanvas" Background="#BB000000" Canvas.ZIndex="99" Visibility="Collapsed">
      <TextBlock Text="name this point" Margin="20,130,0,0"/>
      <TextBox Height="72" HorizontalAlignment="Left" Margin="8,160,0,0" Name="NameTextBox"  VerticalAlignment="Top" Width="460" KeyUp="NameTextBox_KeyUp" />
    </Canvas>
    
    
  4. MainPage.xaml.cs 파일에서 파일의 맨 위에 있는 기존 using 문에 다음 using 문을 추가합니다. Microsoft.Devices.Sensors 네임스페이스는 Motion API에 대한 액세스를 제공합니다. Microsoft.DevicesPhotoCamera API에 액세스하는 데 사용됩니다. 이 응용프로그램은 XNA Framework를 사용하여 그래픽을 렌더링하지 않지만, 이들 네임스페이스는 화면 공간의 지점을 현실 공간과 뒤쪽으로 프로젝션하는 데 필요한 수식 계산에 사용할 도우미 함수를 노출합니다. 마지막 using 지시문은 XNA Framework Matrix 형식을 명확히 하는 데 사용되며, 이에 따라 나머지 코드를 읽기가 쉬워집니다.

    using Microsoft.Devices.Sensors;
    using Microsoft.Devices;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;
    using Matrix = Microsoft.Xna.Framework.Matrix;
    
    
  5. MainPage 클래스 정의의 맨 위에 있는 클래스 멤버 변수를 선언합니다. 먼저 MotionPhotoCamera 개체가 선언됩니다. 그러면 현실 공간의 지점을 나타내는 Vector3 개체 및 각 지점에 레이블을 표시하는 데 사용되는 TextBlock 개체를 저장하기 위해 목록이 선언됩니다. 다음으로 Point 변수가 만들어집니다. 이 변수는 사용자가 단말기 화면에서 터치하는 지점을 저장합니다. 마지막 변수는 Viewport 개체와 일부 Matrix 개체로, 현실 공간의 지점을 화면 공간 및 뒤쪽으로 프로젝션하는 데 사용됩니다.

    public partial class MainPage : PhoneApplicationPage
    {
      Motion motion;
      PhotoCamera cam;
    
      List<TextBlock> textBlocks;
      List<Vector3> points;
      System.Windows.Point pointOnScreen;
    
      Viewport viewport;
      Matrix projection;
      Matrix view;
      Matrix attitude;
    
    
  6. 페이지 생성자에서 PointTextBlock 개체의 목록을 초기화합니다.

    // Constructor
    public MainPage()
    {
      InitializeComponent();
    
      // Initialize the list of TextBlock and Vector3 objects.
      textBlocks = new List<TextBlock>();
      points = new List<Vector3>();
    }
    
    
  7. 다음 메서드는 화면 공간에서 현실 공간 및 뒤쪽으로 지점을 변환하는 데 사용되는 ViewportMatrix 개체를 초기화하는 도우미 메서드입니다. Viewport는 3D 볼륨이 프로젝션되는 사각형을 정의합니다. 이를 초기화하려면 렌더링 표면의 너비와 높이를 전달합니다. 이 경우에는 페이지의 너비와 높이입니다. Viewport 구조는 화면 공간과 현실 공간 간의 지점 프로젝션 계산을 수행하는 ProjectUnproject 메서드를 노출합니다. 이러한 메서드에는 또한 여기서 초기화되는 프로젝션 매트릭스와 보기 메트릭스가 필요합니다.

    public void InitializeViewport()
    {
      // Initialize the viewport and matrixes for 3d projection.
      viewport = new Viewport(0, 0, (int)this.ActualWidth, (int)this.ActualHeight);
      float aspect = viewport.AspectRatio;
      projection = Matrix.CreatePerspectiveFieldOfView(1, aspect, 1, 12);
      view = Matrix.CreateLookAt(new Vector3(0, 0, 1), Vector3.Zero, Vector3.Up);
    }
    
    
  8. OnNavigatedTo(NavigationEventArgs)에서 카메라가 초기화되고 XAML로 정의한 VideoBrush의 소스로 설정됩니다. 다음으로 단말기에서 API가 지원되는지 확인한 후 Motion 개체가 초기화됩니다. 마지막으로 MouseLeftButtonUp 이벤트의 이벤트 처리기가 등록됩니다. 이 처리기는 사용자가 화면을 터치하면 호출됩니다.

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
      // Initialize the camera and set the video brush source.
      cam = new Microsoft.Devices.PhotoCamera();
      viewfinderBrush.SetSource(cam);
    
      if (!Motion.IsSupported)
      {
        MessageBox.Show("the Motion API is not supported on this device.");
        return;
      }
    
      // If the Motion object is null, initialize it and add a CurrentValueChanged
      // event handler.
      if (motion == null)
      {
        motion = new Motion();
        motion.TimeBetweenUpdates = TimeSpan.FromMilliseconds(20);
        motion.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<MotionReading>>(motion_CurrentValueChanged);
      }
    
      // Try to start the Motion API.
      try
      {
        motion.Start();
      }
      catch (Exception ex)
      {
        MessageBox.Show("unable to start the Motion API.");
      }
    
      // Hook up the event handler for when the user taps the screen.
      this.MouseLeftButtonUp += new MouseButtonEventHandler(MainPage_MouseLeftButtonUp);
    
      base.OnNavigatedTo(e);
    }
    
    
  9. MouseLeftButtonUp 이벤트 처리기에서 TextBox 컨트롤을 포함하는 CanvasVisibility 속성이 확인됩니다. Canvas가 보이면 사용자가 텍스트를 입력하는 중이므로 이벤트는 무시됩니다. Canvas가 보이지 않으면 사용자가 터치한 화면 지점이 pointOnScreen 변수로 저장됩니다. Motion 개체의 CurrentValue 속성은 클래스 변수 attitude로 저장되는 단말기의 현재 자세에 대해 쿼리됩니다. 마지막으로, TextBox를 포함한 Canvas가 보이게 되고 TextBox에 초점이 지정됩니다.

    void MainPage_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
      // If the Canvas containing the TextBox is visible, ignore
      // this event.
      if (TextBoxCanvas.Visibility == Visibility.Visible)
      {
        return;
      }
    
      // Save the location where the user touched the screen.
      pointOnScreen = e.GetPosition(LayoutRoot);
    
      // Save the device attitude when the user touched the screen.
      attitude = motion.CurrentValue.Attitude.RotationMatrix;
    
      // Make the Canvas containing the TextBox visible and
      // give the TextBox focus.
      TextBoxCanvas.Visibility = Visibility.Visible;
      NameTextBox.Focus();
    }
    
    
  10. Motion 클래스의 CurrentValueChanged 이벤트에 대한 이벤트 처리기는 백그라운드 스레드에서 호출됩니다. BeginInvoke를 사용하여 UI 스레드에서 다른 처리기를 호출합니다.

    void motion_CurrentValueChanged(object sender, SensorReadingEventArgs<MotionReading> e)
    {
      // This event arrives on a background thread. Use BeginInvoke
      // to call a method on the UI thread.
      Dispatcher.BeginInvoke(() => CurrentValueChanged(e.SensorReading));
    }
    
    
  11. 아래에서 정의되는 CurrentValueChanged 메서드는 단말기의 현재 자세에 따라 3D 공간의 지점 목록이 화면 공간으로 프로젝션되는 위치입니다. 먼저 Viewport 구조가 확인되고 필요한 경우 위에서 정의한 InitializeViewport가 호출됩니다. 다음으로 MotionReading 개체에서 장치 자세가 수집됩니다. 동작 API의 좌표계는 XNA Framework에서 사용하는 것과 다르므로 지점이 올바르게 변환되는지, 자세 매트릭스가 X축 주위를 90도 회전하는지 확인합니다.

    다음으로 메서드가 응용프로그램 지점 목록의 각 지점을 반복합니다. 각 지점마다 현실 공간의 오프셋을 지점으로 나타내는 현실 공간 매트릭스가 만들어집니다. 이 매트릭스는 앞서 정의한 보기 및 프로젝션 매트릭스와 함께 Viewport 구조의 Project 메서드로 전달됩니다. 이 메서드는 X 및 Y 값이 프로젝션된 지점의 화면 좌표인 Vector3 개체를 반환합니다. Z 값은 지점의 깊이를 나타냅니다. 값이 0보다 적거나 1보다 크면 이 지점은 카메라 "뒤쪽"에 있는 것이므로 지점의 TextBlock이 숨겨집니다. 지점이 카메라 앞쪽에 있으면 프로젝션된 지점의 X 및 Y 값을 사용하여 TranslateTransform 개체가 만들어진 다음 해당 지점에 연결된 TextBlock에 할당됩니다.

    private void CurrentValueChanged(MotionReading reading)
    {
      // If the viewport width is 0, it needs to be initialized.
      if (viewport.Width == 0)
      {
        InitializeViewport();
      }
    
      // Get the RotationMatrix from the MotionReading.
      // Rotate it 90 degrees around the X axis to put it in the XNA Framework coordinate system.
      Matrix attitude = Matrix.CreateRotationX(MathHelper.PiOver2) * reading.Attitude.RotationMatrix;
    
      // Loop through the points in the list.
      for (int i = 0; i < points.Count; i++)
      {
        // Create a World matrix for the point.
        Matrix world = Matrix.CreateWorld(points[i], new Vector3(0, 0, 1), new Vector3(0, 1, 0));
    
        // Use Viewport.Project to project the point from 3D space into screen coordinates.
        Vector3 projected = viewport.Project(Vector3.Zero, projection, view, world * attitude);
    
        if (projected.Z > 1 || projected.Z < 0)
        {
          // If the point is outside of this range, it is behind the camera.
          // So hide the TextBlock for this point.
           textBlocks[i].Visibility = Visibility.Collapsed;
        }
        else
        {
          // Otherwise, show the TextBlock.
          textBlocks[i].Visibility = Visibility.Visible;
    
          // Create a TranslateTransform to position the TextBlock.
          // Offset by half of the TextBlock's RenderSize to center it on the point.
          TranslateTransform tt = new TranslateTransform();
          tt.X = projected.X - (textBlocks[i].RenderSize.Width / 2);
          tt.Y = projected.Y - (textBlocks[i].RenderSize.Height / 2);
          textBlocks[i].RenderTransform = tt;
        }
      }
    }
    
    
  12. 다음으로 TextBox 컨트롤에 할당된 KeyUp 이벤트 처리기를 XAML로 구현합니다. 이 이벤트는 사용자가 TextBox에 텍스트를 입력하면 호출됩니다. 이 응용프로그램의 경우 처리기는 사용자가 Enter 키를 누를 때 새 지점을 추가하는 데 사용되므로 다른 키를 누른 경우 코드의 첫 부분이 처리기를 종료합니다. Enter를 눌렀으면 Canvas가 다시 숨겨집니다. 다음으로 처리기는 이 작업에 필요한 개체 중에 null 개체가 있는지 확인하고, 그런 경우 메서드를 종료합니다.

    그리고 앞서 MouseLeftButtonUp 이벤트 처리기에서 가져온 지점이 Viewport 구조의 Unproject 메서드에 필요한 형식으로 변환됩니다. MouseLeftButtonUp에 수집된 자세 값은 X축 주위를 90도 회전하여 XNA 좌표 공간으로 변환됩니다. 그런 다음 Unproject가 호출되어 화면 공간의 지점을 3D 공간의 지점으로 변환합니다. 프로젝션되지 않은 지점은 정규화 및 크기가 조정되며, AddPoint 도우미 메서드가 호출되어 이 지점과 해당 TextBox를 응용프로그램 목록에 추가합니다.

    private void NameTextBox_KeyUp(object sender, KeyEventArgs e)
    {
      // If the key is not the Enter key, don't do anything.
      if (e.Key != Key.Enter)
      {
        return;
      }
    
      // When the TextBox loses focus. Hide the Canvas containing it.
      TextBoxCanvas.Visibility = Visibility.Collapsed;
    
      // If any of the objects we need are not present, exit the event handler.
      if (NameTextBox.Text == "" || pointOnScreen == null || motion == null)
      {
        return;
      }
    
      // Translate the point before projecting it.
      System.Windows.Point p = pointOnScreen;
      p.X = LayoutRoot.RenderSize.Width - p.X;
      p.Y = LayoutRoot.RenderSize.Height - p.Y;
      p.X *= .5;
      p.Y *= .5;
    
      // Use the attitude Matrix saved in the OnMouseLeftButtonUp handler.
      // Rotate it 90 degrees around the X axis to put it in the XNA Framework coordinate system.
      attitude = Matrix.CreateRotationX(MathHelper.PiOver2) * attitude;
    
    
      // Use Viewport.Unproject to translate the point on the screen to 3D space.
      Vector3 unprojected = viewport.Unproject(new Vector3((float)p.X, (float)p.Y, -.9f), projection, view, attitude);
      unprojected.Normalize();
      unprojected *= -10;
    
      // Call the helper method to add this point.
      AddPoint(unprojected, NameTextBox.Text);
    
      // Clear the TextBox.
      NameTextBox.Text = "";
    }
    
    
  13. AddPoint는 3D 공간의 지점과 문자열을 포착하여 UI 및 응용프로그램 목록에 추가하는 도우미 메서드입니다. 목록에 추가된 PointTextBox는 앞서 정의한 CurrentValueChanged 메서드에 표시됩니다.

    private void AddPoint(Vector3 point, string name)
    {
      // Create a new TextBlock. Set the Canvas.ZIndexProperty to make sure
      // it appears above the camera rectangle.
      TextBlock textblock = new TextBlock();
      textblock.Text = name;
      textblock.FontSize = 124;
      textblock.SetValue(Canvas.ZIndexProperty, 2);
      textblock.Visibility = Visibility.Collapsed;
    
      // Add the TextBlock to the LayoutRoot container.
      LayoutRoot.Children.Add(textblock);
    
      // Add the TextBlock and the point to the List collections.
      textBlocks.Add(textblock);
      points.Add(point);
    }
    
    
  14. 마지막으로, 응용프로그램이 비활성 상태일 때 카메라의 전력 소비를 최소화하고 카메라를 신속하게 종료할 수 있도록 PhoneApplicationPageOnNavigatedFrom(NavigationEventArgs) 메서드에서 PhotoCamera 개체의 Dispose()()()()를 호출해야 합니다.

    protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
    {
      // Dispose camera to minimize power consumption and to expedite shutdown.
      cam.Dispose();
    }
    
    

이제 단말기에서 응용프로그램을 실행할 수 있습니다. 단말기를 세로 방향으로 계속 들고 계십시오. 카메라 뷰파인더로 문이나 창문 등의 사물을 찾아봅니다. 뷰파인더에 표시된 사물을 터치하여 이름을 입력할 수 있는 텍스트 상자를 불러옵니다. 공간 지점에 대한 이름을 입력하고 Enter 키를 누릅니다. 단말기를 회전하여 레이블이 항상 동일한 공간 지점 위에 표시되는지 확인해야 합니다. 이 응용프로그램은 공간에서의 단말기 움직임을 사용하는 것이 아니라 방향만 사용하므로 단말기를 심하게 움직이면 레이블이 지정된 지점이 제대로 정렬되지 않습니다.

다음 도우미 메서드를 응용프로그램에 추가하면 단말기 센서에 관해 3D 공간의 앞, 뒤, 왼쪽, 오른쪽, 위, 아래에 레이블을 지정할 수 있습니다. 이 메서드는 응용프로그램이 작동하는 방식을 시각적으로 나타내는 데 유용합니다.

private void AddDirectionPoints()
{
  AddPoint(new Vector3(0, 0, -10), "front");
  AddPoint(new Vector3(0, 0, 10) , "back");
  AddPoint(new Vector3(10, 0, 0) , "right");
  AddPoint(new Vector3(-10, 0, 0) , "left");
  AddPoint(new Vector3(0, 10, 0) , "top");
  AddPoint(new Vector3(0, -10, 0), "bottom");
}

표시:
© 2014 Microsoft