180 likes | 300 Views
Lecture 5 Multiple Collisions Creating an Object Class Generic Lists Using Delegates. fireFlies. For this lecture we will review the major components of the demo XNA 4.0 program called fireFlies. Collision Detection Concept.
E N D
Lecture 5 Multiple Collisions Creating an Object Class Generic Lists Using Delegates
fireFlies For this lecture we will review the major components of the demo XNA 4.0 program called fireFlies.
Collision Detection Concept When two fireflies collide we tag them for removal and add an explosion to the blasts list. The position and velocity of the explosion is set to the average of the two colliding flies. graphical display fireflies list blasts list Blast objects are removed from the blasts list when their animation sequence is completed.
When there is only one interactive player the collision detection is reduced to a single loop. graphical display fireflies list blasts list player
Managing Generic Lists A generic list can hold any type of object, so they are especially well suited for holding structured data types. In Object Oriented Programming we can define a structured data type as an instance class. The class has the additional advantage of holding methods and properties related to the class. In this application we will create a class for the firefly and a class for the blast (explosion). Each firefly will have its own position and velocity. To add more natural appearance to their flight we can randomize the rate of the wing flapping animation for each fly and add a random element to their flight. Warning! MS Visual Studio .NET has a quirky random number generator. When a new Random object is created it's initial seed is determined by the clock time, which means that if multiple instances of a Random object are created in a short time (within a few milliseconds of each other) they will have the same initial seed and will therefore may generate the same pseudo-random sequence.
firefly Class publicclassfirefly { publicVector2Pos; publicVector2Vel; publicintMaxX; publicintMaxY; publicRectangleRect = newRectangle(); publicintAnimCount; publicintAnimTimer; publicint Time; publicintTotCount; publicbool Hit; Random rand = newRandom(); public firefly(intmaxX, intmaxY, inttotcount, intsheetwidth, intsheetheight, intanimtimer, int seed) { Randomrnd = newRandom(seed); MaxX = maxX; MaxY = maxY; Pos.X = rnd.Next(maxX); Pos.Y = rnd.Next(maxY); Vel.X = rnd.Next(10) - 5; Vel.Y = rnd.Next(10) - 5; Rect.X = 0; Rect.Y = 0; Rect.Width = sheetwidth / totcount; Rect.Height = sheetheight; AnimTimer = animtimer; AnimCount = 0; TotCount = totcount; Time = 0; Hit = false; } OK, just so you know, this is a stripped-down version of an instance class which would distress any OOP purist. In this course we use OOP to program, we DO NOT program to use OOP. Which means we will readily ignore any OOP protocol and we will break an OOP rule that interferes with our primary goal to build an efficiently running and fun game project. Hopping off the soapbox, the constructor for firefly creates a random object which needs its a unique seed. We will use a random number generator in the main program to provide this seed.
Move( ) Method for firefly Class publicvoid Move(intdeltime) { Time += deltime; Pos.X += Vel.X; if (Pos.X > MaxX) Pos.X = 0; if (Pos.X < 0) Pos.X = MaxX; Pos.Y += Vel.Y; if (Pos.Y > MaxY) Pos.Y = 0; if (Pos.Y < 0) Pos.Y = MaxY; if (rand.Next(1000) > 990) Vel.X += rand.Next(3) - 1; if (rand.Next(1000) > 990) Vel.Y += rand.Next(3) - 1; if (Vel.X > 5) Vel.X = 5; if (Vel.X <-5) Vel.X = -5; if (Vel.Y > 5) Vel.Y = 5; if (Vel.Y <-5) Vel.Y = -5; if (Time >= AnimTimer) { AnimCount = (AnimCount + 1) % TotCount; Time = 0; } Rect.X = AnimCount * Rect.Width; } } The Move( ) method updates the position of the firefly and increments the AnimCount (frame # being displayed from the firefly spritesheet. Position (Pos) is updated using the velocity (Vel) and velocity is randomly updated with a 10/1000 probability. Magnitude of velocity is limited to 5 units. AnimCount is updated each time accumulated Time exceeds the randomly set number of milliseconds per frame, AnimTimer. The location of Rect is the region of the spritesheet being displayed for the current frame of the animation. flies.png
blast Class publicVector2Pos; publicVector2Vel; publicRectangleRect = newRectangle(); publicintAnimCount; publicbool Done; publicintAnimTimer; intTotCount; publicint Width; publicint Height; publicint Time; intrownum; intcolnum; public blast(Vector2pos, Vector2 vel1, Vector2 vel2, inttotcount, int width, int height, intanimtimer) { Pos = pos; Vel.X = (vel1.X + vel2.X) / 2; Vel.Y = (vel1.Y + vel2.Y) / 2; AnimCount = 0; Rect.X = 0; Rect.Y = 0; Rect.Width = width; Rect.Height = height; Width = width; Height = height; TotCount = totcount; AnimTimer = animtimer; rownum = 0; colnum = 0; Done = false; } When two fireflies collide they are tagged for removal and an explosion (an instance the the blast class) is created. The blast will exist for 16 display frames as the 16 frames of the explosion sequence is used to animate the blast. At the end of the animation, the completed instance of blast is tagged for removal. An interesting feature of a blast is that its velocity is set to the average of the velocities of the two colliding fireflies. This gives a more realistic appearance than a static explosion sequence. In this demo we set the rate of animation to 60 milliseconds per frame (AnimTimer = 60).
Move( ) Method for blast Class The individual sprites blits are 64x64 pixels. The rownum and colnum are in the range 0 through and specify the sprite frame 0 through 15. When all frames have been displayed the Boolean field Done is set to TRUE. 256 x 256 pixels publicvoid Move(intdeltime) { Time += deltime; Pos.X += Vel.X; Pos.Y += Vel.Y; if (Time >= AnimTimer) { AnimCount += 1; Time = 0; } colnum = AnimCount % 4; rownum = AnimCount / 4; Rect.X = colnum * Width; Rect.Y = rownum * Height; if (AnimCount >= TotCount) Done = true; } explosion.bmp
Main Program Atomic Fireflies Collision Detection Demo GraphicsDeviceManagergraphics; List<firefly> flies = newList<firefly>(); List<blast> blasts = newList<blast>(); SpriteBatchspriteBatch; SoundEffect[] explosion = newSoundEffect[4]; Vector2spos; Vector2bpos; Texture2Dbkg; Texture2Dflysprite; Texture2Dexpsprite; Rectanglerect = newRectangle(); KeyboardStatekbstate; SpriteFont font; Vector2txtpos = newVector2(10, 10); Randomrnd = newRandom(); intminDist = 10; protectedoverridevoid Initialize() { base.Initialize(); spos.X = 26; spos.Y = 25; bpos.X = 32; bpos.Y = 32; graphics.IsFullScreen = true; graphics.ApplyChanges(); } Generic lists are created for firefly and blast objects. Four different explosion sounds will be called randomly for each collision. The evening sky background, the firefly sprite sheet and the explosion sprite sheet are loaded as Texture2D data types. An instance of the Random number generator rnd is created to be used to generated unique seeds for the generation of Random generators in the firefly class. This application runs in fullscreen mode comment out the call to graphics.ApplyChanges( ) to convert this application to Windows UI mode.
Atomic Fireflies LoadContent( ) Method protectedoverridevoidLoadContent() { spriteBatch = newSpriteBatch(GraphicsDevice); explosion[0] = Content.Load<SoundEffect>("explosion-01"); explosion[1] = Content.Load<SoundEffect>("explosion-02"); explosion[2] = Content.Load<SoundEffect>("explosion-03"); explosion[3] = Content.Load<SoundEffect>("explosion-04"); bkg = Content.Load<Texture2D>("bkg"); flysprite = Content.Load<Texture2D>("flies"); expsprite = Content.Load<Texture2D>("explosion"); graphics.PreferredBackBufferWidth = bkg.Width; graphics.PreferredBackBufferHeight = bkg.Height; font = Content.Load<SpriteFont>("SpriteFont1"); rect.X = 0; rect.Y = 0; rect.Width = bkg.Width; rect.Height = bkg.Height; graphics.ApplyChanges(); for (int i = 0; i < 10; i++) flies.Add(newfirefly(bkg.Width, bkg.Height, 4, 208, 50, rnd.Next(60)+20, rnd.Next(100000))); } To start the demo 10 fireflies are instantiated and added to the flies list. Note the use of rnd.Next(100000) to generate a different seed for each instance of firefly. Also the AnimTimer is set to a random range of values between 20 and 79 milliseconds.
Collision Detection Method This method demonstrates the versatility of the generic list. Although we do not have to managed the placement of items in a generic list when new items are added and/or old items are removed, we can control the order of access of individual elements in a generic list by index value. The nested for loops compare the locations of every pair of fireflies in the list. When two files are found at or near (within 10 pixels) the same location they are tagged for removal from the list, a new blast object is added to the blasts list and an explosion sound is played. When the number of items to be compared becomes very large we will need to partition the list of items into bins and compare only those items in the same or neighboring bins. For 2D games, a two-dimensional array will be used to partition the objects by their index values. publicvoid collision() { for(inti=0;i<flies.Count-1;i++) for(int j = i + 1; j < flies.Count; j++) { if((Math.Abs(flies[i].Pos.X - flies[j].Pos.X) + Math.Abs(flies[i].Pos.Y - flies[j].Pos.Y)) < minDist) { blasts.Add(newblast(flies[i].Pos,flies[i].Vel,flies[j].Vel,16,64,64,60)); flies[i].Hit = true; flies[j].Hit = true; explosion[rnd.Next(4)].Play(); } } }
Atomic Fireflies Update( ) Method protectedoverridevoid Update(GameTimegameTime) { if(GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); kbstate = Keyboard.GetState(); if (kbstate.IsKeyDown(Keys.Left) && kbstate.IsKeyDown(Keys.Right)) this.Exit(); if (kbstate.IsKeyDown(Keys.Right)) flies.Add(newfirefly(bkg.Width, bkg.Height, 4, 208, 50, rnd.Next(20) + 40, rnd.Next(100000))); if (kbstate.IsKeyDown(Keys.Left) && flies.Count > 0) flies[flies.Count-1].Hit = true; collision(); foreach (firefly fly in flies) fly.Move(gameTime.ElapsedGameTime.Milliseconds); foreach (blastpowin blasts) pow.Move(gameTime.ElapsedGameTime.Milliseconds); flies.RemoveAll(delegate(firefly fly) { returnfly.Hit; }); blasts.RemoveAll(delegate(blastpow) { returnpow.Done; }); base.Update(gameTime); } Fireflies can be removed or added to the flies list by pressing the Left-Arrow and Right-Arrow keys. The Move( ) methods for each firefly and each blast are called to update the position and animation frame for each object. At the end of Update( ) flies that have been involved in a collision and blasts that have completed their animation sequence are removed from their respective lists using delegate functions as shown.
Atomic Fireflies Draw( ) Method protectedoverridevoid Draw(GameTimegameTime) { GraphicsDevice.Clear(Color.White); spriteBatch.Begin(); spriteBatch.Draw(bkg, rect, Color.White); foreach (firefly fly in flies) spriteBatch.Draw(flysprite, fly.Pos, fly.Rect, Color.White, 0, spos, (float)0.5, 0, 0); foreach (blastpowin blasts) spriteBatch.Draw(expsprite, pow.Pos, pow.Rect, Color.White, 0, bpos,1, 0, 0); spriteBatch.DrawString(font, Convert.ToString(flies.Count), txtpos, Color.White); spriteBatch.End(); base.Draw(gameTime); } For each frame the Draw( ) method draws the background images followed by the fireflies and finally the active blasts being displayed. The fireflies.zip file contains the complete project
Summary of Lab 4 Features/Goals • Features of Lab 4.1/4.3 include: • One player with a single-player icon. • Other types of objects are stored in a generic array. • Only player object interacts with other objects. I.e. they do not interact with each other. • Colliding with some objects gain points, colliding with others lose points. (Alternatively, the gaining or losing of points can depend on the manner in which the colliding object is contacted. • Total score and number of remaining lives is maintained as part of the status. • Control is limited to keyboard entry. • Game should include collision-event animation, sound and game state changes. • Variations are encouraged with approval.