From 055a62e7e56dc0d9c697fc0f245606ccaa0796b7 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 29 May 2014 11:48:15 -0700 Subject: [PATCH 1/5] Replaced nonfunctioning particleBird.js with awesome particleBirds.js --- examples/particleBird.js | 202 ---------------------------------- examples/particleBirds.js | 222 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 222 insertions(+), 202 deletions(-) delete mode 100644 examples/particleBird.js create mode 100644 examples/particleBirds.js diff --git a/examples/particleBird.js b/examples/particleBird.js deleted file mode 100644 index 5241b3550b..0000000000 --- a/examples/particleBird.js +++ /dev/null @@ -1,202 +0,0 @@ -// -// particleBird.js -// examples -// -// Copyright 2014 High Fidelity, Inc. -// -// This sample script moves a voxel around like a bird and sometimes makes tweeting noises -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -function vLength(v) { - return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); -} -function printVector(v) { - print(v.x + ", " + v.y + ", " + v.z + "\n"); -} -// Create a random vector with individual lengths between a,b -function randVector(a, b) { - var rval = { x: a + Math.random() * (b - a), y: a + Math.random() * (b - a), z: a + Math.random() * (b - a) }; - return rval; -} - -function vMinus(a, b) { - var rval = { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z }; - return rval; -} - -function vPlus(a, b) { - var rval = { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }; - return rval; -} - -function vCopy(a, b) { - a.x = b.x; - a.y = b.y; - a.z = b.z; - return; -} - -// Returns a vector which is fraction of the way between a and b -function vInterpolate(a, b, fraction) { - var rval = { x: a.x + (b.x - a.x) * fraction, y: a.y + (b.y - a.y) * fraction, z: a.z + (b.z - a.z) * fraction }; - return rval; -} - -// Decide what kind of bird we are -var tweet; -var color; -var size; -var which = Math.random(); -if (which < 0.2) { - tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw"); - color = { r: 100, g: 50, b: 120 }; - size = 0.08; -} else if (which < 0.4) { - tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/rosyfacedlovebird.raw"); - color = { r: 100, g: 150, b: 75 }; - size = 0.09; -} else if (which < 0.6) { - tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/saysphoebe.raw"); - color = { r: 84, g: 121, b: 36 }; - size = 0.05; -} else if (which < 0.8) { - tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw"); - color = { r: 23, g: 197, b: 230 }; - size = 0.12; -} else { - tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/westernscreechowl.raw"); - color = { r: 50, g: 67, b: 144 }; - size = 0.15; -} - - -var startTimeInSeconds = new Date().getTime() / 1000; - -var birdLifetime = 20; // lifetime of the bird in seconds! -var position = { x: 0, y: 0, z: 0 }; -var targetPosition = { x: 0, y: 0, z: 0 }; -var range = 1.0; // Over what distance in meters do you want your bird to fly around -var frame = 0; -var moving = false; -var tweeting = 0; -var moved = false; - -var CHANCE_OF_MOVING = 0.00; -var CHANCE_OF_FLAPPING = 0.05; -var CHANCE_OF_TWEETING = 0.05; -var START_HEIGHT_ABOVE_ME = 1.5; -var BIRD_GRAVITY = -0.1; -var BIRD_FLAP = 1.0; -var myPosition = MyAvatar.position; -var properties = { - lifetime: birdLifetime, - position: { x: myPosition.x, y: myPosition.y + START_HEIGHT_ABOVE_ME, z: myPosition.z }, - velocity: { x: 0, y: Math.random() * BIRD_FLAP, z: 0 }, - gravity: { x: 0, y: BIRD_GRAVITY, z: 0 }, - radius : 0.1, - color: { red: 0, - green: 255, - blue: 0 } -}; -var range = 1.0; // Distance around avatar where I can move -// Create the actual bird -var particleID = Particles.addParticle(properties); -function moveBird(deltaTime) { - - // check to see if we've been running long enough that our bird is dead - var nowTimeInSeconds = new Date().getTime() / 1000; - if ((nowTimeInSeconds - startTimeInSeconds) >= birdLifetime) { - - print("our bird is dying, stop our script"); - Script.stop(); - return; - } - - myPosition = MyAvatar.position; - frame++; - if (frame % 3 == 0) { - // Tweeting behavior - if (tweeting == 0) { - if (Math.random() < CHANCE_OF_TWEETING) { - //print("tweet!" + "\n"); - var options = new AudioInjectionOptions(); - options.position = position; - options.volume = 0.75; - Audio.playSound(tweet, options); - tweeting = 10; - } - } else { - tweeting -= 1; - } - if (Math.random() < CHANCE_OF_FLAPPING) { - // Add a little upward impulse to our bird - // TODO: Get velocity - // - var newProperties = { - velocity: { x:0.0, y: Math.random() * BIRD_FLAP, z: 0.0 } - }; - Particles.editParticle(particleID, newProperties); - print("flap!"); - } - // Moving behavior - if (moving == false) { - if (Math.random() < CHANCE_OF_MOVING) { - targetPosition = randVector(-range, range); - targetPosition = vPlus(targetPosition, myPosition); - - if (targetPosition.x < 0) { - targetPosition.x = 0; - } - if (targetPosition.y < 0) { - targetPosition.y = 0; - } - if (targetPosition.z < 0) { - targetPosition.z = 0; - } - if (targetPosition.x > TREE_SCALE) { - targetPosition.x = TREE_SCALE; - } - if (targetPosition.y > TREE_SCALE) { - targetPosition.y = TREE_SCALE; - } - if (targetPosition.z > TREE_SCALE) { - targetPosition.z = TREE_SCALE; - } - //printVector(position); - moving = true; - } - } - if (moving) { - position = vInterpolate(position, targetPosition, 0.5); - if (vLength(vMinus(position, targetPosition)) < (size / 5.0)) { - moved = false; - moving = false; - } else { - moved = true; - } - } - if (moved || (tweeting > 0)) { - if (tweeting > 0) { - var newProperties = { - position: position, - radius : size * 1.5, - color: { red: Math.random() * 255, green: 0, blue: 0 } - }; - } else { - var newProperties = { - position: position, - radius : size, - color: { red: color.r, green: color.g, blue: color.b } - }; - } - Particles.editParticle(particleID, newProperties); - moved = false; - } - } -} - -// register the call back so it fires before each data send -Script.update.connect(moveBird); diff --git a/examples/particleBirds.js b/examples/particleBirds.js new file mode 100644 index 0000000000..b7ecc851ba --- /dev/null +++ b/examples/particleBirds.js @@ -0,0 +1,222 @@ +// +// particleBirds.js +// examples +// +// Copyright 2014 High Fidelity, Inc. +// +// This sample script creates a swarm of tweeting bird particles that fly around the avatar. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// Normalizes a vector to unit length +function vNormalize(v) { + var length = vLength(v); + var rval = { x: v.x / length, y: v.y / length, z: v.z / length }; + return rval; +} + +// Multiply vector by scalar +function vScalarMult(v, s) { + var rval = { x: v.x * s, y: v.y * s, z: v.z * s }; + return rval; +} + +function vLength(v) { + return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); +} + +function printVector(v) { + print(v.x + ", " + v.y + ", " + v.z + "\n"); +} +// Create a random vector with individual lengths between a,b +function randVector(a, b) { + var rval = { x: a + Math.random() * (b - a), y: a + Math.random() * (b - a), z: a + Math.random() * (b - a) }; + return rval; +} + +function vMinus(a, b) { + var rval = { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z }; + return rval; +} + +function vPlus(a, b) { + var rval = { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }; + return rval; +} + +function vCopy(a, b) { + a.x = b.x; + a.y = b.y; + a.z = b.z; + return; +} + +// Returns a vector which is fraction of the way between a and b +function vInterpolate(a, b, fraction) { + var rval = { x: a.x + (b.x - a.x) * fraction, y: a.y + (b.y - a.y) * fraction, z: a.z + (b.z - a.z) * fraction }; + return rval; +} + +var startTimeInSeconds = new Date().getTime() / 1000; + +var birdLifetime = 20; // lifetime of the birds in seconds! +var range = 1.0; // Over what distance in meters do you want the flock to fly around +var frame = 0; +var moving = false; +var tweeting = 0; + +var CHANCE_OF_MOVING = 0.1; +var CHANCE_OF_TWEETING = 0.05; +var BIRD_GRAVITY = -0.1; +var BIRD_FLAP_SPEED = 10.0; +var BIRD_VELOCITY = 0.5; +var myPosition = MyAvatar.position; +var targetPosition = myPosition; + +var range = 1.0; // Distance around avatar where I can move + +var particleID; +var birdParticleIDs = []; +var birdTweetSounds = []; +var previousFlapOffsets = []; + +function addBird() +{ + // Decide what kind of bird we are + var tweet; + var color; + var size; + var which = Math.random(); + if (which < 0.2) { + tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw"); + color = { red: 100, green: 50, blue: 120 }; + size = 0.08; + } else if (which < 0.4) { + tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/rosyfacedlovebird.raw"); + color = { red: 100, green: 150, blue: 75 }; + size = 0.09; + } else if (which < 0.6) { + tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/saysphoebe.raw"); + color = { red: 84, green: 121, blue: 36 }; + size = 0.05; + } else if (which < 0.8) { + tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw"); + color = { red: 23, green: 197, blue: 230 }; + size = 0.12; + } else { + tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/westernscreechowl.raw"); + color = { red: 50, green: 67, blue: 144 }; + size = 0.15; + } + + var properties = { + lifetime: birdLifetime, + position: randVector(-range, range), + velocity: { x: 0, y: 0, z: 0 }, + gravity: { x: 0, y: BIRD_GRAVITY, z: 0 }, + radius : size, + color: color + }; + + previousFlapOffsets.push(0.0); + birdTweetSounds.push(tweet); + birdParticleIDs.push(Particles.addParticle(properties)); +} + +var numBirds = 5; + +// Generate the birds +for (var i = 0; i < numBirds; i++) { + addBird(); +} + +// Main update function +function updateBirds(deltaTime) { + + // Check to see if we've been running long enough that our birds are dead + var nowTimeInSeconds = new Date().getTime() / 1000; + if ((nowTimeInSeconds - startTimeInSeconds) >= birdLifetime) { + + print("our birds are dying, stop our script"); + Script.stop(); + return; + } + + frame++; + // Only update every third frame + if ((frame % 3) == 0) { + myPosition = MyAvatar.position; + + // Update all the birds + for (var i = 0; i < numBirds; i++) { + particleID = birdParticleIDs[i]; + var properties = Particles.getParticleProperties(particleID); + + // Tweeting behavior + if (tweeting == 0) { + if (Math.random() < CHANCE_OF_TWEETING) { + var options = new AudioInjectionOptions(); + options.position = properties.position; + options.volume = 0.75; + Audio.playSound(birdTweetSounds[i], options); + tweeting = 10; + } + } else { + tweeting -= 1; + } + + // Begin movement by getting a target + if (moving == false) { + if (Math.random() < CHANCE_OF_MOVING) { + targetPosition = vPlus(randVector(-range, range), myPosition); + + if (targetPosition.x < 0) { + targetPosition.x = 0; + } + if (targetPosition.y < 0) { + targetPosition.y = 0; + } + if (targetPosition.z < 0) { + targetPosition.z = 0; + } + if (targetPosition.x > TREE_SCALE) { + targetPosition.x = TREE_SCALE; + } + if (targetPosition.y > TREE_SCALE) { + targetPosition.y = TREE_SCALE; + } + if (targetPosition.z > TREE_SCALE) { + targetPosition.z = TREE_SCALE; + } + + moving = true; + } + } + // If we are moving, move towards the target + if (moving) { + var desiredVelocity = vMinus(targetPosition, properties.position); + desiredVelocity = vScalarMult(vNormalize(desiredVelocity), BIRD_VELOCITY); + + properties.velocity = vInterpolate(properties.velocity, desiredVelocity, 0.2); + // If we are near the target, we should get a new target + if (vLength(vMinus(properties.position, targetPosition)) < (properties.radius / 5.0)) { + moving = false; + } + } + + // Use a cosine wave offset to make it look like its flapping. + var offset = Math.cos(nowTimeInSeconds * BIRD_FLAP_SPEED) * properties.radius; + properties.position.y = properties.position.y + (offset - previousFlapOffsets[i]); + // Change position relative to previous offset. + previousFlapOffsets[i] = offset; + + // Update the particle + Particles.editParticle(particleID, properties); + } + } +} + +// register the call back so it fires before each data send +Script.update.connect(updateBirds); From 79979463896b5be99cfb1bbfda1a07b9b77da849 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 2 Jun 2014 09:43:01 -0700 Subject: [PATCH 2/5] Added particleBirds.js example script --- examples/particleBirds.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/particleBirds.js b/examples/particleBirds.js index b7ecc851ba..4c761f1baa 100644 --- a/examples/particleBirds.js +++ b/examples/particleBirds.js @@ -2,6 +2,7 @@ // particleBirds.js // examples // +// Created by Benjamin Arnold on May 29, 2014 // Copyright 2014 High Fidelity, Inc. // // This sample script creates a swarm of tweeting bird particles that fly around the avatar. @@ -110,7 +111,7 @@ function addBird() color = { red: 50, green: 67, blue: 144 }; size = 0.15; } - +size = 10000; var properties = { lifetime: birdLifetime, position: randVector(-range, range), @@ -125,7 +126,7 @@ function addBird() birdParticleIDs.push(Particles.addParticle(properties)); } -var numBirds = 5; +var numBirds = 1; // Generate the birds for (var i = 0; i < numBirds; i++) { @@ -151,6 +152,7 @@ function updateBirds(deltaTime) { // Update all the birds for (var i = 0; i < numBirds; i++) { + print("Updating"); particleID = birdParticleIDs[i]; var properties = Particles.getParticleProperties(particleID); @@ -214,6 +216,7 @@ function updateBirds(deltaTime) { // Update the particle Particles.editParticle(particleID, properties); + print("Updated yay"); } } } From 863e2c56ae8c4347034004f815e5a6589826dc29 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 2 Jun 2014 10:41:10 -0700 Subject: [PATCH 3/5] Fixed mirror mode for non-headless models. --- interface/src/Application.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0e731fde79..5f9a57f52b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2789,16 +2789,19 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { glm::vec3 absoluteSkeletonTranslation = _myAvatar->getSkeletonModel().getTranslation(); glm::vec3 absoluteFaceTranslation = _myAvatar->getHead()->getFaceModel().getTranslation(); - // get the eye positions relative to the neck and use them to set the face translation - glm::vec3 leftEyePosition, rightEyePosition; - _myAvatar->getHead()->getFaceModel().setTranslation(glm::vec3()); - _myAvatar->getHead()->getFaceModel().getEyePositions(leftEyePosition, rightEyePosition); - _myAvatar->getHead()->getFaceModel().setTranslation((leftEyePosition + rightEyePosition) * -0.5f); - - // get the neck position relative to the body and use it to set the skeleton translation + // get the neck position so we can translate the face relative to it glm::vec3 neckPosition; _myAvatar->getSkeletonModel().setTranslation(glm::vec3()); _myAvatar->getSkeletonModel().getNeckPosition(neckPosition); + + // get the eye position relative to the body + glm::vec3 eyePosition = _myAvatar->getHead()->calculateAverageEyePosition(); + float eyeHeight = eyePosition.y - _myAvatar->getPosition().y; + + // set the translation of the face relative to the neck position + _myAvatar->getHead()->getFaceModel().setTranslation(neckPosition - glm::vec3(0, eyeHeight, 0)); + + // translate the neck relative to the face _myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead()->getFaceModel().getTranslation() - neckPosition); From 9e882957c49368cb6b7c9acd66bf496d09567589 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 2 Jun 2014 10:44:50 -0700 Subject: [PATCH 4/5] Removed debug prints from particleBirds.js --- examples/particleBirds.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/particleBirds.js b/examples/particleBirds.js index 4c761f1baa..326b2cee3d 100644 --- a/examples/particleBirds.js +++ b/examples/particleBirds.js @@ -152,7 +152,6 @@ function updateBirds(deltaTime) { // Update all the birds for (var i = 0; i < numBirds; i++) { - print("Updating"); particleID = birdParticleIDs[i]; var properties = Particles.getParticleProperties(particleID); @@ -216,7 +215,6 @@ function updateBirds(deltaTime) { // Update the particle Particles.editParticle(particleID, properties); - print("Updated yay"); } } } From e669f1d835aa3e610e71f81c8db0eab261ae350b Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 2 Jun 2014 11:41:50 -0700 Subject: [PATCH 5/5] Fixed and tested particle birds --- examples/particleBirds.js | 88 ++++++++++++++------------------------- 1 file changed, 32 insertions(+), 56 deletions(-) diff --git a/examples/particleBirds.js b/examples/particleBirds.js index 326b2cee3d..9492a321ce 100644 --- a/examples/particleBirds.js +++ b/examples/particleBirds.js @@ -11,23 +11,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// Normalizes a vector to unit length -function vNormalize(v) { - var length = vLength(v); - var rval = { x: v.x / length, y: v.y / length, z: v.z / length }; - return rval; -} - // Multiply vector by scalar function vScalarMult(v, s) { var rval = { x: v.x * s, y: v.y * s, z: v.z * s }; return rval; } -function vLength(v) { - return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); -} - function printVector(v) { print(v.x + ", " + v.y + ", " + v.z + "\n"); } @@ -37,23 +26,6 @@ function randVector(a, b) { return rval; } -function vMinus(a, b) { - var rval = { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z }; - return rval; -} - -function vPlus(a, b) { - var rval = { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }; - return rval; -} - -function vCopy(a, b) { - a.x = b.x; - a.y = b.y; - a.z = b.z; - return; -} - // Returns a vector which is fraction of the way between a and b function vInterpolate(a, b, fraction) { var rval = { x: a.x + (b.x - a.x) * fraction, y: a.y + (b.y - a.y) * fraction, z: a.z + (b.z - a.z) * fraction }; @@ -65,8 +37,6 @@ var startTimeInSeconds = new Date().getTime() / 1000; var birdLifetime = 20; // lifetime of the birds in seconds! var range = 1.0; // Over what distance in meters do you want the flock to fly around var frame = 0; -var moving = false; -var tweeting = 0; var CHANCE_OF_MOVING = 0.1; var CHANCE_OF_TWEETING = 0.05; @@ -74,14 +44,21 @@ var BIRD_GRAVITY = -0.1; var BIRD_FLAP_SPEED = 10.0; var BIRD_VELOCITY = 0.5; var myPosition = MyAvatar.position; -var targetPosition = myPosition; var range = 1.0; // Distance around avatar where I can move -var particleID; -var birdParticleIDs = []; -var birdTweetSounds = []; -var previousFlapOffsets = []; +// This is our Bird object +function Bird (particleID, tweetSound, targetPosition) { + this.particleID = particleID; + this.tweetSound = tweetSound; + this.previousFlapOffset = 0; + this.targetPosition = targetPosition; + this.moving = false; + this.tweeting = -1; +} + +// Array of birds +var birds = []; function addBird() { @@ -111,22 +88,19 @@ function addBird() color = { red: 50, green: 67, blue: 144 }; size = 0.15; } -size = 10000; var properties = { lifetime: birdLifetime, - position: randVector(-range, range), + position: Vec3.sum(randVector(-range, range), myPosition), velocity: { x: 0, y: 0, z: 0 }, gravity: { x: 0, y: BIRD_GRAVITY, z: 0 }, radius : size, color: color }; - previousFlapOffsets.push(0.0); - birdTweetSounds.push(tweet); - birdParticleIDs.push(Particles.addParticle(properties)); + birds.push(new Bird(Particles.addParticle(properties), tweet, properties.position)); } -var numBirds = 1; +var numBirds = 30; // Generate the birds for (var i = 0; i < numBirds; i++) { @@ -152,26 +126,26 @@ function updateBirds(deltaTime) { // Update all the birds for (var i = 0; i < numBirds; i++) { - particleID = birdParticleIDs[i]; + particleID = birds[i].particleID; var properties = Particles.getParticleProperties(particleID); // Tweeting behavior - if (tweeting == 0) { + if (birds[i].tweeting == 0) { if (Math.random() < CHANCE_OF_TWEETING) { var options = new AudioInjectionOptions(); options.position = properties.position; options.volume = 0.75; - Audio.playSound(birdTweetSounds[i], options); - tweeting = 10; + Audio.playSound(birds[i].tweetSound, options); + birds[i].tweeting = 10; } } else { - tweeting -= 1; + birds[i].tweeting -= 1; } // Begin movement by getting a target - if (moving == false) { + if (birds[i].moving == false) { if (Math.random() < CHANCE_OF_MOVING) { - targetPosition = vPlus(randVector(-range, range), myPosition); + var targetPosition = Vec3.sum(randVector(-range, range), myPosition); if (targetPosition.x < 0) { targetPosition.x = 0; @@ -192,26 +166,28 @@ function updateBirds(deltaTime) { targetPosition.z = TREE_SCALE; } - moving = true; + birds[i].targetPosition = targetPosition; + + birds[i].moving = true; } } // If we are moving, move towards the target - if (moving) { - var desiredVelocity = vMinus(targetPosition, properties.position); - desiredVelocity = vScalarMult(vNormalize(desiredVelocity), BIRD_VELOCITY); + if (birds[i].moving) { + var desiredVelocity = Vec3.subtract(birds[i].targetPosition, properties.position); + desiredVelocity = vScalarMult(Vec3.normalize(desiredVelocity), BIRD_VELOCITY); properties.velocity = vInterpolate(properties.velocity, desiredVelocity, 0.2); // If we are near the target, we should get a new target - if (vLength(vMinus(properties.position, targetPosition)) < (properties.radius / 5.0)) { - moving = false; + if (Vec3.length(Vec3.subtract(properties.position, birds[i].targetPosition)) < (properties.radius / 5.0)) { + birds[i].moving = false; } } // Use a cosine wave offset to make it look like its flapping. var offset = Math.cos(nowTimeInSeconds * BIRD_FLAP_SPEED) * properties.radius; - properties.position.y = properties.position.y + (offset - previousFlapOffsets[i]); + properties.position.y = properties.position.y + (offset - birds[i].previousFlapOffset); // Change position relative to previous offset. - previousFlapOffsets[i] = offset; + birds[i].previousFlapOffset = offset; // Update the particle Particles.editParticle(particleID, properties);