SpriteKit项目——消灭怪兽、效果图及详细代码,
#pragma mark - 隐藏状态栏
- (BOOL)prefersStatusBarHidden
{
return YES;
}
二、添加忍者
// 1. 设置背景颜色
self.backgroundColor = [SKColor colorWithWhite:1.0 alpha:1.0];
// 2. 创建忍者
SKSpriteNode *player = [SKSpriteNode spriteNodeWithImageNamed:@"player"];
CGPoint location = CGPointMake(0, self.size.height / 2);
[player setPosition:location];
// 3. 添加到场景
[self addChild:player];
三、在iOS中视图大小变化简述
{
[super viewWillLayoutSubviews];
// 1. 获取根视图
SKView * skView = (SKView *)self.view;
// 2. 判断场景是否已经创建
if (!skView.scene) {
// 2.1 配置SK视图
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// 2.2 创建并配置游戏场景
SKScene * scene = [GameScene sceneWithSize:skView.bounds.size];
// 2.3 展现游戏场景
[skView presentScene:scene];
}
}
五、忍者登场
CGPoint location = CGPointMake(player.size.width / 2,
self.size.height / 2);
六、添加妖怪
SKSpriteNode *monster = [SKSpriteNode spriteNodeWithImageNamed:@"monster"];
// 2. 计算怪物出现的位置
NSInteger maxY = self.size.height - monster.size.height;
NSInteger randomY = arc4random_uniform(maxY);
NSInteger monsterY = randomY + monster.size.height / 2;
NSInteger monsterX = self.size.width - monster.size.width / 2;
monster.position = CGPointMake(monsterX, monsterY);
// 3. 添加妖怪
[self addChild:monster];
// 4. 妖怪从右向左移动
CGFloat duration = arc4random_uniform(4) + 2.0;
CGPoint to = CGPointMake(-monster.size.width / 2, monsterY);
SKAction *move = [SKAction moveTo:to duration:duration];
SKAction *moveDone = [SKAction removeFromParent];
[monster runAction:[SKAction sequence:@[move, moveDone]]];
七、示意图
八、不断添加妖怪
// 4.1 添加一个妖怪的Action
SKAction *addMonster = [SKAction runBlock:^{
[self addMonster];
}];
// 4.2 等待Action
SKAction *waitAction = [SKAction waitForDuration:1.0f];
// 4.3 系列Action
SKAction *seqence = [SKAction sequence:@[addMonster, waitAction]];
// 4.4 循环执行Action,不断添加妖怪
[self runAction:[SKAction repeatActionForever:seqence]];
九、滚滚不断地妖怪大军来啦~~~
十、发射飞镖示意图
十一、发射飞镖
CGPoint from = CGPointMake(_player.size.width + projectile.size.width / 2, self.size.height / 2);
CGPoint offset = CGPointMake(location.x - from.x, location.y - from.y);
// 仅允许向右侧发射
if (offset.x <= 0) return;
// 3. 计算目标点
CGFloat toX = projectile.size.width / 2.0 + self.size.width;
CGFloat toY = offset.y / offset.x * toX + from.y;
CGPoint to = CGPointMake(toX, toY);
// 4. 通过飞镖距离,计算飞行时间
CGPoint p = CGPointMake(to.x - from.x, to.y - from.y);
CGFloat distance = sqrtf(powf(p.x, 2.0) + powf(p.y, 2.0));
CGFloat volcity = self.size.width;
NSTimeInterval duration = distance / volcity;
十二、多点触摸支持
// 多点触摸支持
for (UITouch *touch in touches) {
[self addProjectileAtPoint:[touch locationInNode:self]];
}
十三、消灭妖怪——在场景更新方法中做碰撞检测
for (SKSpriteNode *projectile in _projectiles) {
NSMutableSet *monsterToDelete = [NSMutableSet set];
for (SKSpriteNode *monster in _monsters) {
if (CGRectIntersectsRect(monster.frame, projectile.frame)) {
[monsterToDelete addObject:monster];
}
}
for (SKSpriteNode *monster in monsterToDelete) {
[_monsters removeObject:monster];
SKAction *rotate = [SKAction rotateToAngle:-M_PI_2 duration:0.1];
[monster runAction:rotate completion:^{
[monster removeFromParent];
}];
}
if (monsterToDelete.count > 0) {
[projectileToDelete addObject:projectile];
}
}
十四、游戏结束处理
_killedMonster++;
if (_killedMonster > 10) {
GameOverScene *gameOver = [[GameOverScene alloc] initWithSize:self.size won:YES];
SKTransition *transition = [SKTransition doorsOpenHorizontalWithDuration:0.5f];
[self.scene.view presentScene:gameOver transition:transition];
}
// 在用户触摸事件中遍历用户点击的节点
SKNode *node = [self nodeAtPoint:[touch locationInNode:self]];
if ([node.name isEqualToString:@"click"]) {
GameScene *gameScene = [GameScene sceneWithSize:self.size];
SKTransition *transition = [SKTransition doorsCloseHorizontalWithDuration:0.5f];
[self.scene.view presentScene:gameScene transition:transition];
}
十五、碰撞检测(1)——设置类别掩码,并设置物理世界
tic uint32_t projectileCategory = 1 << 0;
static uint32_t monsterCategory = 1 << 1;
// 1) 物理仿真世界的重力方向
self.physicsWorld.gravity = CGVectorMake(0, 0);
// 2) 仿真世界的关联代理
self.physicsWorld.contactDelegate = self;
十六、碰撞检测(2)——设置妖怪的物理刚体属性
// 1.2 设置妖怪的物理刚体属性
// 1) 使用妖怪的尺寸创建一个矩形刚体
monster.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:monster.size];
// 2) 标示物体的移动是否由仿真引擎负责
monster.physicsBody.dynamic = YES;
// 3) 设置类别掩码
monster.physicsBody.categoryBitMask = monsterCategory;
// 4) 设置碰撞检测类别掩码
monster.physicsBody.contactTestBitMask = projectileCategory;
// 5) 设置回弹掩码
monster.physicsBody.collisionBitMask = 0;
十七、碰撞检测(3)——设置飞镖的物理刚体属性
属性
// 1) 使用飞镖的尺寸创建一个圆形刚体
projectile.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:projectile.size.width / 2.0];
// 2) 标示物体的移动是否由仿真引擎负责
projectile.physicsBody.dynamic = YES;
// 3) 设置类别掩码
projectile.physicsBody.categoryBitMask = projectileCategory;
// 4) 设置碰撞检测类别掩码
projectile.physicsBody.contactTestBitMask = monsterCategory;
// 5) 设置回弹掩码
projectile.physicsBody.collisionBitMask = 0;
// 6) 设置精确的碰撞检测,避免出现“遂穿”
projectile.physicsBody.usesPreciseCollisionDetection = YES;
十八、碰撞检测——消灭妖怪
/ 1. 使用类别掩码判断发生碰撞的对象类别
SKPhysicsBody *projectileBody = nil;
SKPhysicsBody *monsterBody = nil;
if (contact.bodyA.categoryBitMask == projectileCategory) {
projectileBody = contact.bodyA;
monsterBody = contact.bodyB;
} else {
projectileBody = contact.bodyB;
monsterBody = contact.bodyA;
}
// 2. 从场景中移除飞镖
[projectileBody.node removeFromParent];
// 3. 从场景中移出妖怪
SKSpriteNode *monster = (SKSpriteNode *)monsterBody.node;
SKAction *rotate = [SKAction rotateToAngle:-M_PI_2 duration:0.1];
SKAction *rotateDone = [SKAction removeFromParent];
[monster runAction:[SKAction sequence:@[rotate, rotateDone]]];
十九、战斗开始啦~~
二十、添加声音