CocosSharp – Implementing a Custom CCNode

This post is part of my Mobile Game Development series. Start at the beginning to catch up. This series was inspired by the things I learned developing a new game for Android and iOS called Mirror Maze.


You can do quite a lot with the node implementations that CocosSharp provides out of the box. However, sometimes you’ll find that you need to do something custom. As a general rule, you’ll want to implement your own custom node any time you start adding several children to a CCNode instance or want a collection of nodes to be considered a logical unit. Implementing your own CCNode is a breeze, since it already provides a ton of functionality that you can leverage right out of the box. As with most things in CocosSharp, though, there are some things you should be aware of to make your life a little easier when you do implement custom nodes.

One reason why you’d want to make your own custom node is that you want to group other nodes together. An example of this from Mirror Maze is each collection of mazes in the level selection menu.

Menu

I developed a simple container UI element, essentially a rectangle with rounded corners, as the background, then added a label toward the top and a bunch of circle buttons below it for choosing which level to jump to. The game contains several of those maze collections, so I wanted to make something that I could re-use.

The custom node I created for that menu is nothing more than a collection of child nodes. However, I can add that menu node to my layout and position it how I like, without having to individually position the background, the header label, and all the buttons. They all move as a unit when I move the menu node.

When I implement a custom node, there are two or three things that I always do. The first two are mandatory and the third is optional, depending on the needs of the node.

Step 1: Constructor

The first thing I do is instantiate any children that will be there for the life of the node. I do this in the constructor of the custom node. If I need to reference any of the children at a later point, for positioning or anything else, I also add a member variable for that child node.

If you have absolute positioning to do for any of these children, meaning positioning that doesn’t depend on anything else, you can set that positioning in the constructor as well. However, at the time that the constructor is ran, the node hasn’t actually been placed into the parent’s scene yet. That is important because that means the node also does not have any size or position associated with it yet.

Step 2: Content Size

The second thing I do, and arguably most important, is to make sure to keep ContentSize updated. ContentSize is the property that tells CocosSharp how big the node actually is. ContentSize factors heavily into positioning and BoundingBox, which I will cover later in this series. It’s important to update ContentSize to reflect the total size of all the children in the node whenever you finish adding children or something that would affect the size changes. ContentSize is of type CCSize, which has Width and Height properties (both floats). This step is one of the things I really wish I knew sooner. You’re welcome.

Step 3: Positioning (Optional)

In a lot of cases, the child nodes I add to a custom nodes need to be placed relative to other child nodes or the parent’s VisibleBounds. VisibleBounds is a property that CCNode provides that returns the bounding box of the visible world. Basically the bounds of what the user can see. In Mirror Maze, I used the VisibleBounds a lot to position children somewhere along the outside of the visible boundary. Since the node hasn’t been placed in the scene yet at the time of that the constructor code is run, this brings us to the third thing I generally do when implementing a custom node: override AddedToScene.

AddedToScene is the method that CCNode will call once the node has actually been added to the scene, which means that it will start to have sizing and positioning available to it. VisibleBounds will always return a default rect before the node is added to the scene, so AddedToScene is the first time you can start to use VisibleBounds.

The above code positions SomeSprite in the bottom-left corner and SomeDrawNode in the bottom-right, then sets the ContentSize to be the full width of the VisibleBounds and the height of SomeSprite (the taller of the two child nodes).

Overriding AddedToScene isn’t always necessary. If you are placing all of your child nodes at specific coordinates that aren’t affected by whether the node has been added to the scene or not, you can skip this step and just stick with the first two steps, putting your positioning in the constructor.

After following the steps that I follow above, you’ll have a usable custom node. If all you want to do is group other nodes together, you’re good to go. If you want to add further functionality, this is where you can go nuts. In my next post, we’ll learn about how positioning works in CocosSharp.

Share

Leave a Reply

Your email address will not be published. Required fields are marked *