Older Version
Newer Version
Alyce
Apr 21, 2009
==Sprite Byte Tutorials Lesson Seven: Collision Detection and Scoring== //by Alyce Watson// http://alycesrestaurant.com/ [[user:Alyce]] [[toc]] ==Sprites Controlled by User and Computer== In [[sprite4|Lesson Four]] we leaned how to move a sprite with code routines, and in [[sprite5|Lesson Five]] we learned how to move a sprite according to user input. We put those methods together in [[sprite6|Lesson Six]] to build the framework for an arcade game. We can make it look more interesting by causing the crab sprite image to cycle so that it appears to be walking. For a review of changing the sprite image, see [[sprite3|Lesson Three.]] These few additional code lines cause the crab to appear to be scuttling around: [[code format="vbnet"]] loadbmp "crab1", "sprites\crab1.bmp" loadbmp "crab2", "sprites\crab2.bmp" loadbmp "crab3", "sprites\crab3.bmp" #w.g "addsprite enemy crab1 crab2 crab3" #w.g "cyclesprite enemy 1" [[code]] We'll add boundary detection for the user-controlled sprite. We've already done this for the computer-controlled sprite in [[sprite6|Lesson Six.]] We check the sprite location stored in variables spriteX and spriteY and make sure those numbers do not go below zero, or get larger than the size of the screen. [[code format="vbnet"]] 'right arrow = X gets bigger: if asc(right$(Inkey$,1))=39 then 'stop sprite at right edge if spriteX<370 then spriteX = spriteX + 3 end if 'left arrow = X gets smaller: if asc(right$(Inkey$,1))=37 then 'stop sprite at left edge if spriteX>3 then spriteX = spriteX - 3 end if 'up arrow = Y gets smaller: if asc(right$(Inkey$,1))=38 then 'stop sprite at top edge if spriteY>3 then spriteY = spriteY - 3 end if 'down arrow = Y gets bigger: if asc(right$(Inkey$,1))=40 then 'stop sprite at bottom edge if spriteY<270 then spriteY = spriteY + 3 end if #w.g "spritexy guy ";spriteX;" ";spriteY [[code]] ==What Are Collisions?== Collisions happen when two or more sprites come in contact with one another. We can check this ourselves, and perhaps we'll use that method in a future lesson. It is easier to ask Liberty BASIC to check for collisions, and we do this with the **SPRITECOLLIDES** statement. ==Spritecollides Statement== The **SPRITECOLLIDES** statement instructs Liberty BASIC to ascertain if any other sprites have collided with a given sprite, and to return a list of their names in a string variable. The following line of code asks Liberty BASIC to see if any sprites have collided with a sprite named **MySprite** and to return the names in a variable called **collideList$**. [[code format="vbnet"]] #handle "spritecollides MySprite collideList$" [[code]] ==Using INSTR() with Spritecollides== There may be many sprites active on the screen, so we need to know which sprites have collided with our named sprite to decide how to proceed. One good method uses the **INSTR()** function. The method checks to see if INSTR() returns a value for a specified sprite, and then to accomplish some action. [[code format="vbnet"]] if instr(collideList$, "OtherSpriteName") then 'do action, such as update score, remove a sprite, change image, etc. end if [[code]] In our demonstration program, which is included at the bottom of this lesson, we'll check to see if the user-controlled sprite has collided with the crab, and if it has done so, to update the score. We keep track of the number of collisions in a variable called **hits**. We display the number of collisions on a statictext control at the bottom of the window. First, we add a variable to keep track of the number of collisions: [[code format="vbnet"]] 'add variable to keep track of number of collisions hits = 0 [[code]] Each time we update the display, we'll see if any sprites collided with our **guy** sprite. [[code format="vbnet"]] #w.g "spritecollides guy list$" [[code]] A list of sprites that collided with the **guy** sprite is now contained in the string variable called **list$**. We use **instr()** to see if the **crab** sprite is in that variable. If it is there, we increment (increase the value by one) the **hits** variable and update the statictext scoreboard. [[code format="vbnet"]] if instr(list$, "enemy") then 'if collision, then increment number of hits hits = hits + 1 'document on statictext scoreboard #w.s "Crabs grabbed: ";hits end if [[code]] We don't want to credit the user with dozens of hits, and that will happen if we do not remove the crab, or move it to another location after the collision. We'll use the **RND()** function to specify a new, random X, Y location for the crab sprite, and it will appear there when the display is updated with the **drawsprites** command. [[code format="vbnet"]] if instr(list$, "enemy") then 'if collision, then increment number of hits hits = hits + 1 'document on statictext scoreboard #w.s "Crabs grabbed: ";hits 'move crab randomly to start chase again crabX=int(rnd(1)*350): crabY=int(rnd(1)*250) end if #w.g "drawsprites" [[code]] ==Review== We've taken the rudimentary game from [[sprite6|Lesson Six]] and added features. We caused the crab sprite image to cycle so that it appears to be walking. We did a boundary check to keep the user sprite on the screen. We added collision detection using the **spritecollides** statement. We examined the list of sprites that collided by using **instr()**. If we found a collision, we updated the scoreboard and moved the crab sprite to a new, random location. ==Demonstration Programs== [[image:sprite7.png caption="Demonstration Program"]] //The following demonstration programs require bitmaps in your Liberty BASIC sprites folder and they must be run from the Liberty BASIC root directory.// **Demo of Collision Detection Between Computer-Controlled Sprite and User-Controlled Sprite** [[code format="vbnet"]] 'Sprite Byte 7 Demo 'Change location of user-controlled sprite when arrows are pressed 'but do not update display, since that is done on timer. 'Accomplish this by removing drawsprites command from '[updatePosition] routine. 'Add boundary detection for user-controlled sprite. 'Cycle crab sprite. (see lesson 3) 'Add collision detection with spritecollides command. 'Keep track of number of collisions with variable called hits. 'Move enemy after collision 'Update scoreboard after collision nomainwin loadbmp "smiley", "sprites\smiley.bmp" loadbmp "crab1", "sprites\crab1.bmp" loadbmp "crab2", "sprites\crab2.bmp" loadbmp "crab3", "sprites\crab3.bmp" loadbmp "landscape", "sprites\bg1.bmp" WindowHeight = 350 : WindowWidth = 408 graphicbox #w.g, 0, 0, 400, 300 statictext #w.s, "",40,300,100,40 open "Grab That Crab!" for window_nf as #w #w "trapclose [quit]" #w.g "down" #w.g "background landscape" #w.g "addsprite guy smiley" spriteX = 10 : spriteY = 30 #w.g "spritexy guy ";spriteX;" ";spriteY 'set up event trapping for key presses #w.g "setfocus" 'MUST setfocus to graphicbox #w.g "when characterInput [updatePosition]" 'add computer-controlled crab sprite #w.g "addsprite enemy crab1 crab2 crab3" 'original location of crab crabX = 360 : crabY = 100 'add variable for increment to move crab each time moveCrabX = -5 : moveCrabY = 2 'add variable to keep track of number of collisions hits = 0 #w.s "Crabs grabbed: ";hits 'cause crab image to cycle: #w.g "cyclesprite enemy 1" #w.g "spritexy enemy ";crabX;" ";crabY #w.g "drawsprites" 'update screen 'set up a timer to move crab sprite timer 100, [updateDisplay] wait [updatePosition] 'change location of user-controlled sprite when arrows are pressed 'but do not update display, since that is done on timer '37 = left arrow '38 = up arrow '39 = right arrow '40 = down arrow 'Arrow keys make Inkey$ two characters long, 'so we check the rightmost character with 'the right$() function. 'right arrow = X gets bigger: if asc(right$(Inkey$,1))=39 then 'stop sprite at right edge if spriteX<370 then spriteX = spriteX + 3 end if 'left arrow = X gets smaller: if asc(right$(Inkey$,1))=37 then 'stop sprite at left edge if spriteX>3 then spriteX = spriteX - 3 end if 'up arrow = Y gets smaller: if asc(right$(Inkey$,1))=38 then 'stop sprite at top edge if spriteY>3 then spriteY = spriteY - 3 end if 'down arrow = Y gets bigger: if asc(right$(Inkey$,1))=40 then 'stop sprite at bottom edge if spriteY<270 then spriteY = spriteY + 3 end if #w.g "spritexy guy ";spriteX;" ";spriteY wait [updateDisplay] 'locate sprites at new positions and update display 'add boundary detection and reverse direction at edges if (crabX > 370) then moveCrabX = -5 if (crabX < 10) then moveCrabX = 5 if (crabY > 270) then moveCrabY = -2 if (crabY < 10) then moveCrabY = 2 'move computer-controlled sprite at timer intervals crabX = crabX + moveCrabX : crabY = crabY + moveCrabY #w.g "spritexy enemy ";crabX;" ";crabY 'add collision detection #w.g "spritecollides guy list$" if instr(list$, "enemy") then 'if collision, then increment number of hits hits = hits + 1 'document on statictext scoreboard #w.s "Crabs grabbed: ";hits 'move crab randomly to start chase again crabX=int(rnd(1)*350): crabY=int(rnd(1)*250) end if #w.g "drawsprites" wait [quit] timer 0 unloadbmp "landscape" unloadbmp "smiley" unloadbmp "crab1":unloadbmp "crab2":unloadbmp "crab3" close #w : end [[code]] ==Challenges== * challenge: use word$() or a different method to detect which sprites have collided. * challenge: add more crabs for the user to grab. * challenge: set a time limit for the game. * challenge: use different images to change the look of this program. ---- [[toc]]