====== Lab 4 - Events, Collision and Movement ======
===== Before Coming to Class =====
* Just be able to run new Panda 3D programs
===== Useful Links =====
* [[http://www.ru.is/kennarar/hannes/useful/PQR2.3.html|Python Quick Reference (LOCAL)]]
* [[http://www.ru.is/kennarar/hannes/useful/Python-Docs-2.5|Complete Python Documentation (LOCAL)]] //This is the latest 2.5 release, but new 2.5 features are well marked//
* [[http://www.ru.is/kennarar/hannes/useful/PandaManual|Panda 3D Manual (LOCAL)]]
* [[http://www.ru.is/kennarar/hannes/useful/PandaReference|Panda 3D Reference(LOCAL)]]
* [[http://www.ru.is/kennarar/hannes/useful/BlenderManual/htmlI|Blender Manual (LOCAL)]]
* [[http://www.ru.is/kennarar/hannes/useful/BlenderManual/htmlII/p2.html|Blender Reference (LOCAL)]]
* [[http://www.ru.is/kennarar/hannes/share/chicken_export1.0.zip|Chicken 1.0 EGG Exporter for Blender 3D (LOCAL)]]
===== In-Class Excercises =====
==== Before You Start ====
* Download and unzip the [[http://www.ru.is/kennarar/hannes/classes/ve2007/Lab4Assets.zip|Lab 4 Asset File]] into your working director
==== Making a functional trap door ====
{{public:t-vien-07-1:ve-lab4-screenshot.jpg|}}
- **Creating a new scene:**\\ Create a new program called "factory.py" and include the following imports at the beginning of the file:
import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject
from pandac.PandaModules import *
from direct.interval.IntervalGlobal import *
In your code, you should disable the mouse and then place the **camera** at **(1,-8,5)** with a pitch of **-25 degrees**. Then define a class called **World** that derives from **DirectObject** (this allows **World** to receive events). In the constructor of the **World** class load and display two models: **./Models/factoryfloor.egg** and **./Models/box.egg**. Position the box at **(1,0,2)**, scale it down to **30%** and give it a heading of **45 degrees**. Make sure you can see both the floor and the box.
- **Opening and closing the door:**\\ The floor contains a **door** object. Acquire a pathnode to it and call it **door**. Create two **LerpPosInterval**s for the **door**, one to open it by moving it to location **(-1,0,0)** and one to close it by moving it to location **(1,0,0)**. You can play with the duration but **2.0 seconds** is a good place to start. Make the **World** accept a 'space' (spacebar key) event and call the associated event handler **self.door_handler**. In that handler, either start the opening interval for the door or the closing interval, depending on whether it's open or closed already (you'll need a new boolean member variable to keep track of that). Make sure you can now open and close the door with the spacebar.\\
- **Improving the door a little:**\\ When you create the opening and closing intervals, give the constructor attribute **blendType** a value of **"easeInOut"** for a bit more realistic trap door motion (you can play with the other values **"easeIn"** and **"easeOut"** as well). Also, give the attribute **name** the same name for both intervals - this ensures that the first interval gets cancelled when the second one gets starts.
- **Creating Collision Solids:**\\ Attach a new **CollisionNode** to the box pathnode and give it the name **"box"** (you pass the name into the constructor of the CollisionNode). To thie node of this new pathnode, add a new collision solid of the type sphere and give it a radius of **1.5**.
# HINT - Creating and attaching a CollisionSphere
nodepath = object.attachNewNode(CollisionNode('object'))
nodepath.node().addSolid(CollisionSphere(0,0,0,radius))
Similarly, attach a new **CollisionNode** to the door pathnode and call it **door**. This time however, add a **CollisionPolygon** to the node of the new pathnode. The verticies of this polygon are **(-1,-1,0)**, **(1,-1,0)**, **(1,1,0)** and **(-1,1,0)** in that order.
# HINT - The CollisionPolygon
nodepath.node().addSolid(CollisionPolygon(Point3(x,y,z),Point3(x,y,z),Point3(x,y,z),Point3(x,y,z)))
You can call **show()** on your new collision node nodepaths to see your collision solids as semi-transparent objects when you view your scene. Verify that your solids are in the right place and then remove the calls to show().
- **Making the box move:**\\ Create a new **LerpPosInterval** for the box and have it move to the location **(1,0,-1)**. Make the **World** accept a 'mouse1' event and call a method that starts this interval playing. A duration of **3.0 seconds** for this movement seems good and you might want to try different blend types. Verify that you can make the box move by clicking the left mouse button. Notice that the box goes right through the door.
- **Making a collision happen:**\\ Create a new instance of a **CollisionHandlerEvent** and add the event pattern shown below:
collhandler = CollisionHandlerEvent()
collhandler.addInPattern('%fn-into-%in')
Initialize the global collision traverser as follows:
base.cTrav = CollisionTraverser('world traverser')
and then add the nodepath to your box collision node as a new collider and associate it with the handler you just created.
#HINT - Adding a new collider to a traverser
base.cTrav.addCollider(nodepath, handler)
Make the world accept an event called "box-into-door" and call a method named **self.collision** where you can put things that should happen when a collision with this event name occurs. Notice that this method receives a paramater called **collEntry** which contains information about the collision event. The following is a simple collision event receiver:
def collision(self, collEntry):
print collEntry
In addition to printing out some information like is done in this example, you should call the **pause()** method on the LerpPosInterval associated with the box object. This will stop the progress of the box movement. Verify that the box stops now when the door is closed but goes all the way down when the door is open.
- **Adding sounds:**\\ To add sounds to this scene, you can use **thud.wav** for when the box hits the door and **close.wav** for when the door slams shut. You don't have to localize these sounds, just load them in like this:
thudsound = loader.loadSfx("thud.wav")
closesound = loader.loadSfx("close.wav")
It should be pretty clear where to put the **thudsound**, but the **closesound** needs to be wrapped into a **SoundInterval** object and then sequenced with the closing door LerpPosInterval so that it happens at the end of the movement.
# HINT - Sequencing intervals
newinterval = Sequence(interval1, interval2, ... )
- It would be great to add lighting and for example have a red flashing warning light go off every time the door moves :)