add support for scripts to call methods on entity scripts

This commit is contained in:
Brad Hefta-Gaub 2015-09-18 14:06:38 -07:00
parent 8f5055d212
commit 0d37511071
7 changed files with 92 additions and 46 deletions

View file

@ -69,6 +69,7 @@ var STATE_CLOSE_GRABBING = 2;
var STATE_CONTINUE_CLOSE_GRABBING = 3;
var STATE_RELEASE = 4;
var GRAB_USER_DATA_KEY = "grabKey";
function controller(hand, triggerAction) {
this.hand = hand;
@ -89,6 +90,7 @@ function controller(hand, triggerAction) {
this.state = 0; // 0 = searching, 1 = distanceHolding, 2 = closeGrabbing
this.pointer = null; // entity-id of line object
this.triggerValue = 0; // rolling average of trigger value
this.alreadyDistanceHolding = false; // FIXME - I'll leave it to Seth to potentially make this another state
this.update = function() {
switch(this.state) {
@ -210,13 +212,20 @@ function controller(hand, triggerAction) {
this.distanceHolding = function() {
if (!this.triggerSmoothedSqueezed()) {
this.state = STATE_RELEASE;
this.alreadyDistanceHolding = false;
return;
}
if (!this.alreadyDistanceHolding) {
this.activateEntity(this.grabbedEntity);
this.alreadyDistanceHolding = true;
}
var handPosition = this.getHandPosition();
var handControllerPosition = Controller.getSpatialControlPosition(this.palm);
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity);
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position","rotation"]);
Entities.callEntityMethod(this.grabbedEntity, "distanceHolding");
this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR);
@ -270,7 +279,9 @@ function controller(hand, triggerAction) {
this.lineOff();
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity);
this.activateEntity(this.grabbedEntity);
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, "position");
var handRotation = this.getHandRotation();
var handPosition = this.getHandPosition();
@ -294,6 +305,7 @@ function controller(hand, triggerAction) {
} else {
this.state = STATE_CONTINUE_CLOSE_GRABBING;
}
Entities.callEntityMethod(this.grabbedEntity, "closeGrabbing");
}
@ -324,6 +336,7 @@ function controller(hand, triggerAction) {
this.currentObjectPosition = grabbedProperties.position;
this.currentObjectTime = now;
Entities.callEntityMethod(this.grabbedEntity, "continueCloseGrabbing");
}
@ -338,6 +351,9 @@ function controller(hand, triggerAction) {
// the action is gone, set the objects velocity to something the holder might expect.
Entities.editEntity(this.grabbedEntity, { velocity: this.grabbedVelocity });
Entities.callEntityMethod(this.grabbedEntity, "release");
this.deactivateEntity(this.grabbedEntity);
this.grabbedVelocity = ZERO_VEC;
this.grabbedEntity = null;
this.actionID = null;
@ -348,6 +364,22 @@ function controller(hand, triggerAction) {
this.cleanup = function() {
release();
}
this.activateEntity = function(entity) {
var data = {
activated: true,
avatarId: MyAvatar.sessionUUID
};
setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data);
}
this.deactivateEntity = function(entity) {
var data = {
activated: false,
avatarId: null
};
setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data);
}
}

View file

@ -12,7 +12,6 @@
//
(function() {
Script.include("../libraries/utils.js");
var _this;
@ -24,39 +23,18 @@
DetectGrabbed.prototype = {
// update() will be called regulary, because we've hooked the update signal in our preload() function
// we will check out userData for the grabData. In the case of the hydraGrab script, it will tell us
// if we're currently being grabbed and if the person grabbing us is the current interfaces avatar.
// we will watch this for state changes and print out if we're being grabbed or released when it changes.
update: function() {
var GRAB_USER_DATA_KEY = "grabKey";
distanceHolding: function () {
print("I am being distance held... entity:" + this.entityID);
},
// because the update() signal doesn't have a valid this, we need to use our memorized _this to access our entityID
var entityID = _this.entityID;
// we want to assume that if there is no grab data, then we are not being grabbed
var defaultGrabData = { activated: false, avatarId: null };
// this handy function getEntityCustomData() is available in utils.js and it will return just the specific section
// of user data we asked for. If it's not available it returns our default data.
var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, defaultGrabData);
// if the grabData says we're being grabbed, and the owner ID is our session, then we are being grabbed by this interface
if (grabData.activated && grabData.avatarId == MyAvatar.sessionUUID) {
// remember we're being grabbed so we can detect being released
_this.beingGrabbed = true;
// print out that we're being grabbed
print("I'm being grabbed...");
} else if (_this.beingGrabbed) {
// if we are not being grabbed, and we previously were, then we were just released, remember that
// and print out a message
_this.beingGrabbed = false;
print("I'm was released...");
}
closeGrabbing: function () {
print("I was just grabbed... entity:" + this.entityID);
},
continueCloseGrabbing: function () {
print("I am still being grabbed... entity:" + this.entityID);
},
release: function () {
print("I was released... entity:" + this.entityID);
},
// preload() will be called when the entity has become visible (or known) to the interface
@ -65,14 +43,6 @@
// * connecting to the update signal so we can check our grabbed state
preload: function(entityID) {
this.entityID = entityID;
Script.update.connect(this.update);
},
// unload() will be called when our entity is no longer available. It may be because we were deleted,
// or because we've left the domain or quit the application. In all cases we want to unhook our connection
// to the update signal
unload: function(entityID) {
Script.update.disconnect(this.update);
},
};

View file

@ -112,6 +112,7 @@ void EntityTreeRenderer::init() {
_scriptingServices->getControllerScriptingInterface());
_scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine);
_entitiesScriptEngine->runInThread();
DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(_entitiesScriptEngine);
}
// make sure our "last avatar position" is something other than our current position, so that on our

View file

@ -0,0 +1,25 @@
//
// EntitiesScriptEngineProvider.h
// libraries/entities/src
//
// Created by Brad Hefta-Gaub on Sept. 18, 2015
// Copyright 2015 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
//
// TODO: How will we handle collision callbacks with Entities
//
#ifndef hifi_EntitiesScriptEngineProvider_h
#define hifi_EntitiesScriptEngineProvider_h
#include <QtCore/QString>
#include "EntityItemID.h"
class EntitiesScriptEngineProvider {
public:
virtual void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName) = 0;
};
#endif // hifi_EntitiesScriptEngineProvider_h

View file

@ -11,6 +11,7 @@
#include "EntityScriptingInterface.h"
#include "EntityItemID.h"
#include <VariantMapToScriptValue.h>
#include "EntitiesLogging.h"
@ -211,6 +212,14 @@ void EntityScriptingInterface::deleteEntity(QUuid id) {
}
}
void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method) {
if (_entitiesScriptEngine) {
EntityItemID entityID{ id };
_entitiesScriptEngine->callEntityScriptMethod(entityID, method);
}
}
QUuid EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const {
EntityItemID result;
if (_entityTree) {

View file

@ -20,18 +20,19 @@
#include <Octree.h>
#include <OctreeScriptingInterface.h>
#include <RegisteredMetaTypes.h>
#include "PolyVoxEntityItem.h"
#include "LineEntityItem.h"
#include "PolyLineEntityItem.h"
#include "EntityTree.h"
#include "EntityEditPacketSender.h"
#include "EntitiesScriptEngineProvider.h"
class EntityTree;
class MouseEvent;
class RayToEntityIntersectionResult {
public:
RayToEntityIntersectionResult();
@ -63,6 +64,7 @@ public:
void setEntityTree(EntityTreePointer modelTree);
EntityTreePointer getEntityTree() { return _entityTree; }
void setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine) { _entitiesScriptEngine = engine; }
public slots:
@ -86,6 +88,11 @@ public slots:
/// deletes a model
Q_INVOKABLE void deleteEntity(QUuid entityID);
/// Allows a script to call a method on an entity's script. The method will execute in the entity script
/// engine. If the entity does not have an entity script or the method does not exist, this call will have
/// no effect.
Q_INVOKABLE void callEntityMethod(QUuid entityID, const QString& method);
/// finds the closest model to the center point, within the radius
/// will return a EntityItemID.isKnownID = false if no models are in the radius
/// this function will not find any models in script engine contexts which don't have access to models
@ -180,6 +187,7 @@ private:
bool precisionPicking);
EntityTreePointer _entityTree;
EntitiesScriptEngineProvider* _entitiesScriptEngine = nullptr;
};
#endif // hifi_EntityScriptingInterface_h

View file

@ -25,6 +25,7 @@
#include <AvatarHashMap.h>
#include <LimitedNodeList.h>
#include <EntityItemID.h>
#include <EntitiesScriptEngineProvider.h>
#include "AbstractControllerScriptingInterface.h"
#include "ArrayBufferClass.h"
@ -46,7 +47,7 @@ public:
QScriptValue scriptObject;
};
class ScriptEngine : public QScriptEngine, public ScriptUser {
class ScriptEngine : public QScriptEngine, public ScriptUser, public EntitiesScriptEngineProvider {
Q_OBJECT
public:
ScriptEngine(const QString& scriptContents = NO_SCRIPT,