Avoiding parallel arrays with Objective-C and C++ -


some context: writing 2d destructible terrain library cocos2d/box2d involves hybrid of c++ , objective-c. encountered situation has stumped me can't think of solution doesn't involve parallel arrays.

in order physics boundaries defined around terrain, initial trace must made of terrain border. make function call trace isolated bodies of pixels within texture , cache them. creates following data structures

  • an nsmutabledictionary "borderpixels" key being equal cgpoint wrapped in nsvalue equal pixel's unique location. holds traced pixels.
  • a circular linked lists terpixels pointing next neighbor pixel
  • an nsmutablearray "tracebodypoints" holds single terpixel representing 'start' point of terrain body. store terpixel *'s here need trace physics body. so, if terrain body has been modified, insert individual terpixel * modified body array. can reference each of these , traverse linked list trace physics body.

here code paint better picture of situation:

-(void)traverseboundarypoints:(cgpoint)startpt {  if (!borderpixels) borderpixels = [[nsmutabledictionary alloc] init]; // temp if (!tracebodypoints) tracebodypoints = [[nsmutablearray alloc] init]; // temp  terpixel * start = [[terpixel alloc] initwithcoords:startpt.x ypt:startpt.y prevx:-1 prevy:0]; terpixel * next = start; //cclog(@"start of traverseboundary next.x , next.y %d, %d", next.x, next.y); terpixel * old;   while (true) {      old = next;     next = [self findnextboundarypixel:next];     [next setnsp:[old subterpixel:next]];     old.nextborderpixel = next;      if (next.x == start.x && next.y == start.y) {         cclog(@"success :: start = next");         next.nextborderpixel = start; // make linked list circular         nsvalue * pixlocval = [next getasvaluewithpoint];         [borderpixels setobject:next forkey:pixlocval];         // add pixel tracepoints array traversed/traced later         [tracebodypoints addobject:start];         break;     } // end if      // connect linked list components      nsvalue * pixlocval = [next getasvaluewithpoint];     [borderpixels setobject:next forkey:pixlocval];  } // end while } // end traverse function 

here can't find solution. need relate each terpixel * in tracebodypoints array box2d b2body created , added physics world. in library, each isolated body of pixels within texture corresponds box2d body. so, when event happens destroys chunk of terrain, need destroy body associated the destroyed pixels , retrace altered bodies. means need way associate given terpixel * box2d body *.

in objective-c arc, knowledge, cannot include c++ objects/pointers in objective-c containers without bridge casting void *'s. problem these operations need incredibly performant , engaging in bridge casting costly. also, don't want include pointer box2d body in every single terpixel. nightmare ensure there no dangling pointers , require pointless iteration nil pointers out.

here logic creating physics boundaries

-(void)createphysicsboundaries {  if ([self->tracebodypoints count] == 0)  {     cclog(@"createphysicsboundaries-> no bodies trace");     return; }  // need logic here delete altered bodies // need delete existing box2d body , re-trace new 1  // each body has been altered, traverse linked list trace body (terpixel * startpixel in self->tracebodypoints) {     terpixel * tracepixel = startpixel.nextborderpixel;      b2bodydef tdef;     tdef.position.set(0, 0);     b2body * b = self->world->createbody(&tdef);     self->groundbodies->push_back(b);     b->setuserdata((__bridge void*) self);     b2edgeshape edgeshape;      cclog(@"startpixel %d, %d", startpixel.x, startpixel.y);     while (tracepixel != startpixel) {         b2vec2 start = b2vec2(tracepixel.x/ptm_ratio, tracepixel.y/ptm_ratio);         //cclog(@"tracepixel before %d, %d", tracepixel.x, tracepixel.y);         tracepixel = tracepixel.nextborderpixel;         //cclog(@"tracepixel after %d, %d", tracepixel.x, tracepixel.y);         b2vec2 end = b2vec2(tracepixel.x/ptm_ratio, tracepixel.y/ptm_ratio);         edgeshape.set(start,end);         b->createfixture(&edgeshape, 0);     } // end while  } // end } // end createphysicsboundaries 

hopefully makes sense. if need visual of happening, here video. http://www.youtube.com/watch?v=iusgjylr6e0&feature=youtu.be green boundaries physics boundaries.

engaging in bridge casting costly

says who? it's still cast , free/negligible. bridge transfer or retain cast adds corresponding reference counting method calls, don't need here.

the solution problem simple. have tracebodypoints array containing instances of terpixel class.

you need wrapper class array, let's call terrainblob. terrainblob class contains property nsmutablearray tracebodypoints , property b2body:

@interface terrainblob : nsobject @property nsmutablearray* tracebodypoints; @property b2body* body; @end 

you can improve terpixel contain reference terrainblob, must weak avoid retain cycles. way each pixel can access b2body. add method in terrainblob adds terpixel , sets terrainblob property convenience.

@interface terpixel : nsobject ... @property (weak) terrainblob* terrainblob; @end 

you can access b2body within terpixel:

b2body* body = _terrainblob.body; if (body) {     // body } 

now need update body in 1 location, , each terpixel needs check whether body nil before using it.

finally, worth mentioning destructible terrain on pixel basis overkill, on retina devices. unless you're doing that, creating approximate line shapes spanning multiple pixels because don't need pixel-perfect accuracy physics simulation.


Comments