Rotating Turrets: How To Make A Simple iPhone Game with Cocos2D Part 2

Like this post? Follow me on Twitter!

Let's Rotate This Turret!

There’s been a surprising amount of interest in the post on How To Make a Simple iPhone Game with Cocos2D – and several of you guys have asked for some more in this series!

Specifically, some of you asked for a tutorial on how to rotate a turret to face the shooting direction. This is a common requirement for a lot of games – including one of my favorite genres, tower defense!

So in this tutorial we’ll cover how do exactly that, and add a rotating turret into the simple game. Special thanks goes to Jason and Robert for suggesting this tutorial!

Getting Set Up

If you have followed along with the last tutorial, you can continue with the project exactly how we left off. If not, just download the code from the last tutorial and let’s start from there.

Next, download the new player sprite and projectile sprite images, add them into your project, and delete the old Player.png and Projectile.png from your project. Then modify the lines of code that create each sprite to read as follows:

// In the init method
CCSprite *player = [CCSprite spriteWithFile:@"Player2.png"];
// In the ccTouchesEnded method
CCSprite *projectile = [CCSprite spriteWithFile:@"Projectile2.png"];

Note that this time, we don’t bother specifying the width and height of our sprites and let Cocos2D handle it for us instead.

Compile and run your project, and if all looks well you should see a turret shooting bullets. However, it doesn’t look right because the turret doesn’t rotate to face where it’s shooting – so let’s fix that!

Rotating To Shoot

Before we can rotate the turret, we first need to store a reference to our Player sprite so we can rotate it later on. Open up HelloWorldScene.h and modify the class to include the following member variable:

CCSprite *_player;

Then modify the code in the init method that adds the player object to the layer as follows:

_player = [[CCSprite spriteWithFile:@"Player2.png"] retain];
_player.position = ccp(_player.contentSize.width/2, winSize.height/2);
[self addChild:_player];

And finally let’s add the cleanup code in dealloc before we forget:

[_player release];
_player = nil;

Ok, now that we’ve got a reference to our player object, let’s rotate it! To rotate it, we first need to figure out the angle that we need to rotate it to.

To figure this out, think back to high school trigonometry. Remember the mnemonic SOH CAH TOA? That helps us remember that the Tangent of an angle is equal to the Opposite over the Adjacent. This picture helps explain:

As shown above, the angle we want to rotate is equal to the arctangent of the Y offset divided by the X offset.

However, there are two things we need to keep in mind. First, when we compute arctangent(offY / offX), the result will be in radians, while Cocos2D deals with degrees. Luckily, Cocos2D provides an easy to use conversion macro we can use.

Secondly, while we’d normally consider the angle in the picture above positive angle (of around 20°), in Cocos2D rotations are positive going clockwise (not counterclockwise), as shown in the following picture:

So to point in the right direction, we’ll need to multiply our result by negative 1. So for exaple, if we multiplied the angle in the picture above by negative 1, we’d get -20°, which would represent a counterclockwise rotation of 20°.

Ok enough talk, let’s put it into code! Add the following code inside ccTouchesEnded, right before you call runAction on the projectile:

// Determine angle to face
float angleRadians = atanf((float)offRealY / (float)offRealX);
float angleDegrees = CC_RADIANS_TO_DEGREES(angleRadians);
float cocosAngle = -1 * angleDegrees;
_player.rotation = cocosAngle;

Compile and run the project and the turret should now turn to face the direction it’s shooting!

Rotate Then Shoot

It’s pretty good so far but is a bit odd because the turret just jumps to shoot in a particular direction rather than smoothly flowing. We can fix this, but it will require a little refactoring.

First open up HelloWorldScene.h and add the following member variables to your class:

CCSprite *_nextProjectile;

Then modify your ccTouchesEnded and add a new method named finishShoot so it looks like the following:

- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {if (_nextProjectile != nil) return;
// Choose one of the touches to work with
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
// Set up initial location of projectile
CGSize winSize = [[CCDirector sharedDirector] winSize];
_nextProjectile = [[CCSprite spriteWithFile:@"Projectile2.png"] retain];
_nextProjectile.position = ccp(20, winSize.height/2);
// Determine offset of location to projectile
int offX = location.x - _nextProjectile.position.x;
int offY = location.y - _nextProjectile.position.y;
// Bail out if we are shooting down or backwards
if (offX <= 0) return;
// Play a sound!
[[SimpleAudioEngine sharedEngine] playEffect:@"pew-pew-lei.caf"];
// Determine where we wish to shoot the projectile to
int realX = winSize.width + (_nextProjectile.contentSize.width/2);
float ratio = (float) offY / (float) offX;
int realY = (realX * ratio) + _nextProjectile.position.y;
CGPoint realDest = ccp(realX, realY);
// Determine the length of how far we're shooting
int offRealX = realX - _nextProjectile.position.x;
int offRealY = realY - _nextProjectile.position.y;
float length = sqrtf((offRealX*offRealX)+(offRealY*offRealY));
float velocity = 480/1; // 480pixels/1sec
float realMoveDuration = length/velocity;
// Determine angle to face
float angleRadians = atanf((float)offRealY / (float)offRealX);
float angleDegrees = CC_RADIANS_TO_DEGREES(angleRadians);
float cocosAngle = -1 * angleDegrees;
float rotateSpeed = 0.5 / M_PI; // Would take 0.5 seconds to rotate 0.5 radians, or half a circle
float rotateDuration = fabs(angleRadians * rotateSpeed);
[_player runAction:[CCSequence actions:
[CCRotateTo actionWithDuration:rotateDuration angle:cocosAngle],
[CCCallFunc actionWithTarget:self selector:@selector(finishShoot)],
nil]];
// Move projectile to actual endpoint
[_nextProjectile runAction:[CCSequence actions:
[CCMoveTo actionWithDuration:realMoveDuration position:realDest],
[CCCallFuncN actionWithTarget:self selector:@selector(spriteMoveFinished:)],
nil]];
// Add to projectiles array
_nextProjectile.tag = 2;
}
- (void)finishShoot {// Ok to add now - we've finished rotation!
[self addChild:_nextProjectile];
[_projectiles addObject:_nextProjectile];
// Release
[_nextProjectile release];
_nextProjectile = nil;
}

That looks like a lot of code, but we actually didn’t change that much – most of it was just some minor refactoring. Here are the changes we made:

  • We bail at the beginning of the function if there is a value in nextProjectile, which means we’re in the process of shooting.
  • Before we used a local object named projectile that we added to the scene right away. In this version we create an object in the member variable nextProjectile, but don’t add it until later.
  • We define the speed at which we want our turret to rotate as half a second for half a circle’s worth of rotation. Remember that a circle has 2 PI radians.
  • So to calculate how long this particular rotation should take, we multiply the radians we’re moving by the speed.
  • Then we start up a sequence of actions where we rotate the turret to the correct angle, then call a function to add the projectile to the scene.

So let’s give it a shot! Compile and run the project, and the turret should now rotate much more smoothly.

What’s Next?

First off, here’s the full code for the simple Cocos2D iPhone game that we’ve made so far.

Next up in the series is a tutorial on how to add harder monsters and more levels!

Or you could always check out my other Cocos2D and Box2D tutorials!

Category: iPhone

Tags: cocos2D, game, iPhone, sample code, tutorial

//

http://www.raywenderlich.com/692/rotating-turrets

Rotating Turrets: How To Make A Simple iPhone Game with Cocos2D Part 2相关推荐

  1. 非常优秀的iphone学习文章总结!

    This site contains a ton of fun tutorials – so many that they were becoming hard to find! So I put t ...

  2. 如何制作一个简单的游戏 Cocos2d x 2 0 4

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本文实践 ...

  3. 如何制作一个简单的游戏 Cocos2d-x 2.0.4

    本文实践自 Ray Wenderlich 的文章< How To Make A Simple iPhone Game with Cocos2D 2.X Tutorial>,文中使用Coco ...

  4. 在Tiled Map中使用碰撞检测

    转 [转载] 在Tiled Map中使用碰撞检测 2014年11月17日 01:46:51 阅读数:6721更多 个人分类: cocos2dx 网上这篇教程的转载非常泛滥,本来以为没什么参考价值.但是 ...

  5. tilemap之基础使用

    转载声明          本文转载于  冥冥之中 的163博客,地址:   http://blog.163.com/fengyi1103@126/blog/static/13835627420108 ...

  6. [转载] 在Tiled Map中使用碰撞检测

    网上这篇教程的转载非常泛滥,本来以为没什么参考价值.但是当我实际用上 tiledmap 做点东西时,发现TiledMap软件本身,以及TMXTiledMap类的使用确实存在一些疑惑.所以,对于想真正使 ...

  7. iOS iPhone官方参考资料明细

    官方的门户站点 Refernce Library Mac OS X Refernce Library http://developer.apple.com/library/mac/navigation ...

  8. (转载)如何学好iphone游戏开发

    转自:http://www.cnblogs.com/zilongshanren/archive/2011/09/19/2181558.html 自从发布<如何学习iphone游戏开发>到 ...

  9. 如何学习iphone游戏开发

    注意,我本人也是刚接触iphone游戏开发不久,之前完全没有mac开发相关经验,只有一些c/c++和java的编程经验. 所以,我想谈一谈我在学习过程中的一些心得和体会.当然,我还会继续学习下去,如果 ...

最新文章

  1. 支持支付宝(Alipay)付款的三个美国主机商
  2. 用户态下init进程1的执行
  3. Verilog中生成语句(generate)的用法
  4. Windows Media Player 损坏提示“出现了内部应用程序错误解决方法
  5. python列表多重赋值
  6. Android—AspectJ实践
  7. LeetCode 47 全排列 II
  8. 一个整数,它加上100后是一个完全平方数,加上168又是一个完全平方数,请问该数是多少?...
  9. android简单小项目实例_300行C代码打造简单的闹钟小程序,适合初学C++同学练手的项目...
  10. 简单工厂模式-Simple Factory Pattern
  11. IP 地址聚合 经典算法 已经过验证
  12. Typora使用教程
  13. 什么是JSTL和EL表达式
  14. CF755F PolandBalls and Gifts
  15. USACO之Section 1.1.2 PROB Greedy Gift Givers
  16. Golang ToLower和ToLowerSpecial源码探究
  17. github帐户和仓库的创建
  18. 微信小程序+nginx+php+mysql实现数据库管理【第一期】数据库与php的正确连接
  19. 4501. 收集卡牌
  20. python绘制三维地形_三维数字场地模型(上篇):Civil3D 地形的生成

热门文章

  1. Qt Tcp网络助手
  2. 使用JavaScript写一个三级下拉框联动
  3. js 获取数组的深度
  4. 什么是轮询、长轮询、长连接一篇文章让你不在懵懂 - 第412篇
  5. mysql的配置和安装
  6. Python实现简单的换脸术
  7. vuejs里:style和:class的区别(vue动态操作绑定class 和 style的方法)
  8. 中端终有逆袭日:高通骁龙660完虐骁龙820
  9. 程序员,你是选择25k的996 还是18k的八小时?
  10. 医院体检管理系统说明书