Impact

This forum is read only and just serves as an archive. If you have any questions, please post them on github.com/phoboslab/impact

1 decade ago by Joncom

It's been said in this thread that
your plugin should require [a] library as an external file
rather than wrap it in a module. Doing so was deemed proper because updating the library is quick and doesn't affect your plugin. It also means not having to rewrap the library in a module every time an update happens.

So how can this be done?

Adding a <script> tag to the <head> of index.html is not an option. This is a plugin remember, and it should happen automatically when added to module requires.

Another concern is the Impact preloader. It's very nice that Impact loads everything before running any game code. And since external libraries are not Impact modules, they cannot be added to requires, but maybe is there a way to inject the loading of an external library into the preloader so that they are loaded as well before game code is run?

1 decade ago by Mediamonkey

I made a plugin for weltmeister and needed a jquery plugin called ocupload.js
My take on the dependency was to inject a script tag into the page.
It's not the best, but it works.

ig.module(
	'upload-button'
)
.requires(
	'weltmeister.weltmeister'
)
.defines(function(){
	"use strict";
	
	// ---- inject scripts and button into weltmeister ----
	
	// insert upload script
	var script = document.createElement('script');
	script.src = "lib/jquery.ocupload.js";
	script.async = true;
	document.body.appendChild(script);
	
	// insert button
	var $uploadButton = $('<input type="button" id="uploadImages" value="Upload" title="Upload Images" class="button"/>');
	$uploadButton.css('margin-right', '4px');
	$("#reloadImages", "#headerMenu > .headerFloat").before($uploadButton);
	$("#reloadImages", "#headerMenu > .headerFloat").val('Reload');

	// etc.

1 decade ago by lTyl

Creating a script tag and adding it to the page is how you would do it if you must load the script dynamically, but it is doing the exact same as simply adding a <script> tag to your .html file.

I don't see why the Impact pre-loader would be an issue. By adding an additional script tag to your html file, your external library is loaded and exists in the window namespace when ig exists. For example, what I do is load the external library by adding a script tag to my .html file, then create an impact plugin and do: myObj = new ExternalLibrary(params); Then simply pass the appropriate data to the external lib through my plugin: myObj.do.something(param);

1 decade ago by Joncom

Edit: Disregard this post. The code does not work. See the code in the post below it.

Mediamonkey, your post inspired this. It's untested code, but the idea is that the game would not run until the external library is also loaded.
ig.module('example')
.requires()
.defines(function(){

    // This block here loads the external library.
    ig._waitForOnload++;
    ig.$new('script');
    script.type = 'text/javascript';
    script.src = 'path/to/library.js';
    script.onload = function() {
        ig._waitForOnload--;
        ig._execModules();
    };
    script.onerror = function() {
        throw(
            'Failed to load external library.'
        );
    };
    ig.$('head')[0].appendChild(script);

    // And then here is where you define 
    // your actual plugin that needs it...

});

1 decade ago by Joncom

OK. Here is how to load any JavaScript file alongside other modules so that it will be loaded using the preloader, and ready before the game starts running.

ig.module('plugins.example')
.requires()
.defines(function(){

    // Load jQuery library.
    var path = 'jquery-1.9.1.min.js';
    var name = 'jquery';
    if(!ig.modules[name]) {
        // Simulate external library as new module.
        ig.modules[name] = {name: name, requires:[], loaded: false, body: null};
        // Add new module as dependency to main.
        ig.modules['game.main'].requires.push(name);
        ig._waitForOnload++;
        var script = ig.$new('script');
        script.type = 'text/javascript';
        script.src = path;
        script.onload = function() {
            ig._waitForOnload--;
            ig.modules[name].loaded = true;
            ig._execModules();
        };
        script.onerror = function() {
            throw(
                'Failed to load module '+ name +' at ' + path + '.'
            );
        };
        ig.$('head')[0].appendChild(script);
    }

});

There are a few problems with this however:

1. You cannot run this code from within the main module, because it depends on main not yet being loaded.

2. You cannot use the external library within the same defines block, because the library won't yet have been loaded.

3. I'm not sure that baking a game will include the external library.

Edit 1

Hmm. On second thought, it is not guarenteed that the preloader will wait for the external library. The game may start before the external library is loaded only if this module is the last one to load. And as the developer there is little control over which modules will finish transferring before another.

Edit 2

My conclusion after spending much of my afternoon working on this: In theory while it would be nice to keep external libraries unmodified, it is not practical for the following reasons:

1. You cannot bake the library into your game.
2. You cannot guarantee the library is loaded before the game starts.

If anyone knows a way to correct either of these two issues without directly modifying the Impact core library, I'd love to hear it.

1 decade ago by lTyl

Here is a (literally) 5 minute plugin I tossed together using the Illuminated library ( Linky)

Here is the result: DEMO (Note that the illuminated lib is baked into the compiled.js file along with Impact)

Here is the plugin:
ig.module('plugins.illuminated')
    .defines(function () {
        var Lamp = illuminated.Lamp
            , Vec2 = illuminated.Vec2
            , DiscObject = illuminated.DiscObject
            , RectangleObject = illuminated.RectangleObject
            , Lighting = illuminated.Lighting
            , DarkMask = illuminated.DarkMask;

        Illuminated = ig.Class.extend({
            maskColor:'rgba(0,0,0,0.9)',
            darkMask:null,
            canvas:null,
            context:null,

            light1:null,
            light2:null,
            disc:null,
            rect:null,
            objects:null,
            lighting1:null,
            lighting2:null,

            init:function () {
                var canvas = ig.$('#canvas');
                var ctx = canvas.getContext('2d');

                this.light1 = new Lamp({
                    position: new Vec2(100,250),
                    distance:400,
                    diffuse:0.8,
                    color:'rgba(239,184,134,1)',
                    radius:100,
                    samples:6,
                    angle:36,
                    roughness:0.5,
                    size:32
                });

                this.light2 = new Lamp({
                    position: new Vec2(300,50),
                    distance:400,
                    diffuse:0.8,
                    color:'#CFF',
                    radius:100,
                    samples:6,
                    angle:36,
                    roughness:0.5,
                    size:32
                });

                this.disc = new DiscObject({ center:new Vec2(100, 100), radius:30 });
                this.rect = new RectangleObject({ topleft:new Vec2(250, 200), bottomright:new Vec2(350, 250) });

                this.objects = [ this.disc, this.rect ];

                this.lighting1 = new Lighting({
                    light:this.light1,
                    objects:this.objects
                });

                this.lighting2 = new Lighting({
                    light:this.light2,
                    objects:[ this.disc, this.rect ]
                });

                this.darkMask = new DarkMask({ lights:[this.light1, this.light2] });
                this.lighting1.compute(640, 480);
                this.lighting2.compute(640,480);
                this.darkMask.compute(640, 480);

                ctx.globalCompositeOperation = "lighter";
                this.lighting1.render(ctx);
                this.lighting2.render(ctx);

                ctx.globalCompositeOperation = "source-over";
                this.darkMask.render(ctx);
            },

            update:function () {
                this.runDemo();
            },

            checkChanged: function(){

                if (ig.input.pressed('leftClick')) {
                    this.light1.position.x = ig.input.mouse.x;
                    this.light1.position.y = ig.input.mouse.y;
                    this.darkMask = new DarkMask({ lights:[this.light1, this.light2] });
                    this.lighting1.compute(640, 480);
                    this.lighting2.compute(640, 480);
                    this.darkMask.compute(640, 480);

                    ig.system.context.globalCompositeOperation = "lighter";
                    this.lighting1.render(ig.system.context);
                    this.lighting2.render(ig.system.context);

                    ig.system.context.globalCompositeOperation = "source-over";
                    this.darkMask.render(ig.system.context);
                }

                if (ig.input.pressed('rightClick')) {
                    this.light2.position.x = ig.input.mouse.x;
                    this.light2.position.y = ig.input.mouse.y;
                    this.darkMask = new DarkMask({ lights:[this.light1, this.light2] });
                    this.lighting1.compute(640, 480);
                    this.lighting2.compute(640, 480);
                    this.darkMask.compute(640, 480);

                    ig.system.context.globalCompositeOperation = "lighter";
                    this.lighting1.render(ig.system.context);
                    this.lighting2.render(ig.system.context);

                    ig.system.context.globalCompositeOperation = "source-over";
                    this.darkMask.render(ig.system.context);
                }
            },

            runDemo:function () {
                this.checkChanged();
                ig.system.context.globalCompositeOperation = "lighter";
                this.lighting1.render(ig.system.context);
                this.lighting2.render(ig.system.context);
                ig.system.context.globalCompositeOperation = "source-over";

            }
        })
    });

Obviously you wouldn't build your plugin like how I built this one, but the idea is the same. In this case, I'm using the Impact plugin to set-up the lights and the dark mask and then calling the illuminated library to do all of the rendering/heavy math, passing in the Impact canvas as a parameter so it all just works without having to wrap an external lib as a module or doing any modification to the lib. If this was to be used outside of a demonstration, I'd add methods to make the library and impact work better together: such as add/remove lights, dynamically add objects, etc.

But this is how I go about creating a plugin for an external library. I hope I helped :)

1 decade ago by drhayes

I just wanted to jump into this thread to cast a vote for not re-implementing the wheel: how about the next major version of ImpactJS uses a separate, major module system like RequireJS or, better yet, browserify?

This complicates things in a few ways: it would be backward incompatible with existing 1.x code; it might require a separate build step (browserify doesn't load things dynamically); neither of these tools provide built-in progress events to provide feedback to the user.

It would provide some benefits: both of these tools can load static files and make them available as JS variables; an entire community devoted to solving loading problems in ingenious ways; more standard, modern JS way of doing things.

Both of these tools provide for "baking" and optimizing steps as well.

There are some ancillary benefits: the loaded modules are isolated from each other instead of being JS globals; RequireJS also allows for passing in config information to the module so you could do things like config a module based on what platform it is being built for, web or iOS; etc.

This would also make available nearly every major JS library to our games in an elegant, sustainable way: underscore/lodash, jQuery (although I bet our games don't need much of it), socket.io, etc.

Just something for Dominic to think about, I guess. ( =

1 decade ago by Joncom

Quote from lTyl
Note that the illuminated lib is baked into the compiled.js file along with Impact.
I see commented out in your index.html source the illuminated library. Two questions:

1. What did you do which made the bake-tool include this library?
2. Just want to confirm that the plugin itself does not load of the library, usually index.html does?

1 decade ago by lTyl

Quote from Joncom
I see commented out in your index.html source the illuminated library. Two questions:

1. What did you do which made the bake-tool include this library?
2. Just want to confirm that the plugin itself does not load of the library, usually index.html does?


1.) I used the default bake tool from Impact to create game.min.js. Then I took the game.min.js and the illuminated.js files and combined them together using Google Closure Compiler. Here is the batch script I used:
java -jar compiler.jar --js=illuminated.js --js=game.min.js --language_in=ECMASCRIPT5 --js_output_file=compiled.js

2.) In the example, the illuminated library is being loaded (From compiled.js from the demo). On page load, Illuminated exists in the window object, so in the plugin I just create new objects from the illuminated library and then manipulate those. (Lamp, Vec, Disk, Rect, etcetcetc)

1 decade ago by Joncom

Thanks ITyl. It's cool to see how that can be done. I was hoping it wouldn't involve something external like Google Closure Compiler, but until a better option is available I will keep it in mind.

1 decade ago by lTyl

Quote from Joncom
Thanks ITyl. It's cool to see how that can be done. I was hoping it wouldn't involve something external like Google Closure Compiler, but until a better option is available I will keep it in mind.


Why not? Google Closure Compiler does a much better job at minifying than the default bake tool. (Bake tool took down that demo to 68kb, with no illuminated baked in. Closure compiler took the baked game.min.js AND the illuminated.ls library down to 67kb.

Typically, my build order is: Run impact bake tool, it minifies all of the modules easily and conveniently. Pass it through closure compiler, attach any external libs as needed. If it is a project using node-webkit with a target build for desktop, I then turn the file into a .PNG image ( Write-up), not for any size saving measures but to simplify updating the source with changes/bug fixes; I just need to push a 60ish kb image and bam updated.
Page 1 of 1
« first « previous next › last »