📜 ⬆️ ⬇️

We write games for Windows Phone


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

At first, a fly in the ointment: it turned out that while Windows Phone would not fully support its own shaders, a number of preset settings with wide capabilities are promised. Well, let's not be upset, everything has its time.
For work, we need Windows Phone Developer Tools

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


So, create a new solution:
')


Let's start by displaying text on the screen, for this we will add a new font file to the project:



Open this file and add an entry for the Cyrillic alphabet to the section:
< CharacterRegions >
< CharacterRegion >
< Start > </ Start >
< End > ~ </ End >
</ CharacterRegion >
< CharacterRegion >
< Start > </ Start >
< End > </ End >
</ CharacterRegion >
</ CharacterRegions >

Also increase the font size.

Font download code and text output to screen:
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>( "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 what a bad luck: the orientation of the screen does not switch with us when the orientation of the device changes in space, and playing on a small screen with a 480/800 aspect ratio is not very convenient. While in XNA there are no means to rotate the screen, you will have to get out on your own. This will help us RenderTarget2D. In fact, this is the texture of the size we specified, in which we can draw, and then also display on the screen. Let's change the Draw function: now the rendering of sprites goes not to the screen, but to 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 screensaver: add title.png to the project. We will display this picture on the screen 4 seconds after launching the game.
Texture2D title;

protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>( "Arial" );
title = Content.Load<Texture2D>( "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 will need a camera class responsible for working with matrices and processing user input, you can read about creating a camera on gamedev.ru. In this article, only the UpdateTouch function, which is responsible for working with touch input, is of interest. The full source code of the camera can be viewed at this link . I recommend to make the logic classes for storing data in 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. Let's write object and scene classes and add a 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 <Entity> Entities = new List <Entity>();
public void Load(ContentManager content)
{
Entities.Add( new Entity() { Model = content.Load<Model>( "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 uncivilized - it changes the rendering settings, and after drawing it does not return the original ones. To solve this problem, we will save the settings we are interested in and set them back after SpriteBatch is completed.



Cool? Yes, but not every player will want to feel like an ethereal shadow; we lack collisions. What do you need to handle collisions? That's right, geometry. Just take it on the go from these models is not very convenient, so we write your ContentProcessor. This is the thing that handles game resources in a special way during project assembly.
In this ContentProcessor, we will extract geometry data from models and store them separately. Add to the solution a new project “Content Pipeline Extension Library”, and write the ModelCollisionProcessor class, which is responsible for processing 3D models. This code is taken from msdn-s examples, all the work that it performs is adding a polygon list 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 written by us, you need to add a link to the CollisionContentProcessor for the game resources project and on the properties tab for each 3D model in the project, set the ContentProcessor value to the ModelCollisionProcessor.



Now that we have geometry data, we need to do something with them. Write a class to handle collisions. I wrote this class guided by this article on gamedev.ru.
Let's create a new GroundCamera class and inherit it from the Camera, the principle of operation is this: on the ground we roll a ball for which we handle collisions, and at some 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 greater clarity, add to the scene another object - a playground. And finally, we will write a simple skybox.
What we got in the end:



Download the source for the article.

What remains beyond the scope of this article:

If this topic is interesting to the community, 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 Contest
And please vote for the article here.

Source: https://habr.com/ru/post/90768/


All Articles