Dear Readers,

Creation - Cocos3d style

I recently got distracted and created my first solar system!

Anyway, I’m so excited and shocked at how easy it was, that I feel the need to share – so read on to find out about Cocos3D and how you can enjoy the act of creation yourself.

[Yes, yes I should be focusing on actually making my game, especially after last time's distraction of Path Finding. This week was I was supposed to be writing the storyline, I'll just have to make a solar-system a vital part of it somehow]

Cocos3D? 

Firstly a quick bit of background – Cocos3D is the next dimensioned version of Cocos2D, the uber-popular game building framework. I have been using Cocos2D for my iphone-dev for about a year and am constantly impressed by how much it can do, and how well it does it.

That said, I had never tried it’s 3D brethren, largely because I was scared, having not touched 3D since messing around with trying to build level’s for Quake over 10 years ago – and struggling. The turning point was this post on the Cocos2d forums which tempted me to into having a go – clearly this shocked me at how easy it was, so I just had to start tinkering…

The Solar System in action:

 

Now, I realise in the history of Solar Systems simulations, this ranks pretty low. However, the fact that you too can have it spinning on your iPad/iPhone (assuming you’re set up for iOS dev) in no-time at all, is what impressed me so much.

The blind leading the blind

So, here we go – Cocos3d the impromptu semi-tutorial based on my few hours of tinkering. What can go wrong. Did I mention I’ve never done a tutorial before. And that I’m new to Cocos3d? Hah, I’m sure it’ll be fine. Right I am assuming you have XCode etc and know a bit about iphone dev. If not look up Ray Wenderlich – his tutorials on the subject are almost universally revered and come back when you know more. For the rest:

Step 1: Get Cocos3D and install http://brenwill.com/cocos3d/ has all the instructions you’ll need.

Step 2: Create new Cocos3D project (File – New Project, find Cocos3D Application template) lets call it “SolarSystem”

Step 3: Collect the files we’ll need – you’ll need to drag them all into your xcode project:

  1. The sphere’s geometry (earth.pod) and it’s texture (earth.png), both are from the post I mentioned before here - many thanks to @renderplace for putting the files up
  2. Planet textures, which I found from this rather helpful site, you’ll see all the low-res pictures are free to use as long as it’s not to make money, so we’re fine here. The images need to be 1024 by 512, which conveniently they all are except for the moon, which you’ll need to resize (just look at it in preview and use Tools->Adjust size, then save)
    1. sunmap.jpg
    2. mercurymap.jpg
    3. venusmap.jpg
    4. mars_1k_color.jpg
    5. moonmap1k.jpg

Step 4: Add some bespoke code (Note 1: the following file names assume you have named the project SolarSystem. Note 2: this was written lazily and quickly, it will almost certainly have memory leaks and poor programming style – you have been warned…)

Firstly find SolarSystemWorld.h, we’re going to add two functions and an tracking array:

1
2
3
4
5
6
7
8
9
@interface SolarSystemWorld : CC3World
{
    NSMutableArray *_planets; //Used to track the planets we add for moving the camera around
}
/** a generic function to add planets */
- (CC3MeshNode*) addPlanetSize: (CGFloat) s name: (NSString*) planetName position: (CC3Vector) p moons: (int) m withTex:(NSString*) tex;
/** a function to change where the camera is looking */
- (void) viewNextPlanet;
@end

Now in SolarSystemWorld.m replace the initializeWorld function with this one, and add the other two as well:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
-(void) initializeWorld {
 
    // Create the camera
    CC3Camera* cam = [CC3Camera nodeWithName: @"Camera"];
    cam.location = cc3v(0.f, 20.f, 60.f);
    [self addChild: cam];
 
    [self addContentFromPODResourceFile: @"earth.pod"];
    [self createGLBuffers];
    [self releaseRedundantData];
 
    //create Sun
    CC3MeshNode *sun = [self addPlanetSize:6.f name:@"Sun" position:cc3v(0.f,0.f,0.f) moons:0 withTex:@"sunmap.jpg"];
    sun.material.emissionColor = ccc4FFromccc4B(ccc4(255, 255, 255, 255));  //make the sun sunny
 
    //Add lighting - first sunlight radiating outwards
    CC3Light *sunlight = [CC3Light nodeWithName:@"SunLight"];
    sunlight.isDirectionalOnly=NO;
    [self addChild:sunlight];
 
    //Add the planets
    _planets = [[NSMutableArray alloc] init];
    [_planets addObject:[self addPlanetSize:0.5 name:@"Mercury" position:cc3v(14.f,0.f,0.f) moons:0 withTex:@"mercurymap.jpg"]];
    [_planets addObject:[self addPlanetSize:0.7 name:@"Venus" position:cc3v(18.f,0.f,0.f) moons:0  withTex:@"venusmap.jpg"]];
    [_planets addObject:[self addPlanetSize:1.f name:@"Earth" position:cc3v(22.f,0.f,0.f) moons:1 withTex:nil]];
    [_planets addObject:[self addPlanetSize:1.1f name:@"Mars" position:cc3v(30.f,0.f,0.f) moons:2 withTex:@"mars_1k_color.jpg"]];
 
    //And make the camera track our Earth node
    CC3Node *earth = (CC3Node*) [self getNodeNamed:@"Earth"];
    cam.target = earth;
    cam.shouldTrackTarget=YES;
}
 
- (CC3MeshNode*) addPlanetSize: (CGFloat) s name: (NSString*) planetName position: (CC3Vector) p moons: (int) m withTex:(NSString*) tex
{
    CC3Node *orbitCentre = [CC3Node node]; //centre of solar system to rotate around
    [self addChild:orbitCentre];             //add this to 3d world
 
    CC3MeshNode *planet = [(CC3MeshNode*)[self getNodeNamed: @"Sphere"] copyWithName:planetName];
    planet.location = p;
    planet.scale = cc3v(s, s, s);
    if (tex!=nil)
    {
        [planet.material removeAllTextures];
        [planet.material addTexture:[CC3Texture textureFromFile:tex]];
    }
    [orbitCentre addChild:planet];           //add this to 3d world as child of spin centre
 
    //Make planet spin & orbit (this condition stops us spinning the sun
    if (p.x>0)
    {
        CGFloat inverseDist = 1000.f/p.x;   //things closer to the sun spin quicker
        CCActionInterval* planetSpin = [CC3RotateBy actionWithDuration: 1.0 rotateBy: cc3v(0.f, (inverseDist)+20.f, 0.f)];
        [planet runAction:[CCRepeatForever actionWithAction:planetSpin]];
        CCActionInterval* planetOrbit = [CC3RotateBy actionWithDuration: 1.0 rotateBy: cc3v(0.f, (inverseDist/10.f)+20.f, 0.f)];
        [orbitCentre runAction:[CCRepeatForever actionWithAction:planetOrbit]];
 
        //add a bunch of moons to this planet
        for (int i=0; i<m; i++)
        {
            CC3Node *spinCentre = [CC3Node nodeWithName:@"spinCentre"];
            CCActionInterval* spinAction = [CC3RotateBy actionWithDuration: 1.f rotateBy: cc3v(0.0, (CCRANDOM_0_1()*30.f)+20.f, 0.0)];
            [spinCentre runAction:[CCRepeatForever actionWithAction:spinAction]];
            [planet addChild:spinCentre];
 
            CC3MeshNode *moon = [(CC3MeshNode*)[self getNodeNamed: @"Sphere"] copy];
            [moon.material removeAllTextures];
            CC3Texture *moonTex = [CC3Texture textureFromFile:@"moonmap1k.jpg"];
            moon.material.texture = moonTex;
            moon.scale = cc3v(0.1f, 0.1f, 0.1f);
            moon.location = cc3v(0.f, 0.f, s+CCRANDOM_0_1());
            [spinCentre addChild:moon];
        }
    }
    return planet;
}
 
- (void) viewNextPlanet
{
/* Ropey camera controls:
 - each call of functions cycles through planets or adopt a zoomed out view
 */
 
    //Get our camera
    CC3Camera *cam = (CC3Camera*) [self getNodeNamed:@"Camera"];
    int viewIndex=0;
    //is the camera looking at something?
    if ([_planets containsObject:cam.target])
    {
        //if so (and should always be) look increment our viewing index
        viewIndex = [_planets indexOfObject:cam.target]+1;
    }
 
    //if out viewing index is not past the end of the planets array we'll look at the next planet
    CC3Node *nextTarget = nil;
    CC3Vector v;
    if (viewIndex<[_planets count])
    {
        //select the planet to focus on
        nextTarget = [_planets objectAtIndex:viewIndex];
 
        //derive a point 5 units directly in towards the sun from the planets current location
        CC3Vector scaledVec = CC3VectorScaleUniform(CC3VectorNormalize(nextTarget.globalLocation), -5.f);
        v = CC3VectorAdd(nextTarget.globalLocation, scaledVec);
    }
 
    // If our view index is outside the planet array we'll look at the sun
    else
    {
        nextTarget = [self getNodeNamed:@"Sun"];
        v = cc3v(0.f, 20.f, 60.f);
    }
 
    //stop camera tracking whilst we move it
    cam.shouldTrackTarget=NO;
 
    //action 1 - pan around to focus on the new planet
    id panCam = [CC3RotateToLookAt actionWithDuration:1.f targetLocation:nextTarget.globalLocation];
 
    //action 2 - set the new planet as a target and start tracking it
    id changeTarget = [CCCallBlock actionWithBlock:^(){
        cam.target = nextTarget;
        cam.shouldTrackTarget=YES;
    }];
 
    //action 3 - move the camera to the point we previously derived near where the planet was
    id moveCam = [CC3MoveTo actionWithDuration:2.f moveTo:v];
 
    //Run the three actions in sequence, it should pan, track then move the camera
    [cam runAction:[CCSequence actions:panCam, changeTarget, moveCam, nil]];
 
}

Finally find SolarSystemLayer.m, we’re going to enable touches so we can do a bit of toggled camera swooping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-(void) initializeControls
{
    self.isTouchEnabled=YES;
}
 
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
    return YES; //makes the layer swallow the touch
}
 
- (void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
    SolarSystemWorld *solsys = (SolarSystemWorld*)self.cc3World;
    [solsys viewNextPlanet];
}

Step 5: Er, that’s it, just build, run and admire. Each touch will swoop the camera around through the preset positions either tracking a planet or giving an overview (in a pretty choppy, shoddy style, but you get the idea).

Now, if even a fool like me can knock something like that out after a few hours, it must be pretty good (in my opinion). True I haven’t done any research into alternatives, I’ve never used Unity and I’m extremely inexperienced, but I still think this is an absolutely cracking framework – major kudos and thanks to the author Mr Bill Hollings.

I hope this inspires others to make far more impressive galactic vistas and, more importantly, to donate towards its development - this is just version 0.6!

Until next time…

Rod