mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 05:17:24 +02:00
airship from hack project
This commit is contained in:
parent
5230d3a705
commit
472dd07625
2 changed files with 363 additions and 0 deletions
303
examples/airship/airship.js
Normal file
303
examples/airship/airship.js
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
//
|
||||||
|
// airship.js
|
||||||
|
//
|
||||||
|
// Animates a pirate airship that chases people and shoots cannonballs at them
|
||||||
|
//
|
||||||
|
// Created by Philip Rosedale on March 7, 2016
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var entityID,
|
||||||
|
minimumDelay = 100, // milliseconds
|
||||||
|
distanceScale = 2.0, // distance at which 100% chance of hopping
|
||||||
|
timeScale = 300.0, // crash
|
||||||
|
hopStrength = 0.4, // meters / second
|
||||||
|
spotlight = null,
|
||||||
|
wantDebug = false,
|
||||||
|
timeoutID = undefined,
|
||||||
|
bullet = null,
|
||||||
|
particles = null,
|
||||||
|
nearbyAvatars = 0,
|
||||||
|
nearbyAvatarsInRange = 0,
|
||||||
|
averageAvatarLocation = { x: 0, y: 0, z: 0 },
|
||||||
|
properties,
|
||||||
|
lightTimer = 0,
|
||||||
|
lightTimeoutID = undefined,
|
||||||
|
audioInjector = null
|
||||||
|
|
||||||
|
var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||||
|
var cannonSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "philip/cannonShot.wav");
|
||||||
|
var explosionSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "philip/explosion.wav");
|
||||||
|
|
||||||
|
var NO_SHOOT_COLOR = { red: 100, green: 100, blue: 100 };
|
||||||
|
|
||||||
|
var MAX_TARGET_RANGE = 200;
|
||||||
|
|
||||||
|
function printDebug(message) {
|
||||||
|
if (wantDebug) {
|
||||||
|
print(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var LIGHT_UPDATE_INTERVAL = 50;
|
||||||
|
var LIGHT_LIFETIME = 700;
|
||||||
|
|
||||||
|
function randomVector(size) {
|
||||||
|
return { x: (Math.random() - 0.5) * size,
|
||||||
|
y: (Math.random() - 0.5) * size,
|
||||||
|
z: (Math.random() - 0.5) * size };
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeLight(parent, position, colorDivisor) {
|
||||||
|
// Create a flickering light somewhere for a while
|
||||||
|
if (spotlight !== null) {
|
||||||
|
// light still exists, do nothing
|
||||||
|
printDebug("light still there");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printDebug("making light");
|
||||||
|
|
||||||
|
var colorIndex = 180 + Math.random() * 50;
|
||||||
|
|
||||||
|
spotlight = Entities.addEntity({
|
||||||
|
type: "Light",
|
||||||
|
name: "Test Light",
|
||||||
|
intensity: 50.0,
|
||||||
|
falloffRadius: 20.0,
|
||||||
|
dimensions: {
|
||||||
|
x: 150,
|
||||||
|
y: 150,
|
||||||
|
z: 150
|
||||||
|
},
|
||||||
|
position: position,
|
||||||
|
parentID: parent,
|
||||||
|
color: {
|
||||||
|
red: colorIndex,
|
||||||
|
green: colorIndex / colorDivisor,
|
||||||
|
blue: 0
|
||||||
|
},
|
||||||
|
lifetime: LIGHT_LIFETIME * 2
|
||||||
|
});
|
||||||
|
|
||||||
|
lightTimer = 0;
|
||||||
|
lightTimeoutID = Script.setTimeout(updateLight, LIGHT_UPDATE_INTERVAL);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
function updateLight() {
|
||||||
|
lightTimer += LIGHT_UPDATE_INTERVAL;
|
||||||
|
if ((spotlight !== null) && (lightTimer > LIGHT_LIFETIME)) {
|
||||||
|
printDebug("deleting light!");
|
||||||
|
Entities.deleteEntity(spotlight);
|
||||||
|
spotlight = null;
|
||||||
|
} else {
|
||||||
|
Entities.editEntity(spotlight, {
|
||||||
|
intensity: 5 + Math.random() * 50,
|
||||||
|
falloffRadius: 5 + Math.random() * 10
|
||||||
|
});
|
||||||
|
lightTimeoutID = Script.setTimeout(updateLight, LIGHT_UPDATE_INTERVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function move() {
|
||||||
|
|
||||||
|
var HOVER_DISTANCE = 30.0;
|
||||||
|
var RUN_TOWARD_STRENGTH = 0.002;
|
||||||
|
var VELOCITY_FOLLOW_RATE = 0.01;
|
||||||
|
|
||||||
|
var range = Vec3.distance(properties.position, averageAvatarLocation);
|
||||||
|
var impulse = { x: 0, y: 0, z: 0 };
|
||||||
|
|
||||||
|
// move in the XZ plane
|
||||||
|
var away = Vec3.subtract(properties.position, averageAvatarLocation);
|
||||||
|
away.y = 0.0;
|
||||||
|
|
||||||
|
if (range > HOVER_DISTANCE) {
|
||||||
|
impulse = Vec3.multiply(-RUN_TOWARD_STRENGTH, Vec3.normalize(away));
|
||||||
|
}
|
||||||
|
|
||||||
|
var rotation = Quat.rotationBetween(Vec3.UNIT_NEG_Z, properties.velocity);
|
||||||
|
Entities.editEntity(entityID, {velocity: Vec3.sum(properties.velocity, impulse), rotation: Quat.mix(properties.rotation, rotation, VELOCITY_FOLLOW_RATE)});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function countAvatars() {
|
||||||
|
var workQueue = AvatarList.getAvatarIdentifiers();
|
||||||
|
var averageLocation = {x: 0, y: 0, z: 0};
|
||||||
|
var summed = 0;
|
||||||
|
var inRange = 0;
|
||||||
|
for (var i = 0; i < workQueue.length; i++) {
|
||||||
|
var avatar = AvatarList.getAvatar(workQueue[i]), avatarPosition = avatar && avatar.position;
|
||||||
|
if (avatarPosition) {
|
||||||
|
averageLocation = Vec3.sum(averageLocation, avatarPosition);
|
||||||
|
summed++;
|
||||||
|
if (Vec3.distance(avatarPosition, properties.position) < MAX_TARGET_RANGE) {
|
||||||
|
inRange++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (summed > 0) {
|
||||||
|
averageLocation = Vec3.multiply(1 / summed, averageLocation);
|
||||||
|
}
|
||||||
|
nearbyAvatars = summed;
|
||||||
|
nearbyAvatarsInRange = inRange;
|
||||||
|
averageAvatarLocation = averageLocation;
|
||||||
|
|
||||||
|
//printDebug(" Avatars: " + summed + "in range: " + nearbyAvatarsInRange);
|
||||||
|
//Vec3.print(" location: ", averageAvatarLocation);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shoot() {
|
||||||
|
if (bullet !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Vec3.distance(MyAvatar.position, properties.position) > MAX_TARGET_RANGE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var SPEED = 7.5;
|
||||||
|
var GRAVITY = -2.0;
|
||||||
|
var DIAMETER = 0.5;
|
||||||
|
var direction = Vec3.subtract(MyAvatar.position, properties.position);
|
||||||
|
var range = Vec3.distance(MyAvatar.position, properties.position);
|
||||||
|
var timeOfFlight = range / SPEED;
|
||||||
|
|
||||||
|
var fall = 0.5 * -GRAVITY * (timeOfFlight * timeOfFlight);
|
||||||
|
|
||||||
|
var velocity = Vec3.multiply(SPEED, Vec3.normalize(direction));
|
||||||
|
velocity.y += 0.5 * fall / timeOfFlight * 2.0;
|
||||||
|
|
||||||
|
var DISTANCE_TO_DECK = 3;
|
||||||
|
var bulletStart = properties.position;
|
||||||
|
bulletStart.y -= DISTANCE_TO_DECK;
|
||||||
|
|
||||||
|
makeLight(entityID, Vec3.sum(properties.position, randomVector(10.0)), 2);
|
||||||
|
|
||||||
|
bullet = Entities.addEntity({
|
||||||
|
type: "Sphere",
|
||||||
|
name: "cannonball",
|
||||||
|
position: properties.position,
|
||||||
|
dimensions: { x: DIAMETER, y: DIAMETER, z: DIAMETER },
|
||||||
|
color: { red: 10, green: 10, blue: 10 },
|
||||||
|
velocity: velocity,
|
||||||
|
damping: 0.01,
|
||||||
|
dynamic: true,
|
||||||
|
ignoreForCollisions: true,
|
||||||
|
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||||
|
lifetime: timeOfFlight * 2
|
||||||
|
});
|
||||||
|
|
||||||
|
Audio.playSound(cannonSound, {
|
||||||
|
position: Vec3.sum(MyAvatar.position, velocity),
|
||||||
|
volume: 1.0
|
||||||
|
});
|
||||||
|
makeParticles(properties.position, bullet, timeOfFlight * 2);
|
||||||
|
Script.setTimeout(explode, timeOfFlight * 1000);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function explode() {
|
||||||
|
var properties = Entities.getEntityProperties(bullet);
|
||||||
|
var direction = Vec3.normalize(Vec3.subtract(MyAvatar.position, properties.position));
|
||||||
|
makeLight(null, properties.position, 10);
|
||||||
|
Audio.playSound(explosionSound, { position: Vec3.sum(MyAvatar.position, direction), volume: 1.0 });
|
||||||
|
bullet = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function maybe() { // every user checks their distance and tries to claim if close enough.
|
||||||
|
var PROBABILITY_OF_SHOOT = 0.015;
|
||||||
|
properties = Entities.getEntityProperties(entityID);
|
||||||
|
countAvatars();
|
||||||
|
|
||||||
|
if (nearbyAvatars && (Math.random() < 1 / nearbyAvatars)) {
|
||||||
|
move();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nearbyAvatarsInRange && (Math.random() < PROBABILITY_OF_SHOOT / nearbyAvatarsInRange)) {
|
||||||
|
shoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeToNextCheck = 33;
|
||||||
|
timeoutID = Script.setTimeout(maybe, timeToNextCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.preload = function (givenEntityID) {
|
||||||
|
printDebug("preload airship v1...");
|
||||||
|
|
||||||
|
entityID = givenEntityID;
|
||||||
|
properties = Entities.getEntityProperties(entityID);
|
||||||
|
timeoutID = Script.setTimeout(maybe, minimumDelay);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.unload = function () {
|
||||||
|
printDebug("unload airship...");
|
||||||
|
if (timeoutID !== undefined) {
|
||||||
|
Script.clearTimeout(timeoutID);
|
||||||
|
}
|
||||||
|
if (lightTimeoutID !== undefined) {
|
||||||
|
Script.clearTimeout(lightTimeoutID);
|
||||||
|
}
|
||||||
|
if (spotlight !== null) {
|
||||||
|
Entities.deleteEntity(spotlight);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function makeParticles(position, parent, lifespan) {
|
||||||
|
particles = Entities.addEntity({
|
||||||
|
type: 'ParticleEffect',
|
||||||
|
position: position,
|
||||||
|
parentID: parent,
|
||||||
|
color: {
|
||||||
|
red: 70,
|
||||||
|
green: 70,
|
||||||
|
blue: 70
|
||||||
|
},
|
||||||
|
isEmitting: 1,
|
||||||
|
maxParticles: 1000,
|
||||||
|
lifetime: lifespan,
|
||||||
|
lifespan: lifespan / 3,
|
||||||
|
emitRate: 80,
|
||||||
|
emitSpeed: 0,
|
||||||
|
speedSpread: 1.0,
|
||||||
|
emitRadiusStart: 1,
|
||||||
|
polarStart: -Math.PI/8,
|
||||||
|
polarFinish: Math.PI/8,
|
||||||
|
azimuthStart: -Math.PI/4,
|
||||||
|
azimuthFinish: Math.PI/4,
|
||||||
|
emitAcceleration: { x: 0, y: 0, z: 0 },
|
||||||
|
particleRadius: 0.25,
|
||||||
|
radiusSpread: 0.1,
|
||||||
|
radiusStart: 0.3,
|
||||||
|
radiusFinish: 0.15,
|
||||||
|
colorSpread: {
|
||||||
|
red: 100,
|
||||||
|
green: 100,
|
||||||
|
blue: 0
|
||||||
|
},
|
||||||
|
colorStart: {
|
||||||
|
red: 125,
|
||||||
|
green: 125,
|
||||||
|
blue: 125
|
||||||
|
},
|
||||||
|
colorFinish: {
|
||||||
|
red: 10,
|
||||||
|
green: 10,
|
||||||
|
blue: 10
|
||||||
|
},
|
||||||
|
alpha: 0.5,
|
||||||
|
alphaSpread: 0,
|
||||||
|
alphaStart: 1,
|
||||||
|
alphaFinish: 0,
|
||||||
|
emitterShouldTrail: true,
|
||||||
|
textures: 'https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
60
examples/airship/makeAirship.js
Normal file
60
examples/airship/makeAirship.js
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
// makeAirship.js
|
||||||
|
//
|
||||||
|
// // Created by Philip Rosedale on March 7, 2016
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
// A firefly which is animated by passerbys. It's physical, no gravity, periodic forces applied.
|
||||||
|
// If a firefly is found to
|
||||||
|
|
||||||
|
var SIZE = 0.2;
|
||||||
|
var TYPE = "Model"; // Right now this can be "Box" or "Model" or "Sphere"
|
||||||
|
|
||||||
|
var MODEL_URL = "https://s3.amazonaws.com/hifi-public/philip/airship_compact.fbx"
|
||||||
|
|
||||||
|
var MODEL_DIMENSION = { x: 19.257, y: 24.094, z: 40.3122 };
|
||||||
|
//var ENTITY_URL = "https://s3.amazonaws.com/hifi-public/scripts/airship/airship.js?"+Math.random();
|
||||||
|
var ENTITY_URL = "file:///c:/users/dev/philip/examples/airship/airship.js?"+Math.random();
|
||||||
|
|
||||||
|
var LIFETIME = 3600 * 48;
|
||||||
|
|
||||||
|
var GRAVITY = { x: 0, y: 0, z: 0 };
|
||||||
|
var DAMPING = 0.05;
|
||||||
|
var ANGULAR_DAMPING = 0.01;
|
||||||
|
|
||||||
|
var collidable = true;
|
||||||
|
var gravity = true;
|
||||||
|
|
||||||
|
var HOW_FAR_IN_FRONT_OF_ME = 30;
|
||||||
|
var HOW_FAR_ABOVE_ME = 15;
|
||||||
|
|
||||||
|
var leaveBehind = true;
|
||||||
|
|
||||||
|
var shipLocation = Vec3.sum(MyAvatar.position, Vec3.multiply(HOW_FAR_IN_FRONT_OF_ME, Quat.getFront(Camera.orientation)));
|
||||||
|
shipLocation.y += HOW_FAR_ABOVE_ME;
|
||||||
|
|
||||||
|
|
||||||
|
var airship = Entities.addEntity({
|
||||||
|
type: TYPE,
|
||||||
|
modelURL: MODEL_URL,
|
||||||
|
name: "airship",
|
||||||
|
position: shipLocation,
|
||||||
|
dimensions: (TYPE == "Model") ? MODEL_DIMENSION : { x: SIZE, y: SIZE, z: SIZE },
|
||||||
|
damping: DAMPING,
|
||||||
|
angularDamping: ANGULAR_DAMPING,
|
||||||
|
gravity: (gravity ? GRAVITY : { x: 0, y: 0, z: 0}),
|
||||||
|
dynamic: collidable,
|
||||||
|
lifetime: LIFETIME,
|
||||||
|
animation: {url: MODEL_URL, running: true, currentFrame: 0, loop: true},
|
||||||
|
script: ENTITY_URL
|
||||||
|
});
|
||||||
|
|
||||||
|
function scriptEnding() {
|
||||||
|
if (!leaveBehind) {
|
||||||
|
Entities.deleteEntity(airship);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(scriptEnding);
|
Loading…
Reference in a new issue