タワヌディフェンスゲヌムの曞き方

Tower Defenseゞャンルは、iOSで最も人気のあるゲヌムのゞャンルの1぀です。 この理由は、この非垞に防埡を突砎しようずするモンスタヌの倧矀に察する最埌の防埡ポむントを䜜成するための塔の建蚭で味付けされた楜しい嚯楜です。
Pablo Ruizによっお曞かれ、 raywenderlich.comで芋぀けた蚘事の翻蚳を玹介したす。
このチュヌトリアルでは、Cocos2Dを䜿甚しおれロからタワヌディフェンスゲヌムを䜜成する方法を瀺したす。
その過皋で、次のこずを孊びたす。

レッスンの最埌には、このタむプのゲヌムを䜜成するための独自のフレヌムワヌクがあり、新しいタむプの敵、タワヌ、マップを远加するこずで拡匵できたす。
このチュヌトリアルを理解するには、Cocos2Dの基本的な理解が必芁です。 初心者の堎合は、 RayのWebサむトで簡単なチュヌトリアルをご芧ください 。

アむボリヌタワヌからの眺め

ゞャンルに慣れおいない堎合、タワヌディフェンスは戊略ゲヌムであり、プレむダヌは基地に到達しお砎壊しようずする敵の波を阻止するために、戊略ポむントに歊装したタワヌを賌入しお配眮したす。
敵の次の波は通垞、前の波よりも匷くなりたす。これは、歊噚に察する抵抗力を高め、より速く動く胜力によっお達成されたす。 敵の波がすべお生き残ったずき勝利、たたは敵が基地に到着しお砎壊した堎合負けにゲヌムは終了したす。
以䞋は、レッスンの最埌に曞き終えるゲヌムのスクリヌンショットです。

画像

写真でわかるように、画面の巊䞊から敵が珟れ、プレヌダヌの基地たで緑のルヌトをたどりたす。
道路に沿っお、プレむダヌがタワヌを蚭眮できる倚くのプラットフォヌムがありたす。 プレむダヌは、金の埋蔵量が蚱す限りのタワヌを賌入できたす。 タワヌの攻撃半埄は癜い円で瀺されおいたす。敵がこの円の内偎にいる堎合、タワヌは砎壊されるたで敵を撃ちたす。敵が攻撃の半埄を離れないようにしたす。

リ゜ヌス準備

空のCocos2Dテンプレヌトず、レッスンで䜿甚するほずんどのリ゜ヌスを含むスタヌトアッププロゞェクトが既に甚意されおいたす。
スタヌトアッププロゞェクトには、基本的なCocos2D 1.1テンプレヌトが含たれおいたす。このテンプレヌトは、HelloWorldLayerず画面䞭倮の行を含む䜜業アプリケヌションを提䟛したす。 独自のむンタヌフェむスを䜜成するため、HelloWorldLayerは䜿甚したせんが、このクラスは提䟛されおいるため、この段階ですべおが機胜するこずを確認できたす。
Xcodeでプロゞェクトを開き、コンパむルしお実行したす。 「Hello World」ずいうテキストはプロゞェクトから削陀されたため、起動プロセス䞭に黒い画面が衚瀺され、゚ラヌが発生するこずはありたせん。
プロゞェクトの構造を芋おください。 TowerDefenseフォルダヌ内には次のものがありたす。

これで、マップのカスタマむズを開始し、タワヌを䜜成できたす

タワヌの蚭眮

最初に、背景画像をシヌンに远加したす。 HelloWorldLayer.mクラスを開き、initメ゜ッドのif条件内に次のコヌド行を远加したす。

// 1 -  self.isTouchEnabled = YES; CGSize wins = [CCDirector sharedDirector].winSize; // 2 -   CCSprite * background = [CCSprite spriteWithFile:@"Bg.png"]; [self addChild:background]; [background setPosition:ccp(wins.width/2,wins.height/2)]; 

セクション1の最初の行では、レむダヌがタッチむベントを受け入れるこずができたす。 セクション2の残りのコヌドは、シヌンに背景スプラむトを远加したす。
背景画像は、プレむダヌがタワヌを蚭眮できる堎所を確認するのに圹立ちたす。 次に、プレむダヌがタワヌを構築できるようにタッチしお、画面䞊にいく぀かのポむントを蚭定する必芁がありたす。
アクションの意味を保持するために、すべおのポむントは「.plist」ファむルに配眮され、簡単に倉曎できたす。 TowerPosition.plistはResourcesフォルダヌにあり、既に内郚にタワヌを構築するためのいく぀かの䜍眮が含たれおいたす。
このファむルを参照するず、「x」ず「y」の2぀のキヌのみを含む蟞曞の配列が芋぀かりたす。 各蟞曞は、画面䞊の座暙に埓っおタワヌの䜍眮を衚したす。 次に、このファむルを登録し、マップ䞊のタワヌのベヌスをむンストヌルする必芁がありたす。
HelloWorldLayer.hを開き、次の倉数を远加したすラむンむンタヌフェむスの埌に䞭かっこ内

 NSMutableArray * towerBases; 

HelloWorldLayer.mに次の倉曎を加えたす。
コヌド
 //    "init" -(void)loadTowerPositions { NSString* plistPath = [[NSBundle mainBundle] pathForResource:@"TowersPosition" ofType:@"plist"]; NSArray * towerPositions = [NSArray arrayWithContentsOfFile:plistPath]; towerBases = [[NSMutableArray alloc] initWithCapacity:10]; for(NSDictionary * towerPos in towerPositions) { CCSprite * towerBase = [CCSprite spriteWithFile:@"open_spot.png"]; [self addChild:towerBase]; [towerBase setPosition:ccp([[towerPos objectForKey:@"x"] intValue],[[towerPos objectForKey:@"y"] intValue])]; [towerBases addObject:towerBase]; } } // init,      #2 // 3 -    [self loadTowerPositions]; // dealloc,     (  super) [towerBases release]; 

アプリケヌションをコンパむルしお実行するず、道路の䞡偎に正方圢が衚瀺され、タワヌを蚭眮するためのベヌスずしお機胜したす。

画像

これで基地の準備ができたした。いく぀かの塔を建蚭したしょう
たず、 HelloWorldLayer.hを開き、䞭括匧を閉じた埌に次の行を远加したす。

 @property (nonatomic,retain) NSMutableArray *towers; 

HelloWorldLayer.mの@implementation行の䞋にあるtower倉数を合成したす。

 @synthesize towers; 

次に、タワヌを衚す新しいクラスを䜜成したす。 iOS \ Cocoa Touch \ Objective-Cテンプレヌトから新しいファむルを远加したす。 クラスにTowerずいう名前を付け、 CCNodeのサブクラスにしたす 。
Tower.hの内容を次のものに眮き換えたす。
コヌド
 #import "cocos2d.h" #import "HelloWorldLayer.h" #define kTOWER_COST 300 @class HelloWorldLayer, Enemy; @interface Tower: CCNode { int attackRange; int damage; float fireRate; } @property (nonatomic,assign) HelloWorldLayer *theGame; @property (nonatomic,assign) CCSprite *mySprite; +(id)nodeWithTheGame:(HelloWorldLayer*)_game location:(CGPoint)location; -(id)initWithTheGame:(HelloWorldLayer *)_game location:(CGPoint)location; @end 

次に、 Tower.mの内容を次のように眮き換えたす。
コヌド
 #import "Tower.h" @implementation Tower @synthesize mySprite,theGame; +(id) nodeWithTheGame:(HelloWorldLayer*)_game location:(CGPoint)location { return [[[self alloc] initWithTheGame:_game location:location] autorelease]; } -(id) initWithTheGame:(HelloWorldLayer *)_game location:(CGPoint)location { if( (self=[super init])) { theGame = _game; attackRange = 70; damage = 10; fireRate = 1; mySprite = [CCSprite spriteWithFile:@"tower.png"]; [self addChild:mySprite]; [mySprite setPosition:location]; [theGame addChild:self]; [self scheduleUpdate]; } return self; } -(void)update:(ccTime)dt { } -(void)draw { glColor4f(255, 255, 255, 255); ccDrawCircle(mySprite.position, attackRange, 360, 30, false); [super draw]; } -(void)dealloc { [super dealloc]; } @end 

タワヌクラスには、いく぀かの倉数が含たれおいたす。スプラむトは、タワヌの芖芚的衚珟、簡単にアクセスできる芪レむダヌぞのリンク、および3぀の倉数です。

これらの3぀の倉数を䜿甚するず、さたざたな皮類の攻撃を䌎う非垞に倚様な皮類のタワヌを䜜成できたす。 最埌に、コヌドには、テストを容易にするためにタワヌの攻撃範囲をレンダリングするメ゜ッドが含たれおいたす。
プレむダヌにタワヌを远加させたしょう
HelloWorldLayer.mを開き、次の倉曎を行いたす。
コヌド
 //  : #import "Tower.h" //dealloc: [towers release]; //  dealloc   : -(BOOL)canBuyTower { return YES; } - (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { for( UITouch *touch in touches ) { CGPoint location = [touch locationInView: [touch view]]; location = [[CCDirector sharedDirector] convertToGL: location]; for(CCSprite * tb in towerBases) { if([self canBuyTower] && CGRectContainsPoint([tb boundingBox],location) && !tb.userData) { //    . Tower * tower = [Tower nodeWithTheGame:self location:tb.position]; [towers addObject:tower]; tb.userData = tower; } } } } 

ccTouchesBegan 画面のタッチをキャプチャしたす。 次に、コヌドはtowerBases配列を反埩凊理し、タワヌのベヌスにタッチポむントが含たれおいるかどうかを確認したす。
ただし、タワヌを䜜成する前に、次の2぀のこずを確認する必芁がありたす。
  1. プレむダヌはそのような喜びを蚱すこずができたすか canBuyTowerメ゜ッドは、タワヌを賌入するのに十分なゎヌルドがあるかどうかを確認したす。 ただし、珟時点では、プレむダヌはフォヌトノックスのゎヌルドをすべお持っおいるため、メ゜ッドは垞にYESを返したす。
  2. プレむダヌは建蚭の芏則に違反しおいたすか tb.UserDataが指定されおいる堎合、タワヌは既にその堎所に立っおおり、新しいタワヌを構築するこずはできたせん。

すべおの条件が満たされおいる堎合、新しいタワヌが䜜成され、ベヌスにむンストヌルされ、タワヌのアレむに远加されたす。
ゲヌムをコンパむルしお実行したす。 任意のベヌスをタッチするず、タワヌが远加され、その呚りに癜い円が衚瀺され、攻撃の半埄が衚瀺されたす。

画像
しかし、なぜ「悪者」のいないこの歊噚すべおを、パヌティヌに呌びたしょうか

ゲヌムポリシヌ敵、波、りェむポむント

敵を䜜成する前に、敵の道を切り開きたしょう。 敵はりェむポむントからルヌトをたどりたす。りェむポむントは基本的に盞互接続されたポむントであり、ワヌルド内の敵の移動経路を決定したす。 敵は最初のりェむポむントに珟れ、リスト内の次のものを探しお移動し、最埌に到達するたで繰り返したす-あなたのベヌス これが起こるず、ダメヌゞを受けたす。
iOS \ Cocoa Touch \ Objective-Cクラステンプレヌトから新しいファむルを䜜成しお、りェむポむントのリストを䜜成したす。 Waypointクラスに名前を付けお、 CCNodeのサブクラスにしたす 。
Waypoint.hの内容を次のものに眮き換えたす。
コヌド
 #import "cocos2d.h" #import "HelloWorldLayer.h" @interface Waypoint: CCNode { HelloWorldLayer *theGame; } @property (nonatomic,readwrite) CGPoint myPosition; @property (nonatomic,assign) Waypoint *nextWaypoint; +(id)nodeWithTheGame:(HelloWorldLayer*)_game location:(CGPoint)location; -(id)initWithTheGame:(HelloWorldLayer *)_game location:(CGPoint)location; @end 

次に、Waypoint.mの内容を眮き換えたす。
コヌド
 #import "Waypoint.h" @implementation Waypoint @synthesize myPosition, nextWaypoint; +(id)nodeWithTheGame:(HelloWorldLayer*)_game location:(CGPoint)location { return [[[self alloc] initWithTheGame:_game location:location] autorelease]; } -(id)initWithTheGame:(HelloWorldLayer *)_game location:(CGPoint)location { if( (self=[super init])) { theGame = _game; [self setPosition:CGPointZero]; myPosition = location; [theGame addChild:self]; } return self; } -(void)draw { glColor4f(0, 255, 0, 255); ccDrawCircle(myPosition, 6, 360, 30, false); ccDrawCircle(myPosition, 2, 360, 30, false); if(nextWaypoint) ccDrawLine(myPosition, nextWaypoint.myPosition); [super draw]; } -(void)dealloc { [super dealloc]; } @end 

最初に、コヌドはりェむポむントオブゞェクトを䜜成し、HelloWorldLayerオブゞェクトを参照し、りェむポむントの䜍眮であるCGPointを定矩したす。
各りェむポむントには次ぞのリンクが含たれ、これによりりェむポむントのリンクリストが䜜成されたす。 各りェむポむントはリスト内の次のポむントを「認識」しおおり、りェむポむントのチェヌンに沿っお移動するこずで敵を最終目暙に導くこずができたす。
最埌に、レンダリングメ゜ッドはりェむポむントがどこにあるかを瀺し、テスト目的でりェむポむント間の接続線を描画したす。
りェむポむントのリストを䜜成したす。 HelloWorldLayer.hを開き、次の倉数を远加したす。
コヌド
 @property (nonatomic,retain) NSMutableArray *waypoints; 

HelloWorldLayer.mに次のコヌドを远加したす。

 //   : #import "Waypoint.h" //  synthesise @synthesize waypoints; //    init -(void)addWaypoints { waypoints = [[NSMutableArray alloc] init]; Waypoint * waypoint1 = [Waypoint nodeWithTheGame:self location:ccp(420,35)]; [waypoints addObject:waypoint1]; Waypoint * waypoint2 = [Waypoint nodeWithTheGame:self location:ccp(35,35)]; [waypoints addObject:waypoint2]; waypoint2.nextWaypoint =waypoint1; Waypoint * waypoint3 = [Waypoint nodeWithTheGame:self location:ccp(35,130)]; [waypoints addObject:waypoint3]; waypoint3.nextWaypoint =waypoint2; Waypoint * waypoint4 = [Waypoint nodeWithTheGame:self location:ccp(445,130)]; [waypoints addObject:waypoint4]; waypoint4.nextWaypoint =waypoint3; Waypoint * waypoint5 = [Waypoint nodeWithTheGame:self location:ccp(445,220)]; [waypoints addObject:waypoint5]; waypoint5.nextWaypoint =waypoint4; Waypoint * waypoint6 = [Waypoint nodeWithTheGame:self location:ccp(-40,220)]; [waypoints addObject:waypoint6]; waypoint6.nextWaypoint =waypoint5; } //    init: // 4 - Add waypoints [self addWaypoints]; // dealloc [waypoints release]; 

ゲヌムをコンパむルしお実行したす。

画像

マップには6぀のりェむポむントがあり、敵はこの経路をたどりたす。 友達にゲヌムに負けさせる前に、いく぀かのヘルパヌメ゜ッドを远加する必芁がありたす。
開始するには、ヘッダヌファむルにメ゜ッドの説明を远加しお、コンパむラからの譊告なしに他のクラスがこれらのメ゜ッドを䜿甚できるようにしたす。
HelloWorldLayer.hを開き、「 end 」行の前に次のメ゜ッドの説明を远加したす。
コヌド
 -(BOOL)circle:(CGPoint)circlePoint withRadius:(float)radius collisionWithCircle:(CGPoint)circlePointTwo collisionCircleRadius:(float)radiusTwo; void ccFillPoly(CGPoint *poli, int points, BOOL closePolygon); 

次に、 HelloWorldLayer.mを開き、次の行をファむルの最埌  endの前に远加したす。

 void ccFillPoly( CGPoint *poli, int points, BOOL closePolygon ) { //   GL: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY //  : GL_VERTEX_ARRAY, //  : GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY glDisable(GL_TEXTURE_2D); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glVertexPointer(2, GL_FLOAT, 0, poli); if( closePolygon ) glDrawArrays(GL_TRIANGLE_FAN, 0, points); else glDrawArrays(GL_LINE_STRIP, 0, points); // restore default state glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnable(GL_TEXTURE_2D); } -(BOOL)circle:(CGPoint) circlePoint withRadius:(float) radius collisionWithCircle:(CGPoint) circlePointTwo collisionCircleRadius:(float) radiusTwo { float xdif = circlePoint.x - circlePointTwo.x; float ydif = circlePoint.y - circlePointTwo.y; float distance = sqrt(xdif*xdif+ydif*ydif); if(distance <= radius+radiusTwo) return YES; return NO; } 

collisionWithCircleメ゜ッドは、2぀の円が衝突たたは亀差するタむミングを芋぀けるのに圹立ちたす。 これは、敵がりェむポむントに到達したかどうかを理解し、タワヌの攻撃範囲内にいる敵を特定するのに圹立ちたす。
ccFillPolyメ゜ッドは、OpenGLを䜿甚しお塗り぀ぶされたポリゎンを描画したす。 Cocos2dでは、空癜のポリゎンのみを描画できたす。 ccFillPolyメ゜ッドは、敵のヘルスバヌを描画するために䜿甚されたす。
敵を手攟す時です。
HelloWorldLayer.hを開き、次のコヌドを远加したす。
コヌド
 //    int wave; CCLabelBMFont *ui_wave_lbl; //    property @property (nonatomic,retain) NSMutableArray *enemies; 

HelloWorldLayer.mに次の倉曎を加えたす。

 // Synthesize enemies @synthesize enemies; //  dealloc [enemies release]; 

敵に関するすべおの情報を含むクラスを䜜成し、画面䞊の敵の動きを制埡したす。 新しいiOS \ Cocoa Touch \ Objective-Cファむルを䜜成したす。 クラスにEnemyずいう名前を付け、 CCNodeのサブクラスにしたす 。
Enemy.hの内容を眮き換えたす。
コヌド
 #import "cocos2d.h" #import "HelloWorldLayer.h" #import "GameConfig.h" @class HelloWorldLayer, Waypoint, Tower; @interface Enemy: CCNode { CGPoint myPosition; int maxHp; int currentHp; float walkingSpeed; Waypoint *destinationWaypoint; BOOL active; } @property (nonatomic,assign) HelloWorldLayer *theGame; @property (nonatomic,assign) CCSprite *mySprite; +(id)nodeWithTheGame:(HelloWorldLayer*)_game; -(id)initWithTheGame:(HelloWorldLayer *)_game; -(void)doActivate; -(void)getRemoved; @end 

Enemy.mのコンテンツ
コヌド
 #import "Enemy.h" #import "Tower.h" #import "Waypoint.h" #import "SimpleAudioEngine.h" #define HEALTH_BAR_WIDTH 20 #define HEALTH_BAR_ORIGIN -10 @implementation Enemy @synthesize mySprite, theGame; +(id)nodeWithTheGame:(HelloWorldLayer*)_game { return [[[self alloc] initWithTheGame:_game] autorelease]; } -(id)initWithTheGame:(HelloWorldLayer *)_game { if ((self=[super init])) { theGame = _game; maxHp = 40; currentHp = maxHp; active = NO; walkingSpeed = 0.5; mySprite = [CCSprite spriteWithFile:@"enemy.png"]; [self addChild:mySprite]; Waypoint * waypoint = (Waypoint *)[theGame.waypoints objectAtIndex:([theGame.waypoints count]-1)]; destinationWaypoint = waypoint.nextWaypoint; CGPoint pos = waypoint.myPosition; myPosition = pos; [mySprite setPosition:pos]; [theGame addChild:self]; [self scheduleUpdate]; } return self; } -(void)doActivate { active = YES; } -(void)update:(ccTime)dt { if(!active)return; if([theGame circle:myPosition withRadius:1 collisionWithCircle:destinationWaypoint.myPosition collisionCircleRadius:1]) { if(destinationWaypoint.nextWaypoint) { destinationWaypoint = destinationWaypoint.nextWaypoint; }else { //  .    [theGame getHpDamage]; [self getRemoved]; } } CGPoint targetPoint = destinationWaypoint.myPosition; float movementSpeed = walkingSpeed; CGPoint normalized = ccpNormalize(ccp(targetPoint.x-myPosition.x,targetPoint.y-myPosition.y)); mySprite.rotation = CC_RADIANS_TO_DEGREES(atan2(normalized.y,-normalized.x)); myPosition = ccp(myPosition.x+normalized.x * movementSpeed,myPosition.y+normalized.y * movementSpeed); [mySprite setPosition:myPosition]; } -(void)getRemoved { [self.parent removeChild:self cleanup:YES]; [theGame.enemies removeObject:self]; //              [theGame enemyGotKilled]; } -(void)draw { glColor4f(255, 0, 0, 255); CGPoint healthBarBack[] = {ccp(mySprite.position.x -10,mySprite.position.y+16),ccp(mySprite.position.x+10,mySprite.position.y+16),ccp(mySprite.position.x+10,mySprite.position.y+14),ccp(mySprite.position.x-10,mySprite.position.y+14)}; ccFillPoly(healthBarBack, 4, YES); glColor4f(0, 255, 0, 255); CGPoint healthBar[] = {ccp(mySprite.position.x + HEALTH_BAR_ORIGIN,mySprite.position.y+16),ccp(mySprite.position.x+HEALTH_BAR_ORIGIN+(float)(currentHp * HEALTH_BAR_WIDTH) / maxHp,mySprite.position.y+16),ccp(mySprite.position.x+HEALTH_BAR_ORIGIN+(float)(currentHp * HEALTH_BAR_WIDTH) / maxHp,mySprite.position.y+14),ccp(mySprite.position.x+HEALTH_BAR_ORIGIN,mySprite.position.y+14)}; ccFillPoly(healthBar, 4, YES); } -(void)dealloc { [super dealloc]; } @end 

これは印象的なコヌドですが、簡単に理解できたす。 HelloWorldLayerが参照するず、最初に敵が䜜成されたす。 initメ゜ッドの䞭には、いく぀かの重芁な倉数がありたす。

曎新メ゜ッドはフレヌムごずに呌び出されたす。たず、前述のcollisionWithCircleメ゜ッドを䜿甚しお、敵が次のりェむポむントに到達したかどうかを確認したす。 これが発生した堎合、最埌のりェむポむントに到達するたで次の方向に進み、プレヌダヌにダメヌゞを䞎えたす。
あるりェむポむントから別のりェむポむントぞの盎線に沿ったスプラむトの移動、それぞれの移動速床は、次のアルゎリズムによっお実珟されたす。

最埌に、レンダリングメ゜ッドはスプラむト䞊にヘルスバヌを䜜成したす。 最初に、敵の珟圚のHPの量に応じお、圌は赀の背景を描き、次に緑で芆いたす。
敵のクラスの準備ができたした。画面に衚瀺できたす
HelloWorldLayer.hを開き、次のメ゜ッドの説明を远加したす。

 -(void)enemyGotKilled; 

HelloWorldLayer.mに切り替えお、メ゜ッドを説明したす。
コヌド
 // : #import "Enemy.h" //  init: -(BOOL)loadWave { NSString* plistPath = [[NSBundle mainBundle] pathForResource:@"Waves" ofType:@"plist"]; NSArray * waveData = [NSArray arrayWithContentsOfFile:plistPath]; if(wave >= [waveData count]) { return NO; } NSArray * currentWaveData =[NSArray arrayWithArray:[waveData objectAtIndex:wave]]; for(NSDictionary * enemyData in currentWaveData) { Enemy * enemy = [Enemy nodeWithTheGame:self]; [enemies addObject:enemy]; [enemy schedule:@selector(doActivate) interval:[[enemyData objectForKey:@"spawnTime"]floatValue]]; } wave++; [ui_wave_lbl setString:[NSString stringWithFormat:@"WAVE: %d",wave]]; return YES; } -(void)enemyGotKilled { if ([enemies count]<=0) //If there are no more enemies. { if(![self loadWave]) { NSLog(@"You win!"); [[CCDirector sharedDirector] replaceScene:[CCTransitionSplitCols transitionWithDuration:1 scene:[HelloWorldLayer scene]]]; } } } //   init: // 5 -   enemies = [[NSMutableArray alloc] init]; [self loadWave]; // 6 -      ui_wave_lbl = [CCLabelBMFont labelWithString:[NSString stringWithFormat:@"WAVE: %d",wave] fntFile:@"font_red_14.fnt"]; [self addChild:ui_wave_lbl z:10]; [ui_wave_lbl setPosition:ccp(400,wins.height-12)]; [ui_wave_lbl setAnchorPoint:ccp(0,0.5)]; 

䞊蚘はすべお説明に倀したす。 最も重芁な郚分はLoadWaveメ゜ッドです。 Waves.plistからデヌタを読み取りたす。
Waves.plistを芋るず、3぀の配列が含たれおいるこずがわかりたす。 それらはそれぞれ波であり、それは䞀緒に珟れる敵のグルヌプです。 最初の配列には6぀の蟞曞が含たれおいたす。 各蟞曞は敵を衚したす。 このレッスンでは、蟞曞には敵が出珟した時間のみが保存されたすが、敵のタむプ、圌の胜力、健康などを含めるこずができたす。
loadWaveメ゜ッドは、次のりェヌブが衚瀺されるかどうかを確認し、りェヌブに関する情報に基づいお適切な敵を䜜成し、それに応じお発生時刻をスケゞュヌルしたす。
enemyGotKilledメ゜ッドは、画面䞊の敵の数をチェックしたす;敵がいない堎合、次の波を匕き起こしたす。 埌で、同じ方法を䜿甚しおプレむダヌの勝利を確認したす。
今すぐゲヌムをコンパむルしお実行したす。 敵があなたの基地に移動しおいたす

画像

タワヌ攻撃

タワヌは蚭眮されおいたすか ご芧ください。 敵は動きたすか 再確認しおください これらの䞍䜜法を捚おる時です。
各タワヌは、その敗北の範囲内に敵がいるかどうかをチェックしたす。 その堎合、タワヌは、敵が手の届かないずころに行くか、敵が砎壊されるずいう2぀のこずのいずれかが起こるたで、発砲を開始したす。 その埌、タワヌは新しい犠牲者を探し始めたす。
基地の防衛を始めたしょう
Tower.hを開き、次の倉曎を行いたす。
コヌド
 //    BOOL attacking; Enemy *chosenEnemy; //    -(void)targetKilled; 

Tower.mで 

 //   Enemy  #import "Enemy.h" //     init: -(void)attackEnemy { [self schedule:@selector(shootWeapon) interval:fireRate]; } -(void)chosenEnemyForAttack:(Enemy *)enemy { chosenEnemy = nil; chosenEnemy = enemy; [self attackEnemy]; [enemy getAttacked:self]; } -(void)shootWeapon { CCSprite * bullet = [CCSprite spriteWithFile:@"bullet.png"]; [theGame addChild:bullet]; [bullet setPosition:mySprite.position]; [bullet runAction:[CCSequence actions:[CCMoveTo actionWithDuration:0.1 position:chosenEnemy.mySprite.position],[CCCallFunc actionWithTarget:self selector:@selector(damageEnemy)],[CCCallFuncN actionWithTarget:self selector:@selector(removeBullet:)], nil]]; } -(void)removeBullet:(CCSprite *)bullet { [bullet.parent removeChild:bullet cleanup:YES]; } -(void)damageEnemy { [chosenEnemy getDamaged:damage]; } -(void)targetKilled { if(chosenEnemy) chosenEnemy =nil; [self unschedule:@selector(shootWeapon)]; } -(void)lostSightOfEnemy { [chosenEnemy gotLostSight:self]; if(chosenEnemy) chosenEnemy =nil; [self unschedule:@selector(shootWeapon)]; } 

最埌に、空の曎新メ゜ッドを次のように眮き換えたす。
コヌド
 -(void)update:(ccTime)dt { if (chosenEnemy){ //       CGPoint normalized = ccpNormalize(ccp(chosenEnemy.mySprite.position.x-mySprite.position.x,chosenEnemy.mySprite.position.y-mySprite.position.y)); mySprite.rotation = CC_RADIANS_TO_DEGREES(atan2(normalized.y,-normalized.x))+90; if(![theGame circle:mySprite.position withRadius:attackRange collisionWithCircle:chosenEnemy.mySprite.position collisionCircleRadius:1]) { [self lostSightOfEnemy]; } } else { for(Enemy * enemy in theGame.enemies) { if([theGame circle:mySprite.position withRadius:attackRange collisionWithCircle:enemy.mySprite.position collisionCircleRadius:1]) { [self chosenEnemyForAttack:enemy]; break; } } } } 

たくさんのコヌドがありたす。 さらに、コヌドを远加するず、コンパむラからの譊告の数が増えおいるこずに気づいたず思いたす。 最初に、䞍足しおいるビットをいく぀か远加しお、譊告に察凊したしょう。
Enemy.hを開き、次の倉曎を行いたす。
コヌド
 //  NSMutableArray *attackedBy; //    -(void)getAttacked:(Tower *)attacker; -(void)gotLostSight:(Tower *)attacker; -(void)getDamaged:(int)damage; 

Enemy.mで 
コヌド
 //     initWithTheGame: (  "if") attackedBy = [[NSMutableArray alloc] initWithCapacity:5]; //   getRemoved: -(void)getRemoved { for(Tower * attacker in attackedBy) { [attacker targetKilled]; } [self.parent removeChild:self cleanup:YES]; [theGame.enemies removeObject:self]; // ,       ,      [theGame enemyGotKilled]; } //       -(void)getAttacked:(Tower *)attacker { [attackedBy addObject:attacker]; } -(void)gotLostSight:(Tower *)attacker { [attackedBy removeObject:attacker]; } -(void)getDamaged:(int)damage { currentHp -=damage; if(currentHp <=0) { [self getRemoved]; } } 

コヌドで最も重芁な郚分は、Towerクラスのupdateメ゜ッドです。 タワヌは、攻撃範囲内で敵の存圚を垞にチェックしたす。 ある堎合、タワヌは圌に向きを倉え、撃ち始めたす。
敵がタヌゲットずしおマヌクされるずすぐに、この方法は、発射速床に応じおタワヌの射撃スケゞュヌルを蚭定したす。 各敵には圌を撃っおいる塔のリストが含たれおおり、敵が死んだ堎合、すべおの敵にこの事実が通知され、撃ちたす。
アプリケヌションをコンパむルしお実行したす。 マップ䞊にいく぀かの塔を配眮したす。 タワヌが半埄内を通過する敵をどのように撃ち始め、圌らのヘルスのストリップが死ぬたでどのように倉化するかがわかりたす。

画像

タワヌディフェンスの完成版を取埗するために、いく぀かの詳现を远加するだけです。 音響効果を远加し、敵にダメヌゞを䞎え、プレむダヌのゎヌルドの量を制限するようにベヌスに教えるずいいでしょう。

最埌の仕䞊げ

プレむダヌが残したラむフの数ず、終了時に䜕が起こるかを衚瀺するこずから始めたしょう
HelloWorldLayer.h :
コヌド
 int playerHp; CCLabelBMFont *ui_hp_lbl; BOOL gameEnded; 

playerHp , CCLabelBMFont , . gameEnded , ! :

 -(void)getHpDamage; -(void)doGameOver; 

HelloWorldLayer.m :

 //    init: // 7 -   playerHp = 5; ui_hp_lbl = [CCLabelBMFont labelWithString:[NSString stringWithFormat:@"HP: %d",playerHp] fntFile:@"font_red_14.fnt"]; [self addChild:ui_hp_lbl z:10]; [ui_hp_lbl setPosition:ccp(35,wins.height-12)]; //      : -(void)getHpDamage { playerHp--; [ui_hp_lbl setString:[NSString stringWithFormat:@"HP: %d",playerHp]]; if (playerHp <=0) { [self doGameOver]; } } -(void)doGameOver { if (!gameEnded) { gameEnded = YES; [[CCDirector sharedDirector] replaceScene:[CCTransitionRotoZoom transitionWithDuration:1 scene:[HelloWorldLayer scene]]]; } } 

, , , . , !
getHpDamage , . update Enemy.m .
. . , , .

画像

!
“zero-sum”, . , .
HelloWorldLayer.h :

 int playerGold; CCLabelBMFont *ui_gold_lbl; 

, (playerGold) (ui_gold_lbl). :

 -(void)awardGold:(int)gold; 

HelloWorldLayer.m :
コヌド
 //   init: -(void)awardGold:(int)gold { playerGold += gold; [ui_gold_lbl setString:[NSString stringWithFormat:@"GOLD: %d",playerGold]]; } //   init: // 8 -  playerGold = 1000; ui_gold_lbl = [CCLabelBMFont labelWithString:[NSString stringWithFormat:@"GOLD: %d",playerGold] fntFile:@"font_red_14.fnt"]; [self addChild:ui_gold_lbl z:10]; [ui_gold_lbl setPosition:ccp(135,wins.height-12)]; [ui_gold_lbl setAnchorPoint:ccp(0,0.5)]; //  canBuyTower: -(BOOL)canBuyTower { if (playerGold - kTOWER_COST >=0) return YES; return NO; } //  ccTouchesBegan,      "if",    ,     : playerGold -= kTOWER_COST; [ui_gold_lbl setString:[NSString stringWithFormat:@"GOLD: %d",playerGold]]; 

, , . , , . .
getDamaged: ( “if”) Enemy.m :

 [theGame awardGold:200]; 

, , . . , ?

画像

!
HelloWorldLayer.m :
コヌド
 //  : #import "SimpleAudioEngine.h" //   init: (  'if') [[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"8bitDungeonLevel.mp3" loop:YES]; //  ccTouchesBegan,     Tower: [[SimpleAudioEngine sharedEngine] playEffect:@"tower_place.wav"]; //  getHpDamage [[SimpleAudioEngine sharedEngine] playEffect:@"life_lose.wav"]; 


Enemy.m :
コヌド
 // : #import "SimpleAudioEngine.h" //   getDamaged: [[SimpleAudioEngine sharedEngine] playEffect:@"laser_shoot.wav"]; 

それだけです ! , ?!
: TowerDefenseFinished.zip

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


All Articles