Merge pull request #3990 from ZappoMan/entityScriptsAndEvents

Entity Scripts bug fixes/improvements
This commit is contained in:
Andrew Meadows 2014-12-19 16:02:00 -08:00
commit 3e087bd75e
7 changed files with 109 additions and 39 deletions

View file

@ -13,34 +13,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function(){
this.oldColor = {};
this.oldColorKnown = false;
this.storeOldColor = function(entityID) {
var oldProperties = Entities.getEntityProperties(entityID);
this.oldColor = oldProperties.color;
this.oldColorKnown = true;
print("storing old color... this.oldColor=" + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue);
};
this.preload = function(entityID) {
print("preload");
this.storeOldColor(entityID);
};
this.hoverEnterEntity = function(entityID, mouseEvent) {
print("hoverEnterEntity");
if (!this.oldColorKnown) {
this.storeOldColor(entityID);
}
Entities.editEntity(entityID, { color: { red: 0, green: 255, blue: 255} });
};
this.hoverLeaveEntity = function(entityID, mouseEvent) {
print("hoverLeaveEntity");
if (this.oldColorKnown) {
print("leave restoring old color... this.oldColor="
+ this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue);
Entities.editEntity(entityID, { color: this.oldColor });
}
};
})
(function() {
Script.include("changeColorOnHoverClass.js");
return new ChangeColorOnHover();
})

View file

@ -0,0 +1,55 @@
//
// changeColorOnHover.js
// examples/entityScripts
//
// Created by Brad Hefta-Gaub on 11/1/14.
// Copyright 2014 High Fidelity, Inc.
//
// This is an example of an entity script which when assigned to a non-model entity like a box or sphere, will
// change the color of the entity when you hover over it. This script uses the JavaScript prototype/class functionality
// to construct an object that has methods for hoverEnterEntity and hoverLeaveEntity;
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
ChangeColorOnHover = function(){
this.oldColor = {};
this.oldColorKnown = false;
};
ChangeColorOnHover.prototype = {
storeOldColor: function(entityID) {
var oldProperties = Entities.getEntityProperties(entityID);
this.oldColor = oldProperties.color;
this.oldColorKnown = true;
print("storing old color... this.oldColor=" + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue);
},
preload: function(entityID) {
print("preload");
this.storeOldColor(entityID);
},
hoverEnterEntity: function(entityID, mouseEvent) {
print("hoverEnterEntity");
if (!this.oldColorKnown) {
this.storeOldColor(entityID);
}
Entities.editEntity(entityID, { color: { red: 0, green: 255, blue: 255} });
},
hoverLeaveEntity: function(entityID, mouseEvent) {
print("hoverLeaveEntity");
if (this.oldColorKnown) {
print("leave restoring old color... this.oldColor="
+ this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue);
Entities.editEntity(entityID, { color: this.oldColor });
}
}
};

View file

@ -27,10 +27,12 @@
};
this.enterEntity = function(entityID) {
print("enterEntity("+entityID.id+")");
playSound();
};
this.leaveEntity = function(entityID) {
print("leaveEntity("+entityID.id+")");
playSound();
};
})

View file

@ -62,6 +62,7 @@ EntityTreeRenderer::~EntityTreeRenderer() {
}
void EntityTreeRenderer::clear() {
leaveAllEntities();
foreach (const EntityItemID& entityID, _entityScripts.keys()) {
checkAndCallUnload(entityID);
}
@ -82,8 +83,7 @@ void EntityTreeRenderer::init() {
// make sure our "last avatar position" is something other than our current position, so that on our
// first chance, we'll check for enter/leave entity events.
glm::vec3 avatarPosition = _viewState->getAvatarPosition();
_lastAvatarPosition = avatarPosition + glm::vec3(1.0f, 1.0f, 1.0f);
_lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3(1.0f, 1.0f, 1.0f);
connect(entityTree, &EntityTree::deletingEntity, this, &EntityTreeRenderer::deletingEntity);
connect(entityTree, &EntityTree::addingEntity, this, &EntityTreeRenderer::checkAndCallPreload);
@ -97,13 +97,15 @@ QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItem
}
QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText) {
QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL) {
QUrl url(scriptMaybeURLorText);
// If the url is not valid, this must be script text...
if (!url.isValid()) {
isURL = false;
return scriptMaybeURLorText;
}
isURL = true;
QString scriptContents; // assume empty
@ -173,7 +175,8 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {
return QScriptValue(); // no script
}
QString scriptContents = loadScriptContents(entityScript);
bool isURL = false; // loadScriptContents() will tell us if this is a URL or just text.
QString scriptContents = loadScriptContents(entityScript, isURL);
QScriptSyntaxCheckResult syntaxCheck = QScriptEngine::checkSyntax(scriptContents);
if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
@ -184,6 +187,9 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {
return QScriptValue(); // invalid script
}
if (isURL) {
_entitiesScriptEngine->setParentURL(entity->getScript());
}
QScriptValue entityScriptConstructor = _entitiesScriptEngine->evaluate(scriptContents);
if (!entityScriptConstructor.isFunction()) {
@ -197,6 +203,10 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {
EntityScriptDetails newDetails = { entityScript, entityScriptObject };
_entityScripts[entityID] = newDetails;
if (isURL) {
_entitiesScriptEngine->setParentURL("");
}
return entityScriptObject; // newly constructed
}
@ -287,6 +297,27 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
}
}
void EntityTreeRenderer::leaveAllEntities() {
if (_tree) {
_tree->lockForWrite(); // so that our scripts can do edits if they want
// for all of our previous containing entities, if they are no longer containing then send them a leave event
foreach(const EntityItemID& entityID, _currentEntitiesInside) {
emit leaveEntity(entityID);
QScriptValueList entityArgs = createEntityArgs(entityID);
QScriptValue entityScript = loadEntityScript(entityID);
if (entityScript.property("leaveEntity").isValid()) {
entityScript.property("leaveEntity").call(entityScript, entityArgs);
}
}
_currentEntitiesInside.clear();
// make sure our "last avatar position" is something other than our current position, so that on our
// first chance, we'll check for enter/leave entity events.
_lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3(1.0f, 1.0f, 1.0f);
_tree->unlock();
}
}
void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) {
if (_tree) {
Model::startScene(renderSide);

View file

@ -129,6 +129,7 @@ private:
QScriptValueList createEntityArgs(const EntityItemID& entityID);
void checkEnterLeaveEntities();
void leaveAllEntities();
glm::vec3 _lastAvatarPosition;
QVector<EntityItemID> _currentEntitiesInside;
@ -138,7 +139,7 @@ private:
QScriptValue loadEntityScript(EntityItem* entity);
QScriptValue loadEntityScript(const EntityItemID& entityItemID);
QScriptValue getPreviouslyLoadedEntityScript(const EntityItemID& entityItemID);
QString loadScriptContents(const QString& scriptMaybeURLorText);
QString loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL);
QScriptValueList createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID);
QScriptValueList createMouseEventArgs(const EntityItemID& entityID, const MouseEvent& mouseEvent);

View file

@ -601,16 +601,20 @@ void ScriptEngine::stopTimer(QTimer *timer) {
}
QUrl ScriptEngine::resolvePath(const QString& include) const {
// first lets check to see if it's already a full URL
QUrl url(include);
// first lets check to see if it's already a full URL
if (!url.scheme().isEmpty()) {
return url;
}
// we apparently weren't a fully qualified url, so, let's assume we're relative
// to the original URL of our script
QUrl parentURL(_fileNameString);
QUrl parentURL;
if (_parentURL.isEmpty()) {
parentURL = QUrl(_fileNameString);
} else {
parentURL = QUrl(_parentURL);
}
// if the parent URL's scheme is empty, then this is probably a local file...
if (parentURL.scheme().isEmpty()) {
parentURL = QUrl::fromLocalFile(_fileNameString);
@ -643,6 +647,7 @@ void ScriptEngine::include(const QString& includeFile) {
#else
QString fileName = url.toLocalFile();
#endif
QFile scriptFile(fileName);
if (scriptFile.open(QFile::ReadOnly | QFile::Text)) {
qDebug() << "Including file:" << fileName;

View file

@ -89,6 +89,8 @@ public:
void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; }
bool isUserLoaded() const { return _isUserLoaded; }
void setParentURL(const QString& parentURL) { _parentURL = parentURL; }
public slots:
void loadURL(const QUrl& scriptURL);
void stop();
@ -120,6 +122,7 @@ signals:
protected:
QString _scriptContents;
QString _parentURL;
bool _isFinished;
bool _isRunning;
bool _isInitialized;