1 / 50

Hoorcollege 13

Hoorcollege 13. Animatie , game physics. De speler. Wordt aangestuurd via toetsenbord Kent verschillende soorten bewegingen Rennen Stilstaan Springen Doodgaan Botsingen moeten afgehandeld worden We willen simpele ‘physics’ voor de speler. Animatie.

zan
Télécharger la présentation

Hoorcollege 13

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Hoorcollege 13 Animatie, game physics

  2. De speler • Wordtaangestuurd via toetsenbord • Kent verschillendesoortenbewegingen • Rennen • Stilstaan • Springen • Doodgaan • Botsingenmoetenafgehandeldworden • We willensimpele ‘physics’ voor de speler

  3. Animatie • We hebben al simpele animaties gezien • In de Painter game: bal, paintcans, … • Bewegende paddles in Pong • Tetrisblokjes die naar beneden vallen • Enzovoorts… • Maar tot nu toe verplaatsen we alleen sprites • Hoe zit het met animaties?

  4. Wat is animatie? • Animatie is een snelle opeenvolging van net iets andere plaatjes • Als dat snel genoeg gebeurt, dan denken we dat iets beweegt. • “Persistence of Vision” + “Persistence of Motion”

  5. Animatie in C# • Verschillendemogelijkheden • Iedere frame = een sprite • Een sprite bevateenaantal frames • Hiervoorkunnen we mooi sprite sheets gebruiken! 1 frame De sprite met alle frames

  6. AnimatedCharacter Geanimeerde sprite

  7. Overzicht • Animation klasse • Uitbreiding van SpriteSheet • Namelijk: sheet index aanpassen aan de verstreken tijd • AnimatedGameObject klasse • Bevat een aantal Animation objecten • Kan verschillende animaties afspelen

  8. Animation klasse public class Animation : SpriteSheet { protectedfloatframeTime; protectedboolisLooping; protectedfloattime; public Animation(string asset, boolisLooping, floatfrtime = 0.1f) : base(asset) { this.frameTime = frtime; this.isLooping = isLooping; } public int CountFrames { get { return this.NumberSheetElements; } } … Handige property dat het aantal frames berekent De tijdtusseniedere frame, oftewel: ditbepaalt de snelheid van de animatie!

  9. public floatFrameTime { get { returnframeTime; } } public boolIsLooping { get { returnisLooping; } } public boolAnimationEnded { get { return !this.isLooping && sheetIndex >= NumberSheetElements - 1; } } Nogmeer properties…

  10. public voidPlay() { this.sheetIndex = 0; this.time = 0.0f; } public override void Update(GameTimegameTime) { time += (float)gameTime.ElapsedGameTime.TotalSeconds; while (time > frameTime) { time -= frameTime; if (isLooping) sheetIndex = (sheetIndex + 1) % this.CountFrames; else sheetIndex = Math.Min(sheetIndex + 1, this.CountFrames - 1); } } Start de animatie. Bereken huidig af te beelden frame.

  11. Spiegelen van sprites • Naar links lopen of naar rechts lopen • Spiegel de sprite in plaats van twee aparte sprites public class SpriteSheet { … protectedboolmirror; … public boolMirror { get { returnmirror; } set { mirror = value; } } … }

  12. SpriteSheet public void Draw(SpriteBatch s, Vector2 position, Vector2 origin) { intcolumnIndex = sheetIndex % sheetColumns; introwIndex = sheetIndex / sheetColumns; Rectangle spritePart = new Rectangle(columnIndex * this.Width, rowIndex * this.Height, this.Width, this.Height); SpriteEffectsspriteEffects = SpriteEffects.None; if(mirror) spriteEffects = SpriteEffects.FlipHorizontally; spriteBatch.Draw(sprite, position, spritePart, Color.White, 0.0f, origin, 1.0f, spriteEffects, 0.0f); } Sprite spiegelen

  13. AnimatedGameObject public class AnimatedGameObject : SpriteGameObject { protected Dictionary<string, Animation> animations; publicAnimatedGameObject(int layer = 0, string id = "") : base(layer, id) { animations = new Dictionary<string, Animation>(); } public AnimationCurrent { get { return sprite as Animation; } } … }

  14. AnimatedGameObject public void LoadAnimation(string asset, string id, bool looping, floatframetime = 0.1f) { Animation anim = new Animation(assetname, looping, frametime); animations[id] = anim; } public voidPlayAnimation(stringid) { if (sprite == animations[id]) return; if(sprite != null) animations[id].Mirror = sprite.Mirror; animations[id].Play(); sprite = animations[id]; origin = new Vector2(sprite.Width / 2, sprite.Height); }

  15. AnimatedGameObject public override void Update(GameTimegameTime) { if (sprite == null) return; Current.Update(gameTime); base.Update(gameTime); }

  16. De Player klasse classPlayer : AnimatedGameObject { public Player(Vector2 start) : base(2, "player") { this.LoadAnimation("spr_idle", "idle", true); this.LoadAnimation("spr_run@13", "run", true, 0.05f); this.LoadAnimation("spr_jump@14", "jump", false, 0.05f); this.LoadAnimation("spr_celeb@14", "celebrate", false, 0.05f); this.LoadAnimation("spr_die@5", "die", false); this.LoadAnimation("spr_expl@5x5", "explode", false, 0.04f); startPosition = start; Reset(); } …

  17. public overridevoidReset() { this.position = startPosition; isOnTheGround = true; this.PlayAnimation("idle"); previousYPosition = BoundingBox.Bottom; } public override void Update(GameTimegameTime) { base.Update(gameTime); if (isOnTheGround) if (velocity.X == 0) this.PlayAnimation("idle"); else this.PlayAnimation("run"); elseif(velocity.Y < 0) this.PlayAnimation("jump"); DoPhysics(); } Spelerbegint op een bepaaldepositie. startconfiguratie “idle” animatie positie

  18. public overridevoidHandleInput(InputHelperinputHelper) { floatwalkingSpeed = 400; if (inputHelper.IsKeyDown(Keys.Left)) velocity.X = -walkingSpeed; elseif(inputHelper.IsKeyDown(Keys.Right)) velocity.X = walkingSpeed; elseif(isOnTheGround) velocity.X = 0.0f; if (velocity.X != 0.0f) Mirror = velocity.X < 0; if((inputHelper.KeyPressed(Keys.Space) || inputHelper.KeyPressed(Keys.Up)) && isOnTheGround) Jump(); } Kies de juiste beweegrichting Willen we springen?

  19. Speler physics public void Jump(float speed = 1100) { velocity.Y = -speed; } private void DoPhysics() { velocity.Y += 55f; HandleCollisions(); } We vallen steeds harder naarbeneden. Springen = negatieve y-snelheid.

  20. Botsingen in games

  21. Botsingen tussen sprites • Twee manieren: • Kijk per pixel of de sprites overlappen • Gebruikeenvereenvoudigdevoorstelling van de sprites

  22. Botsingen tussen sprites • Twee veel voorkomende vormen: cirkel, rechthoek • Noemen we ook wel ‘bounding box’ en ‘bounding circle’ (of ‘bounding sphere’ in 3D) • Dit kunnen we ook generaliseren: • Convexe polygonen met behulp van Separating Axis Theorem • Wij beperken ons tot cirkels en rechthoeken

  23. Botsingen tussen sprites • Berekenen van bounding cirkels en bounding boxes. Rectangle bounding = new Rectangle(spritePositie.X, spritePositie.Y, sprite.Width, sprite.Height); sprite.Width spritePositie (X,Y) sprite.Height

  24. Botsingen tussen sprites • Bounding cirkel berekenen we als volgt: Vector2 middelpunt = new Vector2(spritePositie.X + sprite.Width / 2, spritePositie.Y + sprite.Height / 2); floatstraal = Math.Max(sprite.Width / 2, sprite.Height / 2); spritePositie (X,Y) sprite.Width straal sprite.Height middelpunt

  25. Botsingen tussen sprites • Hoe berekenen we dit? • Drie mogelijkheden:

  26. Botsingen tussen sprites • Twee cirkels botsen met elkaar als de afstand tussen de middelpunten kleiner is dan de som van de twee stralen

  27. Botsingen tussen sprites • In code: boolInBotsing(Vector2 middel1, float straal1, Vector2 middel2, float straal2) { floatafstand = (middel1 – middel2).Length(); returnafstand < straal1 + straal2 == true; } #$&*(@!!!

  28. Botsingen tussen sprites • Twee rechthoeken • Is al voor ons gedaan! Rectangle box1, box2; … boolbotst = box1.Intersects(box2);

  29. Botsingen tussen sprites • Tussen een cirkel en een rechthoek • Via het dichtstbijzijnde punt op de rechthoek tot middelpunt van de cirkel

  30. Botsingen tussen sprites boolInBotsing(Vector2 middel, floatstraal, Rectangle box) { Vector2 dichtstbij = new Vector2( MathHelper.Clamp(middel.X, box.Left, box.Right), MathHelper.Clamp(middel.Y, box.Top, box.Bottom)); floatafstand = (dichtstbij – middel).Length(); returnafstand < straal; }

  31. Wat te doen bij een botsing? • Snelheidomdraaien • Twee biljartballen die botsen • Het balletje in Pong vliegt de anderekant op • Een van de sprites nietmeertekenen • Als je over een ‘powerup’ vliegt • Game over • De spelerloopttegeneenvijandaan • …

  32. Botsingen verwerken • De spelerkanbotsen met: • Vijanden • Waterdruppels • Walltiles • Vijanden + waterdruppelshandelen we af in de Enemy/WaterDrop-klassen (komt later) • Speler + Tiles doen we in de Spelerklasse

  33. Botsingen met tiles • Kijkvoorelke tile of hijbotst met de speler • Dithoeven we alleenmaartedoenvoor tiles die geenachtergrondtilezijn. • En alleenvoor tiles die in de buurt van de spelerzijn

  34. Botsingen verwerken private void HandleCollisions() { isOnTheGround = false; TileField tiles = GameWorld.Find("tiles") asTileField; intx_floor = (int)position.X / tiles.CellWidth; inty_floor = (int)position.Y / tiles.CellHeight; for (int y = y_floor - 2; y <= y_floor + 1; ++y) for (int x = x_floor - 1; x <= x_floor + 1; ++x) { TileTypetileType = tiles.GetTileType(x, y); if (tileType == TileType.Background) continue; // kijk of ereenbotsing is } .. }

  35. Break vs continue • break • Stopt de huidige iteratie en verlaat de loop • continue • Stopt ook de huidige iteratie, maar gaat door met de loop! • Wat is de uiteindelijke waarde van z? inti, z = 0; for (i = 0; i < 10; i++) { if (i == 3) break; z++; } inti, z = 0; for (i = 0; i < 10; i++) { if (i == 3) continue; z++; } inti, z = 0; for (i = 0; i < 10; i++) { z++; } 10 3 9

  36. Botsingen verwerken Rectangle tileBounds = new Rectangle(x * tiles.CellWidth, y * tiles.CellHeight, tiles.CellWidth, tiles.CellHeight); RectangleboundingBox = this.BoundingBox; boundingBox.Height += 1; TilecurrentTile = tiles.Get(x, y) asTile; if (((currentTile != null&& !currentTile.CollidesWith(this)) || currentTile == null) && !tileBounds.Intersects(boundingBox)) continue; • De tiles bewegennietindienereenbotsing is • Dusmoeten we de spelerpositiecorrigeren!

  37. Botsing tussen twee sprites Y intersectiediepte X intersectiediepte

  38. Botsing tussen twee sprites

  39. Botsing tussen twee sprites

  40. Botsing tussen twee sprites

  41. Diepte in de X-richting is kleiner. Botsingen verwerken Dezemethodemoeten we nogmaken. Vector2 depth = Collision.CalculateIntersectionDepth(boundingBox, tileBounds); if(Math.Abs(depth.X) < Math.Abs(depth.Y)) { if(tileType == TileType.Normal) position.X += depth.X; }

  42. Botsingen verwerken Diepte in de Y-richting is kleiner. else { if (previousYPosition <= tileBounds.Top && tileType != TileType.Background) { isOnTheGround = true; velocity.Y = 0; } if (tileType == TileType.Normal || isOnTheGround) position.Y += depth.Y; } position = new Vector2((float)Math.Floor(position.X), (float)Math.Floor(position.Y)); Indien op de grond of geenplatformtile… Staan we op de grond? float afrondingsfouten voorkomen

  43. Intersectiediepte • Stap 1: bereken de maximaleafstandtussen de twee middenpunten • Stap 2: bereken de werkelijkeafstandtussen de twee middenpunten • Stap 3: bereken het verschil, dit is de intersectiediepte • Het teken (+/-) geeft de volgorde van de 2 objectenaan - + =

  44. Intersectiediepteberekenen public static Vector2 CalculateIntersectionDepth(Rectangle rectA, Rectangle rectB) { Vector2 minDistance = new Vector2(rectA.Width + rectB.Width, rectA.Height + rectB.Height) / 2; Vector2 centerA = new Vector2(rectA.Center.X, rectA.Center.Y); Vector2 centerB = new Vector2(rectB.Center.X, rectB.Center.Y); Vector2 distance = centerA - centerB; Vector2 depth = Vector2.Zero; if(distance.X > 0) depth.X = minDistance.X - distance.X; elsedepth.X = -minDistance.X - distance.X; if(distance.Y > 0) depth.Y = minDistance.Y - distance.Y; elsedepth.Y = -minDistance.Y - distance.Y; returndepth; }

  45. Intersectiediepteberekenen public static Vector2 CalculateIntersectionDepth(Rectangle rectA, Rectangle rectB) { Vector2 minDistance = new Vector2(rectA.Width + rectB.Width, rectA.Height + rectB.Height) / 2; Vector2 centerA = new Vector2(rectA.Center.X, rectA.Center.Y); Vector2 centerB = new Vector2(rectB.Center.X, rectB.Center.Y); Vector2 distance = centerA - centerB; Vector2 depth = Vector2.Zero; depth.X = distance.X > 0 ? minDistance.X - distance.X; : -minDistance.X - distance.X; depth.Y = distance.Y > 0 ? minDistance.Y - distance.Y; : -minDistance.Y - distance.Y; return depth; } Verkorte versie if-opdracht

  46. Dan is het resultaat Uitgebreide versie Alternatiefresultaat “if (distance.X > 0)” “else” Verkorte versie if-opdracht Vector2 depth = Vector2.Zero; depth.X = distance.X > 0 ? minDistance.X - distance.X; : -minDistance.X - distance.X; … Vector2 depth = Vector2.Zero; if (distance.X > 0) depth.X = minDistance.X - distance.X; else depth.X = -minDistance.X - distance.X; if (distance.Y > 0) // etc…

  47. Per-pixel botsing afhandelen • De bounding boxes overlappen, maar de ‘echte’ objecten botsen niet met elkaar • Enige oplossing: botsing afhandelen door per pixel te kijken. • Hoeft alleen voor het overlappende stuk te gebeuren!

  48. Per pixel botsing afhandelen public staticRectangleIntersection(Rectangle rect1, Rectangle rect2) { intxmin = (int)MathHelper.Max(rect1.Left, rect2.Left); int xmax = (int)MathHelper.Min(rect1.Right, rect2.Right); intymin = (int)MathHelper.Max(rect1.Top, rect2.Top); intymax = (int)MathHelper.Min(rect1.Bottom, rect2.Bottom); return new Rectangle(xmin, ymin, xmax - xmin, ymax - ymin); }

  49. Per pixel botsing afhandelen public boolCollidesWith(SpriteGameObjectobj) { if (!this.Visible || !obj.Visible || !BoundingBox.Intersects(obj.BoundingBox)) return false; Rectangle b = Collision.Intersection(BoundingBox, obj.BoundingBox); for (int x = 0; x < b.Width; x++) for (int y = 0; y < b.Height; y++) { intthisx = b.X - (int)(GlobalPosition.X - origin.X) + x; intthisy = b.Y - (int)(GlobalPosition.Y - origin.Y) + y; intobjx = b.X - (int)(obj.GlobalPosition.X - obj.origin.X) + x; intobjy = b.Y - (int)(obj.GlobalPosition.Y - obj.origin.Y) + y; if (sprite.GetPixelColor(thisx, thisy).A != 0 && obj.sprite.GetPixelColor(objx, objy).A != 0) return true; } return false; }

  50. De volgende keer Be afraid… Be VERY afraid!! I’mthirsty… I hatemy life  • Vijanden!

More Related