Compare commits

...

6 Commits

Author SHA1 Message Date
Florian Rival
201f1cd03c [WIP] Added Tracked Behavior 2016-12-29 22:51:09 +01:00
Florian Rival
430dee6c52 Update gdjs.RuntimeScene._updateObjects to apply forces as done in GDC++
Could potentially change slightly the behavior of some game but it's now
consistent between GDJS and GDCpp.
This also avoid an extra iteration on all instances in GDJS.
2016-12-29 17:00:12 +01:00
Florian Rival
fc3aa161c5 Replace HSHG in platformruntimebehavior.js by RBush
TODO: Check if RBush create/destroy a lot of temporary JS objects and arrays
and try to reduce this.
2016-12-29 16:59:56 +01:00
Florian Rival
e279424ee0 Small optimisations for rendering of gdjs.SpriteRuntimeObject 2016-12-26 01:48:57 +01:00
Florian Rival
4104d7bdc5 Prevent long lags/freezes in code generated for GDJS games (especially on Android)
This is done by generating every events list code in separate functions, reducing
the stress on garbage collectors of JS engines.
2016-12-25 22:40:32 +01:00
Florian Rival
e8c6d707fa Avoid unncessary copy of objects lists for last event of events lists 2016-12-25 18:58:55 +01:00
24 changed files with 2239 additions and 119 deletions

View File

@@ -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() )

View File

@@ -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.
};
}

View File

@@ -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);

View File

@@ -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

View File

@@ -48,3 +48,6 @@ IF (NOT EMSCRIPTEN)
ADD_SUBDIRECTORY(TimedEvent)
ENDIF()
ADD_SUBDIRECTORY(TopDownMovementBehavior)
IF (NOT EMSCRIPTEN)
ADD_SUBDIRECTORY(TrackedBehavior)
ENDIF()

View File

@@ -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(){

View 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)

View 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

View 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

View 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;
}

View 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

View File

@@ -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);
});
});

View 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;
}

View File

@@ -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);

View File

@@ -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)

View File

@@ -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
*

View File

@@ -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]);

View File

@@ -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
View 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)
});

View File

@@ -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.

View File

@@ -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();

View 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&#x0A; turn&#x0A; 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&#x0A; turn&#x0A; 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&#x0A; turn&#x0A; 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>&lt;</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>&lt;</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>&lt;</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>&lt;</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>&lt;</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>&lt;</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>

View 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(&quot;&quot;,0)</parameter>
<parameter>=</parameter>
<parameter>MouseY(&quot;&quot;,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>&gt;</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(&quot;&quot;,0)</parameter>
<parameter>MouseY(&quot;&quot;,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(&quot;&quot;,0)</parameter>
<parameter>MouseY(&quot;&quot;,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>

View File

@@ -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',