1. Trang chủ
  2. » Công Nghệ Thông Tin

Building XNA 2.0 Games- P13 potx

30 276 0

Đ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

Thông tin cơ bản

Định dạng
Số trang 30
Dung lượng 1,28 MB

Nội dung

348 CHAPTER 11 ■ POSTPROCESSING EFFECTS technique Water { pass P0 { PixelShader = compile ps_2_0 Water(); } } It’s a bit of ugly trig, but here’s the gist: we tell our shader where the water “horizon” is (denoted by the dotted line in Figure 11-6), and then feed it two values: delta and theta, which are floats that range between 0 and 2 S. Since we can’t update variables from one to the next in a shader, we’ll be updating these values from Game1 and feeding them into our water shader. For the technique function, we set tex.y as horizon - tex.y, resulting in a flipped image where the line of symmetry is 0 (panel 2 of Figure 11-6). Then, applying some trig functions (which, honestly, were reached by trial and error), we nudge our texture coordinates around a bit, resulting in the rippling effect. Finally, we smoothly fade out the top 20% of the image. As for implementing this in the game, we have a problem. The way we have our map set up, it would look really nice to have the water drawn between the main draw (map, characters, particles, and so on) and the foreground. However, if we kept everything else intact and intro- duced water between the main draw and the foreground, we would need to either introduce another render target or draw the foreground twice. Let’s see how the solutions would pan out. Adding a new render target goes like this: • Set render target to mainTarg • Draw background and main (characters, particles, and so on) • Set render target to waterTarg • Draw with water effect • Set render target to auxTarg • Draw mainTarg and waterTarg to auxTarg • Set render target to bloomTarg[0] •Draw auxTarg with bloom effect • Set render target to bloomTarg[1] •Draw auxTarg with bloom effect • Set render target to gameTarg •Draw auxTarg •Draw bloomTarg[0] •Draw bloomTarg[1] • Set render target to backbuffer •Draw gameTarg CHAPTER 11 ■ POSTPROCESSING EFFECTS 349 •Draw HUD •Present And here’s what we would need to do to draw the foreground twice: • Set render target to mainTarg • Draw the game • Set render target to waterTarg • Draw with water effect • Set render target to bloomTarg[0] •Draw mainTarg with bloom effect • Set render target to bloomTarg[1] •Draw mainTarg with bloom effect • Set render target to gameTarg •Draw mainTarg •Draw waterTarg • Draw map foreground •Draw bloomTarg[0] •Draw bloomTarg[1] • Set render target to backbuffer •Draw gameTarg •Draw HUD •Present This goes to show how trying to work a simple change into the render loop—where render targets are concerned—can really throw a wrench in things. Both solutions are feasible but a little wasteful. So we came up with a third solution that doesn’t leave our current setup intact—it adds a bit of feedback to our bloom. The way our bloom is set up currently, we draw the image, then calculate the bloom, and then apply the bloom to the image. To use feedback, we need to draw the image, apply the bloom, and then calculate the bloom to use on the next frame. We must make sure that we don’t draw any bloom on the first frame, of course, but every subsequent frame will be fine. This will give us a sort of dreamy, hazy effect, and is also a bit dangerous. Since we’re operating on the previous frame, if we set our bloom alpha too high, the image will rapidly grow brighter until it is solid white. 350 CHAPTER 11 ■ POSTPROCESSING EFFECTS The render loop to use feedback looks like this: • Set render target to mainTarg • Draw background and main (characters, particles, and so on) • Set render target to waterTarg • Draw with water effect • Set render target to gameTarg •Draw mainTarg •Draw waterTarg • Draw map foreground •Draw bloomTarg[0] •Draw bloomTarg[1] • Set render target to bloomTarg[0] • Draw mainTarg with bloom effect • Set render target to bloomTarg[1] •Draw mainTarg with bloom effect • Set render target to backbuffer •Draw gameTarg •Draw HUD •Present This gives us the best of both worlds, and the feedback bloom effect is really appropriate for our moody cemetery. Here’s the actual code: graphics.GraphicsDevice.SetRenderTarget(0, mainTarget); pManager.DrawParticles(spritesTex, false); EffectPass pass; We add a class-level field to Map called water. The water field specifies the water level; 0 for no water. We also add a new script command, COMMAND_WATER, to let us set the water level through the map init script. CHAPTER 11 ■ POSTPROCESSING EFFECTS 351 float waterLevel = map.water - (.2f * screenSize.Y); if (map.water > 0f) { graphics.GraphicsDevice.SetRenderTarget(0, waterTarget); float wLev = (screenSize.Y / 2f + waterLevel - scroll.Y) / screenSize.Y; waterEffect.Parameters["delta"].SetValue(waterDelta); waterEffect.Parameters["theta"].SetValue(waterTheta); waterEffect.Parameters["horizon"].SetValue(wLev); waterEffect.Begin(); spriteBatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.SaveState); pass = waterEffect.CurrentTechnique.Passes[0]; pass.Begin(); spriteBatch.Draw(mainTarget.GetTexture(), new Rectangle(0, 0, 256, 256), Color.White); pass.End(); spriteBatch.End(); waterEffect.End(); } graphics.GraphicsDevice.SetRenderTarget(0, gameTarget); if (gameMode == GameMode.Menu) { } else { filterEffect.Parameters["burn"].SetValue(.15f); filterEffect.End(); if (map.water > 0f) { spriteBatch.Begin(SpriteBlendMode.AlphaBlend); spriteBatch.Draw(waterTarget.GetTexture(), new Rectangle(0, (int)(waterLevel - scroll.Y), (int)screenSize.X, (int)screenSize.Y), Color.White); 352 CHAPTER 11 ■ POSTPROCESSING EFFECTS spriteBatch.End(); } map.Draw(spriteBatch, mapsTex, mapBackTex, 2, 3); We’ll use a class-level float, hasBloom, to let us know that our bloom targets have been drawn on. Once we draw our bloom targets a few lines later, hasBloom will always be true, but if we don’t throw this failsafe in, we’ll get an error. if (hasBloom) { spriteBatch.Begin(SpriteBlendMode.Additive); for (int i = 0; i < 2; i++) spriteBatch.Draw(bloomTarget[i].GetTexture(), new Rectangle(0, 0, (int)screenSize.X, (int)screenSize.Y), Color.White); spriteBatch.End(); } } Now we’ll calculate our bloom from our already-bloomed gameTarget (we previously used mainTarget). for (int i = 0; i < 2; i++) { hasBloom = true; graphics.GraphicsDevice.SetRenderTarget(0, bloomTarget[i]); bloomEffect.Parameters["a"].SetValue(.25f); spriteBatch.Draw(gameTarget.GetTexture(), new Rectangle(0, 0, 128 * (i + 1), 128 * (i + 1)), Color.White); bloomEffect.End(); } graphics.GraphicsDevice.SetRenderTarget(0, null); spriteBatch.Begin(SpriteBlendMode.None); spriteBatch.Draw(gameTarget.GetTexture(), new Vector2(), Color.White); spriteBatch.End(); spriteBatch.Begin(SpriteBlendMode.AlphaBlend); CHAPTER 11 ■ POSTPROCESSING EFFECTS 353 if (QuakeManager.blast.val > 0f) { } spriteBatch.End(); In UpdateGame(), we update the theta and delta values: waterDelta += frameTime * 8f; waterTheta += frameTime * 10f; The end result (provided we implemented the new map script command) is shown in Figure 11-7. Figure 11-7. Reflecting water Refraction Effects Moving up the complexity ladder, we arrive at refraction. Refraction involves distorting the produced image specifically. We can use it for effects like shockwaves and heat haze. Our strategy is shown in Figure 11-8. It will go something like this: • Draw the main stuff to a render target (first panel) • Draw the refraction stuff to a second render target (second panel) 354 CHAPTER 11 ■ POSTPROCESSING EFFECTS • Draw a third image using one shader and the two render target textures on separate texture levels (third panel) Figure 11-8. A refraction plan This is actually really easy to set up. We can start off by just changing the filter.fx to accept another sampler and adding the refraction functionality. All the refraction functionality is handled through a function called GetDif(), which gets the difference in the red value of horizontally and vertically neighboring pixels and adjusts the texture coordinates accordingly, returning the adjusted amount as a float2. //filter.fx sampler samplerState; sampler refractSampler; float burn = 0.01f; float saturation = 1.0f; float r = 1.0f; float g = 1.0f; float b = 1.0f; float brite = 0.0f; struct PS_INPUT { float2 TexCoord : TEXCOORD0; }; float2 GetDif(float2 _tex) { float2 dif; float2 tex = _tex; float2 btex = _tex; tex.x -= 0.003; btex.x += 0.003; CHAPTER 11 ■ POSTPROCESSING EFFECTS 355 dif.x = tex2D(refractSampler, tex).r - tex2D(refractSampler, btex).r; tex = _tex; btex = _tex; tex.y -= 0.003; btex.y += 0.003; dif.y = tex2D(refractSampler, tex).r - tex2D(refractSampler, btex).r; tex = _tex; dif *= (1.5 - tex2D(refractSampler, tex).r); return dif; } float4 Filter(PS_INPUT Input) : COLOR0 { float2 tex = Input.TexCoord + GetDif(Input.TexCoord) * 0.1f; float4 col = tex2D(samplerState, tex); float d = sqrt(pow((tex.x - 0.5), 2) + pow((tex.y - 0.5), 2)); That’s all we need for our filter.fx. Now we’ll modify Game1 just enough to show that refraction is working. We’ll start by creating a class-level RenderTarget2D called refractTarget, which will instantiate in LoadContent() to be identical to mainTarget. Then, in DrawGame(), we’ll draw our sprites texture to refractTarget as a test, immediately after we finish drawing the main game stuff: pManager.DrawParticles(spritesTex, false); graphics.GraphicsDevice.SetRenderTarget(0, refractTarget); graphics.GraphicsDevice.Clear(Color.Black); spriteBatch.Begin(SpriteBlendMode.AlphaBlend); spriteBatch.Draw(spritesTex, new Rectangle(0, 0, 800, 600), Color.Red); spriteBatch.End(); Moving along, we draw. mainTarget to gameTarget using the filter effect. Since we’ve modi- fied filter.fx to include another sampler, we need to set our graphics device to include the additional sampler: graphics.GraphicsDevice.Textures[1] = refractTarget.GetTexture(); filterEffect.Parameters["burn"].SetValue(.15f); filterEffect.End(); graphics.GraphicsDevice.Textures[1] = null; 356 CHAPTER 11 ■ POSTPROCESSING EFFECTS The result of this is shown in Figure 11-9. Notice how the text looks like it puts an inner bevel on the explosion. Also, can you see the hearts in the row above the text? Figure 11-9. Refraction test It’s not much of an extra stretch to plug this effect into our game. We’ll just modify our particle setup by adding a Boolean to the Particles base class: refract. Any particles for which refract is true will be drawn to our refractTarget; otherwise, particles will be drawn as normal. After adding the refract Boolean, we make a refract particle called Heat. We’ll use it for heat haze, which we can attach to our muzzle flashes, rocket contrails, torches, and so on. Heat looks like this: class Heat : Particle { public Heat(Vector2 loc, Vector2 traj, float size) { this.Location = loc; this.Trajectory = traj; CHAPTER 11 ■ POSTPROCESSING EFFECTS 357 this.Size = size; this.Flag = Rand.GetRandomInt(0, 4); this.Owner = -1; this.Exists = true; this.rotation = Rand.GetRandomFloat(0f, 6.28f); this.Frame = Rand.GetRandomFloat(.5f, .785f); this.Refract = true; } public override void Draw(SpriteBatch sprite, Texture2D spritesTex) { Rectangle sRect = new Rectangle(flag * 64, 64, 64, 64); a = (float)Math.Sin((double)frame * 4.0) * .1f; sprite.Draw(spritesTex, GameLocation, sRect, new Color( new Vector4(1f, 0f, 0f, a)), rotation + frame * 16f, new Vector2(32.0f, 32.0f), Size, SpriteEffects.None, 1.0f); } } We’ll add some special cases to our DrawParticles() method in ParticleManager. Currently, it draws all alpha-blended particles, and then draws all additive-blended particles. We need to add a little condition to make sure it doesn’t try to draw any refract particles: public void DrawParticles(Texture2D spritesTex, bool background) { sprite.Begin(SpriteBlendMode.AlphaBlend); foreach (Particle p in particle) { if (p != null) { if (!p.Additive && p.Background == background && !p.Refract) p.Draw(sprite, spritesTex); } } sprite.End(); sprite.Begin(SpriteBlendMode.Additive); foreach (Particle p in particle) { if (p != null) [...]... numerous hurdles, networking can really do a great deal to define your game The XNA Framework alleviates a number of classic networking hassles, like ensuring data ordering and delivery, and network game state management Even better, it allows you to take advantage of Xbox Live matchmaking features With a good grasp on networking with XNA Game Studio, you could feasibly make your own Soldat-like 31-player... things the old way For those of you familiar with DirectX of old, you can consider networking in XNA to be everything DirectPlay should have been and more Not only do you get built-in local-area network (LAN) and Xbox Live capabilities, you also get built-in voice chat To set up networking in Zombie Smashers XNA, here’s what we’ll be doing: • Add gamer services to the game, enabling networking • Add functionality... game session Our final product will be a two-player online co-op arcade game in which two zombiesmashing heroes face off against wave after wave of zombies Networking with XNA Game Studio We have a few requirements for networking with XNA Game Studio Our first order of business is the physical setup of our development environment You can’t use two instances of the same game running on the same machine;... testing purposes We’ll simulate lag on System Link to get a good feel for how our game will play over Games for Windows LIVE For any Xbox 360 deployment, you will need one XNA Creators Club membership per Xbox 360—at least, that’s what the XNA documentation says At the time of writing, we would get an exception while attempting to create a System Link session from a Windows game where no profile was signed... shown in Figure 12-1 ■Note You can use the Games for Windows LIVE Guide in Windows to test profile functionality, but here’s the deal: if you sign on with a profile that does not have an XNA Creators Club membership, XNA Game Studio will cry foul, throwing a GamerServicesNotAvailable exception in Game1.Update() You can opt to either leave profiles alone on the Windows environment or make sure you use... game where no profile was signed in, and would also get an exception when signing in to a profile without a Creators Club membership It seems like the way to go is one Xbox Silver Live membership and one XNA Creators Club membership per account Adding the Gamer Service Component To make our game network-ready, we need to initialize a component called the Gamer Services Component This just requires adding... talk about sending and receiving game messages Network Control We’ll start with NetPlay: public class NetPlay { public NetConnect netConnect; public NetGame netGame; NetworkSession is the object that the XNA Framework will use for all network session management: player states, data sending and receiving, and so on NetworkSession does a lot of work on its own, provided we Update() it every frame public . = 4; i < 12; i++) AddParticle(new Heat( Location+ (Trajectory* (float)i) * 0. 001 f + Rand.GetRandomVector2(-30f, 30f, -30f, 30f), Rand.GetRandomVector2(-30f, 30f, - 100 f, 0f), Rand.GetRandomFloat(.5f,. Heat(mapSeg[LAYER_MAP, i].Location * 2f + new Vector2 ( 20 f, -50f), Rand.GetRandomVector2(-50f, 50f, - 400 f, - 300 f), Rand.GetRandomFloat(1f, 2f))); The results, as shown in Figure 11- 10, look pretty nice. You. 0. 0f; struct PS_INPUT { float2 TexCoord : TEXCOORD0; }; float2 GetDif(float2 _tex) { float2 dif; float2 tex = _tex; float2 btex = _tex; tex.x -= 0. 003 ; btex.x += 0. 003 ; CHAPTER 11 ■ POSTPROCESSING

Ngày đăng: 01/07/2014, 22:20