We write games for Windows Phone


    Not so long ago, the Windows Phone platform was presented to the public. The platform is very interesting in terms of development, as There is support for .Net Framework, multitasking and the XNA Framework, and the differences between the XNA and the desktop version are minimal.

    At first, a fly in the ointment: it turned out that while on Windows Phone there will be no full-fledged support for their own shaders, a number of pre-configured settings with great features are promised. Well, let's not worry, everything has its time.
    For work, we need Windows Phone Developer Tools.

    Before reading further, I recommend that you look at the articles by links, they contain basic information on the architecture of XNA games:


    So, we are creating a new solution:



    Let's start by displaying the text on the screen. To do this, add a new font file to the project:



    Open this file and add an entry for the Cyrillic alphabet in the section:
      
       
        
        ~
       

       
        А
        я
       

      

    Also increase the font size.

    Font download code and display text:
    protected override void LoadContent()
    {
      spriteBatch = new SpriteBatch(GraphicsDevice);
      font = Content.Load("Arial");
    }

    protected override void Draw(GameTime gameTime)
    {
      GraphicsDevice.Clear(Color.Black);
      spriteBatch.Begin();
      spriteBatch.DrawString(font, "Привет Хабрахабр!", new Vector2(120, 400), Color.White);
      spriteBatch.End();
      base.Draw(gameTime);
    }




    Screen rotation



    But here's a bad luck: the screen orientation does not switch when changing the orientation of the device in space, and playing on the screen with an aspect ratio of 480/800 is not very convenient. While XNA does not have the means to rotate the screen, you have to get it yourself. RenderTarget2D will help us with this. In fact, this is the texture of the size we have specified, in which we can draw, and then also display it on the screen. Change the Draw function: now the sprite is drawn not on the screen, but in renderTarget.

    protected override void Initialize()
    {
      base.Initialize();
      renderTarget = new RenderTarget2D(GraphicsDevice, 800, 480, false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8);
    }

    private void DrawSprites(GameTime gameTime)  //В этой функции мы рисуем в renderTarget размером 800x480
    {
      spriteBatch.Begin();
      spriteBatch.DrawString(font, "Привет Хабрахабр!", new Vector2(40, 40), Color.White);
      spriteBatch.End();
    }

    protected override void Draw(GameTime gameTime)
    {
      GraphicsDevice.SetRenderTarget(renderTarget);
      GraphicsDevice.Clear(Color.Black);  //Очищаем renderTarget
      DrawSprites(gameTime);      //Рисуем в него
      GraphicsDevice.SetRenderTarget(null);  //Возвращаем стандартный
      GraphicsDevice.Clear(Color.Black);
      spriteBatch.Begin();  //И здесь рисуем повернутую и отраженную текстуру из нашего renderTarget
      spriteBatch.Draw((Texture2D)renderTarget, Vector2.Zero, null, Color.White, -MathHelper.PiOver2, new Vector2(800, 0), 1, SpriteEffects.FlipHorizontally | SpriteEffects.FlipVertically, 0);
      spriteBatch.End();
      base.Draw(gameTime);
    }


    What we got:



    Our “game” definitely needs a start-up splash screen: add title.png to the project. We will display this picture 4 seconds after starting the game.
    Texture2D title;

    protected override void LoadContent()
    {
      spriteBatch = new SpriteBatch(GraphicsDevice);
      font = Content.Load("Arial");
      title = Content.Load("Title");
    }

    private void DrawSprites(GameTime gameTime)
    {
      spriteBatch.Begin();
      if (gameTime.TotalGameTime.TotalSeconds > 4)
        spriteBatch.DrawString(font, "Привет Хабрахабр!", new Vector2(40, 40), Color.White);
      else
        spriteBatch.Draw(title, Vector2.Zero, Color.White);
      spriteBatch.End();
    }




    3D graphics


    We need a camera class that is responsible for working with matrices and processing user input, you can read about creating a camera on gamedev.ru. In the framework of this article, only the UpdateTouch function, which is responsible for working with touch input, is of interest. The entire source code of the camera can be viewed at this link . I recommend putting logic, classes for storing data into a separate Dll. To do this, add to the solution a new project "WindowsPhoneGameDemoObjects"

    private void UpdateTouch(GameTime gameTime)
    {
      TouchCollection tc = TouchPanel.GetState();  //Получаем все касания
      if (tc.Count > 0)
      {
        TouchLocation current = tc[0]; //Получаем первое касание
        TouchLocation prev;
        if (current.TryGetPreviousLocation(out prev))  //Если это касание было и в прошлом цикле обновления
        {
        //То рассчитываем приращения углов
          Angle.X -= MathHelper.ToRadians((current.Position.X - prev.Position.X) * turnSpeed); // pitch
          Angle.Y += MathHelper.ToRadians((current.Position.Y - prev.Position.Y) * turnSpeed); // yaw

          while (Angle.Y > MathHelper.Pi * 2)
            Angle.Y -= MathHelper.Pi * 2;
          while (Angle.Y < -MathHelper.Pi * 2)
            Angle.Y += MathHelper.Pi * 2;

          if (Angle.X > maxPitch)
            Angle.X = maxPitch;

          if (Angle.X < -maxPitch)
            Angle.X = -maxPitch;

          float time = (float)gameTime.ElapsedGameTime.TotalSeconds;
          Vector3 forward = -Vector3.Normalize(new Vector3((float)Math.Sin(-Angle.Y),
            (float)Math.Sin(Angle.X),
            (float)Math.Cos(-Angle.Y)));
          if (DenyVerticalMovement)
          {
            forward = new Vector3(forward.X, 0, forward.Z);
            forward.Normalize();
          }
          Position += forward * movementSpeed * time;
        }
        else
          touchStartTime = gameTime;
      }
    }


    I note that the camera is inherited from DrawableGameComponent - i.e. if we add a camera to the list of components of our game (this.Components.Add (camera);) then the Update method will be called automatically. We will write the classes of the object and the scene and add the model to the project that we will render.

    public class Entity
    {
      public Matrix World //Матрица мира объкта
      {
        get
        {
          return Matrix.CreateScale(Scale)
               * Matrix.CreateRotationY(Rotation.Y)
               * Matrix.CreateRotationX(Rotation.X)
               * Matrix.CreateRotationZ(Rotation.Z)
               * Matrix.CreateTranslation(Position)
               ;
        }
      }
      public Model Model; //Модель объекта
      public Vector3 Scale = Vector3.One; //Названия переменных говорят сами за себя :)
      public Vector3 Rotation;
      public Vector3 Position;

      public void Draw(Camera camera)
      {
        Matrix[] transforms = new Matrix[Model.Bones.Count]; //Получаем матрицы трансформаций костей модели
        Model.CopyAbsoluteBoneTransformsTo(transforms);

        foreach (ModelMesh mesh in Model.Meshes)  //И в цикле отрисовываем все элементы модели
        {
          foreach (BasicEffect be in mesh.Effects)
          {
            be.EnableDefaultLighting();
            be.PreferPerPixelLighting = true;
            be.Projection = camera.Projection;
            be.View = camera.View;
            be.World = World * mesh.ParentBone.Transform;
          }
          mesh.Draw();
        }
      }
    }

    public class Scene
    {
      public List Entities = new List();
      public void Load(ContentManager content)
      {
        Entities.Add(new Entity() { Model = content.Load("Scene\\Scene") });
      }

      public void Draw(Camera camera)
      {
        foreach (Entity e in Entities)
          e.Draw(camera);
      }
    }


    Almost everything is ready, it remains only to solve one unpleasant problem. The fact is that the SpriteBatch object behaves uncultured - it changes the rendering settings, and after rendering does not return the original ones. To solve this problem, we will save the settings we are interested in and install them back after the completion of SpriteBatch.



    Cool? Yes, but not every player wants to feel like an ethereal shadow, we do not have enough collisions. And what is needed to handle collisions? That's right, geometry. Just taking it on the go from these models is not very convenient, so let's write our ContentProcessor. This is a thing that specifically processes game resources during the assembly of a project.
    In this ContentProcessor, we will isolate geometry data from models and store them separately. Add a new project “Content Pipeline Extension Library” to the solution, and write the ModelCollisionProcessor class, which is responsible for processing 3D models. This codetaken from msdn examples, all the work he does is adding a list of polygons to the model. The list of model polygons is stored in its Tag property. In this property we can store arbitrary data.
    To use the ContentProcessor we wrote, you need to add a link to the CollisionContentProcessor for the game’s resource project and set the ContentProcessor value to ModelCollisionProcessor on the properties tab for each 3D model in the project.



    Now that we have geometry data, we need to do something with them. Let's write a collision handling class. I wrote this class guided by this article on gamedev.ru.
    Let's create a new GroundCamera class and inherit it from Camera, the principle of operation is as follows: we have a ball rolling on the ground for which we are processing collisions, and at a certain distance from it our camera is attached on top.
    Replace Camera with GroundCamera in Game1.cs, in the Update method we will call the collision handling function. For clarity, add another object to the scene - a playground. And finally, let's write a simple skybox.
    What we got as a result:



    Download sources for the article.

    What is left out of the scope of this article:
    • Sound
    • Shaders
    • Animations
    • Multi touch
    • AI

    If this topic will be interesting to the habrasociety, I promise to write a couple more articles on XNA on Windows Phone.
    What to read:
    http://creators.xna.com
    http://blogs.msdn.com/shawnhar/default.aspx
    http://gamedev.ru The

    article participates in the Competition
    And please vote for the article here

    Also popular now: