Documentation


All tutorials

CopperLicht Tutorial: Collision detection and response


This tutorial demonstrates how to do collision detection in CopperLicht.
The final result of this tutorial will look about like this:

copperlicht tutorial 6
A room where the user cannot move trough walls, and he can walk up stairs.


Creating a room

In order to collide against walls, we need a room. Start up the 3d editor CopperCube and create a simple room, like in this example:

a room

In the zip archive of this tutorial, you'll find a .ccb file named room.ccb which contains exactly this example, you can also use this one.

Use CopperCube to save the scene into a directory of your choice and publish the scene as WebGL/JavaScript (Choose Tools -> Test as JavaScript/WebGL). This will create a .ccbjs or .ccbz file with this scene which we can load using CopperLicht.

Writing CopperLicht code

Now create a .html file in that directory and paste the following code into it. What it does will be explained in detail below.
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
	<script type="text/javascript" src="copperlichtdata/copperlicht.js"></script>
</head>
<body>
	<b>Tutorial 06: Collision detection.</b><br/>
	Demonstrates how to do various ways of collision detection: Walking around in a room and picking the wall<br/><br/>
	<div style="width:640px; margin:auto; position:relative; font-size: 9pt; color: #777777;">
		<canvas id="3darea" width="640" height="480" style="background-color:#000000">
		</canvas>
		<div style="display:block; color:#ffffff; padding:5px; position:absolute; left:20px; top:420px; background-color:#000000; height:37px; width:300px; border-radius:5px; border:1px solid #777777; opacity:0.5;" id="helptext"> 
			Look with the mouse, move with the cursor keys or WASD. Press space to 'shoot' a cube at the next wall.
		</div> 
	</div>
	<script type="text/javascript">
	<!--
	var engine = startCopperLichtFromFile('3darea', 'copperlichtdata/room.ccbjs');
	var cubeCollisionPosition = null;
	// this is called when loading the 3d scene has finished
	
	engine.OnLoadingComplete = function() 
	{		
		var scene = engine.getScene();
		if (!scene)
			return;
			
		// in the CopperCube 3d editor, we already created a camera which collides against the wall in this scene.
		// But to demonstrate how this would work manually, we create a new camera here which does this as well:
		
		// add a user controlled camera
		
		var cam = new CL3D.CameraSceneNode();
		
		// ensure to place the camera inside the room, or it will fall out, into the endless void
		
		cam.Pos.X = -50; 
		cam.Pos.Y = 180;
		cam.Pos.Z = -20;
		
		// add an animator which makes the camera move by keyboard and mouse input
		
		var animator = new CL3D.AnimatorCameraFPS(cam, engine);	
		animator.MoveSpeed = 0.2;
		animator.RotateSpeed = 250;
		animator.setLookByMouseDown(false); //  look when the mouse is moved
		cam.addAnimator(animator);	
		
		animator.lookAt(new CL3D.Vect3d(-200,90,200));			
		
		scene.getRootSceneNode().addChild(cam);
		scene.setActiveCamera(cam);		
		
		// add the collision response animator to collide against walls
		
		var colanimator = new CL3D.AnimatorCollisionResponse(
			new CL3D.Vect3d(20,40,20), // size of the player ellipsoid
			new CL3D.Vect3d(0,30,0), // position of the eye in the ellipsoid
			scene.getCollisionGeometry());
			
		cam.addAnimator(colanimator);	
	}
	
	// every time the user presses space, we want to do a collision test with the wall
	// and create a cube where we hit the wall
	
	document.onkeyup = function(event)
	{
		var scene = engine.getScene();
		if (!scene)
			return;
			
		if (event.keyCode == 32) // space has been pressed
		{
			var cam = scene.getActiveCamera();
			
			// calculate the start and end 3d point of the line, the beinning being
			// the camera position and the end about 2000 units away in the direction of the
			// camera target
			
			var startLine = cam.getAbsolutePosition();
			var endLine = startLine.add(cam.getTarget().substract(startLine).multiplyWithScal(2000));
			
			// test our line for a collision with the world
			
			var collisionPoint = scene.getCollisionGeometry().getCollisionPointWithLine(startLine, endLine, true, null);
						
			if (collisionPoint)
			{
				// a collision has been found.
				// create a cube at the point where the collision happened
				
				if (!cubeCollisionPosition)
				{
					cubeCollisionPosition = new CL3D.CubeSceneNode();
					scene.getRootSceneNode().addChild(cubeCollisionPosition);
					cubeCollisionPosition.getMaterial(0).Tex1 = engine.getTextureManager().getTexture('ground_stone.jpg', true);
				}
				
				cubeCollisionPosition.Pos = collisionPoint;
			}
		}		
		
		// we need to call the key handler of the 3d engine as well, so that the user is
		// able to move the camera using the keys
		engine.handleKeyUp(event);
	};
	-->
	</script>
</body>
</html>
Also, put a .jpg file named 'ground_stone.jpg' into that directory, so that the cube will have a texture.

What the code does

As always, the first few lines of the html code are creating a canvas element inside a container. Inside this container, there is a <div>, used for the 2d overlay. The style of these divs with its absolute positioning is making it possible to move them over the canvas:
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
  <script type="text/javascript" src="copperlichtdata/copperlicht.js"></script>
</head>
<body>
  <b>Tutorial 06: Collision detection.</b><br/>
  Demonstrates how to do various ways of collision detection: Walking around in a room and picking the wall<br/><br/>
  <div style="width:640px; margin:auto; position:relative; font-size: 9pt; color: #777777;">
    <canvas id="3darea" width="640" height="480" style="background-color:#000000">
    </canvas>
    <div style="display:block; color:#ffffff; padding:5px; position:absolute; left:20px; top:420px; background-color:#000000; height:37px; width:300px; border-radius:5px; border:1px solid #777777; opacity:0.5;" id="helptext"> 
      Look with the mouse, move with the cursor keys or WASD. Press space to 'shoot' a cube at the next wall.
    </div> 
  </div>
So let's start with the javascript code. First, we simply initialize the 3d engine and tell it to load the scene file we created in the editor. You might have to adjust the file name of the .ccbjs file to fit the name you picked when saving the scene. We also store a variable named cubeCollisionPosition where we later save a scene node used to mark the position where we hit the wall when 'shooting':
<script type="text/javascript">
  <!--
  var engine = startCopperLichtFromFile('3darea', 'copperlichtdata/room.ccbjs');
  var cubeCollisionPosition = null;
  
Next, we add a camera to the scene when the .ccbjs file has finished loading. But this time, the camera should not only move when the user presses the cursor keys which is achieved using a AnimatorCameraFPS, we also want the camera to collide against walls. For this, we also add another animator which is named AnimatorCollisionResponse. The AnimatorCollisionResponse gets several parameters like the size of the player, and also the collision geometry as a triangle selector. Luckly, we don't have to create this triangle selector ourselves, we can simply use the one of the scene by calling scene.getCollisionGeometry(). This is done here:
  engine.OnLoadingComplete = function() 
  {    
    var scene = engine.getScene();
    if (!scene)
      return;
      
     // in the CopperCube 3d editor, we already created a camera which collides against the wall in this scene.
    // But to demonstrate how this would work manually, we create a new camera here which does this as well:
    
     // add a user controlled camera
    
    var cam = new CL3D.CameraSceneNode();
    
     // ensure to place the camera inside the room, or it will fall out, into the endless void
    
    cam.Pos.X = -50; 
    cam.Pos.Y = 180;
    cam.Pos.Z = -20;
    
     // add an animator which makes the camera move by keyboard and mouse input
    
    var animator = new CL3D.AnimatorCameraFPS(cam, engine);  
    animator.MoveSpeed = 0.2;
    animator.RotateSpeed = 250;
	animator.setLookByMouseDown(false); //  look when the mouse is moved
    cam.addAnimator(animator);                    
    animator.lookAt(new CL3D.Vect3d(-200,90,200));      
    
    scene.getRootSceneNode().addChild(cam);
    scene.setActiveCamera(cam);    
    
     // add the collision response animator to collide against walls
    
    var colanimator = new CL3D.AnimatorCollisionResponse(
      new CL3D.Vect3d(20,40,20),   // size of the player ellipsoid
      new CL3D.Vect3d(0,30,0),   // position of the eye in the ellipsoid
      scene.getCollisionGeometry());
      
    cam.addAnimator(colanimator);  
  }
Instead of scene.getCollisionGeometry(), we could have also supplied some other collision geometry represented as TriangleSelector. Triangle selectors in CopperLicht simply provide a way to access geometry. If you have a Mesh for example, you can create a new triangle selector using new MeshTriangleSelector(yourMesh, yourSceneNode);. If the mesh is big, you could use new OctTreeTriangleSelector(yourMesh, yourSceneNode) instead to speed up the collision queries a bit.
CopperLicht automaticly provides a triangle selector for all geometry where the 'Collision' attribute was checked in the editor via scene.getCollisionGeometry(), so you won't have to build it yourself.

You could try out the program already now, it should work and you can walk around in the room without being able to walk through walls. But to demonstrate another collision detection feature, we'll add this final code part:
  // every time the user presses space, we want to do a collision test with the wall
  // and create a cube where we hit the wall
  
  document.onkeyup = function(event)
  {
    var scene = engine.getScene();
    if (!scene)
      return;
      
    if (event.keyCode == 32) // space has been pressed
    {
      var cam = scene.getActiveCamera();
      
      // calculate the start and end 3d point of the line, the beinning being
      // the camera position and the end about 2000 units away in the direction of the
      // camera target
      
      var startLine = cam.getAbsolutePosition();
      var endLine = startLine.add(cam.getTarget().substract(startLine).multiplyWithScal(2000));
      
      // test our line for a collision with the world
      
      var collisionPoint = scene.getCollisionGeometry().getCollisionPointWithLine(startLine, endLine, true, null);
            
      if (collisionPoint)
      {
        // a collision has been found.
        // create a cube at the point where the collision happened
        
        if (!cubeCollisionPosition)
        {
          cubeCollisionPosition = new CL3D.CubeSceneNode();
          scene.getRootSceneNode().addChild(cubeCollisionPosition);
          cubeCollisionPosition.getMaterial(0).Tex1 = engine.getTextureManager().getTexture('ground_stone.jpg', true);
        }
        
        cubeCollisionPosition.Pos = collisionPoint;
      }
    }    
    
    // we need to call the key handler of the 3d engine as well, so that the user is
    // able to move the camera using the keys
    engine.handleKeyUp(event);
  };
  -->
  </script>
  </body>
</body>
</html>
	
Basically, we just added a function which causes a cube to be created at the point of the wall we are looking at when pressing SPACE. To find this collision point, we used the getCollisionPointWithLine() function of the TriangleSelector class. It returns the collision point as Vect3d or null if there was no collision.

And that's it, now you know how to do basic collision detection in CopperLicht.


More Tutorials


© 2011-2017 N.Gebhardt, Ambiera
Documentation generated by JsDoc Toolkit