《Cocos2D权威指南》——2.2 构建游戏场景

    xiaoxiao2021-04-16  252

    2.2 构建游戏场景

    在构建游戏场景之前,我们需要将制作该游戏所需要资源文件添加进来。2.2.1 添加资源目录找到本书的随书源代码,打开chapter2/resource/arts目录,把需要的图片和声音资源都添加到项目中。右键单击Resource,选择“Add Files to"VerticalShootingGame"…”,如图2-3所示。浏览chapter2/resource/arts目录,确保选中“Copy items into destination group抯 folder(if needed)”,然后单击“Add”,如图2-4所示。

    ![image](https://yqfile.alicdn.com/57e5a9fb36f81851618ffc1929c9c0d8c0998108.png)

    2.2.2 添加游戏背景在一个漆黑的游戏背景上玩游戏,肯定会让人觉得乏味。接下来,我们给游戏添加一个静态背景图片。iPhone屏幕大小是480*320,这里设置竖直放置,图片长度是480,宽度为320。打开HelloWorldLayer.m,找到init方法,在if判断里添加代码清单2-5所示代码。代码清单2-5 在if判断里添加代码

    if( (self=[super init]) ) { //1.get screen's size CGSizewinSize = [[CCDirector sharedDirector] winSize]; //2.add background CCSprite *bgSprite = [CCSprite spriteWithFile:@"background_1.jpg"]; bgSprite.position = ccp(winSize.width / 2,winSize.height/2); [selfaddChild:bgSprite z:0]; }

    按照代码注释中的标号解释这段代码的作用。1)通过CCDirector对象获得当前设备的屏幕大小。屏幕大小以point为单位,支持Retina显示的iPhone4以及不支持Retina显示的iPhone 3GS屏幕都为(320,480)。想获得实际像素大小,也可以调用与之相应的像素级API winSizeInPixels。2)通过sprite的spriteWithFile方法,从background_1.jpg实例化一个精灵,然后通过之前获得的屏幕大小计算出背景精灵图片放置的坐标位置。因为精灵的坐标定位是相对于它的锚点(anchorPoint)而来,而sprite的anchorPoint为图片的中心点。所以,这里我们取屏幕的中点作为精灵的坐标点。最后,把该背景精灵图片作为当前层的子节点添加进去,Cocos2D框架会自动处理背景图片的渲染。注意 这里背景图片格式为JPG,而不是大家常见的PNG图片,因为背景图片一般不需要有透明像素。JPG图片和PNG图片最大的区别就是JPG图片没有Alpha通道,因而能够缩小背景图片的大小,并且渲染的速度也会更快一些。编译并运行,结果如图2-5所示。

    2.2.3 添加玩家飞机 好了,是时候添加玩家操控的飞机了。 打开HelloWorldLayer.m文件,在@implementation HelloWorldLayer的上面添加下列枚举定义,如代码清单2-6所示。 代码清单2-6 在@implementation HelloWorldLayer的上面加上枚举定义 enum { kTagPalyer = 1, };

    在init方法中添加背景图片的代码后面添加代码清单2-7所示代码。代码清单2-7 在init方法中添加背景图片的代码后面添加代码

    //3.add player's plane CCSprite *playerSprite = [CCSprite spriteWithFile:@"hero_1.png"]; playerSprite.position = CGPointMake(winSize.width / 2, playerSprite.contentSize.height/2 + 20); [selfaddChild:playerSprite z:4 tag:kTagPalyer];

    接下来,我们为大家解释代码清单2-7中的这段代码。首先,从hero_1.png文件初始化一个player精灵,通过屏幕大小与精灵纹理大小的运算,计算放置player精灵的坐标。注意 这里我们使用的是相对坐标,而不是绝对坐标,这样写有助于代码的可适应性,比如移植到Android平台后,有多种不同分辨率的设备,如果使用绝对坐标定位就会导致界面布局混乱。这里的CGPointMake等价于ccp宏。然后,调用CCNode的addChild方法,把player精灵加到当前layer中。这里我们使用带tag的addChild版本,方便后面使用getChildByTag提取层中的精灵。编译并运行结果如图2-6所示。

    2.2.4 添加敌机 现在,我们需要添加一些敌机,希望敌机会从屏幕上方随机出现,然后向下俯冲。为了简单起见,这些敌机暂时不会发射子弹,随着学习的深入,玩家可以自行添加这个特性。 步骤1 添加一个CCArray *_enemySprites实例变量。 打开HelloWorldLayer.m文件,在init里代码清单2-7后面初始化此数组,如代码清单2-8所示。 代码清单2-8 在init里初始化数组 //4.init enemy sprites array _enemySprites = [[CCArray alloc] init];

    注意 这里的成员变量命名加了下划线作为前缀,它是一种编码风格,主要用于区别成员变量与局部变量。步骤2 为了防止忘记释放内存,立刻在dealloc方法里释放掉该数组,如代码清单2-9所示。代码清单2-9 在dealloc方法里释放掉数组

    [_enemySprites release]; _enemySprites = nil;

    步骤3 初始化一系列的敌机精灵,并把这些精灵都添加到数组中。找到init方法,在代码清单2-8所示代码后添加代码清单2-10所示代码。代码清单2-10 找到init方法添加代码

    //5.initialize 10 enemy sprites & add them to _enemySprites array for future useage const int NUM_OF_ENEMIES = 10; for (int i=0; i < NUM_OF_ENEMIES; ++i) { CCSprite *enemySprite = [CCSprite spriteWithFile:@"enemy1.png"]; enemySprite.position = ccp(0,winSize.height + enemySprite.contentSize.height + 10); enemySprite.visible = NO; [selfaddChild:enemySprite z:4]; [_enemySpritesaddObject:enemySprite]; }

    这段代码和前面的类似,唯一区别就是在一个循环里初始10个sprite,并且把sprite初始可见性设置为NO,最后把这些sprite都添加到_enemySprites数组中。如果现在运行项目,结果与上一次编译的没有区别,因为新初始化的敌机精灵是不可见的,同时它们的位置是在设备屏幕上方之外。现在,我们需要一些代码,希望能够隔一段时间有一架敌机随机从上方俯冲下来。步骤4 打开init方法,在代码清单2-10后添加代码清单2-11所示代码。代码清单2-11 init方法继续添加代码

    //6.spawn enemy after 1.0 sec [self performSelector:@selector(spawnEnemy) withObject:nil afterDelay:1.0f];

    该方法的作用是:调用完init方法之后,隔1.0秒调用spawnEnemy方法。这种特性非常有用,因为我们有时候希望在触发某个事件之后,不要求马上响应,而是隔一段时间再做处理。步骤5 定义一些私有方法。其实在Objective-C世界里不存在真正意义的私有方法,有时为了不让API暴露给客户端程序员,一些类的内部使用的方法和变量需要放在私有扩展里,而不是直接声明在头文件中。找到HelloWorldLayer.m的@implementation HelloWorldLayer部分,紧跟上面代码清单2-11添加代码清单2-12所示代码。代码清单2-12 @implementation HelloWorldLayer部分后添加代码

    @interface HelloWorldLayer() -(void) spawnEnemy; -(CCSprite*) getAvailableEnemySprite; @end

    这里定义一个匿名的Category,把一些私有方法直接放到这里就可以。步骤6 实现这两个方法。在代码清单2-12中找到@end,在该行之前添加代码清单2-13所示代码。代码清单2-13 @end之前添加代码

    #pragma mark - private methods -(void) spawnEnemy{ //1. CGSizewinSize = [CCDirector sharedDirector].winSize; CCSprite *enemySprite = [self getAvailableEnemySprite]; //2. floatdurationTime = arc4random() % 4 + 1; idmoveBy = [CCMoveBy actionWithDuration:durationTime position:ccp(0,-enemySprite.position.y-enemySprite.contentSize.height)]; id callback = [CCCallBlockN actionWithBlock:^(id sender) { CCSprite *sp = (CCSprite*)sender; sp.visible = NO; sp.position = ccp(0,winSize.height + sp.contentSize.height + 10); CCLOG(@"reset enemy plane!"); }]; id action = [CCSequence actions:moveBy,callback, nil]; enemySprite.visible = YES; enemySprite.position = ccp( arc4random() % (int)(winSize.width - enemySprite.contentSize.width) + enemySprite.contentSize.width/2 , enemySprite.position.y); CCLOG(@"enemySprite x = %f, y = %f",enemySprite.position.x, enemySprite.position.y); [enemySpriterunAction:action]; //3. [selfperformSelector:_cmdwithObject:nil afterDelay:arc4random()%3 + 1]; } -(CCSprite*) getAvailableEnemySprite{ CCSprite *result = nil; CCARRAY_FOREACH(_enemySprites, result) { if (!result.visible) { break; } } return result; }

    我们按照代码注释的序号逐步讲解spawnEnemy方法。1)通过CCDirector的winSize获得设备的大小(以point为单位);通过调用自定义的getAvailableEnemySprite方法从_enemySprites数组中获取一个还没有飞行的飞机。2)通过arc4random方法获取一个随机时间,以此时间建立一个CCMoveBy的action,同时计算出此action要移动的偏移量;接着,使用Block建立了一个回调action,最后通过CCSequence action把这两个action串在一起,把需要俯冲的敌机的可见性设置为YES;同时,为了视觉效果,在runAction之前,先设置敌机出现的坐标,改为随机位置出现;在moveBy的动作结束以后,精灵的可见性又变成NO,同时坐标也要发回初始化时的坐标位置。这一切都通过一种神奇函数式编程方式block做到。3)隔1~4秒调用spawnEnemy方法,此处_cmd参数就是spawnEnemy方法本身。这样,随机地每隔一段时间,就会有一架敌机以不同速度向下俯冲。编译并运行,我们应该会得到如图2-7所示的效果!

    相关资源:七夕情人节表白HTML源码(两款)

    最新回复(0)