Dear Readers,
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
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