ビリーヤードの定番、ナインボールを模したシンプルなiPhoneゲームのサンプルコードを書いてみます。
サンプルを動画で確認
サンプルコード
#import “ViewController.h”
#import <SpriteKit/SpriteKit.h>
@protocol BallDelegate <NSObject>
– (void)selectBall:(UIColor*)ballColor;
@end
@interface BallScene : SKScene
@property BOOL contentCreated;
@property SKNode *selected;
@property (strong, nonatomic) id<BallDelegate> balldelegate;
– (void)hitAtAngle:(float)angle impulse:(float)impulse;
@end
@implementation BallScene
– (void)didMoveToView:(SKView *)view
{
if (!self.contentCreated) {
[self createSceneContents];
self.contentCreated = YES;
}
}
– (void)createSceneContents
{
self.backgroundColor = [UIColor greenColor];
self.physicsWorld.gravity = CGVectorMake(0, 0);
[self createWall];
[self createPocket];
[self createBall];
}
– (void)createWall
{
CGRect wallRects[] = {
CGRectMake(0, 0, 10, 320),
CGRectMake(0, 0, 320, 10),
CGRectMake(310, 0, 10, 320),
CGRectMake(0, 310, 320, 10),
};
for (int i=0; i<4; i++) {
SKSpriteNode *wall = [SKSpriteNode spriteNodeWithColor:[SKColor brownColor] size:wallRects[i].size];
wall.position = CGPointMake(CGRectGetMidX(wallRects[i]), CGRectGetMidY(wallRects[i]));
[self addChild:wall];
wall.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:wall.size];
wall.physicsBody.dynamic = NO;
}
}
– (void)createBall
{
CGPoint points[] = {
CGPointMake(160, 160),
CGPointMake(150, 180),
CGPointMake(170, 180),
CGPointMake(140, 200),
CGPointMake(160, 200),
CGPointMake(180, 200),
CGPointMake(130, 220),
CGPointMake(150, 220),
CGPointMake(170, 220),
CGPointMake(190, 220),
};
UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-10, –10, 20, 20)];
for (int i=0; i<sizeof(points)/sizeof(CGPoint); i++) {
SKShapeNode *ball = [SKShapeNode node];
ball.name = @”ball”;
ball.path = circle.CGPath;
ball.fillColor = [SKColor colorWithHue:i/12.0 saturation:1 brightness:1 alpha:1];
ball.position = points[i];
[self addChild:ball];
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:10];
ball.physicsBody.restitution = 0.8;
ball.physicsBody.linearDamping = 0.3;
}
}
– (void)createPocket
{
CGPoint points[] = {
CGPointMake(20, 20),
CGPointMake(300, 20),
CGPointMake(300, 300),
CGPointMake(20, 300),
};
for (int i=0; i<4; i++) {
UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-15, –15, 30, 30)];
SKShapeNode *pocket = [SKShapeNode node];
pocket.name = @”pocket”;
pocket.lineWidth = 0;
pocket.fillColor = [SKColor blackColor];
pocket.path = circle.CGPath;
pocket.position = points[i];
[self addChild:pocket];
}
}
– (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint p = [[touches anyObject] locationInNode:self];
[self enumerateChildNodesWithName:@”ball” usingBlock:^(SKNode *node, BOOL *stop) {
if (CGRectContainsPoint(node.frame, p)) {
self.selected = node;
[self.balldelegate selectBall:[(SKShapeNode*)node fillColor]];
}
}];
}
– (void)hitAtAngle:(float)angle impulse:(float)impulse
{
CGPoint p = CGPointMake(10 * cos(angle) + 10, 10 * sin(angle) + 10);
CGVector v = CGVectorMake(impulse * cos(angle), impulse * sin(angle));
[self.selected.physicsBody applyImpulse:v atPoint:p];
}
– (void)didSimulatePhysics
{
[self enumerateChildNodesWithName:@”ball” usingBlock:^(SKNode *ball, BOOL *stop) {
[self enumerateChildNodesWithName:@”pocket” usingBlock:^(SKNode *pocket, BOOL *stop) {
if (CGRectContainsPoint(pocket.frame, ball.position)) {
SKAction *small = [SKAction scaleTo:0 duration:0.5];
SKAction *move = [SKAction moveTo:pocket.position duration:0.5];
[ball runAction:[SKAction group:@[small, move]] completion:^{
[ball removeFromParent];
}];
}
}];
}];
}
@end
@interface ViewController () <BallDelegate>
@property (weak, nonatomic) UIView *ballGuide;
@property (weak, nonatomic) UIView *hitMark;
@property (weak, nonatomic) BallScene *ballScene;
@end
@implementation ViewController
– (void)viewDidLoad
{
[super viewDidLoad];
[self createBallScene];
[self createConsole];
}
– (void)createBallScene
{
SKView *spriteView = [[SKView alloc] initWithFrame:CGRectMake(0, 0, 320, 320)];
[self.view addSubview:spriteView];
BallScene *scene = [[BallScene alloc] initWithSize:spriteView.frame.size];
[spriteView presentScene:scene];
scene.balldelegate = self;
self.ballScene = scene;
}
– (void)createConsole
{
UIView *b = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
b.backgroundColor = [UIColor whiteColor];
b.center = CGPointMake(160, CGRectGetMaxY(self.view.frame) – 160);
b.layer.cornerRadius = 50;
[self.view addSubview:b];
self.ballGuide = b;
UIView *m = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
m.backgroundColor = [UIColor redColor];
m.layer.cornerRadius = 10;
m.center = CGPointMake(50, 100);
[b addSubview:m];
self.hitMark = m;
self.view.backgroundColor = [UIColor blackColor];
UISlider *s = [[UISlider alloc] init];
s.value = 0;
s.maximumValue = 10;
s.center = CGPointMake(160, CGRectGetMaxY(self.view.frame) – 50);
[self.view addSubview:s];
[s addTarget:self action:@selector(pushBall:) forControlEvents:UIControlEventTouchUpOutside | UIControlEventTouchUpInside];
}
– (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint p = [[touches anyObject] locationInView:self.view];
if (CGRectContainsPoint(self.ballGuide.frame, p)) {
[self updateMarker:p];
}
}
– (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint p = [[touches anyObject] locationInView:self.view];
if (CGRectContainsPoint(self.ballGuide.frame, p)) {
[self updateMarker:p];
}
}
– (void)updateMarker:(CGPoint)p
{
CGPoint local = [self.ballGuide convertPoint:p fromView:self.view];
float angle = atan2f(local.x – 50, local.y – 50) – M_PI/2.0;
CGPoint newP = CGPointMake(50 * cos(angle) + 50, –50 * sin(angle) + 50);
self.hitMark.center = newP;
}
– (void)pushBall:(UISlider*)sender
{
float angle = atan2f(self.hitMark.center.x – 50, self.hitMark.center.y – 50) + M_PI/2.0;
[self.ballScene hitAtAngle:angle impulse:sender.value];
sender.value = 0;
}
– (void)selectBall:(UIColor *)ballColor
{
self.ballGuide.backgroundColor = ballColor;
}
– (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end