iOSでFootballoloidを曞いたように

最も名誉あるHabralyudiぞのご挚拶


少し前、新しい仕事を探しおいる間に、 ZeptoLabから非垞に興味深いテストタスクを受け取りたした。Cocos2dやBox2dなどのサヌドパヌティラむブラリを䜿甚せずに、぀たり「クリヌンな」 OpenGLでiOS向けのアルカノむドを䜜成したす 。それは私にずっお非垞に興味深いようでした。 ずころで、Habréでのこのタスクに぀いおは 、すでに報告し 、さらに報告䌚を準備したした。 だから、私は運呜に挑戊し、グラフィカルモヌドでVasikを䜿っお孊校で緎習した埌に初めおgamedevを取り䞊げたした

OpenGLの知識はすでにある皋床ありたしたが、非垞に衚面的なこずを明確にしたす。 私たちはそれらがほずんどたったくなかったず蚀うこずができたす、私はビュヌポヌトずは䜕かを知っおいお、そこにいく぀かのスプラむトがありたす、倉換マトリックスがありたす...したがっお、この蚘事は「 玔粋なOpenGLでiOS甚のシンプルなゲヌムを曞く方法圌を知らずに、 「しかし、それは長すぎたす。

䞀般的に、〜10時間の開発ず〜2時間の読曞でこれをどのように行ったかに興味がある堎合は、猫をお願いしたす。 泚意たくさんのコヌド小さな写真最埌にgithubずリラックスビデオぞのリンク

正盎になりたす。 Image_to_attract_attention™は、私に起こったこずのスクリヌンショットではありたせん。 そしお、これが私に起こったこずです


このごたかしを蚱しおください、でもあなたは私の蚘事にただ興味がありたすよね 

新しいテクノロゞヌを䜿甚したプログラムの䜜成はどのように開始したすか そうです、ドキュメント、䟋、蚘事を読むこずからたあ、あなたの目の前のようなものです。 Googleに数分座った埌、 玠晎らしい蚘事が発芋されたした著者Ray Wenderlich。簡単なおもちゃを䜜成するプロセスを詳现に調べたした。 ここで、必芁に応じお、私の蚘事を閉じおRayの蚘事を読み始めるこずができたすが、ただアルカノむドず母囜語での説明のために来た人のために、話を続けたす。

最初に、ゲヌム甚の空のプロゞェクトを䜜成したす。 Xcodeで、[ ファむル ] -> [新芏 ] -> [プロゞェクト... ]をクリックし、 iOSテンプレヌト->アプリケヌション-> OpenGLゲヌムを遞択したす。 個人的には、ARCを䜿甚し、StoryBoardを䜿甚しないプロゞェクトを遞択したしたが、読者の皆さん、お奜きなように自由に実行できたす。 さお、プロゞェクトが䜜成されたした。すぐに[実行]を抌しお、回転するキュヌブを楜しむこずができたす。 しかし、これはたさに私たちがやりたいこずではないので、テンプレヌトが挿入したほずんどすべおを削陀し、必芁なものだけを残したす。 たず、远加されたシェヌダヌを削陀し、ViewController.mからすべおの列挙ずinterface ViewController ()向かうグロヌバル倉数を削陀しloadShaders, compileShader:type:file:, linkProgram:, validateProgram:シェヌダヌは䜿甚したせん。 もちろん、方法ず理由がわかっおいれば䜿甚できたすが、私はこれを気にしたせんでした=。

次に、残りのすべおのメ゜ッドから䜙分なものをすべお捚おたす。 setupGLずtearDownGLを芋おみたしょう。
 - (void)setupGL { [EAGLContextsetCurrentContext:self.context]; self.effect = [[GLKBaseEffectalloc] init]; } - (void)tearDownGL { [EAGLContextsetCurrentContext:self.context]; self.effect = nil; } 

次。 プリミティブアヌキテクチャでプリミティブゲヌムを䜜成するため、ViewControllerでゲヌム党䜓を制埡できたす。 友奜的な方法では、特定のGameControllerを䜜成し、これらすべおの機胜を割り圓おる必芁がありたすが、今のずころは管理したす。 したがっお、ViewControllerにそのようなプロパティずメ゜ッドを远加したす。

 #define kGameStateNone 0 #define kGameStateLose 1 #define kGameStateWon 2 @property (assign) int gameState; // see kGameState... @property (assign) BOOL gameRunning; - (void)loadBricks; - (void)startGame; - (void)endGameWithWin:(BOOL)win; 

圌らが責任を負うもの-そしお、コメントは明確ではありたせん。 さお、今がゲヌムのロゞック、より正確にはプログラムのロゞックに぀いお考えるずきです。 䜕をどのように行いたすか どのオブゞェクトがありたすか これのために䜕をする必芁がありたすか

たくさんの質問、はい。 しかし、単玔な考えおよびいく぀かのスプラむトがあるずいう知識を䜿甚しお、このモデルに到達したす。プレヌダヌが制埡する「バット」があり、砎壊する必芁があるレンガがあり、すべおを跳ね返しおレンガを砎壊するボヌルがありたす。 さらに、背景があり、メニュヌがありたす-ゲヌムを起動しお、その結果勝ち負けを確認できたす。 画面に衚瀺されるすべおのものは、システムのりィンドりなど、レンダリングの基本オブゞェクトであるスプラむトを䜿甚しお描画されたす。 さお、たたはりィンドり内のボタン。 座暙、サむズ、描画する画像など、さたざたな属性を蚭定できたす。 今埌、動きの速床ず方向を远加したす。 さお、これらのプロパティを甚意し、Xcodeで⌘Nを倧胆に抌しおください NSObjectから継承した新しいクラスを䜜成しお、GameSpriteず呌びたしょう。 そしお、そのようなプロパティずメ゜ッドを远加したす

 @interface GameSprite : NSObject - (id)initWithTexture:(GLKTextureInfo *)textureInfo effect:(GLKBaseEffect *)effect; - (id)initWithImage:(UIImage *)image effect:(GLKBaseEffect *)effect; - (void)render; - (void)update:(float)dt; - (CGRect)boundingRect; @property (assign) GLKVector2 position; @property (assign) CGSize contentSize; @property (assign) GLKVector2 moveVelocity; // points/sec @end 

実際には、画像たたはテクスチャに基づいおスプラむトを䜜成し、それらを描画し、時間に応じお曎新し、境界線を取埗するこずができたす。 たた、その䜍眮、サむズ、速床を尋ねお取埗したす。 しかし、スプラむトの実装では、楜しみが始たりたす 頂点ず四角圢が必芁です。 これは䜕ですか 単玔な堎合、頂点は空間内のポむントであり、クワッドは4぀の頂点のコレクションです。 さらに、Vertextには、スプラむト自䜓ずそのテクスチャの2぀のポむントが実際に含たれおいたす。 察応する構造を宣蚀したす。

 typedef struct { CGPoint geometryVertex; CGPoint textureVertex; } TexturedVertex; typedef struct { TexturedVertex bl; TexturedVertex br; TexturedVertex tl; TexturedVertex tr; } TexturedQuad; 

この堎合、次の機胜を考慮する必芁がありたす。テクスチャ座暙は正芏化されたす。぀たり、垞に0〜1の範囲で倉化したす。スプラむトでテクスチャを描画するにはこのクワッドが必芁なので、GameSpriteクラスではこのようなプラむベヌトプロパティを宣蚀したす

 @interface GameSprite() @property (strong) GLKBaseEffect *effect; @property (assign) TexturedQuad quad; @property (strong) GLKTextureInfo *textureInfo; - (void)initQuadAndSize; @end 

したがっお、効果があり、テクスチャ情報は同じ方法でプロパティに栌玍されたすが、パブリックではありたせん。 さお、スプラむトの初期化の実装を開始できたす。

 - (id)initWithTexture:(GLKTextureInfo *)textureInfo effect:(GLKBaseEffect *)effect { if ((self = [super init])) { self.effect = effect; self.textureInfo = textureInfo; if (self.textureInfo == nil) { NSLog(@"Error loading texture! Texture info is nil!"); return nil; } [self initQuadAndSize]; } return self; } - (id)initWithImage:(UIImage *)image effect:(GLKBaseEffect *)effect { if ((self = [super init])) { self.effect = effect; NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], GLKTextureLoaderOriginBottomLeft, nil]; NSError *error; self.textureInfo = [GLKTextureLoader textureWithCGImage:image.CGImage options:options error:&error]; if (self.textureInfo == nil) { NSLog(@"Error loading image: %@", [error localizedDescription]); return nil; } [self initQuadAndSize]; } return self; } - (void)initQuadAndSize { self.contentSize = CGSizeMake(self.textureInfo.width, self.textureInfo.height); TexturedQuad newQuad; newQuad.bl.geometryVertex = CGPointMake(0, 0); newQuad.br.geometryVertex = CGPointMake(self.textureInfo.width, 0); newQuad.tl.geometryVertex = CGPointMake(0, self.textureInfo.height); newQuad.tr.geometryVertex = CGPointMake(self.textureInfo.width, self.textureInfo.height); newQuad.bl.textureVertex = CGPointMake(0, 0); newQuad.br.textureVertex = CGPointMake(1, 0); newQuad.tl.textureVertex = CGPointMake(0, 1); newQuad.tr.textureVertex = CGPointMake(1, 1); self.quad = newQuad; } 

トリッキヌなこずはありたせん。画像からテクスチャを割り圓おたり読み蟌んだり、クワッドを割り圓おるだけです。 ここでは、4぀の頂点のそれぞれに぀いお、ゞオメトリずテクスチャの座暙が異なる単䜍で蚭定されおいるこずがわかりたす。

それでは、最も興味深い郚分に移りたしょう-スプラむトのレンダリングです。このためにrenderメ゜ッドが責任を負いたす

 - (void)render { self.effect.texture2d0.name = self.textureInfo.name; self.effect.texture2d0.enabled = YES; self.effect.transform.modelviewMatrix = self.modelMatrix; [self.effect prepareToDraw]; long offset = (long)&_quad; glEnableVertexAttribArray(GLKVertexAttribPosition); glEnableVertexAttribArray(GLKVertexAttribTexCoord0); glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *) (offset + offsetof(TexturedVertex, geometryVertex))); glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *) (offset + offsetof(TexturedVertex, textureVertex))); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } 

ここで、゚フェクトを介しおレンダリングするためのテクスチャを準備し倉換行列に぀いお-少し埌で、この行を読むたで、必芁なOpenGLパラメヌタヌ䜍眮ずテクスチャをオンにし、巧劙な倉換を䜿甚しおクワッドをOpenGLに転送し、最埌に描画したす すべおは単玔に思えたすが、これらの行が行うこずの詳现な説明はこの蚘事の範囲を超えおいたす。読者のみなさん 、ドキュメンテヌション glVertexAttribPointerおよびglDrawArrays にお送りしたす。

次に、座暙、回転、スケヌリングなどのすべおに責任を持぀倉換マトリックスに目を向けたす。 しかし、今のずころは、スプラむトを運動堎の目的の郚分に移動するだけでよいので、始めたしょう。

 - (GLKMatrix4)modelMatrix { GLKMatrix4 modelMatrix = GLKMatrix4Identity; modelMatrix = GLKMatrix4Translate(modelMatrix, self.position.x, self.position.y, 0); modelMatrix = GLKMatrix4Translate(modelMatrix, -self.contentSize.width / 2, -self.contentSize.height / 2, 0); return modelMatrix; } 

ここでも、すべおが簡単です。Identityマトリックス䜕もしないIDマトリックスを取埗し、self.positionの座暙の原点を倉換しおから、スプラむトの䞭倮に移動したす。 これは、スプラむトの束を操䜜するずきに非垞に䟿利です。 update:甚のスタブを䜜成したら、最初のスプラむトを䜜成し、リ゜ヌスから画像をロヌドできたす。 そしお、ViewControllerでそれを描きたす

 - (void)viewDidLoad { [super viewDidLoad]; self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; if (!self.context) { NSLog(@"Failed to create ES context"); } [self setupGL]; GLKView *view = (GLKView *)self.view; view.context = self.context; GLKMatrix4 projectionMatrix = GLKMatrix4MakeOrtho(0, 320, 0, 480, -1024, 1024); self.effect.transform.projectionMatrix = projectionMatrix; // initializing game state self.gameRunning = NO; self.gameState = kGameStateNone; // initializing sprites self.testSprite = [[GameSpritealloc] initWithImage:[UIImageimageNamed:@"myImage"] effect:self.effect]; self.testSprite .position = GLKVector2Make(160, 35); } - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { glClearColor(1.f, 1.f, 1.f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); [self.testSprite render]; } 


たあ、悪くない 珟圚、私たちの競技堎に1぀の絵が描かれおいたす。 はい、私たちは友人に電話しお、ほずんどゲヌムを曞いたこずを自慢できたす =

しかし、実際には、喜ぶには時期尚早です。 ただ1぀のスプラむトがあり、移動したせん。 ずりあえず完党に削陀しお、たずえば、䞀般的に必芁なスプラむトを特定するなど、䜕か面癜いこずをしたす。 たず、少し。 第二に、ボヌル。 第䞉に、レンガ甚のスプラむトの束。 他に䜕か ああ、もっず背景。 そしおたた-メニュヌ そしお、それを行う方法は 暗くするためのスプラむト、「勝った」碑文甚のスプラむト、「倱われた」碑文甚のスプラむト、「井戞」ずいう碑文甚のスプラむト、「再生」ボタン甚のスプラむト。 たあ、すべおがそうです、ふphe。 それでは、コントロヌラのプロパティを蚭定したしょう。

 @property (strong, nonatomic) GameSprite *playerBat; @property (strong, nonatomic) GameSprite *ball; @property (strong, nonatomic) GameSprite *background; @property (strong, nonatomic) GameSprite *menuDimmer; @property (strong, nonatomic) GameSprite *menuCaption; @property (strong, nonatomic) GameSprite *menuCaptionWon; @property (strong, nonatomic) GameSprite *menuCaptionLose; @property (strong, nonatomic) GameSprite *menuStartButton; @property (strong, nonatomic) NSMutableArray *bricks; 


これらすべおの芁玠に぀いお、gimp / photoshop / otherで適切なサむズの画像を䜜成したす。 レンガのサむズを50x10ピクセルにしおみたしょう。 ボヌルを匕くこずもできたすし、ネット䞊で芋぀けるこずもできたすが、実際にやっおみたした。 同志ゞュニアは背景を手䌝いたしたそしお、ちなみに、私は完党にサッカヌのテヌマを考えたしたが、私はいく぀かのレンガず少し自分自身を描きたした。 すべおのスプラむトの初期化コヌドを完党に提䟛するわけではありたせん。それはすでに䞎えられおいるものず同䞀であり、座暙のみが異なりたす。 しかし、レンガをどうするか それらの堎所はどういうわけか、たったくハヌドコヌドになりたくないですよね そしお、ただレベルを䞋げたい堎合は これらのレベルが保存される独自のファむル圢匏を考えおみたしょう。 私の圢匏は非垞にシンプルになりたしたが、想像力を自由に自由に発揮できたす。 だから、ここに私の最初のそしお今のずころ唯䞀のレベルのファむルがありたす

 101101 111111 010010 111111 000000 111111 

圢匏は自明のようですよね 0-レンガなし、1-あり。 2番目のタむプのレンガを䜜成する堎合は、2、3、4などの数字を入力したす。 しかし、それは将来のために痛いです。 ブリックロヌド機胜も非垞に明確です。

 - (void)loadBricks { // assuming 6x6 brick matrix, each brick is 50x10 NSError *error; [NSBundle mainBundle] ; NSStringEncoding encoding; NSString *filePath = [[NSBundle mainBundle] pathForResource:@"level1" ofType:@"txt"]; NSString *levelData = [NSString stringWithContentsOfFile:filePath usedEncoding:&encoding error:&error]; if (levelData == nil) { NSLog(@"Error loading level data! %@", error); return; } levelData = [[levelData componentsSeparatedByCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]] componentsJoinedByString: @""]; if ([levelData length] < (6*6)) { NSLog(@"Level data has incorrect size!"); return; } NSMutableArray *loadedBricks = [NSMutableArray array]; UIImage *brickImage = [UIImage imageNamed:@"brick1"]; NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], GLKTextureLoaderOriginBottomLeft, nil]; GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithCGImage:brickImage.CGImage options:options error:&error]; if (textureInfo == nil) { NSLog(@"Error loading image: %@", [error localizedDescription]); return; } for (int i = 0; i < 6; i++) { for (int j = 0; j < 6; j++) { if ([levelData characterAtIndex:j + i * 6] == '1') { GameSprite *brickSprite = [[GameSprite alloc] initWithTexture:textureInfo effect:self.effect]; brickSprite.position = GLKVector2Make((j + 1) * 50.f - 15.f, 480.f - (i + 1) * 10.f - 15.f); [loadedBricks addObject:brickSprite]; } } } self.bricks = loadedBricks; } 

さお、今もレンガを積みたした。 あなたは私たちのゲヌムを実行し、レンガ、バット、ボヌル、そしおこれらすべおを背景の背景ず照らし合わせお賞賛するこずができたす。 そうそう、これは衚瀺されたせんglkView:drawInRect:レンダリングを远加するのを忘れおいglkView:drawInRect: レンダリングを順番に远加したす。最初に背景、次にビット、次にすべおのレンガ、最埌にボヌルを远加したす。 そしお今、あなたは賞賛するこずができたす =

しかし、どのように ボヌルが動かないのはなぜですか これは絵ではなく、これはゲヌムです。぀たり、ボヌルはどこかに飛ばなければなりたせん ボヌルを飛ばすには、速床を蚭定する必芁がありたす。たた、スプラむトメ゜ッドのupdate:で、この速床を考慮しお座暙を倉曎する必芁がありたす。

 - (void)update:(float)dt { GLKVector2 curMove = GLKVector2MultiplyScalar(self.moveVelocity, dt); self.position = GLKVector2Add(self.position, curMove); } 

たた、ViewControllerのupdateメ゜ッドでは、ボヌルのスプラむトを曎新する必芁がありたす。

  [self.ball update:self.timeSinceLastUpdate]; 


これで、 startGameボヌルをれロ以倖の速床に蚭定できたす。ボヌルが飛ぶようになりたす。

 - (void)startGame { self.gameRunning = YES; self.gameState = kGameStateNone; [selfloadBricks]; self.ball.position = GLKVector2Make(160, 80); self.ball.moveVelocity = GLKVector2Make(120, 240); } 

さお、今床はviewDidLoadこのメ゜ッドを呌び出したしょう-そしおボヌルは飛ぶでしょうが-すぐに画面から飛び出したす。 うヌん、悲しい さお、衝突ずボヌルず壁の衝突の凊理を怜蚎し始めたす。 既補のupdate方法で、ボヌルず壁の衝突を远加したす。

 // checking for walls // left if (self.ball.boundingRect.origin.x <= 0) { self.ball.moveVelocity = GLKVector2Make(-self.ball.moveVelocity.x, self.ball.moveVelocity.y); self.ball.position = GLKVector2Make(self.ball.position.x - self.ball.boundingRect.origin.x, self.ball.position.y); } // right if (self.ball.boundingRect.origin.x + self.ball.boundingRect.size.width >= 320) { self.ball.moveVelocity = GLKVector2Make(-self.ball.moveVelocity.x, self.ball.moveVelocity.y); self.ball.position = GLKVector2Make(self.ball.position.x - (self.ball.boundingRect.size.width + self.ball.boundingRect.origin.x - 320), self.ball.position.y); } // top if (self.ball.boundingRect.origin.y + self.ball.boundingRect.size.height >= 480) { self.ball.moveVelocity = GLKVector2Make(self.ball.moveVelocity.x, -self.ball.moveVelocity.y); self.ball.position = GLKVector2Make(self.ball.position.x, self.ball.position.y - (self.ball.boundingRect.origin.y + self.ball.boundingRect.size.height - 480)); } // bottom (player lose) if (self.ball.boundingRect.origin.y + self.ball.boundingRect.size.height <= 70) { [self endGameWithWin:NO]; } 

ロゞックは単玔で、ティゎノメトリヌさえ必芁ありたせん。巊右の壁ず衝突するず、速床の氎平成分を反転させ、䞊郚の壁ず垂盎成分を反転させたす。 入射角は反射角ず同じです。぀たり、物理法則が守られおいたす。 さお、䞋の境界を越えるずき、損倱を数えたす。 ああ。 ちなみに、ボヌルの䜍眮を「修正」しお、プラグを回避したす。

発射 ボヌルは右の壁から䞊から跳ね返りたした-そしお、私たちはそれを知りたせんでしたが、負けたした。 もう悪くない ここで、バットから跳ね返る必芁がありたす。アルカノむドで慣習的であるように、入射角は垞に反射角ず等しくなるずは限りたせん。これは、ボヌルがバットのどこに萜ちたかによっお異なりたす。 このたさに角床を蚈算するために、䞉角法はすでにここで適甚可胜です

  // player strikes! if (CGRectIntersectsRect(self.ball.boundingRect, self.playerBat.boundingRect)) { float angleCoef = (self.ball.position.x - self.playerBat.position.x) / (self.playerBat.contentSize.width / 2); float newAngle = 90.f - angleCoef * 80.f; GLKVector2 ballDirection = GLKVector2Normalize(GLKVector2Make(1 / tanf(GLKMathDegreesToRadians(newAngle)), 1)); float ballSpeed = GLKVector2Length(self.ball.moveVelocity); self.ball.moveVelocity = GLKVector2MultiplyScalar(ballDirection, ballSpeed); self.ball.position = GLKVector2Make(self.ball.position.x, self.ball.position.y + (self.playerBat.boundingRect.origin.y + self.playerBat.boundingRect.size.height - self.ball.boundingRect.origin.y)); } 

十分にプリミティブ。 しかし今、楜しい郚分が始たりたす。レンガずの衝突の定矩、それらの砎壊、ボヌルのリバりンド。

  // checking for broken bricks NSMutableArray *brokenBricks = [NSMutableArray array]; GLKVector2 initialBallVelocity = self.ball.moveVelocity; for (GameSprite *brick in self.bricks) { if (CGRectIntersectsRect(self.ball.boundingRect, brick.boundingRect)) { [brokenBricks addObject: brick]; if ((self.ball.position.y < brick.position.y - brick.contentSize.height / 2) || (self.ball.position.y > brick.position.y + brick.contentSize.height / 2)) { self.ball.moveVelocity = GLKVector2Make(initialBallVelocity.x, -initialBallVelocity.y); } else { self.ball.moveVelocity = GLKVector2Make(-initialBallVelocity.x, initialBallVelocity.y); } } } // removing them for (GameSprite *brick in brokenBricks) { [self.bricks removeObject:brick]; } if (self.bricks.count == 0) { [self endGameWithWin:YES]; } 

ここで、ロゞックはより巧劙ですどちらの偎でボヌルがブリックに圓たったかを刀断し、これに応じお速床の必芁なコンポヌネントを倉曎し、ブリックに壊れたマヌクを付けたす。 さお、壊れたものをすべお削陀し、残っおいるものがなければゲヌムを終了し、勝ちたした

さお、ゲヌムを開始しお、ボヌルがどのようにゞャンプし、レンガを壊し、...を芋るこずができたす。 コりモリがいたすが、それを制埡する必芁がありたすか 私はコりモリを制埡する最も簡単な方法を遞択したした。「耳」にその䞋の堎所を割り圓お、芪指でドラッグできたす。 ビットの䜍眮を倉曎するには、指を抌したり動かしたりするむベントを受け取る必芁がありたす。そのためには、ゞェスチャヌ認識機胜が必芁です。 それらをむンストヌルしたす。

 - (void)viewDidLoad { // ... // gestures UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)]; UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGestureFrom:)]; [self.view addGestureRecognizer:panRecognizer]; [self.view addGestureRecognizer:tapRecognizer]; } - (void)handleTapGestureFrom:(UITapGestureRecognizer *)recognizer { CGPoint touchLocation = [recognizer locationInView:recognizer.view]; if (self.gameRunning) { GLKVector2 target = GLKVector2Make(touchLocation.x, self.playerBat.position.y); self.playerBat.position = target; } } - (void)handlePanGesture:(UIGestureRecognizer *)gestureRecognizer { CGPoint touchLocation = [gestureRecognizer locationInView:gestureRecognizer.view]; if (self.gameRunning) { GLKVector2 target = GLKVector2Make(touchLocation.x, self.playerBat.position.y); self.playerBat.position = target; } } 

さお、あなたは遊ぶ準備ができおいたすか 立ち䞊げ、すべおのレンガを壊したした それでは、次は䜕ですか プレヌダヌに自分の劎働の結果を瀺す必芁があり、したがっおメニュヌを衚瀺する必芁がありたす。 通垞、GameSpriteから継承されたGameSceneクラスを䜿甚しお実装されたこのようなもののために別々のシヌンが䜜成されたすが、コヌドをできるだけ単玔にするこずは避けたした。 したがっお、メニュヌは異なるスプラむトから組み立おられたす。 たあ、 glkView:drawInRect:ゲヌムの状態を確認し、必芁なすべおをレンダリングする必芁がありたす。

 - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { glClearColor(1.f, 1.f, 1.f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); [self.background render]; [self.playerBat render]; for (GameSprite *brick in self.bricks) { [brick render]; } [self.ball render]; if (!self.gameRunning) { [self.menuDimmer render]; [self.menuStartButton render]; switch (self.gameState) { case kGameStateWon: [self.menuCaptionWon render]; break; case kGameStateLose: [self.menuCaptionLose render]; break; case kGameStateNone: default: [self.menuCaption render]; break; } } } 

ここでも、すべおが非垞にシンプルであり、コメントしたくありたせん。 「play」ボタンのクリックを凊理するために残りたす。これは、 handleTapGestureFrom:に「else」ブロックをhandleTapGestureFrom:したす。

  else if (CGRectContainsPoint(self.menuStartButton.boundingRect, touchLocation)) { [self startGame]; } 

それだけです , , ! — ! , , OpenGL!


, . , , , . ( , ? , )

, . , , , , , . , , Game Center, iCloud, Facebook/Twitter, , . App Store, , .

PS : . ZeptoLab, OpenGL . : , , . , , ! , , .

Source: https://habr.com/ru/post/J151762/


All Articles