Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save kazukitanaka0611/8e9ca074c2a44d359cd7 to your computer and use it in GitHub Desktop.
Save kazukitanaka0611/8e9ca074c2a44d359cd7 to your computer and use it in GitHub Desktop.
SpriteKit Pazzle and Dragon clone
#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