Collision Q

Blitz3D Forums/Blitz3D Beginners Area/Collision Q

Happy Llama(Posted 2012) [#1]
I'll right, i'm back with ANOTHER question. I have a type snowball in which has the variable: id. I tried to set up collisions but how can I make it so that when id=1 it is set to playersnowball and when id=2 the collisions are set to enemy snowball.




Drak(Posted 2012) [#2]
You'll need to specify that in your code when the snowballs are created. You can use the s\id field to specify whether a snowball is friendly or harmful. I'm suggesting 1 = friendly and 2 = harmful. So:

;change this:
s\id = 1 
entitytype s\mesh, type_playersnowball
;to this:
s\id = 2
entitytype s\mesh, type_enemysnowball


Then later in your code you will check ALL snowballs at once to see if they had a collision. THEN for the ones that DO have a collision you can easily check if the collision was friendly, or harmful.

;pseudocode!
if collided_entity = type_enemy and s\id = 1
     damage_enemy
else if collided_entity = type_player and s\id = 2
     damage_player
end if 


I can give you some more specific help on the collision detection if needed, but give it a shot!


Rob the Great(Posted 2012) [#3]
Just a tip, not really related to your question, but:
;Making it snow!

Dim snowflake(1000)

For p=0 To 1000

snowflake(p)=CreateSphere()

ScaleEntity snowflake(p),0.05,0.05,0.05

PositionEntity snowflake(p),Rnd(-50,50),Rnd(1,100),Rnd(-50,50)

Next

This may not be the best way to approach this. You've got the right idea using arrays and For loops to create snowflakes, but 1000 spheres, each containing 224 polygons (default), results in having to render 112,000 backface culled triangles per loop, which could cause some slowdown. Depending on my setup, I can usually get around 20-100 thousand before I notice slowdown, but I have a relatively old system too.

Instead, I would consider using artificial sprites in place of the spheres. Artificial sprites use a quad mesh (2 triangles) with an image for a texture, and they have neat functions like always being able to face the camera so they appear to be 3D rather than 2D images. I say "artificial" because Blitz's native sprite commands aren't much better regarding speed, but there's some good replacements in the archives. I'm using JFK's Sprites Substitute, which basically uses the same function names as Blitz's, so it's easy to implement.

Instead of creating 1000 spheres, you should draw a picture of a white snowflake on a black or alpha channel background, and then load the image as a sprite with the alpha flag. Then just copy the sprite 1000 times in place of your sphere, and by doing so, you will reduce the amount of triangles from 112,000 to 2,000, and that should help speed up your game and reduce the amount of stress on the CPU. Plus, you can have a complex snowflake shape without it having any effect on your processing time.

I've never thought about making a game about a snowball fight, but that sounds like it would be a lot of fun to play. Good luck!


Happy Llama(Posted 2012) [#4]
What command would I use to check if the snowball had collided with the enemy?


Rob the Great(Posted 2012) [#5]
If EntityCollided(enemymesh,type_playersnowball)
   ;Take enemy's life away
EndIf
If EntityCollided(playermesh,type_enemysnowball)
   ;Take player's life away
EndIf

This is essentially the same thing as Drak's pseudo above with the lines:
;pseudocode!
if collided_entity = type_enemy and s\id = 1
     damage_enemy
else if collided_entity = type_player and s\id = 2
     damage_player
end if 



Happy Llama(Posted 2012) [#6]
Tell me why this isn't working!




Rob the Great(Posted 2012) [#7]
I glanced over your code, and the logic seems like it should work. The only thing missing is letting Blitz know what should collide with what and what happens when they do.

To do this, you need to add in a "Collisions" command for each type of collision you want.

I typically do this right before the main loop so that I don't mistakenly add new collision types later and forget to add a corresponding "Collisions" command.

Also, if you're planning on using an imaginary bubble around your entity for collision detection (such as the player camera), you need to also use EntityRadius().

For your purposes, this should help:
;Change this spot of code for your player loading
;Player
player=CreateCamera()
CameraFogRange player,0.1,400
CameraFogMode player,True
CameraClsColor player,100,130,150
CameraFogColor player,100,130,150
;HERE'S A NEW COMMAND, ENTITYRADIUS
EntityRadius player,1,2 ;Play around with the values until it looks right
EntityType player,type_player

;Then, right before you call the Main, add these lines
Collisions type_player,type_enemysnowball,1,1
Collisions type_enemy,type_playersnowball,2,1
;###############
;#  Main Loop  #
;###############
;....

Read up on the Collisions command in the manual if this is confusing.

Basically, if you add in those lines, it should work (I think it will, I haven't tested it, I've just read over the logic).

EDIT: The way this is set up, you will need some way to let Blitz know that once a snowball collides, it should not be checked for damage again. Otherwise, one hit from a snowball will continually drain your health until the game ends. You can either delete the snowball, or better yet, add in an additional field for the snowball type that keeps track if that particular snowball has already taken life away. If you use the latter, then you have more options such as making the snowball splat on the screen and then gradually melt away.

If you need help, feel free to ask.

Last edited 2012


Guy Fawkes(Posted 2012) [#8]
First create a sorta "Collision type" or.. a variable which defines which objects u will be colliding.


IE:






Then, afterwords, define which type of collision it is. Is it a 1: sphere-to-sphere collision, 2: sphere-to-polygon collision, or 3: sphere-to-box collision ?






Next, you need to define whether it's a 1: stop collision, 2: a full sliding collision, or 3: a non-sliding slope collision.


IE:





Now, what you want to do, is apply an "EntityType" to each object.


Here's an example:







Hope this helps! :)


Drak(Posted 2012) [#9]
Rez you didn't add anything useful to his code. You're still missing the actual Collision command that sets up the actual collisions, as Rob said.


Guy Fawkes(Posted 2012) [#10]
My name is Thundros now -.-



Here's a fix:






Happy Llama(Posted 2012) [#11]
The snowball hits the enemy but slides over it and does not decrease the variable enemyhealth. Why! Why!!

P.S Thanks for all your help!


Rob the Great(Posted 2012) [#12]
The first thing to assume is that the snowball is not colliding. In the If statement where you decrease the player's health, either add in a DebugLog command, or if your not familiar how that works, just add in an "End" command. For example:
If EntityCollided(enemy,type_playersnowball)
   End
   enemyhealth = enemyhealth - 1
EndIf

If the snowball is successfully colliding, the program should end on its own as soon as the player snowball touches the enemy. If the program never ends, then you know the snowball is not colliding with the enemy.

Start with that, and let me know what the results are. It might be a while before I respond, but go with that for now.


Happy Llama(Posted 2012) [#13]
ok cool ill try it.


Happy Llama(Posted 2012) [#14]
No the program does not end.


Rob the Great(Posted 2012) [#15]
Then what we've gathered from this is that something is wrong with the collision system. Things to check are:

1. Does every entity involved in collisions have an EntityType() assigned to it? I know you're using types for your snowballs, which is perfect, but make sure whenever you create a new snowball, you give it a collision type. e.g.
s.snowball = New snowball
s\entity = CreateSphere()
EntityType(s\snowball,type_playersnowball)


2. Have you called a Collisions() command at least once in your program? You can assign entity's their collision types until you're blue in the face, but it won't mean anything unless you tell Blitz that you want two types to collide with each other. e.g.
Collisions type_player,type_level,2,3
Collisions type_snowball,type_enemy,2,1
Collisions type_this,type_that,1,1
;ect.

As a sub note, make sure you call them in the correct order. For your program, your snowballs are round, and therefore, you want to use EntityRadius() on them. Then, when you check for collisions with them, make sure their entity type is first in the list, such as "Collisions type_playersnowball,type_enemy,2,1". This is because you want Blitz to use their radius against either the radius, exact shape, or bounding box of the enemy, and if it was reversed, it may not collide successfully.

3. Are you calling UpdateWorld() before RenderWorld()? If you neglect to include an UpdateWorld() command, no collisions with ever be checked.

4. Are you using EntityRadius() at all, and if so, is your radius too large or too small? If you aren't using this command, it's OK because Blitz will assume an object has an EntityRadius of 1 unit surrounding it. I imagine that your snowballs are fine, but they might pass through other objects if the radius is too small. For complex geometry, it would be even better to use mode 2 in the Collisions() command (radius against polygons), and this ensures that the snowball will hit the enemy. But, if you're using collision mode 1 (radius against a radius), the enemy radius might be small enough where it will pass right through the enemy except for the 1x1 sphere right in the center of mass of your enemy.

5. Do other collision commands work? Instead of this:
If EntityCollided(enemy,type_snowball)
     End
EndIf

Try this:
If MeshesIntersect(enemy,s\snowball)
      End
EndIf

The second option will check if ANY part of the snowball is within any part of the enemy. Take caution, because MeshesIntersect() is a very slow command, but it is useful to know for sure whether or not you are receiving any kind of collision at all.

6. If all the above fails, try this:
Global type_playersnowball = 1
Global type_enemy = 2
enemy = LoadMesh("enemy.b3d")
EntityType enemy,type_enemy
s.snowball = New snowball
s\entity = CreateSphere()
EntityType(s\snowball,type_playersnowball)
Collisions type_playersnowball,type_enemy,2,3
While Not KeyDown(1)

   PointEntity s\entity,enemy
   MoveEntity s\entity,0,0,0.1
   If EntityCollided(enemy,type_playersnowball)
      End
   EndIf
   UpdateWorld
   RenderWorld
   Flip

Wend

End

This will ensure that the snowball will touch the enemy if the program runs long enough. Eventually, it will end, but if it doesn't, then it's time to post some code and we'll see if we can't figure out what's going wrong.

Hopefully this all helps.


Drak(Posted 2012) [#16]
I believe you have to discern which snowball is colliding with the enemy to fix this.

The EntityCollided() command only tells you that ONE of the snowballs collided with an enemy, and not which one. You'll have to have your coding figure out which snowball hit the enemy, then act accordingly. If you could edit your code so it contains no loaded media and repost it, I could help out better. You can always load your media after you get everything working with primitives.


Ross C(Posted 2012) [#17]
Can't you CountCollisions() and return the collision entity involved?


Drak(Posted 2012) [#18]
Yes that's exactly how it's done. With that command you can pick out which entity (of the Type snowball) had the collision. Let me switch computers and I'll post an example for LLama.


Drak(Posted 2012) [#19]
Ok try adding this:

For this to work you need 2 global variables initiated at the beggining of the program:
Global collision_id
Global collision_entitiy

And add this function into your program instead of the other entitycolided() commands:

Function snowball_collisions()	;This will work for various collisions that can be added later, example if you want a different collision for the snowfort, etc
	For s.snowball = Each snowball						;For EACH snowball
		For collision_id = 1 To CountCollisions(s\mesh)	;Assign a temporary collision id# to each collision this particular snowball has
			collision_entity = CollisionEntity(s\mesh, collision_id)	;Now we store WHICH entity the snowball collided with
				If GetEntityType(collision_entity) = type_enemy			;If the collided entity is a TYPE_ENEMY
					enemyhealth = enemyhealth - s\damage				;reduce the enemie's health by the snowball damage factor
						FreeEntity s\mesh	;delete the snowball mesh
						Delete s			;delete the reference to it
							If enemyhealth <= 0 Then FreeEntity enemy	;delete the enemy if it's dead
						Exit
				End If
		Next
	Next
End Function 



Drak(Posted 2012) [#20]
I added a few things and altered a few things. I replaced all the media with primitive objects to get things working. There were a number of small errors, some as small as a spelling error, but you should be able to build off of this. The collision does work now. Let us know if there's anything else we can help with!




Happy Llama(Posted 2012) [#21]
Edit: Figured it out on my own! Ha Ha!

Last edited 2012