Welcome to iPhoneGameTutorials.com! Check out our Tutorials page for a list of everything we've written
so far, and drop by the Forums to let us know what you would like to see written about on the site.

How To Build a Tower Defense Game for the iPhone – Part 3 – Rotation and Realism.

Welcome to part 3 of the tower defense series – Today we are going to be delving into the fine art of realism. Nothing ruins the immersion of games like something that doesn’t make sense in the real world (unless that’s what your game is completely about. Since most tower defense games are rooted in the real world, we need to make sure that the items in the game act like real object. If you haven’t read “Cocos2d Game Tutorial – How To Build a Tower Defense Game for the iPhone – Part 1 – Creep Waves!“, you may want to review it before you continue on.

We’re doing a two fold thing, in this tutorial – We’re going to bring Part 1 and Part 2 together and add realism by adding some rotation and targeting to our towers and creeps.

  • Towers need to be able to target the nearest creep
  • Towers need to face in the direction of the creep they’re targeting
  • Creeps need to rotate when they move in a specific direction

Source code after the break.

Here is the source code: Tower Defense Part 3. Once again, we’re using the Cocos2d framework which you can download here!

As game developers we have a tough job ahead of us too, making a game that also engages the player. However, we don’t want to be too bogged down with the tedious stuff to make it real, making things real is expensive in time and energy. So we do what true game developer do – we fake it. To this end we’ll start with the towers first, by adding in code that will track the closest creep.

- (Creep *)getClosestTarget {
	Creep *closestCreep = nil;
	double maxDistant = 99999;

	DataModel *m = [DataModel getModel];

	for (CCSprite *target in m._targets) {
		Creep *creep = (Creep *)target;
		double curDistance = ccpDistance(self.position, creep.position);

		if (curDistance < maxDistant) {
			closestCreep = creep;
			maxDistant = curDistance;
		}
	}
	if (maxDistant < self.range)
		return closestCreep;
	return nil;
}

This code is found in Tower.m and uses the DataModel class to search through all available creeps on the map and see which one is close by comparing their distances. For each creep we look through we check to see if it's distance is less than the one we have stored before. By default we pick a distance of 99999 which is way larger than any distance to any creep once they start coming out.

The code we use to rotate the towers around uses a little bit of math, but if you think about it in common sense terms, the logic should make sense.

-(void)towerLogic:(ccTime)dt {

	self.target = [self getClosestTarget];

	if (self.target != nil) {

		// Rotate player to face shooting direction
		CGPoint shootVector = ccpSub(self.target.position, self.position);
		CGFloat shootAngle = ccpToAngle(shootVector);
		CGFloat cocosAngle = CC_RADIANS_TO_DEGREES(-1 * shootAngle);

		float rotateSpeed = 0.5 / M_PI;
		float rotateDuration = fabs(shootAngle * rotateSpeed);    

		[self runAction:[CCSequence actions:
							[CCRotateTo actionWithDuration:rotateDuration angle:cocosAngle],
							nil]];
	}
}

You'll also note that the code for both the tower and creep logic is similar, that is because the math is very similar. So lets kill two birds with one stone and explain both creep and tower looking logic at the same time.

-(void)creepLogic:(ccTime)dt {

	// Rotate creep to face next waypoint
	WayPoint *waypoint = [self getCurrentWaypoint ];

	CGPoint waypointVector = ccpSub(waypoint.position, self.position);
	CGFloat waypointAngle = ccpToAngle(waypointVector);
	CGFloat cocosAngle = CC_RADIANS_TO_DEGREES(-1 * waypointAngle);

	float rotateSpeed = 0.5 / M_PI;
	float rotateDuration = fabs(waypointAngle * rotateSpeed);    

	[self runAction:[CCSequence actions:
					 [CCRotateTo actionWithDuration:rotateDuration angle:cocosAngle],
					 nil]];
}

We're looking to find out the rotation we'll need from the current direction our tower and/or creep is facing and the position of the point of interest (say a waypoint if your a creep or a creep if you'e a tower). To this end we need to first figure out the direction vector between our current point and that position. We then use a handy cocos2d function that will return the arc tan angle based off the "shootVector" - The problem is that this is in radians and we want the degrees. So we need a simple "Radian" to "degrees" conversion to get that angle.

Finally, we normally consider angles from 0 to 359, but cocos2d handles rotation from 0 to 180 and 0 to -180.

That's it - Pretty straight forward when you think about it... This was a short tutorial, but a key one - next time I'm going to add tower projectiles and finally kill us some creeps.

Till then!

Walter Reid is a game development professional focusing on play, interaction design, community building and mobile platforms. He has spent the last 12 years building products that combine interactive platforms with the socialization strengths of the web in unique and engaging ways. He is the co-founder of Goofball Games a mobile game development studio and currently resides in New York City.
Walter Reid
View all posts by Walter Reid
Walters website
Share this:
Share this page via Email Share this page via Stumble Upon Share this page via Digg this Share this page via Facebook Share this page via Twitter

12 Responses to “How To Build a Tower Defense Game for the iPhone – Part 3 – Rotation and Realism.”


  • tutorial is coming along great, thanks again. quick Q, the creeps (since moveDuration is a fixed value) move at different speeds from point to point depending on how far the next point is. any way to make them move at the same speed regardless?

    • Hi Thom,

      Paul mentioned a way that gives you the same speed each time in the comments of part 2 – It is based on the Tom the Turret example that is packaged with cocos2d’s examples. I didn’t include it for two reasons – I wanted to keep the tutorials available to the largest possible audience so i opted against explaining the CW and CCW differences in cocos2d when making rotation changes. Second, all the code coding is done for the most part and I didn’t want to have to change the code in 9 different zip files :D Here is the code if you include the more stable rotation:

      In Tower.m

      -(void)towerLogic:(ccTime)dt {
      	self.target = [self getClosestTarget];
      	if (self.target != nil) {
      		CGPoint shootVector = ccpSub(self.target.position, self.position);
      		CGFloat shootAngle = ccpToAngle(shootVector);
      		CGFloat cocosAngle = CC_RADIANS_TO_DEGREES(-1 * shootAngle);
      
      		CGFloat curAngle = self.rotation;
      		CGFloat rotateDiff = cocosAngle - curAngle;
      		if (rotateDiff > 180)
      			rotateDiff -= 360;
      		if (rotateDiff < -180)
      			rotateDiff += 360;
      		CGFloat rotateSpeed = 0.5 / 180; // Would take 0.5 seconds to rotate half a circle
      		CGFloat rotateDuration = fabs(rotateDiff * rotateSpeed);
      
      		[self runAction:[CCSequence actions:
      							[CCRotateTo actionWithDuration:rotateDuration angle:cocosAngle],
      							nil]];
      	}
      }
      

      And the code would look the same for creeps as well, just remove the if case for (target != nil) and get the current waypoint position. I hope it helps!

  • Actually I think what Thom means is this:

    if you check the speed of creeps, before they reach to the last turn which is “turn to right”, you can see that they low their speed. Here is the code to make creeps move with a same speed all the way:

    In TutorialScene.m :

    -(void)addTarget {

    DataModel *m = [DataModel getModel];
    Wave * wave = [self getCurrentWave];

    wave.totalCreeps–;
    if (wave.totalCreeps < 0) {
    return; //[self getNextWave];
    }

    Creep *target = nil;
    if ((arc4random() % 2) == 0) {
    target = [FastRedCreep creep];
    } else {
    target = [StrongGreenCreep creep];
    }

    WayPoint *waypoint = [target getCurrentWaypoint ];
    target.position = waypoint.position;
    waypoint = [target getNextWaypoint ];

    [self addChild:target z:1];

    float distance = ccpDistance(target.position,waypoint.position);
    float moveDuration = (distance*target.moveDuration)/400.0;
    id actionMove = [CCMoveTo actionWithDuration:moveDuration position:waypoint.position];
    id actionMoveDone = [CCCallFuncN actionWithTarget:self selector:@selector(FollowPath:)];
    [target runAction:[CCSequence actions:actionMove,actionMoveDone, nil]];

    // Add to targets array
    target.tag = 1;
    [m._targets addObject:target];

    }

    -(void)FollowPath:(id)sender {

    Creep *creep = (Creep *)sender;
    WayPoint * waypoint = [creep getNextWaypoint];

    float distance = ccpDistance(creep.position,waypoint.position);
    if (distance == 0) {
    return;
    }

    float moveDuration = (distance*creep.moveDuration)/400.0;
    id actionMove = [CCMoveTo actionWithDuration:moveDuration position:waypoint.position];
    id actionMoveDone = [CCCallFuncN actionWithTarget:self selector:@selector(FollowPath:)];
    [creep stopAllActions];
    [creep runAction:[CCSequence actions:actionMove,actionMoveDone, nil]];
    }
    ==============================================
    in function addTarget what I've done is that I used the Velocity Formula which is "T(time) = D(distance) / V(Velocity)"
    so I got the length of the distance between current waypoint and the next waypoint by using ccpDistance… in here targer.position is the location of current waypoint and waypoint.position is the location for the next waypoint.
    now we've got the distance between two points so multiply by the speed of each creep and divide by 400.0 which is our velocity.
    btw we've got a recursive function called FollowPath. all we've to do is to change the moveDuration with the new formula and put a little "if" at the beginning of this function which means if any creep reached to the last waypoint (and stopped) , terminate the function so the creeps who reached to the last waypoint are not supposed to call this function.
    . . . still curious about the "shooting tower" code soon . . . :(

    • Good one Paul! I was trying to do the same thing, and was calling -getCurrentWaypoint from within -followPath. Didn’t see the recursive call which (I think) is why the creeps would stop as soon as the first one made it to the end.

  • Thank you Paul, that’s exactly what I meant. Works great! and thanks again tutor, your tutorials are awesome :D

  • I just noticed something about creepLogin…..if we change:

    CGPoint waypointVector = ccpSub(waypoint.position, self.position);

    ->

    CGPoint waypointVector = ccpSub(self.position, waypoint.position);

    we wont have that small rotation at the first moment when creeps show up….!!!

  • I have one question, in the distance calculation function isn’t it way better to just do the calculation without the expensive function calls to ccpDistance wich also uses more expensive sqrt? Especiall when it comes to expanding later on and having more towers and/or more creeps this is a big performance point. Especially if we look at the hardware.

Leave a Reply

Notify me of followup comments via e-mail. You can also subscribe without commenting.

Tip: When posting code, put the following around your code for a better view:
<pre class="brush:c++">your code here</pre>




Switch to our mobile site