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

Building XNA 2.0 Games- P6 pptx

30 261 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 621,12 KB

Nội dung

CHAPTER 6 ■ BRINGING IT TO THE GAME 137 ■Note In-line code is code that is kept in one place, rather than componentized into methods and/or classes. It is not a great idea in terms of organization, but can be useful for optimizing memory usage. Updating the Animation We’re basically copying and pasting the Update() code from Game1 in CharacterEditor to Character, with a few changes. We’ll remove any functionality to determine whether the animation preview is playing. We’ll also change some of the field names to make a bit more sense in the context. However, the core logic remains unchanged: update the frame index until we can’t update it anymore, and then loop back to the beginning. public void Update(GameTime gameTime) { float et = (float)gameTime.ElapsedGameTime.TotalSeconds; #region Update Animation Animation animation = charDef.Animations[Anim]; KeyFrame keyFrame = animation.KeyFrames[AnimFrame]; frame += et * 30.0f; if (frame > (float)keyFrame.Duration) { frame -= (float)keyFrame.Duration; AnimFrame++; if (AnimFrame >= animation.KeyFrames.Length) AnimFrame = 0; keyFrame = animation.KeyFrames[AnimFrame]; if(keyFrame.FrameRef < 0) AnimFrame = 0; } #endregion Updating the Location To update the location, we’ll simply add the trajectory, multiplied by Game1.frameTime, to the character’s current location. If the character’s state is STATE_GROUNDED and the character’s x trajectory is not zero, reduce the trajectory by Game1.friction. If the character is airborne, the y trajectory’s value will be increased by Game1.gravity, giving him a nice airborne arc. 138 CHAPTER 6 ■ BRINGING IT TO THE GAME #region Update Location By Trajectory Vector2 pLoc = new Vector2(Location.X, Location.Y); if (State == CharState.Grounded) { if (Trajectory.X > 0f) { Trajectory.X -= Game1.Friction * et; if (Trajectory.X < 0f) Trajectory.X = 0f; } if (Trajectory.X < 0f) { Trajectory.X += Game1.Friction * et; if (Trajectory.X > 0f) Trajectory.X = 0f; } } Location.X += Trajectory.X * et; if (State == CharState.Air) { Location.Y += Trajectory.Y * et; Trajectory.Y += et * Game1.Gravity; } #endregion Collision Detection Here comes a big chunk of code: collision detection. We’ll split this further into regions for better organization. What basically must happen here is as follows: • Airborne state collision: • Check horizontal collisions (moving left or right into walls) • Check vertical collisions: • Landing on ledge? • Landing on collision cell? • Grounded state collision: • Check horizontal collisions • Check to make sure the character still has ground below him: • Falling off ledge? • Falling off collision cell? CHAPTER 6 ■ BRINGING IT TO THE GAME 139 Let’s look at the code. We’ll be using a few tiny functions, which will be defined in a few pages, but based on the preceding outline and their names, their purpose should be pretty obvious. #region Collision detection if (State == CharState.Air) { #region Air State CheckXCol(map, pLoc); To check whether our character has landed on a ledge, we’ll do the following: • Make sure our character is moving downward (trajectory.Y > 0.0f). • Iterate through map ledges. • Check map ledges where the number of nodes is > 1. • Store the ledge section the character is over or under as s. • Store the ledge section the character was over or under before his location was updated as ts. •If s or ts is -1, the character isn’t and wasn’t over or under the ledge; otherwise, do this: • Store the interpolated y value for the character’s current location as fY. • Store the interpolated y value for the character’s previous location as tfY. • If the character’s previous y location is <= tfY and the character’s current location is >= fY, this means the character is attempting to pass through the ledge in this current Update(). Land him! Figure 6-3 shows a few scenarios. Figure 6-3. Ledge landing scenario. The grayed figures represent the character’s previous location for each scenario; the black figures represent the current locations. 140 CHAPTER 6 ■ BRINGING IT TO THE GAME Here’s the code for landing on a ledge: #region Land on ledge if (trajectory.Y > 0.0f) { for (int i = 0; i < 16; i++) { if (map.GetLedgeTotalNodes(i) > 1) { int ts = map.GetLedgeSec(i, pLoc.X); int s = map.GetLedgeSec(i, Location.X); float fY; float tfY; if (s > -1 && ts > -1) { tfY = map.GetLedgeYLoc(i, s, pLoc.X); fY = map.GetLedgeYLoc(i, s, Location.X); if (pLoc.Y <= tfY && Location.Y >= fY) { if (trajectory.Y > 0.0f) { Location.Y = fY; ledgeAttach = i; Land(); } } else if (map.GetLedgeFlags(i) == LedgeFlags.Solid && Location.Y >= fY) { Location.Y = fY; ledgeAttach = i; Land(); } } } } } #endregion We’ll use a much simpler algorithm to detect whether a character has landed on a collision cell. If the location at the character’s feet occupies a collision cell, we’ll move the character’s y location to the top of that cell and land him. CHAPTER 6 ■ BRINGING IT TO THE GAME 141 #region Land on col if (State == CharState.Air) { if (trajectory.Y > 0f) { if (map.checkCol(new Vector2(loc.X, loc.Y + 15f))) { loc.Y = (float)((int)((loc.Y + 15f) / 64f) * 64); Land(); } } } #endregion #endregion } else if (State == CharState.Grounded) { With the grounded character, instead of checking to see if he has landed on something, we check to see if he has fallen off something. If he is attached to a ledge, we check only if GetLedgeSec() returns -1, meaning there is no section for the character’s current x location, or the character is not on a ledge. If the character is still on a ledge, we update his y location to the interpolated value we get from GetLedgeYLoc(). #region Grounded State if (ledgeAttach > -1) { if (map.GetLedgeSec(ledgeAttach, loc.X) == -1) { FallOff(); } else { loc.Y = map.GetLedgeYLoc(ledgeAttach, map.GetLedgeSec(ledgeAttach, loc.X), loc.X); } } else { Likewise, if the character is not attached to a ledge, we’ll check to see if he has a collision cell below him. If not, he falls off. 142 CHAPTER 6 ■ BRINGING IT TO THE GAME if (!map.checkCol(new Vector2(loc.X, loc.Y + 15f))) FallOff(); } CheckXCol(map, pLoc); #endregion } #endregion Character Input We’ll handle our input with another code block in Character.Update(). We’ll do some case-by- case logic, so that you can do certain things only while in certain animations. The player can switch between idle and running animations based on which keys are pressed, and can jump while idle or running. Trajectory is also updated accordingly. #region Key input if (animName == "idle" || animName == "run") { if (keyLeft) { SetAnim("run"); trajectory.X = -200f; Face = CharDir.Left; } else if (keyRight) { SetAnim("run"); trajectory.X = 200f; Face = CharDir.Right; } else { SetAnim("idle"); } if (keyJump) { SetAnim("fly"); trajectory.Y = -600f; State = CharState.Air; ledgeAttach = -1; if (keyRight) trajectory.X = 200f; if (keyLeft) trajectory.X = -200f; } } CHAPTER 6 ■ BRINGING IT TO THE GAME 143 An airborne player can move either left or right for now—violating some physics in the name of game play. Pressing Left or Right on the gamepad while in midair nudges the trajec- tory slightly left or right. if (animName == "fly") { if (keyLeft) { Face = CharDir.Left; if (trajectory.X > -200f) trajectory.X -= 500f * Game1.frameTime; } if (keyRight) { Face = CharDir.Right; if (trajectory.X < 200f) trajectory.X += 500f * Game1.frameTime; } } #endregion That concludes the massive Update() function. You might want to organize it differently, but regions work well enough for the time being. New Character Functions We’ve thrown a couple more functions into the mix, so let’s define them before moving on. CheckXCol() To simplify movement, we check x movement collisions separately from y movement. We’ve defined a function for this. CheckXCol() checks whether the character location overlaps a collision cell on the left or right (with the location padded by 25f) and returns the character’s x location to pLoc.X if so. We’ll eventually use a padding value that’s a function of the character’s scale, so larger characters won’t overlap collision cells. private void CheckXCol(Map map, Vector2 pLoc) { if (trajectory.X > 0f) if (map.checkCol(new Vector2(loc.X + 25f, loc.Y - 15f))) loc.X = pLoc.X; if (trajectory.X < 0f) if (map.checkCol(new Vector2(loc.X - 25f, loc.Y - 15f))) loc.X = pLoc.X; } 144 CHAPTER 6 ■ BRINGING IT TO THE GAME FallOff() The function FallOff() is called when a grounded character realizes that he no longer has ground below him, which could occur if he was on a collision cell or a ledge. He gets set to airborne state, has his animation set to fly, and has his y trajectory reset. private void FallOff() { State = CharState.Air; SetAnim("fly"); trajectory.Y = 0f; } Land() The character can land on collision cells or ledges, so we define a simple Land() function to set him to grounded state and idle animation. private void Land() { State = CharState.Grounded; SetAnim("idle"); } Notice how we transition from airborne state to grounded state and vice versa. The char- acter will simply launch into the air without crouching first, and will land on stiff legs. This won’t look quite right, but it’s the best implementation we can hope for before we get into scripting—and honestly, we just want to see something cool soon. Now it’s time to make a Draw() function. Drawing the Character We’ll be reusing the drawing code from CharacterEditor. We’ll just move it into Character and modify it slightly. For starters, we don’t need all of the parameters; the SpriteBatch is enough (everything else is now a class-level field). public void Draw(SpriteBatch spriteBatch) { Rectangle sRect = new Rectangle(); int frameIdx = charDef.GetAnimation(anim).GetKeyFrame(animFrame).frameRef; Frame frame = charDef.GetFrame(frameIdx); spriteBatch.Begin(SpriteBlendMode.AlphaBlend); . . . CHAPTER 6 ■ BRINGING IT TO THE GAME 145 float rotation = part.rotation; Vector2 location = part.location * scale + loc - Game1.scroll; Vector2 scaling = part.scaling * scale; . . . Color color = new Color(new Vector4(1.0f, 1.0f, 1.0f, 1f)); We can remove the line that changes the color for preview mode. bool flip = false; . . . Everything else can be left the way it was. There are a few inherent changes going on where we didn’t actually need to modify the code, such as loc and face now being class-level fields. Texture Loading Remember how we declared our character textures as statics? We can also make a static function to load them in Character, which we’ll call from Game1. internal static void LoadTextures(ContentManager content) { for (int i = 0; i < headTex.Length; i++) headTex[i] = content.Load<Texture2D>(@"gfx/head" + (i + 1).ToString()); for (int i = 0; i < torsoTex.Length; i++) torsoTex[i] = content.Load<Texture2D>(@"gfx/torso" + (i + 1).ToString()); for (int i = 0; i < legsTex.Length; i++) legsTex[i] = content.Load<Texture2D>(@"gfx/legs" + (i + 1).ToString()); for (int i = 0; i < weaponTex.Length; i++) weaponTex[i] = content.Load<Texture2D>(@"gfx/weapon" + (i + 1).ToString()); } Lastly, we should handle some input. 146 CHAPTER 6 ■ BRINGING IT TO THE GAME Gamepad Input At this point, we’re going to let our character move only left and right and jump. Eventually, we’ll add all sorts of nonsense. We’ll pass the controller index to the method, and then compare the state of the gamepad with the way it looked the last time we checked it. This way, we can test to see if a button has just been pressed. public void DoInput(int index) { curState = GamePad.GetState((PlayerIndex)index); keyLeft = false; keyRight = false; keyJump = false; keyAttack = false; keySecondary = false; keyUp = false; keyDown = false; if (curState.ThumbSticks.Left.X < -0.1f) keyLeft = true; if (curState.ThumbSticks.Left.X > 0.1f) keyRight = true; if (curState.ThumbSticks.Left.Y < -0.1f) keyDown = true; if (curState.ThumbSticks.Left.Y > 0.1f) keyUp = true; if (curState.Buttons.A == ButtonState.Pressed && prevState.Buttons.A == ButtonState.Released) keyJump = true; if (curState.Buttons.Y == ButtonState.Pressed && prevState.Buttons.Y == ButtonState.Released) keyAttack = true; if (curState.Buttons.X == ButtonState.Pressed && prevState.Buttons.X == ButtonState.Released) keySecondary = true; prevState = curState; } . Rectangle(5 90, 0, 300 , 600 ), new Color(new Vector4 (0. 0f, 0. 0f, 0. 0f, 0. 5f))); spriteBatch.Draw(nullTex, new Rectangle ( 20 0, 0, 1 50, 1 10) , new Color(new Vector4 (0. 0f, 0. 0f, 0. 0f, 0. 5f))); spriteBatch.End(); This. Vector2 targ = new Vector2( Game1.ScreenSize.X / 2f - ((Game1.scroll.X / xLim) - 0. 5f) * 100 f, Game1.ScreenSize.Y / 2f - ((Game1.scroll.Y / yLim) - 0. 5f) * 100 f ); sprite.Draw(mapBackTex [0] ,. sprite.Draw(mapBackTex [0] , targ, new Rectangle (0, 0, 128 0, 7 20 ), Color.White, 0f, new Vector2(640f, 360f), 1f, SpriteEffects.None, 1f); } for (int l = startLayer; l < endLayer; l++) 1 52 CHAPTER 6 ■ BRINGING IT

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

TỪ KHÓA LIÊN QUAN