Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

When two pics hit

Status
Not open for further replies.

Stainnd

Programmer
Jul 9, 2001
65
US
This is something I've wondered for a long time. I used to program in VB a lot (i lost the disc and got a new comp, so that is kinda out the window), and now i've been programming in qb. When I was in vb, I made this app that was a collection of games I had made. I was quite proud of it, but then again, i'm quite proud of most coding i do so... Anyway, what I wanted to do was making a game called Chubby Rain (from Bowfinger, the movie). You are a little guy that is controlled with left and right. Rain falls down on the screen, and if it hits u, you take some damage. Too much damage and you die. What I could never get right was how to tell when the rain hits you. I would use code that should have worked, but it never did. I figured that maybe some of you could help me solve this problem in qb. The coding would be totally different, but the question is still the same. Thanks -Mike
 
hmm, I don't know the most efficient way to do this but here's one that I think might be pretty good:

put Chubby in a black box that's a litte taller than him so theres some room above his head but only one pixel wider on either side. then scan down with a for loop starting at the upper left corner all down to the bottom.

when you hit the first non zero point, translate this coordinate to the bottom left corner of the box:

X , Y - BottomLeftY

Then exit that for and continue to the next X column.

Proceeding in this way you will end up with a bunch of points that look like this: 2, - 4, 3, -5, 4, -6, etc..

Just picture that like a little force shield around chubby.

Since it's translated to the bottom left corner, what you can do is take this point relative to where chubby is standing in your game, then loop through these points adding those coordinates to that point, and use a POINT statement to check if there is rain at those points, and if rain touches the force shield it hit him.

I think that's efficient because it keeps IF statements and memory writes pretty low, but I don't know if there are any "gotchyas" without trying it. Other techniques would be to keep an offscreen array and do your checking in that but that seems like a lot of memory writes and IFs at first thought....

Hope that helps... have fun





 
actually I meant to say subtract 1 from the Y coordinate when you hit the first non zero point.

but there is a "gotchya", that will only work if you increment your raindrops by 1 downward each loop, which I'm guessing you're not doing if the game is in an old style.

you can just find the width of Chubby and then do an IF on your raindrops to see if they are in a vertical column above him, and then check to see if they occupy the space that he takes up in an offscreen array.

say if Chubby is 20 pixels wide, and his leftmost coordinate is currently 150 on the screen, then you only check raindrops that are within 150 to 170.

to get precise collisions you can do something what I was saying before, but a little different,

put Chubby in a black box again. this time scan horizontally and downward. Have a Type for his borders:

Type Borders
X1 as Integer
X2 as Integer
End Type

Dim Bounds(ScreenHeight - ChubbyHeight to ScreenHeight) as Borders


'loop downward until you hit the first nonzero point

For Y = 0 to ScreenHeight
For X = 0 to ScreenWidth

If POINT(X,Y) <> 0 And NOT FirstFound Then

FirstFound = True
BorderX1 = X
Endif

If POINT(X,Y) = 0 And FirstFound Then

BorderX2 = X-1
Exit For
Endif

Next

FirstFound = False
Bounds(Y).X1 = BorderX1
Bounds(Y).X2 = BorderX2

Next

For each row, you are looping across until you find a nonzero point, set a flag to indicate you have found the first point and save this is the first border point. Then when you come to another nonzero point, you are on the other side of Chubby(as long as chubby doesn't have any black on him, if so use a different backcolor) since you're on the other side you back up 1 to find his rightmost border for that column.

Now what you have is a list of border points for each row which you can use like this:

You've found a raindrop that lies within Chubby's column, and you know the Y coordinate of that drop(or the center or leftmost point, probably good enough)

Now all you have to do is go,

IF RainDropX => Bounds(RainDropY).X1 and RainDropX <= Bounds(RainDropY).X2 Then ' process a hit



I don't think there's any gotchyas there but it will depend of course on how you're doing things. Hope that helps(again..) :)






















 
What would probably be most efficient would be to maintain an array of raindrops, where each entry contained the location and speed of the raindrop. Then, every time you go through the loop, move each raindrop the appropriate distance relative to the corresponding speed in the array. Now, when you move a raindrop down, it is possible that it may go off of the bottom of the screen. You need to test for this, and this is also where you should test for hitting Chubby. Here is a simple example:
[tt]
TYPE rainDropType
inUse AS INTEGER
x AS INTEGER
y AS INTEGER
speed AS INTEGER
END TYPE

DIM
rainDrop(50) AS rainDropType

health% = 100
playerPos% = 40

WIDTH 80, 50
SCREEN 0, , 1, 0
DO
st# = TIMER
CLS
COLOR
1
added% = 0
FOR i% = 1 TO 50
IF rainDrop(i%).inUse THEN
rainDrop(i%).y = rainDrop(i%).y + rainDrop(i%).speed
ELSEIF added% = 0 THEN
added% = 1
rainDrop(i%).inUse = -1
rainDrop(i%).x = INT(RND * 80) + 1
rainDrop(i%).speed = INT(RND * 3) + 1
rainDrop(i%).y = -INT(RND * rainDrop(i%).speed)
END IF
IF
(rainDrop(i%).y >= 50) AND (rainDrop(i%).x = playerPos%) THEN
health% = health% - (2 + INT(RND * (2 * rainDrop(i%).speed + 1)))
rainDrop(i%).inUse = 0
ELSEIF (rainDrop(i%).y >= 51) THEN
rainDrop(i%).inUse = 0
ELSEIF (rainDrop(i%).y > 0) THEN
LOCATE
rainDrop(i%).y, rainDrop(i%).x
PRINT &quot;*&quot;;
END IF
NEXT
i%
COLOR 7
LOCATE 50, playerPos%
PRINT &quot;@&quot;;
LOCATE 50, 75
PRINT USING &quot;###%&quot;; health%;
SELECT CASE INKEY$
CASE CHR$
(0) + &quot;K&quot;: 'left
playerPos% = playerPos% - 1
CASE CHR$(0) + &quot;M&quot;: 'right
playerPos% = playerPos% + 1
CASE CHR$(27): 'escape
EXIT DO
END SELECT
PCOPY
1, 0
WHILE st# = TIMER: WEND 'don't run too fast
LOOP WHILE
health%
[/tt]
 
looks like a good idea to me. I forgot about needing to check if the raindrops went off the screen.

I wonder if a simple algorithim could be used to assign a life to each raindrop which would allow it to live only to the bottom of the screen or slightly below (if you can PSET off the screen, I can't remember) in order to eliminate checking this, and then use another array which was the screen width wide(representing the X coordinates of the raindrops) and the elements of which stored the Y coordinates of the raindrops in that column. Then you could access raindrops that are above Chubby simply by accessing the X coordinates above him based on his width, then just run through these checking IF Bounds(RainDropY).X1 > RainDropX and Bounds(RainDropY).X2 < RainDropX Then process a hit.


I can't see why that wouldn't be very efficient, but it is assuming there is an efficient method to assign a life to each drop.



hehe... a little silly to discuss speeding up a program which must be slowed down, isn't it? &quot;let's speed it up so we can slow it down some more!&quot;









 
There is nothing inherently faster about decrementing a &quot;life&quot; value each time and checking it against zero, and simply checking the Y value against the bottom of the screen. Either way, you're comparing on every loop iteration.
 
guess I wasn't very clear. I meant discarding the check all together.

:) I know this is ridicously complex for the given problem, but I like to learn new ways of constructing dynamic routines without the use of IFs...

Say you have a constant number of raindrops in the game and you measure time in iterations of your loop. When you create a raindrop, you could calculate the exact time(iteration) that it will hit the bottom with the velocity and size value(and adjust the start position upward if necessary so they will hit exactly on the bottom of the screen)

You have to make sure that each raindrop you create will hit at a time later than the previous, and keep an array like this:

Type RainDrop
TimeofArrival As Integer
ID as Integer
End Type

You start out first by filling the array with raindrops(there velocity and current position like you normally do)

and in a parrellel array you store their time of arrival with values that look like this:

123
125
126
131
134
140

Which is the exact iteration that the raindrop will hit the bottom of the screen.

Then you can just tell your loop to loop X times(X = CurrentTime - 123), and when it does, since you're keeping an index of the current drop your on, you could simply automatically know which raindrop hit by the associated ID, and replace it with a new raindrop(again one that will hit after the next one down on the list - one whose velocity is not great enough to overcome the next one down), then advance your index up one and loop CurrentTime - 125, etc.

Then all you need to do is reset the index when it gets to the bottom of the list, and also reset the clock to prevent an overflow. (This could be done without an IF too)

I realize now that checking the space underneath Chubby is the best way, because my suggestion could let drops pass through his arms depending on the velocity of the drop.


Like I said, I'm always looking for ways to cut out IFs and am welcome to suggestions.




























 
You still misunderstand :) You still need to check every raindrop's TimedArrival value, and how is that different from checking every raindrop's Y value?
 
No matter how you decide to handle the raindrops, you will be running through a loop, right? And at some point you will be creating a rain drop with a size and velocity, color, or whatever. It is simple to calculate how many times the raindrop will need to be advanced downward before it hits the bottom, and it may need to go below that, but you can raise the start point upward by whatever to make it hit exactly at the bottom. I'm saying you could have an outer loop to your main loop that tells your main loop to loop so many times, the number of times before a rain drop hits. So you only calculate the TimedArrival value once, when the raindrop is created, and when it hits you don't need to check anything because you know which one it is by the TimedArrival index. Then you can replace this drop with a new one(at the same position in the raindrop array) and go down the next TimedArrival index.

I'm not sure if rain created this way would have aesthetic limitations, and like I said before it's ridicously complex for this game, I just like learning ways to avoid IFs... Hope that more intelligble..:)
 
I see what you're saying: maintain a queue of raindrops in the order that they are going to hit the bottom of the screen. I was considering this, but it seemed overkill at the time :)
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top