#VRML V2.0 utf8
PROTO Critter [
field SFVec3f position 0 0 0
field SFFloat speed 1
field SFVec3f obstacle 0 0 1
eventIn SFVec3f set_obstacle
]
{
Group {
children [
DEF MOVEME Transform {
translation IS position
children Shape {
appearance Appearance { material DEF MAT Material {} }
geometry Box { size .5 .5 .5 }
}
}
DEF HEART TimeSensor { loop TRUE }
# update direction and position when receive "heartbeats"
DEF SCRIPT Script {
eventIn SFTime beat
eventIn SFVec3f set_obstacle IS set_obstacle
eventIn SFBool touched
eventOut SFVec3f position_changed
eventOut SFColor color_changed
field SFVec3f direction 0 0 1
field SFVec3f position IS position
field SFVec3f goal 0 0 0
field SFFloat speed IS speed
field SFTime lastBeat 0
field SFVec3f obstacle IS obstacle
field SFVec3f prevObstacle IS obstacle
field SFBool first TRUE
field SFFloat repulse 5
field SFFloat attract 1
field SFFloat power 1
field SFInt32 touches 0
field SFBool afraid TRUE # if not afraid, then curious
field SFBool asleep FALSE
field SFBool angry FALSE
url "vrmlscript:
function touched(val) {
if(val) {
asleep = FALSE;
++touches;
color_changed = new SFColor(Math.random(), Math.random(), Math.random());
}
if(touches > 2) {
if(afraid) { afraid = FALSE; }
else { afraid = TRUE; }
touches = 0;
}
}
function repulseForce() {
v = position.subtract(obstacle);
ods = repulse * 1/Math.pow(v.length(), power);
return v.normalize().multiply(ods);
}
function calcDirection() {
// compute force of attraction towards goal
gforce = goal.subtract(position).normalize().multiply(attract);
rforce = repulseForce();
if(!afraid) { rforce = rforce.multiply(-1); }
force = gforce.add(rforce);
direction = force.normalize();
}
function set_goal(val) {
goal = val;
// calcDirection();
}
function beat(val) {
if(first) { // initialize lastBeat on first heartbeat
lastBeat = val;
first = FALSE;
}
else {
timeElapsed = val - lastBeat;
calcDirection();
if(!asleep) {
position = position.add(direction.multiply(speed*timeElapsed));
position_changed = position;
}
// occasionally wake or fall asleep at random
if(Math.random() < .003) {
if(asleep) { // wakeup!
asleep = FALSE;
color_changed = new SFColor(Math.random(), Math.random(), Math.random());
}
else { // go to sleep
asleep = TRUE;
color_changed = new SFColor(.1, .1, .1);
}
}
lastBeat = val;
}
}
function set_obstacle(val) {
obstacle = val;
}
"
}
DEF TOUCH TouchSensor {}
]
}
ROUTE HEART.time TO SCRIPT.beat
ROUTE SCRIPT.position_changed TO MOVEME.translation
ROUTE TOUCH.isActive TO SCRIPT.touched
ROUTE SCRIPT.color_changed TO MAT.diffuseColor
}