Created
June 24, 2014 09:19
-
-
Save kazukitanaka0611/8e9ca074c2a44d359cd7 to your computer and use it in GitHub Desktop.
SpriteKit Pazzle and Dragon clone
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <SpriteKit/SpriteKit.h> | |
typedef NS_ENUM(NSInteger, BallType) | |
{ | |
Blue, | |
Red, | |
Green, | |
Yellow, | |
Purple, | |
Pink, | |
SIZE, | |
}; | |
static const NSInteger BALL_NUM_X = 6; | |
static const NSInteger BALL_NUM_Y = 5; | |
static const NSInteger BALL_SIZE = 53; | |
static const CGFloat ONE_ACTION_TIME = 0.2; | |
@interface BallSprite : SKSpriteNode | |
@property (nonatomic, assign) BallType ballType; | |
@property (nonatomic, assign) CGPoint positionIndex; | |
@property (nonatomic, assign) BOOL checkedX; | |
@property (nonatomic, assign) BOOL checkedY; | |
@property (nonatomic, assign) NSInteger removeNo; | |
@property (nonatomic, assign) NSInteger fallCount; | |
- (instancetype)initWithBallType:(BallType)type visible:(BOOL)visible; | |
- (void)setPositionIndexAndChangePosition:(CGPoint)positionIndex; | |
+ (NSString *)generateTag:(CGPoint)positionIndex; | |
- (void)resetPosition; | |
- (void)resetParams; | |
- (void)removingAndFallingAnimation:(NSInteger)maxRemovedNo; | |
@end | |
#import "BallSprite.h" | |
@implementation BallSprite | |
- (instancetype)initWithBallType:(BallType)type visible:(BOOL)visible | |
{ | |
if (self = [super initWithImageNamed:[self getBallImageFilePath:type]]) | |
{ | |
self.alpha = visible ? 1.0f : 0; | |
_ballType = type; | |
} | |
return self; | |
} | |
- (NSString *)getBallImageFilePath:(BallType)type | |
{ | |
switch (type) { | |
case Red: | |
return @"red.png"; | |
break; | |
case Blue: | |
return @"blue.png"; | |
break; | |
case Yellow: | |
return @"yellow.png"; | |
break; | |
case Green: | |
return @"green.png"; | |
break; | |
case Purple: | |
return @"purple.png"; | |
break; | |
default: | |
return @"pink.png"; | |
break; | |
} | |
} | |
- (void)setPositionIndexAndChangePosition:(CGPoint)positionIndex | |
{ | |
[self setPositionIndex:positionIndex]; | |
[self resetPosition]; | |
} | |
- (void)setPositionIndex:(CGPoint)positionIndex | |
{ | |
_positionIndex = positionIndex; | |
self.name = [BallSprite generateTag:_positionIndex]; | |
} | |
+ (NSString *)generateTag:(CGPoint)positionIndex | |
{ | |
return [NSString stringWithFormat:@"%d", (int)(positionIndex.x * 10 + positionIndex.y)]; | |
} | |
- (void)resetPosition | |
{ | |
self.position = [self getPositionForPositionIndex:_positionIndex]; | |
} | |
- (CGPoint)getPositionForPositionIndex:(CGPoint)positionIndex | |
{ | |
return CGPointMake(BALL_SIZE * (positionIndex.x - 0.5) + 1, | |
BALL_SIZE * (positionIndex.y - 0.5) + 1); | |
} | |
- (void)resetParams | |
{ | |
self.removeNo = 0; | |
self.checkedX = NO; | |
self.checkedY = NO; | |
self.fallCount = 0; | |
} | |
- (void)removingAndFallingAnimation:(NSInteger)maxRemovedNo | |
{ | |
[self removingAnimation:maxRemovedNo]; | |
[self fallingAnimation:maxRemovedNo]; | |
} | |
- (void)removingAnimation:(NSInteger)maxRemovedNo; | |
{ | |
if (self.removeNo > 0) | |
{ | |
SKAction *delay1 = [SKAction waitForDuration:ONE_ACTION_TIME * (self.removeNo - 1)]; | |
SKAction *fade = [SKAction fadeOutWithDuration:ONE_ACTION_TIME]; | |
SKAction *delay2 = [SKAction waitForDuration:ONE_ACTION_TIME * (maxRemovedNo - self.removeNo)]; | |
SKAction *removeSelf = [SKAction removeFromParent]; | |
[self runAction:[SKAction sequence:@[delay1, fade, delay2, removeSelf]]]; | |
} | |
} | |
- (void)fallingAnimation:(NSInteger)maxRemovedNo | |
{ | |
if (self.fallCount > 0) | |
{ | |
self.positionIndex = CGPointMake(self.positionIndex.x, self.positionIndex.y - self.fallCount); | |
SKAction *delay = [SKAction waitForDuration:ONE_ACTION_TIME * maxRemovedNo]; | |
SKAction *show = [SKAction fadeAlphaTo:1.0 duration:0]; | |
SKAction *move = [SKAction moveTo:[self getPositionForPositionIndex:self.positionIndex] duration:ONE_ACTION_TIME]; | |
[self runAction:[SKAction sequence:@[delay, show, move]]]; | |
} | |
} | |
@end | |
#import "MyScene.h" | |
#import "BallSprite.h" | |
typedef NS_ENUM(NSInteger, Direction) | |
{ | |
directionX, | |
directionY, | |
}; | |
@interface MyScene() | |
@property (nonatomic, strong) BallSprite *movingBall; | |
@property (nonatomic, assign) BOOL touchable; | |
@property (nonatomic, assign) NSInteger chainNumber; | |
@property (nonatomic, strong) NSMutableArray *removeNumbers; | |
@property (nonatomic, assign) NSInteger maxRemovedNo; | |
@end | |
@implementation MyScene | |
-(id)initWithSize:(CGSize)size | |
{ | |
if (self = [super initWithSize:size]) | |
{ | |
srand((unsigned int)time(NULL)); | |
self.movingBall = nil; | |
self.touchable = YES; | |
self.removeNumbers = [[NSMutableArray alloc] init]; | |
[self initBalls]; | |
} | |
return self; | |
} | |
#pragma mark - touches | |
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event | |
{ | |
UITouch *touch = [touches anyObject]; | |
self.movingBall = [self getTouchBall:[touch locationInNode:self] | |
withoutPosIndex:CGPointMake(0, 0)]; | |
} | |
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event | |
{ | |
if (!self.touchable) | |
{ | |
return; | |
} | |
if (!self.movingBall) | |
{ | |
return; | |
} | |
UITouch *touch = [touches anyObject]; | |
CGPoint current = [touch locationInNode:self]; | |
CGPoint previous = [touch previousLocationInNode:self]; | |
CGPoint delta = CGPointMake(current.x - previous.x, current.y - previous.y); | |
self.movingBall.position = CGPointMake(self.movingBall.position.x + delta.x, | |
self.movingBall.position.y + delta.y); | |
BallSprite *touchBall = [self getTouchBall:current | |
withoutPosIndex:self.movingBall.positionIndex]; | |
if (touchBall && self.movingBall != touchBall) | |
{ | |
CGPoint touchBallPositionIndex = touchBall.positionIndex; | |
[touchBall setPositionIndexAndChangePosition:self.movingBall.positionIndex]; | |
self.movingBall.positionIndex = touchBallPositionIndex; | |
} | |
} | |
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event | |
{ | |
if (!self.movingBall) | |
{ | |
return; | |
} | |
[self movedBall]; | |
} | |
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event | |
{ | |
if (!self.movingBall) | |
{ | |
return; | |
} | |
[self movedBall]; | |
} | |
#pragma mark - private method | |
- (void)initBalls | |
{ | |
NSMutableArray *allBalls = [[NSMutableArray alloc] init]; | |
for (NSInteger x = 1; x <= BALL_NUM_X; x++) | |
{ | |
for (NSInteger y = 1; y <= BALL_NUM_Y; y++) | |
{ | |
BallSprite *newBall = [self newBalls:CGPointMake(x, y) allBalls:allBalls]; | |
[allBalls addObject:newBall]; | |
} | |
} | |
} | |
- (BallSprite *)newBalls:(CGPoint)positionIndex allBalls:(NSMutableArray *)allBalls | |
{ | |
NSInteger ballType; | |
while (true) | |
{ | |
ballType = rand() % SIZE; | |
if (!allBalls) | |
{ | |
break; | |
} | |
__block BallSprite *ballX1; | |
[self enumerateChildNodesWithName: | |
[BallSprite generateTag:CGPointMake(positionIndex.x -1, positionIndex.y)] | |
usingBlock:^(SKNode *node, BOOL *stop) { | |
ballX1 = (BallSprite *)node; | |
}]; | |
__block BallSprite *ballX2; | |
[self enumerateChildNodesWithName: | |
[BallSprite generateTag:CGPointMake(positionIndex.x -2, positionIndex.y)] | |
usingBlock:^(SKNode *node, BOOL *stop) { | |
ballX2 = (BallSprite *)node; | |
}]; | |
if (!(ballX1 && ballType == ballX1.ballType) || | |
!(ballX2 && ballType == ballX2.ballType)) | |
{ | |
__block BallSprite *ballY1; | |
[self enumerateChildNodesWithName: | |
[BallSprite generateTag:CGPointMake(positionIndex.x, positionIndex.y -1)] | |
usingBlock:^(SKNode *node, BOOL *stop) { | |
ballY1 = (BallSprite *)node; | |
}]; | |
__block BallSprite *ballY2; | |
[self enumerateChildNodesWithName: | |
[BallSprite generateTag:CGPointMake(positionIndex.x, positionIndex.y -2)] | |
usingBlock:^(SKNode *node, BOOL *stop) { | |
ballY2 = (BallSprite *)node; | |
}]; | |
if (!(ballY1 && ballType == ballY1.ballType) || | |
!(ballY2 && ballType == ballY2.ballType)) | |
{ | |
break; | |
} | |
} | |
} | |
BallSprite *ball = [[BallSprite alloc] initWithBallType:ballType visible:YES]; | |
[ball setPositionIndexAndChangePosition:positionIndex]; | |
[self addChild:ball]; | |
return ball; | |
} | |
- (BallSprite *)getTouchBall:(CGPoint)touchPos withoutPosIndex:(CGPoint)withoutPosIndex | |
{ | |
for (NSInteger x = 1; x <= BALL_NUM_X; x++) | |
{ | |
for (NSInteger y = 1; y <= BALL_NUM_Y; y++) | |
{ | |
if (x == withoutPosIndex.x && y == withoutPosIndex.y) | |
{ | |
continue; | |
} | |
__block BallSprite *ball; | |
[self enumerateChildNodesWithName: | |
[BallSprite generateTag:CGPointMake(x, y)] | |
usingBlock:^(SKNode *node, BOOL *stop) { | |
ball = (BallSprite *)node; | |
}]; | |
if (ball) | |
{ | |
CGFloat distance = hypotf(touchPos.x - ball.position.x, | |
touchPos.y - ball.position.y ); | |
if (distance <= BALL_SIZE / 2) | |
{ | |
return ball; | |
} | |
} | |
} | |
} | |
return nil; | |
} | |
- (void)movedBall | |
{ | |
[self.movingBall resetPosition]; | |
self.movingBall = nil; | |
self.chainNumber = 0; | |
[self.removeNumbers removeAllObjects]; | |
[self checksLinedBalls]; | |
} | |
- (void)checksLinedBalls | |
{ | |
if ([self existsLinedBalls]) | |
{ | |
self.touchable = NO; | |
self.chainNumber++; | |
[self removeAndGenerateBalls]; | |
SKAction *delay = [SKAction waitForDuration:(ONE_ACTION_TIME * (self.maxRemovedNo + 1))]; | |
SKAction *func = [SKAction performSelector:@selector(checksLinedBalls) onTarget:self]; | |
SKAction *seq = [SKAction sequence:@[delay, func]]; | |
[self runAction:seq]; | |
} | |
else | |
{ | |
self.touchable = YES; | |
} | |
} | |
- (BOOL)existsLinedBalls | |
{ | |
NSDictionary *allBalls = [self getAllBalls]; | |
[self initBallParams:allBalls]; | |
self.maxRemovedNo = 0; | |
for (NSInteger x = 1; x <= BALL_NUM_X; x++) | |
{ | |
for (NSInteger y = 1; y <= BALL_NUM_Y; y++) | |
{ | |
[self checkedBall:CGPointMake(x, y) direction:directionX allBalls:allBalls]; | |
[self checkedBall:CGPointMake(x, y) direction:directionY allBalls:allBalls]; | |
} | |
} | |
return self.maxRemovedNo > 0; | |
} | |
- (NSMutableDictionary *)getAllBalls | |
{ | |
NSMutableDictionary *balls = [[NSMutableDictionary alloc] init]; | |
for (BallSprite * ball in self.children) | |
{ | |
if (ball) | |
{ | |
[balls setObject:ball forKey:ball.name]; | |
} | |
} | |
return balls; | |
} | |
- (void)initBallParams:(NSDictionary *)allBalls | |
{ | |
for (NSString *key in allBalls.allKeys) | |
{ | |
BallSprite *ball = [allBalls objectForKey:key]; | |
[ball resetParams]; | |
} | |
} | |
- (void)checkedBall:(CGPoint)current direction:(Direction)direction allBalls:(NSDictionary *)allBalls | |
{ | |
NSString *tag = [BallSprite generateTag:CGPointMake(current.x, current.y)]; | |
BallSprite *ball = [allBalls objectForKey:tag]; | |
BOOL checked; | |
if (direction == directionX) | |
{ | |
checked = ball.checkedX; | |
} | |
else | |
{ | |
checked = ball.checkedY; | |
} | |
if (!checked) | |
{ | |
NSInteger num = 0; | |
while (true) | |
{ | |
CGPoint nextPosition; | |
if (direction == directionX) | |
{ | |
nextPosition = CGPointMake(current.x + num, current.y); | |
} | |
else | |
{ | |
nextPosition = CGPointMake(current.x, current.y + num); | |
} | |
if ([self isSameBallType:nextPosition direction:direction allBalls:allBalls]) | |
{ | |
NSString *nextTag = [BallSprite generateTag:nextPosition]; | |
BallSprite *nextBall = [allBalls objectForKey:nextTag]; | |
if (direction == directionX) | |
{ | |
nextBall.checkedX = YES; | |
} | |
else | |
{ | |
nextBall.checkedY = YES; | |
} | |
num++; | |
} | |
else | |
{ | |
if (num >= 2) | |
{ | |
NSInteger removedNo = 0; | |
if (self.removeNumbers.count <= self.chainNumber) | |
{ | |
NSMutableDictionary *removeNumber = [[NSMutableDictionary alloc] init]; | |
//[removeNumber setObject:@(num) forKey:@(5)]; | |
[self.removeNumbers addObject:removeNumber]; | |
} | |
// TODO: change | |
// NSDictionary *dict = self.removeNumbers[self.chainNumber]; | |
// NSNumber *value = dict[@(ball.ballType)]; | |
// NSInteger numTemp = value.intValue ; | |
// numTemp += num + 1; | |
// num = numTemp; | |
NSLog(@"numnumnum =%ld",(long)num); | |
for (NSInteger i = 0; i <= num; i++) | |
{ | |
CGPoint linedPosition; | |
if (direction == directionX) | |
{ | |
linedPosition = CGPointMake(current.x + i, current.y); | |
} | |
else | |
{ | |
linedPosition = CGPointMake(current.x, current.y + i); | |
} | |
NSString *linedBallTag = [BallSprite generateTag:linedPosition]; | |
BallSprite *linedBall = [allBalls objectForKey:linedBallTag]; | |
if (linedBall.removeNo > 0) | |
{ | |
removedNo = linedBall.removeNo; | |
break; | |
} | |
} | |
if (removedNo == 0) | |
{ | |
removedNo = ++self.maxRemovedNo; | |
} | |
for (NSInteger i = 0; i <= num; i++) | |
{ | |
CGPoint linedPosition; | |
if (direction == directionX) | |
{ | |
linedPosition = CGPointMake(current.x + i, current.y); | |
} | |
else | |
{ | |
linedPosition = CGPointMake(current.x, current.y + i); | |
} | |
NSString *linedBallTag = [BallSprite generateTag:linedPosition]; | |
BallSprite *linedBall = [allBalls objectForKey:linedBallTag]; | |
linedBall.removeNo = removedNo; | |
} | |
} | |
break; | |
} | |
} | |
if (direction == directionX) | |
{ | |
ball.checkedX = YES; | |
} | |
else | |
{ | |
ball.checkedY = YES; | |
} | |
} | |
} | |
- (BOOL)isSameBallType:(CGPoint)current direction:(Direction)direction allBalls:(NSDictionary *)allBalls | |
{ | |
if (direction == directionX) | |
{ | |
if (current.x + 1 > BALL_NUM_X) | |
{ | |
return NO; | |
} | |
} | |
else | |
{ | |
if (current.y + 1 > BALL_NUM_Y) | |
{ | |
return NO; | |
} | |
} | |
NSString *currentTag = [BallSprite generateTag:CGPointMake(current.x, current.y)]; | |
BallSprite *currentBall = [allBalls objectForKey:currentTag]; | |
NSString *nextTag; | |
if (direction == directionX) | |
{ | |
nextTag = [BallSprite generateTag:CGPointMake(current.x + 1, current.y)]; | |
} | |
else | |
{ | |
nextTag = [BallSprite generateTag:CGPointMake(current.x, current.y + 1)]; | |
} | |
BallSprite *nextBall = [allBalls objectForKey:nextTag]; | |
if (currentBall.ballType == nextBall.ballType) | |
{ | |
return YES; | |
} | |
return NO; | |
} | |
- (void)removeAndGenerateBalls | |
{ | |
NSMutableDictionary *allBalls = [self getAllBalls]; | |
NSInteger maxRemovedNo = 0; | |
for (NSInteger x = 1; x <= BALL_NUM_X; x++) | |
{ | |
NSInteger fallCount = 0; | |
for (NSInteger y = 1; y <= BALL_NUM_Y; y++) | |
{ | |
NSString *tag = [BallSprite generateTag:CGPointMake(x, y)]; | |
BallSprite *ball = [allBalls objectForKey:tag]; | |
if (ball) | |
{ | |
NSInteger removedNoForBall = ball.removeNo; | |
if (removedNoForBall > 0) | |
{ | |
fallCount++; | |
if (removedNoForBall > maxRemovedNo) | |
{ | |
maxRemovedNo = removedNoForBall; | |
} | |
} | |
else | |
{ | |
ball.fallCount = fallCount; | |
} | |
} | |
} | |
[self generateBalls:x fallCount:fallCount allBalls:allBalls]; | |
} | |
[self animationBall:allBalls]; | |
} | |
- (void)generateBalls:(NSInteger)xLineNum fallCount:(NSInteger)fallCount allBalls:(NSMutableDictionary *)allBalls | |
{ | |
for (NSInteger i = 1; i <= fallCount; i++) | |
{ | |
CGPoint positionIndex = CGPointMake(xLineNum, BALL_NUM_Y + i); | |
BallSprite *ball = [self newBalls:positionIndex allBalls:nil]; | |
ball.fallCount = fallCount; | |
[allBalls setObject:ball forKey:[BallSprite generateTag:positionIndex]]; | |
} | |
} | |
- (void)animationBall:(NSDictionary *)allBalls | |
{ | |
for (NSString *key in allBalls.allKeys) | |
{ | |
BallSprite *ball = [allBalls objectForKey:key]; | |
[ball removingAndFallingAnimation:self.maxRemovedNo]; | |
} | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment