Using requirejs for a modular game engine - javascript

I'm an experienced javascript programmer, and I'm trying to write my own modular game engine from scratch. I haven't used requirejs before, but after reading a bit about it, it sounds like it's probably the best way to manage all the components of the engine and load them into one coherent structure.
My main problem is that I'm not really sure how to really use requirejs. I've been looking over their API documentation, but I'm not sure how to integrate it with the way I've laid out my project.
Currently, my project uses the following structure:
src
engine.js //This contains the common engine stuff, most notably the engine namespace
resource
resource-module.js //This is the module constructor, which handles all the common stuff between the different
substructures
sprites.js //This is a substructure that contains sprite loading code
render
etc...
third-party
jquery
requirejs
I want to be able to load the modules independently of each other. It should be possible for instance to remove the audio module from the engine, and it still work. It should also be easy to substitute modules, or add new modules.
Also, I'm using jQuery for event handling, so it needs to be loaded before the engine is initiated.
You can find my current source here: https://github.com/superlinkx/lithium-engine
I know the current code is messy, and there isn't a whole lot built yet, but I'm mostly still figuring out how to best structure it. Any help/advice would be appreciated, but my main concern is how to structure it with requirejs so that it will be easier to compile into a single file for production use.

Require.js does not enforce any specific structure of your files. You can either specify the full paths in the require configuration, or just use the full path in the require() or define() calls. Both will work, however the latter will save you some typing and makes it easier to move stuff around when you include something from a lot of different places:
var $ = require("third-party/jquery");
require.config({
paths: {
"jquery": "third-party/jquery"
}
});
var $ = require("jquery");
I want to be able to load the modules independently of each other. It should be possible for instance to remove the audio module from the engine, and it still work. It should also be easy to substitute modules, or add new modules.
This is not something require.js does four you. You can decide when and when not to load it, but you would have to make sure that it won't break the rest of your code.
Also, I'm using jQuery for event handling, so it needs to be loaded before the engine is initiated.
You can do this in several different ways.
require() it in your main.js so that it is always loaded (you can also use the require-jquery.js, which has jQuery included).
Specify jQuery as a dependency of your engine
require.config({
paths: {
"jquery": "path.to.jquery",
"engine": "path.to.engine"
},
shim: {
"engine": {
"deps": ["jquery"]
}
}
});
Pass jQuery to the define() call in your module (probably the best choice)
define(["jquery"], function ($) {
// your engine code
}

Related

Bundling a durandal single page application into one HTML page

I have a very strange requirement that I need to bundle everything together in one HTML page with my Durandal Single Page application. I can make this away with my dependencies as I am defining them with a name:
define("models.mapper", [], function() {
});
However, it seems like it will not be possible to bundle durandal stuff as it defines modules without names:
define(['require', 'jquery'], function(require, $) {
// ....
}
This is fine when you want to make it work with path references but it seems like this will make it hard to inline this into HTML. Any ideas or suggestions on this?
Require.JS requires you to have only one anonymous define per file so that it can use the file path+name relative to the base path to give it a name. If you would like to have the durandal source inline on your page as well then you'll need to update their define lines to give them the appropriate names (i.e. define('durnadal/system', ......).
An easier approach may be to just build your source code in the structure of a normal durandal project and then use the RequireJS optimizer (http://requirejs.org/docs/optimization.html) to build them into a single JS file - if you configure this correctly without minification then you can just paste the file contents into a script tag on your page and it'll still be legible!
If you really wanted to you could then just continue developing in the single HTML file however you really should look at automating all of this into a grunt workflow and it shouldn't be too hard and you'll have much easier to manage code. Note that you may even be able to use the durandal grunt task to do this, but I'm not sure what options it allows you to provide but you can definitely use the requirejs grunt task and build it into your workflow without minification. With some templating task you could then inject that output into your final HTML page.

Requirejs multi-page & multi-app not executing define callbacks

I have a multi-page / mulit-application minifcation I'm trying to make work.
the structure is as follows
common/
--main config file that defines the common libs
common/build
--app.build.js (see example below)
application1/
--application
--milion other files
application2/
--application
--thousand other files
this is placed in the header the page(s)
</style><script data-main='common/main' src='libraries/require.js'></script>
footer like this
<script>require(['../application1/application']);</script>
<script>require(['../application2/application']);</script>
This approach works perfect when using a bunch of files, and the applications run and interact perfect
However trying to make them into one seems a bit more of a challenge in terms of making it work.Granted I could just be an idiot
Build File
({
baseUrl: "../",
mainConfigFile: '../main.js',
optimize: 'uglify',
optimizeCss: 'standard',
out: "../global.min.js",
//insertRequire: ['main'],
include: ['../application1/application', '../application2/application'],
wrap: true // have tried both options - this makes the scenario at the bottom work
})
This works perfect and produces a lovely global.min.js. Challenge is it does not execute the callbacks on define calls I include it like so:
this is placed in the header the page(s)
</style><script data-main='common/main' src='libraries/require.js'></script>
footer like this
<script>require(['../common/global.min']);</script>
HOWEVER: if I keep the old requires in places like so:
<script>require(['../application1/application']);</script>
<script>require(['../application2/application']);</script>
it downloads 3 files
common/global.min.js
application1/application.js
application2/application.js
NOTE: At this point it is missing about 150 files, but the application(s) work perfect.
Having spent some serious time on this, my head is now properly wrecked and I can't figure out how to make it work from a single minified file.
Any help much appreciated as I have tried whatever I could think of. Happy to share all the files, howvere there is a lot of them ;-)
This is an educated guess, based on what you've shown in the question and experience with RequireJS' failure modes. I'm guessing "./common/global.min" is not a module name. (If you want to check this, open your optimized bundle and look at all the define calls. If you find that none of them has "./common/global.min", then it is not a module name.)
When you require "./common/global.min", RequireJS loads the corresponding file, and then looks in it for a module named "./common/global.min". It finds none, and your code does not get to run.
One way to fix this is to use a RequireJS configuration with your optimized bundle that maps names like this:
paths: {
'../application1/application': './common/global.min'
'../application2/application': './common/global.min'
}
And call require with these module names instead of "./common/global.min". This configuration tells RequireJS "when you are looking for these modules, look in that file: they are there" and indeed that's what the optimization process does.

Using RequireJS with legacy code

I'm working with a very large project that uses:
Legacy JSP pages that includes javascript files with script tags
Backbone models and views that uses other javascript modules without RequireJS
We now want to start using RequireJS with jQuery, BackboneJS and UnderscoreJS for everything we develop from now on, but we don't have the resources to rewrite all the legacy JSP pages. We may have time to rewrite the Backbone models and views we have already developed.
The problem is that for our legacy code (both 1 and 2 above) we include all our javascript files in a huge file and ship to the browsers. This huge file must be able to co-exist with our new RequireJS templates, but how can I e.g. separate certain parts of the huge file, so I can use them with the templates using RequireJS? Without having to rewrite all pages that uses this part of the file, or having duplicate code.
Thanks!
I don't know if I fully grasp the problem at hand, but I think a the shim or map functions of RequireJS will help you out.
Extract the parts you want in a new module from your huge javascript file. Then tell RequireJS that your huge javascript file is a dependecy for this new module using shim. Something like:
requirejs.config({
shim: {
'new-module': {
deps: ['huge-javascript-file'],
exports: 'NewModule'
}
});
Shim documentation: http://requirejs.org/docs/api.html#config-shim
The map function might be useful when only portions of your new code have to use your old huge file. Check out this documentation: http://requirejs.org/docs/api.html#config-map
I don't think there is One True Way to achieve this, but I've approached a similar problem by defining module "facades" around the globally scoped code. Let's say your legacy scripts define a global variable called foo. You can define a AMD module and export that variable from it:
//foo.js
define(function() {
return window.foo;
});
//bar.js
define(['foo'], function(foo) {
//do something with foo
});
This way you only need to write a single-line facade every time you need to use a new piece of the existing, globally defined code, without breaking existing code which expects the same code to be globally defined. Over time you can move and refactor the actual implementation into the module without breaking consumer code.

BreezeJS and RequireJS not working as expected

I am integrating breezeJS into an existing requireJS project which already uses knockoutJS. I ran into a couple of problems.
The first was that breeze is unable to load the Q library unless i include it on my html wrapper as a <script> tag, not as a loaded AMD dependency. In my project i am trying to keep my code down to a single script tag, so this isnt ideal.
The second issue is that breezeJS is unable to load knockout. In my main.js I have defined a path for knockout to be:
knockout: '../libs/knockout/knockout-2.2.0',
(I do this because I like knowing for sure that I am not accessing a global ko)
However when i added breeze to my project, breeze wasn't able to load my knockout library. Looking into the breeze code i can see that it has been hardcoded to load the knockout library as ko.
Not wanting to change all of my code i found that i could add my AMD loaded knockout library to the global window object as window['ko']. But this feels like quite a bodge. Also weirdly adding Q this way and removing the <script> tag didn't work, as i think Q is required too early in the application's lifecycle, even before i get to pollute the global - i did nest my require() calls in main.js but that hid the majority of my application files from the build process so i abandoned that approach.
How can i include Q and knockout and breeze in my project and still use a single line <script> tag, at the moment I am having to include Q as a separate <script> tag and pollute the global to get breeze and knockout to play nicely.
I am using quite a few other libraries in my project and none of them have been this difficult to integrate in.
Any help is much appreciated
Cheers
Gav
EDIT: Here is my full require config:
require.config({
/**
* shims are for 3rd party libraries that have not been written in AMD format.
* shims define AMD modules definitions that get created at runtime.
*/
shim: {
'jqueryUI': { deps: ['jquery'] },
'jqueryAnimateEnhanced': { deps: ['jqueryUI'] },
'jqueryScrollTo': { deps: ['jquery'] },
'touchPunch': { deps: ['jquery'] },
//'Q': { exports: 'Q' },
//'breeze': { deps: ['Q', 'knockout'], exports: 'breeze' },
'path': { exports: 'Path' },
//'signalR': { deps: ['jquery'] },
},
paths: {
//jquery
jquery: '../libs/jquery/jquery-1.7.2.min',
'jquery.adapter': '../libs/jquery/jquery.adapter',
//jquery plugins
horizontalNav: '../libs/jquery/plugins/horizontalNav/jquery.horizontalNav',
jqueryUI: '../libs/jquery/plugins/jquery-ui/jquery-ui-1.9.2.custom',
jqueryAnimateEnhanced: '../libs/jquery/plugins/animate-enhanced/jquery.animate-enhanced',
touchPunch: '../libs/jquery/plugins/touch-punch/jquery.ui.touch-punch.min',
//jqueryScrollTo: '../libs/jquery/plugins/jquery-scrollTo/jquery.scrollTo.min',
//reveal: '../libs/jquery/plugins/reveal/jquery.reveal',
//opentip: '../libs/jquery/plugins/opentip/opentip-jquery',
//RequireJS
domReady: '../libs/require/plugins/domReady',
text: '../libs/require/plugins/text',
async: '../libs/require/plugins/async',
depend: '../libs/require/plugins/depend',
json: '../libs/require/plugins/json',
noext: '../libs/require/plugins/noext',
//coffee-script
'coffee-script': '../libs/coffee/coffee-script',
cs: '../libs/require/plugins/cs',
//Path
path: '../libs/path/path.min',
//Knockout
knockout: '../libs/knockout/knockout-2.2.0',
knockoutTemplateSource: '../libs/knockout/ko.templateSource',
knockoutValidation: '../libs/knockout/ko.validation',
//breeze
Q: '../libs/breeze/q',
breeze: '../libs/breeze/breeze.debug',
//Signals (Observer pattern)
signals: '../libs/signals/signals',
//SignalR - Push notifications
signalR: '../libs/signalR/jquery.signalR-0.5.2.min',
//utils
logger: 'common/logging/logger',
tinycolor: '../libs/tinycolor/tinycolor',
composing: 'common/composition/composing',
//app specific
BaseWidgetViewModel: 'app/view/core/BaseWidgetViewModel',
}
});
Sorry for the (holiday) delay in addressing your question.
I understand and appreciate your goal of eliminating every script tag except the one for RequireJS. This goal is not easily obtained in my experience.
You did uncover a Breeze defect. Breeze internally is referencing a different 'require' function than your application's 'require' function. It doesn't know about the application 'require' function or its configuration. Therefore, when you omit the Q script tag, Breeze cannot find Q ... no matter how well you configure the application 'require'.
We'll have to fix that. I'll add a comment here when we do.
Meanwhile you'll must use a script tag for 'Q' and for 'KO' and you must put those tags above the script tag for RequireJs. Please continue to use require for your application scripts.
Unfortunately, you have other problems that are unrelated to the dual-require-function problem.
First, I think you will always have trouble keeping KO out of the global namespace ... and this has nothing to do with Breeze.
In KO's AMD implementation (at least when I last looked), KO is either in the global namespace or in the require container; never both. Unfortunately, many useful plug-ins (bindingHandlers, debug.helpers) assume it is in the global namespace; you won't be able to use them if you load KO with require.
You can load Knockout in script tags before Require and then shim KO into the Require container during configuration. Your configuration might look like this:
define('knockout', [], function () { return window.ko; });
The jQuery developers realized this would be a problem. Too many good plugins assume jQuery is in the global namespace. Rather than be purists, the jQuery maintainers (correctly in my view) put jQuery in both the Require container and in the global namespace.
Second, many other useful libraries do not support Require. Your code can be written as if these scripts were designed for require. I still think the easiest, clearest way to handle this is specify them in script tags. Then you can add them into the container by defining them with define calls as shown above. With this shimming, your own modules can be consistent in relying on Require for service location and dependency injection of these libraries.
Third, I think you have a bug in your configuration. For reasons that escape me, you decided to refer to the Knockout module as "knockout" in your Require universe.
Fine ... but you shouldn't expect Breeze to anticipate that module name when we fix it to use the application's require container. Breeze will be looking for KnockoutJs under its standard name, "ko". Breeze will not find it under your preferred name, "knockout".
This is not a big deal. If you're wedded to the "knockout" name, you can make the same instance available under both names. I'm pretty sure the following shim line will work when added to your configuration code:
define('ko', ['knockout'], function (ko) { return ko; });
Again ... this will be effective only after we fix our Breeze require bug.
John Papa had to address many similar issues in Code Camper; you might look at what he did there, especially at main.js.
This should be fixed as of Breeze v 1.2.4. We no longer use an internal implementation of "require".
Sounds like your require config is not right. Could you post the code of your require config?
I am curious:
keep my code down to a single script tag
why that?
Edit: Now i get it. Still sounds like the require config is wrong.

Questions about Dojo modules from Dojo newb?

I'm new to Dojo so I don't quite understand all of Dojo's features. Here are some questions, but I'm sure some of them seem really stupid since I don't quite get how Dojo is structured yet:
How do you create multiple modules within a single js file and access the module in the file it's created? Also, how do you access specific modules from a file containing multiple modules?
What is the difference between require and define?
I successfully required a module from a file, but I can't figure out how to get the variables out of the file, how do you do this?
I was looking at how Dojo required its modules and notice that it does a http request for each file, but isn't that really inefficient when you're dealing with lots of modules and/or on a large site, you really would like to minimize the number of http requests necessary? What's the way around this?
Reading through The Dojo Loader will provide answers.
Basically module = file and very often (as a matter of best practice) module = file = class (more precisely public class defined via dojo/_base/declare).
Ad #4: You will need to employ The Dojo Build System, that will resolve all the dependencies and put all your modules into a single file (or more files, this depends on your build profile). Have a look at Dojo Boilerplate project, it may help with building your application.
How do you create multiple modules within a single js file?
While this is possible to do, and is done by the build system when it creates layer files, it is not recommended. Putting multiple modules in a single file means you have to use a different form of define that gives an explicit ID to eahc of them. This is less flexible then having the module IDs automatically derived from the file name and path (this, together with relative paths makes it much easier to move and rename AMD modules, compared to old-style modules)
What is the difference between require and define?
define is a beefed up version of require that defines a new module with its return value.
I successfully required a module from a file, but I can't figure out how to get the variables out of the file.
I am not sure wha toyu mean with this (without giving a concrete runnable example) but all you have to do is create an object to be the value of your module and return it from define. This is not very far off from how you would define modules the old way, with manual "namespaces"
//foo.js
define([], function(){
var M = {}; //the exported stuff will go here
var myVariable = 16; //private function variables are private (like they are everywhere else)
//and cannot be seen from the outside
M.anumber = myvariable + 1; //things on the returned object can be seen from the outside
return M; //people who require this module will get this return value
});
//bar.js
define(['foo'], function(foo){
console.log( foo.anumber );
});
I was looking at how Dojo required its modules and notice that it does a http request for each file...
As phusick pointed out, the build system can be used to bundle all the modules into a single file (giving you the best of both worlds - modularity during development and performance during deployment). It can also do other cool stuff, like bundling CSS and passing the Javascript through a minifier or through the Closure compiler, checking ifdef for creating platform-dependent builds, etc.

Categories