Andrew Meadowsarchive remaining JS scriptsc0b446c10 years agoHistory
182 lines · 7.5 KB · JavaScript
RawDownload
////  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 frameScript.update.connect(updateFish); //  Delete our little friends if script is stoppedScript.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 }        })      });    }}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t