青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

兔子的技術(shù)博客

兔子

   :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
  202 Posts :: 0 Stories :: 43 Comments :: 0 Trackbacks

留言簿(10)

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

免責(zé)申明(必讀!):本博客提供的所有教程的翻譯原稿均來自于互聯(lián)網(wǎng),僅供學(xué)習(xí)交流之用,切勿進(jìn)行商業(yè)傳播。同時(shí),轉(zhuǎn)載時(shí)不要移除本申明。如產(chǎn)生任何糾紛,均與本博客所有人、發(fā)表該翻譯稿之人無任何關(guān)系。謝謝合作!

原文鏈接地址:http://www.raywenderlich.com/3997/introduction-to-augmented-reality-on-the-iphone

教程截圖:

Create a simple augmented reality game where you can blast some aliens!

Create a simple augmented reality game where you can blast some aliens!

  在這篇教程中,你將學(xué)習(xí)到如何為你的iphone和ipod touch制作一個(gè)簡單的增強(qiáng)現(xiàn)實(shí)游戲。

  在這個(gè)游戲中,你將使用到攝像頭,陀螺儀和cocos2d框架。聽起來很振奮人心吧?

  在寫作這篇教程的時(shí)候,探索上面提到的一些技術(shù)的過程真的是非常有趣。這里有一些數(shù)學(xué)和轉(zhuǎn)換,不過不要擔(dān)心---沒有什么事情是很難的!

  學(xué)習(xí)這篇教程的時(shí)候,你需要一個(gè)iPhone4,因?yàn)檫@個(gè)教程使用陀螺儀來移動(dòng)游戲世界視圖。

  你也需要一些基本的cocos2d方面的知識(shí),當(dāng)然要安裝好cocos2d。如果你對(duì)于cocos2d完全陌生的話,你可以先看看本博客上的其它c(diǎn)ocos2d教程

  你準(zhǔn)備好爆頭一些虛擬外星人了嗎?跟我來吧!

 

Getting Started

  打開Xcode,然后從File菜單中選擇New\New Project。然后選擇 iOS\cocos2d\cocos2d template,接著點(diǎn)擊Next。把工程命名為ARSpaceShips,并點(diǎn)擊Next,同時(shí)選擇一個(gè)文件夾位置來保存本項(xiàng)目,最后點(diǎn)Create。

  我們將重用Space Shooter游戲的一些資源文件,所以,先下載它們并解壓縮。

  下載完后,把Fonts,Sounds和Spritesheet文件夾拖到Resources分組下面。同時(shí)確保 Copy items into destination group’s folder被復(fù)選中,并且ARSpaceships target也要被選中。然后點(diǎn)擊Finish。這時(shí),你的工程看起來應(yīng)該如下圖所示:

  我們將在后面使用到這些資源。

玩一玩攝像頭!

  如果你現(xiàn)在運(yùn)行項(xiàng)目,那簡單太無聊了,就是一個(gè)黑屏的HelloWorld,這玩意兒誰沒看過!讓我們往里面添加一些非常好玩的內(nèi)容吧。選擇AppDelegate.h,然后在interface里面添加一個(gè)UIView成員變量。

UIView *overlay;

  現(xiàn)在,打開AppDelegate.m文件,找到EAGLView *glView所在的代碼行,把像素格式改成kEAGLColorFormatRGBA8。改完后如下圖所示:

EAGLView *glView = [EAGLView viewWithFrame:[window bounds]
pixelFormat:kEAGLColorFormatRGBA8 depthFormat:
0];

  如果你不改變像素格式的話,那么攝像頭里拍攝出來的圖像就顯示不出來。因?yàn)槲覀儸F(xiàn)在正在做一個(gè)增強(qiáng)現(xiàn)實(shí)的游戲,所以必須這樣做!

  在 [window addSubview: viewController.view];下面,我們添加了以下代碼:


// set the background color of the view
[CCDirector sharedDirector].openGLView.backgroundColor = [UIColor clearColor];
[CCDirector sharedDirector].openGLView.opaque
= NO;

// set value for glClearColor
glClearColor(0.0, 0.0, 0.0, 0.0);

// prepare the overlay view and add it to the window
overlay = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
overlay.opaque
= NO;
overlay.backgroundColor
=[UIColor clearColor];
[window addSubview:overlay];

  這里,我們把openGLView的背景顏色清除了,把視圖設(shè)置為透明的,同時(shí)指定了glClearColor,最后,我們創(chuàng)建并添加了一個(gè)新的視圖,叫做overlay。這個(gè)視圖后面用來顯示camera里的內(nèi)容。

  接下來,在你剛剛添加的代碼后面增加以下代碼行:

#define CAMERA_TRANSFORM 1.24299

UIImagePickerController
*uip;

@try {
uip
= [[[UIImagePickerController alloc] init] autorelease];
uip.sourceType
= UIImagePickerControllerSourceTypeCamera;
uip.showsCameraControls
= NO;
uip.toolbarHidden
= YES;
uip.navigationBarHidden
= YES;
uip.wantsFullScreenLayout
= YES;
uip.cameraViewTransform
= CGAffineTransformScale(uip.cameraViewTransform,
CAMERA_TRANSFORM, CAMERA_TRANSFORM);
}
@catch (NSException * e) {
[uip release];
uip
= nil;
}
@finally {
if(uip) {
[overlay addSubview:[uip view]];
[overlay release];
}
}

[window bringSubviewToFront:viewController.view];

  首先,我們了一個(gè)常量來縮放攝像頭。攝像頭的比率是4:3,而iphone屏幕的比例是3:4,所以,我們需要縮放一下攝像頭,使之與屏幕匹配。

  其次,我們創(chuàng)建了一個(gè)UIImagePickerController,設(shè)置了它的一些屬性,具體效果就是沒有控件并且縮放了的攝像頭。然后,我們把它添加到了overlay視圖中。

  最后,我們需要把 viewController.view顯示到前面來(因?yàn)樗薱ocos2d的顯示內(nèi)容)。這樣子攝像頭拍攝的內(nèi)容也就會(huì)顯示到前臺(tái)來了。

  現(xiàn)在,運(yùn)行一下應(yīng)用程序。現(xiàn)在,你將看到攝像頭里面捕捉的畫面是你的Hello World的背景了。

Shake, Rattle, and Roll…Well at Least Yaw!

  因?yàn)槲覀儸F(xiàn)在的程序已經(jīng)可以把現(xiàn)實(shí)捕捉并顯示出來了,接下來,我們將集中精力來解決本教程中比較難的部分。

  首先,我們需要把CoreMotion framework添加到項(xiàng)目中去。點(diǎn)擊project文件,然后選擇ARSpaceships target,再選擇Build Phases標(biāo)簽頁,展開 Link Binary With Libraries.。

  點(diǎn)擊上圖中的+號(hào),選擇 CoreMotion.framework,然后點(diǎn)擊Add按鈕。現(xiàn)在,我們就可以在項(xiàng)目中使用陀螺儀啦。

  打開HelloWorldLayer.h,然后導(dǎo)入一些頭文件:

#include <CoreMotion/CoreMotion.h>
#import <CoreFoundation/CoreFoundation.h>

  然后在interface里面添加一些成員變量:

CMMotionManager *motionManager;
CCLabelTTF
*yawLabel;
CCLabelTTF
*posIn360Label;

  同時(shí)添加屬性聲明語句:

@property (nonatomic, retain) CMMotionManager *motionManager;

  現(xiàn)在,重點(diǎn)要來了!打開HelloWorldLayer.m文件,在if ((self=[super init]))語句內(nèi)部,刪除原來的“Hello World”標(biāo)簽語句,同時(shí)添加下面的代碼來設(shè)置一些新的標(biāo)簽.

// add and position the labels
yawLabel = [CCLabelTTF labelWithString:@"Yaw: " fontName:@"Marker Felt" fontSize:12];
posIn360Label
= [CCLabelTTF labelWithString:@"360Pos: " fontName:@"Marker Felt" fontSize:12];
yawLabel.position
= ccp(50, 240);
posIn360Label.position
= ccp(50, 300);
[self addChild: yawLabel];
[self addChild:posIn360Label];

  目前為止,并沒有什么特別的。只是添加了一些標(biāo)簽,指明了字體和一些文字。標(biāo)簽的位置都是在屏幕的左邊。

  接下來,你需要設(shè)置motion manager,它會(huì)啟動(dòng)陀螺儀。

self.motionManager = [[[CMMotionManager alloc] init] autorelease];
motionManager.deviceMotionUpdateInterval
= 1.0/60.0;
if (motionManager.isDeviceMotionAvailable) {
[motionManager startDeviceMotionUpdates];
}

[self scheduleUpdate];

  這里,我們分配并初始化了 motion manager對(duì)象。同時(shí),我們還設(shè)置了更新間隔為每秒60次。如果設(shè)置支持陀螺儀的話,那么就啟動(dòng)更新。最后,我們觸發(fā)一個(gè)update定時(shí)器。

  不要忘了在.m文件中添加synthesize語句,如下所示:

@synthesize motionManager;

  因?yàn)槲覀冇|發(fā)了update定時(shí)器,所以我們需要添加一個(gè)update方法。同時(shí),在init方法的下面增加下面一個(gè)方法:

-(void)update:(ccTime)delta {
CMDeviceMotion
*currentDeviceMotion = motionManager.deviceMotion;
CMAttitude
*currentAttitude = currentDeviceMotion.attitude;

// 1: Convert the radians yaw value to degrees then round up/down
float yaw = roundf((float)(CC_RADIANS_TO_DEGREES(currentAttitude.yaw)));

// 2: Convert the degrees value to float and use Math function to round the value
[yawLabel setString:[NSString stringWithFormat:@"Yaw: %.0f", yaw]];

// 3: Convert the yaw value to a value in the range of 0 to 360
int positionIn360 = yaw;
if (positionIn360 < 0) {
positionIn360
= 360 + positionIn360;
}

[posIn360Label setString:[NSString stringWithFormat:
@"360Pos: %d", positionIn360]];

}

  現(xiàn)在,你可以運(yùn)行應(yīng)用程序了。你將會(huì)看到Y(jié)aw和positionIn360的對(duì)應(yīng)標(biāo)簽值在改變。

那玩意兒究竟如何工作?

  盡管可以跑起來了,但是你可能會(huì)問,它究竟是如何工作的呢?讓我們花幾分鐘時(shí)間來一步步解釋上面的代碼是如何工作的。

  首先,下載iTunes app store上面的免費(fèi)程序 Gyrosocope app。安裝并運(yùn)行它,當(dāng)你移動(dòng)iphone的時(shí)候,就可以看到每個(gè)值究竟是怎么變化的。

  我們關(guān)心的值是Yaw。這個(gè)值代表往左或往右移動(dòng)。在Gyroscope程序中,它的單位是度,然而 motion manager獲取的值卻是弧度。這就是為什么我們需要使用內(nèi)置的函數(shù)CC_RADIANS_TO_DEGREES來把弧度轉(zhuǎn)換成角度的原因啦。

  因此,在第一部分中,我們得到了yaw的弧度值,并且把它轉(zhuǎn)換成角度,最后賦值給yaw變量。第二部分,我們只是把yaw的值顯示到屏幕上去。如果你運(yùn)行工程,你會(huì)看到y(tǒng)aw值的變化范圍是從0~180,然后又從-180回到0.

  現(xiàn)在看看第三部分,你可能會(huì)奇怪positionIn360的值倒底是什么啊?好吧,這里只是一個(gè)手段,目的是使得放置飛碟的過程變得容易。

  這里的邏輯其實(shí)非常簡單。如果yaw值是正的,那么我們什么也不做。如果是負(fù)的,那么就減去360.(加上一個(gè)負(fù)值和減去一個(gè)正值是一樣的)。最后一行代碼只是在屏幕上顯示那個(gè)值。

  如果你還沒完全理解,沒關(guān)系,運(yùn)行一下程序,看看具體值是怎么變化的吧。

燈光,攝像機(jī),Action!

  現(xiàn)在,我們?yōu)橥勇輧x的使用奠定基礎(chǔ)了,讓我們往里面添加一些太空飛船吧!我們將創(chuàng)建一個(gè)新的文件。首先,左鍵點(diǎn)ARSpaceships工程文件,然后選擇New File。接著選 iOS\Cocoa Touch\Objective-C class,然后點(diǎn)擊Next。確保NSObject被選中基類,然后點(diǎn)Next。把文件命名為EnemyShip.m,最后點(diǎn)Save。

  把 EnemyShip.h里的內(nèi)容換成下面的代碼:

#import "cocos2d.h"

@interface EnemyShip : CCSprite {
int yawPosition;
int timeToLive;
}

@property (readwrite)
int yawPosition;
@property (readwrite)
int timeToLive;

@end

  同時(shí)修改EnemyShip.m:

#import "EnemyShip.h"


@implementation EnemyShip

@synthesize yawPosition, timeToLive;

-(id)init {
self
= [super init];
if (self){
yawPosition
= 0;
timeToLive
= 0;
}
return self;
}

@end

  現(xiàn)在,回到HelloWorldLayer.h。在頂部導(dǎo)入EnemyShip類的頭文件,如下所示:

#import "EnemyShip.h"

  在interface里面聲明以下成員變量:

NSMutableArray *enemySprites;
int enemyCount;
CCSpriteBatchNode
*batchNode;

  最后,在interface聲明下面,添加enemyCount屬性,同時(shí)定義一些方法,具體如下圖所示:

@property (readwrite) int enemyCount;

-(EnemyShip *)addEnemyShip:(int)shipTag;
-(void)checkEnemyShipPosition:(EnemyShip *)enemyShip withYaw:(float)yawPosition;
-(void)updateEnemyShipPosition:(int)positionIn360 withEnemy:(EnemyShip *)enemyShip;
-(void)runStandardPositionCheck:(int)positionIn360 withDiff:(int)difference withEnemy:(EnemyShip *)enemyShip;

  打開 HelloWorldLayer.m文件,同時(shí)作以下修改:

// Place after the #import statement
#include <stdlib.h>

// Place after the other @synthesize statement
@synthesize enemyCount;
#define kXPositionMultiplier 15
#define kTimeToLive 100

// Add to the bottom of init
batchNode = [CCSpriteBatchNode batchNodeWithFile:@"Sprites.pvr.ccz"];
[self addChild:batchNode];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:
@"Sprites.plist"];

  這里,我們加載了spritesheet,它的資源文件在一開始的時(shí)候,我們就把它拖到項(xiàng)目中來了。

  接下來,我們將添加一個(gè)新的方法來創(chuàng)建新的敵人太空飛船。在dealloc方法的上面添加下列方法:

-(EnemyShip *)addEnemyShip:(int)shipTag {

EnemyShip
*enemyShip = [EnemyShip spriteWithSpriteFrameName:@"enemy_spaceship.png"];

// Set position of the space ship randomly
int x = arc4random() % 360;
enemyShip.yawPosition
= x;

// Set the position of the space ship off the screen, but in the center of the y axis
// we will update it in another method
[enemyShip setPosition:ccp(5000, 160)];

// Set time to live on the space ship
enemyShip.timeToLive = kTimeToLive;
enemyShip.visible
= true;

[batchNode addChild:enemyShip z:
3 tag:shipTag];

return enemyShip;
}

  這個(gè)方法接收一個(gè)整數(shù)值作為tag,并且返回一個(gè)EnemyShip CCSprite。下面一行代碼,我們從精靈表單中創(chuàng)建一個(gè)EnemyShip精靈。接著,我們使用arc4random方法來得到一個(gè)0~360的隨機(jī)數(shù)。最后,我們?cè)O(shè)置了飛船的位置,并把timeToLive的值設(shè)置為100,把飛船添加到batchNode里面,最后返回飛船精靈對(duì)象。

  在addEnemyShip下面,我們添加一個(gè)checkEnemyShipPosition方法:

-(void)checkEnemyShipPosition:(EnemyShip *)enemyShip withYaw:(float)yawPosition {
// Convert the yaw value to a value in the range of 0 to 360
int positionIn360 = yawPosition;
if (positionIn360 < 0) {
positionIn360
= 360 + positionIn360;
}

BOOL checkAlternateRange
= false;

// Determine the minimum position for enemy ship
int rangeMin = positionIn360 - 23;
if (rangeMin < 0) {
rangeMin
= 360 + rangeMin;
checkAlternateRange
= true;
}

// Determine the maximum position for the enemy ship
int rangeMax = positionIn360 + 23;
if (rangeMax > 360) {
rangeMax
= rangeMax - 360;
checkAlternateRange
= true;
}

if (checkAlternateRange) {
if ((enemyShip.yawPosition < rangeMax || enemyShip.yawPosition > rangeMin ) || (enemyShip.yawPosition > rangeMin || enemyShip.yawPosition < rangeMax)) {
[self updateEnemyShipPosition:positionIn360 withEnemy:enemyShip];
}
}
else {
if (enemyShip.yawPosition > rangeMin && enemyShip.yawPosition < rangeMax) {
[self updateEnemyShipPosition:positionIn360 withEnemy:enemyShip];
}
}
}

  這個(gè)方法看起來似乎有點(diǎn)讓人摸不著頭腦,一會(huì)最大值,一會(huì)兒最小值,一會(huì)候選值。不過,不要擔(dān)心,其實(shí)非常簡單。首先,我們檢查設(shè)置的yaw坐標(biāo)址(positionIn360),然后把此值限制在0~360之間。

  因?yàn)椋覀冇袃啥朔秶际?~360,所以需要檢查一下設(shè)置的positionIn360具體屬于哪一端。我們使用一個(gè)任意數(shù)23來代表將在屏幕一半處顯示的度數(shù)。

  因?yàn)椋覀冎恍枰P(guān)心變化范圍是0~23和337~360的空間了。因?yàn)椋硪欢说木€將會(huì)包過來。(這里相像成3維空間的一個(gè)圓)

  最后,如果敵人飛船在屏幕46度的范圍之內(nèi)的話,那么就要更新敵人飛船。checkAlternateRange判斷語句只是用來決定什么時(shí)候來更新敵人飛船。

  如果checkAlternateRange為真的話,那么我們就檢查敵船的位置是否落在min和max的范圍之內(nèi)。

positionIn360 = 20
rangeMin
= 357
rangeMax
= 20
enemyShip.yawPosition
= 359

  因?yàn)槲覀円紤]線段的兩端,我們的min范圍比max范圍要大一些。現(xiàn)在,我們做一個(gè)判斷,看敵船的位置是不是大于rangeMin,如果是,則顯示在屏幕上。

  if語句中的else就更加明了了。他檢查敵船的位置是不是大于min且小于max。

  多么復(fù)雜的一個(gè)update方法啊!我們?cè)赾heckEnemyShipPosition方法下面添加以下代碼:

-(void)updateEnemyShipPosition:(int)positionIn360 withEnemy:(EnemyShip *)enemyShip {
int difference = 0;
if (positionIn360 < 23) {
// Run 1
if (enemyShip.yawPosition > 337) {
difference
= (360 - enemyShip.yawPosition) + positionIn360;
int xPosition = 240 + (difference * kXPositionMultiplier);
[enemyShip setPosition:ccp(xPosition, enemyShip.position.y)];
}
else {
// Run Standard Position Check
[self runStandardPositionCheck:positionIn360 withDiff:difference withEnemy:enemyShip];
}
}
else if(positionIn360 > 337) {
// Run 2
if (enemyShip.yawPosition < 23) {
difference
= enemyShip.yawPosition + (360 - positionIn360);
int xPosition = 240 - (difference * kXPositionMultiplier);
[enemyShip setPosition:ccp(xPosition, enemyShip.position.y)];
}
else {
// Run Standard Position Check
[self runStandardPositionCheck:positionIn360 withDiff:difference withEnemy:enemyShip];
}
}
else {
// Run Standard Position Check
[self runStandardPositionCheck:positionIn360 withDiff:difference withEnemy:enemyShip];
}
}

  在這個(gè)方法中,我們測(cè)試,看是否設(shè)備的positionIn360是不是在討論的3個(gè)范圍內(nèi)。在第一個(gè)測(cè)試中,我們檢測(cè)positionIn360是不理小于23,如果是,我們就看看是不是有一些敵船在線的另一端(大于337)。

  第二部分測(cè)試,看是否positionIn360大于337.如果是的話,就再檢測(cè)它是否小于23.

  第二部分測(cè)試,看敵船是否在23和337之間。如果是,則調(diào)用runStandardPositionCheck方法。這個(gè)方法的定義如下所示:

-(void)runStandardPositionCheck:(int)positionIn360 withDiff:(int)difference withEnemy:(EnemyShip *)enemyShip {
if (enemyShip.yawPosition > positionIn360) {
difference
= enemyShip.yawPosition - positionIn360;
int xPosition = 240 - (difference * kXPositionMultiplier);
[enemyShip setPosition:ccp(xPosition, enemyShip.position.y)];
}
else {
difference
= positionIn360 - enemyShip.yawPosition;
int xPosition = 240 + (difference * kXPositionMultiplier);
[enemyShip setPosition:ccp(xPosition, enemyShip.position.y)];
}
}

  在這個(gè)方法中,我們檢查看是否enemyShip是否在設(shè)備的positionIn360的左邊還是右邊。當(dāng)enemyShip的位置小于positionIn360時(shí),它將出現(xiàn)在屏幕的左邊。當(dāng)enemyShip的位置大于positionIn360,那么它將出現(xiàn)在屏幕的右邊。

  現(xiàn)在,你會(huì)說,請(qǐng)等一下!你忘了描述這些變量的作用了!好吧,接下來就解釋一下。

  如果敵船的yaw坐標(biāo)值在屏幕的范圍之內(nèi)(從 positionIn360 – 23到 positionIn360 + 23),然后,首先我們計(jì)算它位于屏幕的哪一邊。如果大于positionIn360,那么就在屏幕的右邊,否則就在屏幕的左邊。

  difference變量的作用是保存設(shè)備的positionIn360和敵船的 yaw位置的角度差值。一旦計(jì)算出來后,我們就把這個(gè)差值乘以一個(gè)任意的倍數(shù)。這個(gè)倍數(shù)代表每一度的像素個(gè)數(shù)。這個(gè)里,我們選擇15.

  基于它位于于當(dāng)前屏幕的位置,我們將把這個(gè)計(jì)算出來的值增加240或者減去240。

  現(xiàn)在,所有需要的方法已經(jīng)全部準(zhǔn)備就緒啦。

  在init方法的底部,添加下面的代碼,在屏幕中增加5個(gè)飛船:

// Loop through 1 - 5 and add space ships
enemySprites = [[NSMutableArray alloc] init];
for(int i = 0; i < 5; ++i) {
EnemyShip
*enemyShip = [self addEnemyShip:i];
[enemySprites addObject:enemyShip];
enemyCount
+= 1;
}

  因?yàn)椋覀兲砑恿藬炒狡聊恢辛耍覀兇_保它們的位置被更新。在udpate方法的底部添加下面方法:

// Loop through array of Space Ships and check the position
for (EnemyShip *enemyShip in enemySprites) {
[self checkEnemyShipPosition:enemyShip withYaw:yaw];
}

  在我們忘記之前,在dealloc里面添加清理代碼來清理我們之前創(chuàng)建的enemySpritesArray數(shù)組:

[enemySprites release];

  現(xiàn)在,編譯并運(yùn)行工程吧!當(dāng)你旋轉(zhuǎn)設(shè)備的時(shí)候,你將會(huì)看到5個(gè)飛船在不同的地方。

免費(fèi)的激光和爆炸

  目前為止,這個(gè)現(xiàn)實(shí)增加的游戲完成的差不多了。不過,還有一個(gè)很嚴(yán)重的問題:這里飛船打中后沒什么感覺。

  很明顯,我們并不想這樣,所以,讓我們添加一些很酷的激光和爆炸效果吧。

  在開始之前,讓我們移除屏幕上的label--他們只是作為調(diào)試時(shí)用的。因此,找開 HelloWorldLayer.m中關(guān)于labels的代碼,并把它們注釋掉。完成之后,編譯并運(yùn)行,保證沒有錯(cuò)誤。

  現(xiàn)在,看看有趣的部分---讓我們往游戲中添加一些火力吧!首先,我們將添加一個(gè)方法用來判斷玩家的開火區(qū)域是否擊中了飛船。在HelloWorldLayer.h文件中,在@end之前聲明下列方法:

- (BOOL) circle:(CGPoint) circlePoint withRadius:(float) radius collisionWithCircle:(CGPoint) circlePointTwo collisionCircleRadius:(float) radiusTwo;

  打開HelloWorldLayer.m,然后在dealloc方法上面實(shí)現(xiàn)上述方法:

- (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;
}
  

  這個(gè)方法用來檢測(cè)是否兩個(gè)點(diǎn)的半徑有交集。輸入的參數(shù)是敵方飛船位置和屏幕的中心點(diǎn)位置。兩個(gè)點(diǎn)的半徑都設(shè)置為50.

  首先,我們計(jì)算兩個(gè)點(diǎn)X和Y值的差。接下來計(jì)算兩點(diǎn)的距離。這個(gè)在高中就學(xué)過的,叫勾股定理。你可以從這里得到更多的信息。

  接下來,我們往屏幕中添加一個(gè)區(qū)域,用作火力瞄準(zhǔn)器。下載這些資源文件,解壓縮,然后把scope.png拖到Resouces文件夾下去。確保“Copy items into destination group’s folder”被復(fù)選中,然后點(diǎn)Finish。

  打開HelloWorldLayer.m的init方法,然后在 [self scheduleUpdate]方法之前,添加以下代碼:

// Add the scope crosshairs
CCSprite *scope = [CCSprite spriteWithFile:@"scope.png"];
scope.position
= ccp(240, 160);
[self addChild:scope z:
15];

// Allow touches with the layer
[self registerWithTouchDispatcher];

  如果你現(xiàn)在運(yùn)行程序,你將看到一個(gè)瞄準(zhǔn)器出現(xiàn)在屏幕的中間。

  非常好,現(xiàn)在讓我們添加一些爆炸效果,在玩家點(diǎn)擊屏幕的時(shí)候就觸發(fā)。我們將按照添加scope.png的方法一樣,來添加Explision.plist。先找到之前下載的本項(xiàng)目資源文件。把Explosion.plist拖到資源文件夾中,確保“Copy items into destination group’s folder”被復(fù)選上,然后點(diǎn)擊Finish。

  你可能會(huì)奇怪這個(gè)文件到底是什么?我使用一個(gè)很酷的軟件制作的,你可能之前也聽說過了,叫做 Particle Designer,它是由71 Squared的工程師所開發(fā)的。我不會(huì)在這里講解如何使用此軟件來制作這樣的粒子效果文件,但是,實(shí)際上這個(gè)過程是非常簡單的。選擇一種粒子效果,然后調(diào)節(jié)一些參數(shù),最后導(dǎo)出plist文件就可以了。

  現(xiàn)在,在dealloc方法之前,添加下列代碼:

-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint location
= CGPointMake(240,160);

// 1
for (EnemyShip *enemyShip in enemySprites) {
if (enemyShip.timeToLive > 0) {
// Check to see if yaw position is in range
BOOL wasTouched = [self circle:location withRadius:50 collisionWithCircle:enemyShip.position collisionCircleRadius:50];

if (wasTouched) {
enemyShip.timeToLive
= 0;
enemyShip.visible
= false;
enemyCount
-= 1;
}
}
}

// 2
CCParticleSystemQuad *particle = [CCParticleSystemQuad particleWithFile:@"Explosion.plist"];
particle.position
= ccp(240,160);
[self addChild:particle z:
20];
particle.autoRemoveOnFinish
= YES;

// 3
if (enemyCount == 0) {
// Show end game
CGSize winSize = [CCDirector sharedDirector].winSize;
CCLabelBMFont
*label = [CCLabelBMFont labelWithString:@"You win!" fntFile:@"Arial.fnt"];
label.scale
= 2.0;
label.position
= ccp(winSize.width/2, winSize.height/2);
[self addChild:label z:
30];
}
}

  這段代碼的第一部分用來做碰撞檢測(cè),用來測(cè)試是否飛船在瞄準(zhǔn)器范圍之內(nèi)。如果其中一個(gè)飛船被擊中了,我們就飛船的屬性來隱藏它,同時(shí)把enemyCount計(jì)數(shù)減1.第二部分代碼,往屏幕中心添加了一個(gè)爆炸粒子系統(tǒng)。第二部分代碼,也是最后一部分代碼,它用來檢查enemyCount是否為0,如果是的話,就顯示一個(gè)label,告知玩家游戲結(jié)束了。

  這個(gè)游戲如果就這樣的話,有點(diǎn)無聊。所以,讓我們往游戲中添加一些基本的AI吧。其實(shí)也很簡單的,就是隨著時(shí)間的推移,我們會(huì)改變一下飛船的位置。所以,在update方法底部添加下列代碼:

// Loop through array of Space Ships and if the timeToLive is zero
// change the yawPosition of the sprite
for (EnemyShip *enemyShip in enemySprites) {
enemyShip.timeToLive
--;
if (enemyShip.timeToLive == 0) {
int x = arc4random() % 360;
[enemyShip setPosition:ccp(
5000, 160)];
enemyShip.yawPosition
= x;
enemyShip.timeToLive
= kTimeToLive;
}
}

  這段代碼將會(huì)遍歷所有的enemySprites,然后更新timeToLive屬性。然后,檢查這個(gè)timeToLive屬性是否等于0,如果是的話,那么就給飛船的yawPositon設(shè)置一個(gè)隨機(jī)值,同時(shí)重置timeToLive屬性。編譯并運(yùn)行工程吧,現(xiàn)在你想要打中飛船的話就有一些難度了,開火!

Pump up the Volume!

  游戲如果沒有聲音的話,那就太沒意思了!所以,讓我們添加一些音樂吧!

  在HellowWorldLayer.m文件頂部包含Simple Audio Engine所需的頭文件,具體如下所示:

#import "SimpleAudioEngine.h"

  然后在init方法的最后添加下列代碼,記得添加在 if ((self=[super init]))語句內(nèi)部:

[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"SpaceGame.caf" loop:YES];
[[SimpleAudioEngine sharedEngine] preloadEffect:
@"explosion_large.caf"];
[[SimpleAudioEngine sharedEngine] preloadEffect:
@"laser_ship.caf"];
  

  這里會(huì)加載背景音樂,同時(shí)預(yù)加載音效。

  現(xiàn)在,找到ccTouchesBegan,然后在這個(gè)方法的開頭添加下列代碼:

[[SimpleAudioEngine sharedEngine] playEffect:@"laser_ship.caf"];

  這里會(huì)在你點(diǎn)擊屏幕的時(shí)候播放一個(gè)發(fā)射激光的音效。

  還是在ccTouchesBegan方法里面,打開遍歷enemySprites那個(gè)循環(huán),然后在 (wasTouched)的if判斷句內(nèi)添加下列代碼:

[[SimpleAudioEngine sharedEngine] playEffect:@"explosion_large.caf"];

  當(dāng)飛船被打中的時(shí)候,將會(huì)播放爆炸的音效!

  編譯并運(yùn)行代碼,盡情玩吧!

何去何從?

  這里有本教程的完整源代碼

  如果你想學(xué)習(xí)更多有關(guān)制作增強(qiáng)現(xiàn)實(shí)的游戲的話,下面有一些比較好的資源:

  我希望你在學(xué)習(xí)這個(gè)教程的時(shí)候會(huì)得到許多快樂!如果有什么建議或意見,請(qǐng)留言,謝謝!


轉(zhuǎn)自:http://www.cnblogs.com/andyque/archive/2011/07/03/2096510.html
posted on 2011-07-04 11:57 會(huì)飛的兔子 閱讀(2186) 評(píng)論(0)  編輯 收藏 引用 所屬分類: 蘋果相關(guān)
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美 亚欧 日韩视频在线| 极品少妇一区二区三区| 亚洲视频一区| 亚洲美女av在线播放| 欧美激情bt| 亚洲国产日韩一区| 久久夜色精品亚洲噜噜国产mv| 久久动漫亚洲| 欧美在线视频网站| 亚洲高清一区二| 亚洲尤物在线视频观看| 美国成人直播| 久久国产高清| 男女精品网站| 亚洲国产精品一区二区www| 国产精品99久久久久久久女警| 欧美成人精品一区二区| 亚洲国产高清自拍| 亚洲美洲欧洲综合国产一区| 亚洲一级在线| 久久亚洲色图| 欧美三级电影精品| 国产日韩欧美一区二区| 亚洲国产精品一区在线观看不卡 | 久久黄色级2电影| 蜜臀av在线播放一区二区三区| 蜜乳av另类精品一区二区| 欧美日韩精品三区| 国产日韩在线看片| 亚洲精品在线三区| 久久精品视频99| 一本色道久久综合| 老巨人导航500精品| 国产精品久久久久秋霞鲁丝| 亚洲成人中文| 久久精品国产亚洲a| 日韩视频免费| 久热精品在线| 国产在线拍偷自揄拍精品| 在线亚洲免费| 欧美激情在线播放| 欧美日韩在线播放一区| 欧美91视频| 香蕉成人啪国产精品视频综合网| 欧美激情精品| 亚洲国产高潮在线观看| 久久久xxx| 亚洲一区三区在线观看| 欧美日韩国产在线看| 亚洲国产精品热久久| 欧美在线视频观看免费网站| 9国产精品视频| 欧美国产日韩精品| 亚洲丶国产丶欧美一区二区三区| 欧美一区二区三区视频免费| 日韩亚洲视频在线| 欧美男人的天堂| 日韩视频专区| 亚洲破处大片| 欧美片在线播放| 一本到高清视频免费精品| 亚洲国产精品久久91精品| 裸体一区二区三区| 亚洲国产成人午夜在线一区| 久久午夜国产精品| 久久久国产成人精品| 尤物在线观看一区| 久久久一区二区| 久久精品一级爱片| 在线看国产日韩| 欧美国产专区| 欧美精品一区二区精品网 | 亚洲三级网站| 欧美成人激情视频| 亚洲六月丁香色婷婷综合久久| 亚洲大片免费看| 亚洲一区二区在线播放| 国产精品你懂得| 久久精品主播| 蜜臀99久久精品久久久久久软件| 亚洲精品小视频| 一本一本久久| 国产专区欧美精品| 欧美成人自拍视频| 欧美日本乱大交xxxxx| 午夜免费电影一区在线观看| 午夜精品久久久久久久99水蜜桃| 国产情人综合久久777777| 久久综合网hezyo| 欧美精品一区在线| 欧美影视一区| 牛牛影视久久网| 午夜久久一区| 蜜臀av一级做a爰片久久| 亚洲一区二区在线免费观看| 久久久91精品国产| 亚洲性色视频| 老司机精品视频一区二区三区| 一区二区三区回区在观看免费视频| 亚洲欧美日韩一区二区| 亚洲精选久久| 久久精品道一区二区三区| 一区二区三区免费观看| 久久精品色图| 欧美一级大片在线观看| 欧美freesex交免费视频| 亚洲欧美制服另类日韩| 欧美成人精品一区二区| 久久久蜜臀国产一区二区| 欧美日本韩国| 欧美成人午夜视频| 国产欧美一区二区视频| 99综合视频| 99在线精品视频在线观看| 久久久久国产成人精品亚洲午夜| 91久久极品少妇xxxxⅹ软件| 亚洲一区二区在线观看视频| 日韩亚洲一区二区| 久久久久成人精品免费播放动漫| 午夜久久福利| 欧美日韩精品一本二本三本| 欧美电影美腿模特1979在线看| 国产精品日本一区二区| 日韩视频一区二区三区在线播放免费观看| 黄网动漫久久久| 午夜亚洲一区| 欧美一级一区| 国产欧美亚洲精品| 中文久久乱码一区二区| 一区二区三区久久| 欧美精品xxxxbbbb| 亚洲第一主播视频| 亚洲观看高清完整版在线观看| 亚洲精品乱码久久久久久黑人| 久久精品人人| 亚洲无线观看| 欧美成人资源网| 欧美国产日韩一区二区| 韩日精品视频| 久久激情综合网| 久久精品在线视频| 国产日韩欧美视频在线| 性色av一区二区三区在线观看| 亚洲一区在线观看视频| 欧美日韩精品免费观看| 日韩五码在线| 亚洲欧美国内爽妇网| 国产精品日韩欧美| 亚洲欧美综合| 免费久久99精品国产自在现线| **性色生活片久久毛片| 葵司免费一区二区三区四区五区| 欧美激情欧美激情在线五月| 亚洲乱码国产乱码精品精可以看| 欧美激情一区二区久久久| 亚洲激情精品| 亚洲国产精品一区二区第一页| 欧美ed2k| 亚洲一区二区三区影院| 久久狠狠久久综合桃花| 精品福利电影| 欧美精品久久久久久久免费观看| 亚洲精品日韩在线观看| 亚洲欧美日韩精品| 狠狠色狠狠色综合系列| 欧美成人第一页| 亚洲深夜福利网站| 久久色在线观看| 一区二区三区高清视频在线观看| 国产精品老女人精品视频| 午夜精品久久久久久久蜜桃app | 欧美日韩一区二区三区四区在线观看| 亚洲精品一二三| 久久久999国产| 亚洲人成网站影音先锋播放| 国产精品国产精品国产专区不蜜| 亚洲欧美中文字幕| 91久久综合亚洲鲁鲁五月天| 久久se精品一区二区| 亚洲国产美女精品久久久久∴| 欧美日韩喷水| 久久综合九色99| 亚洲欧美www| 亚洲三级网站| 久久视频在线看| 亚洲一区二区三区国产| 亚洲第一综合天堂另类专| 国产精品国产三级国产专区53| 久久久久高清| 亚洲欧美国内爽妇网| 亚洲国内精品| 久久天天躁狠狠躁夜夜爽蜜月| 一本久道久久久| 国产精品乱码人人做人人爱| 欧美jizz19性欧美| 欧美一区精品| 亚洲欧美国产不卡| 亚洲精品欧洲精品| 欧美成人按摩| 亚洲欧美国产精品va在线观看 |