new start

This commit is contained in:
James B. Pollack 2016-02-23 12:24:10 -08:00
parent f82e5e2cd6
commit fb7943efae
2 changed files with 254 additions and 0 deletions

View file

@ -0,0 +1,182 @@
//
// flockOfFish.js
// examples
//
// Philip Rosedale
// Copyright 2016 High Fidelity, Inc.
// Fish smimming around in a space in front of you
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
var LIFETIME = 300; // Fish live for 5 minutes
var NUM_FISH = 20;
var TANK_WIDTH = 3.0;
var TANK_HEIGHT = 1.0;
var FISH_WIDTH = 0.03;
var FISH_LENGTH = 0.15;
var MAX_SIGHT_DISTANCE = 0.8;
var MIN_SEPARATION = 0.15;
var AVOIDANCE_FORCE = 0.2;
var COHESION_FORCE = 0.05;
var ALIGNMENT_FORCE = 0.05;
var SWIMMING_FORCE = 0.05;
var SWIMMING_SPEED = 1.5;
var fishLoaded = false;
var fish = [];
var lowerCorner = { x: 0, y: 0, z: 0 };
var upperCorner = { x: 0, y: 0, z: 0 };
function randomVector(scale) {
return { x: Math.random() * scale - scale / 2.0, y: Math.random() * scale - scale / 2.0, z: Math.random() * scale - scale / 2.0 };
}
function updateFish(deltaTime) {
if (!Entities.serversExist() || !Entities.canRez()) {
return;
}
if (!fishLoaded) {
loadFish(NUM_FISH);
fishLoaded = true;
return;
}
var averageVelocity = { x: 0, y: 0, z: 0 };
var averagePosition = { x: 0, y: 0, z: 0 };
var birdPositionsCounted = 0;
var birdVelocitiesCounted = 0;
// First pre-load an array with properties on all the other fish so our per-fish loop
// isn't doing it.
var flockProperties = [];
for (var i = 0; i < fish.length; i++) {
var otherProps = Entities.getEntityProperties(fish[i].entityId, ["position", "velocity", "rotation"]);
flockProperties.push(otherProps);
}
for (var i = 0; i < fish.length; i++) {
if (fish[i].entityId) {
// Get only the properties we need, because that is faster
var properties = flockProperties[i];
// If fish has been deleted, bail
if (properties.id != fish[i].entityId) {
fish[i].entityId = false;
return;
}
// Store old values so we can check if they have changed enough to update
var velocity = { x: properties.velocity.x, y: properties.velocity.y, z: properties.velocity.z };
var position = { x: properties.position.x, y: properties.position.y, z: properties.position.z };
averageVelocity = { x: 0, y: 0, z: 0 };
averagePosition = { x: 0, y: 0, z: 0 };
var othersCounted = 0;
for (var j = 0; j < fish.length; j++) {
if (i != j) {
// Get only the properties we need, because that is faster
var otherProps = flockProperties[j];
var separation = Vec3.distance(properties.position, otherProps.position);
if (separation < MAX_SIGHT_DISTANCE) {
averageVelocity = Vec3.sum(averageVelocity, otherProps.velocity);
averagePosition = Vec3.sum(averagePosition, otherProps.position);
othersCounted++;
}
if (separation < MIN_SEPARATION) {
var pushAway = Vec3.multiply(Vec3.normalize(Vec3.subtract(properties.position, otherProps.position)), AVOIDANCE_FORCE);
velocity = Vec3.sum(velocity, pushAway);
}
}
}
if (othersCounted > 0) {
averageVelocity = Vec3.multiply(averageVelocity, 1.0 / othersCounted);
averagePosition = Vec3.multiply(averagePosition, 1.0 / othersCounted);
// Alignment: Follow group's direction and speed
velocity = Vec3.mix(velocity, Vec3.multiply(Vec3.normalize(averageVelocity), Vec3.length(velocity)), ALIGNMENT_FORCE);
// Cohesion: Steer towards center of flock
var towardCenter = Vec3.subtract(averagePosition, position);
velocity = Vec3.mix(velocity, Vec3.multiply(Vec3.normalize(towardCenter), Vec3.length(velocity)), COHESION_FORCE);
}
// Try to swim at a constant speed
velocity = Vec3.mix(velocity, Vec3.multiply(Vec3.normalize(velocity), SWIMMING_SPEED), SWIMMING_FORCE);
// Keep fish in their 'tank'
if (position.x < lowerCorner.x) {
position.x = lowerCorner.x;
velocity.x *= -1.0;
} else if (position.x > upperCorner.x) {
position.x = upperCorner.x;
velocity.x *= -1.0;
}
if (position.y < lowerCorner.y) {
position.y = lowerCorner.y;
velocity.y *= -1.0;
} else if (position.y > upperCorner.y) {
position.y = upperCorner.y;
velocity.y *= -1.0;
}
if (position.z < lowerCorner.z) {
position.z = lowerCorner.z;
velocity.z *= -1.0;
} else if (position.z > upperCorner.z) {
position.z = upperCorner.z;
velocity.z *= -1.0;
}
// Orient in direction of velocity
var rotation = Quat.rotationBetween(Vec3.UNIT_NEG_Z, velocity);
var VELOCITY_FOLLOW_RATE = 0.30;
// Only update properties if they have changed, to save bandwidth
var MIN_POSITION_CHANGE_FOR_UPDATE = 0.001;
if (Vec3.distance(properties.position, position) < MIN_POSITION_CHANGE_FOR_UPDATE) {
Entities.editEntity(fish[i].entityId, { velocity: velocity, rotation: Quat.mix(properties.rotation, rotation, VELOCITY_FOLLOW_RATE) });
} else {
Entities.editEntity(fish[i].entityId, { position: position, velocity: velocity, rotation: Quat.slerp(properties.rotation, rotation, VELOCITY_FOLLOW_RATE) });
}
}
}
}
// Connect a call back that happens every frame
Script.update.connect(updateFish);
// Delete our little friends if script is stopped
Script.scriptEnding.connect(function() {
for (var i = 0; i < fish.length; i++) {
Entities.deleteEntity(fish[i].entityId);
}
});
var STARTING_FRACTION = 0.25;
function loadFish(howMany) {
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), 2 * TANK_WIDTH));
lowerCorner = { x: center.x - TANK_WIDTH / 2, y: center.y, z: center.z - TANK_WIDTH / 2 };
upperCorner = { x: center.x + TANK_WIDTH / 2, y: center.y + TANK_HEIGHT, z: center.z + TANK_WIDTH / 2 };
for (var i = 0; i < howMany; i++) {
var position = {
x: lowerCorner.x + (upperCorner.x - lowerCorner.x) / 2.0 + (Math.random() - 0.5) * (upperCorner.x - lowerCorner.x) * STARTING_FRACTION,
y: lowerCorner.y + (upperCorner.y - lowerCorner.y) / 2.0 + (Math.random() - 0.5) * (upperCorner.y - lowerCorner.y) * STARTING_FRACTION,
z: lowerCorner.z + (upperCorner.z - lowerCorner.x) / 2.0 + (Math.random() - 0.5) * (upperCorner.z - lowerCorner.z) * STARTING_FRACTION
};
fish.push({
entityId: Entities.addEntity({
type: "Box",
position: position,
rotation: { x: 0, y: 0, z: 0, w: 1 },
dimensions: { x: FISH_WIDTH, y: FISH_WIDTH, z: FISH_LENGTH },
velocity: { x: SWIMMING_SPEED, y: SWIMMING_SPEED, z: SWIMMING_SPEED },
damping: 0.0,
dynamic: false,
lifetime: LIFETIME,
color: { red: 0, green: 255, blue: 255 }
})
});
}
}

View file

@ -0,0 +1,72 @@
(function() {
function FishTank() {
_this = this;
}
FishTank.prototype = {
preload: function(entityID) {
print("preload");
this.entityID = entityID;
//we only need to do this once since tank is static
_this.currentProperties = Entities.getEntityProperties(entityID);
Script.update.connect(this.update)
},
unload: function() {
},
update: function() {
_this.updateWithoutSort();
},
updateWithSort: function() {
var avatars = AvatarList.getAvatarIdentifiers();
var distances = [];
avatars.forEach(function(avatar) {
var _avatar = AvatarList.getAvatar(avatar);
var distanceFromTank = Vec3.distance(_this.currentProperties.position, _avatar.position);
distances.push([distanceFromTank, avatar]);
})
var sortedDistances = distances.sort(function(a, b) {
return a[0] - b[0]
});
//only the closest person should run the update loop
if (sortedDistances[0][1] !== MyAvatar.sessionUUID) {
return;
} else {
}
},
updateWithoutSort: function() {
var avatars = AvatarList.getAvatarIdentifiers();
var minDistance = 100000;
var closest;
print('AVATARS' + avatars)
avatars.forEach(function(avatar) {
var _avatar = AvatarList.getAvatar(avatar);
var distanceFromTank = Vec3.distance(_this.currentProperties.position, _avatar.position);
if (distanceFromTank < minDistance) {
minDistance = distanceFromTank;
closest = avatar;
}
})
//only the closest person should run the update loop
// print('closest:', closest)
// print('me', MyAvatar.sessionUUID)
if (closest !== MyAvatar.sessionUUID) {
//print('not me')
return;
} else {
print('i should run this entity because im closest:' + _this.entityID)
}
}
};
return new FishTank();
});