History

added tetherball create and entity scripts

Rob Kayson committed 9 years ago

Commit 6763ca91 parent 999bd0d2 files · +484 −0Browse files

Showing the changes to scripts/tutorials/entity_scripts (1 of 2 files). Show all

Unified · Split

scripts/tutorials/entity_scripts/tetherballStick.js+411−0
@@ -0,0 +1,411 @@
1+"use strict";
2+/* jslint vars: true, plusplus: true, forin: true*/
3+/* globals Tablet, Script, AvatarList, Users, Entities, MyAvatar, Camera, Overlays, Vec3, Quat, Controller, print, getControllerWorldLocation */
4+/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
5+//
6+// tetherballStick.js
7+//
8+// Created by Triplelexx on 17/03/04
9+// Updated by MrRoboman on 17/03/26
10+// Copyright 2017 High Fidelity, Inc.
11+//
12+// Entity script for an equippable stick with a tethered ball
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+(function() {
18+ var _this;
19+
20+ var NULL_UUID = "{00000000-0000-0000-0000-000000000000}";
21+ var ENTITY_CHECK_INTERVAL = 5; // time in sec
22+ var LINE_WIDTH = 0.02;
23+ var BALL_SIZE = 0.175;
24+ var BALL_DAMPING = 0.5;
25+ var BALL_ANGULAR_DAMPING = 0.5;
26+ var BALL_RESTITUTION = 0.4;
27+ var BALL_DENSITY = 1000;
28+ var MAX_DISTANCE_MULTIPLIER = 2;
29+ var ACTION_DISTANCE = 0.25;
30+ var ACTION_TIMESCALE = 0.025;
31+ var ACTION_TAG = "TetherballStick Action";
32+ var BALL_NAME = "TetherballStick Ball";
33+ var LINE_NAME = "TetherballStick Line";
34+ var COLLISION_SOUND_URL = "http://public.highfidelity.io/sounds/Footsteps/FootstepW3Left-12db.wav";
35+ var INTERACT_SOUND_URL = "http://hifi-public.s3.amazonaws.com/sounds/color_busters/powerup.wav";
36+ var INTERACT_SOUND_VOLUME = 0.2;
37+ var USE_INTERACT_SOUND = false;
38+ var AVATAR_CHECK_RANGE = 5; // in meters
39+ var TIP_OFFSET = 0.26;
40+ var LIFETIME = 3600;
41+
42+ tetherballStick = function() {
43+ _this = this;
44+ return;
45+ };
46+
47+ tetherballStick.prototype = {
48+ lastCheckForEntities: ENTITY_CHECK_INTERVAL,
49+ originalDimensions: null,
50+ ownerID: NULL_UUID,
51+ userID: NULL_UUID,
52+ ballID: NULL_UUID,
53+ lineID: NULL_UUID,
54+ actionID: NULL_UUID,
55+ INTERACT_SOUND: undefined,
56+
57+ preload: function(entityID) {
58+ this.entityID = entityID;
59+ if (USE_INTERACT_SOUND) {
60+ this.INTERACT_SOUND = SoundCache.getSound(INTERACT_SOUND_URL);
61+ }
62+ this.originalDimensions = Entities.getEntityProperties(this.entityID).dimensions;
63+ Script.update.connect(this.update);
64+ },
65+
66+ unload: function() {
67+ Script.update.disconnect(this.update);
68+ },
69+
70+ update: function(dt) {
71+ // _this during update due to loss of scope
72+ if (_this.lastCheckForEntities >= ENTITY_CHECK_INTERVAL) {
73+ _this.lastCheckForEntities = 0;
74+ // everyone will be running this update loop
75+ // the only action is to increment the timer till ENTITY_CHECK_INTERVAL
76+ // when that is reached the userdata ownerID is simply validated by everyone
77+ // if it's invalid call setNewOwner, to set a new valid user
78+ if (!_this.checkForOwner()) {
79+ return;
80+ }
81+
82+ // only one person should ever be running this far
83+ if (_this.ownerID == MyAvatar.sessionUUID) {
84+ _this.checkForEntities();
85+ }
86+ } else {
87+ _this.lastCheckForEntities += dt;
88+ }
89+
90+ _this.drawLine();
91+ },
92+
93+ getScale: function() {
94+ var stickProps = Entities.getEntityProperties(this.entityID);
95+ return stickProps.dimensions.z / this.originalDimensions.z;
96+ },
97+
98+ checkForOwner: function() {
99+ var stickProps = Entities.getEntityProperties(this.entityID);
100+ try {
101+ var stickData = JSON.parse(stickProps.userData);
102+ var owner = AvatarManager.getAvatar(stickData.ownerID);
103+ if (owner.sessionUUID !== undefined) {
104+ this.ownerID = owner.sessionUUID;
105+ return true;
106+ } else {
107+ // UUID is invalid
108+ this.setNewOwner();
109+ return false;
110+ }
111+ } catch (e) {
112+ // all other errors
113+ this.setNewOwner();
114+ return false;
115+ }
116+ },
117+
118+ setNewOwner: function() {
119+ var stickProps = Entities.getEntityProperties(this.entityID);
120+ // I only want the closest client to be in charge of creating objects.
121+ // the AvatarList also contains a null representing MyAvatar,
122+ // a new array is created to start with containing the proper UUID
123+ var avatarList = AvatarList.getAvatarIdentifiers()
124+ .filter(Boolean) // remove the null
125+ .concat(MyAvatar.sessionUUID); // add the ID
126+
127+ var closestAvatarID = undefined;
128+ var closestAvatarDistance = AVATAR_CHECK_RANGE;
129+ avatarList.forEach(function(avatarID) {
130+ var avatar = AvatarList.getAvatar(avatarID);
131+ var distFrom = Vec3.distance(avatar.position, stickProps.position);
132+ if (distFrom < closestAvatarDistance) {
133+ closestAvatarDistance = distFrom;
134+ closestAvatarID = avatarID;
135+ }
136+ });
137+
138+ // add reference to userData
139+ try {
140+ var stickData = JSON.parse(stickProps.userData);
141+ stickData.ownerID = closestAvatarID; //NOTE: this will assign undefined if all avatars are further than AVATAR_CHECK_RANGE
142+ Entities.editEntity(this.entityID, {
143+ userData: JSON.stringify(stickData)
144+ });
145+ } catch (e) {
146+ }
147+ },
148+
149+ checkForEntities: function() {
150+ if (!this.hasBall()) {
151+ this.createBall();
152+ }
153+ if (!this.hasAction()) {
154+ this.createAction();
155+ }
156+ },
157+
158+ playInteractionSound: function() {
159+ var stickProps = Entities.getEntityProperties(this.entityID);
160+ var INTERACT_SOUND_OPTIONS = {
161+ volume: INTERACT_SOUND_VOLUME,
162+ loop: false,
163+ position: stickProps.position
164+ };
165+ Audio.playSound(this.INTERACT_SOUND, INTERACT_SOUND_OPTIONS);
166+ },
167+
168+ getTipPosition: function() {
169+ var stickProps = Entities.getEntityProperties(this.entityID);
170+
171+ var scale = this.getScale();
172+ var frontVec = Quat.getFront(stickProps.rotation);
173+ var frontOffset = Vec3.multiply(frontVec, TIP_OFFSET * scale);
174+ var tipPosition = Vec3.sum(stickProps.position, frontOffset);
175+
176+ return tipPosition;
177+ },
178+
179+ getStickFrontPosition: function() {
180+ var stickProps = Entities.getEntityProperties(this.entityID);
181+ var stickFront = Quat.getFront(stickProps.rotation);
182+ var tipPosition = this.getTipPosition();
183+ return Vec3.sum(tipPosition, Vec3.multiply(TIP_OFFSET*.4, stickFront));
184+ },
185+
186+ capBallDistance: function() {
187+ var stickProps = Entities.getEntityProperties(this.entityID);
188+ var ballProps = Entities.getEntityProperties(this.ballID);
189+ var tipPosition = this.getTipPosition();
190+ var scale = this.getScale();
191+ var distance = Vec3.distance(tipPosition, ballProps.position);
192+ var maxDistance = ACTION_DISTANCE * MAX_DISTANCE_MULTIPLIER * scale;
193+
194+ if(distance > maxDistance) {
195+ var direction = Vec3.normalize(Vec3.subtract(ballProps.position, tipPosition));
196+ var newPosition = Vec3.sum(tipPosition, Vec3.multiply(maxDistance, direction));
197+ Entities.editEntity(this.ballID, {
198+ position: newPosition
199+ })
200+ }
201+ },
202+
203+ startNearGrab: function(id, params) {
204+ var stickProps = Entities.getEntityProperties(this.entityID);
205+ // set variables from data in case someone else created the components
206+ try {
207+ var stickData = JSON.parse(stickProps.userData);
208+ this.userID = MyAvatar.sessionUUID;
209+ this.ballID = stickData.ballID;
210+ this.actionID = stickData.actionID;
211+ if(!this.hasLine()) {
212+ this.createLine();
213+ }
214+ if (USE_INTERACT_SOUND) {
215+ var hand = params[0];
216+ Controller.triggerShortHapticPulse(1, hand);
217+ this.playInteractionSound();
218+ }
219+ } catch (e) { }
220+ },
221+
222+ releaseGrab: function(id, params) {
223+ this.userID = NULL_UUID;
224+ if (USE_INTERACT_SOUND) {
225+ this.playInteractionSound();
226+ }
227+ },
228+
229+ continueNearGrab: function(id, params) {
230+ this.repositionAction();
231+ this.updateDimensions();
232+ this.capBallDistance();
233+ },
234+
235+ createLine: function() {
236+ var stickProps = Entities.getEntityProperties(this.entityID);
237+ this.lineID = Entities.addEntity({
238+ type: "PolyLine",
239+ name: LINE_NAME,
240+ color: {
241+ red: 0,
242+ green: 120,
243+ blue: 250
244+ },
245+ textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
246+ position: stickProps.position,
247+ dimensions: {
248+ x: 10,
249+ y: 10,
250+ z: 10
251+ },
252+ lifetime: LIFETIME
253+ });
254+ },
255+
256+ deleteLine: function() {
257+ Entities.deleteEntity(this.lineID);
258+ this.lineID = NULL_UUID;
259+ },
260+
261+ hasLine: function() {
262+ var lineProps = Entities.getEntityProperties(this.lineID);
263+ return lineProps.name == LINE_NAME;
264+ },
265+
266+ createBall: function() {
267+ var stickProps = Entities.getEntityProperties(this.entityID);
268+
269+ this.ballID = Entities.addEntity({
270+ type: "Model",
271+ modelURL: "http://hifi-content.s3.amazonaws.com/Examples%20Content/production/marblecollection/Star.fbx",
272+ name: BALL_NAME,
273+ shapeType: "Sphere",
274+ position: this.getStickFrontPosition(),
275+ lifetime: LIFETIME,
276+ collisionSoundURL: COLLISION_SOUND_URL,
277+ dimensions: {
278+ x: BALL_SIZE,
279+ y: BALL_SIZE,
280+ z: BALL_SIZE
281+ },
282+ gravity: {
283+ x: 0.0,
284+ y: -9.8,
285+ z: 0.0
286+ },
287+ damping: BALL_DAMPING,
288+ angularDamping: BALL_ANGULAR_DAMPING,
289+ density: BALL_DENSITY,
290+ restitution: BALL_RESTITUTION,
291+ dynamic: true,
292+ collidesWith: "static,dynamic,otherAvatar,",
293+ userData: JSON.stringify({
294+ grabbableKey: {
295+ grabbable: false
296+ }
297+ })
298+ });
299+
300+ // add reference to userData
301+ try {
302+ var stickData = JSON.parse(stickProps.userData);
303+ stickData.ballID = this.ballID;
304+ Entities.editEntity(this.entityID, {
305+ userData: JSON.stringify(stickData)
306+ });
307+ } catch (e) {}
308+ },
309+
310+ hasBall: function() {
311+ // validate the userData to handle unexpected item deletion
312+ var stickProps = Entities.getEntityProperties(this.entityID);
313+ try {
314+ var data = JSON.parse(stickProps.userData);
315+ var ballProps = Entities.getEntityProperties(data.ballID);
316+ return ballProps.name == BALL_NAME;
317+ } catch (e) {
318+ return false;
319+ }
320+ },
321+
322+ createAction: function() {
323+ var stickProps = Entities.getEntityProperties(this.entityID);
324+ this.actionID = Entities.addAction("offset", this.ballID, {
325+ pointToOffsetFrom: stickProps.position,
326+ linearDistance: ACTION_DISTANCE,
327+ tag: ACTION_TAG,
328+ linearTimeScale: ACTION_TIMESCALE
329+ });
330+
331+ // add reference to userData
332+ try {
333+ var stickData = JSON.parse(stickProps.userData);
334+ stickData.actionID = this.actionID;
335+ Entities.editEntity(this.entityID, {
336+ userData: JSON.stringify(stickData)
337+ });
338+ } catch (e) {}
339+ },
340+
341+ hasAction: function() {
342+ // validate the userData to handle unexpected item deletion
343+ var stickProps = Entities.getEntityProperties(this.entityID);
344+ try {
345+ var stickData = JSON.parse(stickProps.userData);
346+ var actionProps = Entities.getActionArguments(stickData.ballID, stickData.actionID);
347+ return actionProps.tag == ACTION_TAG;
348+ } catch (e) {
349+ return false;
350+ }
351+ },
352+
353+ hasRequiredComponents: function() {
354+ return this.hasBall() && this.hasAction() && this.hasLine();
355+ },
356+
357+ updateDimensions: function() {
358+ var scale = this.getScale();
359+ Entities.editEntity(this.ballID, {
360+ dimensions: {
361+ x: BALL_SIZE * scale,
362+ y: BALL_SIZE * scale,
363+ z: BALL_SIZE * scale
364+ }
365+ });
366+ },
367+
368+ drawLine: function() {
369+ if(!this.hasLine())
370+ return;
371+
372+ var stickProps = Entities.getEntityProperties(this.entityID);
373+ var tipPosition = this.getTipPosition();
374+ var ballProps = Entities.getEntityProperties(this.ballID);
375+ var cameraQuat = Vec3.multiplyQbyV(Camera.getOrientation(), Vec3.UNIT_NEG_Z);
376+ var linePoints = [];
377+ var normals = [];
378+ var strokeWidths = [];
379+ linePoints.push(Vec3.ZERO);
380+ normals.push(cameraQuat);
381+ strokeWidths.push(LINE_WIDTH);
382+ linePoints.push(Vec3.subtract(ballProps.position, tipPosition));
383+ normals.push(cameraQuat);
384+ strokeWidths.push(LINE_WIDTH);
385+
386+ var lineProps = Entities.getEntityProperties(this.lineID);
387+ Entities.editEntity(this.lineID, {
388+ linePoints: linePoints,
389+ normals: normals,
390+ strokeWidths: strokeWidths,
391+ position: tipPosition,
392+ });
393+ },
394+
395+ repositionAction: function() {
396+ var stickProps = Entities.getEntityProperties(this.entityID);
397+ var tipPosition = this.getTipPosition();
398+ var scale = this.getScale();
399+
400+ Entities.updateAction(this.ballID, this.actionID, {
401+ pointToOffsetFrom: tipPosition,
402+ linearDistance: ACTION_DISTANCE * scale,
403+ tag: ACTION_TAG,
404+ linearTimeScale: ACTION_TIMESCALE
405+ });
406+ }
407+ };
408+
409+ // entity scripts should return a newly constructed object of our type
410+ return new tetherballStick();
411+});

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