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