History

add some new tutorial JS files

Andrew Meadows committed 10 years ago

Commit 50117e21 parent 3a2cb3713 files · +1613 −0Browse files

Showing the changes to scripts/tutorials/entity_scripts (4 of 13 files). Show all

Unified · Split

4 files
  1. scripts/tutorials/entity_scripts/flashlight.js +268 −0
  2. scripts/tutorials/entity_scripts/golfClub.js +150 −0
  3. scripts/tutorials/entity_scripts/pingPongGun.js +177 −0
  4. scripts/tutorials/entity_scripts/soundMaker.js +28 −0
scripts/tutorials/entity_scripts/flashlight.js+268−0
@@ -0,0 +1,268 @@
1+//
2+// flashlight.js
3+//
4+// Script Type: Entity
5+//
6+// Created by Sam Gateau on 9/9/15.
7+// Additions by James B. Pollack @imgntn on 9/21/2015
8+// Copyright 2015 High Fidelity, Inc.
9+//
10+// This is a toy script that can be added to the Flashlight model entity:
11+// "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"
12+// that creates a spotlight attached with the flashlight model while the entity is grabbed
13+//
14+// Distributed under the Apache License, Version 2.0.
15+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
16+//
17+
18+(function() {
19+
20+ var ON_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_on.wav';
21+ var OFF_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_off.wav';
22+
23+ //we are creating lights that we don't want to get stranded so lets make sure that we can get rid of them
24+ //if you're going to be using this in a dungeon or something and holding it for a long time, increase this lifetime value.
25+ var LIFETIME = 100;
26+
27+
28+ // this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
29+ // our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
30+ function Flashlight() {
31+ return;
32+ }
33+
34+ //if the trigger value goes below this while held, the flashlight will turn off. if it goes above, it will
35+ var DISABLE_LIGHT_THRESHOLD = 0.7;
36+
37+ // These constants define the Spotlight position and orientation relative to the model
38+ var MODEL_LIGHT_POSITION = {
39+ x: 0,
40+ y: -0.3,
41+ z: 0
42+ };
43+ var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, {
44+ x: 1,
45+ y: 0,
46+ z: 0
47+ });
48+
49+ //there's another small point light we put at the end of the flashlight to make the end glow
50+
51+ var GLOW_LIGHT_POSITION = {
52+ x: 0,
53+ y: -0.1,
54+ z: 0
55+ };
56+
57+ // Evaluate the world light entity positions and orientations from the model ones
58+ function evalLightWorldTransform(modelPosition, modelRotation) {
59+
60+ return {
61+ p: Vec3.sum(modelPosition, Vec3.multiplyQbyV(modelRotation, MODEL_LIGHT_POSITION)),
62+ q: Quat.multiply(modelRotation, MODEL_LIGHT_ROTATION)
63+ };
64+ }
65+
66+ function glowLightWorldTransform(modelPosition, modelRotation) {
67+ return {
68+ p: Vec3.sum(modelPosition, Vec3.multiplyQbyV(modelRotation, GLOW_LIGHT_POSITION)),
69+ q: Quat.multiply(modelRotation, MODEL_LIGHT_ROTATION)
70+ };
71+ }
72+
73+ Flashlight.prototype = {
74+ lightOn: false,
75+ hand: null,
76+ whichHand: null,
77+ hasSpotlight: false,
78+ spotlight: null,
79+
80+ startNearGrab: function(entityID, args) {
81+ //this first second parameter in this method contains arguments from the grab script, the first of which is the 'hand' that was used to start the grab
82+ this.hand = args[0];
83+ if (!this.hasSpotlight) {
84+
85+ var modelProperties = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
86+ var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation);
87+ var glowLightTransform = glowLightWorldTransform(modelProperties.position, modelProperties.rotation);
88+
89+
90+ //this light casts the beam
91+ this.spotlight = Entities.addEntity({
92+ type: "Light",
93+ isSpotlight: true,
94+ dimensions: {
95+ x: 2,
96+ y: 2,
97+ z: 20
98+ },
99+ parentID: this.entityID,
100+ color: {
101+ red: 255,
102+ green: 255,
103+ blue: 255
104+ },
105+ intensity: 1,
106+ falloffRadius:0.9,
107+ exponent: 0.5,
108+ cutoff: 20,
109+ lifetime: LIFETIME,
110+ position: lightTransform.p,
111+ rotation: lightTransform.q,
112+ });
113+
114+ //this light creates the effect of a bulb at the end of the flashlight
115+ this.glowLight = Entities.addEntity({
116+ type: "Light",
117+ dimensions: {
118+ x: 0.25,
119+ y: 0.25,
120+ z: 0.25
121+ },
122+ parentID: this.entityID,
123+ isSpotlight: false,
124+ color: {
125+ red: 255,
126+ green: 255,
127+ blue: 255
128+ },
129+ exponent: 0,
130+ intensity:1.0,
131+ falloffRadius:0.3,
132+ lifetime: LIFETIME,
133+ cutoff: 90, // in degrees
134+ position: glowLightTransform.p,
135+ rotation: glowLightTransform.q,
136+
137+ });
138+
139+ this.hasSpotlight = true;
140+
141+ }
142+
143+ },
144+ startEquip: function(id, params) {
145+ this.startNearGrab(id, params);
146+ },
147+
148+ setWhichHand: function() {
149+ this.whichHand = this.hand;
150+ },
151+
152+ continueNearGrab: function() {
153+ if (this.whichHand === null) {
154+ //only set the active hand once -- if we always read the current hand, our 'holding' hand will get overwritten
155+ this.setWhichHand();
156+ } else {
157+ this.changeLightWithTriggerPressure(this.whichHand);
158+ }
159+ },
160+ continueEquip: function(entityID, args) {
161+ this.continueNearGrab(entityID, args);
162+ },
163+
164+ releaseGrab: function(entityID, args) {
165+ //delete the lights and reset state
166+ if (this.hasSpotlight) {
167+ Entities.deleteEntity(this.spotlight);
168+ Entities.deleteEntity(this.glowLight);
169+ this.hasSpotlight = false;
170+ this.glowLight = null;
171+ this.spotlight = null;
172+ this.whichHand = null;
173+ this.lightOn = false;
174+ }
175+ },
176+
177+ releaseEquip: function(entityID, args) {
178+ this.releaseGrab(entityID, args);
179+ },
180+
181+ changeLightWithTriggerPressure: function(flashLightHand) {
182+ //read the value of the controller trigger and use it to turn the light on / off when the value reaches certain thresholds
183+ if (flashLightHand === 'LEFT') {
184+ this.triggerValue = Controller.getValue(Controller.Standard.LT);
185+ }
186+ if (flashLightHand === 'RIGHT') {
187+ this.triggerValue = Controller.getValue(Controller.Standard.RT);
188+
189+ }
190+
191+ if (this.triggerValue < DISABLE_LIGHT_THRESHOLD && this.lightOn === true) {
192+ this.turnLightOff();
193+ } else if (this.triggerValue >= DISABLE_LIGHT_THRESHOLD && this.lightOn === false) {
194+ this.turnLightOn();
195+ }
196+ return;
197+ },
198+
199+ turnLightOff: function() {
200+ this.playSoundAtCurrentPosition(false);
201+ Entities.editEntity(this.spotlight, {
202+ intensity: 0
203+ });
204+ Entities.editEntity(this.glowLight, {
205+ intensity: 0
206+ });
207+ this.lightOn = false;
208+ },
209+
210+ turnLightOn: function() {
211+ this.playSoundAtCurrentPosition(true);
212+
213+ Entities.editEntity(this.glowLight, {
214+ intensity: 2
215+ });
216+ Entities.editEntity(this.spotlight, {
217+ intensity: 2
218+ });
219+ this.lightOn = true;
220+ },
221+
222+ playSoundAtCurrentPosition: function(playOnSound) {
223+ //when someone grabs, play a clicking noise for turning the flashlight on/off
224+ var position = Entities.getEntityProperties(this.entityID, "position").position;
225+
226+ var audioProperties = {
227+ volume: 0.25,
228+ position: position
229+ };
230+
231+ if (playOnSound) {
232+ Audio.playSound(this.ON_SOUND, audioProperties);
233+ } else {
234+ Audio.playSound(this.OFF_SOUND, audioProperties);
235+ }
236+ },
237+
238+ preload: function(entityID) {
239+ // preload() will be called when the entity has become visible (or known) to the interface
240+ // it gives us a chance to set our local JavaScript object up. In this case it means:
241+ // * remembering our entityID, so we can access it in cases where we're called without an entityID
242+ // * preloading sounds
243+ this.entityID = entityID;
244+ this.ON_SOUND = SoundCache.getSound(ON_SOUND_URL);
245+ this.OFF_SOUND = SoundCache.getSound(OFF_SOUND_URL);
246+
247+ },
248+
249+ unload: function() {
250+ // unload() will be called when our entity is no longer available. It may be because we were deleted,
251+ // or because we've left the domain or quit the application.
252+ if (this.hasSpotlight) {
253+ Entities.deleteEntity(this.spotlight);
254+ Entities.deleteEntity(this.glowLight);
255+ this.hasSpotlight = false;
256+ this.glowLight = null;
257+ this.spotlight = null;
258+ this.whichHand = null;
259+ this.lightOn = false;
260+ }
261+
262+ },
263+
264+ };
265+
266+ // entity should return a newly constructed object of our type
267+ return new Flashlight();
268+});
scripts/tutorials/entity_scripts/golfClub.js+150−0
@@ -0,0 +1,150 @@
1+//
2+// golfClub.js
3+//
4+// Created by Philip Rosedale on April 11, 2016.
5+// Copyright 2016 High Fidelity, Inc.
6+//
7+//
8+// Distributed under the Apache License, Version 2.0.
9+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+//
11+// A simple golf club. If you have equipped it, and pull trigger, it will either make
12+// you a new golf ball, or take you to your ball if one is not made.
13+
14+(function() {
15+ var ball = null;
16+ var collisionSoundURL = "http://hifi-production.s3.amazonaws.com/tutorials/golfClub/collision1.wav";
17+ var triggerState = false;
18+ var BALL_GRAVITY = -9.8;
19+ var BALL_START_VELOCITY = 0.1;
20+ var BALL_MAX_RANGE = 10;
21+ var BALL_DROP_DISTANCE = 0.6;
22+ var BALL_DIAMETER = 0.07;
23+ var BALL_LIFETIME = 3600;
24+ var MAX_BRAKING_SPEED = 0.2;
25+ var BALL_BRAKING_RATE = 0.5;
26+
27+ var TRIGGER_CONTROLS = [
28+ Controller.Standard.LT,
29+ Controller.Standard.RT,
30+ ];
31+
32+ function triggerPulled(hand) {
33+ // Return true if the trigger has just been pulled
34+ var triggerValue = Controller.getValue(TRIGGER_CONTROLS[hand]);
35+ var oldTriggerState = triggerState;
36+ var TRIGGER_PULL_THRESHOLD = 0.5;
37+ var TRIGGER_RELEASE_THRESHOLD = 0.4;
38+ if (triggerValue > TRIGGER_PULL_THRESHOLD) {
39+ triggerState = true;
40+ } else if (triggerValue < TRIGGER_RELEASE_THRESHOLD) {
41+ triggerState = false;
42+ }
43+ return (triggerState && (oldTriggerState != triggerState));
44+ }
45+
46+ function ballPosition(ball) {
47+ // return the position of this entity
48+ var properties = Entities.getEntityProperties(ball, ['position']);
49+ if (!properties) {
50+ return null;
51+ } else {
52+ return properties.position;
53+ }
54+ }
55+
56+ function gotoPointOnGround(position, away) {
57+ // Position yourself facing in the direction you were originally facing, but with a
58+ // point on the ground *away* meters from *position* and in front of you.
59+
60+ var offset = Quat.getFront(MyAvatar.orientation);
61+ offset.y = 0.0;
62+ offset = Vec3.multiply(-away, Vec3.normalize(offset));
63+ var newAvatarPosition = Vec3.sum(position, offset);
64+
65+ // Assuming position is on ground, put me distance from eyes to hips higher, plus a 50% adjust for longer legs.
66+ // TODO: Need avatar callback for exact foot-to-hips height.
67+
68+ var halfHeight = avatarHalfHeight();
69+ print("Height = " + halfHeight);
70+ newAvatarPosition.y += (halfHeight * 1.5);
71+ MyAvatar.position = newAvatarPosition;
72+ }
73+
74+ function inFrontOfMe() {
75+ return Vec3.sum(MyAvatar.position, Vec3.multiply(BALL_DROP_DISTANCE, Quat.getFront(MyAvatar.orientation)));
76+ }
77+
78+ function avatarHalfHeight() {
79+ return MyAvatar.getDefaultEyePosition().y - MyAvatar.position.y;
80+ }
81+
82+ function brakeBall(ball) {
83+ // Check the ball's velocity and slow it down if beyond a threshold
84+ var properties = Entities.getEntityProperties(ball, ['velocity']);
85+ if (properties) {
86+ var velocity = Vec3.length(properties.velocity);
87+ if ((velocity > 0) && (velocity < MAX_BRAKING_SPEED)) {
88+ Entities.editEntity(ball, {
89+ velocity: Vec3.multiply(BALL_BRAKING_RATE, properties.velocity)
90+ });
91+ }
92+ }
93+ }
94+
95+ function makeBall(position) {
96+ // Create a new sphere entity
97+ ball = Entities.addEntity({
98+ type: "Sphere",
99+ position: position,
100+ color: {
101+ red: 255,
102+ green: 255,
103+ blue: 255
104+ },
105+ dimensions: {
106+ x: BALL_DIAMETER,
107+ y: BALL_DIAMETER,
108+ z: BALL_DIAMETER
109+ },
110+ gravity: {
111+ x: 0,
112+ y: BALL_GRAVITY,
113+ z: 0
114+ },
115+ velocity: {
116+ x: 0,
117+ y: BALL_START_VELOCITY,
118+ z: 0
119+ },
120+ friction: 0.5,
121+ restitution: 0.5,
122+ shapeType: "sphere",
123+ dynamic: true,
124+ lifetime: BALL_LIFETIME,
125+ collisionSoundURL: collisionSoundURL
126+ });
127+ }
128+
129+ function checkClub(params) {
130+ var hand = params[0] == "left" ? 0 : 1;
131+ var makeNewBall = false;
132+ if (triggerPulled(hand)) {
133+ // If trigger just pulled, either drop new ball or go to existing one
134+ var position = ballPosition(ball);
135+ if (position && (Vec3.distance(MyAvatar.position, position) < BALL_MAX_RANGE)) {
136+ gotoPointOnGround(position, BALL_DROP_DISTANCE);
137+ } else {
138+ Entities.deleteEntity(ball);
139+ makeBall(inFrontOfMe());
140+ }
141+ }
142+ brakeBall(ball);
143+ }
144+
145+ this.continueEquip = function(id, params) {
146+ // While holding the club, continuously check for trigger pull and brake ball if moving.
147+ checkClub(params);
148+ }
149+
150+});
scripts/tutorials/entity_scripts/pingPongGun.js+177−0
@@ -0,0 +1,177 @@
1+// pingPongGun.js
2+//
3+// Script Type: Entity
4+// Created by James B. Pollack @imgntn on 9/21/2015
5+// Copyright 2015 High Fidelity, Inc.
6+//
7+// This script shoots a ping pong ball.
8+// Distributed under the Apache License, Version 2.0.
9+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+//
11+
12+(function() {
13+ var _this = this;
14+
15+ var SHOOTING_SOUND_URL = 'http://hifi-production.s3.amazonaws.com/tutorials/pingPongGun/pong_sound.wav';
16+
17+ function PingPongGun() {
18+ return;
19+ }
20+
21+ //if the trigger value goes below this value, reload the gun.
22+ var RELOAD_THRESHOLD = 0.95;
23+ var GUN_TIP_FWD_OFFSET = -0.6;
24+ var GUN_TIP_UP_OFFSET = 0.12;
25+ var GUN_FORCE = 9;
26+ var BALL_RESTITUTION = 0.6;
27+ var BALL_LINEAR_DAMPING = 0.4;
28+ var BALL_GRAVITY = {
29+ x: 0,
30+ y: -4.8,
31+ z: 0
32+ };
33+
34+ var PING_PONG_GUN_GRAVITY = {
35+ x: 0,
36+ y: -10,
37+ z: 0
38+ };
39+
40+ var BALL_DIMENSIONS = {
41+ x: 0.04,
42+ y: 0.04,
43+ z: 0.04
44+ };
45+
46+
47+ var BALL_COLOR = {
48+ red: 255,
49+ green: 255,
50+ blue: 255
51+ };
52+
53+ var TRIGGER_CONTROLS = [
54+ Controller.Standard.LT,
55+ Controller.Standard.RT,
56+ ];
57+
58+
59+ PingPongGun.prototype = {
60+ hand: null,
61+ gunTipPosition: null,
62+ canShoot: false,
63+ canShootTimeout: null,
64+
65+ startEquip: function(entityID, args) {
66+ this.hand = args[0] == "left" ? 0 : 1;
67+ },
68+
69+ continueEquip: function(entityID, args) {
70+ if (this.canShootTimeout !== null) {
71+ Script.clearTimeout(this.canShootTimeout);
72+ }
73+ this.checkTriggerPressure(this.hand);
74+ },
75+
76+ releaseEquip: function(entityID, args) {
77+ var _this = this;
78+ this.canShootTimeout = Script.setTimeout(function() {
79+ _this.canShoot = false;
80+ }, 250);
81+ },
82+
83+ checkTriggerPressure: function(gunHand) {
84+ this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[gunHand]);
85+ if (this.triggerValue < RELOAD_THRESHOLD) {
86+ this.canShoot = true;
87+ } else if (this.triggerValue >= RELOAD_THRESHOLD && this.canShoot === true) {
88+ var gunProperties = Entities.getEntityProperties(this.entityID, ["position", "rotation"]);
89+ this.shootBall(gunProperties);
90+ this.canShoot = false;
91+ }
92+
93+ return;
94+ },
95+
96+ shootBall: function(gunProperties) {
97+ var forwardVec = Quat.getFront(Quat.multiply(gunProperties.rotation, Quat.fromPitchYawRollDegrees(0, 180, 0)));
98+ forwardVec = Vec3.normalize(forwardVec);
99+ forwardVec = Vec3.multiply(forwardVec, GUN_FORCE);
100+
101+ var properties = {
102+ name: 'Tutorial Ping Pong Ball',
103+ shapeType: 'sphere',
104+ type: 'Sphere',
105+ color: BALL_COLOR,
106+ dimensions: BALL_DIMENSIONS,
107+ damping: BALL_LINEAR_DAMPING,
108+ gravity: BALL_GRAVITY,
109+ restitution: BALL_RESTITUTION,
110+ dynamic: true,
111+ rotation: gunProperties.rotation,
112+ position: this.getGunTipPosition(gunProperties),
113+ gravity: PING_PONG_GUN_GRAVITY,
114+ velocity: forwardVec,
115+ lifetime: 10
116+ };
117+
118+ Entities.addEntity(properties);
119+
120+ this.playSoundAtCurrentPosition(gunProperties.position);
121+ },
122+
123+ playSoundAtCurrentPosition: function(position) {
124+ var audioProperties = {
125+ volume: 0.2,
126+ position: position
127+ };
128+
129+ Audio.playSound(this.SHOOTING_SOUND, audioProperties);
130+ },
131+
132+ getGunTipPosition: function(properties) {
133+ //the tip of the gun is going to be in a different place than the center, so we move in space relative to the model to find that position
134+ var frontVector = Quat.getFront(properties.rotation);
135+ var frontOffset = Vec3.multiply(frontVector, GUN_TIP_FWD_OFFSET);
136+ var upVector = Quat.getUp(properties.rotation);
137+ var upOffset = Vec3.multiply(upVector, GUN_TIP_UP_OFFSET);
138+
139+ var gunTipPosition = Vec3.sum(properties.position, frontOffset);
140+ gunTipPosition = Vec3.sum(gunTipPosition, upOffset);
141+
142+ return gunTipPosition;
143+ },
144+
145+ preload: function(entityID) {
146+ this.entityID = entityID;
147+ this.SHOOTING_SOUND = SoundCache.getSound(SHOOTING_SOUND_URL);
148+ // this.createTipEntity(entityID);
149+ },
150+ createTipEntity: function(entityID) {
151+ //for debugging where its going to shoot from
152+ var gunProperties = Entities.getEntityProperties(entityID, ["position", "rotation"]);
153+
154+ var tipProps = {
155+ name: 'Ping pong tip test',
156+ dimensions: {
157+ x: 0.1,
158+ y: 0.1,
159+ z: 0.1
160+ },
161+ color: {
162+ red: 0,
163+ green: 255,
164+ blue: 0
165+ },
166+ type: 'Box',
167+ parentID: entityID,
168+ position: this.getGunTipPosition(gunProperties)
169+ };
170+ var tip = Entities.addEntity(tipProps);
171+ }
172+
173+ };
174+
175+ // entity scripts should return a newly constructed object of our type
176+ return new PingPongGun();
177+});
scripts/tutorials/entity_scripts/soundMaker.js+28−0
@@ -0,0 +1,28 @@
1+//
2+// Copyright 2016 High Fidelity, Inc.
3+//
4+//
5+// Distributed under the Apache License, Version 2.0.
6+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
7+//
8+
9+(function(){
10+
11+ var soundURL ='http://hifi-production.s3.amazonaws.com/tutorials/soundMaker/bell.wav';
12+ var ringSound;
13+
14+ this.preload = function(entityID) {
15+ print("preload("+entityID+")");
16+ ringSound = SoundCache.getSound(soundURL);
17+ };
18+
19+ this.clickDownOnEntity = function(entityID, mouseEvent) {
20+ var bellPosition = Entities.getEntityProperties(entityID).position;
21+ print("clickDownOnEntity()...");
22+ Audio.playSound(ringSound, {
23+ position: bellPosition,
24+ volume: 0.5
25+ });
26+ };
27+
28+})