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 – 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 here - many thanks to @renderplace for putting the files up

  • 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