mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
6 Commits
v5.4.207
...
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)
|
||||
{
|
||||
if ( emptyObjectsListsToBeDeclared.find(objectName) == emptyObjectsListsToBeDeclared.end() )
|
||||
|
@@ -32,7 +32,13 @@ public:
|
||||
* 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.
|
||||
*/
|
||||
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() {};
|
||||
|
||||
/**
|
||||
@@ -41,6 +47,28 @@ public:
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@@ -150,6 +178,7 @@ private:
|
||||
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.
|
||||
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 )
|
||||
{
|
||||
//Each event has its own context : Objects picked in an event are totally different than the one picked in another.
|
||||
gd::EventsCodeGenerationContext context;
|
||||
context.InheritsFrom(parentContext); //Events in the same "level" share the same context as their parent.
|
||||
gd::EventsCodeGenerationContext newContext;
|
||||
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 scopeBegin = GenerateScopeBegin(context);
|
||||
|
@@ -46,7 +46,7 @@ public:
|
||||
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.
|
||||
*/
|
||||
@@ -57,9 +57,9 @@ public:
|
||||
*
|
||||
* \param events std::vector of events
|
||||
* \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
|
||||
|
@@ -48,3 +48,6 @@ IF (NOT EMSCRIPTEN)
|
||||
ADD_SUBDIRECTORY(TimedEvent)
|
||||
ENDIF()
|
||||
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)
|
||||
{
|
||||
this._platformsHSHG = new gdjs.HSHG.HSHG();
|
||||
//this._hshgNeedUpdate = true; Useless: The behaviors track by themselves changes in objects size or position.
|
||||
this._platformRBush = new rbush(9, ['.owner.getAABB().min[0]', '.owner.getAABB().min[1]', '.owner.getAABB().max[0]', '.owner.getAABB().max[1]']);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -39,7 +38,7 @@ gdjs.PlatformObjectsManager.getManager = function(runtimeScene) {
|
||||
* @method addPlatform
|
||||
*/
|
||||
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
|
||||
*/
|
||||
gdjs.PlatformObjectsManager.prototype.removePlatform = function(platformBehavior) {
|
||||
this._platformsHSHG.removeObject(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;
|
||||
this._platformRBush.remove(platformBehavior);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -98,16 +63,15 @@ gdjs.PlatformObjectsManager.prototype.getAllPlatformsAround = function(object, m
|
||||
var oh = object.getHeight();
|
||||
var x = object.getDrawableX()+object.getCenterX();
|
||||
var y = object.getDrawableY()+object.getCenterY();
|
||||
var objBoundingRadius = Math.sqrt(ow*ow+oh*oh)/2.0 + maxMovementLength;
|
||||
|
||||
if (!this._aroundVertex)
|
||||
this._aroundVertex = new gdjs.PlatformObjectsManager.Vertex(x, y, objBoundingRadius);
|
||||
else
|
||||
gdjs.PlatformObjectsManager.Vertex.call(this._aroundVertex, x, y, objBoundingRadius);
|
||||
|
||||
this._platformsHSHG.addObject(this._aroundVertex);
|
||||
this._platformsHSHG.queryForCollisionWith(this._aroundVertex, result);
|
||||
this._platformsHSHG.removeObject(this._aroundVertex);
|
||||
var searchArea = gdjs.staticObject(gdjs.PlatformObjectsManager.prototype.getAllPlatformsAround);
|
||||
searchArea.minX = x - ow / 2 - maxMovementLength;
|
||||
searchArea.minY = y - oh / 2 - maxMovementLength;
|
||||
searchArea.maxX = x + ow / 2 + maxMovementLength;
|
||||
searchArea.maxY = y + oh / 2 + maxMovementLength;
|
||||
var nearbyPlatforms = this._platformRBush.search(searchArea);
|
||||
result.length = 0;
|
||||
result.push.apply(result, nearbyPlatforms);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -167,12 +131,6 @@ gdjs.PlatformRuntimeBehavior.prototype.doStepPreEvents = function(runtimeScene)
|
||||
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.
|
||||
if (!this.activated() && this._registeredInManager)
|
||||
{
|
||||
@@ -202,7 +160,6 @@ gdjs.PlatformRuntimeBehavior.prototype.doStepPreEvents = function(runtimeScene)
|
||||
};
|
||||
|
||||
gdjs.PlatformRuntimeBehavior.prototype.doStepPostEvents = function(runtimeScene) {
|
||||
//this._manager._hshgNeedUpdate = true; //Useless, see above.
|
||||
};
|
||||
|
||||
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.
|
||||
gd::EventsCodeGenerationContext context;
|
||||
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);
|
||||
|
||||
@@ -244,6 +245,7 @@ CommonInstructionsExtension::CommonInstructionsExtension()
|
||||
//Context is "reset" each time the event is repeated ( i.e. objects are picked again )
|
||||
gd::EventsCodeGenerationContext context;
|
||||
context.InheritsFrom(parentContext);
|
||||
context.ForbidReuse();
|
||||
if ( event.HasInfiniteLoopWarning() && !codeGenerator.GenerateCodeForRuntime() ) codeGenerator.AddIncludeFile("GDCpp/Extensions/Builtin/RuntimeSceneTools.h");
|
||||
|
||||
//Prepare codes
|
||||
@@ -300,6 +302,7 @@ CommonInstructionsExtension::CommonInstructionsExtension()
|
||||
//Context is "reset" each time the event is repeated ( i.e. objects are picked again )
|
||||
gd::EventsCodeGenerationContext context;
|
||||
context.InheritsFrom(parentContext);
|
||||
context.ForbidReuse();
|
||||
|
||||
//Prepare conditions/actions codes
|
||||
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 )
|
||||
gd::EventsCodeGenerationContext context;
|
||||
context.InheritsFrom(parentContext);
|
||||
context.ForbidReuse();
|
||||
|
||||
//Prepare conditions/actions codes
|
||||
gd::String conditionsCode = codeGenerator.GenerateConditionsListCode(event.GetConditions(), context);
|
||||
|
@@ -101,15 +101,15 @@ gd::String EventsCodeGenerator::GenerateSceneEventsCompleteCode(gd::Project & pr
|
||||
|
||||
output +=
|
||||
codeGenerator.GetCustomCodeOutsideMain()+"\n\n"
|
||||
+globalObjectLists+"\n"
|
||||
+globalConditionsBooleans+"\n"
|
||||
+"gdjs."+gd::SceneNameMangler::GetMangledSceneName(scene.GetName())+"Code.func = function(runtimeScene, context) {\n"
|
||||
+"context.startNewFrame();\n"
|
||||
+globalObjectListsReset+"\n"
|
||||
+codeGenerator.GetCustomCodeInMain()
|
||||
+wholeEventsCode
|
||||
+"return;\n"
|
||||
+"}\n";
|
||||
+ globalObjectLists+"\n"
|
||||
+ globalConditionsBooleans+"\n"
|
||||
+ codeGenerator.GetCodeNamespace() + "func = function(runtimeScene, context) {\n"
|
||||
+ "context.startNewFrame();\n"
|
||||
+ globalObjectListsReset+"\n"
|
||||
+ codeGenerator.GetCustomCodeInMain()
|
||||
+ wholeEventsCode
|
||||
+ "return;\n"
|
||||
+ "}\n";
|
||||
|
||||
//Export the symbols to avoid them being stripped by the Closure Compiler:
|
||||
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)
|
||||
{
|
||||
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;
|
||||
for ( set<gd::String>::iterator it = context.GetObjectsListsToBeDeclared().begin() ; it != context.GetObjectsListsToBeDeclared().end(); ++it )
|
||||
for (auto object : context.GetObjectsListsToBeDeclared())
|
||||
{
|
||||
declarationsCode += GetObjectListName(*it, context);
|
||||
if ( !context.ObjectAlreadyDeclared(*it) )
|
||||
gd::String objectListDeclaration = "";
|
||||
if ( !context.ObjectAlreadyDeclared(object) )
|
||||
{
|
||||
declarationsCode += ".createFrom(runtimeScene.getObjects(\""+ConvertToString(*it)+"\"));\n";
|
||||
context.SetObjectDeclared(*it);
|
||||
objectListDeclaration += GetObjectListName(object, context) + ".createFrom(runtimeScene.getObjects(\"" + ConvertToString(object) + "\"));";
|
||||
context.SetObjectDeclared(object);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (context.GetParentContext())
|
||||
declarationsCode += ".createFrom("+GetObjectListName(*it, *context.GetParentContext())+");\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;
|
||||
}
|
||||
objectListDeclaration = declareObjectList(object, context);
|
||||
|
||||
declarationsCode += objectListDeclaration + "\n";
|
||||
}
|
||||
for ( set<gd::String>::iterator it = context.GetObjectsListsToBeDeclaredEmpty().begin() ; it != context.GetObjectsListsToBeDeclaredEmpty().end(); ++it )
|
||||
for (auto object : context.GetObjectsListsToBeDeclaredEmpty())
|
||||
{
|
||||
declarationsCode += GetObjectListName(*it, context);
|
||||
if ( !context.ObjectAlreadyDeclared(*it) )
|
||||
gd::String objectListDeclaration = "";
|
||||
if ( !context.ObjectAlreadyDeclared(object) )
|
||||
{
|
||||
declarationsCode +=".length = 0;\n";
|
||||
context.SetObjectDeclared(*it);
|
||||
objectListDeclaration = GetObjectListName(object, context) + ".length = 0;\n";
|
||||
context.SetObjectDeclared(object);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (context.GetParentContext())
|
||||
declarationsCode += ".createFrom("+GetObjectListName(*it, *context.GetParentContext())+");\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;
|
||||
}
|
||||
objectListDeclaration = declareObjectList(object, context);
|
||||
|
||||
declarationsCode += objectListDeclaration + "\n";
|
||||
}
|
||||
|
||||
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)
|
||||
|
@@ -42,6 +42,19 @@ public:
|
||||
const gd::Layout & scene, const gd::EventsList & events,
|
||||
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
|
||||
*
|
||||
|
@@ -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.
|
||||
gd::EventsCodeGenerationContext context;
|
||||
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);
|
||||
|
||||
@@ -231,6 +232,7 @@ CommonInstructionsExtension::CommonInstructionsExtension()
|
||||
//Context is "reset" each time the event is repeated ( i.e. objects are picked again )
|
||||
gd::EventsCodeGenerationContext context;
|
||||
context.InheritsFrom(parentContext);
|
||||
context.ForbidReuse();
|
||||
|
||||
//Prepare codes
|
||||
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 )
|
||||
gd::EventsCodeGenerationContext context;
|
||||
context.InheritsFrom(parentContext);
|
||||
context.ForbidReuse();
|
||||
|
||||
//Prepare conditions/actions codes
|
||||
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 )
|
||||
gd::EventsCodeGenerationContext context;
|
||||
context.InheritsFrom(parentContext);
|
||||
context.ForbidReuse();
|
||||
|
||||
for (unsigned int i = 0;i<realObjects.size();++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, "gd.js");
|
||||
InsertUnique(includesFiles, "libs/hshg.js");
|
||||
InsertUnique(includesFiles, "libs/rbush.js");
|
||||
InsertUnique(includesFiles, "inputmanager.js");
|
||||
InsertUnique(includesFiles, "timemanager.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() {
|
||||
this._cacheOrClearRemovedInstances();
|
||||
|
||||
this.updateObjectsForces();
|
||||
|
||||
//It is *mandatory* to create and iterate on a external list of all objects, as the behaviors
|
||||
//may delete the objects.
|
||||
this._constructListOfAllInstances();
|
||||
for( var i = 0, len = this._allInstancesList.length;i<len;++i) {
|
||||
this._allInstancesList[i].update(this);
|
||||
this._allInstancesList[i].stepBehaviorsPostEvents(this);
|
||||
var obj = this._allInstancesList[i];
|
||||
|
||||
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.
|
||||
|
@@ -215,31 +215,35 @@ gdjs.SpriteRuntimeObject.prototype.extraInitializationFromInitialInstance = func
|
||||
* @method update
|
||||
*/
|
||||
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 ||
|
||||
this._currentDirection >= this._animations[this._currentAnimation].directions.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var direction = this._animations[this._currentAnimation].directions[this._currentDirection];
|
||||
var oldFrame = this._currentFrame;
|
||||
|
||||
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 (!direction.loop && this._currentFrame >= direction.frames.length) {
|
||||
//*Optimization*: Animation is finished, don't change the current frame
|
||||
//and compute nothing more.
|
||||
} else {
|
||||
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 ) {
|
||||
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 ( oldFrame != this._currentFrame || this._frameDirty ) this._updateFrame();
|
||||
if ( oldFrame != this._currentFrame ) this.hitBoxesDirty = true;
|
||||
if ( oldFrame !== this._currentFrame || this._frameDirty ) this._updateFrame();
|
||||
if ( oldFrame !== this._currentFrame ) this.hitBoxesDirty = true;
|
||||
|
||||
this._renderer.ensureUpToDate();
|
||||
};
|
||||
@@ -367,8 +371,7 @@ gdjs.SpriteRuntimeObject.prototype.setDirectionOrAngle = function(newValue) {
|
||||
|
||||
if (newValue === this._currentDirection
|
||||
|| newValue >= anim.directions.length
|
||||
|| anim.directions[newValue].frames.length === 0
|
||||
|| this._currentDirection === newValue )
|
||||
|| anim.directions[newValue].frames.length === 0)
|
||||
return;
|
||||
|
||||
this._currentDirection = newValue;
|
||||
@@ -405,7 +408,7 @@ gdjs.SpriteRuntimeObject.prototype.setAnimationFrame = function(newFrame) {
|
||||
}
|
||||
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._frameDirty = true;
|
||||
this.hitBoxesDirty = true;
|
||||
@@ -600,6 +603,8 @@ gdjs.SpriteRuntimeObject.prototype.getAngle = function(angle) {
|
||||
//Visibility and display :
|
||||
|
||||
gdjs.SpriteRuntimeObject.prototype.setBlendMode = function(newMode) {
|
||||
if (this._blendMode === newMode) return;
|
||||
|
||||
this._blendMode = newMode;
|
||||
this._renderer.update();
|
||||
};
|
||||
@@ -685,8 +690,8 @@ gdjs.SpriteRuntimeObject.prototype.setHeight = function(newHeight) {
|
||||
};
|
||||
|
||||
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 === Math.abs(this._scaleX) && newScale === Math.abs(this._scaleY) ) return;
|
||||
|
||||
this._scaleX = newScale * (this._flippedX ? -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) {
|
||||
if ( newScale === Math.abs(this._scaleX) ) return;
|
||||
if ( newScale < 0 ) newScale = 0;
|
||||
if ( newScale === Math.abs(this._scaleX) ) return;
|
||||
|
||||
this._scaleX = newScale * (this._flippedX ? -1 : 1);
|
||||
this._renderer.update();
|
||||
@@ -704,8 +709,8 @@ gdjs.SpriteRuntimeObject.prototype.setScaleX = function(newScale) {
|
||||
};
|
||||
|
||||
gdjs.SpriteRuntimeObject.prototype.setScaleY = function(newScale) {
|
||||
if ( newScale === Math.abs(this._scaleY) ) return;
|
||||
if ( newScale < 0 ) newScale = 0;
|
||||
if ( newScale === Math.abs(this._scaleY) ) return;
|
||||
|
||||
this._scaleY = newScale * (this._flippedY ? -1 : 1);
|
||||
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/gd.js',
|
||||
'../Runtime/libs/hshg.js',
|
||||
'../Runtime/libs/rbush.js',
|
||||
'../Runtime/cocos-renderers/cocos-director-manager.js',
|
||||
'../Runtime/pixi-renderers/pixi.js',
|
||||
'../Runtime/pixi-renderers/*.js',
|
||||
|
Reference in New Issue
Block a user