Another Crafty Prototype - cocos2d delta & touch input

I built another simple game prototype using Crafty. My goals when building this prototype were:

  • Gain more familiarity with Crafty in the lead up to Ludum Dare
  • Test out animations
  • Load animations from TexturePacker output
  • Test touch input for mobile devices
  • Test cross browser viability

The prototype was a success in regards to all the goals above, but I think the best thing to come out of the prototype was a pile of notes on the differences between Crafty and cocos2d. I'll share some of them here.

    Transitioning From Cocos2d

    When you spend enough time with a particular framework you start to think in its terms. Here are a few points that will give a head start someone moving from cocos2d to Crafty:

    Origin: The origin is in the top-left instead of the bottom-left. This should be familiar to anyone used to web development.

    Anchor Points: The anchor point is in the top-left of a sprite, not at its center. The anchor point is not adjustable. This bothered me enough that I whipped up a little component that adds an adjustable anchor point. There might be a better way to do this, but here is the code I used:

    Asset Pipeline: If you use cocos2d, then you likely use TexturePacker (and if not, what is wrong with you?). You can continue using TexturePacker with Crafty by changing the data format of the output from 'cocos2d' to 'JSON'. I recommend that you also disable 'Allow Rotation' and 'Trim'.

    Flipping: 2D objects have a flip method but once an object is flipped you can't unflip it. I created another component that provides flipX and flipY methods that can be used to flip a sprite back and forth:

    Scaling: I didn't see any methods or properties for scaling a sprite in the documentation or the source. I may write a component and post it in a future post.

    Tags: Tags are a great way to attach extra information to cocos2d nodes without much hassle. Onlarger projects you should probably create components that properly incapsulate the data that you represent with tags, but for a quick prototype tags are priceless. Here is a component for adding tags:

    Gameloop: Most cocos2d games have a gameloop in the form of a scheduled update or step function. To emulate that pattern in Crafty you can use the EnterFrame event. EnterFrame does not pass the time since the last event, so you will need to jury rig your own step event:

    Aside: Touch Input

    Crafty will treat taps as mouse clicks but the event handling code does not properly handle all touch events. This bit of code does convert touch events to mouse events:

    BUT touch events have different data than mouse events. This conversion is not enough for touch events (in particular touchmove) in Safari. This also ignores the fact that multiple touches can be passed in a single event.

    I added a quick hack so that single touches would work as expected:

    More work is needed to add proper multitouch handling.

    The Prototype

    The prototype feels like it could be a fun little game after a serious round of polishing. Caveat: I didn't test this on a wide array of platforms so it may explode on you. And without further ado:

    Your browser does not support iframes.

    Hey Cocos2d Noob - A Letter To Past Me: Part 2

    If you missed it, catch Part 1 here.

    Another week has slipped into the past and I've yet to find a Delorean, an FTL spacecraft, a TARDIS, a wrinkle in spacetime, or any other means of time travel. I guess I am back to leaving notes in the now for alternate me's.

    While working on a project, I tend to use an old fashion notebook in which I keep ideas, todo lists, discovered gotchas, and other scribbled bits of madness. There is something about crossing an item off a list or working through an idea on paper that I enjoy. Call me old school.

    Fortunately for everyone else, this notebook is littered with evidence of my past mistakes and misunderstandings. I took some time to review this catalogue of errors to pull out some of the more helpful pointers for anyone who finds this.

    Yet Another Point

    CGRect

    I covered CGPoints and all the hidden gems in CGPointExtension last time, so I thought it was high time to move on to the second dimension. Really, this is more of an Objective-C slash Apple Library thing than a cocos2d thing, but often both are learned at the same time. Here are a few key helpers, I also suggest you read the docs.

    CGRectMake - Construct a rectangle given the x and y of the origin, the width, and the height.

    CGRectIntersectsRect - Check if two rectangles intersect.

    CGRectContainsPoint - Check if a rectangle contains a point.

    Rectangles are great for collision and hitbox checks that are not part of a physics system. Once you start dialing up the complexity or simulating physics you should use something like chipmunk or box2d.

    Back to My Point - A few forgotten helpers

    In building a game that had a HUD layer and a world layer ("Level") that panned and zoomed, I found myself having to convert coordinates between the two distinct spaces. CCNode provides a few helpers that will save you a ton of conversion math.

    My explanations assumes that the HUD and the Level are both children of the game layer (if you are using the cocos2d Hello World example this would be your HelloWorld layer). It also assume that the HUD has a fixed position and is in the same coordinate space as your HelloWorld layer (or HelloWorld layer equivalent).

    convertToWorldSpace - Converts a point to world space. This means you can take a point in your panned and zoomed Level and figure out the corresponding point on your HUD.

    convertToNodeSpace - Converts a point to node space. You can take a point on the HUD and figure out the corresponding point in the game world. ie [level convertToNodeSpace:targetPosition]

    convertTouchToNodeSpace - Converts a touch to node space. This is extremely useful in figuring out where a touch point corresponds to in the Level.

    Multitouch

    There comes a point in every little games life (well, "every" is hyperbole) where it wants to be touched in two places at once. This usually results in a burst of outrage or a post to the cocos2d forums. Some cocos2d samples start with the single touch delegate enabled, as if rubbing your belly and patting your head at the same time wasn't hard enough. There is actually a good reason for this, it is much easier to handle one touch than a set of them. Anywho… on to the magic:

    Inside your scene's registerWithTouchDispatcher you need to add the standard delegate instead of the targeted one. Edit: The cocos2d templates should all have the standard delegate enabled by default rather than the targeted delegate. In that case skip to the other steps.

    Replace:

     

    [[CCTouchDispatcher sharedDispatcheraddTargetedDelegate:self priority: swallowsTouches:YES]

     

    With:

    [[CCTouchDispatcher sharedDispatcheraddStandardDelegate:self priority:0];;

    If you are currently handling touch begin, moved, and ended events for single touch, then you will have to replace them with the multitouch versions:

    ccTouchBegan:withEvent:event becomes ccTouchesBegan:withEvent:event

    ccTouchMoved:withEvent:event becomes ccTouchesMoved:withEvent:event

    ccTouchEnded:withEvent:event becomes ccTouchesEnded:withEvent:event

    Note that the first argument passed to the multitouch version is a set of touches rather than a single touch.

    After doing all this work you might get excited that you have your fancy new multitouch code working, only to be disappointed when you realize that it is still only handling a single touch! One last change to make: In your app delegate be sure to enable multitouch. Somewhere after you initialize and set your EAGLView, do this:

    [glView setMultipleTouchEnabled:YES];

    Caveat: If you are using gesture recognizers then they may be swallowing additional touches. If you have all this code in place, are using gesture recognizers, and your multitouch code isn't getting multiple touches, then the gestures are the likely culprit.

    Edit: As Gaminghorror points out you need to clean up your delegates as well to prevent crashes and memory leaks with:

    [[CCTouchDispatcher sharedDispatcher] removeDelegate:self];

    And if you are looking for other material to learn cocos2d go check you his site: learn-cocos2d.com.

    CCParticleSystem & Particle Designer

    Rolling my own particle system is one of those bad habits that I have. There comes a point when working with a new framework and/or language that I decide to build a particle system. It is usually a good exercise in building something more complex than a Hello World application. Needless to say, I did this for cocos2d. Also needless to say, it was a lot of fun but did not move the ball forward on actually getting a game built.

    Cocos2d comes with a decent particle system, CCParticleSystem. Go read the documentation: http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:particles Welcome back! In all seriousness, odds are that this will meet all of your needs, it will be updated and maintained by other people, and if you really need to do something magical it is open source.

    I blabbered on about the glorious merits of Texture Packer last time so I figured I would pick a new tool this time. Particle Designer, which you read all about at that link up there is an excellent tool. True to my nature, I started rolling my own and then I bought Particle Designer when I realized I had games to build. It doesn't support everything that CCParticleSystem does and there are some minor differences, but they are pretty well documented. I recommend buying Particle Designer after reading the CCParticleSystem docs. The source code for CCParticleSystem is also highly informative.

    Protip: Don't be like me, read the docs and learn about the tools available.

    Protip 2: Reading other people's source code will learn you good.

    Next Time

    I am not going to bother pretending I have a plan. I have a near limitless bag of errors for my past self to avoid, I will attempt to dig out the most useful stuff and share it. Worst case scenario, I will try something new, make lots of mistakes, and share those.

    Editor's Notes:

    1. I think there is a word I forgot to use in this part of the series.
    2. Next time will likely be a few weeks away. I plan on covering a few other hopefully useful things first.
    3. There has been an underuse of taco references lately, please correct. -ed