programming windows phone 7 phần 9 potx

102 195 0
programming windows phone 7 phần 9 potx

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

You can then draw on this RenderTarget2D in the same way you draw on the back buffer. After you’re finished drawing, you disassociate the RenderTarget2D from the GraphicsDevice with another call to SetRenderTarget with a null argument: this.GraphicsDevice.SetRenderTarget(null); Now the GraphicsDevice is back to normal. If you’re creating a RenderTarget2D that remains the same for the duration of the program, you’ll generally perform this entire operation during the LoadContent override. If the RenderTarget2D needs to change, you can also draw on the bitmap during the Update override. Because RenderTarget2D derives from Texture2D you can display the RenderTarget2D on the screen during your Draw override just as you display any other Texture2D image. Of course, you’re not limited to one RenderTarget2D object. If you have a complex series of images that form some kind of animation, you can create a series of RenderTarget2D objects that you then display in sequence as a kind of movie. Suppose you want to display something that looks like this: That’s a bunch of text strings all saying “Windows Phone 7” rotated around a center point with colors that vary between cyan and yellow. Of course, you can have a loop in the Draw override that makes 32 calls to the DrawString method of SpriteBatch, but if you assemble those text strings on a single bitmap, you can reduce the Draw override to just a single call to the Draw method of SpriteBatch. Moreover, it becomes easier to treat this assemblage of text strings as a single entity, and then perhaps rotate it like a pinwheel. That’s the idea behind the PinwheelText program. The program’s content includes the 14- point Segoe UI Mono SpriteFont, but a SpriteFont object is not included among the program’s fields, nor is the text itself: 801 XNA Project: PinwheelText File: Game1.cs (excerpt showing fields) public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Vector2 screenCenter; RenderTarget2D renderTarget; Vector2 textureCenter; float rotationAngle; … } The LoadContent method is the most involved part of the program, but it only results in setting the screenCenter, renderTarget, and textureCenter fields. The segoe14 and textSize variables set early on in the method are normally saved as fields but here they’re only required locally: XNA Project: PinwheelText File: Game1.cs (excerpt) protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); // Get viewport info Viewport viewport = this.GraphicsDevice.Viewport; screenCenter = new Vector2(viewport.Width / 2, viewport.Height / 2); // Load font and get text size SpriteFont segoe14 = this.Content.Load<SpriteFont>("Segoe14"); string text = " Windows Phone 7"; Vector2 textSize = segoe14.MeasureString(text); // Create RenderTarget2D renderTarget = new RenderTarget2D(this.GraphicsDevice, 2 * (int)textSize.X, 2 * (int)textSize.X); // Find center textureCenter = new Vector2(renderTarget.Width / 2, renderTarget.Height / 2); Vector2 textOrigin = new Vector2(0, textSize.Y / 2); // Set the RenderTarget2D to the GraphicsDevice this.GraphicsDevice.SetRenderTarget(renderTarget); 802 // Clear the RenderTarget2D and render the text this.GraphicsDevice.Clear(Color.Transparent); spriteBatch.Begin(); for (float t = 0; t < 1; t += 1f / 32) { float angle = t * MathHelper.TwoPi; Color clr = Color.Lerp(Color.Cyan, Color.Yellow, t); spriteBatch.DrawString(segoe14, text, textureCenter, clr, angle, textOrigin, 1, SpriteEffects.None, 0); } spriteBatch.End(); // Restore the GraphicsDevice back to normal this.GraphicsDevice.SetRenderTarget(null); } The RenderTarget2D is created with a width and height that is twice the width of the text string. The RenderTarget2D is set into the GraphicsDevice with a call to SetRenderTarget and then cleared to a transparent color with the Clear method. At this point a sequence of calls on the SpriteBatch object renders the text 32 times on the RenderTarget2D. The LoadContent call concludes by restoring the GraphicsDevice to the normal back buffer. The Update method calculates a rotation angle for the resultant bitmap so it rotates 360° every eight seconds: XNA Project: PinwheelText File: Game1.cs (excerpt) protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); rotationAngle = (MathHelper.TwoPi * (float) gameTime.TotalGameTime.TotalSeconds / 8) % MathHelper.TwoPi; base.Update(gameTime); } As promised, the Draw override can then treat that RenderTarget2D as a normal Texture2D in a single Draw call on the SpriteBatch. All 32 text strings seem to rotate in unison: 803 XNA Project: PinwheelText File: Game1.cs (excerpt) protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.Navy); spriteBatch.Begin(); spriteBatch.Draw(renderTarget, screenCenter, null, Color.White, rotationAngle, textureCenter, 1, SpriteEffects.None, 0); spriteBatch.End(); base.Draw(gameTime); } The FlyAwayHello program in the previous chapter loaded two white bitmaps as program content. That wasn’t really necessary. The program could have created those two bitmaps as RenderTarget2D objects and then just colored them white with a few simple statements. In FlyAwayHello you can replace these two statements in LoadContent: Texture2D horzBar = Content.Load<Texture2D>("HorzBar"); Texture2D vertBar = Content.Load<Texture2D>("VertBar"); with these: RenderTarget2D horzBar = new RenderTarget2D(this.GraphicsDevice, 45, 5); this.GraphicsDevice.SetRenderTarget(horzBar); this.GraphicsDevice.Clear(Color.White); this.GraphicsDevice.SetRenderTarget(null); RenderTarget2D vertBar = new RenderTarget2D(this.GraphicsDevice, 5, 75); this.GraphicsDevice.SetRenderTarget(vertBar); this.GraphicsDevice.Clear(Color.White); this.GraphicsDevice.SetRenderTarget(null); Yes, I know there’s more code involved, but you no longer need the two bitmap files as program content, and if you ever wanted to change the sizes of the bitmaps, doing it in code is trivial. The DragAndDraw program coming up lets you draw multiple solid-color rectangles by dragging your finger on the screen. Every time you touch and drag along the screen a new rectangle is drawn with a random color. Yet the entire program uses only one RenderTarget2D object containing just one white pixel! That single RenderTarget2D object is stored as a field, along with a collection of RectangleInfo objects that will describe each drawn rectangle: 804 XNA Project: DragAndDraw File: Game1.cs (excerpt showing fields) public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; struct RectangleInfo { public Vector2 point1; public Vector2 point2; public Color color; } List<RectangleInfo> rectangles = new List<RectangleInfo>(); Random rand = new Random(); RenderTarget2D tinyTexture; bool isDragging; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; // Frame rate is 30 fps by default for Windows Phone. TargetElapsedTime = TimeSpan.FromTicks(333333); // Enable dragging gestures TouchPanel.EnabledGestures = GestureType.FreeDrag | GestureType.DragComplete; } … } Notice also that the bottom of the Game1 constructor enables two touch gestures, FreeDrag and DragComplete. These are gestures that correspond to touching the screen, dragging the finger (whatever which way), and lifting. The LoadContent method creates the tiny RenderTarget2D object and colors it white: XNA Project: DragAndDraw File: Game1.cs (excerpt) protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); // Create a white 1x1 bitmap tinyTexture = new RenderTarget2D(this.GraphicsDevice, 1, 1); 805 this.GraphicsDevice.SetRenderTarget(tinyTexture); this.GraphicsDevice.Clear(Color.White); this.GraphicsDevice.SetRenderTarget(null); } The Update method handles the drag gestures. As you might recall from Chapter 3, the static TouchPanel class supports both low-level touch input and high-level gesture recognition. I’m using the gesture support in this program. If gestures are enabled, then gestures are available when TouchPanel.IsGestureAvailable is true. You can then call TouchPanel.ReadGesture to return an object of type GestureSample. TouchPanel.IsGestureAvailable returns false when no more gestures are available during this particular Update call. For this program, the GestureType property of GestureSample will be one of the two enumeration members, GestureType.FreeDrag or GestureType.DragComplete. The FreeDrag type indicates that the finger has touched the screen or is moving around the screen. DragComplete indicates that the finger has lifted. For the FreeDrag gesture, two other properties of GestureSample are valid: Position is a Vector2 object that indicates the current position of the finger relative to the screen; Delta is also a Vector2 object that indicates the difference between the current position of the finger and the position of the finger in the last FreeDrag sample. (I don’t use the Delta property in this program.) These properties are not valid with the DragComplete gesture. The program maintains an isDragging field to help it discern when a finger first touches the screen and when a finger is moving around the screen, both of which are FreeDrag gestures: XNA Project: DragAndDraw File: Game1.cs (excerpt) protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); while (TouchPanel.IsGestureAvailable) { GestureSample gesture = TouchPanel.ReadGesture(); switch (gesture.GestureType) { case GestureType.FreeDrag: if (!isDragging) { RectangleInfo rectInfo = new RectangleInfo(); rectInfo.point1 = gesture.Position; rectInfo.point2 = gesture.Position; 806 rectInfo.color = new Color(rand.Next(256), rand.Next(256), rand.Next(256)); rectangles.Add(rectInfo); isDragging = true; } else { RectangleInfo rectInfo = rectangles[rectangles.Count - 1]; rectInfo.point2 = gesture.Position; rectangles[rectangles.Count - 1] = rectInfo; } break; case GestureType.DragComplete: if (isDragging) isDragging = false; break; } } base.Update(gameTime); } If isDragging is false, then a finger is first touching the screen and the program creates a new RectangleInfo object and adds it to the collection. At this time, the point1 and point2 fields of RectangleInfo are both set to the point where the finger touched the screen, and color is a random Color value. With subsequent FreeDrag gestures, the point2 field of the most recent RectangleInfo in the collection is re-set to indicate the current position of the finger. With DragComplete, nothing more needs to be done and the isDragging field is set to false. In the Draw override (shown below), the program calls the Draw method of SpriteBatch once for each RectangleInfo object in the collection, in each case using the version of Draw that expands the Texture2D to the size of a Rectangle destination: Draw(Texture2D texture, Rectangle destination, Color color) The first argument is always the 1×1 white RenderTarget2D called tinyTexture, and the last argument is the random color stored in the RectangleInfo object. The Rectangle argument to Draw requires some massaging, however. Each RectangleInfo object contains two points named point1 and point2 that are opposite corners of the rectangle drawn by the user. But depending how the finger dragged across the screen, point1 might be the upper-right corner and point2 the lower-left corner, or point1 the lower-right corner and point2 the upper-left corner, or two other possibilities. The Rectangle object passed to Draw requires a point indicating the upper-left corner with non-negative width and heights values. (Actually, Rectangle also accepts a point indicating the 807 lower-right corner with width and height values that are both negative, but that little fact doesn’t help simplify the logic.) That’s the purpose of the calls to Math.Min and Math.Abs: XNA Project: DragAndDraw File: Game1.cs (excerpt) protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.Navy); spriteBatch.Begin(); foreach (RectangleInfo rectInfo in rectangles) { Rectangle rect = new Rectangle((int)Math.Min(rectInfo.point1.X, rectInfo.point2.X), (int)Math.Min(rectInfo.point1.Y, rectInfo.point2.Y), (int)Math.Abs(rectInfo.point2.X - rectInfo.point1.X), (int)Math.Abs(rectInfo.point2.Y - rectInfo.point1.Y)); spriteBatch.Draw(tinyTexture, rect, rectInfo.color); } spriteBatch.End(); base.Draw(gameTime); } Here it is after I’ve drawn a couple rectangles: Preserving Render Target Contents I mentioned earlier that the pixels in the Windows Phone 7 back buffer—and the video display itself—were only 16 bits wide. What is the color format of the bitmap created with RenderTarget2D? 808 By default, the RenderTarget2D is created with 32 bits per pixel—8 bits each for red, green, blue, and alpha—corresponding to the enumeration member SurfaceFormat.Color. I’ll have more to say about this format before the end of this chapter, but this 32-bit color format is now commonly regarded as fairly standard. It is the only color format supported in Silverlight bitmaps, for example. To maximize performance, you might want to create a RenderTarget2D or a Texture2D object that has the same pixel format as the back buffer and the display surface. Both classes support constructors that include arguments of type SurfaceFormat to indicate a color format. For the PinwheelText program, creating a RenderTarget2D object with SurfaceFormat.Bgr565 wouldn’t work well. There’s no alpha channel in this format so the background of the RenderTarget2D can’t be transparent. The background would have to be specifically colored to match the background of the back buffer. The following program creates a RenderTarget2D object that is not only the size of the back buffer but also the same color format. The program, however, is rather retro, and you might wonder what the point is. Back in the early days of Microsoft Windows, particularly at trade shows where lots of computers were running, it was common to see programs that simply displayed a continuous series of randomly sized and colored rectangles. But the strategy of writing such a program using XNA is not immediately obvious. It makes sense to add a new rectangle to the mix during the Update method but you don’t want to do it like the DragAndDraw program. The rectangle collection would increase by 30 rectangles every second, and by the end of an hour the Draw override would be trying to render over a hundred thousand rectangles every 33 milliseconds! Instead, you probably want to build up the random rectangles on a RenderTarget2D that’s the size of the back buffer. The rectangles you successively plaster on this RenderTarget2D can be based on the same 1×1 white bitmap used in DragAndDraw. These two bitmaps are stored as fields of the RandomRectangles program together with a Random object: XNA Project: RandomRectangles File: Game1.cs (excerpt showing fields) public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Random rand = new Random(); RenderTarget2D tinyTexture; RenderTarget2D renderTarget; 809 … } The LoadContent method creates the two RenderTarget2D objects. The big one requires an extensive constructor, some arguments of which refer to features beyond the scope of this book: XNA Project: RandomRectangles File: Game1.cs (excerpt) protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); tinyTexture = new RenderTarget2D(this.GraphicsDevice, 1, 1); this.GraphicsDevice.SetRenderTarget(tinyTexture); this.GraphicsDevice.Clear(Color.White); this.GraphicsDevice.SetRenderTarget(null); renderTarget = new RenderTarget2D( this.GraphicsDevice, this.GraphicsDevice.PresentationParameters.BackBufferWidth, this.GraphicsDevice.PresentationParameters.BackBufferHeight, false, this.GraphicsDevice.PresentationParameters.BackBufferFormat, DepthFormat.None, 0, RenderTargetUsage.PreserveContents); } You can see the reference to the BackBufferFormat in the constructor, but also notice the last argument: the enumeration member RenderTargetUsage.PreserveContents. This is not the default option. Normally when a RenderTarget2D is set in a GraphicsDevice, the existing contents of the bitmap are ignored and essentially discarded. The PreserveContents option retains the existing render target data and allows each new rectangle to be displayed on top of all the previous rectangles. The Update method determines some random coordinates and color values, sets the large RenderTarget2D object in the GraphicsDevice, and draws the tiny texture over the existing content with random Rectangle and Color values: XNA Project: RandomRectangles File: Game1.cs (excerpt) protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); 810 [...]... rotated in increments of 90 degrees, and that’s a snap To rotate vector by 90 degrees clockwise, switch the X and Y coordinates while negating the Y coordinate: Vector2 vect90 = new Vector2(-vector.Y, vector.X) A vector rotated 90 degrees from vector is the negation of vect90 If vector points from pt1 to pt2, then the vector from pt1 to pt1a (for example) is that vector rotated 90 degrees with a length... in a XNA library project called Petzold .Phone. Xna I created this project in Visual Studio by selecting a project type of Windows Phone Game Library (4.0) Here’s the complete class I call LineRenderer: 812 XNA Project: Petzold .Phone. Xna File: LineRenderer.cs using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace Petzold .Phone. Xna { public class LineRenderer {... Color.White); spriteBatch.End(); base.Draw(gameTime); } Here’s the gradient: Although the code seems to imply hundreds of gradations between pure blue and pure red, the 16-bit color resolution of the Windows Phone 7 video display clearly shows 32 bands For this particular example, where the Texture2D is the same from top to bottom, it’s not necessary to have quite so many rows In fact, you can create the... filled The Petzold .Phone. Xna project contains several structures that help draw lines in a Texture2D (I made them structures rather than classes because they will probably be frequently instantiated during Update calls.) All these structures implement this little interface: XNA Project: Petzold .Phone. Xna File: IGeometrySegment.cs using System.Collections.Generic; namespace Petzold .Phone. Xna { public... is similar but for a generalized arc on the circumference of a circle: XNA Project: Petzold .Phone. Xna File: ArcSegment.cs using System; using System.Collections.Generic; using Microsoft.Xna.Framework; namespace Petzold .Phone. Xna { public struct ArcSegment : IGeometrySegment { readonly double angle1, angle2; 8 27 public ArcSegment(Vector2 center, float radius, Vector2 point1, Vector2 point2) : this()... lineRenderer; Vector2 center; float radius; int vertexCount = 3; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; // Frame rate is 30 fps by default for Windows Phone TargetElapsedTime = TimeSpan.FromTicks(333333); // Enable taps TouchPanel.EnabledGestures = GestureType.Tap; } … } 814 Notice that the Tap gesture is enabled in the constructor That LineRenderer... spriteBatch; RenderTarget2D renderTarget; LineRenderer lineRenderer; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; // Frame rate is 30 fps by default for Windows Phone TargetElapsedTime = TimeSpan.FromTicks(333333); // Enable gestures TouchPanel.EnabledGestures = GestureType.FreeDrag; } 816 … } Notice that only the FreeDrag gesture is enabled Each gesture will... collection will be returned empty Sometimes it will contain one value, and sometimes two Here’s the LineSegment structure: XNA Project: Petzold .Phone. Xna File: LineSegment.cs using System.Collections.Generic; using Microsoft.Xna.Framework; namespace Petzold .Phone. Xna 826 { public struct LineSegment : IGeometrySegment { readonly float a, b; // as in x = ay + b public LineSegment(Vector2 point1, Vector2... References section in the Solution Explorer and select Add Reference In the Add Reference dialog select the Browse label Navigate to the directory with Petzold .Phone. Xna.dll and select it In the code file you’ll need a using directive: using Petzold .Phone. Xna; 813 You’ll probably create a LineRenderer object in the LoadContent override and then call DrawLine in the Draw override, passing to it the SpriteBatch... but not including angle2 For now, the final structure involved with line drawing represents a line with rounded caps: 828 XNA Project: Petzold .Phone. Xna File: RoundCappedLines.cs using System.Collections.Generic; using Microsoft.Xna.Framework; namespace Petzold .Phone. Xna { public class RoundCappedLine : IGeometrySegment { LineSegment lineSegment1; ArcSegment arcSegment1; LineSegment lineSegment2; ArcSegment . want to display something that looks like this: That’s a bunch of text strings all saying Windows Phone 7 rotated around a center point with colors that vary between cyan and yellow. Of course,. segoe14 = this.Content.Load<SpriteFont>("Segoe14"); string text = " Windows Phone 7& quot;; Vector2 textSize = segoe14.MeasureString(text); // Create RenderTarget2D renderTarget. rectangles: Preserving Render Target Contents I mentioned earlier that the pixels in the Windows Phone 7 back buffer—and the video display itself—were only 16 bits wide. What is the color format

Ngày đăng: 13/08/2014, 08:20

Tài liệu cùng người dùng

Tài liệu liên quan