iPhone Game Development - Chris Craft [41]
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
for (int i=0 ; i<(currentLevel.gridSize*currentLevel.gridSize)-1; i++) {
if ([touch view] == tiles[i]) {
if ([self tileCanMoveToIndexFromIndex:tiles[i].location]) {
int oldLocation = tiles[i].location;
int newLocation = emptyTile;
// only move to the new location if the center of the moving tile is
// inside the empty tile
CGRect emptyTileRect = [TileView getRectForLocation:emptyTile
withDimension:currentLevel.gridSize
withSize:currentLevel.tileSize];
if (CGRectContainsPoint(emptyTileRect, tiles[i].center)) {
[self moveTile:tiles[i] toLocation:newLocation];
emptyTile = oldLocation;
}
else {
[self moveTile:tiles[i] toLocation:oldLocation];
}
}
break;
}
}
[self testDidWin];
}
After reviewing these two methods that handle touch, play around with the example on the iPhone. We believe you will find that this treatment of the touch events creates a realistic experience for players.
Reviewing the utility methods
The moveTile method is used to snap a tile into place. First, it begins the animation. Next, we set the location of the tile. By setting the location index of the tile view, we will internally update its frame. Finally, we commit the animation. This produces our nice little effect of snapping the tile into place:
- (void)moveTile:(TileView*)tileView toLocation:(int)aLocation {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.25];
tileView.location = aLocation;
[UIView commitAnimations];
[tileView updateArrowPoint];
}
You will see the method tileCanMoveToIndexFromIndex used a lot. As expected, this method returns true if a tile can make a legal move from its current index to the empty tile index. This is calculated by checking to see if the tile in question is adjacent to the empty tile:
- (bool)tileCanMoveToIndexFromIndex:(int)index {
int fromX = [TileView getXForLocation:index
withDimension:currentLevel.gridSize];
int fromY = [TileView getYForLocation:index
withDimension:currentLevel.gridSize];
int toX = [TileView getXForLocation:emptyTile
withDimension:currentLevel.gridSize];
int toY = [TileView getYForLocation:emptyTile
withDimension:currentLevel.gridSize];
if (fromX == toX && (fromY+1 == toY || fromY-1 == toY) )
return true;
if (fromY == toY && (fromX+1 == toX || fromX-1 == toX) )
return true;
return false;
}
The method tilesInCorrectLocation checks to see if all arrow tiles are currently seated in their correct location. We can determine this by comparing the tile's index to the tile's location. If this is the case for every arrow tile, this method returns true:
- (bool)tilesInCorrectLocation {
for (int i=0 ; i<(currentLevel.gridSize*currentLevel.gridSize)-1; i++) {
TileView *tileView = tiles[i];
if (tileView.tileType == arrow && tileView.location != i)
return false;
}
return true;
}
The last method we will review in this example is testDidWin. This method is called at the completion of each move (look at the earlier method touchesEnded). If the method tilesInCorrectLocation returns true, we know that the player has completed the level.
If we pass this condition, we see an alert view that proclaims the player's success. Finally, we call loadLevel to automatically load the next level in the sequence. When the player dismisses the alert, he can begin playing the next level:
- (void)testDidWin {
if (![self tilesInCorrectLocation]) return;
NSString *message = [NSString stringWithFormat:
@”You have just completed level %d on to the next level”,
currentLevelIndex+1];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:
@”Level Complete!” message:message delegate:nil cancelButtonTitle:
@”OK” otherButtonTitles:nil];
[alertView show];
[alertView release];
[Helper setUserValue:[NSString stringWithFormat: