Director Tutorials

 

Collision Detection

Using modelsUnderRay

This tutorial builds on the last. So, you will need your completed Director file. If you want to download the completed one, it is available here (3Denv_collision1.dir).

1. Open the movie and open the scene control behavior.

2.  Add the following to the top of the script:
-- reference character's bounding box
property pCharBoundingBox 

Often when setting up collision detection between complex models, a simplified mesh is created around the model to define the collision area. This is what we're going to do with our character model.

3.  Add the following to the end of the beginSprite handler.
createCharacterBoundingBox

4. Add the following to the script.

on createCharacterBoundingBox

  -- create model resource
  charModRes = \
p3Dmember.newModelresource("charModRes",#box)
  charModRes.height = 25
  charModRes.width = 25
  charModRes.length = 50

  -- create model and reference to it
  pCharBoundingBox = \
p3Dmember.newModel("CharBoundingBox",charModRes)

  pCharBoundingBox.worldPosition = \
pCharacter.worldPosition
  pCharacter.addChild(pCharBoundingBox, \
#preserveWorld
)

  boxShader = \
p3Dmember.newShader("transparentShad",#standard)
  boxShader.transparent = TRUE

  -- set opacity of shader 0 is invisible
  boxShader.blend = 50
  pCharBoundingBox.shaderList = \
p3Dmember.shader("transparentShad")

end


In the above script we created a bounding box for the character with its position corresponding to the character model. The box was made a child of the character so it would always move with it. A shader was created with the transparent property on (TRUE). This allows the blend property to be set to less than 100%. Setting the blend to 0 will make the model invisible. For now, we set it to 50% so we can see the bounding box (as in the pic).

5.  In the beginSprite handler, delete the line createCollisionDetect and this custom message. Add the following to the end of the beginSprite handler.

  -- register the member for regular timeMS events in
  -- order to respond to user input and resolve camera
  -- collisions i.e. after specified time segments
  -- activate collisionDetect handler
 p3Dmember.registerForEvent(#timeMS,#collisionDetect,me,1000,10,0)


In the above statement me is the scriptObject parameter and indicates the collisionDetect handler is in the same script as the registerForEvent command. 1000 is the begin parameter and indicates that the first time the collisionDetect handler is to be activated will be 1 second (or 1000 milliseconds) after the registerForEvent command has occurred. 10 is the period parameter and indicates the subsequent time interval (in milliseconds) for the collisionDetect handler to be activated. 0 is the repetitions parameter and indicates the #timeMS event will occur indefinitely. Using 0 for repetitions makes the period parameter insignificant (it will be ignored).

6. Delete the createCollisionDetect handler and replace it with the following:

on collisionDetect me   

  -- create a list to store collision data created by
  -- modelsUnderRay and cast ray to left
  collisionList = \
  p3Dmember.modelsUnderRay(pCharBoundingBox.worldPosition, \
-pCharacter.transform.yAxis,#detailed)


In the above statement, we create a list (collisionList), to hold the information generated by the modelsUnderRay command. #detailed is used for the levelOfDetail parameter and will return a list of property lists, each representing an intersected model. #distance is one of the properties that will appear on the property list, which, in our case, represents the distance from the character to the point of intersection with the model.

  -- if ray picks up models, then activate
  -- checkForCollion handler and send the
  -- collisionList as a parameter
 
if (collisionList.count) then \
me
.checkForCollision(collisionList[1])

  -- cast ray to right
  collisionList = \
p3Dmember.modelsUnderRay(pCharBoundingBox.worldPosition, \
pCharacter.transform.yAxis,#detailed)

  -- if ray picks up models, then check for collision
 
if (collisionList.count) then \
me.checkForCollision(collisionList[1])

  -- cast ray forward
  collisionList = \
p3Dmember.modelsUnderRay(pCharBoundingBox.worldPosition, \
pCharacter.transform.xAxis,#detailed)

  -- if ray picks up models, then check for collision
 
if (collisionList.count) then \
me.checkForCollision(collisionList[1])

  -- cast ray backward
  collisionList = \
p3Dmember.modelsUnderRay(pCharBoundingBox.worldPosition, \
-pCharacter.transform.xAxis,#detailed)

  -- if ray picks up models, then check for collision
  if (collisionList.count) then \
me.checkForCollision(collisionList[1])

end

on checkForCollision me, collisData

This handler is activated when a model is picked up by modelsUnderRay. The statement we used above:
me.checkForCollision(collisionList[1]) - - dot syntax
is equivalent to
checkForCollision me, collisionList[1] - - verbose syntax
So, collisionList[1] is assigned as a value to the parameter thisData.

  -- grab the #distance value from the CollisionList
  dist = collisData.distance

  -- check if distance is less than bounding box width
  if (dist < pCharBoundingBox.resource.width/2) then

    -- get distance of penetration
    diff = pCharBoundingBox.resource.width/2 - dist

    -- calculate vector perpendicular to the wall's
    -- surface to move the character using the
    -- #isectNormal property
    tVector = collisData.isectNormal * diff

    -- move the character in order to resolve the
    -- collision
   
pCharacter.translate(tVector,#world)

  end if

end


7.  Play the movie and test what you just created. If you want to remove the bounding box, change the blend to 0.
You can download the completed movie from here.