Skip to content
mucaho edited this page May 4, 2017 · 22 revisions

This is a draft FAQ that could eventually be put at craftyjs.com

This is my first time ever using javascript. Can I do a project using Crafty?

Sure! Doing a project is a great way to learn a new skill. However, you should also work through a javascript course or tutorial to learn how javascript works in general (syntax, variables, functions). A good course will also show you how to debug javascript. Finally, keep your project as simple as possible when you're first learning.

How do events (bind, trigger) work?

There are two types of event bindings:

  • Bindings "owned" by a certain entity (the usual case). These are created by some_entity.bind("PlayerDeath", some_function)
  • Global bindings (less common). These are created by Crafty.bind("PlayerDeath", some_function)

Then there are two types of event triggers:

  • An entity-specific event trigger (the usual case). You would run some_entity.trigger("PlayerDeath"). It runs the functions bound to PlayerDeath, but ONLY those owned by the same entity, i.e. some_entity.
  • A global event trigger (less common). You would run Crafty.trigger("PlayerDeath"). It runs EVERYTHING bound to PlayerDeath, no matter what entity owns it, and also including the global events.

For debugging, starting in version 0.6.0, you can run the command console.log(Crafty.debug('handlers')); to see the internal object that stores all the information about which functions are bound to which events.

On an HTML5 canvas, I can draw arbitrary lines, shapes, gradients, etc. How do I do that in Crafty?

If you are always drawing the same image, it is usually easier to just load the image file and use the Image component or Crafty.sprite. But if the image changes in too many ways to pre-load, you can do custom canvas drawing. You should make your own component with the custom drawing routine bound to the "Draw" event. Here is a simple example to use as a template. Let's call it "DiagonalLine":

Crafty.c("DiagonalLine", {
    init: function () {
        this.requires("2D, Canvas");
        this.bind("Draw", this._draw_me);
        this.ready = true;
    },
    _draw_me: function (e) {
        var ctx = e.ctx;
        ctx.lineWidth = 2;
        ctx.strokeStyle = "green";
        ctx.beginPath();
        ctx.moveTo(e.pos._x, e.pos._y);
        ctx.lineTo(e.pos._x + 5, e.pos._y + 7);
        ctx.stroke();
    }
});

Crafty.e("DiagonalLine").attr({x: 10, y: 20, w: 5, h: 7});

(The last line is just an example: It draws a diagonal line from (10,20) to (15, 27).)

Keep in mind:

  • Whenever the appearance (shape, color, position, etc.) of the drawing changes, be sure to run this.trigger("Change"). (Or "Invalidate" from version 0.6.2 on). Otherwise Crafty does not realize that it needs to redraw. Changing this.x or this.y triggers Change automatically, but other kinds of changes may not.
  • An entity with this component needs to have the 2D and Canvas components too, and it needs to have x, y, w, h properties that define an invisible rectangular box which is as big or bigger than the drawing. This is important - it communicates to Crafty where the entity is, so that Crafty can correctly redraw only the parts of the canvas that need to be updated. (If you want, you can make the invisible rectangular box the size of the whole canvas; the only disadvantage of doing this is that it will force Crafty to redraw more entities more often, so the game may run slower.)
  • Nothing will draw until you set this.ready = true;
  • You should think of e.pos._x as a synonym of this._x, and e.pos._y as a synonym of this._y, but with the special advantage that if you rotate or flip the entity (using the normal Crafty commands, e.g. this.rotation = 90;, this.flip('X');, this.origin('center');, etc.), your drawing will rotate or flip in the correct way.

I have an idea to improve Crafty -- either changing the code or improving a description on the documentation pages -- What is the next step?

The easiest way to proceed is to write an email to the mailing list, or open a "New Issue" on the github repository. Someone else can change the code for you.

Alternatively, if you already know how to use git, you can change the code yourself and then submit a pull request to the "develop" branch of the github repository. Before you submit the pull request, you should make sure that the change actually works, by re-building and testing the crafty.js file and/or the API documentation pages. See instructions here.

Where can I find more help?

Please search the mailing list to see if anyone has already asked the same question that you have. You can join and post to the mailing list. If your question is about code that behaves wrong or in a funny way, it is helpful if you can capture the issue in a small jsfiddle demonstration. If you include the link in your email, everyone will be able to understand your question very easily and you will get a better answer.

How can I use WebFonts?

    <html>
      <head><meta charset="UTF-8"> </head>
      <body>
        <script src="https://rawgithub.com/craftyjs/Crafty/release/dist/crafty-min.js"></script>
        <script>
        // Init Crafty:
        Crafty.init();

        WebFontConfig = {
            google: { families: [ 'Scheherazade::arabic,latin', 'Indie+Flower::latin' ] },
            /* code to execute once all font families are loaded */
            active: function() {
              Crafty.scene("main");
            }
          };
          (function() {
            var wf = document.createElement('script');
            wf.type = 'text/javascript';
            wf.async = 'true';
            wf.src = ('https:' == document.location.protocol ? 'https' : 'http') +
              '://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
            var s = document.getElementsByTagName('script')[0];
            s.parentNode.insertBefore(wf, s);
          })();


        Crafty.scene("main", function() {
            Crafty.e("2D, Canvas, Text")
                .attr({x: 100,y: 60})
                .text("Hello, العربية!").textFont("size", '72px').textFont("family", 'Indie Flower');
        });
        </script>
      </body>
    </html>

Why are my bullets passing through other entities without registering hits?

This phenomen is called "tunneling", which happens when fast moving, small entities pass through other thin entities. In short, the problem is that the entity moves so fast that it never touches another entity and just jumps over it between two consecutive frames, as if no collision occurred.
This is usually circumvented by performing continuous collision detection, as described in more detail here.
However, you can approximate the CCD by considering the area the entity has skipped between frames (referred to as ccdbr). Using this .ccdbr()you can perform a collision search. Since it's an approximation you have to choose the appropriate entity hit among multiple entities returned from such a search - in this case you would probably choose the nearest entity to the bullet's position from the previous frame. Take a look at the Supportable component implementation for example.

How can I use custom GUI elements alongside Crafty?

Add GUI elements outside Crafty's stage

All Crafty's visible entities are placed inside Crafty's stage, more specifically they are placed as children of Crafty's stage DOM element which defaults to <div id='cr-stage' />. You can add your own custom DOM elements alongside it to the <body />:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>My CraftyJs Game</title>
    <script src="crafty.js"></script>
  </head>
  <body>
    <div id="header">My custom header</div>

    <div id="content">
      <div><p>My game description</p></div>
      <div id="cr-stage"></div>
    </div>

    <div id="footer">My custom footer</div>

    <script>
      window.addEventListener('load', function() {
        // will init the game with specified dimensions in the #cr-stage div
        Crafty.init(600, 300, document.getElementById('cr-stage'));

        // code your game
        ...
      }, false);
    </script>
  </body>
</html>

Note: If you change the DOM structure dynamically after Crafty is initialized (by adding / removing, resizing or moving DOM elements), you need to tell Crafty that the DOM has changed. Call Crafty.viewport.reload() every time the DOM changes in that case.

Add GUI elements on top of Crafty's stage

There are several alternatives on how to add GUI elements that work on top of a Crafty game. Some of them are:

Use basic modal dialogs

Treat custom GUI elements as Crafty entities

A dedicated Crafty entity can contain your custom HTML code. This entity behaves just like a normal entity and can be drag & dropped, moved by input controls or shifted out-of-view by the viewport.
In many cases you want to have static GUI elements, so you would probably add the entity to a static UI layer.
You can hook up native DOM events to trigger Crafty specific code or vice versa.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>My CraftyJs Game</title>
    <script src="crafty.js"></script>
  </head>
  <body>
    <div id="cr-stage"></div>
    <script>
      window.addEventListener('load', function() {
        // will init the game with specified dimensions in the #cr-stage div
        Crafty.init(600, 300, document.getElementById('cr-stage'));

        // Define a UI layer that is completely static and sits above the other layers
        Crafty.createLayer("UI", "DOM", {
          xResponse: 0, yResponse:0, scaleResponse:0, z: 50
        });

        // Some text that displays current button clicked, sits in the UI layer
        Crafty.e("2D, UI, Text")
          .textColor("black")
          .textFont({size: '20px', family:'Arial'})
          .attr({x: 20, y: 50, w: 200})
          .text("No button clicked.")
          // Listen to Crafty event 'ButtonClick' and change text accordingly
          .bind("ButtonClick", function(buttonName) {
            this.text("Clicked " + buttonName + ".");
          });

        // Custom GUI elements that sit in the UI layer, in this case clickable buttons
        Crafty.e("2D, UI, HTML")
          .attr({x: 20, y: 20, w: 200, h: 20})
          .append(
            "<button class='button' name='Button 1'>Button 1</button>" +
            "<button class='button' name='Button 2'>Button 2</button>"
          );
        // Listen to DOM event 'click' on buttons, then trigger Crafty event 'ButtonClick'
        var buttons = document.getElementsByClassName('button');
        for (var i = 0, l = buttons.length; i < l; ++i) {
          buttons[i].onclick = function(evt) {
            Crafty.trigger("ButtonClick", evt.target.name);
          };
        }

      }, false);
    </script>
  </body>
</html>

Add custom GUI elements as children of Crafty's stage

If you need more flexibility than in the previously described approaches, you can inject arbitrary HTML/CSS/JS into custom children of Crafty's stage DOM element.
Crafty code can modify the custom GUI elements or vice versa.
Note: If you change the DOM structure dynamically after Crafty is initialized (by adding / removing, resizing or moving DOM elements), you need to tell Crafty that the DOM has changed. Call Crafty.viewport.reload() every time the DOM changes in that case.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>My CraftyJs Game</title>
    <script src="crafty.js"></script>
    <!-- CSS for GUI: static main container and contained text component -->
    <style>
      #gui {
        position: relative;
        text-align: center;
        z-index: 6000000;
      }
      #scoreText {
        float: left;
        color: blue;
        font-size: x-large;
      }
    </style>
  </head>
  <body>
    <div id="cr-stage"></div>
    <script>
      window.addEventListener('load', function() {
        // will init the game with specified dimensions in the #cr-stage div
        Crafty.init(600, 300, document.getElementById('cr-stage'));

        // green box which will go out-of-view with elapsed time
        Crafty.e("2D, Canvas, Color, green_box")
          .attr({x: 0, y: 0, w: 50, h: 50})
          .color("rgb(0,255,0)");

        // main gui component, put all your static gui stuff in here
        var gui = document.createElement("div");
        gui.setAttribute("id", "gui"); // set id!

        // one of the gui components: score text
        var scoreText = document.createElement("div");
        scoreText.setAttribute("id", "scoreText"); // set id!
        scoreText.appendChild(document.createTextNode("0"));
        Crafty.bind("Score", function(newValue) {
          // modify external gui component in response to Crafty event
          scoreText.innerHTML = newValue;
        });

        // append external gui components - requires viewport reload!
        gui.appendChild(scoreText);
        Crafty.stage.elem.appendChild(gui);
        Crafty.viewport.reload();

        Crafty.bind("EnterFrame", function() {
          Crafty.viewport.x += 1; // all entites will move but gui won't
          Crafty.trigger("Score", Crafty.viewport.x);
        });

      }, false);
    </script>
  </body>
</html>