Compare commits

...

23 Commits

Author SHA1 Message Date
Florian Rival
a48d7017ea Bump newIDE version 2018-11-08 22:15:09 +00:00
Florian Rival
7d1f52ded4 Fix tests broken since AdMobObject extension was removed 2018-11-05 18:58:32 +00:00
Florian Rival
569adc1500 Fix ReosurcesMergingHelper wrongly modifing extensions of files having filename conflicts during export 2018-11-05 18:52:23 +00:00
Florian Rival
e6eb193e8e Add Get/SetMetadata to Sprite's Direction 2018-11-05 16:36:54 +00:00
Florian Rival
b8c63dade1 Add jfxr-editor to the list of git ignored folder 2018-11-05 16:36:11 +00:00
Todor Imreorov
82f92d36f1 Replace Jsfx by Jfxr sound effects generator (#716)
Jfxr yields better results and has a better interface
2018-11-03 21:40:15 +00:00
Florian Rival
6b17c1febd Add new example for the AdMob extension 2018-10-31 23:45:10 +00:00
Florian Rival
95882d1289 Remove AdMobObject extension (replaced by AdMob extension) 2018-10-31 23:11:04 +00:00
Florian Rival
70340bba7f Fix typo and floating label 2018-10-31 22:05:55 +00:00
Florian Rival
c809be9a07 Fix cordova version for AdMob extension 2018-10-31 22:05:37 +00:00
Franco Maciel
cc7b0f524e Add a new AdMob extension (#702)
* Add AdMob application ID to the project
* Export AdMob application ID with Cordova
* Switch to cordova-plugin-admob-free
* Add banners and interstitials support
2018-10-31 00:08:43 +00:00
Florian Rival
a61784bb6c Add validation for function parameters names 2018-10-28 06:41:09 -07:00
Florian Rival
3f92fc2ee5 Add support for key and mouse parameters in functions 2018-10-28 06:41:09 -07:00
Florian Rival
a4c08305c7 Improve UI of function parameters 2018-10-28 06:41:09 -07:00
Florian Rival
067798ff2c Fix EventsSheet toolbar buttons when navigating between functions 2018-10-28 06:41:09 -07:00
Florian Rival
7a838fc8f9 Refactor GDJS benchmarks 2018-10-28 06:41:09 -07:00
Florian Rival
e669190ca2 Add commented lines that can be uncomment to display debug draw 2018-10-28 06:41:09 -07:00
Florian Rival
b84bb8630a Improve accuracy/performance of various computations and add benchmarks
Add tests for gdjs.Force
Add tests for gdjs.Layer
2018-10-28 06:41:09 -07:00
Florian Rival
fb849be246 Fix AABB computation for rotate objects
AABB was not properly computed for rotated objects or objects
with a non default center/origin. This could create issues while checking
collisions and for behaviors/logic relying on spatial hashing (platformer,
pathfinding).

* Add benchmark to compare the speed of the implementation for non rotated object
(simple/faster) and for rotated objects (always exact but 12-15% slower).
* Add a method to render AABB of objects to ease debugging.
* Also add a test game.
* Add tests for AABB
* Add tests for getHitboxes for gdjs.SpriteRuntimeObject
2018-10-28 06:41:09 -07:00
Florian Rival
382aa0f086 Bump newIDE version 2018-10-22 22:43:31 +01:00
Florian Rival
303873974c Improve grammar/wording/typos in SubscriptionDetails 2018-10-22 22:18:47 +01:00
Florian Rival
9f0ec46064 Fix GetArgumentAsString (and fix test game using it) 2018-10-22 22:03:32 +01:00
Wend1go
9b2fa5d080 Fix typos/grammer (#710) (#713)
Thanks to @ValiantCuriosity (See #710)
2018-10-22 21:38:57 +01:00
204 changed files with 12639 additions and 1290 deletions

View File

@@ -75,6 +75,11 @@ void Direction::UnserializeFrom(const gd::SerializerElement& element) {
SetTimeBetweenFrames(
element.GetDoubleAttribute("timeBetweenFrames", 1, "tempsEntre"));
SetLoop(element.GetBoolAttribute("looping", false, "boucle"));
#if defined(GD_IDE_ONLY)
SetMetadata(element.HasAttribute("metadata")
? element.GetStringAttribute("metadata")
: "");
#endif
const gd::SerializerElement& spritesElement =
element.GetChild("sprites", 0, "Sprites");
@@ -182,6 +187,7 @@ void SaveSpritesDirection(const vector<Sprite>& sprites,
void Direction::SerializeTo(gd::SerializerElement& element) const {
element.SetAttribute("looping", IsLooping());
element.SetAttribute("timeBetweenFrames", GetTimeBetweenFrames());
if (!GetMetadata().empty()) element.SetAttribute("metadata", GetMetadata());
SaveSpritesDirection(sprites, element.AddChild("sprites"));
}
#endif

View File

@@ -6,17 +6,16 @@
#ifndef GDCORE_DIRECTION_H
#define GDCORE_DIRECTION_H
#include <vector>
#include "GDCore/String.h"
namespace gd {
class Sprite;
}
namespace gd {
class SerializerElement;
}
namespace gd {
/**
* \brief Class defining a direction of an Animation.
* \brief Class defining a direction (set of frames) of an Animation.
*
* \see SpriteObject
* \see Animation
@@ -32,78 +31,101 @@ class GD_CORE_API Direction {
virtual ~Direction();
/**
* Return true if sprites looping is activated
* \brief Return true if sprites looping is activated
*/
inline bool IsLooping() const { return loop; }
/**
* Set if the sprites must be looping or not.
* \brief Set if the sprites must be looping or not.
*/
void SetLoop(bool loop_);
/**
* Get the time between each sprite
* \brief Get the time between each sprite
*/
inline float GetTimeBetweenFrames() const { return timeBetweenFrame; }
/**
* Set the time between each sprite
* \brief Set the time between each sprite
*
* \param time Time between each sprite, in seconds.
*/
void SetTimeBetweenFrames(float time);
/**
* Return a reference to a sprite of the direction.
* \brief Return a reference to a sprite of the direction.
*
* \param nb The index of the sprite to be accessed. Bound checking is not
* made. \return A reference to the sprite.
* made.
*
* \return A reference to the sprite.
*/
const Sprite& GetSprite(std::size_t nb) const;
/**
* Return a reference to a sprite of the direction.
* \brief Return a reference to a sprite of the direction.
*
* \param nb The index of the sprite to be accessed. Bound checking is not
* made. \return A reference to the sprite.
* made.
*
* \return A reference to the sprite.
*/
Sprite& GetSprite(std::size_t nb);
/**
* Check if the direction contains sprites.
* \brief Check if the direction contains sprites.
*
* \return true if the direction does not have any sprite.
*/
bool HasNoSprites() const;
/**
* Return the number of sprite used in the direction
* \brief Return the number of sprite used in the direction
*
* \return The number of sprite used in the direction
*/
std::size_t GetSpritesCount() const;
/**
* Remove the sprite at the specified position.
* \brief Remove the sprite at the specified position.
*
* Bound-checking is made.
*/
void RemoveSprite(std::size_t index);
/**
* Clear the direction from all of its sprites
* \brief Clear the direction from all of its sprites
*/
void RemoveAllSprites();
/**
* Add a new sprite at the end of the list.
* \brief Add a new sprite at the end of the list.
*/
void AddSprite(const Sprite& sprite);
/**
* Swap the position of two sprites
* \brief Swap the position of two sprites
*/
void SwapSprites(std::size_t firstSpriteIndex, std::size_t secondSpriteIndex);
/**
* Change the position of the specified sprite.
* \brief Change the position of the specified sprite.
*/
void MoveSprite(std::size_t oldIndex, std::size_t newIndex);
#if defined(GD_IDE_ONLY)
/**
* \brief Set the metadata (any string) associated to the Direction.
* \note Can be used by external editors to store extra information.
*/
virtual void SetMetadata(const gd::String& metadata_) { metadata = metadata_; }
/**
* \brief Return the (optional) metadata associated to the Direction.
*/
virtual const gd::String& GetMetadata() const { return metadata; }
#endif
void UnserializeFrom(const gd::SerializerElement& element);
#if defined(GD_IDE_ONLY)
void SerializeTo(gd::SerializerElement& element) const;
@@ -113,6 +135,9 @@ class GD_CORE_API Direction {
bool loop; ///< true if the animation must loop.
float timeBetweenFrame; ///< The time between each sprite of the animation.
std::vector<Sprite> sprites; ///< The sprites of the direction.
#if defined(GD_IDE_ONLY)
gd::String metadata;
#endif
};
} // namespace gd

View File

@@ -49,11 +49,25 @@ void ResourcesMergingHelper::SetNewFilename(gd::String oldFilename,
gd::String newFilename) {
if (oldFilenames.find(oldFilename) != oldFilenames.end()) return;
// Make sure that the new filename is not already used.
gd::String finalFilename = gd::NewNameGenerator::Generate(
newFilename, [this](const gd::String& name) {
return newFilenames.find(name) != newFilenames.end();
});
// Extract baseName and extension from the new filename
size_t extensionPos = newFilename.find_last_of(".");
gd::String extension =
extensionPos != gd::String::npos
? newFilename.substr(extensionPos, newFilename.length())
: "";
gd::String baseName = newFilename.substr(0, extensionPos);
// Make sure that the new filename is not already used. Generate a
// new filename while there is a collision.
// Preserving extension is important.
gd::String finalFilename =
gd::NewNameGenerator::Generate(
baseName,
[this, extension](const gd::String& newBaseName) {
return newFilenames.find(newBaseName + extension) !=
newFilenames.end();
}) +
extension;
oldFilenames[oldFilename] = finalFilename;
newFilenames[finalFilename] = oldFilename;

View File

@@ -18,8 +18,10 @@ class AbstractFileSystem;
namespace gd {
/**
* \brief ResourcesMergingHelper is used (mainly during compilation) so
* as to inventory resources and change their filenames
* \brief ResourcesMergingHelper is used (mainly during export)
* to list resources and generate new filenames, to allow them to be all copied
* in a single directory (potentially changing the filename to avoid conflicts,
* but preserving extensions).
*
* \see ArbitraryResourceWorker
*
@@ -82,9 +84,9 @@ class GD_CORE_API ResourcesMergingHelper : public ArbitraryResourceWorker {
///< baseDirectory, will be preserved in
///< filenames.
bool preserveAbsoluteFilenames; ///< If set to true, the filenames which are
///< absolute ( C:\MyFile.png ) will not be
///< transformed into their filenames (
///< MyFile.png ).
///< absolute (C:\MyFile.png will not be
///< transformed into a relative filename
///< (MyFile.png).
gd::AbstractFileSystem&
fs; ///< The gd::AbstractFileSystem used to manipulate files.
};

View File

@@ -65,6 +65,7 @@ Project::Project()
version("1.0.0"),
packageName("com.example.gamename"),
orientation("landscape"),
adMobAppId(""),
folderProject(false),
#endif
windowWidth(800),
@@ -640,6 +641,7 @@ void Project::UnserializeFrom(const SerializerElement& element) {
SetAuthor(propElement.GetChild("author", 0, "Auteur").GetValue().GetString());
SetPackageName(propElement.GetStringAttribute("packageName"));
SetOrientation(propElement.GetStringAttribute("orientation", "default"));
SetAdMobAppId(propElement.GetStringAttribute("adMobAppId", ""));
SetFolderProject(propElement.GetBoolAttribute("folderProject"));
SetProjectFile(propElement.GetStringAttribute("projectFile"));
SetLastCompilationDirectory(propElement
@@ -916,6 +918,7 @@ void Project::SerializeTo(SerializerElement& element) const {
propElement.SetAttribute("folderProject", folderProject);
propElement.SetAttribute("packageName", packageName);
propElement.SetAttribute("orientation", orientation);
propElement.SetAttribute("adMobAppId", adMobAppId);
platformSpecificAssets.SerializeTo(
propElement.AddChild("platformSpecificAssets"));
loadingScreen.SerializeTo(propElement.AddChild("loadingScreen"));
@@ -1251,6 +1254,7 @@ void Project::Init(const gd::Project& game) {
author = game.author;
packageName = game.packageName;
orientation = game.orientation;
adMobAppId = game.adMobAppId;
folderProject = game.folderProject;
latestCompilationDirectory = game.latestCompilationDirectory;
platformSpecificAssets = game.platformSpecificAssets;

View File

@@ -116,6 +116,20 @@ class GD_CORE_API Project : public ObjectsContainer {
*/
const gd::String& GetOrientation() const { return orientation; }
/**
* \brief Change the project AdMob application ID (needed
* to use the AdMob extension). This has no effect on desktop
* and web browsers.
*/
void SetAdMobAppId(const gd::String& adMobAppId_) {
adMobAppId = adMobAppId_;
};
/**
* \brief Get the project AdMob application ID.
*/
const gd::String& GetAdMobAppId() const { return adMobAppId; }
/**
* Called when project file has changed.
*/
@@ -962,6 +976,7 @@ class GD_CORE_API Project : public ObjectsContainer {
gd::String packageName; ///< Game package name
gd::String orientation; ///< Lock game orientation (on mobile devices).
///< "default", "landscape" or "portrait".
gd::String adMobAppId; ///< AdMob application ID.
bool
folderProject; ///< True if folder project, false if single file project.
gd::String gameFile; ///< File of the game

View File

@@ -0,0 +1,361 @@
/**
* This is a declaration of an extension for GDevelop 5.
*
* Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change
* to this extension file or to any other *.js file that you reference inside.
*
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
* ⚠️ If you make a change and the extension is not loaded, open the developer console
* and search for any errors.
*
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
*/
module.exports = {
createExtension: function(t, gd) {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
"AdMob",
t("AdMob"),
t(
"Allow the game to display AdMob banner, interstitial and reward video ads"
),
"Franco Maciel",
"MIT"
);
// Banner
extension
.addCondition(
"BannerLoading",
t("Banner loading"),
t("Check if a banner is currently loading."),
t("Banner is loading"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.isBannerLoading");
extension
.addCondition(
"BannerReady",
t("Banner ready"),
t("Check if a banner is ready to be displayed."),
t("Banner is ready"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.isBannerReady");
extension
.addCondition(
"BannerShowing",
t("Banner showing"),
t("Check if there is a banner being displayed."),
t("Banner is showing"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.isBannerShowing");
extension
.addCondition(
"BannerExists",
t("Banner exists"),
t("Check if there is a banner in memory (visible or hidden)."),
t("Banner exists"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.existBanner");
extension
.addAction(
"LoadBanner",
t("Load banner"),
t(
"Start loading a banner, you can display it automatically when finish loading.\nIf test mode is set to true a test banner will be displayed."
),
t(
"Load banner (at top: _PARAM2_, overlap: _PARAM3_, show on load: _PARAM4_, test mode: _PARAM5_)"
),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.addParameter("string", t("Android banner ID"), "", false)
.addParameter("string", t("iOS banner ID"), "", false)
.addParameter(
"yesorno",
t("Display at top? (bottom otherwise)"),
"",
false
)
.setDefaultValue("false")
.addParameter("yesorno", t("Overlap webview?"), "", false)
.setDefaultValue("true")
.addParameter("yesorno", t("Display on load complete?"), "", false)
.setDefaultValue("true")
.addParameter("yesorno", t("Test mode?"), "", false)
.setDefaultValue("true")
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.loadBanner");
extension
.addAction(
"ShowBanner",
t("Show banner"),
t("Show the banner, will work only when the banner is fully loaded."),
t("Show banner"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.showBanner");
extension
.addAction(
"HideBanner",
t("Hide banner"),
t(
"Hide the banner. You can show it again with the corresponding action."
),
t("Hide banner"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.hideBanner");
extension
.addAction(
"RemoveBanner",
t("Remove banner"),
t(
"Remove the banner. You have to load another banner to show it again."
),
t("Remove banner"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.removeBanner");
// Interstitial
extension
.addCondition(
"InterstitialLoading",
t("Interstitial loading"),
t("Check if an interstitial is currently loading."),
t("Interstitial is loading"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.isInterstitialLoading");
extension
.addCondition(
"InterstitialReady",
t("Interstitial ready"),
t("Check if an interstitial is ready to be displayed."),
t("Interstitial is ready"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.isInterstitialReady");
extension
.addCondition(
"InterstitialShowing",
t("Interstitial showing"),
t("Check if there is an interstitial being displayed."),
t("Interstitial is showing"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.isInterstitialShowing");
extension
.addAction(
"LoadInterstitial",
t("Load interstitial"),
t(
"Start loading an interstitial, you can display it automatically when finish loading.\nIf test mode is set to true a test interstitial will be displayed."
),
t("Load interstitial (show on load: _PARAM2_, test mode: _PARAM3_)"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.addParameter("string", t("Android interstitial ID"), "", false)
.addParameter("string", t("iOS interstitial ID"), "", false)
.addParameter("yesorno", t("Display on load complete?"), "", false)
.setDefaultValue("true")
.addParameter("yesorno", t("Test mode?"), "", false)
.setDefaultValue("true")
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.loadInterstitial");
extension
.addAction(
"ShowInterstitial",
t("Show interstitial"),
t(
"Show the interstitial, will work only when the interstitial is fully loaded."
),
t("Show interstitial"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.showInterstitial");
// Reward video
extension
.addCondition(
"VideoLoading",
t("Video loading"),
t("Check if a reward video is currently loading."),
t("Reward video is loading"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.isVideoLoading");
extension
.addCondition(
"VideoReady",
t("Video ready"),
t("Check if a reward video is ready to be displayed."),
t("Reward video is ready"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.isVideoReady");
extension
.addCondition(
"VideoShowing",
t("Video showing"),
t("Check if there is a reward video being displayed."),
t("Reward video is showing"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.isVideoShowing");
extension
.addCondition(
"VideoReward",
t("Video reward"),
t(
"Check if there is a video reward.\nYou can mark it as non-claimed yet, so you can check this reward in other events."
),
t("Video reward given"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.addParameter("yesorno", t("Mark as claimed"), "", false)
.setDefaultValue("true")
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.existVideoReward");
extension
.addAction(
"LoadVideo",
t("Load video"),
t(
"Start loading a reward video, you can display it automatically when finish loading.\nIf test mode is set to true a test video will be displayed."
),
t("Load reward video (show on load: _PARAM2_, test mode: _PARAM3_)"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.addParameter("string", t("Android reward video ID"), "", false)
.addParameter("string", t("iOS reward video ID"), "", false)
.addParameter("yesorno", t("Display on load complete?"), "", false)
.setDefaultValue("true")
.addParameter("yesorno", t("Test mode?"), "", false)
.setDefaultValue("true")
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.loadVideo");
extension
.addAction(
"ShowVideo",
t("Show video"),
t(
"Show the reward video, will work only when the video is fully loaded."
),
t("Show reward video"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.showVideo");
extension
.addAction(
"ClaimReward",
t("Claim reward"),
t("Mark the video reward as claimed."),
t("Claim video reward"),
t("AdMob"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png"
)
.getCodeExtraInformation()
.setIncludeFile("Extensions/AdMob/admobtools.js")
.setFunctionName("gdjs.adMob.claimVideoReward");
return extension;
},
runExtensionSanityTests: function(gd, extension) {
return [];
}
};

View File

@@ -0,0 +1,331 @@
/**
* @memberof gdjs
* @class adMob
* @static
* @private
*/
gdjs.adMob = {
// Banner
bannerLoading: false,
bannerReady: false,
bannerShowing: false,
bannerExists: false,
bannerAutoshow: false, // Needed because the banner event listeners bug
// Interstitial
interstitialLoading: false,
interstitialReady: false,
interstitialShowing: false,
// Reward video
videoLoading: false,
videoReady: false,
videoShowing: false,
videoReward: false
};
gdjs.adMob._getPlatformName = function() {
if (/(android)/i.test(navigator.userAgent)) {
return "android";
} else if (/(ipod|iphone|ipad)/i.test(navigator.userAgent)) {
return "ios";
} else {
return "windowsPhone";
}
};
// Banner
gdjs.adMob.isBannerLoading = function() {
return gdjs.adMob.bannerLoading;
};
gdjs.adMob.isBannerReady = function() {
// This block is needed because the banner event listeners bug
if (gdjs.adMob.bannerAutoshow && gdjs.adMob.bannerReady) {
gdjs.adMob.bannerReady = false;
gdjs.adMob.bannerShowing = true;
gdjs.adMob.bannerExists = true;
}
return gdjs.adMob.bannerReady;
};
gdjs.adMob.isBannerShowing = function() {
// This block is needed because the banner event listeners bug
if (gdjs.adMob.bannerAutoshow && gdjs.adMob.bannerReady) {
gdjs.adMob.bannerReady = false;
gdjs.adMob.bannerShowing = true;
gdjs.adMob.bannerExists = true;
}
return gdjs.adMob.bannerShowing;
};
gdjs.adMob.existBanner = function() {
// This block is needed because the banner event listeners bug
if (gdjs.adMob.bannerAutoshow && gdjs.adMob.bannerReady) {
gdjs.adMob.bannerReady = false;
gdjs.adMob.bannerShowing = true;
gdjs.adMob.bannerExists = true;
}
return gdjs.adMob.bannerExists;
};
gdjs.adMob.loadBanner = function(
androidID,
iosID,
atTop,
overlap,
displayOnLoading,
testMode
) {
if (typeof admob === "undefined") return;
admob.banner.config({
id: gdjs.adMob._getPlatformName() === "android" ? androidID : iosID, // Support Android & iOS
bannerAtTop: atTop,
overlap: overlap,
autoShow: displayOnLoading,
isTesting: testMode
});
admob.banner.prepare();
gdjs.adMob.bannerLoading = true;
gdjs.adMob.bannerReady = false;
// These lines are needed because the banner event listeners bug
gdjs.adMob.bannerAutoshow = displayOnLoading;
gdjs.adMob.bannerShowing = false;
gdjs.adMob.bannerExists = false;
};
gdjs.adMob.showBanner = function() {
if (typeof admob === "undefined") return;
// This block is needed because the banner event listeners bug
if (gdjs.adMob.bannerReady) {
gdjs.adMob.bannerReady = false;
gdjs.adMob.bannerExists = true;
}
if (gdjs.adMob.bannerExists) gdjs.adMob.bannerShowing = true;
admob.banner.show();
};
gdjs.adMob.hideBanner = function() {
if (typeof admob === "undefined") return;
if (gdjs.adMob.bannerExists) gdjs.adMob.bannerShowing = false;
admob.banner.hide();
};
gdjs.adMob.removeBanner = function() {
if (typeof admob === "undefined") return;
// These lines are needed because the banner event listeners bug
gdjs.adMob.bannerExists = false;
gdjs.adMob.bannerShowing = false;
admob.banner.remove();
};
// Interstitial
gdjs.adMob.isInterstitialLoading = function() {
return gdjs.adMob.interstitialLoading;
};
gdjs.adMob.isInterstitialReady = function() {
return gdjs.adMob.interstitialReady;
};
gdjs.adMob.isInterstitialShowing = function() {
return gdjs.adMob.interstitialShowing;
};
gdjs.adMob.loadInterstitial = function(
androidID,
iosID,
displayOnLoading,
testMode
) {
if (typeof admob === "undefined") return;
admob.interstitial.config({
id: gdjs.adMob._getPlatformName() === "android" ? androidID : iosID, // Support Android & iOS
autoShow: displayOnLoading,
isTesting: testMode
});
admob.interstitial.prepare();
gdjs.adMob.interstitialLoading = true;
gdjs.adMob.interstitialReady = false;
};
gdjs.adMob.showInterstitial = function() {
if (typeof admob === "undefined") return;
admob.interstitial.show();
};
// Reward video
gdjs.adMob.isVideoLoading = function() {
return gdjs.adMob.videoLoading;
};
gdjs.adMob.isVideoReady = function() {
return gdjs.adMob.videoReady;
};
gdjs.adMob.isVideoShowing = function() {
return gdjs.adMob.videoShowing;
};
gdjs.adMob.existVideoReward = function(markAsClaimed) {
var reward = gdjs.adMob.videoReward;
if (markAsClaimed) gdjs.adMob.videoReward = false;
return reward;
};
gdjs.adMob.loadVideo = function(androidID, iosID, displayOnLoading, testMode) {
if (typeof admob === "undefined") return;
admob.rewardvideo.config({
id: gdjs.adMob._getPlatformName() === "android" ? androidID : iosID, // Support Android & iOS
autoShow: displayOnLoading,
isTesting: testMode
});
admob.rewardvideo.prepare();
gdjs.adMob.videoLoading = true;
gdjs.adMob.videoReady = false;
};
gdjs.adMob.showVideo = function() {
if (typeof admob === "undefined") return;
admob.rewardvideo.show();
};
gdjs.adMob.claimVideoReward = function() {
gdjs.adMob.videoReward = false;
};
// Banner event listeners
document.addEventListener(
"admob.banner.events.LOAD",
function() {
gdjs.adMob.bannerReady = true;
gdjs.adMob.bannerLoading = false;
},
false
);
document.addEventListener(
"admob.banner.events.LOAD_FAIL",
function() {
gdjs.adMob.bannerLoading = false;
},
false
);
// BUG: These two never get called
/*
document.addEventListener(
"admob.banner.events.OPEN",
function() {
gdjs.adMob.bannerExists = true;
gdjs.adMob.bannerShowing = true;
gdjs.adMob.bannerReady = false;
},
false
);
document.addEventListener(
"admob.banner.events.CLOSE",
function() {
gdjs.adMob.bannerExists = false;
gdjs.adMob.bannerShowing = false;
},
false
);
*/
// Interstitial event listeners
document.addEventListener(
"admob.interstitial.events.LOAD",
function() {
gdjs.adMob.interstitialReady = true;
gdjs.adMob.interstitialLoading = false;
},
false
);
document.addEventListener(
"admob.interstitial.events.LOAD_FAIL",
function() {
gdjs.adMob.interstitialLoading = false;
},
false
);
document.addEventListener(
"admob.interstitial.events.OPEN",
function() {
gdjs.adMob.interstitialShowing = true;
gdjs.adMob.interstitialReady = false;
},
false
);
document.addEventListener(
"admob.interstitial.events.CLOSE",
function() {
gdjs.adMob.interstitialShowing = false;
},
false
);
// Reward video event listeners
document.addEventListener(
"admob.rewardvideo.events.LOAD",
function() {
gdjs.adMob.videoReady = true;
gdjs.adMob.videoLoading = false;
},
false
);
document.addEventListener(
"admob.rewardvideo.events.LOAD_FAIL",
function() {
gdjs.adMob.videoLoading = false;
},
false
);
document.addEventListener(
"admob.rewardvideo.events.OPEN",
function() {
gdjs.adMob.videoShowing = true;
gdjs.adMob.videoReady = false;
},
false
);
document.addEventListener(
"admob.rewardvideo.events.CLOSE",
function() {
gdjs.adMob.videoShowing = false;
},
false
);
document.addEventListener(
"admob.rewardvideo.events.REWARD",
function() {
gdjs.adMob.videoReward = true;
},
false
);

View File

@@ -1,117 +0,0 @@
/**
GDevelop - AdMob Object Extension
Copyright (c) 2008-2016 Florian Rival (Florian.Rival@gmail.com)
This project is released under the MIT License.
*/
#include "AdMobObject.h"
#include <SFML/Graphics.hpp>
#include "GDCore/IDE/Dialogs/PropertyDescriptor.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/Tools/Localization.h"
#include "GDCpp/Runtime/CommonTools.h"
#include "GDCpp/Runtime/ImageManager.h"
#include "GDCpp/Runtime/Project/InitialInstance.h"
#include "GDCpp/Runtime/Project/Object.h"
#include "GDCpp/Runtime/Serialization/SerializerElement.h"
#if defined(GD_IDE_ONLY) && !defined(GD_NO_WX_GUI)
#include <wx/bitmap.h>
sf::Texture AdMobObject::edittimeIconImage;
sf::Sprite AdMobObject::edittimeIcon;
#endif
AdMobObject::AdMobObject(gd::String name_)
: Object(name_), isTesting(true), overlap(true), showOnStartup(true) {}
std::map<gd::String, gd::PropertyDescriptor> AdMobObject::GetProperties(
gd::Project& project) const {
std::map<gd::String, gd::PropertyDescriptor> properties;
properties[_("Banner ID (Android)")].SetValue(androidBannerId);
properties[_("Interstitial ID (Android)")].SetValue(androidInterstitialId);
properties[_("Banner ID (iOS)")].SetValue(iosBannerId);
properties[_("Interstitial ID (iOS)")].SetValue(iosInterstitialId);
properties[_("Testing mode")]
.SetValue(isTesting ? "true" : "false")
.SetType("Boolean");
properties[_("Overlap game")]
.SetValue(overlap ? "true" : "false")
.SetType("Boolean");
properties[_("Show banner on startup")]
.SetValue(showOnStartup ? "true" : "false")
.SetType("Boolean");
properties[_("Banner position")]
.SetValue(position == "Bottom" ? _("Bottom of the screen")
: _("Top of the screen"))
.SetType("Choice")
.AddExtraInfo(_("Top of the screen"))
.AddExtraInfo(_("Bottom of the screen"));
return properties;
}
bool AdMobObject::UpdateProperty(const gd::String& name,
const gd::String& value,
gd::Project& project) {
if (name == _("Banner ID (Android)")) androidBannerId = value;
if (name == _("Interstitial ID (Android)")) androidInterstitialId = value;
if (name == _("Banner ID (iOS)")) iosBannerId = value;
if (name == _("Interstitial ID (iOS)")) iosInterstitialId = value;
if (name == _("Testing mode")) isTesting = value == "1";
if (name == _("Overlap game")) overlap = value == "1";
if (name == _("Show banner on startup")) showOnStartup = value == "1";
if (name == _("Banner position"))
position = value == _("Top of the screen") ? "Top" : "Bottom";
return true;
}
void AdMobObject::DoUnserializeFrom(gd::Project& project,
const gd::SerializerElement& element) {
androidBannerId = element.GetStringAttribute("androidBannerId");
androidInterstitialId = element.GetStringAttribute("androidInterstitialId");
iosBannerId = element.GetStringAttribute("iosBannerId");
iosInterstitialId = element.GetStringAttribute("iosInterstitialId");
position = element.GetStringAttribute("position");
isTesting = element.GetBoolAttribute("isTesting");
overlap = element.GetBoolAttribute("overlap");
showOnStartup = element.GetBoolAttribute("showOnStartup");
}
void AdMobObject::DoSerializeTo(gd::SerializerElement& element) const {
element.SetAttribute("androidBannerId", androidBannerId);
element.SetAttribute("androidInterstitialId", androidInterstitialId);
element.SetAttribute("iosBannerId", iosBannerId);
element.SetAttribute("iosInterstitialId", iosInterstitialId);
element.SetAttribute("position", position);
element.SetAttribute("isTesting", isTesting);
element.SetAttribute("overlap", overlap);
element.SetAttribute("showOnStartup", showOnStartup);
}
#if !defined(GD_NO_WX_GUI)
void AdMobObject::DrawInitialInstance(gd::InitialInstance& instance,
sf::RenderTarget& renderTarget,
gd::Project& project,
gd::Layout& layout) {
edittimeIcon.setPosition(instance.GetX(), instance.GetY());
renderTarget.draw(edittimeIcon);
}
void AdMobObject::LoadEdittimeIcon() {
edittimeIconImage.loadFromFile("JsPlatform/Extensions/admobicon.png");
edittimeIcon.setTexture(edittimeIconImage);
}
bool AdMobObject::GenerateThumbnail(const gd::Project& project,
wxBitmap& thumbnail) const {
thumbnail =
wxBitmap("JsPlatform/Extensions/admobicon24.png", wxBITMAP_TYPE_ANY);
return true;
}
#endif
gd::Object* CreateAdMobObject(gd::String name) { return new AdMobObject(name); }

View File

@@ -1,73 +0,0 @@
/**
GDevelop - AdMob Object Extension
Copyright (c) 2008-2016 Florian Rival (Florian.Rival@gmail.com)
This project is released under the MIT License.
*/
#ifndef ADMOBOBJECT_H
#define ADMOBOBJECT_H
#include "GDCpp/Runtime/Project/Object.h"
#include "GDCpp/Runtime/String.h"
namespace gd {
class InitialInstance;
}
namespace gd {
class Project;
}
namespace sf {
class Texture;
}
namespace sf {
class Sprite;
}
class wxBitmap;
class GD_EXTENSION_API AdMobObject : public gd::Object {
public:
AdMobObject(gd::String name_);
virtual ~AdMobObject(){};
virtual std::unique_ptr<gd::Object> Clone() const override {
return gd::make_unique<AdMobObject>(*this);
}
#if !defined(GD_NO_WX_GUI)
void DrawInitialInstance(gd::InitialInstance& instance,
sf::RenderTarget& renderTarget,
gd::Project& project,
gd::Layout& layout) override;
bool GenerateThumbnail(const gd::Project& project,
wxBitmap& thumbnail) const override;
static void LoadEdittimeIcon();
#endif
std::map<gd::String, gd::PropertyDescriptor> GetProperties(
gd::Project& project) const override;
bool UpdateProperty(const gd::String& name,
const gd::String& value,
gd::Project& project) override;
private:
void DoUnserializeFrom(gd::Project& project,
const gd::SerializerElement& element) override;
void DoSerializeTo(gd::SerializerElement& element) const override;
gd::String androidBannerId;
gd::String androidInterstitialId;
gd::String iosBannerId;
gd::String iosInterstitialId;
gd::String position; //"Top" or "Bottom"
bool isTesting;
bool overlap;
bool showOnStartup;
#if !defined(GD_NO_WX_GUI)
static sf::Texture edittimeIconImage;
static sf::Sprite edittimeIcon;
#endif
};
gd::Object* CreateAdMobObject(gd::String name);
#endif // ADMOBOBJECT_H

View File

@@ -1,21 +0,0 @@
cmake_minimum_required(VERSION 2.6)
cmake_policy(SET CMP0015 NEW)
project(AdMobObject)
gd_add_extension_includes()
#Defines
###
gd_add_extension_definitions(AdMobObject)
#The targets
###
include_directories(.)
file(GLOB source_files *.cpp *.h)
gd_add_clang_utils(AdMobObject "${source_files}")
gd_add_extension_target(AdMobObject "${source_files}" "JsPlatform")
#Linker files for the IDE extension
###
gd_extension_link_libraries(AdMobObject)

View File

@@ -1,94 +0,0 @@
/**
GDevelop - AdMob Object Extension
Copyright (c) 2008-2016 Florian Rival (Florian.Rival@gmail.com)
This project is released under the MIT License.
*/
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Tools/Localization.h"
#include "AdMobObject.h"
void DeclareAdMobObjectExtension(gd::PlatformExtension& extension) {
extension
.SetExtensionInformation(
"AdMobObject",
_("AdMob banners and interstitial screens"),
_("Display an ads banner and interstitial screens powered by AdMob."),
"Florian Rival",
"Open source (MIT License)")
.SetExtensionHelpPath("/objects/admob");
gd::ObjectMetadata& obj = extension.AddObject<AdMobObject>(
"AdMob",
_("AdMob banner"),
_("Display an ad banner or interstitial screen using AdMob"),
"JsPlatform/Extensions/admobicon.png");
obj.SetHelpUrl("/gdevelop/documentation/manual/built_admob");
#if !defined(GD_NO_WX_GUI)
AdMobObject::LoadEdittimeIcon();
#endif
obj.AddAction("ShowBanner",
_("Show banner ad"),
_("Show the banner ad"),
_("Show the banner ad of _PARAM0_"),
_("Banner"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png")
.AddParameter("object", _("Object"), "AdMob");
obj.AddAction("HideBanner",
_("Hide banner ad"),
_("Hide the banner ad"),
_("Hide the banner ad of _PARAM0_"),
_("Banner"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png")
.AddParameter("object", _("Object"), "AdMob");
obj.AddCondition(
"BannerDisplayed",
_("Banner is displayed"),
_("Return true if the object is currently displaying a banner"),
_("_PARAM0_ is displaying a banner"),
"",
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png")
.AddParameter("object", _("Object"), "AdMob");
obj.AddAction("PreloadInterstitial",
_("Preload interstitial screen"),
_("Preload the interstitial screen in memory, so that it can "
"be shown later.\nYou can use this action at the beginning "
"of a level for example."),
_("Preload an interstitial screen for _PARAM0_"),
_("Interstitial screen"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png")
.AddParameter("object", _("Object"), "AdMob");
obj.AddAction(
"ShowInterstitial",
_("Show interstitial screen"),
_("Show the interstitial screen.\nIf the interstitial screen has not "
"been preloaded, it will be loaded and displayed when ready."),
_("Show the interstitial screen of _PARAM0_"),
_("Interstitial screen"),
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png")
.AddParameter("object", _("Object"), "AdMob");
obj.AddCondition("InterstitialReady",
_("Interstitial screen is ready"),
_("Return true if the interstitial screen was loaded and is "
"ready to be shown."),
_("Interstitial screen of _PARAM0_ is ready"),
"",
"JsPlatform/Extensions/admobicon24.png",
"JsPlatform/Extensions/admobicon16.png")
.AddParameter("object", _("Object"), "AdMob");
}

View File

@@ -1,66 +0,0 @@
/**
GDevelop - AdMob Object Extension
Copyright (c) 2008-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 "AdMobObject.h"
#include <iostream>
#include "GDCore/Tools/Localization.h"
void DeclareAdMobObjectExtension(gd::PlatformExtension& extension);
/**
* \brief This class declares information about the JS extension.
*/
class AdMobObjectJsExtension : public gd::PlatformExtension {
public:
/**
* Constructor of an extension declares everything the extension contains:
* objects, actions, conditions and expressions.
*/
AdMobObjectJsExtension() {
DeclareAdMobObjectExtension(*this);
GetObjectMetadata("AdMobObject::AdMob")
.SetIncludeFile("Extensions/AdMobObject/admobruntimeobject.js");
GetAllActionsForObject("AdMobObject::AdMob")["AdMobObject::ShowBanner"]
.SetFunctionName("showBanner");
GetAllActionsForObject("AdMobObject::AdMob")["AdMobObject::HideBanner"]
.SetFunctionName("hideBanner");
GetAllConditionsForObject(
"AdMobObject::AdMob")["AdMobObject::BannerDisplayed"]
.SetFunctionName("isBannerDisplayed");
GetAllActionsForObject(
"AdMobObject::AdMob")["AdMobObject::PreloadInterstitial"]
.SetFunctionName("prepareInterstitial");
GetAllActionsForObject(
"AdMobObject::AdMob")["AdMobObject::ShowInterstitial"]
.SetFunctionName("showInterstitial");
GetAllConditionsForObject(
"AdMobObject::AdMob")["AdMobObject::InterstitialReady"]
.SetFunctionName("isInterstitialReady");
GD_COMPLETE_EXTENSION_COMPILATION_INFORMATION();
};
};
#if defined(EMSCRIPTEN)
extern "C" gd::PlatformExtension* CreateGDJSAdMobObjectExtension() {
return new AdMobObjectJsExtension;
}
#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 AdMobObjectJsExtension;
}
#endif
#endif

View File

@@ -1,160 +0,0 @@
/**
GDevelop - AdMob Object Extension
Copyright (c) 2008-2016 Florian Rival (Florian.Rival@gmail.com)
This project is released under the MIT License.
*/
if (typeof AdMob !== "undefined")
console.warn("AdMob plugin for Cordova is not installed - no ads will be displayed. Ensure you have installed com.google.cordova.admob or cordova-plugin-admobpro.");
/**
* The AdMobRuntimeObject displays an AdMob ad banner on screen.
* This works with Cordova compatible platforms with `cordova-plugin-admobpro` plugin installed.
*
* @memberof gdjs
* @class AdMobRuntimeObject
* @extends RuntimeObject
* @memberof gdjs
*/
gdjs.AdMobRuntimeObject = function(runtimeScene, objectData)
{
gdjs.RuntimeObject.call(this, runtimeScene, objectData);
this._androidBannerId = objectData.androidBannerId;
this._androidInterstitialId = objectData.androidInterstitialId;
this._iosBannerId = objectData.iosBannerId;
this._iosInterstitialId = objectData.iosInterstitialId;
this._isTesting = objectData.isTesting;
this._position = objectData.position;
this._overlap = objectData.overlap;
this._showOnStartup = objectData.showOnStartup;
this._bannerDisplayed = false;
this._interstitialReady = false;
if (this._showOnStartup)
this.createBanner();
document.addEventListener('onAdPresent', this._onAdPresent.bind(this), false);
document.addEventListener('onAdDismiss', this._onAdDismiss.bind(this), false);
};
gdjs.AdMobRuntimeObject.prototype = Object.create( gdjs.RuntimeObject.prototype );
gdjs.AdMobRuntimeObject.thisIsARuntimeObjectConstructor = "AdMobObject::AdMob";
gdjs.AdMobRuntimeObject.getPlatformName = function() {
if( /(android)/i.test(navigator.userAgent) ) {
return "android";
} else if(/(ipod|iphone|ipad)/i.test(navigator.userAgent)) {
return "ios";
} else {
return "windowsPhone";
}
};
gdjs.AdMobRuntimeObject.prototype._onAdPresent = function(data) {
if (data.adType == 'interstitial')
this._interstitialReady = false;
};
gdjs.AdMobRuntimeObject.prototype._onAdDismiss = function(data) {
if (data.adType == 'interstitial')
this._interstitialReady = false;
};
gdjs.AdMobRuntimeObject.prototype.createBanner = function() {
if (typeof AdMob === "undefined") return;
var adName = "_" + gdjs.AdMobRuntimeObject.getPlatformName() + "BannerId";
if (!this.hasOwnProperty(adName)) return;
var adId = this[adName];
var position = AdMob.AD_POSITION.TOP_CENTER;
if (this._position === "Bottom")
position = AdMob.AD_POSITION.BOTTOM_CENTER;
var that = this;
AdMob.createBanner({
adId: adId || 'not-specified-xxx', //Avoid a crash by never letting the id empty.
position: position,
autoShow: true,
overlap: this._overlap,
isTesting: this._isTesting
}, function() {
that._bannerDisplayed = true;
}, function() {
that._bannerDisplayed = false;
});
};
gdjs.AdMobRuntimeObject.prototype.showBanner = function() {
if (typeof AdMob === "undefined") return;
if (!this._bannerDisplayed)
this.createBanner();
}
gdjs.AdMobRuntimeObject.prototype.hideBanner = function() {
if (typeof AdMob === "undefined") return;
this._bannerDisplayed = false;
AdMob.removeBanner();
};
gdjs.AdMobRuntimeObject.prototype.isBannerDisplayed = function() {
return this._bannerDisplayed;
};
gdjs.AdMobRuntimeObject.prototype.showInterstitial = function(runtimeScene) {
if (typeof AdMob === "undefined") return;
if (!this._interstitialReady) {
this.prepareInterstitial(function() {
AdMob.showInterstitial();
})
} else {
AdMob.showInterstitial();
}
};
gdjs.AdMobRuntimeObject.prototype.prepareInterstitial = function(cb) {
if (typeof AdMob === "undefined") return;
var adName = "_" + gdjs.AdMobRuntimeObject.getPlatformName() + "InterstitialId";
if (!this.hasOwnProperty(adName)) return;
var adId = this[adName];
var that = this;
AdMob.prepareInterstitial({
adId: adId || 'not-specified-xxx', //Avoid a crash by never letting the id empty.
autoShow: false
}, function() {
that._interstitialReady = true;
cb();
}, function() {
that._interstitialReady = false;
cb();
});
};
gdjs.AdMobRuntimeObject.prototype.isInterstitialReady = function() {
return this._interstitialReady;
};
gdjs.AdMobRuntimeObject.prototype.onDeletedFromScene = function(runtimeScene) {
gdjs.RuntimeObject.prototype.onDeletedFromScene.call(this, runtimeScene);
if (typeof AdMob === "undefined") return;
document.removeEventListener('onAdPresent', this._onAdPresent, false);
document.removeEventListener('onAdDismiss', this._onAdDismiss, false);
if (this._bannerDisplayed) this.hideBanner();
};
gdjs.AdMobRuntimeObject.prototype.setLayer = function(layer) {
// No renderable object
};
gdjs.AdMobRuntimeObject.prototype.setZOrder = function(z) {
// No renderable object
};

View File

@@ -8,7 +8,6 @@ project(GD-Extensions)
include(CMakeUtils.txt) #Functions to factor common tasks done in CMakeLists.txt of extensions
#Add all the CMakeLists:
ADD_SUBDIRECTORY(AdMobObject)
ADD_SUBDIRECTORY(AnchorBehavior)
IF (NOT EMSCRIPTEN)
ADD_SUBDIRECTORY(AdvancedXML)

View File

@@ -152,10 +152,6 @@ gdjs.PlatformRuntimeBehavior.prototype.doStepPreEvents = function(runtimeScene)
gdjs.PlatformRuntimeBehavior.prototype.doStepPostEvents = function(runtimeScene) {
};
gdjs.PlatformRuntimeBehavior.prototype.getAABB = function(){
return this.owner.getAABB();
};
gdjs.PlatformRuntimeBehavior.prototype.onActivate = function() {
if (this._registeredInManager) return;

View File

@@ -120,7 +120,7 @@ AdvancedExtension::AdvancedExtension() {
parameterNameCode + ")) || 0 : 0)";
});
GetAllExpressions()["GetArgumentAsString"]
GetAllStrExpressions()["GetArgumentAsString"]
.GetCodeExtraInformation()
.SetCustomCodeGenerator([&generateParameterNameCode](
const std::vector<gd::Expression>& parameters,

View File

@@ -133,7 +133,6 @@ gd::PlatformExtension *CreateGDJSTiledSpriteObjectExtension();
gd::PlatformExtension *CreateGDJSDraggableBehaviorExtension();
gd::PlatformExtension *CreateGDJSTopDownMovementBehaviorExtension();
gd::PlatformExtension *CreateGDJSTextObjectExtension();
gd::PlatformExtension *CreateGDJSAdMobObjectExtension();
gd::PlatformExtension *CreateGDJSPanelSpriteObjectExtension();
gd::PlatformExtension *CreateGDJSAnchorBehaviorExtension();
gd::PlatformExtension *CreateGDJSPrimitiveDrawingExtension();
@@ -252,9 +251,6 @@ JsPlatform::JsPlatform() : gd::Platform() {
AddExtension(std::shared_ptr<gd::PlatformExtension>(
CreateGDJSPhysicsBehaviorExtension()));
std::cout.flush();
AddExtension(
std::shared_ptr<gd::PlatformExtension>(CreateGDJSAdMobObjectExtension()));
std::cout.flush();
#endif
std::cout << "done." << std::endl;
};

View File

@@ -229,6 +229,13 @@ bool ExporterHelper::ExportCordovaConfigFile(const gd::Project &project,
.FindAndReplace("GDJS_ICON_IOS_100",
getIconFilename("ios", "icon-100"));
if(!project.GetAdMobAppId().empty()){
str = str.FindAndReplace("<!-- GDJS_ADMOB_PLUGIN_AND_APPLICATION_ID -->",
"<plugin name=\"cordova-plugin-admob-free\" spec=\"~0.21.0\">\n"
"\t\t<variable name=\"ADMOB_APP_ID\" value=\"" + project.GetAdMobAppId() + "\" />\n"
"\t</plugin>");
}
if (!fs.WriteToFile(exportDir + "/config.xml", str)) {
lastError = "Unable to write configuration file.";
return false;

View File

@@ -55,4 +55,6 @@
</platform>
<preference name="orientation" value="GDJS_ORIENTATION" />
<preference name="BackgroundColor" value="0xff000000"/>
<preference name="phonegap-version" value="cli-8.0.0" />
<!-- GDJS_ADMOB_PLUGIN_AND_APPLICATION_ID -->
</widget>

View File

@@ -112,10 +112,12 @@ gdjs.LayerCocosRenderer.prototype.updatePosition = function() {
this._cocosLayer.setRotation(-this._layer.getCameraRotation());
this._cocosLayer.setScale(zoomFactor, zoomFactor);
var centerX = (this._layer.getCameraX()-this._layer.getWidth()/2)*Math.cos(-angle)
- (this._layer.getCameraY()-this._layer.getHeight()/2)*Math.sin(-angle);
var centerY = (this._layer.getCameraX()-this._layer.getWidth()/2)*Math.sin(-angle)
+ (this._layer.getCameraY()-this._layer.getHeight()/2)*Math.cos(-angle);
var cosValue = Math.cos(-angle);
var sinValue = Math.sin(-angle);
var centerX = (this._layer.getCameraX()-this._layer.getWidth()/2)*cosValue
- (this._layer.getCameraY()-this._layer.getHeight()/2)*sinValue;
var centerY = (this._layer.getCameraX()-this._layer.getWidth()/2)*sinValue
+ (this._layer.getCameraY()-this._layer.getHeight()/2)*cosValue;
this._cocosLayer.setPositionX(-centerX);
this._cocosLayer.setPositionY(+centerY);

View File

@@ -168,6 +168,10 @@ gdjs.RuntimeSceneCocosRenderer.prototype.makeEventListeners = function() {
})];
}
gdjs.RuntimeSceneCocosRenderer.prototype.renderDebugDraw = function() {
// Not implemented
}
gdjs.RuntimeSceneCocosRenderer.prototype.hideCursor = function() {
//TODO
}

View File

@@ -17,7 +17,7 @@ gdjs.Force = function(x,y, clearing)
{
this._x = x || 0;
this._y = y || 0;
this._angle = Math.atan2(y,x)*180/3.14159;
this._angle = Math.atan2(y,x)*180/Math.PI;
this._length = Math.sqrt(x*x+y*y);
this._dirty = false;
this._clearing = clearing;
@@ -67,8 +67,9 @@ gdjs.Force.prototype.setAngle = function(angle) {
}
this._angle = angle;
this._x = Math.cos(angle/180*3.14159)*this._length;
this._y = Math.sin(angle/180*3.14159)*this._length;
var angleInRadians = angle/180*Math.PI;
this._x = Math.cos(angleInRadians)*this._length;
this._y = Math.sin(angleInRadians)*this._length;
}
/**
@@ -78,13 +79,14 @@ gdjs.Force.prototype.setAngle = function(angle) {
gdjs.Force.prototype.setLength = function(len) {
if ( this._dirty ) {
this._angle = Math.atan2(this._y, this._x)*180/3.14159;
this._angle = Math.atan2(this._y, this._x)*180/Math.PI;
this._dirty = false;
}
this._length = len;
this._x = Math.cos(this._angle/180*3.14159)*this._length;
this._y = Math.sin(this._angle/180*3.14159)*this._length;
var angleInRadians = this._angle/180*Math.PI;
this._x = Math.cos(angleInRadians)*this._length;
this._y = Math.sin(angleInRadians)*this._length;
}
/**
@@ -92,7 +94,7 @@ gdjs.Force.prototype.setLength = function(len) {
*/
gdjs.Force.prototype.getAngle = function() {
if ( this._dirty ) {
this._angle = Math.atan2(this._y, this._x)*180/3.14159;
this._angle = Math.atan2(this._y, this._x)*180/Math.PI;
this._length = Math.sqrt(this._x*this._x+this._y*this._y);
this._dirty = false;
@@ -107,7 +109,7 @@ gdjs.Force.prototype.getAngle = function() {
*/
gdjs.Force.prototype.getLength = function() {
if ( this._dirty ) {
this._angle = Math.atan2(this._y, this._x)*180/3.14159;
this._angle = Math.atan2(this._y, this._x)*180/Math.PI;
this._length = Math.sqrt(this._x*this._x+this._y*this._y);
this._dirty = false;

View File

@@ -171,23 +171,31 @@ gdjs.Layer.prototype.convertCoords = function(x, y, cameraId) {
x /= Math.abs(this._zoomFactor);
y /= Math.abs(this._zoomFactor);
// Only compute angle and cos/sin once (allow heavy optimization from JS engines).
var angleInRadians = this._cameraRotation/180*Math.PI;
var tmp = x;
x = Math.cos(this._cameraRotation/180*3.14159)*x - Math.sin(this._cameraRotation/180*3.14159)*y;
y = Math.sin(this._cameraRotation/180*3.14159)*tmp + Math.cos(this._cameraRotation/180*3.14159)*y;
var cosValue = Math.cos(angleInRadians);
var sinValue = Math.sin(angleInRadians);
x = cosValue*x - sinValue*y;
y = sinValue*tmp + cosValue*y;
return [x + this.getCameraX(cameraId), y + this.getCameraY(cameraId)];
};
gdjs.Layer.prototype.convertInverseCoords = function(x, y, cameraId) {
x -= this.getCameraX(cameraId);
y -= this.getCameraY(cameraId);
x -= this.getCameraX(cameraId);
y -= this.getCameraY(cameraId);
// Only compute angle and cos/sin once (allow heavy optimization from JS engines).
var angleInRadians = this._cameraRotation/180*Math.PI;
var tmp = x;
x = Math.cos(-this._cameraRotation/180*3.14159)*x - Math.sin(-this._cameraRotation/180*3.14159)*y;
y = Math.sin(-this._cameraRotation/180*3.14159)*tmp + Math.cos(-this._cameraRotation/180*3.14159)*y;
var cosValue = Math.cos(-angleInRadians);
var sinValue = Math.sin(-angleInRadians);
x = cosValue*x - sinValue*y;
y = sinValue*tmp + cosValue*y;
x *= Math.abs(this._zoomFactor);
y *= Math.abs(this._zoomFactor);
x *= Math.abs(this._zoomFactor);
y *= Math.abs(this._zoomFactor);
return [x + this._width/2, y + this._height/2];
};

View File

@@ -23,10 +23,12 @@ gdjs.LayerPixiRenderer.prototype.updatePosition = function() {
this._pixiContainer.scale.x = zoomFactor;
this._pixiContainer.scale.y = zoomFactor;
var centerX = (this._layer.getCameraX()*zoomFactor)*Math.cos(angle)
- (this._layer.getCameraY()*zoomFactor)*Math.sin(angle);
var centerY = (this._layer.getCameraX()*zoomFactor)*Math.sin(angle)
+ (this._layer.getCameraY()*zoomFactor)*Math.cos(angle);
var cosValue = Math.cos(angle);
var sinValue = Math.sin(angle);
var centerX = (this._layer.getCameraX()*zoomFactor)*cosValue
- (this._layer.getCameraY()*zoomFactor)*sinValue;
var centerY = (this._layer.getCameraX()*zoomFactor)*sinValue
+ (this._layer.getCameraY()*zoomFactor)*cosValue;
this._pixiContainer.position.x = -centerX;
this._pixiContainer.position.y = -centerY;

View File

@@ -47,6 +47,33 @@ gdjs.RuntimeScenePixiRenderer.prototype._renderProfileText = function() {
this._profilerText.text = outputs.join("\n");
};
gdjs.RuntimeScenePixiRenderer.prototype.renderDebugDraw = function(instances, layersCameraCoordinates) {
if (!this._debugDraw) {
this._debugDraw = new PIXI.Graphics();
this._pixiContainer.addChild(this._debugDraw);
}
/** @type PIXI.Graphics */
var debugDraw = this._debugDraw;
debugDraw.clear();
debugDraw.beginFill(0x6868e8);
debugDraw.lineStyle(1, 0x6868e8, 1);
debugDraw.fillAlpha = 0.1;
debugDraw.alpha = 0.8;
for(var i = 0;i < instances.length;i++) {
var object = instances[i];
var cameraCoords = layersCameraCoordinates[object.getLayer()];
var rendererObject = object.getRendererObject();
if (!cameraCoords || !rendererObject) continue;
var aabb = object.getAABB();
debugDraw.drawRect(aabb.min[0], aabb.min[1], aabb.max[0] - aabb.min[0], aabb.max[1] - aabb.min[1]);
}
debugDraw.endFill();
};
gdjs.RuntimeScenePixiRenderer.prototype.hideCursor = function() {
this._pixiRenderer.view.style.cursor = "none";
};

View File

@@ -601,8 +601,9 @@ gdjs.RuntimeObject.prototype.addForce = function(x,y, clearing) {
* @param {number} clearing Set the force clearing
*/
gdjs.RuntimeObject.prototype.addPolarForce = function(angle, len, clearing) {
var forceX = Math.cos(angle/180*3.14159)*len;
var forceY = Math.sin(angle/180*3.14159)*len;
var angleInRadians = angle/180*3.14159; //TODO: Benchmark with Math.PI
var forceX = Math.cos(angleInRadians)*len;
var forceY = Math.sin(angleInRadians)*len;
this._forces.push(this._getRecycledForce(forceX, forceY, clearing));
};
@@ -713,8 +714,10 @@ gdjs.RuntimeObject.prototype.averageForceAngleIs = function(angle, toleranceInDe
/**
* Get the hit boxes for the object.<br>
* The default implementation returns a basic bouding box based on the result of getWidth and
* getHeight. You should probably redefine updateHitBoxes instead of this function.
* The default implementation returns a basic bouding box based the size (getWidth and
* getHeight) and the center point of the object (getCenterX and getCenterY).
*
* You should probably redefine updateHitBoxes instead of this function.
*
* @return {Array} An array composed of polygon.
*/
@@ -732,35 +735,61 @@ gdjs.RuntimeObject.prototype.getHitBoxes = function() {
};
/**
* Update the hit boxes for the object.<br>
* The default implementation set a basic bouding box based on the result of getWidth and
* getHeight.
* Update the hit boxes for the object.
*
* The default implementation set a basic bounding box based on the size (getWidth and
* getHeight) and the center point of the object (getCenterX and getCenterY).
* Result is cached until invalidated (by a position change, angle change...).
*
* You should not call this function by yourself, it is called when necessary by getHitBoxes method.
* However, you can redefine it if your object need custom hit boxes.
*
*/
gdjs.RuntimeObject.prototype.updateHitBoxes = function() {
//Ensure we're using the default hitbox (a single rectangle)
this.hitBoxes = this._defaultHitBoxes;
var width = this.getWidth();
var height = this.getHeight();
this.hitBoxes[0].vertices[0][0] =-width/2.0;
this.hitBoxes[0].vertices[0][1] =-height/2.0;
this.hitBoxes[0].vertices[1][0] =+width/2.0;
this.hitBoxes[0].vertices[1][1] =-height/2.0;
this.hitBoxes[0].vertices[2][0] =+width/2.0;
this.hitBoxes[0].vertices[2][1] =+height/2.0;
this.hitBoxes[0].vertices[3][0] =-width/2.0;
this.hitBoxes[0].vertices[3][1] =+height/2.0;
var centerX = this.getCenterX();
var centerY = this.getCenterY();
this.hitBoxes[0].rotate(this.getAngle()/180*3.14159);
this.hitBoxes[0].move(this.getDrawableX()+this.getCenterX(), this.getDrawableY()+this.getCenterY());
if (this.getCenterX() === width / 2 && this.getCenterY() === height / 2) {
this.hitBoxes[0].vertices[0][0] =-width/2.0;
this.hitBoxes[0].vertices[0][1] =-height/2.0;
this.hitBoxes[0].vertices[1][0] =+width/2.0;
this.hitBoxes[0].vertices[1][1] =-height/2.0;
this.hitBoxes[0].vertices[2][0] =+width/2.0;
this.hitBoxes[0].vertices[2][1] =+height/2.0;
this.hitBoxes[0].vertices[3][0] =-width/2.0;
this.hitBoxes[0].vertices[3][1] =+height/2.0;
-
this.hitBoxes[0].rotate(this.getAngle()/180*Math.PI);
this.hitBoxes[0].move(this.getDrawableX()+this.getCenterX(), this.getDrawableY()+this.getCenterY());
} else {
this.hitBoxes[0].vertices[0][0] = 0;
this.hitBoxes[0].vertices[0][1] = 0;
this.hitBoxes[0].vertices[1][0] = width;
this.hitBoxes[0].vertices[1][1] = 0;
this.hitBoxes[0].vertices[2][0] = width;
this.hitBoxes[0].vertices[2][1] = height;
this.hitBoxes[0].vertices[3][0] = 0;
this.hitBoxes[0].vertices[3][1] = height;
this.hitBoxes[0].move(-centerX, -centerY);
this.hitBoxes[0].rotate(this.getAngle()/180*Math.PI);
this.hitBoxes[0].move(this.getDrawableX()+centerX, this.getDrawableY()+centerY);
}
};
//Experimental
/**
* Get the AABB (axis aligned bounding box) for the object.
*
* The default implementation uses either the position/size of the object (when angle is 0) or
* hitboxes (when angle is not 0) to compute the bounding box.
* Result is cached until invalidated (by a position change, angle change...).
*
* You should probably redefine updateAABB instead of this function.
*
* @return The bounding box (example: {min: [10,5], max:[20,10]})
*/
gdjs.RuntimeObject.prototype.getAABB = function() {
if ( this.hitBoxesDirty ) {
this.updateHitBoxes();
@@ -771,11 +800,48 @@ gdjs.RuntimeObject.prototype.getAABB = function() {
return this.aabb;
};
/**
* Update the AABB (axis aligned bounding box) for the object.
*
* Default implementation uses either the position/size of the object (when angle is 0) or
* hitboxes (when angle is not 0) to compute the bounding box.
*
* You should not call this function by yourself, it is called when necessary by getAABB method.
* However, you can redefine it if your object can have a faster implementation.
*/
gdjs.RuntimeObject.prototype.updateAABB = function() {
this.aabb.min[0] = this.getDrawableX();
this.aabb.min[1] = this.getDrawableY();
this.aabb.max[0] = this.aabb.min[0] + this.getWidth();
this.aabb.max[1] = this.aabb.min[1] + this.getHeight();
if (this.getAngle() === 0) {
// Fast/simple computation of AABB for non rotated object
// (works even for object with non default center/origin
// because we're using getDrawableX/Y)
this.aabb.min[0] = this.getDrawableX();
this.aabb.min[1] = this.getDrawableY();
this.aabb.max[0] = this.aabb.min[0] + this.getWidth();
this.aabb.max[1] = this.aabb.min[1] + this.getHeight();
} else {
// Use hitboxes if object is rotated to ensure that the AABB
// is properly bounding the whole object.
// Slower (10-15% slower).
var first = true;
for(var i = 0;i<this.hitBoxes.length;i++) {
for(var j = 0;j<this.hitBoxes[i].vertices.length;j++) {
var vertex = this.hitBoxes[i].vertices[j];
if (first) {
this.aabb.min[0] = vertex[0];
this.aabb.max[0] = vertex[0];
this.aabb.min[1] = vertex[1];
this.aabb.max[1] = vertex[1];
first = false;
} else {
this.aabb.min[0] = Math.min(this.aabb.min[0], vertex[0]);
this.aabb.max[0] = Math.max(this.aabb.max[0], vertex[0]);
this.aabb.min[1] = Math.min(this.aabb.min[1], vertex[1]);
this.aabb.max[1] = Math.max(this.aabb.max[1], vertex[1]);
}
}
}
}
};
//Behaviors:

View File

@@ -240,7 +240,14 @@ gdjs.RuntimeScene.prototype.renderAndStep = function(elapsedTime) {
this._updateObjectsVisibility();
if (this._profiler) this._profiler.end("objects (visibility)");
if (this._profiler) this._profiler.begin("render");
if (this._profiler) this._profiler.begin("render");
// Uncomment to enable debug rendering (look for the implementation in the renderer
// to see what is rendered)
// if (this._layersCameraCoordinates) {
// this.getRenderer().renderDebugDraw(this._allInstancesList, this._layersCameraCoordinates); //TODO
// }
this.render();
if (this._profiler) this._profiler.end("render");

View File

@@ -554,8 +554,13 @@ gdjs.SpriteRuntimeObject.prototype._transformToGlobal = function(x, y, result) {
//Rotation
var oldX = x;
x = cx + Math.cos(this.angle/180*3.14159)*(x-cx) - Math.sin(this.angle/180*3.14159)*(y-cy);
y = cy + Math.sin(this.angle/180*3.14159)*(oldX-cx) + Math.cos(this.angle/180*3.14159)*(y-cy);
var angleInRadians = this.angle/180*Math.PI;
var cosValue = Math.cos(angleInRadians); // Only compute cos and sin once (10% faster than doing it twice)
var sinValue = Math.sin(angleInRadians);
var xToCenterXDelta = x-cx;
var yToCenterYDelta = y-cy;
x = cx + cosValue*(xToCenterXDelta) - sinValue*(yToCenterYDelta);
y = cy + sinValue*(xToCenterXDelta) + cosValue*(yToCenterYDelta);
result.length = 2;
result[0] = x + this.getDrawableX();

View File

@@ -0,0 +1,14 @@
describe('gdjs.Force', function() {
it('benchmark setting angle and length', function(){
this.timeout(6000);
var layer = new gdjs.Force();
const benchmarkSuite = makeBenchmarkSuite();
benchmarkSuite.add('setAngle and setLength', (i) => {
layer.setAngle(i);
layer.setLength(200);
});
console.log(benchmarkSuite.run());
});
});

View File

@@ -0,0 +1,47 @@
/**
* Helper allowing to run a benchmark of the time spent the execute a certain
* number of iterations of one or more functions.
*
* Note that this could surely be replaced by a more robust solution like
* Benchmark.js
*
* @param {*} options
*/
let makeBenchmarkSuite = (options = {}) => {
const benchmarkTimings = {};
const benchmarksCount = options.benchmarksCount || 1000;
const iterationsCount = options.iterationsCount || 100000;
const testCases = [];
const suite = {};
suite.add = (title, fn) => {
testCases.push({ title, fn });
return suite;
};
suite.run = () => {
for (
let benchmarkIndex = 0;
benchmarkIndex < benchmarksCount;
benchmarkIndex++
) {
testCases.forEach(testCase => {
const description = testCase.title + '(' + iterationsCount + 'x)';
const start = performance.now();
for (let i = 0; i < iterationsCount; i++) {
testCase.fn(i);
}
benchmarkTimings[description] = benchmarkTimings[description] || [];
benchmarkTimings[description].push(performance.now() - start);
});
}
const results = {};
for (let benchmarkName in benchmarkTimings) {
results[benchmarkName] =
benchmarkTimings[benchmarkName].reduce((sum, value) => sum + value, 0) /
benchmarksCount;
}
return results;
};
return suite;
};

View File

@@ -0,0 +1,29 @@
describe('gdjs.Layer', function() {
var runtimeGame = new gdjs.RuntimeGame({
variables: [],
properties: { windowWidth: 800, windowHeight: 600 },
});
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
it('benchmark convertCoords and convertInverseCoords', function() {
this.timeout(6000);
var layer = new gdjs.Layer(
{ name: 'My layer', visibility: true, effects: [] },
runtimeScene
);
layer.setCameraX(100, 0);
layer.setCameraY(200, 0);
layer.setCameraRotation(90, 0);
const benchmarkSuite = makeBenchmarkSuite();
benchmarkSuite
.add('convertCoords', () => {
layer.convertCoords(350, 450, 0);
})
.add('convertInverseCoords', () => {
layer.convertInverseCoords(350, 450, 0);
});
console.log(benchmarkSuite.run());
});
});

View File

@@ -0,0 +1,53 @@
describe('gdjs.RuntimeObject', function() {
const runtimeScene = new gdjs.RuntimeScene(null);
it('benchmark getAABB of rotated vs non rotated objects', function(){
var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []});
object.getWidth = function() { return 10; };
object.getHeight = function() { return 20; };
object.setPosition(15, 20);
const benchmarkSuite = makeBenchmarkSuite({
benchmarksCount: 60,
iterationsCount: 60000,
});
benchmarkSuite
.add('getAABB of a non rotated, default center', (i) => {
object.setX(i);
object.getAABB();
})
.add('getAABB of a rotated, default center', (i) => {
object.setX(i);
object.getAABB();
});
console.log(benchmarkSuite.run());
});
it('benchmark getAABB of rotated vs non rotated objects, with non default center', function(){
var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []});
object.getWidth = function() { return 10; };
object.getHeight = function() { return 20; };
object.getCenterX = function() { return 0 };
object.getCenterY = function() { return 0 };
object.setPosition(15, 20);
const benchmarkSuite = makeBenchmarkSuite({
benchmarksCount: 60,
iterationsCount: 60000,
});
benchmarkSuite
.add('getAABB of a non rotated, non default center', (i) => {
object.setAngle(0);
object.setX(i);
object.getAABB();
})
.add('getAABB of a rotated, non default center', (i) => {
object.setAngle(90);
object.setX(i);
object.getAABB();
});
console.log(benchmarkSuite.run());
});
});

View File

@@ -0,0 +1,86 @@
describe('gdjs.SpriteRuntimeObject', function() {
var runtimeGame = new gdjs.RuntimeGame({variables: [], properties: {windowWidth: 800, windowHeight: 600}});
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
const makeSpriteRuntimeObjectWithCustomHitBox = (runtimeScene) => new gdjs.SpriteRuntimeObject(runtimeScene, {
"name": "obj1",
"type": "Sprite",
"updateIfNotVisible": false,
"variables": [],
"behaviors": [],
"animations": [
{
"name": "NewObject2",
"useMultipleDirections": false,
"directions": [
{
"looping": false,
"timeBetweenFrames": 1,
"sprites": [
{
"hasCustomCollisionMask": true,
"image": "NewObject2-2.png",
"points": [],
"originPoint": {
"name": "origine",
"x": 32,
"y": 16
},
"centerPoint": {
"automatic": false,
"name": "centre",
"x": 64,
"y": 31
},
"customCollisionMask": [
[
{
"x": 12.5,
"y": 1
},
{
"x": 41.5,
"y": 2
},
{
"x": 55.5,
"y": 31
},
{
"x": 24.5,
"y": 30
}
]
]
}
]
}
]
}
]
});
it('benchmark getAABB of rotated vs non rotated sprite, with custom hitboxes, origin and center', function(){
this.timeout(6000);
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
const benchmarkSuite = makeBenchmarkSuite({
benchmarksCount: 60,
iterationsCount: 60000,
});
benchmarkSuite
.add('getAABB of a non rotated sprite, with custom hitboxes, origin and center', (i) => {
object.setAngle(0);
object.setX(i);
object.getAABB();
})
.add('getAABB of a rotated sprite, with custom hitboxes, origin and center', (i) => {
object.setAngle(90);
object.setX(i);
object.getAABB();
});
console.log(benchmarkSuite.run());
});
});

View File

@@ -111,6 +111,7 @@
"alwaysLoaded": false,
"file": "p1_stand.png",
"kind": "image",
"metadata": "",
"name": "p1_stand.png",
"smoothed": true,
"userAdded": false
@@ -119,6 +120,7 @@
"alwaysLoaded": false,
"file": "p1_jump.png",
"kind": "image",
"metadata": "",
"name": "p1_jump.png",
"smoothed": true,
"userAdded": false
@@ -127,6 +129,7 @@
"alwaysLoaded": false,
"file": "p1_walk01.png",
"kind": "image",
"metadata": "",
"name": "p1_walk01.png",
"smoothed": true,
"userAdded": false
@@ -135,6 +138,7 @@
"alwaysLoaded": false,
"file": "p1_walk02.png",
"kind": "image",
"metadata": "",
"name": "p1_walk02.png",
"smoothed": true,
"userAdded": false
@@ -143,6 +147,7 @@
"alwaysLoaded": false,
"file": "p1_walk03.png",
"kind": "image",
"metadata": "",
"name": "p1_walk03.png",
"smoothed": true,
"userAdded": false
@@ -151,6 +156,7 @@
"alwaysLoaded": false,
"file": "p1_walk04.png",
"kind": "image",
"metadata": "",
"name": "p1_walk04.png",
"smoothed": true,
"userAdded": false
@@ -159,6 +165,7 @@
"alwaysLoaded": false,
"file": "p1_walk05.png",
"kind": "image",
"metadata": "",
"name": "p1_walk05.png",
"smoothed": true,
"userAdded": false
@@ -167,6 +174,7 @@
"alwaysLoaded": false,
"file": "p1_walk06.png",
"kind": "image",
"metadata": "",
"name": "p1_walk06.png",
"smoothed": true,
"userAdded": false
@@ -175,6 +183,7 @@
"alwaysLoaded": false,
"file": "p1_walk07.png",
"kind": "image",
"metadata": "",
"name": "p1_walk07.png",
"smoothed": true,
"userAdded": false
@@ -183,6 +192,7 @@
"alwaysLoaded": false,
"file": "p1_walk08.png",
"kind": "image",
"metadata": "",
"name": "p1_walk08.png",
"smoothed": true,
"userAdded": false
@@ -191,6 +201,7 @@
"alwaysLoaded": false,
"file": "p1_walk09.png",
"kind": "image",
"metadata": "",
"name": "p1_walk09.png",
"smoothed": true,
"userAdded": false
@@ -199,6 +210,7 @@
"alwaysLoaded": false,
"file": "p1_walk10.png",
"kind": "image",
"metadata": "",
"name": "p1_walk10.png",
"smoothed": true,
"userAdded": false
@@ -207,6 +219,7 @@
"alwaysLoaded": false,
"file": "p1_walk11.png",
"kind": "image",
"metadata": "",
"name": "p1_walk11.png",
"smoothed": true,
"userAdded": false
@@ -215,6 +228,7 @@
"alwaysLoaded": false,
"file": "brickWall.png",
"kind": "image",
"metadata": "",
"name": "brickWall.png",
"smoothed": true,
"userAdded": false
@@ -223,6 +237,7 @@
"alwaysLoaded": false,
"file": "bridge.png",
"kind": "image",
"metadata": "",
"name": "bridge.png",
"smoothed": true,
"userAdded": false
@@ -231,6 +246,7 @@
"alwaysLoaded": false,
"file": "grassHalfMid.png",
"kind": "image",
"metadata": "",
"name": "grassHalfMid.png",
"smoothed": true,
"userAdded": true
@@ -239,6 +255,7 @@
"alwaysLoaded": false,
"file": "castleCenter.png",
"kind": "image",
"metadata": "",
"name": "castleCenter.png",
"smoothed": true,
"userAdded": true
@@ -247,6 +264,7 @@
"alwaysLoaded": false,
"file": "bridgeLogs.png",
"kind": "image",
"metadata": "",
"name": "bridgeLogs.png",
"smoothed": true,
"userAdded": true
@@ -255,6 +273,7 @@
"alwaysLoaded": false,
"file": "Left.png",
"kind": "image",
"metadata": "",
"name": "Left.png",
"smoothed": true,
"userAdded": false
@@ -263,6 +282,7 @@
"alwaysLoaded": false,
"file": "Right.png",
"kind": "image",
"metadata": "",
"name": "Right.png",
"smoothed": true,
"userAdded": false
@@ -271,6 +291,7 @@
"alwaysLoaded": false,
"file": "ladder_mid.png",
"kind": "image",
"metadata": "",
"name": "ladder_mid.png",
"smoothed": true,
"userAdded": true
@@ -279,6 +300,7 @@
"alwaysLoaded": false,
"file": "Grass.png",
"kind": "image",
"metadata": "",
"name": "grass.png",
"smoothed": true,
"userAdded": true
@@ -287,6 +309,7 @@
"alwaysLoaded": false,
"file": "PlayerArea.png",
"kind": "image",
"metadata": "",
"name": "PlayerArea.png",
"smoothed": true,
"userAdded": false
@@ -295,6 +318,7 @@
"alwaysLoaded": false,
"file": "slimeWalk1.png",
"kind": "image",
"metadata": "",
"name": "slimeWalk1.png",
"smoothed": true,
"userAdded": false
@@ -303,6 +327,7 @@
"alwaysLoaded": false,
"file": "slimeWalk2.png",
"kind": "image",
"metadata": "",
"name": "slimeWalk2.png",
"smoothed": true,
"userAdded": false
@@ -311,6 +336,7 @@
"alwaysLoaded": false,
"file": "slimeDead.png",
"kind": "image",
"metadata": "",
"name": "slimeDead.png",
"smoothed": true,
"userAdded": false
@@ -319,6 +345,7 @@
"alwaysLoaded": false,
"file": "flyFly1.png",
"kind": "image",
"metadata": "",
"name": "flyFly1.png",
"smoothed": true,
"userAdded": false
@@ -327,6 +354,7 @@
"alwaysLoaded": false,
"file": "flyFly2.png",
"kind": "image",
"metadata": "",
"name": "flyFly2.png",
"smoothed": true,
"userAdded": false
@@ -335,6 +363,7 @@
"alwaysLoaded": false,
"file": "flyDead.png",
"kind": "image",
"metadata": "",
"name": "flyDead.png",
"smoothed": true,
"userAdded": false
@@ -343,6 +372,7 @@
"alwaysLoaded": false,
"file": "cloud1.png",
"kind": "image",
"metadata": "",
"name": "cloud1.png",
"smoothed": false,
"userAdded": false
@@ -351,6 +381,7 @@
"alwaysLoaded": false,
"file": "cloud2.png",
"kind": "image",
"metadata": "",
"name": "cloud2.png",
"smoothed": false,
"userAdded": false
@@ -359,6 +390,7 @@
"alwaysLoaded": false,
"file": "cloud3.png",
"kind": "image",
"metadata": "",
"name": "cloud3.png",
"smoothed": false,
"userAdded": false
@@ -367,6 +399,7 @@
"alwaysLoaded": false,
"file": "bush.png",
"kind": "image",
"metadata": "",
"name": "bush.png",
"smoothed": true,
"userAdded": false
@@ -375,6 +408,7 @@
"alwaysLoaded": false,
"file": "cactus.png",
"kind": "image",
"metadata": "",
"name": "cactus.png",
"smoothed": true,
"userAdded": false
@@ -383,6 +417,7 @@
"alwaysLoaded": false,
"file": "plant.png",
"kind": "image",
"metadata": "",
"name": "plant.png",
"smoothed": true,
"userAdded": false
@@ -391,6 +426,7 @@
"alwaysLoaded": false,
"file": "coinGold.png",
"kind": "image",
"metadata": "",
"name": "coinGold.png",
"smoothed": true,
"userAdded": true
@@ -399,6 +435,7 @@
"alwaysLoaded": false,
"file": "shadedDark06.png",
"kind": "image",
"metadata": "",
"name": "shadedDark06.png",
"smoothed": true,
"userAdded": false
@@ -407,6 +444,7 @@
"alwaysLoaded": false,
"file": "shadedDark05.png",
"kind": "image",
"metadata": "",
"name": "shadedDark05.png",
"smoothed": true,
"userAdded": false
@@ -415,6 +453,7 @@
"alwaysLoaded": false,
"file": "shadedDark45.png",
"kind": "image",
"metadata": "",
"name": "shadedDark45.png",
"smoothed": true,
"userAdded": false
@@ -423,6 +462,7 @@
"alwaysLoaded": false,
"file": "shadedDark09.png",
"kind": "image",
"metadata": "",
"name": "shadedDark09.png",
"smoothed": true,
"userAdded": false
@@ -3631,7 +3671,7 @@
"textG": 0,
"textR": 0
},
"comment": "Platform should rotate =====>",
"comment": "Platform should rotate, platforms should be colored",
"comment2": ""
},
{
@@ -3684,7 +3724,9 @@
},
"parameters": [
"",
"Platform"
"Platform",
"\"0;255;100\"",
""
],
"subInstructions": []
}
@@ -5227,8 +5269,8 @@
"version": "",
"eventsFunctions": [
{
"description": "t",
"fullName": "t",
"description": "Rotate the given object",
"fullName": "Rotate function",
"functionType": "Action",
"name": "RotatePlease",
"sentence": "Rotate the _PARAM1_ at speed _PARAM2_deg/sec",
@@ -5256,15 +5298,6 @@
}
],
"parameters": [
{
"codeOnly": true,
"defaultValue": "",
"description": "",
"name": "runtimeScene",
"optional": false,
"supplementaryInformation": "",
"type": "currentScene"
},
{
"codeOnly": false,
"defaultValue": "",
@@ -5312,17 +5345,7 @@
"events": []
}
],
"parameters": [
{
"codeOnly": true,
"defaultValue": "",
"description": "",
"name": "runtimeScene",
"optional": false,
"supplementaryInformation": "",
"type": "currentScene"
}
]
"parameters": []
},
{
"description": "Return a speed of rotation",
@@ -5351,24 +5374,14 @@
"events": []
}
],
"parameters": [
{
"codeOnly": true,
"defaultValue": "",
"description": "",
"name": "runtimeScene",
"optional": false,
"supplementaryInformation": "",
"type": "currentScene"
}
]
"parameters": []
},
{
"description": "Color the given sprite objects",
"fullName": "Color sprites",
"functionType": "Action",
"name": "ColorThings",
"sentence": "Color _PARAM1_",
"sentence": "Color _PARAM1_ with color: _PARAM2_",
"events": [
{
"disabled": false,
@@ -5383,7 +5396,7 @@
},
"parameters": [
"ObjectToColor",
"\"0;255;50\""
"GetArgumentAsString(\"Color\")"
],
"subInstructions": []
}
@@ -5392,15 +5405,6 @@
}
],
"parameters": [
{
"codeOnly": true,
"defaultValue": "",
"description": "",
"name": "runtimeScene",
"optional": false,
"supplementaryInformation": "",
"type": "currentScene"
},
{
"codeOnly": false,
"defaultValue": "",
@@ -5409,6 +5413,15 @@
"optional": false,
"supplementaryInformation": "Sprite",
"type": "objectList"
},
{
"codeOnly": false,
"defaultValue": "",
"description": "Color string",
"name": "Color",
"optional": false,
"supplementaryInformation": "",
"type": "string"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

View File

@@ -0,0 +1,779 @@
{
"firstLayout": "",
"gdVersion": {
"build": 97,
"major": 4,
"minor": 0,
"revision": 0
},
"properties": {
"folderProject": false,
"linuxExecutableFilename": "",
"macExecutableFilename": "",
"orientation": "landscape",
"packageName": "com.example.gamename",
"projectFile": "/Users/florianrival/Projects/F/GD/GDJS/tests/games/rotated-objects-hitboxes/game.json",
"sizeOnStartupMode": "adaptWidth",
"useExternalSourceFiles": false,
"version": "1.0.0",
"winExecutableFilename": "",
"winExecutableIconFile": "",
"name": "Project",
"author": "",
"windowWidth": 800,
"windowHeight": 600,
"latestCompilationDirectory": "",
"maxFPS": 60,
"minFPS": 10,
"verticalSync": false,
"platformSpecificAssets": {},
"loadingScreen": {
"showGDevelopSplash": false
},
"extensions": [
{
"name": "BuiltinObject"
},
{
"name": "BuiltinAudio"
},
{
"name": "BuiltinVariables"
},
{
"name": "BuiltinTime"
},
{
"name": "BuiltinMouse"
},
{
"name": "BuiltinKeyboard"
},
{
"name": "BuiltinJoystick"
},
{
"name": "BuiltinCamera"
},
{
"name": "BuiltinWindow"
},
{
"name": "BuiltinFile"
},
{
"name": "BuiltinNetwork"
},
{
"name": "BuiltinScene"
},
{
"name": "BuiltinAdvanced"
},
{
"name": "Sprite"
},
{
"name": "BuiltinCommonInstructions"
},
{
"name": "BuiltinCommonConversions"
},
{
"name": "BuiltinStringInstructions"
},
{
"name": "BuiltinMathematicalTools"
},
{
"name": "BuiltinExternalLayouts"
}
],
"platforms": [
{
"name": "GDevelop JS platform"
}
],
"currentPlatform": "GDevelop JS platform"
},
"resources": {
"resources": [
{
"alwaysLoaded": false,
"file": "NewObject-1.png",
"kind": "image",
"metadata": "",
"name": "NewObject-1.png",
"smoothed": true,
"userAdded": true
},
{
"alwaysLoaded": false,
"file": "NewObject2-1.png",
"kind": "image",
"metadata": "",
"name": "NewObject2-1.png",
"smoothed": true,
"userAdded": false
},
{
"alwaysLoaded": false,
"file": "Image-1.png",
"kind": "image",
"metadata": "",
"name": "Image-1.png",
"smoothed": true,
"userAdded": true
},
{
"alwaysLoaded": false,
"file": "NewObject2-1-0.png",
"kind": "image",
"metadata": "",
"name": "NewObject2-1-0.png",
"smoothed": true,
"userAdded": false
},
{
"alwaysLoaded": false,
"file": "NewObject2-2.png",
"kind": "image",
"metadata": "",
"name": "NewObject2-2.png",
"smoothed": true,
"userAdded": false
}
],
"resourceFolders": []
},
"objects": [],
"objectsGroups": [],
"variables": [],
"layouts": [
{
"b": 209,
"disableInputWhenNotFocused": true,
"mangledName": "NewScene",
"name": "NewScene",
"oglFOV": 90,
"oglZFar": 500,
"oglZNear": 1,
"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
},
"objectsGroups": [],
"variables": [],
"instances": [
{
"angle": 45,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "DefaultSprite",
"width": 0,
"x": 39,
"y": 32,
"zOrder": 1,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 45,
"customSize": true,
"height": 66,
"layer": "",
"locked": false,
"name": "CenterTopLeft",
"width": 107,
"x": 80,
"y": 219,
"zOrder": 2,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 32.0054,
"customSize": true,
"height": 24,
"layer": "",
"locked": false,
"name": "TextObject",
"width": 136,
"x": 346,
"y": 51,
"zOrder": 3,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 16.9908,
"customSize": true,
"height": 32,
"layer": "",
"locked": false,
"name": "TiledSprite",
"width": 349,
"x": 452,
"y": 61,
"zOrder": 4,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 0,
"customSize": true,
"height": 48,
"layer": "",
"locked": false,
"name": "CenterTopLeft",
"width": 94,
"x": 177,
"y": 219,
"zOrder": 2,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 44.3415,
"customSize": true,
"height": 72,
"layer": "",
"locked": false,
"name": "CenterBottomRight",
"width": 104,
"x": 575,
"y": 288,
"zOrder": 5,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 0,
"customSize": true,
"height": 63,
"layer": "",
"locked": false,
"name": "CenterBottomRight",
"width": 101,
"x": 433,
"y": 254,
"zOrder": 5,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 38.4969,
"customSize": true,
"height": 63,
"layer": "",
"locked": false,
"name": "OriginMiddleCenterBottomRight",
"width": 90,
"x": 251,
"y": 518,
"zOrder": 6,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 0,
"customSize": true,
"height": 59,
"layer": "",
"locked": false,
"name": "OriginMiddleCenterBottomRight",
"width": 101,
"x": 96,
"y": 473,
"zOrder": 6,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 0,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "CenterTopLeft",
"width": 0,
"x": 177,
"y": 171,
"zOrder": 2,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 45,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "CenterTopLeft",
"width": 0,
"x": 93,
"y": 153,
"zOrder": 2,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 0,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "CenterBottomRight",
"width": 0,
"x": 434,
"y": 193,
"zOrder": 5,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 44.3415,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "CenterBottomRight",
"width": 0,
"x": 552,
"y": 193,
"zOrder": 5,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 0,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "OriginMiddleCenterBottomRight",
"width": 0,
"x": 77,
"y": 414,
"zOrder": 6,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 38.4969,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "OriginMiddleCenterBottomRight",
"width": 0,
"x": 215,
"y": 424,
"zOrder": 6,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 45,
"customSize": true,
"height": 71,
"layer": "",
"locked": false,
"name": "CustomHitboxesOriginMiddleCenterBottomRight",
"width": 105,
"x": 647,
"y": 566,
"zOrder": 7,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 0,
"customSize": true,
"height": 65,
"layer": "",
"locked": false,
"name": "CustomHitboxesOriginMiddleCenterBottomRight",
"width": 112,
"x": 515,
"y": 538,
"zOrder": 7,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 45,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "CustomHitboxesOriginMiddleCenterBottomRight",
"width": 0,
"x": 632,
"y": 451,
"zOrder": 7,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 0,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "CustomHitboxesOriginMiddleCenterBottomRight",
"width": 0,
"x": 510.005,
"y": 450.563,
"zOrder": 7,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 32.0054,
"customSize": false,
"height": 0,
"layer": "",
"locked": false,
"name": "TextObject",
"width": 0,
"x": 141,
"y": 52,
"zOrder": 3,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
},
{
"angle": 0,
"customSize": true,
"height": 24,
"layer": "",
"locked": false,
"name": "TextObject",
"width": 127,
"x": 323,
"y": 530,
"zOrder": 8,
"numberProperties": [],
"stringProperties": [],
"initialVariables": []
}
],
"objects": [
{
"name": "DefaultSprite",
"type": "Sprite",
"updateIfNotVisible": false,
"variables": [],
"behaviors": [],
"animations": [
{
"name": "NewObject",
"useMultipleDirections": false,
"directions": [
{
"looping": false,
"timeBetweenFrames": 1,
"sprites": [
{
"hasCustomCollisionMask": false,
"image": "NewObject-1.png",
"points": [],
"originPoint": {
"name": "origine",
"x": 0,
"y": 0
},
"centerPoint": {
"automatic": true,
"name": "centre",
"x": 0,
"y": 0
},
"customCollisionMask": []
}
]
}
]
}
]
},
{
"name": "CenterBottomRight",
"type": "Sprite",
"updateIfNotVisible": false,
"variables": [],
"behaviors": [],
"animations": [
{
"name": "NewObject2",
"useMultipleDirections": false,
"directions": [
{
"looping": false,
"timeBetweenFrames": 1,
"sprites": [
{
"hasCustomCollisionMask": false,
"image": "NewObject2-1.png",
"points": [],
"originPoint": {
"name": "origine",
"x": 0,
"y": 0
},
"centerPoint": {
"automatic": false,
"name": "centre",
"x": 64,
"y": 31
},
"customCollisionMask": []
}
]
}
]
}
]
},
{
"name": "CenterTopLeft",
"type": "Sprite",
"updateIfNotVisible": false,
"variables": [],
"behaviors": [],
"animations": [
{
"name": "NewObject2",
"useMultipleDirections": false,
"directions": [
{
"looping": false,
"timeBetweenFrames": 1,
"sprites": [
{
"hasCustomCollisionMask": false,
"image": "NewObject2-1.png",
"points": [],
"originPoint": {
"name": "origine",
"x": 0,
"y": 0
},
"centerPoint": {
"automatic": false,
"name": "centre",
"x": 0,
"y": 0
},
"customCollisionMask": []
}
]
}
]
}
]
},
{
"bold": false,
"italic": false,
"name": "TextObject",
"smoothed": true,
"type": "TextObject::Text",
"underlined": false,
"variables": [],
"behaviors": [],
"string": "Text blabl balbalbal",
"font": "",
"characterSize": 20,
"color": {
"b": 255,
"g": 255,
"r": 255
}
},
{
"name": "CustomHitboxesOriginMiddleCenterBottomRight",
"type": "Sprite",
"updateIfNotVisible": false,
"variables": [],
"behaviors": [],
"animations": [
{
"name": "NewObject2",
"useMultipleDirections": false,
"directions": [
{
"looping": false,
"timeBetweenFrames": 1,
"sprites": [
{
"hasCustomCollisionMask": true,
"image": "NewObject2-2.png",
"points": [],
"originPoint": {
"name": "origine",
"x": 32,
"y": 16
},
"centerPoint": {
"automatic": false,
"name": "centre",
"x": 64,
"y": 31
},
"customCollisionMask": [
[
{
"x": 12.5,
"y": 1
},
{
"x": 41.5,
"y": 2
},
{
"x": 55.5,
"y": 31
},
{
"x": 24.5,
"y": 30
}
]
]
}
]
}
]
}
]
},
{
"name": "OriginMiddleCenterBottomRight",
"type": "Sprite",
"updateIfNotVisible": false,
"variables": [],
"behaviors": [],
"animations": [
{
"name": "NewObject2",
"useMultipleDirections": false,
"directions": [
{
"looping": false,
"timeBetweenFrames": 1,
"sprites": [
{
"hasCustomCollisionMask": false,
"image": "NewObject2-1.png",
"points": [],
"originPoint": {
"name": "origine",
"x": 32,
"y": 16
},
"centerPoint": {
"automatic": false,
"name": "centre",
"x": 64,
"y": 31
},
"customCollisionMask": []
}
]
}
]
}
]
},
{
"height": 32,
"name": "TiledSprite",
"texture": "Image-1.png",
"type": "TiledSpriteObject::TiledSprite",
"width": 32,
"variables": [],
"behaviors": []
}
],
"events": [
{
"disabled": false,
"folded": false,
"type": "BuiltinCommonInstructions::Comment",
"color": {
"b": 109,
"g": 230,
"r": 255,
"textB": 0,
"textG": 0,
"textR": 0
},
"comment": "Verify the hitboxes of the objects, in particular the rotated ones and the ones with a center point and/or origin that is not at the default position, and/or that are scaled",
"comment2": ""
}
],
"layers": [
{
"name": "",
"visibility": true,
"cameras": [
{
"defaultSize": true,
"defaultViewport": true,
"height": 0,
"viewportBottom": 1,
"viewportLeft": 0,
"viewportRight": 1,
"viewportTop": 0,
"width": 0
}
],
"effects": []
}
],
"behaviorsSharedData": []
}
],
"externalEvents": [],
"eventsFunctionsExtensions": [],
"externalLayouts": [],
"externalSourceFiles": []
}

View File

@@ -54,7 +54,11 @@ module.exports = function(config) {
//All tests files:
'./tests/init.js',
'../../Extensions/**/tests/**.spec.js',
'./tests/**/*.js'
'./tests/**/*.js',
//All benchmark files:
'./benchmarks/init.js',
'./benchmarks/**/*.js'
]
});
};

16
GDJS/tests/tests/force.js Normal file
View File

@@ -0,0 +1,16 @@
/**
* Common tests for gdjs game engine.
* See README.md for more information.
*/
describe('gdjs.Force', function() {
it('can set angle and length', function(){
var layer = new gdjs.Force();
layer.setAngle(-45);
layer.setLength(200);
expect(layer.getX()).to.be.within(141.42, 141.422);
expect(layer.getY()).to.be.within(-141.422, -141.42);
});
});

29
GDJS/tests/tests/layer.js Normal file
View File

@@ -0,0 +1,29 @@
/**
* Common tests for gdjs game engine.
* See README.md for more information.
*/
describe('gdjs.Layer', function() {
var runtimeGame = new gdjs.RuntimeGame({variables: [], properties: {windowWidth: 800, windowHeight: 600}});
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
it('can convert coordinates', function(){
var layer = new gdjs.Layer({name: 'My layer', visibility: true, effects:[]}, runtimeScene)
layer.setCameraX(100, 0);
layer.setCameraY(200, 0);
layer.setCameraRotation(90, 0);
expect(layer.convertCoords(350, 450, 0)[0]).to.be.within(-50.001, -49.99999);
expect(layer.convertCoords(350, 450, 0)[1]).to.be.within(149.9999, 150.001);
});
it('can convert inverse coordinates', function(){
var layer = new gdjs.Layer({name: 'My layer', visibility: true, effects:[]}, runtimeScene)
layer.setCameraX(100, 0);
layer.setCameraY(200, 0);
layer.setCameraRotation(90, 0);
expect(layer.convertInverseCoords(350, 450, 0)[0]).to.be.within(649.999, 650.001);
expect(layer.convertInverseCoords(350, 450, 0)[1]).to.be.within(49.9999, 50.001);
});
});

View File

@@ -3,7 +3,7 @@
* See README.md for more information.
*/
describe('gdjs.runtimeObject', function() {
describe('gdjs.RuntimeObject', function() {
var runtimeScene = new gdjs.RuntimeScene(null);
it('should compute distances properly', function(){
@@ -12,4 +12,42 @@ describe('gdjs.runtimeObject', function() {
expect(object.getSqDistanceTo(-110, 200)).to.be(48025);
});
it('should compute AABB properly', function(){
var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []});
object.getWidth = function() { return 10; };
object.getHeight = function() { return 20; };
expect(object.getAABB()).to.eql({
min: [0,0],
max: [10,20]
});
object.setPosition(15, 20);
expect(object.getAABB()).to.eql({
min: [15,20],
max: [25,40]
});
object.setAngle(90);
expect(object.getAABB()).to.eql({
min: [10,25],
max: [30,35]
});
object.setAngle(0);
object.getCenterX = function() { return 0 };
object.getCenterY = function() { return 0 };
expect(object.getAABB()).to.eql({
min: [15,20],
max: [25,40]
});
object.setPosition(15, 20);
object.setAngle(90);
expect(object.getAABB()).to.eql({
min: [-5,20],
max: [15,30]
});
});
});

View File

@@ -3,6 +3,64 @@
* See README.md for more information.
*/
const makeSpriteRuntimeObjectWithCustomHitBox = (runtimeScene) => new gdjs.SpriteRuntimeObject(runtimeScene, {
"name": "obj1",
"type": "Sprite",
"updateIfNotVisible": false,
"variables": [],
"behaviors": [],
"animations": [
{
"name": "NewObject2",
"useMultipleDirections": false,
"directions": [
{
"looping": false,
"timeBetweenFrames": 1,
"sprites": [
{
"hasCustomCollisionMask": true,
"image": "NewObject2-2.png",
"points": [],
"originPoint": {
"name": "origine",
"x": 32,
"y": 16
},
"centerPoint": {
"automatic": false,
"name": "centre",
"x": 64,
"y": 31
},
"customCollisionMask": [
[
{
"x": 12.5,
"y": 1
},
{
"x": 41.5,
"y": 2
},
{
"x": 55.5,
"y": 31
},
{
"x": 24.5,
"y": 30
}
]
]
}
]
}
]
}
]
});
describe('gdjs.SpriteRuntimeObject', function() {
var runtimeGame = new gdjs.RuntimeGame({variables: [], properties: {windowWidth: 800, windowHeight: 600}});
var runtimeScene = new gdjs.RuntimeScene(runtimeGame);
@@ -61,4 +119,22 @@ describe('gdjs.SpriteRuntimeObject', function() {
expect(object.getAnimationName()).to.be('firstAnimation');
});
});
it('should properly compute hitboxes', function(){
// Create an object with a custom hitbox
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
// Check the hitboxes without any rotation (only the non default origin
// which is at 32;16 is to be used).
expect(object.getHitBoxes()[0].vertices[0]).to.eql([12.5 - 32, 1 - 16]);
expect(object.getHitBoxes()[0].vertices[1]).to.eql([41.5 - 32, 2 - 16]);
expect(object.getHitBoxes()[0].vertices[2]).to.eql([55.5 - 32, 31 - 16]);
expect(object.getHitBoxes()[0].vertices[3]).to.eql([24.5 - 32, 30 - 16]);
object.setAngle(90);
expect(object.getHitBoxes()[0].vertices[0][0]).to.be.within(61.9999, 62.0001);
expect(object.getHitBoxes()[0].vertices[0][1]).to.be.within(-36.5001, -36.49999);
expect(object.getHitBoxes()[0].vertices[2][0]).to.be.within(31.999, 32.0001);
expect(object.getHitBoxes()[0].vertices[2][1]).to.be.within(6.4999, 6.5001);
});
});

View File

@@ -23,7 +23,7 @@ public/libGD.js
public/external/Piskel/piskel-editor
public/external/Piskel/piskel-editor.zip
public/external/monaco-editor-min
public/external/jsfx/loov-jsfx/
public/external/jfxr/jfxr-editor/
# Resources
public/res

View File

@@ -29,7 +29,6 @@
"i18next": "^10.0.3",
"keen-tracking": "1.1.3",
"lodash": "4.17.4",
"loov-jsfx": "1.2.0",
"material-ui": "0.20",
"material-ui-search-bar": "0.4.1",
"node-require-function": "^1.2.0",
@@ -61,7 +60,7 @@
},
"scripts": {
"postinstall": "npm run import-resources",
"import-resources": "cd scripts && node import-libGD.js && node import-res-folder.js && node import-GDJS-Runtime.js && node import-piskel-editor.js && node import-monaco-editor.js && node import-jsfx-editor.js",
"import-resources": "cd scripts && node import-libGD.js && node import-res-folder.js && node import-GDJS-Runtime.js && node import-monaco-editor.js && node import-zipped-editor.js piskel 5.0.0-beta34 && node import-zipped-editor.js jfxr 5.0.0-beta55",
"start": "npm run import-resources && react-scripts start",
"build": "npm run import-resources && react-scripts build",
"format": "prettier --write \"src/**/*.js\"",

View File

@@ -0,0 +1,7 @@
This folder contains sources to embed Jfxr editor (https://github.com/ttencate/jfxr) so that it can
be used directly from GDevelop to edit sound effects.
Jfxr sources are downloaded by `import-zipped-editor.js` script. They are raw, unchanged sources
of the Jfxr editor build. Sources will be stored in `Jfxr-editor` folder.
See `jfxr-main.js` for the code running the editor.
See `ModalWindow.js` and `LocalJfxrBridge.js` files for the bridge that open the Window and pass data from GDevelop to Jfxr.

View File

@@ -0,0 +1,14 @@
<html>
<head>
<title>GDevelop Jfxr Editor</title>
<link rel="stylesheet" type="text/css" href="jfxr-style.css">
</head>
<body>
<div id='path-editor-header'></div>
<iframe id='jfxr-frame'></iframe>
<script type="module" src="gdide://external/jfxr/jfxr-main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,88 @@
import { createPathEditorHeader } from '../utils/path-editor.js'
const electron = require('electron')
const ipcRenderer = electron.ipcRenderer
const fs = require('fs')
const remote = electron.remote
let jfxr = null
const closeWindow = () => {
remote.getCurrentWindow().close()
}
const loadMetaData = metaData => {
if ('jfxr' in metaData) {
jfxr.getSound().parse(metaData.jfxr.data);
} else {
jfxr.applyPreset(jfxr.presets[1])
}
}
const saveSoundEffect = pathEditor => {
const metadata = {
data: jfxr.getSound().serialize(),
name: pathEditor.state.name
}
jfxr.synth.run().then(data => {
var blob = new Blob([data.toWavBytes()], {
type: 'audio/wav'
})
var fileReader = new FileReader()
fileReader.onload = function () {
fs.writeFileSync(pathEditor.state.fullPath, Buffer(new Uint8Array(this.result)))
ipcRenderer.send('jfxr-changes-saved', pathEditor.state.fullPath, metadata)
closeWindow()
}
fileReader.readAsArrayBuffer(blob)
})
}
// Gain access to JFXR controller by using the signal that the JFXR author kindly provided.
// It gets fired upon loading of jfxr in the iframe element
const editorFrameEl = document.getElementById('jfxr-frame')
window.addEventListener('jfxrReady', (e) => {
jfxr = e.mainCtrl;
ipcRenderer.send('jfxr-ready');
});
// Trigger the load of Jfxr manually, to ensure the event listener "jfxrReady" is registered already
editorFrameEl.src = 'jfxr-editor/index.html'
// Called to load a sound. Should be called after the window is fully loaded.
ipcRenderer.on('jfxr-open', (event, receivedOptions) => {
loadMetaData(receivedOptions.metadata);
// Load a custom save file(s) header
const pathEditorHeaderDiv = document.getElementById('path-editor-header');
const headerStyle = {
saveFolderLabel: 'float: left;margin-left: 2px; font-size:15px;margin-top: 10px;color:aqua',
nameInput: 'font-family:"Courier New";height:27px;width:90px;float:left;margin-left: 2px;padding:4px;margin-top: 4px;font-size:15px;border: 2px solid #e5cd50;border-radius: 3px;background-color:black; color: #e5cd50;',
saveButton: 'float:right;margin-left:2px;margin-right:4px;border: 2px solid white;border-radius: 1px;margin-top: 5px;background-color:white;',
cancelButton: 'float:right;margin-right:2px;border: 2px solid white;border-radius: 1px;margin-top: 5px;background-color:white;',
setFolderButton: 'float:right;margin-left:2px;margin-right:4px;border: 2px solid white;border-radius: 1px;margin-top: 5px;background-color:white;',
fileExistsLabel: 'height:27px;color:blue;float: left;margin-left: 2px;margin-top: 10px; font-size:15px;'
};
createPathEditorHeader({
parentElement: pathEditorHeaderDiv,
editorContentDocument: document,
onSaveToGd: saveSoundEffect,
onCancelChanges: closeWindow,
projectPath: receivedOptions.projectPath,
initialResourcePath: receivedOptions.resourcePath,
extension: '.wav',
headerStyle
});
// Disable google analytics from collecting personal information
editorFrameEl.contentWindow.ga('set', 'allowAdFeatures', false);
// Alter the interface of the external editor
const editorContentDocument = editorFrameEl.contentDocument;
editorContentDocument.getElementsByClassName('github')[0].remove();
// Disable inside iframe links - they break the embedding
editorContentDocument.getElementsByClassName('titlepane column-left')[0].childNodes[0].onclick = () => {
return false
};
editorContentDocument.getElementsByClassName('titlepane column-left')[0].childNodes[1].onclick = () => {
return false
};
})

View File

@@ -7,10 +7,10 @@ body {
font-family: Helvetica;
margin: 0;
overflow-y: hidden;
background-color: white;
background-color: black;
}
#jsfx-frame {
#jfxr-frame {
width: 100%;
height: 100%;
border: none;

View File

@@ -1,7 +0,0 @@
This folder contains sources to embed Jsfx editor (https://github.com/loov/jsfx/) so that it can
be used directly from GDevelop to create sound effects.
Jsfx sources are copied by `import-jsfx-editor.js` script. They are raw, unchanged sources
of the Jsfx editor. Sources will be stored in `loov-jsfx` folder.
See `jsfx-main.js` for the code running the editor.
See `ModalWindow.js` and `LocalJsfxBridge.js` files for the bridge that open the Window and pass data from GDevelop to Jsfx.

View File

@@ -1,14 +0,0 @@
<html>
<head>
<title>GDevelop Jsfx Editor</title>
<link rel="stylesheet" type="text/css" href="jsfx-style.css">
</head>
<body>
<div id='path-editor-header'></div>
<iframe id='jsfx-frame' src="loov-jsfx/index.html"></iframe>
<script type="module" src="gdide://external/jsfx/jsfx-main.js"></script>
</body>
</html>

View File

@@ -1,111 +0,0 @@
import { createPathEditorHeader } from '../utils/path-editor.js';
const electron = require('electron');
const ipcRenderer = electron.ipcRenderer;
const fs = require('fs');
const remote = electron.remote;
let editorContentDocument,
jsfx = null;
const loadMetaData = metaData => {
jsfx.CurrentParams = metaData;
jsfx.UpdateCurrentView();
jsfx.PlayCurrent();
};
const closeWindow = () => {
remote.getCurrentWindow().close();
};
const saveSoundEffect = pathEditor => {
jsfx.UpdateDownloadLink(); //Update base64 data
const rawData = editorContentDocument
.getElementById('download')
.href.replace(/^data:audio\/wav;base64,/, '');
fs.writeFile(pathEditor.state.fullPath, rawData, 'base64', err => {
ipcRenderer.send(
'jsfx-changes-saved',
pathEditor.state.fullPath,
jsfx.CurrentParams
);
closeWindow();
});
};
// Wait for the window to be fully initialized before sending the
// ready event. Don't use DOMContentLoaded as it was observed to be fired
// even if jsfx DOM/scripts are not yet loaded.
window.addEventListener('load', function() {
ipcRenderer.send('jsfx-ready');
});
// Called to load a sound. Should be called after the window is fully loaded.
ipcRenderer.on('jsfx-open', (event, receivedOptions) => {
const editorFrameEl = document.getElementById('jsfx-frame');
jsfx = editorFrameEl.contentWindow;
editorContentDocument = editorFrameEl.contentDocument;
const presetsPanel = editorContentDocument.getElementById('presets');
// Load metadata, if it exists
if ('jsfx' in receivedOptions.metadata) {
loadMetaData(receivedOptions.metadata.jsfx);
} else {
// If not, simulate a click on the 'Lucky' button to create a random sound effect
const generateRandomSoundEffectButton = presetsPanel.childNodes[11];
generateRandomSoundEffectButton.click();
}
// load a custom header
const pathEditorHeaderDiv = document.getElementById('path-editor-header');
const headerStyle = {
saveFolderLabel:
'height:27px;color:SlateGrey;float: left;margin-left: 2px;margin-top: 10px; font-size:15px;',
nameInput:
'font-family:"Courier New";height:27px;width:90px;color:SlateGrey;float:left;margin-left: 2px;padding:4px;margin-top: 4px;font-size:15px;border: 2px solid #e5cd50;border-radius: 3px; ',
fileExistsLabel:
'height:27px;color:blue;float: left;margin-left: 2px;margin-top: 10px; font-size:15px;',
saveButton:
'height:27px;float:right;margin-left:2px;margin-right:4px;border: 2px solid DeepSkyBlue;border-radius: 1px;margin-top: 5px;background-color:white;',
cancelButton:
'height:27px;float:right;margin-right:2px;border: 2px solid DeepSkyBlue;border-radius: 1px;margin-top: 5px;background-color:white;',
setFolderButton:
'height:27px;float:right;margin-left:2px;margin-right:4px;border: 2px solid DeepSkyBlue;border-radius: 1px;margin-top: 5px;background-color:white;',
};
createPathEditorHeader({
parentElement: pathEditorHeaderDiv,
editorContentDocument: document,
onSaveToGd: saveSoundEffect,
onCancelChanges: closeWindow,
projectPath: receivedOptions.projectPath,
initialResourcePath: receivedOptions.resourcePath,
extension: '.wav',
headerStyle,
});
// alter the interface of the external editor
editorContentDocument.getElementById('jsfx').firstChild.style = 'float:top';
const defaultTitle = editorContentDocument.getElementsByClassName('title')[0]
.firstChild;
defaultTitle.remove();
presetsPanel.className = 'description';
presetsPanel.style = 'float:left;';
const generatorsTitle = editorContentDocument.getElementsByClassName(
'panel-title'
)[1].firstChild;
generatorsTitle.data = 'Create a Random Sound Effect:';
const description = editorContentDocument.getElementsByClassName(
'description'
)[0];
description.remove();
const libraryPanel = editorContentDocument.getElementsByClassName(
'panel wide'
)[0];
libraryPanel.style = 'visibility:hidden;height:0px;width:0px';
const defaultButtons = editorContentDocument.getElementById('control');
defaultButtons.style = 'visibility:hidden;height:0px;width:0px';
});

View File

@@ -1,7 +1,7 @@
This folder contains sources to embed Piskel editor (https://www.piskelapp.com/) so that it can
be used directly from GDevelop to edit images.
Piskel sources are downloaded by `import-piskel-editor.js` script. They are raw, unchanged sources
of the Piskel editor. Sources will be stored in `piskel-editor` folder.
Piskel sources are downloaded by `import-zipped-editor.js` script. They are raw, unchanged sources
of the Piskel editor build. Sources will be stored in `piskel-editor` folder.
See `piskel-main.js` for the code running the editor.
See `ModalWindow.js` and `LocalPiskelBridge.js` files for the bridge that open the Window and pass data from GDevelop to Piskel.

View File

@@ -1,9 +1,8 @@
const electron = require('electron');
const fs = require('fs');
const path = require('path');
const remote = electron.remote;
const {
dialog
} = remote;
const { dialog } = remote;
export const createPathEditorHeader = ({
parentElement,
@@ -15,25 +14,21 @@ export const createPathEditorHeader = ({
extension,
headerStyle,
}) => {
if (fs.lstatSync(initialResourcePath).isDirectory()) {
initialResourcePath = initialResourcePath + '/NewFile' + extension;
if (fs.existsSync(initialResourcePath)) {
if (fs.lstatSync(initialResourcePath).isDirectory()) {
initialResourcePath = initialResourcePath + '/NewFile' + extension;
}
} else {
initialResourcePath = projectPath + '/NewFile' + extension;
}
initialResourcePath = path.normalize(initialResourcePath)
const headerObject = {
state: {
folderPath: initialResourcePath.substring(
0,
initialResourcePath.lastIndexOf('/')
),
name: initialResourcePath.substring(
initialResourcePath.lastIndexOf('/') + 1,
initialResourcePath.lastIndexOf('.')
),
extension: initialResourcePath.substring(
initialResourcePath.lastIndexOf('.'),
initialResourcePath.length
),
projectBasePath: projectPath,
folderPath: path.dirname(initialResourcePath),
name: path.basename(initialResourcePath, path.extname(initialResourcePath)),
extension: path.extname(initialResourcePath),
projectBasePath: path.normalize(projectPath),
},
};
@@ -124,15 +119,16 @@ const selectBaseFolderPath = headerObject => {
if (!selectedDir) {
return;
}
if (!selectedDir.toString().startsWith(state.projectBasePath)) {
const selectedDirPath = selectedDir[0];
if (!selectedDirPath.startsWith(state.projectBasePath)) {
alert(
'Please select a folder inside your project path!\n' +
state.projectBasePath +
'\n\nSelected:\n' +
selectedDir
selectedDirPath
);
return;
}
state.folderPath = selectedDir;
state.folderPath = selectedDirPath;
render(headerObject);
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1 @@
Demonstrate how to show ads from AdMob in your game: banners, interstitial screen or reward videos. Also useful to test that your AdMob account is working properly.

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 B

View File

@@ -1,14 +0,0 @@
var shell = require('shelljs');
var source = '../node_modules/loov-jsfx';
var success = true;
success &= !shell.mkdir('-p', '../public/external/jsfx').stderr;
success &= !shell.cp('-Rf', source, '../public/external/jsfx').stderr;
if (!success) {
shell.echo(
`❌ Error(s) occurred while copying Jsfx Editor sources from node_modules/loov-jsfx`
);
} else {
shell.echo('✅ Sources of Jsfx Editor properly copied in public folder');
}

View File

@@ -1,67 +0,0 @@
/**
* This script download and extract a zipped version of the Piskel editor
* (https://www.piskelapp.com/).
* The zip file contains the raw, unchanged piskel editor sources into a folder
* called "piskel-editor".
*/
var shell = require('shelljs');
var https = require('follow-redirects').https;
var fs = require('fs');
var unzip2 = require('unzip2');
const zipFilePath = '../public/external/piskel/piskel-editor.zip';
if (shell.test('-d', '../public/external/piskel/piskel-editor')) {
//Nothing to do
shell.echo(
'✅ piskel-editor already existing in public/external/piskel folder - skipping download'
);
} else {
shell.echo(
'🌐 Unable to find piskel-editor, downloading it from github.com/4ian/GD (be patient)...'
);
var file = fs.createWriteStream(zipFilePath);
https.get(
'https://github.com/4ian/GDevelop/releases/download/v5.0.0-beta34/piskel-editor.zip',
function(response) {
if (response.statusCode !== 200) {
shell.echo(
`❌ Can't download piskel-editor.zip (${response.statusMessage}), please check your internet connection`
);
shell.exit(1);
return;
}
response.pipe(file).on('finish', function() {
shell.echo(
'📂 Extracting piskel-editor.zip to public/external/piskel folder'
);
try {
fs
.createReadStream(zipFilePath)
.pipe(unzip2.Extract({ path: '../public/external/piskel' }))
.on('close', function() {
shell.echo(
'✅ Extracted piskel-editor.zip to public/external/piskel folder'
);
shell.rm(zipFilePath);
if (
!shell.test('-d', '../public/external/piskel/piskel-editor')
) {
shell.echo(
"❌ Can't verify that piskel-editor exists. Was the piskel-editor.zip file malformed?"
);
}
});
} catch (e) {
shell.echo(
'❌ Error while extracting piskel-editor.zip to public/external/piskel folder:',
e.message
);
}
});
}
);
}

View File

@@ -0,0 +1,73 @@
/**
* This script will download and extract a zipped version of a prebuilt external html5 editor
* The zip file contains the raw, unchanged editor sources, which will be extracted into a folder
* The zip should be uploaded with one of the git releases (use gitRelease variable for version where you released it)
*/
var shell = require('shelljs');
var https = require('follow-redirects').https;
var fs = require('fs');
var unzip2 = require('unzip2');
var process = require('process')
const editor = process.argv[2];
const gitRelease = process.argv[3];
const gitUrl = 'https://github.com/4ian/GDevelop';
const basePath = '../public/external/' + editor + '/' + editor + '-editor';
const zipFilePath = basePath + '.zip';
if (shell.test('-d', basePath)) {
//Nothing to do
shell.echo(
'✅ ' + editor + '-editor already existing in public/external/' + editor + ' folder - skipping download'
);
} else {
shell.echo(
'🌐 Unable to find ' + editor + '-editor, downloading it from ' + gitUrl + ' (be patient)...'
);
var file = fs.createWriteStream(zipFilePath);
https.get(
gitUrl + '/releases/download/v' + gitRelease + '/' + editor + '-editor.zip',
function (response) {
if (response.statusCode !== 200) {
shell.echo(
`❌ Can't download ` + editor + `-editor.zip (${response.statusMessage}), please check your internet connection`
);
shell.exit(1);
return;
}
response.pipe(file).on('finish', function () {
shell.echo(
'📂 Extracting ' + editor + '-editor.zip to public/external/' + editor + ' folder'
);
try {
fs
.createReadStream(zipFilePath)
.pipe(unzip2.Extract({
path: '../public/external/' + editor
}))
.on('close', function () {
shell.echo(
'✅ Extracted ' + editor + '-editor.zip to public/external/' + editor + ' folder'
);
shell.rm(zipFilePath);
if (
!shell.test('-d', basePath)
) {
shell.echo(
"❌ Can't verify that " + editor + "-editor exists. Was the " + editor + "-editor.zip file malformed?"
);
}
});
} catch (e) {
shell.echo(
'❌ Error while extracting ' + editor + '-editor.zip to public/external/' + editor + ' folder:',
e.message
);
}
});
}
);
}

View File

@@ -14,9 +14,11 @@ import {
enumerateObjectTypes,
type EnumeratedObjectMetadata,
} from '../ObjectsList/EnumerateObjects';
import Divider from 'material-ui/Divider';
import ThemeConsumer from '../UI/Theme/ThemeConsumer';
import HelpButton from '../UI/HelpButton';
import SemiControlledTextField from '../UI/SemiControlledTextField';
import MiniToolbar, { MiniToolbarText } from '../UI/MiniToolbar';
import { showWarningBox } from '../UI/Messages/MessageBox';
const gd = global.gd;
type Props = {|
@@ -43,6 +45,17 @@ const styles = {
},
};
const validateParameterName = (newName: string) => {
if (!gd.Project.validateObjectName(newName)) {
showWarningBox(
'This name contains forbidden characters: please only use alphanumeric characters (0-9, a-z) and underscores in your parameter name.'
);
return false;
}
return true;
};
export default class EventsFunctionConfigurationEditor extends React.Component<
Props,
State
@@ -150,143 +163,134 @@ export default class EventsFunctionConfigurationEditor extends React.Component<
</Line>
</Column>
<Line noMargin>
<ThemeConsumer>
{muiTheme => (
<div
style={{
...styles.parametersContainer,
backgroundColor: muiTheme.list.itemsBackgroundColor,
}}
>
{mapVector(
parameters,
(parameter: gdParameterMetadata, i: number) => (
<React.Fragment key={i}>
<Column>
<Line noMargin expand>
<Column noMargin expand>
<TextField
hintText="Parameter name"
value={parameter.getName()}
onChange={(e, text) => {
parameter.setName(text);
this.forceUpdate();
}}
onBlur={() => {
this.props.onParametersUpdated();
}}
fullWidth
/>
</Column>
<Column noMargin expand>
<SelectField
hintText="Type"
value={parameter.getType()}
onChange={(e, i, value) => {
parameter.setType(value);
this.forceUpdate();
this.props.onParametersUpdated();
}}
fullWidth
>
<MenuItem
value="objectList"
primaryText="Objects"
/>
<MenuItem
value="expression"
primaryText="Number"
/>
<MenuItem
value="string"
primaryText="String (text)"
/>
</SelectField>
</Column>
{parameter.getType() === 'objectList' && (
<Column noMargin expand>
<SelectField
value={parameter.getExtraInfo()}
onChange={(e, i, value) => {
parameter.setExtraInfo(value);
this.forceUpdate();
this.props.onParametersUpdated();
}}
fullWidth
>
<MenuItem value="" primaryText="Any object" />
{objectMetadata.map(
(metadata: EnumeratedObjectMetadata) => {
if (metadata.name === '') {
// Base object is an "abstract" object
return null;
}
<div style={styles.parametersContainer}>
{mapVector(
parameters,
(parameter: gdParameterMetadata, i: number) => (
<React.Fragment key={i}>
<MiniToolbar>
<MiniToolbarText>Parameter #{i + 1}:</MiniToolbarText>
<Column expand noMargin>
<SemiControlledTextField
hintText="Enter the parameter name"
value={parameter.getName()}
onChange={text => {
if (!validateParameterName(text)) return;
return (
<MenuItem
key={metadata.name}
value={metadata.name}
primaryText={metadata.fullName}
/>
);
}
)}
</SelectField>
</Column>
)}
</Line>
</Column>
<Line expand noMargin>
<Column expand>
<TextField
hintText="Description"
value={parameter.getDescription()}
onChange={(e, text) => {
parameter.setDescription(text);
this.forceUpdate();
}}
fullWidth
/>
</Column>
<Column>
<IconMenu
iconButtonElement={
<IconButton>
<MoreVertIcon />
</IconButton>
}
buildMenuTemplate={() => [
{
label: 'Delete',
click: () => this._removeParameter(i),
},
]}
/>
</Column>
</Line>
<Divider />
</React.Fragment>
)
)}
{parameters.size() <= 1 ? (
<EmptyMessage>
No parameters for this function.
</EmptyMessage>
) : null}
<Line justifyContent="space-between">
<Column>
<HelpButton helpPagePath="/events/functions" />
</Column>
<Column>
<FlatButton
label="Add a parameter"
onClick={this._addParameter}
parameter.setName(text);
this.forceUpdate();
this.props.onParametersUpdated();
}}
commitOnBlur
/>
</Column>
<IconMenu
iconButtonElement={
<IconButton>
<MoreVertIcon />
</IconButton>
}
buildMenuTemplate={() => [
{
label: 'Delete',
click: () => this._removeParameter(i),
},
]}
/>
</Column>
</Line>
</div>
</MiniToolbar>
<Line expand noMargin>
<Column expand>
<SelectField
floatingLabelText="Type"
value={parameter.getType()}
onChange={(e, i, value) => {
parameter.setType(value);
this.forceUpdate();
this.props.onParametersUpdated();
}}
fullWidth
>
<MenuItem value="objectList" primaryText="Objects" />
<MenuItem value="expression" primaryText="Number" />
<MenuItem
value="string"
primaryText="String (text)"
/>
<MenuItem
value="key"
primaryText="Keyboard Key (text)"
/>
<MenuItem
value="mouse"
primaryText="Mouse button (text)"
/>
</SelectField>
</Column>
{parameter.getType() === 'objectList' && (
<Column expand>
<SelectField
floatingLabelText="Object type"
floatingLabelFixed
value={parameter.getExtraInfo()}
onChange={(e, i, value) => {
parameter.setExtraInfo(value);
this.forceUpdate();
this.props.onParametersUpdated();
}}
fullWidth
>
<MenuItem value="" primaryText="Any object" />
{objectMetadata.map(
(metadata: EnumeratedObjectMetadata) => {
if (metadata.name === '') {
// Base object is an "abstract" object
return null;
}
return (
<MenuItem
key={metadata.name}
value={metadata.name}
primaryText={metadata.fullName}
/>
);
}
)}
</SelectField>
</Column>
)}
</Line>
<Line expand noMargin>
<Column expand>
<TextField
floatingLabelText="Description"
value={parameter.getDescription()}
onChange={(e, text) => {
parameter.setDescription(text);
this.forceUpdate();
}}
fullWidth
/>
</Column>
</Line>
</React.Fragment>
)
)}
</ThemeConsumer>
{parameters.size() === 0 ? (
<EmptyMessage>No parameters for this function.</EmptyMessage>
) : null}
<Line justifyContent="space-between">
<Column>
<HelpButton helpPagePath="/events/functions" />
</Column>
<Column>
<FlatButton
label="Add a parameter"
onClick={this._addParameter}
/>
</Column>
</Line>
</div>
</Line>
</div>
</Column>

View File

@@ -176,6 +176,7 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
this._globalObjectsContainer &&
this._objectsContainer ? (
<EventsSheet
key={selectedEventsFunction.ptr}
ref={editor => (this.editor = editor)}
project={project}
layout={null}

View File

@@ -2,18 +2,6 @@
exports[`EnumerateInstructions can create the tree of instructions 1`] = `
Object {
"AdMob banners and interstitial screens": Object {
"Banner is displayed": Object {
"displayedName": "Banner is displayed",
"fullGroupName": "AdMob banners and interstitial screens/",
"type": "AdMobObject::BannerDisplayed",
},
"Interstitial screen is ready": Object {
"displayedName": "Interstitial screen is ready",
"fullGroupName": "AdMob banners and interstitial screens/",
"type": "AdMobObject::InterstitialReady",
},
},
"Advanced": Object {
"And": Object {
"displayedName": "And",
@@ -2376,26 +2364,6 @@ Array [
"fullGroupName": "Physics behavior/Movement",
"type": "PhysicsBehavior::SetStatic",
},
Object {
"displayedName": "Hide banner ad",
"fullGroupName": "AdMob banners and interstitial screens/Banner",
"type": "AdMobObject::HideBanner",
},
Object {
"displayedName": "Preload interstitial screen",
"fullGroupName": "AdMob banners and interstitial screens/Interstitial screen",
"type": "AdMobObject::PreloadInterstitial",
},
Object {
"displayedName": "Show banner ad",
"fullGroupName": "AdMob banners and interstitial screens/Banner",
"type": "AdMobObject::ShowBanner",
},
Object {
"displayedName": "Show interstitial screen",
"fullGroupName": "AdMob banners and interstitial screens/Interstitial screen",
"type": "AdMobObject::ShowInterstitial",
},
]
`;
@@ -3346,15 +3314,5 @@ Array [
"fullGroupName": "Physics behavior/Displacement",
"type": "PhysicsBehavior::LinearVelocityY",
},
Object {
"displayedName": "Banner is displayed",
"fullGroupName": "AdMob banners and interstitial screens/",
"type": "AdMobObject::BannerDisplayed",
},
Object {
"displayedName": "Interstitial screen is ready",
"fullGroupName": "AdMob banners and interstitial screens/",
"type": "AdMobObject::InterstitialReady",
},
]
`;

View File

@@ -70,7 +70,7 @@ export default class InstructionParametersEditor extends React.Component<
}, 300); // Let the time to the dialog that is potentially containing the InstructionParametersEditor to finish its transition.
}
}
focus() {
// Verify that there is a field to focus.
if (

View File

@@ -104,7 +104,7 @@ export default class InstancePropertiesEditor extends Component {
_renderEmpty() {
return (
<EmptyMessage>
Click on an instance on the scene to display its properties
Click on an instance in the scene to display its properties
</EmptyMessage>
);
}

View File

@@ -39,11 +39,11 @@ export default class ViewPosition {
var viewRotation = 0;
var tmp = x;
x =
Math.cos(viewRotation / 180 * 3.14159) * x -
Math.sin(viewRotation / 180 * 3.14159) * y;
Math.cos(viewRotation / 180 * Math.PI) * x -
Math.sin(viewRotation / 180 * Math.PI) * y;
y =
Math.sin(viewRotation / 180 * 3.14159) * tmp +
Math.cos(viewRotation / 180 * 3.14159) * y;
Math.sin(viewRotation / 180 * Math.PI) * tmp +
Math.cos(viewRotation / 180 * Math.PI) * y;
return [x + this.viewX, y + this.viewY];
};
@@ -59,11 +59,11 @@ export default class ViewPosition {
var viewRotation = -0;
var tmp = x;
x =
Math.cos(viewRotation / 180 * 3.14159) * x -
Math.sin(viewRotation / 180 * 3.14159) * y;
Math.cos(viewRotation / 180 * Math.PI) * x -
Math.sin(viewRotation / 180 * Math.PI) * y;
y =
Math.sin(viewRotation / 180 * 3.14159) * tmp +
Math.cos(viewRotation / 180 * 3.14159) * y;
Math.sin(viewRotation / 180 * Math.PI) * tmp +
Math.cos(viewRotation / 180 * Math.PI) * y;
x *= Math.abs(this._pixiContainer.scale.x);
y *= Math.abs(this._pixiContainer.scale.y);

View File

@@ -1,27 +0,0 @@
// @flow
import * as React from 'react';
import { type EditorProps } from './EditorProps.flow';
import { Line, Column } from '../../UI/Grid';
import PropertiesEditor from '../../PropertiesEditor';
import propertiesMapToSchema from '../../PropertiesEditor/PropertiesMapToSchema';
export default class AdMobEditor extends React.Component<EditorProps, void> {
render() {
const { object, project } = this.props;
const properties = object.getProperties(project);
const propertiesSchema = propertiesMapToSchema(
properties,
object => object.getProperties(project),
(object, name, value) => object.updateProperty(name, value, project)
);
return (
<Column>
<Line>
<PropertiesEditor schema={propertiesSchema} instances={[object]} />
</Line>
</Column>
);
}
}

View File

@@ -4,7 +4,7 @@ import Checkbox from 'material-ui/Checkbox';
import TextField from 'material-ui/TextField';
import { Line, Column } from '../../UI/Grid';
import ColorPicker from '../../UI/ColorField/ColorPicker';
import MiniToolbar from '../../UI/MiniToolbar';
import MiniToolbar, { MiniToolbarText } from '../../UI/MiniToolbar';
import { type EditorProps } from './EditorProps.flow';
const gd = global.gd;
@@ -18,9 +18,6 @@ const styles = {
...toolbarItemStyle,
},
toolbarItem: toolbarItemStyle,
toolbarText: {
marginRight: 5,
},
checkbox: {
width: 'auto',
...toolbarItemStyle,
@@ -35,7 +32,7 @@ export default class TextEditor extends React.Component<EditorProps, void> {
return (
<Column noMargin>
<MiniToolbar>
<p style={styles.toolbarText}>Size:</p>
<MiniToolbarText>Size:</MiniToolbarText>
<TextField
type="number"
style={styles.sizeTextField}
@@ -45,7 +42,7 @@ export default class TextEditor extends React.Component<EditorProps, void> {
this.forceUpdate();
}}
/>
<p style={styles.toolbarText}>Color:</p>
<MiniToolbarText>Color:</MiniToolbarText>
<ColorPicker
style={styles.sizeTextField}
disableAlpha

View File

@@ -4,7 +4,6 @@ import PanelSpriteEditor from './Editors/PanelSpriteEditor';
import SpriteEditor from './Editors/SpriteEditor';
import EmptyEditor from './Editors/EmptyEditor';
import ShapePainterEditor from './Editors/ShapePainterEditor';
import AdMobEditor from './Editors/AdMobEditor';
import ParticleEmitterEditor from './Editors/ParticleEmitterEditor';
const gd = global.gd;
@@ -68,11 +67,6 @@ export default {
newObjectCreator: () => new gd.PanelSpriteObject(''),
castToObjectType: object => gd.asPanelSpriteObject(object),
},
'AdMobObject::AdMob': {
component: AdMobEditor,
newObjectCreator: () => new gd.AdMobObject(''),
castToObjectType: object => gd.asAdMobObject(object),
},
'TextObject::Text': {
component: TextEditor,
newObjectCreator: () => new gd.TextObject(''),

View File

@@ -2,7 +2,6 @@ import RenderedUnknownInstance from './Renderers/RenderedUnknownInstance';
import RenderedSpriteInstance from './Renderers/RenderedSpriteInstance';
import RenderedTiledSpriteInstance from './Renderers/RenderedTiledSpriteInstance';
import RenderedPanelSpriteInstance from './Renderers/RenderedPanelSpriteInstance';
import RenderedAdMobInstance from './Renderers/RenderedAdMobInstance';
import RenderedTextInstance from './Renderers/RenderedTextInstance';
import RenderedShapePainterInstance from './Renderers/RenderedShapePainterInstance';
import RenderedTextEntryInstance from './Renderers/RenderedTextEntryInstance';
@@ -20,7 +19,6 @@ export default {
Sprite: RenderedSpriteInstance,
'TiledSpriteObject::TiledSprite': RenderedTiledSpriteInstance,
'PanelSpriteObject::PanelSprite': RenderedPanelSpriteInstance,
'AdMobObject::AdMob': RenderedAdMobInstance,
'TextObject::Text': RenderedTextInstance,
'PrimitiveDrawing::Drawer': RenderedShapePainterInstance,
'TextEntryObject::TextEntry': RenderedTextEntryInstance,

View File

@@ -1,91 +0,0 @@
import RenderedInstance from './RenderedInstance';
import PIXI from 'pixi.js';
/**
* Renderer for an AdMob object.
*
* @extends RenderedInstance
* @class RenderedAdMobInstance
* @constructor
*/
function RenderedAdMobInstance(
project,
layout,
instance,
associatedObject,
pixiContainer,
pixiResourcesLoader
) {
RenderedInstance.call(
this,
project,
layout,
instance,
associatedObject,
pixiContainer,
pixiResourcesLoader
);
//Setup the PIXI object:
this._pixiObject = new PIXI.Container();
this._pixiContainer.addChild(this._pixiObject);
this._titleText = new PIXI.Text('Ad banner object');
this._titleText.style = {
fill: 'white',
font: 'bold 18px Arial',
};
this._titleText.position.x =
this.getDefaultWidth() / 2 - this._titleText.width / 2;
this._text = new PIXI.Text(
'displayed on Android at the top or bottom of the screen'
);
this._text.style = {
fill: 'white',
font: 'italic 14px Arial',
};
this._text.position.x = this.getDefaultWidth() / 2 - this._text.width / 2;
this._text.position.y = 30;
this._placeholder = new PIXI.Graphics();
this._placeholder.beginFill(0x15b4f9);
this._placeholder.lineStyle(1, 0x12286f, 1);
this._placeholder.moveTo(0, 0);
this._placeholder.lineTo(this.getDefaultWidth(), 0);
this._placeholder.lineTo(this.getDefaultWidth(), this.getDefaultHeight());
this._placeholder.lineTo(0, this.getDefaultHeight());
this._placeholder.lineTo(0, 0);
this._placeholder.endFill();
this._pixiObject.addChild(this._placeholder);
this._pixiObject.addChild(this._text);
this._pixiObject.addChild(this._titleText);
}
RenderedAdMobInstance.prototype = Object.create(RenderedInstance.prototype);
/**
* Return a URL for thumbnail of the specified object.
*/
RenderedAdMobInstance.getThumbnail = function(
project,
resourcesLoader,
object
) {
return 'JsPlatform/Extensions/admobicon24.png';
};
RenderedAdMobInstance.prototype.update = function() {
this._pixiObject.position.x = this._instance.getX();
this._pixiObject.position.y = this._instance.getY();
};
RenderedAdMobInstance.prototype.getDefaultWidth = function() {
return 400;
};
RenderedAdMobInstance.prototype.getDefaultHeight = function() {
return 64;
};
export default RenderedAdMobInstance;

View File

@@ -23,10 +23,10 @@ export default ({ profile }: Props) =>
<Column>
<Line alignItems="center">
<Avatar src={getGravatarUrl(profile.email || '', { size: 40 })} />
<span style={styles.title}>You are connect as {profile.email}</span>
<span style={styles.title}>You are connected as {profile.email}</span>
</Line>
<Line>
<p>With your account, you can access to GDevelop online services.</p>
<p>An account allows you to access GDevelop services online.</p>
</Line>
</Column>
) : (

Some files were not shown because too many files have changed in this diff Show More