CrashLandingAppDelegate.m
上传用户:jsxyqc
上传日期:2015-08-08
资源大小:1251k
文件大小:21k
源码类别:

MacOS编程

开发平台:

Objective-C

  1. /*
  2. ===== IMPORTANT =====
  3. This is sample code demonstrating API, technology or techniques in development.
  4. Although this sample code has been reviewed for technical accuracy, it is not
  5. final. Apple is supplying this information to help you plan for the adoption of
  6. the technologies and programming interfaces described herein. This information
  7. is subject to change, and software implemented based on this sample code should
  8. be tested with final operating system software and final documentation. Newer
  9. versions of this sample code may be provided with future seeds of the API or
  10. technology. For information about updates to this and other developer
  11. documentation, view the New & Updated sidebars in subsequent documentation
  12. seeds.
  13. =====================
  14. File: CrashLandingAppDelegate.m
  15. Abstract: The UIApplication delegate class, which is the central controller of
  16. the application.
  17. Version: 1.6
  18. Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Inc.
  19. ("Apple") in consideration of your agreement to the following terms, and your
  20. use, installation, modification or redistribution of this Apple software
  21. constitutes acceptance of these terms.  If you do not agree with these terms,
  22. please do not use, install, modify or redistribute this Apple software.
  23. In consideration of your agreement to abide by the following terms, and subject
  24. to these terms, Apple grants you a personal, non-exclusive license, under
  25. Apple's copyrights in this original Apple software (the "Apple Software"), to
  26. use, reproduce, modify and redistribute the Apple Software, with or without
  27. modifications, in source and/or binary forms; provided that if you redistribute
  28. the Apple Software in its entirety and without modifications, you must retain
  29. this notice and the following text and disclaimers in all such redistributions
  30. of the Apple Software.
  31. Neither the name, trademarks, service marks or logos of Apple Inc. may be used
  32. to endorse or promote products derived from the Apple Software without specific
  33. prior written permission from Apple.  Except as expressly stated in this notice,
  34. no other rights or licenses, express or implied, are granted by Apple herein,
  35. including but not limited to any patent rights that may be infringed by your
  36. derivative works or by other works in which the Apple Software may be
  37. incorporated.
  38. The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
  39. WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
  40. WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  41. PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
  42. COMBINATION WITH YOUR PRODUCTS.
  43. IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
  44. CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  45. GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  46. ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
  47. DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
  48. CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
  49. APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  50. Copyright (C) 2008 Apple Inc. All Rights Reserved.
  51. */
  52. #import <mach/mach_time.h>
  53. #import "CrashLandingAppDelegate.h"
  54. #import "MyEAGLView.h"
  55. #import "SoundEngine.h"
  56. // CONSTANTS
  57. #define kUserNameDefaultKey @"userName"   // NSString
  58. #define kHighScoresDefaultKey @"highScores" // NSArray of NSStrings
  59. #define kAccelerometerFrequency 100 // Hz
  60. #define kFilteringFactor 0.1 // For filtering out gravitational affects
  61. #define kRenderingFPS 30.0 // Hz
  62. #define kListenerDistance 1.0  // Used for creating a realistic sound field
  63. // MACROS
  64. // Converts degrees to radians for calculating the orientation of the rocket.
  65. #define DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) / 180.0 * M_PI)
  66. // Used to randomize the starting condtions of the game
  67. #define RANDOM_SEED() srandom((unsigned)(mach_absolute_time() & 0xFFFFFFFF))
  68. // Used to randomize the position of the base the rocket must land on.
  69. #define RANDOM_FLOAT() ((float)random() / (float)INT32_MAX)
  70. // CLASS INTERFACE
  71. @interface CrashLandingAppDelegate ()
  72. - (void) renderScene;
  73. - (void) resetGame;
  74. - (void) saveScore;
  75. @end
  76. // CLASS IMPLEMENTATIONS
  77. @implementation CrashLandingAppDelegate
  78. + (void) initialize {
  79. if(self == [CrashLandingAppDelegate class]) {
  80. RANDOM_SEED();
  81. //Make sure we have a default set of high-scores in the preferences
  82. [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObject:[NSArray array] forKey:kHighScoresDefaultKey]];
  83. }
  84. }
  85. - (void) applicationDidFinishLaunching:(UIApplication*)application {
  86. NSBundle* bundle = [NSBundle mainBundle];
  87. CGRect rect = [[UIScreen mainScreen] bounds];
  88. // Set up variable for starting the game
  89. _firstTap = YES;
  90. //Create and editable text field. This is used only when the user successfully lands the rocket.
  91. _textField = [[UITextField alloc] initWithFrame:CGRectMake(60, 214, 200, 30)];
  92. [_textField setDelegate:self];
  93. [_textField setBackgroundColor:[UIColor colorWithWhite:0.0 alpha:0.5]];
  94. [_textField setTextColor:[UIColor whiteColor]];
  95. [_textField setFont:[UIFont fontWithName:kFontName size:kStatusFontSize]];
  96. [_textField setPlaceholder:@"Tap to edit"];
  97. //Set up OpenGL projection matrix
  98. glMatrixMode(GL_PROJECTION);
  99. glOrthof(0, rect.size.width, 0, rect.size.height, -1, 1);
  100. glMatrixMode(GL_MODELVIEW);
  101. //Initialize OpenGL states
  102. glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  103. glEnable(GL_TEXTURE_2D);
  104. glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  105. glEnableClientState(GL_VERTEX_ARRAY);
  106. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  107. //Loadthe  background texture and configure it
  108. _textures[kTexture_Title] = [[Texture2D alloc] initWithImage: [UIImage imageNamed:@"Title.png"]];
  109. glBindTexture(GL_TEXTURE_2D, [_textures[kTexture_Title] name]);
  110. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  111. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  112. //Load other textures
  113. _textures[kTexture_Lander] = [[Texture2D alloc] initWithImage: [UIImage imageNamed:@"Ship.png"]];
  114. _textures[kTexture_Base] = [[Texture2D alloc] initWithImage: [UIImage imageNamed:@"Platform.png"]];
  115. _textures[kTexture_MainThrust] = [[Texture2D alloc] initWithImage: [UIImage imageNamed:@"ThrustMiddle.png"]];
  116. _textures[kTexture_LeftThrust] = [[Texture2D alloc] initWithImage: [UIImage imageNamed:@"ThrustLeft.png"]];
  117. _textures[kTexture_RightThrust] = [[Texture2D alloc] initWithImage: [UIImage imageNamed:@"ThrustRight.png"]];
  118. _textures[kTexture_Explosion] = [[Texture2D alloc] initWithImage: [UIImage imageNamed:@"Explosion.png"]];
  119. _textures[kTexture_FuelBar] = [[Texture2D alloc] initWithImage: [UIImage imageNamed:@"EmptyFuelBar.png"]];
  120. _textures[kTexture_FuelLevel] = [[Texture2D alloc] initWithImage: [UIImage imageNamed:@"FuelBar.png"]];
  121. _textures[kTexture_LightGreen] = [[Texture2D alloc] initWithImage: [UIImage imageNamed:@"LightGreen.png"]];
  122. _textures[kTexture_LightRed] = [[Texture2D alloc] initWithImage: [UIImage imageNamed:@"LightRed.png"]];
  123. _textures[kTexture_LabelSpeed] = [[Texture2D alloc] initWithString:@"Speed" dimensions:CGSizeMake(64, 32) alignment:UITextAlignmentLeft fontName:kFontName fontSize:kLabelFontSize];
  124. _textures[kTexture_LabelAngle] = [[Texture2D alloc] initWithString:@"Angle" dimensions:CGSizeMake(64, 32) alignment:UITextAlignmentLeft fontName:kFontName fontSize:kLabelFontSize];
  125. _textures[kTexture_LabelPosition] = [[Texture2D alloc] initWithString:@"Position" dimensions:CGSizeMake(64, 32) alignment:UITextAlignmentLeft fontName:kFontName fontSize:kLabelFontSize];
  126. // Note that each of the Sound Engine functions defined in SoundEngine.h return an OSStatus value.
  127. // Although the code in this application does not check for errors, you'll want to add error checking code 
  128. // in your own application, particularly during development.
  129. //Setup sound engine. Run  it at 44Khz to match the sound files
  130. SoundEngine_Initialize(44100);
  131. // Assume the listener is in the center at the start. The sound will pan as the position of the rocket changes.
  132. SoundEngine_SetListenerPosition(0.0, 0.0, kListenerDistance);
  133. // Load each of the four sounds used in the game.
  134. SoundEngine_LoadEffect([[bundle pathForResource:@"Start" ofType:@"caf"] UTF8String], &_sounds[kSound_Start]);
  135. SoundEngine_LoadEffect([[bundle pathForResource:@"Success" ofType:@"caf"] UTF8String], &_sounds[kSound_Success]);
  136. SoundEngine_LoadEffect([[bundle pathForResource:@"Failure" ofType:@"caf"] UTF8String], &_sounds[kSound_Failure]);
  137. SoundEngine_LoadLoopingEffect([[bundle pathForResource:@"Thrust" ofType:@"caf"] UTF8String], NULL, NULL, &_sounds[kSound_Thrust]);
  138. // Compute the land's "bounds"
  139. _landerBounds = CGRectMake(0, 0, 84, 66);
  140. _landerBounds = CGRectOffset(_landerBounds, -_landerBounds.size.width / 2, -_landerBounds.size.height / 2);
  141. //Show window
  142. // [_window makeKeyAndVisible];
  143. //Configure and start accelerometer
  144. [[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / kAccelerometerFrequency)];
  145. [[UIAccelerometer sharedAccelerometer] setDelegate:self];
  146. //Render the Title frame 
  147. glDisable(GL_BLEND);
  148. [_textures[kTexture_Title] drawInRect:[glView bounds]];
  149. glEnable(GL_BLEND);
  150. //Swap the framebuffer
  151. [glView swapBuffers];
  152. }
  153. // Release resources when they are no longer needed
  154. - (void) dealloc {
  155. unsigned i;
  156. [_statusTexture release];
  157. SoundEngine_Teardown();
  158. for(i = 0; i < kNumTextures; ++i)
  159. [_textures[i] release];
  160. [_textField release];
  161. [glView release];
  162. [window release];
  163. [super dealloc];
  164. }
  165. // Implement this method to get the lastest data from the accelerometer 
  166. - (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration {
  167. //Use a basic low-pass filter to only keep the gravity in the accelerometer values
  168. _accelerometer[0] = acceleration.x * kFilteringFactor + _accelerometer[0] * (1.0 - kFilteringFactor);
  169. _accelerometer[1] = acceleration.y * kFilteringFactor + _accelerometer[1] * (1.0 - kFilteringFactor);
  170. _accelerometer[2] = acceleration.z * kFilteringFactor + _accelerometer[2] * (1.0 - kFilteringFactor);
  171. }
  172. // Saves the user name and score after the user enters it in the provied text field. 
  173. - (void)textFieldDidEndEditing:(UITextField*)textField {
  174. //Save name
  175. [[NSUserDefaults standardUserDefaults] setObject:[textField text] forKey:kUserNameDefaultKey];
  176. //Save the score
  177. [self saveScore];
  178. }
  179. // Terminates the editing session
  180. - (BOOL)textFieldShouldReturn:(UITextField*)textField {
  181. //Terminate editing
  182. [textField resignFirstResponder];
  183. return YES;
  184. }
  185. // Called by touchesEnded:withEvent: when the user taps the screen
  186. - (void)handleTap {
  187. if (_firstTap){ // Replace the title screen with the background
  188. //Load background texture and configure it
  189. _textures[kTexture_Background] = [[Texture2D alloc] initWithImage:[UIImage imageNamed:@"Background.png"]];
  190. glBindTexture(GL_TEXTURE_2D, [_textures[kTexture_Background] name]);
  191. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  192. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  193. //Reset
  194. [self resetGame];
  195. _firstTap = NO;
  196. } else { // Either the user tapped to start a new game or the user successfully landed the rocket.
  197. //Stop rendering timer
  198. [_timer invalidate];
  199. _timer = nil;
  200. //In the lander was landed successfully, save the current score or start a new game
  201. if(_state == kState_Success)
  202. [self saveScore];
  203. else
  204. [self resetGame];
  205. }
  206. }
  207. // Saves the user's score in the application preferences
  208. - (void)saveScore {
  209. NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  210. NSString* name = [defaults stringForKey:kUserNameDefaultKey];
  211. NSDate* date = [NSDate date];
  212. NSMutableArray* scores;
  213. NSMutableString* string;
  214. unsigned i;
  215. NSDictionary* dictionary;
  216. //Dismiss text field
  217. [_textField endEditing:YES];
  218. [_textField removeFromSuperview];
  219. //Make sure a player name exists, if only the default
  220. if(![name length])
  221. name = @"Player";
  222. //Update the high-scores in the preferences
  223. scores = [NSMutableArray arrayWithArray:[defaults objectForKey:kHighScoresDefaultKey]];
  224. [scores addObject:[NSDictionary dictionaryWithObjectsAndKeys:name, @"name", [NSNumber numberWithUnsignedInt:_score], @"score", date, @"date", nil]];
  225. [scores sortUsingDescriptors:[NSArray arrayWithObject:[[[NSSortDescriptor alloc] initWithKey:@"score" ascending:NO] autorelease]]];
  226. [defaults setObject:scores forKey:kHighScoresDefaultKey];
  227. //Display high-scores in status texture
  228. string = [NSMutableString stringWithString:@"       HIGH-SCORESn"];
  229. for(i = 0; i < MIN([scores count], 10); ++i) {
  230. dictionary = [scores objectAtIndex:i];
  231. [string appendFormat:@"n%s%i. %@ (%@ Pts)", ([[dictionary objectForKey:@"date"] isEqualToDate:date] ? "> " : "   "), i + 1, [dictionary objectForKey:@"name"], [dictionary objectForKey:@"score"]/*, [[dictionary objectForKey:@"date"] descriptionWithCalendarFormat:@"%m/%d %I:%M %p" timeZone:nil locale:nil]*/];
  232. }
  233. [_statusTexture release];
  234. _statusTexture = [[Texture2D alloc] initWithString:string dimensions:CGSizeMake(256, 256) alignment:UITextAlignmentLeft fontName:kFontName fontSize:kScoreFontSize];
  235. _state = kState_StandBy;
  236. //Render a frame
  237. [self renderScene];
  238. }
  239. // Release the status texture and initialized values in preparation for starting a new game
  240. - (void)resetGame {
  241. CGRect bounds = [glView bounds];
  242. //Destroy the status texture
  243. [_statusTexture release];
  244. _statusTexture = nil;
  245. //Reset the state to running mode
  246. _state = kState_Running;
  247. _lastTime = CFAbsoluteTimeGetCurrent();
  248. _lastThrust = NO;
  249. //Randomize the landing base position
  250. _basePosition = RANDOM_FLOAT() * (bounds.size.width - kBaseSize) + kBaseSize / 2;
  251. //Set the initial state or the rocket
  252. _fuel = kInitialFuel;
  253. _rotation = 0.0;
  254. _rotationVelocity = 0.0;
  255. _position.x = RANDOM_FLOAT() * (bounds.size.width - _landerBounds.size.width) + _landerBounds.size.width / 2;
  256. _position.y = bounds.size.height + _landerBounds.size.height;
  257. _velocity.x = 0.0;
  258. _velocity.y = -kInitialVelocity;
  259. //Render a frame immediately
  260. [self renderScene];
  261. //Start rendering timer
  262. _timer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / kRenderingFPS) target:self selector:@selector(renderScene) userInfo:nil repeats:YES];
  263. //Play start sound
  264. SoundEngine_StartEffect( _sounds[kSound_Start]);
  265. }
  266. // Renders one scene of the game
  267. - (void)renderScene {
  268. CGRect bounds = [glView bounds];
  269. float maxDistance = (kBaseSize - _landerBounds.size.width) / 2.0;
  270. BOOL thrust = NO;
  271. CFTimeInterval time;
  272. float dTime;
  273. Vector2D force,
  274. orientation;
  275. CGRect rect;
  276. float lateralAcceleration;
  277. CGSize size;
  278. //Update game state
  279. if(_state == kState_Running) {
  280. time = CFAbsoluteTimeGetCurrent();
  281. dTime = time - _lastTime;
  282. //Update lander position
  283. _position.x += _velocity.x * dTime;
  284. _position.y += _velocity.y * dTime;
  285. //Wrap the lander horizontal position
  286. if(_position.x < 0.0)
  287. _position.x = bounds.size.width + _position.x;
  288. else if(_position.x > bounds.size.width)
  289. _position.x = bounds.size.width - _position.x;
  290. //Clamp the lander top vertical position
  291. if(_position.y > bounds.size.height - _landerBounds.size.height / 2) {
  292. _position.y = bounds.size.height - _landerBounds.size.height / 2;
  293. _velocity.y = 0.0;
  294. }
  295. //Update the rocket orientation
  296. _rotation += _rotationVelocity * dTime;
  297. //Check to see if the rocket  touched the ground
  298. orientation.x = sinf(DEGREES_TO_RADIANS(_rotation));
  299. orientation.y = cosf(DEGREES_TO_RADIANS(_rotation));
  300. rect = CGRectApplyAffineTransform(_landerBounds, CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(_rotation)));
  301. if(_position.y + CGRectGetMinY(rect) <= kBaseOffset + (GLfloat)[_textures[kTexture_Base] pixelsHigh] / 2) {
  302. //Check whether the landing is successful  or not
  303. if((_velocity.y >= -kMaxVelocity) && (fabsf(_rotation) <= kMaxRotation) && (fabsf(_position.x - _basePosition) < maxDistance)) {
  304. SoundEngine_StartEffect(_sounds[kSound_Success]);
  305. _state = kState_Success;
  306. _score = kScoreVelocity * (1.0 - _velocity.y / -kMaxVelocity) + kScoreFuel * (_fuel / kInitialFuel) + kScoreRotation * (1.0 - _rotation / kMaxRotation) + kScoreDistance * (1.0 - fabsf(_position.x - _basePosition) / maxDistance);
  307. _statusTexture = [[Texture2D alloc] initWithString:[NSString stringWithFormat:@"SUCCESS!nYou scored %i PointsnnEnter your name:", _score] dimensions:CGSizeMake(256, 128) alignment:UITextAlignmentCenter fontName:kFontName fontSize:kStatusFontSize];
  308. //Show text field that allows the user to enter a name for the score
  309. [_textField setText:[[NSUserDefaults standardUserDefaults] stringForKey:kUserNameDefaultKey]];
  310. [window addSubview:_textField];
  311. }
  312. else {  // The landing is not successful; the rocket crashed!
  313. SoundEngine_Vibrate();
  314. SoundEngine_SetEffectPosition(_sounds[kSound_Failure], 2.0 * (_position.x / bounds.size.width) - 1.0, 0.0, 0.0);
  315. SoundEngine_StartEffect(_sounds[kSound_Failure]);
  316. _state = kState_Failure;
  317. _statusTexture = [[Texture2D alloc] initWithString:@"YOU CRASHED!" dimensions:CGSizeMake(256, 32) alignment:UITextAlignmentCenter fontName:kFontName fontSize:kStatusFontSize];
  318. }
  319. //Stop rendering timer
  320. [_timer invalidate];
  321. _timer = nil;
  322. }
  323. //Update lander velocity, rotation speed and fuel
  324. else {
  325. thrust = (_accelerometer[1] <= kMainThrustThreshold ? YES : NO);
  326. force.x = 0.0;
  327. force.y = -kMass * kGravity;
  328. if(thrust && (_fuel > 0.0)) {
  329. force.x = -orientation.x * kMainThrust;
  330. force.y = orientation.y * kMainThrust;
  331. }
  332. else
  333. thrust = NO;
  334. _velocity.x += force.x / kMass * dTime;
  335. _velocity.y += force.y / kMass * dTime;
  336. lateralAcceleration = _accelerometer[0];
  337. if(fabsf(lateralAcceleration) >= kLateralThrustThreshold) {
  338. _velocity.x += (lateralAcceleration > 0.0 ? lateralAcceleration - kLateralThrustThreshold : lateralAcceleration + kLateralThrustThreshold) * kLateralSpeed;
  339. _rotationVelocity = -(lateralAcceleration > 0.0 ? lateralAcceleration - kLateralThrustThreshold : lateralAcceleration + kLateralThrustThreshold) * kRotationSpeed;
  340. }
  341. if(thrust) {  // Update the fuel level
  342. _fuel -= dTime;
  343. if(_fuel < 0.0)
  344. _fuel = 0.0;
  345. }
  346. _lastTime = time;
  347. }
  348. //Start or stop thurst sound & update its position
  349. if(thrust && !_lastThrust)
  350. SoundEngine_StartEffect( _sounds[kSound_Thrust]);
  351. else if(!thrust && _lastThrust)
  352.  SoundEngine_StopEffect(_sounds[kSound_Thrust], false);
  353. if(thrust)
  354. SoundEngine_SetEffectPosition(_sounds[kSound_Thrust], 2.0 * (_position.x / bounds.size.width) - 1.0, 0.0, 0.0);
  355. _lastThrust = thrust;
  356. }
  357. //Draw background
  358. glDisable(GL_BLEND);
  359. [_textures[kTexture_Background] drawInRect:bounds];
  360. glEnable(GL_BLEND);
  361. //Draw the game elements
  362. if(_state != kState_StandBy) {
  363. //Draw the landing base
  364. [_textures[kTexture_Base] drawAtPoint:CGPointMake(_basePosition, kBaseOffset)];
  365. //Draw the lander
  366. glPushMatrix();
  367. glTranslatef(_position.x, _position.y, 0);
  368. if(_rotation)
  369. glRotatef(_rotation, 0, 0, 1);
  370. [_textures[kTexture_Lander] drawAtPoint:CGPointZero];
  371. if(_state == kState_Running) {
  372. if(thrust)
  373. [_textures[kTexture_MainThrust] drawAtPoint:CGPointMake(0, -56)];
  374. if(lateralAcceleration > kLateralThrustThreshold)
  375. [_textures[kTexture_LeftThrust] drawAtPoint:CGPointMake(-35.5, -48)];
  376. if(lateralAcceleration < -kLateralThrustThreshold)
  377. [_textures[kTexture_RightThrust] drawAtPoint:CGPointMake(35.5, -48)];
  378. }
  379. glPopMatrix();
  380. //Draw the status lights
  381. [_textures[(_velocity.y >= -kMaxVelocity ? kTexture_LightGreen : kTexture_LightRed)] drawAtPoint:CGPointMake(kSpeedX, kLightY)];
  382. [_textures[(fabsf(_rotation) <= kMaxRotation ? kTexture_LightGreen : kTexture_LightRed)] drawAtPoint:CGPointMake(kAngleX, kLightY)];
  383. [_textures[(fabsf(_position.x - _basePosition) < maxDistance ? kTexture_LightGreen : kTexture_LightRed)] drawAtPoint:CGPointMake(kPositionX, kLightY)];
  384. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  385. [_textures[kTexture_LabelSpeed] drawAtPoint:CGPointMake(kSpeedX + kLabelOffset, kLabelY)];
  386. [_textures[kTexture_LabelAngle] drawAtPoint:CGPointMake(kAngleX + kLabelOffset, kLabelY)];
  387. [_textures[kTexture_LabelPosition] drawAtPoint:CGPointMake(kPositionX + kLabelOffset, kLabelY)];
  388. glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  389. //Draw the explosion if the lander is crashed
  390. if(_state == kState_Failure)
  391. [_textures[kTexture_Explosion] drawAtPoint:CGPointMake(_position.x, _position.y)];
  392. //Draw the fuel bar
  393. if(_state == kState_Running) {
  394. size = [_textures[kTexture_FuelBar] contentSize];
  395. [_textures[kTexture_FuelBar] drawAtPoint:CGPointMake(kFuelBarX, kFuelBarY)];
  396. if(_fuel > 0)
  397. [_textures[kTexture_FuelLevel] drawInRect:CGRectMake(kFuelBarX - size.width / 2 + 1, kFuelBarY - size.height / 2 + 1, size.width - 2, _fuel / kInitialFuel * (size.height - 2))];
  398. }
  399. }
  400. //Draw the overlay status texture
  401. if(_statusTexture) {
  402. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  403. [_statusTexture drawAtPoint:CGPointMake(bounds.size.width / 2, bounds.size.height * 2 / 3)];
  404. glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  405. }
  406. //Swap the framebuffer
  407. [glView swapBuffers];
  408. }
  409. @end