mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
6 Commits
v5.5.233
...
nearby-beh
Author | SHA1 | Date | |
---|---|---|---|
![]() |
201f1cd03c | ||
![]() |
430dee6c52 | ||
![]() |
fc3aa161c5 | ||
![]() |
e279424ee0 | ||
![]() |
4104d7bdc5 | ||
![]() |
e8c6d707fa |
@@ -33,6 +33,13 @@ void EventsCodeGenerationContext::InheritsFrom(const EventsCodeGenerationContext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EventsCodeGenerationContext::Reuse(const EventsCodeGenerationContext & parent_)
|
||||||
|
{
|
||||||
|
InheritsFrom(parent_);
|
||||||
|
if (parent_.CanReuse())
|
||||||
|
contextDepth = parent_.GetContextDepth(); // Keep same context depth
|
||||||
|
}
|
||||||
|
|
||||||
void EventsCodeGenerationContext::ObjectsListNeeded(const gd::String & objectName)
|
void EventsCodeGenerationContext::ObjectsListNeeded(const gd::String & objectName)
|
||||||
{
|
{
|
||||||
if ( emptyObjectsListsToBeDeclared.find(objectName) == emptyObjectsListsToBeDeclared.end() )
|
if ( emptyObjectsListsToBeDeclared.find(objectName) == emptyObjectsListsToBeDeclared.end() )
|
||||||
|
@@ -32,7 +32,13 @@ public:
|
|||||||
* Default constructor. You may want to call InheritsFrom just after.
|
* Default constructor. You may want to call InheritsFrom just after.
|
||||||
* \param maxDepthLevel Optional pointer to an unsigned integer that will be updated to contain the maximal scope depth reached.
|
* \param maxDepthLevel Optional pointer to an unsigned integer that will be updated to contain the maximal scope depth reached.
|
||||||
*/
|
*/
|
||||||
EventsCodeGenerationContext(unsigned int * maxDepthLevel_ = nullptr) : contextDepth(0), customConditionDepth(0), maxDepthLevel(maxDepthLevel_), parent(NULL) {};
|
EventsCodeGenerationContext(unsigned int * maxDepthLevel_ = nullptr) :
|
||||||
|
contextDepth(0),
|
||||||
|
customConditionDepth(0),
|
||||||
|
maxDepthLevel(maxDepthLevel_),
|
||||||
|
parent(NULL),
|
||||||
|
reuseExplicitlyForbidden(false)
|
||||||
|
{};
|
||||||
virtual ~EventsCodeGenerationContext() {};
|
virtual ~EventsCodeGenerationContext() {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,6 +47,28 @@ public:
|
|||||||
*/
|
*/
|
||||||
void InheritsFrom(const EventsCodeGenerationContext & parent);
|
void InheritsFrom(const EventsCodeGenerationContext & parent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief As InheritsFrom, mark the context as being the child of another one, but enabling
|
||||||
|
* the child context to use the same object lists.
|
||||||
|
*
|
||||||
|
* Used for example for optimizing the last event of a list.
|
||||||
|
*/
|
||||||
|
void Reuse(const EventsCodeGenerationContext & parent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Forbid any optimization that would reuse and modify the object list from this context
|
||||||
|
* in children context.
|
||||||
|
*
|
||||||
|
* Used in while/for each/repeat or any event that have a loop and must ensure that
|
||||||
|
* the list of objects stay clean.
|
||||||
|
*/
|
||||||
|
void ForbidReuse() { reuseExplicitlyForbidden = true; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Return false if the object lists of the context can not be reused in a child context.
|
||||||
|
*/
|
||||||
|
bool CanReuse() const { return !reuseExplicitlyForbidden && parent != nullptr; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Returns the depth of the inheritance of the context.
|
* \brief Returns the depth of the inheritance of the context.
|
||||||
*
|
*
|
||||||
@@ -150,6 +178,7 @@ private:
|
|||||||
unsigned int customConditionDepth; ///< The depth of the conditions being generated.
|
unsigned int customConditionDepth; ///< The depth of the conditions being generated.
|
||||||
unsigned int * maxDepthLevel; ///< A pointer to a unsigned int updated with the maximum depth reached.
|
unsigned int * maxDepthLevel; ///< A pointer to a unsigned int updated with the maximum depth reached.
|
||||||
const EventsCodeGenerationContext * parent; ///< The parent of the current context. Can be NULL.
|
const EventsCodeGenerationContext * parent; ///< The parent of the current context. Can be NULL.
|
||||||
|
bool reuseExplicitlyForbidden; ///< If set to true, forbid children context to reuse this one without inheriting.
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -675,8 +675,17 @@ gd::String EventsCodeGenerator::GenerateEventsListCode(gd::EventsList & events,
|
|||||||
for ( std::size_t eId = 0; eId < events.size();++eId )
|
for ( std::size_t eId = 0; eId < events.size();++eId )
|
||||||
{
|
{
|
||||||
//Each event has its own context : Objects picked in an event are totally different than the one picked in another.
|
//Each event has its own context : Objects picked in an event are totally different than the one picked in another.
|
||||||
gd::EventsCodeGenerationContext context;
|
gd::EventsCodeGenerationContext newContext;
|
||||||
context.InheritsFrom(parentContext); //Events in the same "level" share the same context as their parent.
|
newContext.InheritsFrom(parentContext); //Events in the same "level" share the same context as their parent.
|
||||||
|
|
||||||
|
//*Optimization*: when the event is the last of a list, we can use the
|
||||||
|
//same lists of objects as the parent (as they will be discarded just after).
|
||||||
|
//This avoids a copy of the lists of objects which is an expensive operation.
|
||||||
|
bool reuseParentContext = parentContext.CanReuse() && eId == events.size()-1;
|
||||||
|
gd::EventsCodeGenerationContext reusedContext;
|
||||||
|
reusedContext.Reuse(parentContext);
|
||||||
|
|
||||||
|
auto & context = reuseParentContext ? reusedContext : newContext;
|
||||||
|
|
||||||
gd::String eventCoreCode = events[eId].GenerateEventCode(*this, context);
|
gd::String eventCoreCode = events[eId].GenerateEventCode(*this, context);
|
||||||
gd::String scopeBegin = GenerateScopeBegin(context);
|
gd::String scopeBegin = GenerateScopeBegin(context);
|
||||||
|
@@ -46,7 +46,7 @@ public:
|
|||||||
virtual ~EventsCodeGenerator() {};
|
virtual ~EventsCodeGenerator() {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Preprocess an events list ( Replacing for example links with the linked event ).
|
* \brief Preprocess an events list (replacing for example links with the linked events).
|
||||||
*
|
*
|
||||||
* This should be called before any code generation.
|
* This should be called before any code generation.
|
||||||
*/
|
*/
|
||||||
@@ -57,9 +57,9 @@ public:
|
|||||||
*
|
*
|
||||||
* \param events std::vector of events
|
* \param events std::vector of events
|
||||||
* \param context Context used for generation
|
* \param context Context used for generation
|
||||||
* \return C++ code
|
* \return Code
|
||||||
*/
|
*/
|
||||||
gd::String GenerateEventsListCode(gd::EventsList & events, const EventsCodeGenerationContext & context);
|
virtual gd::String GenerateEventsListCode(gd::EventsList & events, const EventsCodeGenerationContext & context);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Generate code for executing a condition list
|
* \brief Generate code for executing a condition list
|
||||||
|
@@ -48,3 +48,6 @@ IF (NOT EMSCRIPTEN)
|
|||||||
ADD_SUBDIRECTORY(TimedEvent)
|
ADD_SUBDIRECTORY(TimedEvent)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
ADD_SUBDIRECTORY(TopDownMovementBehavior)
|
ADD_SUBDIRECTORY(TopDownMovementBehavior)
|
||||||
|
IF (NOT EMSCRIPTEN)
|
||||||
|
ADD_SUBDIRECTORY(TrackedBehavior)
|
||||||
|
ENDIF()
|
||||||
|
@@ -15,8 +15,7 @@ Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)
|
|||||||
*/
|
*/
|
||||||
gdjs.PlatformObjectsManager = function(runtimeScene, sharedData)
|
gdjs.PlatformObjectsManager = function(runtimeScene, sharedData)
|
||||||
{
|
{
|
||||||
this._platformsHSHG = new gdjs.HSHG.HSHG();
|
this._platformRBush = new rbush(9, ['.owner.getAABB().min[0]', '.owner.getAABB().min[1]', '.owner.getAABB().max[0]', '.owner.getAABB().max[1]']);
|
||||||
//this._hshgNeedUpdate = true; Useless: The behaviors track by themselves changes in objects size or position.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,7 +38,7 @@ gdjs.PlatformObjectsManager.getManager = function(runtimeScene) {
|
|||||||
* @method addPlatform
|
* @method addPlatform
|
||||||
*/
|
*/
|
||||||
gdjs.PlatformObjectsManager.prototype.addPlatform = function(platformBehavior) {
|
gdjs.PlatformObjectsManager.prototype.addPlatform = function(platformBehavior) {
|
||||||
this._platformsHSHG.addObject(platformBehavior);
|
this._platformRBush.insert(platformBehavior);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,41 +48,7 @@ gdjs.PlatformObjectsManager.prototype.addPlatform = function(platformBehavior) {
|
|||||||
* @method removePlatform
|
* @method removePlatform
|
||||||
*/
|
*/
|
||||||
gdjs.PlatformObjectsManager.prototype.removePlatform = function(platformBehavior) {
|
gdjs.PlatformObjectsManager.prototype.removePlatform = function(platformBehavior) {
|
||||||
this._platformsHSHG.removeObject(platformBehavior);
|
this._platformRBush.remove(platformBehavior);
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tool class which represents a simple point with a radius and a getAABB method.
|
|
||||||
* @class Vertex
|
|
||||||
* @namespace gdjs.PlatformObjectsManager
|
|
||||||
* @private
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
gdjs.PlatformObjectsManager.Vertex = function(x,y,radius) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.radius = radius;
|
|
||||||
this._aabbComputed = false;
|
|
||||||
|
|
||||||
if (!this._aabb) {
|
|
||||||
this._aabb = {min: [0, 0], max: [0, 0]};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an axis aligned bouding box for the vertex.
|
|
||||||
* @method getAABB
|
|
||||||
*/
|
|
||||||
gdjs.PlatformObjectsManager.Vertex.prototype.getAABB = function(){
|
|
||||||
if (!this._aabbComputed) {
|
|
||||||
this._aabb.min[0] = this.x - this.radius;
|
|
||||||
this._aabb.min[1] = this.y - this.radius;
|
|
||||||
this._aabb.max[0] = this.x + this.radius;
|
|
||||||
this._aabb.max[1] = this.y + this.radius;
|
|
||||||
this._aabbComputed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._aabb;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,16 +63,15 @@ gdjs.PlatformObjectsManager.prototype.getAllPlatformsAround = function(object, m
|
|||||||
var oh = object.getHeight();
|
var oh = object.getHeight();
|
||||||
var x = object.getDrawableX()+object.getCenterX();
|
var x = object.getDrawableX()+object.getCenterX();
|
||||||
var y = object.getDrawableY()+object.getCenterY();
|
var y = object.getDrawableY()+object.getCenterY();
|
||||||
var objBoundingRadius = Math.sqrt(ow*ow+oh*oh)/2.0 + maxMovementLength;
|
|
||||||
|
|
||||||
if (!this._aroundVertex)
|
var searchArea = gdjs.staticObject(gdjs.PlatformObjectsManager.prototype.getAllPlatformsAround);
|
||||||
this._aroundVertex = new gdjs.PlatformObjectsManager.Vertex(x, y, objBoundingRadius);
|
searchArea.minX = x - ow / 2 - maxMovementLength;
|
||||||
else
|
searchArea.minY = y - oh / 2 - maxMovementLength;
|
||||||
gdjs.PlatformObjectsManager.Vertex.call(this._aroundVertex, x, y, objBoundingRadius);
|
searchArea.maxX = x + ow / 2 + maxMovementLength;
|
||||||
|
searchArea.maxY = y + oh / 2 + maxMovementLength;
|
||||||
this._platformsHSHG.addObject(this._aroundVertex);
|
var nearbyPlatforms = this._platformRBush.search(searchArea);
|
||||||
this._platformsHSHG.queryForCollisionWith(this._aroundVertex, result);
|
result.length = 0;
|
||||||
this._platformsHSHG.removeObject(this._aroundVertex);
|
result.push.apply(result, nearbyPlatforms);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -167,12 +131,6 @@ gdjs.PlatformRuntimeBehavior.prototype.doStepPreEvents = function(runtimeScene)
|
|||||||
registeredInManager = false;
|
registeredInManager = false;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
//No need for update as we take care of this below.
|
|
||||||
/*if ( this._hshgNeedUpdate ) {
|
|
||||||
this._manager._platformsHSHG.update();
|
|
||||||
this._manager._hshgNeedUpdate = false;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
//Make sure the platform is or is not in the platforms manager.
|
//Make sure the platform is or is not in the platforms manager.
|
||||||
if (!this.activated() && this._registeredInManager)
|
if (!this.activated() && this._registeredInManager)
|
||||||
{
|
{
|
||||||
@@ -202,7 +160,6 @@ gdjs.PlatformRuntimeBehavior.prototype.doStepPreEvents = function(runtimeScene)
|
|||||||
};
|
};
|
||||||
|
|
||||||
gdjs.PlatformRuntimeBehavior.prototype.doStepPostEvents = function(runtimeScene) {
|
gdjs.PlatformRuntimeBehavior.prototype.doStepPostEvents = function(runtimeScene) {
|
||||||
//this._manager._hshgNeedUpdate = true; //Useless, see above.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gdjs.PlatformRuntimeBehavior.prototype.getAABB = function(){
|
gdjs.PlatformRuntimeBehavior.prototype.getAABB = function(){
|
||||||
|
25
Extensions/TrackedBehavior/CMakeLists.txt
Normal file
25
Extensions/TrackedBehavior/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
cmake_minimum_required(VERSION 2.6)
|
||||||
|
cmake_policy(SET CMP0015 NEW)
|
||||||
|
|
||||||
|
project(TrackedBehavior)
|
||||||
|
gd_add_extension_includes()
|
||||||
|
|
||||||
|
#Defines
|
||||||
|
###
|
||||||
|
gd_add_extension_definitions(TrackedBehavior)
|
||||||
|
|
||||||
|
#The targets
|
||||||
|
###
|
||||||
|
include_directories(.)
|
||||||
|
file(GLOB source_files *)
|
||||||
|
|
||||||
|
gd_add_extension_target(TrackedBehavior "${source_files}")
|
||||||
|
gdcpp_add_runtime_extension_target(TrackedBehavior_Runtime "${source_files}")
|
||||||
|
|
||||||
|
#Linker files for the IDE extension
|
||||||
|
###
|
||||||
|
gd_extension_link_libraries(TrackedBehavior)
|
||||||
|
|
||||||
|
#Linker files for the GD C++ Runtime extension
|
||||||
|
###
|
||||||
|
gdcpp_runtime_extension_link_libraries(TrackedBehavior_Runtime)
|
78
Extensions/TrackedBehavior/Extension.cpp
Normal file
78
Extensions/TrackedBehavior/Extension.cpp
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
|
||||||
|
GDevelop - Tracked Behavior Extension
|
||||||
|
Copyright (c) 2014-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||||
|
This project is released under the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "GDCpp/Extensions/ExtensionBase.h"
|
||||||
|
|
||||||
|
#include "TrackedBehavior.h"
|
||||||
|
|
||||||
|
|
||||||
|
void DeclareTrackedBehaviorExtension(gd::PlatformExtension & extension)
|
||||||
|
{
|
||||||
|
extension.SetExtensionInformation("TrackedBehavior",
|
||||||
|
_("Tracked Behavior"),
|
||||||
|
_("TODO"),
|
||||||
|
"Florian Rival",
|
||||||
|
"Open source (MIT License)");
|
||||||
|
|
||||||
|
gd::BehaviorMetadata & aut = extension.AddBehavior("Tracked",
|
||||||
|
_("Tracked object"),
|
||||||
|
_("Tracked"),
|
||||||
|
_("TODO"),
|
||||||
|
"",
|
||||||
|
"CppPlatform/Extensions/draggableicon.png", //TODO
|
||||||
|
"TrackedBehavior",
|
||||||
|
std::make_shared<TrackedBehavior>(),
|
||||||
|
std::shared_ptr<gd::BehaviorsSharedData>());
|
||||||
|
|
||||||
|
#if defined(GD_IDE_ONLY)
|
||||||
|
aut.SetIncludeFile("TrackedBehavior/TrackedBehavior.h");
|
||||||
|
|
||||||
|
extension.AddCondition("TrackedBehaviorAround",
|
||||||
|
_("Get objects around a position"),
|
||||||
|
_("Get the objects that are around the specified position"),
|
||||||
|
_("Consider all _PARAM1_ around _PARAM2_;_PARAM3_, with maximum distance: _PARAM4_ pixels"),
|
||||||
|
_(""),
|
||||||
|
"CppPlatform/Extensions/draggableicon24.png",
|
||||||
|
"CppPlatform/Extensions/draggableicon16.png")
|
||||||
|
|
||||||
|
.AddCodeOnlyParameter("currentScene", "")
|
||||||
|
.AddParameter("objectListWithoutPicking", _("Objects to find"))
|
||||||
|
.AddParameter("expression", _("X position"))
|
||||||
|
.AddParameter("expression", _("Y position"))
|
||||||
|
.AddParameter("expression", _("Maximum distance (in pixels)"))
|
||||||
|
|
||||||
|
.SetFunctionName("TODO").SetIncludeFile("TrackedBehavior/TrackedBehavior.h");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief This class declares information about the extension.
|
||||||
|
*/
|
||||||
|
class Extension : public ExtensionBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of an extension declares everything the extension contains: objects, actions, conditions and expressions.
|
||||||
|
*/
|
||||||
|
Extension()
|
||||||
|
{
|
||||||
|
DeclareTrackedBehaviorExtension(*this);
|
||||||
|
GD_COMPLETE_EXTENSION_COMPILATION_INFORMATION();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !defined(EMSCRIPTEN)
|
||||||
|
/**
|
||||||
|
* Used by GDevelop to create the extension class
|
||||||
|
* -- Do not need to be modified. --
|
||||||
|
*/
|
||||||
|
extern "C" ExtensionBase * GD_EXTENSION_API CreateGDExtension() {
|
||||||
|
return new Extension;
|
||||||
|
}
|
||||||
|
#endif
|
55
Extensions/TrackedBehavior/JsExtension.cpp
Normal file
55
Extensions/TrackedBehavior/JsExtension.cpp
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
|
||||||
|
GDevelop - Tracked Behavior Extension
|
||||||
|
Copyright (c) 2014-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||||
|
This project is released under the MIT License.
|
||||||
|
*/
|
||||||
|
#if defined(GD_IDE_ONLY)
|
||||||
|
#include "GDCore/Extensions/PlatformExtension.h"
|
||||||
|
#include "GDCore/Tools/Localization.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
void DeclareTrackedBehaviorExtension(gd::PlatformExtension & extension);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief This class declares information about the JS extension.
|
||||||
|
*/
|
||||||
|
class TrackedBehaviorJsExtension : public gd::PlatformExtension
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Constructor of an extension declares everything the extension contains: objects, actions, conditions and expressions.
|
||||||
|
*/
|
||||||
|
TrackedBehaviorJsExtension()
|
||||||
|
{
|
||||||
|
SetExtensionInformation("TrackedBehavior",
|
||||||
|
_("Tracked Behavior"),
|
||||||
|
_("Behavior allowing to move objects with the mouse"),
|
||||||
|
"Florian Rival",
|
||||||
|
"Open source (MIT License)");
|
||||||
|
|
||||||
|
DeclareTrackedBehaviorExtension(*this);
|
||||||
|
|
||||||
|
GetBehaviorMetadata("TrackedBehavior::Tracked").SetIncludeFile("TrackedBehavior/trackedruntimebehavior.js");
|
||||||
|
GetAllConditions()["TrackedBehavior::TrackedBehaviorAround"].SetFunctionName("gdjs.TrackedRuntimeBehavior.aroundTest").SetIncludeFile("TrackedBehavior/trackedruntimebehavior.js");
|
||||||
|
GD_COMPLETE_EXTENSION_COMPILATION_INFORMATION();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(EMSCRIPTEN)
|
||||||
|
extern "C" gd::PlatformExtension * CreateGDJSTrackedBehaviorExtension() {
|
||||||
|
return new TrackedBehaviorJsExtension;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/**
|
||||||
|
* Used by GDevelop to create the extension class
|
||||||
|
* -- Do not need to be modified. --
|
||||||
|
*/
|
||||||
|
extern "C" gd::PlatformExtension * GD_EXTENSION_API CreateGDJSExtension() {
|
||||||
|
return new TrackedBehaviorJsExtension;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
80
Extensions/TrackedBehavior/TrackedBehavior.cpp
Normal file
80
Extensions/TrackedBehavior/TrackedBehavior.cpp
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
|
||||||
|
GDevelop - Tracked Behavior Extension
|
||||||
|
Copyright (c) 2014-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||||
|
This project is released under the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <iostream>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
#include "TrackedBehavior.h"
|
||||||
|
#include "GDCpp/Runtime/Project/Layout.h"
|
||||||
|
#include "GDCpp/Runtime/RuntimeLayer.h"
|
||||||
|
#include "GDCpp/Runtime/Serialization/SerializerElement.h"
|
||||||
|
#include "GDCpp/Runtime/RuntimeScene.h"
|
||||||
|
#include "GDCpp/Runtime/RuntimeObject.h"
|
||||||
|
#include "GDCpp/Runtime/CommonTools.h"
|
||||||
|
|
||||||
|
bool TrackedBehavior::somethingDragged = false;
|
||||||
|
bool TrackedBehavior::leftPressedLastFrame = false;
|
||||||
|
|
||||||
|
TrackedBehavior::TrackedBehavior() :
|
||||||
|
dragged(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TrackedBehavior::DoStepPreEvents(RuntimeScene & scene)
|
||||||
|
{
|
||||||
|
//Begin drag ?
|
||||||
|
if ( !dragged && scene.GetInputManager().IsMouseButtonPressed("Left") &&
|
||||||
|
!leftPressedLastFrame && !somethingDragged )
|
||||||
|
{
|
||||||
|
RuntimeLayer & theLayer = scene.GetRuntimeLayer(object->GetLayer());
|
||||||
|
for (std::size_t cameraIndex = 0;cameraIndex < theLayer.GetCameraCount();++cameraIndex)
|
||||||
|
{
|
||||||
|
sf::Vector2f mousePos = scene.renderWindow->mapPixelToCoords(
|
||||||
|
scene.GetInputManager().GetMousePosition(), theLayer.GetCamera(cameraIndex).GetSFMLView());
|
||||||
|
|
||||||
|
if ( object->GetDrawableX() <= mousePos.x
|
||||||
|
&& object->GetDrawableX() + object->GetWidth() >= mousePos.x
|
||||||
|
&& object->GetDrawableY() <= mousePos.y
|
||||||
|
&& object->GetDrawableY() + object->GetHeight() >= mousePos.y )
|
||||||
|
{
|
||||||
|
dragged = true;
|
||||||
|
somethingDragged = true;
|
||||||
|
xOffset = mousePos.x - object->GetX();
|
||||||
|
yOffset = mousePos.y - object->GetY();
|
||||||
|
dragCameraIndex = cameraIndex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//End dragging ?
|
||||||
|
else if ( !scene.GetInputManager().IsMouseButtonPressed("Left") ) {
|
||||||
|
dragged = false;
|
||||||
|
somethingDragged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Being dragging ?
|
||||||
|
if ( dragged ) {
|
||||||
|
RuntimeLayer & theLayer = scene.GetRuntimeLayer(object->GetLayer());
|
||||||
|
sf::Vector2f mousePos = scene.renderWindow->mapPixelToCoords(
|
||||||
|
scene.GetInputManager().GetMousePosition(), theLayer.GetCamera(dragCameraIndex).GetSFMLView());
|
||||||
|
|
||||||
|
object->SetX(mousePos.x-xOffset);
|
||||||
|
object->SetY(mousePos.y-yOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TrackedBehavior::DoStepPostEvents(RuntimeScene & scene)
|
||||||
|
{
|
||||||
|
leftPressedLastFrame = scene.GetInputManager().IsMouseButtonPressed("Left");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TrackedBehavior::OnDeActivate()
|
||||||
|
{
|
||||||
|
if (dragged) somethingDragged = false;
|
||||||
|
dragged = false;
|
||||||
|
}
|
47
Extensions/TrackedBehavior/TrackedBehavior.h
Normal file
47
Extensions/TrackedBehavior/TrackedBehavior.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
|
||||||
|
GDevelop - Tracked Behavior Extension
|
||||||
|
Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||||
|
This project is released under the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DRAGGABLEBEHAVIOR_H
|
||||||
|
#define DRAGGABLEBEHAVIOR_H
|
||||||
|
#include "GDCpp/Runtime/Project/Behavior.h"
|
||||||
|
#include "GDCpp/Runtime/Project/Object.h"
|
||||||
|
#include <SFML/System/Vector2.hpp>
|
||||||
|
#include <map>
|
||||||
|
class RuntimeScene;
|
||||||
|
namespace gd { class SerializerElement; }
|
||||||
|
namespace gd { class Layout; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief TODO
|
||||||
|
*/
|
||||||
|
class GD_EXTENSION_API TrackedBehavior : public Behavior
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TrackedBehavior();
|
||||||
|
virtual ~TrackedBehavior() {};
|
||||||
|
virtual Behavior* Clone() const { return new TrackedBehavior(*this); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Return true if the object is being dragged.
|
||||||
|
*/
|
||||||
|
bool IsDragged() const { return dragged; };
|
||||||
|
|
||||||
|
virtual void OnDeActivate();
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void DoStepPreEvents(RuntimeScene & scene);
|
||||||
|
virtual void DoStepPostEvents(RuntimeScene & scene);
|
||||||
|
|
||||||
|
float xOffset;
|
||||||
|
float yOffset;
|
||||||
|
std::size_t dragCameraIndex; ///< The camera being used to move the object. ( The layer is the object's layer ).
|
||||||
|
bool dragged; ///< True if the object is being dragged.
|
||||||
|
static bool somethingDragged; ///< Used to avoid start dragging an object while another is being dragged.
|
||||||
|
static bool leftPressedLastFrame; ///< Used to only start dragging when clicking.
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DRAGGABLEBEHAVIOR_H
|
@@ -0,0 +1,119 @@
|
|||||||
|
|
||||||
|
describe('gdjs.TrackedRuntimeBehavior', function() {
|
||||||
|
var runtimeGame = new gdjs.RuntimeGame({variables: [], properties: {windowWidth: 800, windowHeight: 600}});
|
||||||
|
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||||
|
runtimeScene.loadFromScene({
|
||||||
|
layers:[{name:"", visibility: true}],
|
||||||
|
variables: [],
|
||||||
|
behaviorsSharedData: [],
|
||||||
|
objects: [],
|
||||||
|
instances: []
|
||||||
|
});
|
||||||
|
|
||||||
|
var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [{type: "TrackedBehavior::Tracked"}]});
|
||||||
|
var object2 = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [{type: "TrackedBehavior::Tracked"}]});
|
||||||
|
runtimeScene.addObject(object);
|
||||||
|
runtimeScene.addObject(object2);
|
||||||
|
|
||||||
|
it('should handle mouse', function() {
|
||||||
|
object.setPosition(450, 500);
|
||||||
|
|
||||||
|
//Drag'n'drop
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
runtimeGame.getInputManager().onMouseMove(450, 500);
|
||||||
|
runtimeGame.getInputManager().onMouseButtonPressed(0);
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
runtimeGame.getInputManager().onMouseMove(750, 600);
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
runtimeGame.getInputManager().onMouseButtonReleased(0);
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
|
||||||
|
expect(object.getX()).to.be(750);
|
||||||
|
expect(object.getY()).to.be(600);
|
||||||
|
|
||||||
|
//Mouse move with dragging
|
||||||
|
runtimeGame.getInputManager().onMouseMove(600, 600);
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
|
||||||
|
expect(object.getX()).to.be(750);
|
||||||
|
expect(object.getY()).to.be(600);
|
||||||
|
|
||||||
|
//Start dragging again
|
||||||
|
runtimeGame.getInputManager().onMouseMove(750, 600);
|
||||||
|
runtimeGame.getInputManager().onMouseButtonPressed(0);
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
runtimeGame.getInputManager().onMouseMove(850, 700);
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
runtimeGame.getInputManager().onMouseButtonReleased(0);
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
|
||||||
|
expect(object.getX()).to.be(850);
|
||||||
|
expect(object.getY()).to.be(700);
|
||||||
|
|
||||||
|
});
|
||||||
|
it('should handle touches', function() {
|
||||||
|
runtimeGame.getInputManager().touchSimulateMouse(false);
|
||||||
|
object.setPosition(450, 500);
|
||||||
|
|
||||||
|
//Drag'n'drop
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
runtimeGame.getInputManager().onTouchStart(1, 10, 20);
|
||||||
|
runtimeGame.getInputManager().onTouchStart(0, 450, 500);
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
runtimeGame.getInputManager().onFrameEnded();
|
||||||
|
runtimeGame.getInputManager().onTouchMove(0, 750, 600);
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
runtimeGame.getInputManager().onFrameEnded();
|
||||||
|
runtimeGame.getInputManager().onTouchEnd(0);
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
runtimeGame.getInputManager().onFrameEnded();
|
||||||
|
|
||||||
|
expect(object.getX()).to.be(750);
|
||||||
|
expect(object.getY()).to.be(600);
|
||||||
|
|
||||||
|
//Move another unrelated touch
|
||||||
|
runtimeGame.getInputManager().onTouchMove(1, 750, 600);
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
runtimeGame.getInputManager().onTouchMove(1, 850, 700);
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
|
||||||
|
expect(object.getX()).to.be(750);
|
||||||
|
expect(object.getY()).to.be(600);
|
||||||
|
|
||||||
|
//Start drag'n'drop with another touch
|
||||||
|
runtimeGame.getInputManager().onTouchEnd(1);
|
||||||
|
runtimeGame.getInputManager().onFrameEnded();
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
runtimeGame.getInputManager().onTouchStart(1, 750, 600);
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
runtimeGame.getInputManager().onTouchMove(1, 850, 700);
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
runtimeGame.getInputManager().onTouchEnd(1);
|
||||||
|
runtimeGame.getInputManager().onFrameEnded();
|
||||||
|
|
||||||
|
expect(object.getX()).to.be(850);
|
||||||
|
expect(object.getY()).to.be(700);
|
||||||
|
});
|
||||||
|
it('should handle multitouch', function() {
|
||||||
|
runtimeGame.getInputManager().touchSimulateMouse(false);
|
||||||
|
object.setPosition(450, 500);
|
||||||
|
object2.setPosition(650, 600);
|
||||||
|
|
||||||
|
//Drag'n'drop
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
runtimeGame.getInputManager().onTouchStart(2, 450, 500);
|
||||||
|
runtimeGame.getInputManager().onTouchStart(1, 650, 600);
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
runtimeGame.getInputManager().onFrameEnded();
|
||||||
|
runtimeGame.getInputManager().onTouchMove(2, 750, 700);
|
||||||
|
runtimeGame.getInputManager().onTouchMove(1, 100, 200);
|
||||||
|
runtimeScene.renderAndStep();
|
||||||
|
runtimeGame.getInputManager().onFrameEnded();
|
||||||
|
runtimeGame.getInputManager().onTouchEnd(2);
|
||||||
|
|
||||||
|
expect(object.getX()).to.be(750);
|
||||||
|
expect(object.getY()).to.be(700);
|
||||||
|
expect(object2.getX()).to.be(100);
|
||||||
|
expect(object2.getY()).to.be(200);
|
||||||
|
});
|
||||||
|
});
|
171
Extensions/TrackedBehavior/trackedruntimebehavior.js
Normal file
171
Extensions/TrackedBehavior/trackedruntimebehavior.js
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
/**
|
||||||
|
GDevelop - Tracked Behavior Extension
|
||||||
|
Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages the tracked objects in a specialized data structure
|
||||||
|
* to ensure a fast retrieval of objects near a position.
|
||||||
|
*
|
||||||
|
* @class TrackedObjectsManager
|
||||||
|
* @namespace gdjs
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
gdjs.TrackedObjectsManager = function(runtimeScene, sharedData)
|
||||||
|
{
|
||||||
|
this._trackedObjectsRBush = new rbush(9, ['.owner.getAABB().min[0]', '.owner.getAABB().min[1]', '.owner.getAABB().max[0]', '.owner.getAABB().max[1]']);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the tracked objects manager of a scene.
|
||||||
|
*
|
||||||
|
* @method getManager
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
gdjs.TrackedObjectsManager.getManager = function(runtimeScene) {
|
||||||
|
if (!runtimeScene.trackedObjectsManager) { //Create the shared manager if necessary.
|
||||||
|
runtimeScene.trackedObjectsManager = new gdjs.TrackedObjectsManager(runtimeScene);
|
||||||
|
}
|
||||||
|
|
||||||
|
return runtimeScene.trackedObjectsManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an object to the data structure containing the tracked objects.
|
||||||
|
*
|
||||||
|
* @method addObject
|
||||||
|
*/
|
||||||
|
gdjs.TrackedObjectsManager.prototype.addObject = function(trackedBehavior) {
|
||||||
|
this._trackedObjectsRBush.insert(trackedBehavior);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an object being tracked.
|
||||||
|
*
|
||||||
|
* @method removeObject
|
||||||
|
*/
|
||||||
|
gdjs.TrackedObjectsManager.prototype.removeObject = function(trackedBehavior) {
|
||||||
|
this._trackedObjectsRBush.remove(trackedBehavior);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all the tracked objects around the specified position.
|
||||||
|
* @method getAllObjectsAround
|
||||||
|
*/
|
||||||
|
gdjs.TrackedObjectsManager.prototype.getAllObjectsAround = function(x, y, radius, result) {
|
||||||
|
var searchArea = gdjs.staticObject(gdjs.TrackedObjectsManager.prototype.getAllObjectsAround);
|
||||||
|
searchArea.minX = x - radius;
|
||||||
|
searchArea.minY = y - radius;
|
||||||
|
searchArea.maxX = x + radius;
|
||||||
|
searchArea.maxY = y + radius;
|
||||||
|
var nearbyBehaviors = this._trackedObjectsRBush.search(searchArea);
|
||||||
|
result.length = 0;
|
||||||
|
result.push.apply(result, nearbyBehaviors);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*
|
||||||
|
* @class TrackedRuntimeBehavior
|
||||||
|
* @namespace gdjs
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
gdjs.TrackedRuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
||||||
|
{
|
||||||
|
gdjs.RuntimeBehavior.call(this, runtimeScene, behaviorData, owner);
|
||||||
|
|
||||||
|
//Note that we can't use getX(), getWidth()... of owner here: The owner is not fully constructed.
|
||||||
|
this._oldX = 0;
|
||||||
|
this._oldY = 0;
|
||||||
|
this._oldWidth = 0;
|
||||||
|
this._oldHeight = 0;
|
||||||
|
|
||||||
|
this._manager = gdjs.TrackedObjectsManager.getManager(runtimeScene);
|
||||||
|
this._registeredInManager = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
gdjs.TrackedRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||||
|
gdjs.TrackedRuntimeBehavior.thisIsARuntimeBehaviorConstructor = "TrackedBehavior::Tracked";
|
||||||
|
|
||||||
|
// Count the number of objects with an updated position/size each frame.
|
||||||
|
// Any update requires the object to be removed/added again in the data structure used
|
||||||
|
// for spatial hashing and is costly, so this update count should be kept as low as possible.
|
||||||
|
gdjs.TrackedRuntimeBehavior.updateCount = 0;
|
||||||
|
|
||||||
|
gdjs.TrackedRuntimeBehavior.prototype.ownerRemovedFromScene = function() {
|
||||||
|
if ( this._manager && this._registeredInManager ) this._manager.removeObject(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
gdjs.TrackedRuntimeBehavior.prototype.doStepPreEvents = function(runtimeScene) {
|
||||||
|
//Make sure the object is or is not in the tracked objects manager.
|
||||||
|
if (!this.activated() && this._registeredInManager)
|
||||||
|
{
|
||||||
|
this._manager.removeObject(this);
|
||||||
|
this._registeredInManager = false;
|
||||||
|
}
|
||||||
|
else if (this.activated() && !this._registeredInManager)
|
||||||
|
{
|
||||||
|
this._manager.addObject(this);
|
||||||
|
this._registeredInManager = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Track changes in size or position
|
||||||
|
if (this._oldX !== this.owner.getX() || this._oldY !== this.owner.getY() ||
|
||||||
|
this._oldWidth !== this.owner.getWidth() || this._oldHeight !== this.owner.getHeight())
|
||||||
|
{
|
||||||
|
gdjs.TrackedRuntimeBehavior.updateCount++;
|
||||||
|
if ( this._registeredInManager ) {
|
||||||
|
this._manager.removeObject(this);
|
||||||
|
this._manager.addObject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._oldX = this.owner.getX();
|
||||||
|
this._oldY = this.owner.getY();
|
||||||
|
this._oldWidth = this.owner.getWidth();
|
||||||
|
this._oldHeight = this.owner.getHeight();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
gdjs.TrackedRuntimeBehavior.prototype.doStepPostEvents = function(runtimeScene) {
|
||||||
|
gdjs.TrackedRuntimeBehavior.updateCount = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
gdjs.TrackedRuntimeBehavior.prototype.getAABB = function(){
|
||||||
|
return this.owner.getAABB();
|
||||||
|
};
|
||||||
|
|
||||||
|
gdjs.TrackedRuntimeBehavior.prototype.onActivate = function() {
|
||||||
|
if (this._registeredInManager) return;
|
||||||
|
|
||||||
|
this._manager.addObject(this);
|
||||||
|
this._registeredInManager = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
gdjs.TrackedRuntimeBehavior.prototype.onDeActivate = function() {
|
||||||
|
if (!this._registeredInManager) return;
|
||||||
|
|
||||||
|
this._manager.removeObject(this);
|
||||||
|
this._registeredInManager = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
gdjs.TrackedRuntimeBehavior.aroundTest = function(runtimeScene, objectsLists, x, y, radius) {
|
||||||
|
var hasOneObject = false;
|
||||||
|
var nearbyBehaviors = gdjs.staticArray(gdjs.TrackedRuntimeBehavior.aroundTest);
|
||||||
|
gdjs.TrackedObjectsManager.getManager(runtimeScene).getAllObjectsAround(x, y, radius, nearbyBehaviors);
|
||||||
|
|
||||||
|
for(var i = 0;i<nearbyBehaviors.length;i++) {
|
||||||
|
var object = nearbyBehaviors[i].owner;
|
||||||
|
var objectList = objectsLists.get(object.name);
|
||||||
|
|
||||||
|
if (objectList && objectList.indexOf(object) === -1) {
|
||||||
|
hasOneObject = true;
|
||||||
|
objectList.push(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasOneObject;
|
||||||
|
};
|
||||||
|
|
||||||
|
gdjs.TrackedRuntimeBehavior.getUpdateCount = function(runtimeScene) {
|
||||||
|
return gdjs.TrackedRuntimeBehavior.updateCount;
|
||||||
|
}
|
@@ -57,6 +57,7 @@ CommonInstructionsExtension::CommonInstructionsExtension()
|
|||||||
//For example, two sub conditions using an object called "MyObject" will both have to declare a "MyObject" object list.
|
//For example, two sub conditions using an object called "MyObject" will both have to declare a "MyObject" object list.
|
||||||
gd::EventsCodeGenerationContext context;
|
gd::EventsCodeGenerationContext context;
|
||||||
context.InheritsFrom(parentContext);
|
context.InheritsFrom(parentContext);
|
||||||
|
context.ForbidReuse(); //TODO: This may not be necessary
|
||||||
|
|
||||||
gd::String conditionCode = codeGenerator.GenerateConditionCode(conditions[cId], "condition"+gd::String::From(cId)+"IsTrue", context);
|
gd::String conditionCode = codeGenerator.GenerateConditionCode(conditions[cId], "condition"+gd::String::From(cId)+"IsTrue", context);
|
||||||
|
|
||||||
@@ -244,6 +245,7 @@ CommonInstructionsExtension::CommonInstructionsExtension()
|
|||||||
//Context is "reset" each time the event is repeated ( i.e. objects are picked again )
|
//Context is "reset" each time the event is repeated ( i.e. objects are picked again )
|
||||||
gd::EventsCodeGenerationContext context;
|
gd::EventsCodeGenerationContext context;
|
||||||
context.InheritsFrom(parentContext);
|
context.InheritsFrom(parentContext);
|
||||||
|
context.ForbidReuse();
|
||||||
if ( event.HasInfiniteLoopWarning() && !codeGenerator.GenerateCodeForRuntime() ) codeGenerator.AddIncludeFile("GDCpp/Extensions/Builtin/RuntimeSceneTools.h");
|
if ( event.HasInfiniteLoopWarning() && !codeGenerator.GenerateCodeForRuntime() ) codeGenerator.AddIncludeFile("GDCpp/Extensions/Builtin/RuntimeSceneTools.h");
|
||||||
|
|
||||||
//Prepare codes
|
//Prepare codes
|
||||||
@@ -300,6 +302,7 @@ CommonInstructionsExtension::CommonInstructionsExtension()
|
|||||||
//Context is "reset" each time the event is repeated ( i.e. objects are picked again )
|
//Context is "reset" each time the event is repeated ( i.e. objects are picked again )
|
||||||
gd::EventsCodeGenerationContext context;
|
gd::EventsCodeGenerationContext context;
|
||||||
context.InheritsFrom(parentContext);
|
context.InheritsFrom(parentContext);
|
||||||
|
context.ForbidReuse();
|
||||||
|
|
||||||
//Prepare conditions/actions codes
|
//Prepare conditions/actions codes
|
||||||
gd::String conditionsCode = codeGenerator.GenerateConditionsListCode(event.GetConditions(), context);
|
gd::String conditionsCode = codeGenerator.GenerateConditionsListCode(event.GetConditions(), context);
|
||||||
@@ -347,6 +350,7 @@ CommonInstructionsExtension::CommonInstructionsExtension()
|
|||||||
//Context is "reset" each time the event is repeated ( i.e. objects are picked again )
|
//Context is "reset" each time the event is repeated ( i.e. objects are picked again )
|
||||||
gd::EventsCodeGenerationContext context;
|
gd::EventsCodeGenerationContext context;
|
||||||
context.InheritsFrom(parentContext);
|
context.InheritsFrom(parentContext);
|
||||||
|
context.ForbidReuse();
|
||||||
|
|
||||||
//Prepare conditions/actions codes
|
//Prepare conditions/actions codes
|
||||||
gd::String conditionsCode = codeGenerator.GenerateConditionsListCode(event.GetConditions(), context);
|
gd::String conditionsCode = codeGenerator.GenerateConditionsListCode(event.GetConditions(), context);
|
||||||
|
@@ -101,15 +101,15 @@ gd::String EventsCodeGenerator::GenerateSceneEventsCompleteCode(gd::Project & pr
|
|||||||
|
|
||||||
output +=
|
output +=
|
||||||
codeGenerator.GetCustomCodeOutsideMain()+"\n\n"
|
codeGenerator.GetCustomCodeOutsideMain()+"\n\n"
|
||||||
+globalObjectLists+"\n"
|
+ globalObjectLists+"\n"
|
||||||
+globalConditionsBooleans+"\n"
|
+ globalConditionsBooleans+"\n"
|
||||||
+"gdjs."+gd::SceneNameMangler::GetMangledSceneName(scene.GetName())+"Code.func = function(runtimeScene, context) {\n"
|
+ codeGenerator.GetCodeNamespace() + "func = function(runtimeScene, context) {\n"
|
||||||
+"context.startNewFrame();\n"
|
+ "context.startNewFrame();\n"
|
||||||
+globalObjectListsReset+"\n"
|
+ globalObjectListsReset+"\n"
|
||||||
+codeGenerator.GetCustomCodeInMain()
|
+ codeGenerator.GetCustomCodeInMain()
|
||||||
+wholeEventsCode
|
+ wholeEventsCode
|
||||||
+"return;\n"
|
+ "return;\n"
|
||||||
+"}\n";
|
+ "}\n";
|
||||||
|
|
||||||
//Export the symbols to avoid them being stripped by the Closure Compiler:
|
//Export the symbols to avoid them being stripped by the Closure Compiler:
|
||||||
output += "gdjs['"+gd::SceneNameMangler::GetMangledSceneName(scene.GetName())+"Code']"
|
output += "gdjs['"+gd::SceneNameMangler::GetMangledSceneName(scene.GetName())+"Code']"
|
||||||
@@ -391,41 +391,77 @@ gd::String EventsCodeGenerator::GetObjectListName(const gd::String & name, const
|
|||||||
|
|
||||||
gd::String EventsCodeGenerator::GenerateObjectsDeclarationCode(gd::EventsCodeGenerationContext & context)
|
gd::String EventsCodeGenerator::GenerateObjectsDeclarationCode(gd::EventsCodeGenerationContext & context)
|
||||||
{
|
{
|
||||||
|
auto declareObjectList = [this](gd::String object, gd::EventsCodeGenerationContext & context) {
|
||||||
|
gd::String newListName = GetObjectListName(object, context);
|
||||||
|
if (!context.GetParentContext())
|
||||||
|
{
|
||||||
|
std::cout << "ERROR: During code generation, a context tried to use an already declared object list without having a parent" << std::endl;
|
||||||
|
return "/* Could not declare " + newListName + " */";
|
||||||
|
}
|
||||||
|
|
||||||
|
//*Optimization*: Avoid expensive copy of the object list if we're using
|
||||||
|
//the same list as the one from the parent context.
|
||||||
|
gd::String copiedListName = GetObjectListName(object, *context.GetParentContext());
|
||||||
|
if (newListName == copiedListName)
|
||||||
|
return "/* Reuse " + copiedListName + " */";
|
||||||
|
|
||||||
|
return newListName + ".createFrom("+copiedListName+");\n";
|
||||||
|
};
|
||||||
|
|
||||||
gd::String declarationsCode;
|
gd::String declarationsCode;
|
||||||
for ( set<gd::String>::iterator it = context.GetObjectsListsToBeDeclared().begin() ; it != context.GetObjectsListsToBeDeclared().end(); ++it )
|
for (auto object : context.GetObjectsListsToBeDeclared())
|
||||||
{
|
{
|
||||||
declarationsCode += GetObjectListName(*it, context);
|
gd::String objectListDeclaration = "";
|
||||||
if ( !context.ObjectAlreadyDeclared(*it) )
|
if ( !context.ObjectAlreadyDeclared(object) )
|
||||||
{
|
{
|
||||||
declarationsCode += ".createFrom(runtimeScene.getObjects(\""+ConvertToString(*it)+"\"));\n";
|
objectListDeclaration += GetObjectListName(object, context) + ".createFrom(runtimeScene.getObjects(\"" + ConvertToString(object) + "\"));";
|
||||||
context.SetObjectDeclared(*it);
|
context.SetObjectDeclared(object);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
objectListDeclaration = declareObjectList(object, context);
|
||||||
if (context.GetParentContext())
|
|
||||||
declarationsCode += ".createFrom("+GetObjectListName(*it, *context.GetParentContext())+");\n";
|
declarationsCode += objectListDeclaration + "\n";
|
||||||
else
|
|
||||||
std::cout << "ERROR: During code generation, a context tried tried to use an already declared object list without having a parent" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for ( set<gd::String>::iterator it = context.GetObjectsListsToBeDeclaredEmpty().begin() ; it != context.GetObjectsListsToBeDeclaredEmpty().end(); ++it )
|
for (auto object : context.GetObjectsListsToBeDeclaredEmpty())
|
||||||
{
|
{
|
||||||
declarationsCode += GetObjectListName(*it, context);
|
gd::String objectListDeclaration = "";
|
||||||
if ( !context.ObjectAlreadyDeclared(*it) )
|
if ( !context.ObjectAlreadyDeclared(object) )
|
||||||
{
|
{
|
||||||
declarationsCode +=".length = 0;\n";
|
objectListDeclaration = GetObjectListName(object, context) + ".length = 0;\n";
|
||||||
context.SetObjectDeclared(*it);
|
context.SetObjectDeclared(object);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
objectListDeclaration = declareObjectList(object, context);
|
||||||
if (context.GetParentContext())
|
|
||||||
declarationsCode += ".createFrom("+GetObjectListName(*it, *context.GetParentContext())+");\n";
|
declarationsCode += objectListDeclaration + "\n";
|
||||||
else
|
|
||||||
std::cout << "ERROR: During code generation, a context tried tried to use an already declared object list without having a parent" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return declarationsCode ;
|
return declarationsCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
gd::String EventsCodeGenerator::GenerateEventsListCode(gd::EventsList & events, const gd::EventsCodeGenerationContext & context)
|
||||||
|
{
|
||||||
|
// *Optimization*: generating all JS code of events in a single, enormous function is
|
||||||
|
// badly handled by JS engines and in particular the garbage collectors,
|
||||||
|
// leading to intermittent lag/freeze while the garbage collector is running.
|
||||||
|
// This is especially noticeable on Android devices. To reduce the stress on the JS
|
||||||
|
// engines, we generate a new function for each list of events.
|
||||||
|
|
||||||
|
gd::String code = gd::EventsCodeGenerator::GenerateEventsListCode(events, context);
|
||||||
|
|
||||||
|
// Generate a unique name for the function.
|
||||||
|
gd::String functionName = GetCodeNamespace() + "eventsList" + gd::String::From(&events);
|
||||||
|
AddCustomCodeOutsideMain(
|
||||||
|
// The only local parameters are runtimeScene and context.
|
||||||
|
// List of objects, conditions booleans and any variables used by events are stored
|
||||||
|
// in static variables that are globally available by the whole code.
|
||||||
|
functionName + " = function(runtimeScene, context) {\n" +
|
||||||
|
code + "\n" +
|
||||||
|
"}; //End of " + functionName+"\n");
|
||||||
|
|
||||||
|
// Replace the code of the events by the call to the function. This does not
|
||||||
|
// interfere with the objects picking as the lists are in static variables globally available.
|
||||||
|
return functionName+"(runtimeScene, context);";
|
||||||
}
|
}
|
||||||
|
|
||||||
gd::String EventsCodeGenerator::GenerateConditionsListCode(gd::InstructionsList & conditions, gd::EventsCodeGenerationContext & context)
|
gd::String EventsCodeGenerator::GenerateConditionsListCode(gd::InstructionsList & conditions, gd::EventsCodeGenerationContext & context)
|
||||||
|
@@ -42,6 +42,19 @@ public:
|
|||||||
const gd::Layout & scene, const gd::EventsList & events,
|
const gd::Layout & scene, const gd::EventsList & events,
|
||||||
std::set < gd::String > & includeFiles, bool compilationForRuntime = false);
|
std::set < gd::String > & includeFiles, bool compilationForRuntime = false);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Generate code for executing an event list
|
||||||
|
* \note To reduce the stress on JS engines, the code is generated inside
|
||||||
|
* a separate JS function (see gd::EventsCodeGenerator::AddCustomCodeOutsideMain).
|
||||||
|
* This method will return the code to call this separate function.
|
||||||
|
*
|
||||||
|
* \param events std::vector of events
|
||||||
|
* \param context Context used for generation
|
||||||
|
* \return Code
|
||||||
|
*/
|
||||||
|
virtual gd::String GenerateEventsListCode(gd::EventsList & events, const gd::EventsCodeGenerationContext & context);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate code for executing a condition list
|
* Generate code for executing a condition list
|
||||||
*
|
*
|
||||||
|
@@ -97,6 +97,7 @@ CommonInstructionsExtension::CommonInstructionsExtension()
|
|||||||
//For example, two sub conditions using an object called "MyObject" will both have to declare a "MyObject" object list.
|
//For example, two sub conditions using an object called "MyObject" will both have to declare a "MyObject" object list.
|
||||||
gd::EventsCodeGenerationContext context;
|
gd::EventsCodeGenerationContext context;
|
||||||
context.InheritsFrom(parentContext);
|
context.InheritsFrom(parentContext);
|
||||||
|
context.ForbidReuse(); //TODO: This may not be necessary
|
||||||
|
|
||||||
gd::String conditionCode = codeGenerator.GenerateConditionCode(conditions[cId], "condition"+gd::String::From(cId)+"IsTrue", context);
|
gd::String conditionCode = codeGenerator.GenerateConditionCode(conditions[cId], "condition"+gd::String::From(cId)+"IsTrue", context);
|
||||||
|
|
||||||
@@ -231,6 +232,7 @@ CommonInstructionsExtension::CommonInstructionsExtension()
|
|||||||
//Context is "reset" each time the event is repeated ( i.e. objects are picked again )
|
//Context is "reset" each time the event is repeated ( i.e. objects are picked again )
|
||||||
gd::EventsCodeGenerationContext context;
|
gd::EventsCodeGenerationContext context;
|
||||||
context.InheritsFrom(parentContext);
|
context.InheritsFrom(parentContext);
|
||||||
|
context.ForbidReuse();
|
||||||
|
|
||||||
//Prepare codes
|
//Prepare codes
|
||||||
gd::String whileConditionsStr = codeGenerator.GenerateConditionsListCode(event.GetWhileConditions(), context);
|
gd::String whileConditionsStr = codeGenerator.GenerateConditionsListCode(event.GetWhileConditions(), context);
|
||||||
@@ -285,6 +287,7 @@ CommonInstructionsExtension::CommonInstructionsExtension()
|
|||||||
//Context is "reset" each time the event is repeated ( i.e. objects are picked again )
|
//Context is "reset" each time the event is repeated ( i.e. objects are picked again )
|
||||||
gd::EventsCodeGenerationContext context;
|
gd::EventsCodeGenerationContext context;
|
||||||
context.InheritsFrom(parentContext);
|
context.InheritsFrom(parentContext);
|
||||||
|
context.ForbidReuse();
|
||||||
|
|
||||||
//Prepare conditions/actions codes
|
//Prepare conditions/actions codes
|
||||||
gd::String conditionsCode = codeGenerator.GenerateConditionsListCode(event.GetConditions(), context);
|
gd::String conditionsCode = codeGenerator.GenerateConditionsListCode(event.GetConditions(), context);
|
||||||
@@ -339,6 +342,7 @@ CommonInstructionsExtension::CommonInstructionsExtension()
|
|||||||
//Context is "reset" each time the event is repeated ( i.e. objects are picked again )
|
//Context is "reset" each time the event is repeated ( i.e. objects are picked again )
|
||||||
gd::EventsCodeGenerationContext context;
|
gd::EventsCodeGenerationContext context;
|
||||||
context.InheritsFrom(parentContext);
|
context.InheritsFrom(parentContext);
|
||||||
|
context.ForbidReuse();
|
||||||
|
|
||||||
for (unsigned int i = 0;i<realObjects.size();++i)
|
for (unsigned int i = 0;i<realObjects.size();++i)
|
||||||
context.ObjectsListNeeded(realObjects[i]);
|
context.ObjectsListNeeded(realObjects[i]);
|
||||||
|
@@ -320,6 +320,7 @@ bool ExporterHelper::ExportEventsCode(gd::Project & project, gd::String outputDi
|
|||||||
InsertUnique(includesFiles, "libs/jshashtable.js");
|
InsertUnique(includesFiles, "libs/jshashtable.js");
|
||||||
InsertUnique(includesFiles, "gd.js");
|
InsertUnique(includesFiles, "gd.js");
|
||||||
InsertUnique(includesFiles, "libs/hshg.js");
|
InsertUnique(includesFiles, "libs/hshg.js");
|
||||||
|
InsertUnique(includesFiles, "libs/rbush.js");
|
||||||
InsertUnique(includesFiles, "inputmanager.js");
|
InsertUnique(includesFiles, "inputmanager.js");
|
||||||
InsertUnique(includesFiles, "timemanager.js");
|
InsertUnique(includesFiles, "timemanager.js");
|
||||||
InsertUnique(includesFiles, "runtimeobject.js");
|
InsertUnique(includesFiles, "runtimeobject.js");
|
||||||
|
624
GDJS/Runtime/libs/rbush.js
Normal file
624
GDJS/Runtime/libs/rbush.js
Normal file
@@ -0,0 +1,624 @@
|
|||||||
|
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.rbush = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = rbush;
|
||||||
|
|
||||||
|
var quickselect = require('quickselect');
|
||||||
|
|
||||||
|
function rbush(maxEntries, format) {
|
||||||
|
if (!(this instanceof rbush)) return new rbush(maxEntries, format);
|
||||||
|
|
||||||
|
// max entries in a node is 9 by default; min node fill is 40% for best performance
|
||||||
|
this._maxEntries = Math.max(4, maxEntries || 9);
|
||||||
|
this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
|
||||||
|
|
||||||
|
if (format) {
|
||||||
|
this._initFormat(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
rbush.prototype = {
|
||||||
|
|
||||||
|
all: function () {
|
||||||
|
return this._all(this.data, []);
|
||||||
|
},
|
||||||
|
|
||||||
|
search: function (bbox) {
|
||||||
|
|
||||||
|
var node = this.data,
|
||||||
|
result = [],
|
||||||
|
toBBox = this.toBBox;
|
||||||
|
|
||||||
|
if (!intersects(bbox, node)) return result;
|
||||||
|
|
||||||
|
var nodesToSearch = [],
|
||||||
|
i, len, child, childBBox;
|
||||||
|
|
||||||
|
while (node) {
|
||||||
|
for (i = 0, len = node.children.length; i < len; i++) {
|
||||||
|
|
||||||
|
child = node.children[i];
|
||||||
|
childBBox = node.leaf ? toBBox(child) : child;
|
||||||
|
|
||||||
|
if (intersects(bbox, childBBox)) {
|
||||||
|
if (node.leaf) result.push(child);
|
||||||
|
else if (contains(bbox, childBBox)) this._all(child, result);
|
||||||
|
else nodesToSearch.push(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node = nodesToSearch.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
collides: function (bbox) {
|
||||||
|
|
||||||
|
var node = this.data,
|
||||||
|
toBBox = this.toBBox;
|
||||||
|
|
||||||
|
if (!intersects(bbox, node)) return false;
|
||||||
|
|
||||||
|
var nodesToSearch = [],
|
||||||
|
i, len, child, childBBox;
|
||||||
|
|
||||||
|
while (node) {
|
||||||
|
for (i = 0, len = node.children.length; i < len; i++) {
|
||||||
|
|
||||||
|
child = node.children[i];
|
||||||
|
childBBox = node.leaf ? toBBox(child) : child;
|
||||||
|
|
||||||
|
if (intersects(bbox, childBBox)) {
|
||||||
|
if (node.leaf || contains(bbox, childBBox)) return true;
|
||||||
|
nodesToSearch.push(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node = nodesToSearch.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
load: function (data) {
|
||||||
|
if (!(data && data.length)) return this;
|
||||||
|
|
||||||
|
if (data.length < this._minEntries) {
|
||||||
|
for (var i = 0, len = data.length; i < len; i++) {
|
||||||
|
this.insert(data[i]);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recursively build the tree with the given data from stratch using OMT algorithm
|
||||||
|
var node = this._build(data.slice(), 0, data.length - 1, 0);
|
||||||
|
|
||||||
|
if (!this.data.children.length) {
|
||||||
|
// save as is if tree is empty
|
||||||
|
this.data = node;
|
||||||
|
|
||||||
|
} else if (this.data.height === node.height) {
|
||||||
|
// split root if trees have the same height
|
||||||
|
this._splitRoot(this.data, node);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (this.data.height < node.height) {
|
||||||
|
// swap trees if inserted one is bigger
|
||||||
|
var tmpNode = this.data;
|
||||||
|
this.data = node;
|
||||||
|
node = tmpNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert the small tree into the large tree at appropriate level
|
||||||
|
this._insert(node, this.data.height - node.height - 1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
insert: function (item) {
|
||||||
|
if (item) this._insert(item, this.data.height - 1);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
clear: function () {
|
||||||
|
this.data = createNode([]);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
remove: function (item, equalsFn) {
|
||||||
|
if (!item) return this;
|
||||||
|
|
||||||
|
var node = this.data,
|
||||||
|
bbox = this.toBBox(item),
|
||||||
|
path = [],
|
||||||
|
indexes = [],
|
||||||
|
i, parent, index, goingUp;
|
||||||
|
|
||||||
|
// depth-first iterative tree traversal
|
||||||
|
while (node || path.length) {
|
||||||
|
|
||||||
|
if (!node) { // go up
|
||||||
|
node = path.pop();
|
||||||
|
parent = path[path.length - 1];
|
||||||
|
i = indexes.pop();
|
||||||
|
goingUp = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.leaf) { // check current node
|
||||||
|
index = findItem(item, node.children, equalsFn);
|
||||||
|
|
||||||
|
if (index !== -1) {
|
||||||
|
// item found, remove the item and condense tree upwards
|
||||||
|
node.children.splice(index, 1);
|
||||||
|
path.push(node);
|
||||||
|
this._condense(path);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!goingUp && !node.leaf && contains(node, bbox)) { // go down
|
||||||
|
path.push(node);
|
||||||
|
indexes.push(i);
|
||||||
|
i = 0;
|
||||||
|
parent = node;
|
||||||
|
node = node.children[0];
|
||||||
|
|
||||||
|
} else if (parent) { // go right
|
||||||
|
i++;
|
||||||
|
node = parent.children[i];
|
||||||
|
goingUp = false;
|
||||||
|
|
||||||
|
} else node = null; // nothing found
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
toBBox: function (item) { return item; },
|
||||||
|
|
||||||
|
compareMinX: compareNodeMinX,
|
||||||
|
compareMinY: compareNodeMinY,
|
||||||
|
|
||||||
|
toJSON: function () { return this.data; },
|
||||||
|
|
||||||
|
fromJSON: function (data) {
|
||||||
|
this.data = data;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
_all: function (node, result) {
|
||||||
|
var nodesToSearch = [];
|
||||||
|
while (node) {
|
||||||
|
if (node.leaf) result.push.apply(result, node.children);
|
||||||
|
else nodesToSearch.push.apply(nodesToSearch, node.children);
|
||||||
|
|
||||||
|
node = nodesToSearch.pop();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
_build: function (items, left, right, height) {
|
||||||
|
|
||||||
|
var N = right - left + 1,
|
||||||
|
M = this._maxEntries,
|
||||||
|
node;
|
||||||
|
|
||||||
|
if (N <= M) {
|
||||||
|
// reached leaf level; return leaf
|
||||||
|
node = createNode(items.slice(left, right + 1));
|
||||||
|
calcBBox(node, this.toBBox);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!height) {
|
||||||
|
// target height of the bulk-loaded tree
|
||||||
|
height = Math.ceil(Math.log(N) / Math.log(M));
|
||||||
|
|
||||||
|
// target number of root entries to maximize storage utilization
|
||||||
|
M = Math.ceil(N / Math.pow(M, height - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
node = createNode([]);
|
||||||
|
node.leaf = false;
|
||||||
|
node.height = height;
|
||||||
|
|
||||||
|
// split the items into M mostly square tiles
|
||||||
|
|
||||||
|
var N2 = Math.ceil(N / M),
|
||||||
|
N1 = N2 * Math.ceil(Math.sqrt(M)),
|
||||||
|
i, j, right2, right3;
|
||||||
|
|
||||||
|
multiSelect(items, left, right, N1, this.compareMinX);
|
||||||
|
|
||||||
|
for (i = left; i <= right; i += N1) {
|
||||||
|
|
||||||
|
right2 = Math.min(i + N1 - 1, right);
|
||||||
|
|
||||||
|
multiSelect(items, i, right2, N2, this.compareMinY);
|
||||||
|
|
||||||
|
for (j = i; j <= right2; j += N2) {
|
||||||
|
|
||||||
|
right3 = Math.min(j + N2 - 1, right2);
|
||||||
|
|
||||||
|
// pack each entry recursively
|
||||||
|
node.children.push(this._build(items, j, right3, height - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
calcBBox(node, this.toBBox);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
|
||||||
|
_chooseSubtree: function (bbox, node, level, path) {
|
||||||
|
|
||||||
|
var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
path.push(node);
|
||||||
|
|
||||||
|
if (node.leaf || path.length - 1 === level) break;
|
||||||
|
|
||||||
|
minArea = minEnlargement = Infinity;
|
||||||
|
|
||||||
|
for (i = 0, len = node.children.length; i < len; i++) {
|
||||||
|
child = node.children[i];
|
||||||
|
area = bboxArea(child);
|
||||||
|
enlargement = enlargedArea(bbox, child) - area;
|
||||||
|
|
||||||
|
// choose entry with the least area enlargement
|
||||||
|
if (enlargement < minEnlargement) {
|
||||||
|
minEnlargement = enlargement;
|
||||||
|
minArea = area < minArea ? area : minArea;
|
||||||
|
targetNode = child;
|
||||||
|
|
||||||
|
} else if (enlargement === minEnlargement) {
|
||||||
|
// otherwise choose one with the smallest area
|
||||||
|
if (area < minArea) {
|
||||||
|
minArea = area;
|
||||||
|
targetNode = child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node = targetNode || node.children[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
|
||||||
|
_insert: function (item, level, isNode) {
|
||||||
|
|
||||||
|
var toBBox = this.toBBox,
|
||||||
|
bbox = isNode ? item : toBBox(item),
|
||||||
|
insertPath = [];
|
||||||
|
|
||||||
|
// find the best node for accommodating the item, saving all nodes along the path too
|
||||||
|
var node = this._chooseSubtree(bbox, this.data, level, insertPath);
|
||||||
|
|
||||||
|
// put the item into the node
|
||||||
|
node.children.push(item);
|
||||||
|
extend(node, bbox);
|
||||||
|
|
||||||
|
// split on node overflow; propagate upwards if necessary
|
||||||
|
while (level >= 0) {
|
||||||
|
if (insertPath[level].children.length > this._maxEntries) {
|
||||||
|
this._split(insertPath, level);
|
||||||
|
level--;
|
||||||
|
} else break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjust bboxes along the insertion path
|
||||||
|
this._adjustParentBBoxes(bbox, insertPath, level);
|
||||||
|
},
|
||||||
|
|
||||||
|
// split overflowed node into two
|
||||||
|
_split: function (insertPath, level) {
|
||||||
|
|
||||||
|
var node = insertPath[level],
|
||||||
|
M = node.children.length,
|
||||||
|
m = this._minEntries;
|
||||||
|
|
||||||
|
this._chooseSplitAxis(node, m, M);
|
||||||
|
|
||||||
|
var splitIndex = this._chooseSplitIndex(node, m, M);
|
||||||
|
|
||||||
|
var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
|
||||||
|
newNode.height = node.height;
|
||||||
|
newNode.leaf = node.leaf;
|
||||||
|
|
||||||
|
calcBBox(node, this.toBBox);
|
||||||
|
calcBBox(newNode, this.toBBox);
|
||||||
|
|
||||||
|
if (level) insertPath[level - 1].children.push(newNode);
|
||||||
|
else this._splitRoot(node, newNode);
|
||||||
|
},
|
||||||
|
|
||||||
|
_splitRoot: function (node, newNode) {
|
||||||
|
// split root node
|
||||||
|
this.data = createNode([node, newNode]);
|
||||||
|
this.data.height = node.height + 1;
|
||||||
|
this.data.leaf = false;
|
||||||
|
calcBBox(this.data, this.toBBox);
|
||||||
|
},
|
||||||
|
|
||||||
|
_chooseSplitIndex: function (node, m, M) {
|
||||||
|
|
||||||
|
var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
|
||||||
|
|
||||||
|
minOverlap = minArea = Infinity;
|
||||||
|
|
||||||
|
for (i = m; i <= M - m; i++) {
|
||||||
|
bbox1 = distBBox(node, 0, i, this.toBBox);
|
||||||
|
bbox2 = distBBox(node, i, M, this.toBBox);
|
||||||
|
|
||||||
|
overlap = intersectionArea(bbox1, bbox2);
|
||||||
|
area = bboxArea(bbox1) + bboxArea(bbox2);
|
||||||
|
|
||||||
|
// choose distribution with minimum overlap
|
||||||
|
if (overlap < minOverlap) {
|
||||||
|
minOverlap = overlap;
|
||||||
|
index = i;
|
||||||
|
|
||||||
|
minArea = area < minArea ? area : minArea;
|
||||||
|
|
||||||
|
} else if (overlap === minOverlap) {
|
||||||
|
// otherwise choose distribution with minimum area
|
||||||
|
if (area < minArea) {
|
||||||
|
minArea = area;
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
},
|
||||||
|
|
||||||
|
// sorts node children by the best axis for split
|
||||||
|
_chooseSplitAxis: function (node, m, M) {
|
||||||
|
|
||||||
|
var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX,
|
||||||
|
compareMinY = node.leaf ? this.compareMinY : compareNodeMinY,
|
||||||
|
xMargin = this._allDistMargin(node, m, M, compareMinX),
|
||||||
|
yMargin = this._allDistMargin(node, m, M, compareMinY);
|
||||||
|
|
||||||
|
// if total distributions margin value is minimal for x, sort by minX,
|
||||||
|
// otherwise it's already sorted by minY
|
||||||
|
if (xMargin < yMargin) node.children.sort(compareMinX);
|
||||||
|
},
|
||||||
|
|
||||||
|
// total margin of all possible split distributions where each node is at least m full
|
||||||
|
_allDistMargin: function (node, m, M, compare) {
|
||||||
|
|
||||||
|
node.children.sort(compare);
|
||||||
|
|
||||||
|
var toBBox = this.toBBox,
|
||||||
|
leftBBox = distBBox(node, 0, m, toBBox),
|
||||||
|
rightBBox = distBBox(node, M - m, M, toBBox),
|
||||||
|
margin = bboxMargin(leftBBox) + bboxMargin(rightBBox),
|
||||||
|
i, child;
|
||||||
|
|
||||||
|
for (i = m; i < M - m; i++) {
|
||||||
|
child = node.children[i];
|
||||||
|
extend(leftBBox, node.leaf ? toBBox(child) : child);
|
||||||
|
margin += bboxMargin(leftBBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = M - m - 1; i >= m; i--) {
|
||||||
|
child = node.children[i];
|
||||||
|
extend(rightBBox, node.leaf ? toBBox(child) : child);
|
||||||
|
margin += bboxMargin(rightBBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
return margin;
|
||||||
|
},
|
||||||
|
|
||||||
|
_adjustParentBBoxes: function (bbox, path, level) {
|
||||||
|
// adjust bboxes along the given tree path
|
||||||
|
for (var i = level; i >= 0; i--) {
|
||||||
|
extend(path[i], bbox);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_condense: function (path) {
|
||||||
|
// go through the path, removing empty nodes and updating bboxes
|
||||||
|
for (var i = path.length - 1, siblings; i >= 0; i--) {
|
||||||
|
if (path[i].children.length === 0) {
|
||||||
|
if (i > 0) {
|
||||||
|
siblings = path[i - 1].children;
|
||||||
|
siblings.splice(siblings.indexOf(path[i]), 1);
|
||||||
|
|
||||||
|
} else this.clear();
|
||||||
|
|
||||||
|
} else calcBBox(path[i], this.toBBox);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_initFormat: function (format) {
|
||||||
|
// data format (minX, minY, maxX, maxY accessors)
|
||||||
|
|
||||||
|
// uses eval-type function compilation instead of just accepting a toBBox function
|
||||||
|
// because the algorithms are very sensitive to sorting functions performance,
|
||||||
|
// so they should be dead simple and without inner calls
|
||||||
|
|
||||||
|
var compareArr = ['return a', ' - b', ';'];
|
||||||
|
|
||||||
|
this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
|
||||||
|
this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
|
||||||
|
|
||||||
|
this.toBBox = new Function('a',
|
||||||
|
'return {minX: a' + format[0] +
|
||||||
|
', minY: a' + format[1] +
|
||||||
|
', maxX: a' + format[2] +
|
||||||
|
', maxY: a' + format[3] + '};');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function findItem(item, items, equalsFn) {
|
||||||
|
if (!equalsFn) return items.indexOf(item);
|
||||||
|
|
||||||
|
for (var i = 0; i < items.length; i++) {
|
||||||
|
if (equalsFn(item, items[i])) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate node's bbox from bboxes of its children
|
||||||
|
function calcBBox(node, toBBox) {
|
||||||
|
distBBox(node, 0, node.children.length, toBBox, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// min bounding rectangle of node children from k to p-1
|
||||||
|
function distBBox(node, k, p, toBBox, destNode) {
|
||||||
|
if (!destNode) destNode = createNode(null);
|
||||||
|
destNode.minX = Infinity;
|
||||||
|
destNode.minY = Infinity;
|
||||||
|
destNode.maxX = -Infinity;
|
||||||
|
destNode.maxY = -Infinity;
|
||||||
|
|
||||||
|
for (var i = k, child; i < p; i++) {
|
||||||
|
child = node.children[i];
|
||||||
|
extend(destNode, node.leaf ? toBBox(child) : child);
|
||||||
|
}
|
||||||
|
|
||||||
|
return destNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function extend(a, b) {
|
||||||
|
a.minX = Math.min(a.minX, b.minX);
|
||||||
|
a.minY = Math.min(a.minY, b.minY);
|
||||||
|
a.maxX = Math.max(a.maxX, b.maxX);
|
||||||
|
a.maxY = Math.max(a.maxY, b.maxY);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareNodeMinX(a, b) { return a.minX - b.minX; }
|
||||||
|
function compareNodeMinY(a, b) { return a.minY - b.minY; }
|
||||||
|
|
||||||
|
function bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); }
|
||||||
|
function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
|
||||||
|
|
||||||
|
function enlargedArea(a, b) {
|
||||||
|
return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
|
||||||
|
(Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
|
||||||
|
}
|
||||||
|
|
||||||
|
function intersectionArea(a, b) {
|
||||||
|
var minX = Math.max(a.minX, b.minX),
|
||||||
|
minY = Math.max(a.minY, b.minY),
|
||||||
|
maxX = Math.min(a.maxX, b.maxX),
|
||||||
|
maxY = Math.min(a.maxY, b.maxY);
|
||||||
|
|
||||||
|
return Math.max(0, maxX - minX) *
|
||||||
|
Math.max(0, maxY - minY);
|
||||||
|
}
|
||||||
|
|
||||||
|
function contains(a, b) {
|
||||||
|
return a.minX <= b.minX &&
|
||||||
|
a.minY <= b.minY &&
|
||||||
|
b.maxX <= a.maxX &&
|
||||||
|
b.maxY <= a.maxY;
|
||||||
|
}
|
||||||
|
|
||||||
|
function intersects(a, b) {
|
||||||
|
return b.minX <= a.maxX &&
|
||||||
|
b.minY <= a.maxY &&
|
||||||
|
b.maxX >= a.minX &&
|
||||||
|
b.maxY >= a.minY;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createNode(children) {
|
||||||
|
return {
|
||||||
|
children: children,
|
||||||
|
height: 1,
|
||||||
|
leaf: true,
|
||||||
|
minX: Infinity,
|
||||||
|
minY: Infinity,
|
||||||
|
maxX: -Infinity,
|
||||||
|
maxY: -Infinity
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
|
||||||
|
// combines selection algorithm with binary divide & conquer approach
|
||||||
|
|
||||||
|
function multiSelect(arr, left, right, n, compare) {
|
||||||
|
var stack = [left, right],
|
||||||
|
mid;
|
||||||
|
|
||||||
|
while (stack.length) {
|
||||||
|
right = stack.pop();
|
||||||
|
left = stack.pop();
|
||||||
|
|
||||||
|
if (right - left <= n) continue;
|
||||||
|
|
||||||
|
mid = left + Math.ceil((right - left) / n / 2) * n;
|
||||||
|
quickselect(arr, mid, left, right, compare);
|
||||||
|
|
||||||
|
stack.push(left, mid, mid, right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},{"quickselect":2}],2:[function(require,module,exports){
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = partialSort;
|
||||||
|
|
||||||
|
// Floyd-Rivest selection algorithm:
|
||||||
|
// Rearrange items so that all items in the [left, k] range are smaller than all items in (k, right];
|
||||||
|
// The k-th element will have the (k - left + 1)th smallest value in [left, right]
|
||||||
|
|
||||||
|
function partialSort(arr, k, left, right, compare) {
|
||||||
|
|
||||||
|
while (right > left) {
|
||||||
|
if (right - left > 600) {
|
||||||
|
var n = right - left + 1;
|
||||||
|
var m = k - left + 1;
|
||||||
|
var z = Math.log(n);
|
||||||
|
var s = 0.5 * Math.exp(2 * z / 3);
|
||||||
|
var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
|
||||||
|
var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
|
||||||
|
var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
|
||||||
|
partialSort(arr, k, newLeft, newRight, compare);
|
||||||
|
}
|
||||||
|
|
||||||
|
var t = arr[k];
|
||||||
|
var i = left;
|
||||||
|
var j = right;
|
||||||
|
|
||||||
|
swap(arr, left, k);
|
||||||
|
if (compare(arr[right], t) > 0) swap(arr, left, right);
|
||||||
|
|
||||||
|
while (i < j) {
|
||||||
|
swap(arr, i, j);
|
||||||
|
i++;
|
||||||
|
j--;
|
||||||
|
while (compare(arr[i], t) < 0) i++;
|
||||||
|
while (compare(arr[j], t) > 0) j--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compare(arr[left], t) === 0) swap(arr, left, j);
|
||||||
|
else {
|
||||||
|
j++;
|
||||||
|
swap(arr, j, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j <= k) left = j + 1;
|
||||||
|
if (k <= j) right = j - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function swap(arr, i, j) {
|
||||||
|
var tmp = arr[i];
|
||||||
|
arr[i] = arr[j];
|
||||||
|
arr[j] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultCompare(a, b) {
|
||||||
|
return a < b ? -1 : a > b ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
},{}]},{},[1])(1)
|
||||||
|
});
|
@@ -360,14 +360,24 @@ gdjs.RuntimeScene.prototype._updateObjectsPreEvents = function() {
|
|||||||
gdjs.RuntimeScene.prototype._updateObjects = function() {
|
gdjs.RuntimeScene.prototype._updateObjects = function() {
|
||||||
this._cacheOrClearRemovedInstances();
|
this._cacheOrClearRemovedInstances();
|
||||||
|
|
||||||
this.updateObjectsForces();
|
|
||||||
|
|
||||||
//It is *mandatory* to create and iterate on a external list of all objects, as the behaviors
|
//It is *mandatory* to create and iterate on a external list of all objects, as the behaviors
|
||||||
//may delete the objects.
|
//may delete the objects.
|
||||||
this._constructListOfAllInstances();
|
this._constructListOfAllInstances();
|
||||||
for( var i = 0, len = this._allInstancesList.length;i<len;++i) {
|
for( var i = 0, len = this._allInstancesList.length;i<len;++i) {
|
||||||
this._allInstancesList[i].update(this);
|
var obj = this._allInstancesList[i];
|
||||||
this._allInstancesList[i].stepBehaviorsPostEvents(this);
|
|
||||||
|
if (!obj.hasNoForces()) {
|
||||||
|
var averageForce = obj.getAverageForce();
|
||||||
|
var elapsedTimeInSeconds = obj.getElapsedTime(this) / 1000;
|
||||||
|
|
||||||
|
obj.setX(obj.getX() + averageForce.getX() * elapsedTimeInSeconds);
|
||||||
|
obj.setY(obj.getY() + averageForce.getY() * elapsedTimeInSeconds);
|
||||||
|
obj.update(this);
|
||||||
|
obj.updateForces(elapsedTimeInSeconds);
|
||||||
|
} else {
|
||||||
|
obj.update(this);
|
||||||
|
}
|
||||||
|
obj.stepBehaviorsPostEvents(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._cacheOrClearRemovedInstances(); //Some behaviors may have request objects to be deleted.
|
this._cacheOrClearRemovedInstances(); //Some behaviors may have request objects to be deleted.
|
||||||
|
@@ -215,31 +215,35 @@ gdjs.SpriteRuntimeObject.prototype.extraInitializationFromInitialInstance = func
|
|||||||
* @method update
|
* @method update
|
||||||
*/
|
*/
|
||||||
gdjs.SpriteRuntimeObject.prototype.update = function(runtimeScene) {
|
gdjs.SpriteRuntimeObject.prototype.update = function(runtimeScene) {
|
||||||
var elapsedTime = this.getElapsedTime(runtimeScene) / 1000;
|
|
||||||
var oldFrame = this._currentFrame;
|
|
||||||
this._frameElapsedTime += this._animationPaused ? 0 : elapsedTime * this._animationSpeedScale;
|
|
||||||
|
|
||||||
if ( this._currentAnimation >= this._animations.length ||
|
if ( this._currentAnimation >= this._animations.length ||
|
||||||
this._currentDirection >= this._animations[this._currentAnimation].directions.length) {
|
this._currentDirection >= this._animations[this._currentAnimation].directions.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var direction = this._animations[this._currentAnimation].directions[this._currentDirection];
|
var direction = this._animations[this._currentAnimation].directions[this._currentDirection];
|
||||||
|
var oldFrame = this._currentFrame;
|
||||||
|
|
||||||
if ( this._frameElapsedTime > direction.timeBetweenFrames ) {
|
if (!direction.loop && this._currentFrame >= direction.frames.length) {
|
||||||
var count = Math.floor(this._frameElapsedTime / direction.timeBetweenFrames);
|
//*Optimization*: Animation is finished, don't change the current frame
|
||||||
this._currentFrame += count;
|
//and compute nothing more.
|
||||||
this._frameElapsedTime = this._frameElapsedTime-count*direction.timeBetweenFrames;
|
} else {
|
||||||
if ( this._frameElapsedTime < 0 ) this._frameElapsedTime = 0;
|
var elapsedTime = this.getElapsedTime(runtimeScene) / 1000;
|
||||||
|
this._frameElapsedTime += this._animationPaused ? 0 : elapsedTime * this._animationSpeedScale;
|
||||||
|
|
||||||
|
if ( this._frameElapsedTime > direction.timeBetweenFrames ) {
|
||||||
|
var count = Math.floor(this._frameElapsedTime / direction.timeBetweenFrames);
|
||||||
|
this._currentFrame += count;
|
||||||
|
this._frameElapsedTime = this._frameElapsedTime-count*direction.timeBetweenFrames;
|
||||||
|
if ( this._frameElapsedTime < 0 ) this._frameElapsedTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this._currentFrame >= direction.frames.length ) {
|
||||||
|
this._currentFrame = direction.loop ? this._currentFrame % direction.frames.length : direction.frames.length-1;
|
||||||
|
}
|
||||||
|
if ( this._currentFrame < 0 ) this._currentFrame = 0; //May happen if there is no frame.
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( this._currentFrame >= direction.frames.length ) {
|
if ( oldFrame !== this._currentFrame || this._frameDirty ) this._updateFrame();
|
||||||
this._currentFrame = direction.loop ? this._currentFrame % direction.frames.length : direction.frames.length-1;
|
if ( oldFrame !== this._currentFrame ) this.hitBoxesDirty = true;
|
||||||
}
|
|
||||||
if ( this._currentFrame < 0 ) this._currentFrame = 0; //May happen if there is no frame.
|
|
||||||
|
|
||||||
if ( oldFrame != this._currentFrame || this._frameDirty ) this._updateFrame();
|
|
||||||
if ( oldFrame != this._currentFrame ) this.hitBoxesDirty = true;
|
|
||||||
|
|
||||||
this._renderer.ensureUpToDate();
|
this._renderer.ensureUpToDate();
|
||||||
};
|
};
|
||||||
@@ -367,8 +371,7 @@ gdjs.SpriteRuntimeObject.prototype.setDirectionOrAngle = function(newValue) {
|
|||||||
|
|
||||||
if (newValue === this._currentDirection
|
if (newValue === this._currentDirection
|
||||||
|| newValue >= anim.directions.length
|
|| newValue >= anim.directions.length
|
||||||
|| anim.directions[newValue].frames.length === 0
|
|| anim.directions[newValue].frames.length === 0)
|
||||||
|| this._currentDirection === newValue )
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._currentDirection = newValue;
|
this._currentDirection = newValue;
|
||||||
@@ -405,7 +408,7 @@ gdjs.SpriteRuntimeObject.prototype.setAnimationFrame = function(newFrame) {
|
|||||||
}
|
}
|
||||||
var direction = this._animations[this._currentAnimation].directions[this._currentDirection];
|
var direction = this._animations[this._currentAnimation].directions[this._currentDirection];
|
||||||
|
|
||||||
if ( newFrame >= 0 && newFrame < direction.frames.length && newFrame != this._currentFrame ) {
|
if ( newFrame >= 0 && newFrame < direction.frames.length && newFrame !== this._currentFrame ) {
|
||||||
this._currentFrame = newFrame;
|
this._currentFrame = newFrame;
|
||||||
this._frameDirty = true;
|
this._frameDirty = true;
|
||||||
this.hitBoxesDirty = true;
|
this.hitBoxesDirty = true;
|
||||||
@@ -600,6 +603,8 @@ gdjs.SpriteRuntimeObject.prototype.getAngle = function(angle) {
|
|||||||
//Visibility and display :
|
//Visibility and display :
|
||||||
|
|
||||||
gdjs.SpriteRuntimeObject.prototype.setBlendMode = function(newMode) {
|
gdjs.SpriteRuntimeObject.prototype.setBlendMode = function(newMode) {
|
||||||
|
if (this._blendMode === newMode) return;
|
||||||
|
|
||||||
this._blendMode = newMode;
|
this._blendMode = newMode;
|
||||||
this._renderer.update();
|
this._renderer.update();
|
||||||
};
|
};
|
||||||
@@ -685,8 +690,8 @@ gdjs.SpriteRuntimeObject.prototype.setHeight = function(newHeight) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
gdjs.SpriteRuntimeObject.prototype.setScale = function(newScale) {
|
gdjs.SpriteRuntimeObject.prototype.setScale = function(newScale) {
|
||||||
if ( newScale === Math.abs(this._scaleX) && newScale === Math.abs(this._scaleY) ) return;
|
|
||||||
if ( newScale < 0 ) newScale = 0;
|
if ( newScale < 0 ) newScale = 0;
|
||||||
|
if ( newScale === Math.abs(this._scaleX) && newScale === Math.abs(this._scaleY) ) return;
|
||||||
|
|
||||||
this._scaleX = newScale * (this._flippedX ? -1 : 1);
|
this._scaleX = newScale * (this._flippedX ? -1 : 1);
|
||||||
this._scaleY = newScale * (this._flippedY ? -1 : 1);
|
this._scaleY = newScale * (this._flippedY ? -1 : 1);
|
||||||
@@ -695,8 +700,8 @@ gdjs.SpriteRuntimeObject.prototype.setScale = function(newScale) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
gdjs.SpriteRuntimeObject.prototype.setScaleX = function(newScale) {
|
gdjs.SpriteRuntimeObject.prototype.setScaleX = function(newScale) {
|
||||||
if ( newScale === Math.abs(this._scaleX) ) return;
|
|
||||||
if ( newScale < 0 ) newScale = 0;
|
if ( newScale < 0 ) newScale = 0;
|
||||||
|
if ( newScale === Math.abs(this._scaleX) ) return;
|
||||||
|
|
||||||
this._scaleX = newScale * (this._flippedX ? -1 : 1);
|
this._scaleX = newScale * (this._flippedX ? -1 : 1);
|
||||||
this._renderer.update();
|
this._renderer.update();
|
||||||
@@ -704,8 +709,8 @@ gdjs.SpriteRuntimeObject.prototype.setScaleX = function(newScale) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
gdjs.SpriteRuntimeObject.prototype.setScaleY = function(newScale) {
|
gdjs.SpriteRuntimeObject.prototype.setScaleY = function(newScale) {
|
||||||
if ( newScale === Math.abs(this._scaleY) ) return;
|
|
||||||
if ( newScale < 0 ) newScale = 0;
|
if ( newScale < 0 ) newScale = 0;
|
||||||
|
if ( newScale === Math.abs(this._scaleY) ) return;
|
||||||
|
|
||||||
this._scaleY = newScale * (this._flippedY ? -1 : 1);
|
this._scaleY = newScale * (this._flippedY ? -1 : 1);
|
||||||
this._renderer.update();
|
this._renderer.update();
|
||||||
|
441
GDJS/tests/games/Picking optimizations.gdg
Normal file
441
GDJS/tests/games/Picking optimizations.gdg
Normal file
@@ -0,0 +1,441 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<project firstLayout="">
|
||||||
|
<gdVersion build="94" major="4" minor="0" revision="0" />
|
||||||
|
<properties folderProject="false" linuxExecutableFilename="" macExecutableFilename="" packageName="com.example.gamename" useExternalSourceFiles="false" winExecutableFilename="" winExecutableIconFile="">
|
||||||
|
<name>Project</name>
|
||||||
|
<author></author>
|
||||||
|
<windowWidth>800</windowWidth>
|
||||||
|
<windowHeight>600</windowHeight>
|
||||||
|
<latestCompilationDirectory></latestCompilationDirectory>
|
||||||
|
<maxFPS>60</maxFPS>
|
||||||
|
<minFPS>10</minFPS>
|
||||||
|
<verticalSync>false</verticalSync>
|
||||||
|
<extensions>
|
||||||
|
<extension name="BuiltinObject" />
|
||||||
|
<extension name="BuiltinAudio" />
|
||||||
|
<extension name="BuiltinVariables" />
|
||||||
|
<extension name="BuiltinTime" />
|
||||||
|
<extension name="BuiltinMouse" />
|
||||||
|
<extension name="BuiltinKeyboard" />
|
||||||
|
<extension name="BuiltinJoystick" />
|
||||||
|
<extension name="BuiltinCamera" />
|
||||||
|
<extension name="BuiltinWindow" />
|
||||||
|
<extension name="BuiltinFile" />
|
||||||
|
<extension name="BuiltinNetwork" />
|
||||||
|
<extension name="BuiltinScene" />
|
||||||
|
<extension name="BuiltinAdvanced" />
|
||||||
|
<extension name="Sprite" />
|
||||||
|
<extension name="BuiltinCommonInstructions" />
|
||||||
|
<extension name="BuiltinCommonConversions" />
|
||||||
|
<extension name="BuiltinStringInstructions" />
|
||||||
|
<extension name="BuiltinMathematicalTools" />
|
||||||
|
<extension name="BuiltinExternalLayouts" />
|
||||||
|
<extension name="TextObject" />
|
||||||
|
</extensions>
|
||||||
|
<platforms>
|
||||||
|
<platform name="GDevelop JS platform" />
|
||||||
|
</platforms>
|
||||||
|
<currentPlatform>GDevelop JS platform</currentPlatform>
|
||||||
|
</properties>
|
||||||
|
<resources>
|
||||||
|
<resources>
|
||||||
|
<resource alwaysLoaded="false" file="../../Desktop/nativeandroidtest/assets/algae-big.png" kind="image" name="algae-big.png" smoothed="true" userAdded="false" />
|
||||||
|
<resource alwaysLoaded="false" file="../../Desktop/nativeandroidtest/assets/algae.png" kind="image" name="algae.png" smoothed="true" userAdded="false" />
|
||||||
|
</resources>
|
||||||
|
<resourceFolders />
|
||||||
|
</resources>
|
||||||
|
<objects />
|
||||||
|
<objectsGroups />
|
||||||
|
<variables />
|
||||||
|
<layouts>
|
||||||
|
<layout b="209" disableInputWhenNotFocused="true" mangledName="New_32scene" name="New scene" oglFOV="90.000000" oglZFar="500.000000" oglZNear="1.000000" r="209" standardSortMethod="true" stopSoundsOnStartup="true" title="" v="209">
|
||||||
|
<uiSettings grid="false" gridB="255" gridG="180" gridHeight="32" gridOffsetX="0" gridOffsetY="0" gridR="158" gridWidth="32" snap="true" windowMask="false" zoomFactor="1.000000" />
|
||||||
|
<objectsGroups />
|
||||||
|
<variables />
|
||||||
|
<instances>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="NewObject" width="0.000000" x="559.000000" y="152.000015" zOrder="1">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="NewObject" width="0.000000" x="114.999969" y="150.000000" zOrder="1">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="NewObject" width="0.000000" x="436.999939" y="140.000015" zOrder="1">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="NewObject" width="0.000000" x="323.000000" y="316.000031" zOrder="1">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="NewObject" width="0.000000" x="233.000000" y="316.000031" zOrder="1">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="NewObject2" width="0.000000" x="335.000000" y="428.000000" zOrder="2">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="ShouldNotMove" width="0.000000" x="440.999969" y="81.999985" zOrder="3">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="ShouldTurnSlowly" width="0.000000" x="333.000000" y="246.000015" zOrder="4">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="ShouldTurnFaster" width="0.000000" x="237.000015" y="248.000000" zOrder="5">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="ShouldTurnFastest" width="0.000000" x="115.000015" y="74.000015" zOrder="6">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
</instances>
|
||||||
|
<objects>
|
||||||
|
<object name="NewObject" type="Sprite">
|
||||||
|
<variables />
|
||||||
|
<behaviors />
|
||||||
|
<animations>
|
||||||
|
<animation name="" useMultipleDirections="false">
|
||||||
|
<directions>
|
||||||
|
<direction looping="false" timeBetweenFrames="1.000000">
|
||||||
|
<sprites>
|
||||||
|
<sprite hasCustomCollisionMask="false" image="algae-big.png">
|
||||||
|
<points />
|
||||||
|
<originPoint name="origine" x="0.000000" y="0.000000" />
|
||||||
|
<centerPoint automatic="true" name="centre" x="36.000000" y="25.500000" />
|
||||||
|
<customCollisionMask>
|
||||||
|
<polygon>
|
||||||
|
<vertice x="0.000000" y="0.000000" />
|
||||||
|
<vertice x="72.000000" y="0.000000" />
|
||||||
|
<vertice x="72.000000" y="51.000000" />
|
||||||
|
<vertice x="0.000000" y="51.000000" />
|
||||||
|
</polygon>
|
||||||
|
</customCollisionMask>
|
||||||
|
</sprite>
|
||||||
|
</sprites>
|
||||||
|
</direction>
|
||||||
|
</directions>
|
||||||
|
</animation>
|
||||||
|
</animations>
|
||||||
|
</object>
|
||||||
|
<object name="NewObject2" type="Sprite">
|
||||||
|
<variables />
|
||||||
|
<behaviors />
|
||||||
|
<animations>
|
||||||
|
<animation name="" useMultipleDirections="false">
|
||||||
|
<directions>
|
||||||
|
<direction looping="false" timeBetweenFrames="1.000000">
|
||||||
|
<sprites>
|
||||||
|
<sprite hasCustomCollisionMask="false" image="algae.png">
|
||||||
|
<points />
|
||||||
|
<originPoint name="origine" x="0.000000" y="0.000000" />
|
||||||
|
<centerPoint automatic="true" name="centre" x="25.500000" y="36.000000" />
|
||||||
|
<customCollisionMask>
|
||||||
|
<polygon>
|
||||||
|
<vertice x="0.000000" y="0.000000" />
|
||||||
|
<vertice x="51.000000" y="0.000000" />
|
||||||
|
<vertice x="51.000000" y="72.000000" />
|
||||||
|
<vertice x="0.000000" y="72.000000" />
|
||||||
|
</polygon>
|
||||||
|
</customCollisionMask>
|
||||||
|
</sprite>
|
||||||
|
</sprites>
|
||||||
|
</direction>
|
||||||
|
</directions>
|
||||||
|
</animation>
|
||||||
|
</animations>
|
||||||
|
</object>
|
||||||
|
<object bold="false" italic="false" name="ShouldNotMove" smoothed="true" type="TextObject::Text" underlined="false">
|
||||||
|
<variables />
|
||||||
|
<behaviors />
|
||||||
|
<string>Should not move:</string>
|
||||||
|
<font></font>
|
||||||
|
<characterSize>20</characterSize>
|
||||||
|
<color b="255" g="255" r="255" />
|
||||||
|
</object>
|
||||||
|
<object bold="false" italic="false" name="ShouldTurnSlowly" smoothed="true" type="TextObject::Text" underlined="false">
|
||||||
|
<variables />
|
||||||
|
<behaviors />
|
||||||
|
<string>Should
 turn
 slowly:</string>
|
||||||
|
<font></font>
|
||||||
|
<characterSize>20</characterSize>
|
||||||
|
<color b="255" g="255" r="255" />
|
||||||
|
</object>
|
||||||
|
<object bold="false" italic="false" name="ShouldTurnFaster" smoothed="true" type="TextObject::Text" underlined="false">
|
||||||
|
<variables />
|
||||||
|
<behaviors />
|
||||||
|
<string>Should
 turn
 faster:</string>
|
||||||
|
<font></font>
|
||||||
|
<characterSize>20</characterSize>
|
||||||
|
<color b="255" g="255" r="255" />
|
||||||
|
</object>
|
||||||
|
<object bold="false" italic="false" name="ShouldTurnFastest" smoothed="true" type="TextObject::Text" underlined="false">
|
||||||
|
<variables />
|
||||||
|
<behaviors />
|
||||||
|
<string>Should
 turn
 fastest:</string>
|
||||||
|
<font></font>
|
||||||
|
<characterSize>20</characterSize>
|
||||||
|
<color b="255" g="255" r="255" />
|
||||||
|
</object>
|
||||||
|
</objects>
|
||||||
|
<events>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Standard</type>
|
||||||
|
<conditions>
|
||||||
|
<condition>
|
||||||
|
<type inverted="false" value="PosX" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>NewObject</parameter>
|
||||||
|
<parameter><</parameter>
|
||||||
|
<parameter>400</parameter>
|
||||||
|
</parameters>
|
||||||
|
<subConditions />
|
||||||
|
</condition>
|
||||||
|
</conditions>
|
||||||
|
<actions>
|
||||||
|
<action>
|
||||||
|
<type inverted="false" value="Rotate" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>NewObject</parameter>
|
||||||
|
<parameter>10</parameter>
|
||||||
|
<parameter></parameter>
|
||||||
|
</parameters>
|
||||||
|
<subActions />
|
||||||
|
</action>
|
||||||
|
</actions>
|
||||||
|
<events>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Standard</type>
|
||||||
|
<conditions>
|
||||||
|
<condition>
|
||||||
|
<type inverted="false" value="PosX" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>NewObject</parameter>
|
||||||
|
<parameter><</parameter>
|
||||||
|
<parameter>300</parameter>
|
||||||
|
</parameters>
|
||||||
|
<subConditions />
|
||||||
|
</condition>
|
||||||
|
</conditions>
|
||||||
|
<actions>
|
||||||
|
<action>
|
||||||
|
<type inverted="false" value="Rotate" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>NewObject</parameter>
|
||||||
|
<parameter>150</parameter>
|
||||||
|
<parameter></parameter>
|
||||||
|
</parameters>
|
||||||
|
<subActions />
|
||||||
|
</action>
|
||||||
|
</actions>
|
||||||
|
<events />
|
||||||
|
</event>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Comment</type>
|
||||||
|
<color b="109" g="230" r="255" textB="0" textG="0" textR="0" />
|
||||||
|
<comment>This event can reuse NewObject list from its parent:</comment>
|
||||||
|
<comment2></comment2>
|
||||||
|
</event>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Standard</type>
|
||||||
|
<conditions>
|
||||||
|
<condition>
|
||||||
|
<type inverted="false" value="PosY" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>NewObject</parameter>
|
||||||
|
<parameter><</parameter>
|
||||||
|
<parameter>200</parameter>
|
||||||
|
</parameters>
|
||||||
|
<subConditions />
|
||||||
|
</condition>
|
||||||
|
</conditions>
|
||||||
|
<actions>
|
||||||
|
<action>
|
||||||
|
<type inverted="false" value="Rotate" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>NewObject</parameter>
|
||||||
|
<parameter>150</parameter>
|
||||||
|
<parameter></parameter>
|
||||||
|
</parameters>
|
||||||
|
<subActions />
|
||||||
|
</action>
|
||||||
|
</actions>
|
||||||
|
<events />
|
||||||
|
</event>
|
||||||
|
</events>
|
||||||
|
</event>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Standard</type>
|
||||||
|
<conditions>
|
||||||
|
<condition>
|
||||||
|
<type inverted="false" value="PosX" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>NewObject</parameter>
|
||||||
|
<parameter><</parameter>
|
||||||
|
<parameter>900</parameter>
|
||||||
|
</parameters>
|
||||||
|
<subConditions />
|
||||||
|
</condition>
|
||||||
|
</conditions>
|
||||||
|
<actions />
|
||||||
|
<events>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Standard</type>
|
||||||
|
<conditions />
|
||||||
|
<actions />
|
||||||
|
<events>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Standard</type>
|
||||||
|
<conditions />
|
||||||
|
<actions>
|
||||||
|
<action>
|
||||||
|
<type inverted="false" value="Rotate" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>NewObject</parameter>
|
||||||
|
<parameter>1</parameter>
|
||||||
|
<parameter></parameter>
|
||||||
|
</parameters>
|
||||||
|
<subActions />
|
||||||
|
</action>
|
||||||
|
<action>
|
||||||
|
<type inverted="false" value="ModVarGlobal" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>loopCount</parameter>
|
||||||
|
<parameter>=</parameter>
|
||||||
|
<parameter>0</parameter>
|
||||||
|
</parameters>
|
||||||
|
<subActions />
|
||||||
|
</action>
|
||||||
|
</actions>
|
||||||
|
<events />
|
||||||
|
</event>
|
||||||
|
<event disabled="false" folded="false" infiniteLoopWarning="true">
|
||||||
|
<type>BuiltinCommonInstructions::While</type>
|
||||||
|
<whileConditions>
|
||||||
|
<condition>
|
||||||
|
<type inverted="false" value="VarGlobal" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>loopCount</parameter>
|
||||||
|
<parameter><</parameter>
|
||||||
|
<parameter>150</parameter>
|
||||||
|
</parameters>
|
||||||
|
<subConditions />
|
||||||
|
</condition>
|
||||||
|
</whileConditions>
|
||||||
|
<conditions />
|
||||||
|
<actions />
|
||||||
|
<events>
|
||||||
|
<event disabled="false" folded="false" infiniteLoopWarning="true">
|
||||||
|
<type>BuiltinCommonInstructions::While</type>
|
||||||
|
<whileConditions>
|
||||||
|
<condition>
|
||||||
|
<type inverted="false" value="VarGlobal" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>loopCount</parameter>
|
||||||
|
<parameter><</parameter>
|
||||||
|
<parameter>150</parameter>
|
||||||
|
</parameters>
|
||||||
|
<subConditions />
|
||||||
|
</condition>
|
||||||
|
</whileConditions>
|
||||||
|
<conditions />
|
||||||
|
<actions>
|
||||||
|
<action>
|
||||||
|
<type inverted="false" value="ModVarGlobal" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>loopCount</parameter>
|
||||||
|
<parameter>+</parameter>
|
||||||
|
<parameter>1</parameter>
|
||||||
|
</parameters>
|
||||||
|
<subActions />
|
||||||
|
</action>
|
||||||
|
</actions>
|
||||||
|
<events />
|
||||||
|
</event>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Standard</type>
|
||||||
|
<conditions />
|
||||||
|
<actions>
|
||||||
|
<action>
|
||||||
|
<type inverted="false" value="Rotate" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>NewObject</parameter>
|
||||||
|
<parameter>-1</parameter>
|
||||||
|
<parameter></parameter>
|
||||||
|
</parameters>
|
||||||
|
<subActions />
|
||||||
|
</action>
|
||||||
|
</actions>
|
||||||
|
<events />
|
||||||
|
</event>
|
||||||
|
</events>
|
||||||
|
</event>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Standard</type>
|
||||||
|
<conditions />
|
||||||
|
<actions />
|
||||||
|
<events />
|
||||||
|
</event>
|
||||||
|
</events>
|
||||||
|
</event>
|
||||||
|
</events>
|
||||||
|
</event>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Standard</type>
|
||||||
|
<conditions />
|
||||||
|
<actions />
|
||||||
|
<events>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Comment</type>
|
||||||
|
<color b="109" g="230" r="255" textB="0" textG="0" textR="0" />
|
||||||
|
<comment>(No optimization here: the event pick NewObject2 from the list of all objects of the scene)</comment>
|
||||||
|
<comment2></comment2>
|
||||||
|
</event>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Standard</type>
|
||||||
|
<conditions />
|
||||||
|
<actions>
|
||||||
|
<action>
|
||||||
|
<type inverted="false" value="Rotate" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>NewObject2</parameter>
|
||||||
|
<parameter>150</parameter>
|
||||||
|
<parameter></parameter>
|
||||||
|
</parameters>
|
||||||
|
<subActions />
|
||||||
|
</action>
|
||||||
|
</actions>
|
||||||
|
<events />
|
||||||
|
</event>
|
||||||
|
</events>
|
||||||
|
</event>
|
||||||
|
</events>
|
||||||
|
<layers>
|
||||||
|
<layer name="" visibility="true">
|
||||||
|
<cameras>
|
||||||
|
<camera defaultSize="true" defaultViewport="true" height="0.000000" viewportBottom="1.000000" viewportLeft="0.000000" viewportRight="1.000000" viewportTop="0.000000" width="0.000000" />
|
||||||
|
</cameras>
|
||||||
|
<effects />
|
||||||
|
</layer>
|
||||||
|
</layers>
|
||||||
|
<behaviorsSharedData />
|
||||||
|
</layout>
|
||||||
|
</layouts>
|
||||||
|
<externalEvents />
|
||||||
|
<externalLayouts />
|
||||||
|
<externalSourceFiles />
|
||||||
|
</project>
|
401
GDJS/tests/games/Tracked objects benchmark.gdg
Normal file
401
GDJS/tests/games/Tracked objects benchmark.gdg
Normal file
@@ -0,0 +1,401 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<project firstLayout="">
|
||||||
|
<gdVersion build="94" major="4" minor="0" revision="0" />
|
||||||
|
<properties folderProject="false" linuxExecutableFilename="" macExecutableFilename="" packageName="" useExternalSourceFiles="false" winExecutableFilename="" winExecutableIconFile="">
|
||||||
|
<name>Projet</name>
|
||||||
|
<author></author>
|
||||||
|
<windowWidth>800</windowWidth>
|
||||||
|
<windowHeight>600</windowHeight>
|
||||||
|
<latestCompilationDirectory></latestCompilationDirectory>
|
||||||
|
<maxFPS>60</maxFPS>
|
||||||
|
<minFPS>10</minFPS>
|
||||||
|
<verticalSync>false</verticalSync>
|
||||||
|
<extensions>
|
||||||
|
<extension name="BuiltinObject" />
|
||||||
|
<extension name="BuiltinAudio" />
|
||||||
|
<extension name="BuiltinVariables" />
|
||||||
|
<extension name="BuiltinTime" />
|
||||||
|
<extension name="BuiltinMouse" />
|
||||||
|
<extension name="BuiltinKeyboard" />
|
||||||
|
<extension name="BuiltinJoystick" />
|
||||||
|
<extension name="BuiltinCamera" />
|
||||||
|
<extension name="BuiltinWindow" />
|
||||||
|
<extension name="BuiltinFile" />
|
||||||
|
<extension name="BuiltinNetwork" />
|
||||||
|
<extension name="BuiltinScene" />
|
||||||
|
<extension name="BuiltinAdvanced" />
|
||||||
|
<extension name="Sprite" />
|
||||||
|
<extension name="BuiltinCommonInstructions" />
|
||||||
|
<extension name="BuiltinCommonConversions" />
|
||||||
|
<extension name="BuiltinStringInstructions" />
|
||||||
|
<extension name="BuiltinMathematicalTools" />
|
||||||
|
<extension name="BuiltinExternalLayouts" />
|
||||||
|
<extension name="TrackedBehavior" />
|
||||||
|
</extensions>
|
||||||
|
<platforms>
|
||||||
|
<platform name="GDevelop JS platform" />
|
||||||
|
<platform name="GDevelop C++ platform" />
|
||||||
|
</platforms>
|
||||||
|
<currentPlatform>GDevelop C++ platform</currentPlatform>
|
||||||
|
</properties>
|
||||||
|
<resources>
|
||||||
|
<resources>
|
||||||
|
<resource alwaysLoaded="false" file="2DWoodBox.jpg" kind="image" name="2DWoodBox.jpg" smoothed="true" userAdded="false" />
|
||||||
|
<resource alwaysLoaded="false" file="spship.png" kind="image" name="spship.png" smoothed="true" userAdded="false" />
|
||||||
|
</resources>
|
||||||
|
<resourceFolders />
|
||||||
|
</resources>
|
||||||
|
<objects />
|
||||||
|
<objectsGroups />
|
||||||
|
<variables />
|
||||||
|
<layouts>
|
||||||
|
<layout b="209" disableInputWhenNotFocused="true" mangledName="Scene" name="Scene" oglFOV="90.000000" oglZFar="500.000000" oglZNear="1.000000" r="209" standardSortMethod="true" stopSoundsOnStartup="true" title="" v="209">
|
||||||
|
<uiSettings grid="false" gridB="255" gridG="180" gridHeight="32" gridOffsetX="0" gridOffsetY="0" gridR="158" gridWidth="32" snap="true" windowMask="false" zoomFactor="1.000000" />
|
||||||
|
<objectsGroups />
|
||||||
|
<variables />
|
||||||
|
<instances>
|
||||||
|
<instance angle="0.000000" customSize="false" height="243.000000" layer="" locked="false" name="Cursor" width="278.000000" x="108.500000" y="59.000000" zOrder="1">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="Box" width="0.000000" x="400.500000" y="253.000000" zOrder="1">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="Box" width="0.000000" x="195.500000" y="95.000000" zOrder="1">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="Box" width="0.000000" x="563.500000" y="370.000000" zOrder="1">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="Box2" width="0.000000" x="693.000000" y="486.000031" zOrder="2">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="Box2" width="0.000000" x="29.000000" y="486.000031" zOrder="2">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="Box2" width="0.000000" x="145.000015" y="486.000031" zOrder="2">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="Box2" width="0.000000" x="333.000000" y="486.000031" zOrder="2">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
<instance angle="0.000000" customSize="false" height="0.000000" layer="" locked="false" name="Box2" width="0.000000" x="495.000031" y="486.000031" zOrder="2">
|
||||||
|
<numberProperties />
|
||||||
|
<stringProperties />
|
||||||
|
<initialVariables />
|
||||||
|
</instance>
|
||||||
|
</instances>
|
||||||
|
<objects>
|
||||||
|
<object name="Box" type="Sprite">
|
||||||
|
<variables>
|
||||||
|
<variable name="NewVariable" value="1" />
|
||||||
|
<variable name="NewVariable2" value="2" />
|
||||||
|
</variables>
|
||||||
|
<behaviors>
|
||||||
|
<behavior name="Tracked" type="TrackedBehavior::Tracked" />
|
||||||
|
</behaviors>
|
||||||
|
<animations>
|
||||||
|
<animation name="" useMultipleDirections="false">
|
||||||
|
<directions>
|
||||||
|
<direction looping="false" timeBetweenFrames="1.000000">
|
||||||
|
<sprites>
|
||||||
|
<sprite hasCustomCollisionMask="false" image="2DWoodBox.jpg">
|
||||||
|
<points />
|
||||||
|
<originPoint name="origine" x="0.000000" y="0.000000" />
|
||||||
|
<centerPoint automatic="true" name="centre" x="32.000000" y="32.000000" />
|
||||||
|
<customCollisionMask>
|
||||||
|
<polygon>
|
||||||
|
<vertice x="0.000000" y="0.000000" />
|
||||||
|
<vertice x="64.000000" y="0.000000" />
|
||||||
|
<vertice x="64.000000" y="64.000000" />
|
||||||
|
<vertice x="0.000000" y="64.000000" />
|
||||||
|
</polygon>
|
||||||
|
</customCollisionMask>
|
||||||
|
</sprite>
|
||||||
|
</sprites>
|
||||||
|
</direction>
|
||||||
|
</directions>
|
||||||
|
</animation>
|
||||||
|
</animations>
|
||||||
|
</object>
|
||||||
|
<object name="Cursor" type="Sprite">
|
||||||
|
<variables />
|
||||||
|
<behaviors />
|
||||||
|
<animations>
|
||||||
|
<animation name="" useMultipleDirections="false">
|
||||||
|
<directions>
|
||||||
|
<direction looping="false" timeBetweenFrames="1.000000">
|
||||||
|
<sprites>
|
||||||
|
<sprite hasCustomCollisionMask="false" image="spship.png">
|
||||||
|
<points />
|
||||||
|
<originPoint name="origine" x="0.000000" y="0.000000" />
|
||||||
|
<centerPoint automatic="true" name="centre" x="41.500000" y="41.500000" />
|
||||||
|
<customCollisionMask>
|
||||||
|
<polygon>
|
||||||
|
<vertice x="0.000000" y="0.000000" />
|
||||||
|
<vertice x="83.000000" y="0.000000" />
|
||||||
|
<vertice x="83.000000" y="83.000000" />
|
||||||
|
<vertice x="0.000000" y="83.000000" />
|
||||||
|
</polygon>
|
||||||
|
</customCollisionMask>
|
||||||
|
</sprite>
|
||||||
|
</sprites>
|
||||||
|
</direction>
|
||||||
|
</directions>
|
||||||
|
</animation>
|
||||||
|
</animations>
|
||||||
|
</object>
|
||||||
|
<object name="Box2" type="Sprite">
|
||||||
|
<variables>
|
||||||
|
<variable name="NewVariable" value="1" />
|
||||||
|
<variable name="NewVariable2" value="2" />
|
||||||
|
</variables>
|
||||||
|
<behaviors>
|
||||||
|
<behavior name="Tracked" type="TrackedBehavior::Tracked" />
|
||||||
|
</behaviors>
|
||||||
|
<animations>
|
||||||
|
<animation name="" useMultipleDirections="false">
|
||||||
|
<directions>
|
||||||
|
<direction looping="false" timeBetweenFrames="1.000000">
|
||||||
|
<sprites>
|
||||||
|
<sprite hasCustomCollisionMask="false" image="2DWoodBox.jpg">
|
||||||
|
<points />
|
||||||
|
<originPoint name="origine" x="0.000000" y="0.000000" />
|
||||||
|
<centerPoint automatic="true" name="centre" x="32.000000" y="32.000000" />
|
||||||
|
<customCollisionMask>
|
||||||
|
<polygon>
|
||||||
|
<vertice x="0.000000" y="0.000000" />
|
||||||
|
<vertice x="64.000000" y="0.000000" />
|
||||||
|
<vertice x="64.000000" y="64.000000" />
|
||||||
|
<vertice x="0.000000" y="64.000000" />
|
||||||
|
</polygon>
|
||||||
|
</customCollisionMask>
|
||||||
|
</sprite>
|
||||||
|
</sprites>
|
||||||
|
</direction>
|
||||||
|
</directions>
|
||||||
|
</animation>
|
||||||
|
</animations>
|
||||||
|
</object>
|
||||||
|
</objects>
|
||||||
|
<events>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Standard</type>
|
||||||
|
<conditions>
|
||||||
|
<condition>
|
||||||
|
<type inverted="false" value="DepartScene" />
|
||||||
|
<parameters>
|
||||||
|
<parameter></parameter>
|
||||||
|
</parameters>
|
||||||
|
<subConditions />
|
||||||
|
</condition>
|
||||||
|
</conditions>
|
||||||
|
<actions />
|
||||||
|
<events>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Repeat</type>
|
||||||
|
<repeatExpression>5000</repeatExpression>
|
||||||
|
<conditions />
|
||||||
|
<actions>
|
||||||
|
<action>
|
||||||
|
<type inverted="false" value="Create" />
|
||||||
|
<parameters>
|
||||||
|
<parameter></parameter>
|
||||||
|
<parameter>Box</parameter>
|
||||||
|
<parameter>Random(400)</parameter>
|
||||||
|
<parameter>Random(300)</parameter>
|
||||||
|
<parameter></parameter>
|
||||||
|
</parameters>
|
||||||
|
<subActions />
|
||||||
|
</action>
|
||||||
|
<action>
|
||||||
|
<type inverted="false" value="Create" />
|
||||||
|
<parameters>
|
||||||
|
<parameter></parameter>
|
||||||
|
<parameter>Cursor</parameter>
|
||||||
|
<parameter>0</parameter>
|
||||||
|
<parameter>0</parameter>
|
||||||
|
<parameter></parameter>
|
||||||
|
</parameters>
|
||||||
|
<subActions />
|
||||||
|
</action>
|
||||||
|
</actions>
|
||||||
|
<events />
|
||||||
|
</event>
|
||||||
|
</events>
|
||||||
|
</event>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Standard</type>
|
||||||
|
<conditions />
|
||||||
|
<actions>
|
||||||
|
<action>
|
||||||
|
<type inverted="false" value="MettreXY" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>Cursor</parameter>
|
||||||
|
<parameter>=</parameter>
|
||||||
|
<parameter>MouseX("",0)</parameter>
|
||||||
|
<parameter>=</parameter>
|
||||||
|
<parameter>MouseY("",0)</parameter>
|
||||||
|
</parameters>
|
||||||
|
<subActions />
|
||||||
|
</action>
|
||||||
|
</actions>
|
||||||
|
<events />
|
||||||
|
</event>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Standard</type>
|
||||||
|
<conditions />
|
||||||
|
<actions>
|
||||||
|
<action>
|
||||||
|
<type inverted="false" value="AddForceXY" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>Box2</parameter>
|
||||||
|
<parameter>150</parameter>
|
||||||
|
<parameter>0</parameter>
|
||||||
|
<parameter>0</parameter>
|
||||||
|
</parameters>
|
||||||
|
<subActions />
|
||||||
|
</action>
|
||||||
|
</actions>
|
||||||
|
<events />
|
||||||
|
</event>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Standard</type>
|
||||||
|
<conditions>
|
||||||
|
<condition>
|
||||||
|
<type inverted="false" value="PosX" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>Box2</parameter>
|
||||||
|
<parameter>></parameter>
|
||||||
|
<parameter>800</parameter>
|
||||||
|
</parameters>
|
||||||
|
<subConditions />
|
||||||
|
</condition>
|
||||||
|
</conditions>
|
||||||
|
<actions>
|
||||||
|
<action>
|
||||||
|
<type inverted="false" value="MettreX" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>Box2</parameter>
|
||||||
|
<parameter>=</parameter>
|
||||||
|
<parameter>0</parameter>
|
||||||
|
</parameters>
|
||||||
|
<subActions />
|
||||||
|
</action>
|
||||||
|
</actions>
|
||||||
|
<events />
|
||||||
|
</event>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Standard</type>
|
||||||
|
<conditions>
|
||||||
|
<condition>
|
||||||
|
<type inverted="false" value="TrackedBehavior::TrackedBehaviorAround" />
|
||||||
|
<parameters>
|
||||||
|
<parameter></parameter>
|
||||||
|
<parameter>Box2</parameter>
|
||||||
|
<parameter>MouseX("",0)</parameter>
|
||||||
|
<parameter>MouseY("",0)</parameter>
|
||||||
|
<parameter>50</parameter>
|
||||||
|
</parameters>
|
||||||
|
<subConditions />
|
||||||
|
</condition>
|
||||||
|
</conditions>
|
||||||
|
<actions>
|
||||||
|
<action>
|
||||||
|
<type inverted="false" value="ChangeDirection" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>Box2</parameter>
|
||||||
|
<parameter>+</parameter>
|
||||||
|
<parameter>1</parameter>
|
||||||
|
</parameters>
|
||||||
|
<subActions />
|
||||||
|
</action>
|
||||||
|
</actions>
|
||||||
|
<events />
|
||||||
|
</event>
|
||||||
|
<event disabled="false" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Standard</type>
|
||||||
|
<conditions>
|
||||||
|
<condition>
|
||||||
|
<type inverted="false" value="TrackedBehavior::TrackedBehaviorAround" />
|
||||||
|
<parameters>
|
||||||
|
<parameter></parameter>
|
||||||
|
<parameter>Box</parameter>
|
||||||
|
<parameter>MouseX("",0)</parameter>
|
||||||
|
<parameter>MouseY("",0)</parameter>
|
||||||
|
<parameter>50</parameter>
|
||||||
|
</parameters>
|
||||||
|
<subConditions />
|
||||||
|
</condition>
|
||||||
|
</conditions>
|
||||||
|
<actions>
|
||||||
|
<action>
|
||||||
|
<type inverted="false" value="ChangeDirection" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>Box</parameter>
|
||||||
|
<parameter>+</parameter>
|
||||||
|
<parameter>1</parameter>
|
||||||
|
</parameters>
|
||||||
|
<subActions />
|
||||||
|
</action>
|
||||||
|
</actions>
|
||||||
|
<events />
|
||||||
|
</event>
|
||||||
|
<event disabled="true" folded="false">
|
||||||
|
<type>BuiltinCommonInstructions::Standard</type>
|
||||||
|
<conditions>
|
||||||
|
<condition>
|
||||||
|
<type inverted="false" value="Distance" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>Box</parameter>
|
||||||
|
<parameter>Cursor</parameter>
|
||||||
|
<parameter>50</parameter>
|
||||||
|
<parameter></parameter>
|
||||||
|
</parameters>
|
||||||
|
<subConditions />
|
||||||
|
</condition>
|
||||||
|
</conditions>
|
||||||
|
<actions>
|
||||||
|
<action>
|
||||||
|
<type inverted="false" value="ChangeDirection" />
|
||||||
|
<parameters>
|
||||||
|
<parameter>Box</parameter>
|
||||||
|
<parameter>+</parameter>
|
||||||
|
<parameter>1</parameter>
|
||||||
|
</parameters>
|
||||||
|
<subActions />
|
||||||
|
</action>
|
||||||
|
</actions>
|
||||||
|
<events />
|
||||||
|
</event>
|
||||||
|
</events>
|
||||||
|
<layers>
|
||||||
|
<layer name="" visibility="true">
|
||||||
|
<cameras>
|
||||||
|
<camera defaultSize="true" defaultViewport="true" height="0.000000" viewportBottom="1.000000" viewportLeft="0.000000" viewportRight="1.000000" viewportTop="0.000000" width="0.000000" />
|
||||||
|
</cameras>
|
||||||
|
<effects />
|
||||||
|
</layer>
|
||||||
|
</layers>
|
||||||
|
<behaviorsSharedData />
|
||||||
|
</layout>
|
||||||
|
</layouts>
|
||||||
|
<externalEvents />
|
||||||
|
<externalLayouts />
|
||||||
|
<externalSourceFiles />
|
||||||
|
</project>
|
@@ -10,6 +10,7 @@ module.exports = function(config) {
|
|||||||
'../Runtime/libs/jshashtable.js',
|
'../Runtime/libs/jshashtable.js',
|
||||||
'../Runtime/gd.js',
|
'../Runtime/gd.js',
|
||||||
'../Runtime/libs/hshg.js',
|
'../Runtime/libs/hshg.js',
|
||||||
|
'../Runtime/libs/rbush.js',
|
||||||
'../Runtime/cocos-renderers/cocos-director-manager.js',
|
'../Runtime/cocos-renderers/cocos-director-manager.js',
|
||||||
'../Runtime/pixi-renderers/pixi.js',
|
'../Runtime/pixi-renderers/pixi.js',
|
||||||
'../Runtime/pixi-renderers/*.js',
|
'../Runtime/pixi-renderers/*.js',
|
||||||
|
Reference in New Issue
Block a user