Friday, July 17, 2015

A Tale Of Two Constructors

Or - Integrating cocos2d-js inheritance with native coffeescript classes. Nah, that just sounds boring.

Here is the thing. Many existing script modules use a simple inheritance model much like the one described by John Resig. That being the case, I can simply extend framework classes using coffeescript's extend keyword:

class Game extends Phaser.State

class Scores extends Backbone.Model

This works because coffeescript uses the same basic model. But try that with cocos2s-js:

class Game extends cc.Layer

Sure, it will compile. But when I run the app, I just see a black screen, and there are no errors showing up in the console. This is because Cocos-js uses a different inheritance model, with it's own constructor and super methods. Instead of extending your object with a superclass, cocos requires you to pass in a prototype object that is used as a mixin to dynamically build a new class:

klass = cc.Layer.extend(Game::)

You can also use a new Game object instead of the prototype. I use a factory method:

class Game
  @create:() -> new (cc.Layer.extend(new Game()))

and then I have to do is remember to call Game.create() instead of new Game()

So where are the 2 constructors? You said there would be 2 constructors.

Suppose we have this class to define the main menu:

class Menu

  @create: (args...) => new (cc.Layer.extend(new @(args...)))

  user: ''
  debug: false

  constructor: (options) ->
    @user = options.user
    @debug = options.debug

  ctor: ->
    @_super()
    quitNormal = new cc.Sprite(res.quit_png, cc.rect(0, 0, 64, 64))
    quitSelected = new cc.Sprite(res.quit_png, cc.rect(64, 0, 64, 64))
    quitDisabled = new cc.Sprite(res.quit_png, cc.rect(128, 0, 64, 64))
    quit = new cc.MenuItemSprite(quitNormal, quitSelected, quitDisabled, @onQuit)
    quitMenu = new cc.Menu(quit)
    {width, height} = cc.director.getWinSize()
    backMenu.setPosition(cc.p(width - 40, height - 10))
    @addChild(backMenu)
    true

  onQuit: =>
    cc.log 'Bye'


Then we invoke it in a scene.

  scene = new cc.Scene()
  scene.addChild(Menu.create(debug:true, user:'Norville Rogers'))
  cc.director.runScene(scene)


  • The first, native constructor is called by new @(args...). 
  • The second constructor, 'ctor' gets called later by new (cc.Layer.extend())


cc.log('Name:', level.name)

Prints to the console:

 Name: Norville Rogers

No comments:

Post a Comment