Bot.js NPCs wander around and make sounds

This commit is contained in:
Philip Rosedale 2014-02-26 13:50:04 -08:00
parent 220f9fda76
commit 0a1e840d10
7 changed files with 108 additions and 39 deletions

View file

@ -3,6 +3,7 @@
// hifi
//
// Created by Stephen Birarda on 2/20/14.
// Modified by Philip on 2/26/14
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// This is an example script that demonstrates an NPC avatar.
@ -17,16 +18,53 @@ function getRandomInt (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var CHANCE_OF_MOVING = 0.05;
var CHANCE_OF_MOVING = 0.005;
var CHANCE_OF_SOUND = 0.005;
var CHANCE_OF_HEAD_TURNING = 0.05;
var CHANCE_OF_BIG_MOVE = 0.1;
var isMoving = false;
var isTurningHead = false;
var STARTING_RANGE = 18.0;
var MOVE_RANGE = 1.0;
var MAX_RANGE = 25.0;
var MOVE_RANGE_SMALL = 0.5;
var MOVE_RANGE_BIG = 5.0;
var TURN_RANGE = 45.0;
var STOP_TOLERANCE = 0.05;
var MOVE_RATE = 0.10;
var MOVE_RATE = 0.05;
var TURN_RATE = 0.15;
var PITCH_RATE = 0.20;
var PITCH_RANGE = 30.0;
var firstPosition = { x: getRandomFloat(0, STARTING_RANGE), y: 0, z: getRandomFloat(0, STARTING_RANGE) };
var targetPosition = { x: 0, y: 0, z: 0 };
var targetDirection = { x: 0, y: 0, z: 0, w: 0 };
var currentDirection = { x: 0, y: 0, z: 0, w: 0 };
var targetHeadPitch = 0.0;
var sounds = [];
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh1.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh2.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh3.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh4.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh5.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh6.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh7.raw"));
sounds.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Vocals/sigh8.raw"));
// Play a random sound from a list of conversational audio clips
function playRandomSound(position) {
var whichSound = Math.floor((Math.random() * sounds.length) % sounds.length);
var audioOptions = new AudioInjectionOptions();
audioOptions.volume = 0.25 + (Math.random() * 0.75);
audioOptions.position = position;
Audio.playSound(sounds[whichSound], audioOptions);
print("PlaySound " + whichSound);
}
// change the avatar's position to the random one
Avatar.position = firstPosition;
@ -62,18 +100,40 @@ Avatar.billboardURL = "https://dl.dropboxusercontent.com/u/1864924/bot-billboard
Agent.isAvatar = true;
function updateBehavior() {
if (Math.random() < CHANCE_OF_SOUND) {
playRandomSound(Avatar.position);
}
if (!isTurningHead && (Math.random() < CHANCE_OF_HEAD_TURNING)) {
targetHeadPitch = getRandomFloat(-PITCH_RANGE, PITCH_RANGE);
isTurningHead = true;
} else {
Avatar.headPitch = Avatar.headPitch + (targetHeadPitch - Avatar.headPitch) * PITCH_RATE;
if (Math.abs(Avatar.headPitch - targetHeadPitch) < STOP_TOLERANCE) {
isTurningHead = false;
}
}
if (!isMoving && (Math.random() < CHANCE_OF_MOVING)) {
// Set new target location
targetPosition = { x: Avatar.position.x + getRandomFloat(-MOVE_RANGE, MOVE_RANGE),
y: Avatar.position.y,
z: Avatar.position.z + getRandomFloat(-MOVE_RANGE, MOVE_RANGE) };
targetDirection = Quat.multiply(Avatar.orientation, Quat.angleAxis(getRandomFloat(-TURN_RANGE, TURN_RANGE), { x:0, y:1, z:0 }));
var front = Quat.getFront(targetDirection);
if (Math.random() < CHANCE_OF_BIG_MOVE) {
targetPosition = Vec3.sum(Avatar.position, Vec3.multiply(front, getRandomFloat(0.0, MOVE_RANGE_BIG)));
} else {
targetPosition = Vec3.sum(Avatar.position, Vec3.multiply(front, getRandomFloat(0.0, MOVE_RANGE_SMALL)));
}
isMoving = true;
} else {
Avatar.position = Vec3.sum(Avatar.position, Vec3.multiply(Vec3.subtract(targetPosition, Avatar.position), MOVE_RATE));
Avatar.orientation = Quat.mix(Avatar.orientation, targetDirection, TURN_RATE);
if (Vec3.length(Vec3.subtract(Avatar.position, targetPosition)) < STOP_TOLERANCE) {
isMoving = false;
}
}
if (Vec3.length(Avatar.position) > MAX_RANGE) {
// Don't let our happy little person get out of the cage
isMoving = false;
Avatar.position = { x: 0, y: 0, z: 0 };
}
}
Script.willSendVisualDataCallback.connect(updateBehavior);

View file

@ -104,36 +104,7 @@ glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) {
return glm::angleAxis(angle, axis);
}
// Safe version of glm::mix; based on the code in Nick Bobick's article,
// http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde,
// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java)
glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) {
float cosa = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
float ox = q2.x, oy = q2.y, oz = q2.z, ow = q2.w, s0, s1;
// adjust signs if necessary
if (cosa < 0.0f) {
cosa = -cosa;
ox = -ox;
oy = -oy;
oz = -oz;
ow = -ow;
}
// calculate coefficients; if the angle is too close to zero, we must fall back
// to linear interpolation
if ((1.0f - cosa) > EPSILON) {
float angle = acosf(cosa), sina = sinf(angle);
s0 = sinf((1.0f - proportion) * angle) / sina;
s1 = sinf(proportion * angle) / sina;
} else {
s0 = 1.0f - proportion;
s1 = proportion;
}
return glm::normalize(glm::quat(s0 * q1.w + s1 * ow, s0 * q1.x + s1 * ox, s0 * q1.y + s1 * oy, s0 * q1.z + s1 * oz));
}
glm::vec3 extractTranslation(const glm::mat4& matrix) {
return glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2]);

View file

@ -45,8 +45,6 @@ float angleBetween(const glm::vec3& v1, const glm::vec3& v2);
glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2);
glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha);
glm::vec3 extractTranslation(const glm::mat4& matrix);
void setTranslation(glm::mat4& matrix, const glm::vec3& translation);

View file

@ -13,8 +13,10 @@
#include <OctreeConstants.h>
#include <SharedUtil.h>
#include <Util.h>
#include "Quat.h"
glm::quat Quat::multiply(const glm::quat& q1, const glm::quat& q2) {
return q1 * q2;
}
@ -31,7 +33,6 @@ glm::quat Quat::inverse(const glm::quat& q) {
return glm::inverse(q);
}
glm::vec3 Quat::getFront(const glm::quat& orientation) {
return orientation * IDENTITY_FRONT;
}
@ -52,3 +53,7 @@ glm::quat Quat::angleAxis(float angle, const glm::vec3& v) {
return glm::angleAxis(angle, v);
}
glm::quat Quat::mix(const glm::quat& q1, const glm::quat& q2, float alpha) {
return safeMix(q1, q2, alpha);
}

View file

@ -29,6 +29,7 @@ public slots:
glm::vec3 getUp(const glm::quat& orientation);
glm::vec3 safeEulerAngles(const glm::quat& orientation);
glm::quat angleAxis(float angle, const glm::vec3& v);
glm::quat mix(const glm::quat& q1, const glm::quat& q2, float alpha);
};
#endif /* defined(__hifi__Quat__) */

View file

@ -65,6 +65,38 @@ bool shouldDo(float desiredInterval, float deltaTime) {
return randFloat() < deltaTime / desiredInterval;
}
// Safe version of glm::mix; based on the code in Nick Bobick's article,
// http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde,
// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java)
glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) {
float cosa = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
float ox = q2.x, oy = q2.y, oz = q2.z, ow = q2.w, s0, s1;
// adjust signs if necessary
if (cosa < 0.0f) {
cosa = -cosa;
ox = -ox;
oy = -oy;
oz = -oz;
ow = -ow;
}
// calculate coefficients; if the angle is too close to zero, we must fall back
// to linear interpolation
if ((1.0f - cosa) > EPSILON) {
float angle = acosf(cosa), sina = sinf(angle);
s0 = sinf((1.0f - proportion) * angle) / sina;
s1 = sinf(proportion * angle) / sina;
} else {
s0 = 1.0f - proportion;
s1 = proportion;
}
return glm::normalize(glm::quat(s0 * q1.w + s1 * ow, s0 * q1.x + s1 * ox, s0 * q1.y + s1 * oy, s0 * q1.z + s1 * oz));
}
void outputBufferBits(const unsigned char* buffer, int length, QDebug* continuedDebug) {

View file

@ -75,6 +75,8 @@ float randFloatInRange (float min,float max);
unsigned char randomColorValue(int minimum);
bool randomBoolean();
glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha);
bool shouldDo(float desiredInterval, float deltaTime);
void outputBufferBits(const unsigned char* buffer, int length, QDebug* continuedDebug = NULL);