iPhone Game Develop Memo

Peeking Inside the iPhone Toolbox

These technologies include Objective-C or C/C++, Xcode, UIKit, Quartz 2D, Core

Animation, OpenGL, audio APIs, networking, and GameKit.


UIKit provides one of the simplest ways to draw images and other useful UI elements, still remains relatively fast due to underlying hardware acceleration. 

UIKit is a great choice for games that don’t have a large number of graphical elements or animations and don’t need to run at the maximum of 60 fps.


Quartz 2D provides developers with a more advanced, low-level drawing engine.

While it may be too slow to use for rendering the main elements of a game, Quartz 2D is another valuable tool in the iPhone developer’s toolbox.


OpenGL ES gives you powerful graphics rendering using a tried-and-true interface, may be the single most important tool in your toolbox.


Audio APIs

You can use more advanced APIs such as OpenAL or simpler built-in services.


Networking

Real-time multiplayer activities can be implemented through sockets and streams with servers and clients, or through GameKit’s Bluetooth matchmaking.


UIKit Controls

The Cocoa Touch app environment

Two frameworks are central to this environment:

 Foundation framework: Defines common classes such as NSObject and NSString that are used throughout Cocoa Touch.

 UIKit framework: Provides classes for the creation of iPhone screens with buttons, labels, text fields, sliders, tables of data, and other UI components. Besides UI components, UIKit defines Objective-C classes that encapsulate app object properties and methods, and prototypes that define various app, view, and input events.


Memory Management in Objective-C

In Objective-C, release is not the same as free in C. release 只是把object的引用计数-1, 该对象的引用计数为0, 会被自动释放.

alloc, clone, retain操作都会增加引用计数, 所以需要对应一个release. 


autorelease pool管理的的object不需要手动释放, 例如

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

// other code here

[pool release];


Cocoa Touch and the UIKit Framework

UIView

A UIView is a container object, contain buttons, icons, labels, text fields, and any sort of UI components.

The Frame Property: 义了viewpositionsize. Origion是左上角点. Sizewidthheight.

CGRect newframe;

newFrame.origin = CGPointMake(100,100);

newFrame.size = CGSizeMake(100,40);

UIView *myView = [[UIView alloc] initWithFrame:newFrame];

颜色:

myView.backgroundColor = [UIColor redColor];

myView.backgroundColor = [UIColor colorWithRed:1.0 green:0.5 blue:1.0 alpha:1.0];

myView.backgroundColor = [UIColor clearColor]; // 设置为透明色! 当然也可以通过设置alpha值为0实现.

The Center Property: view的中心点.

myView.center = CGPointMake(15,25);


Loading and Displaying Images Programmatically

UIImage* myImageObj;

NSString* imagePath = [[NSBundle mainBundle] pathForResource:@"myImage" ofType:@"png"];

// load it like this

myImageObj = [[UIImage alloc] initWithContentsOfFile:imagePath];

// or like this

myImageObj = [UIImage imageNamed:imagePath]; 

be aware that [UIImage imageNamed:] tends to retain images longer than necessary sometimes.


UIImageimmutable, 意味着你不能改image的属性, 要想draw image, 须依靠ImageView:

UIImageView* myImgView = [[UIImageView alloc] initWithImage:myImageObj];

[mainView addSubview:myImgView];


Check Collision:

CGRectIntersectsRect: detect whether one view’s frame overlaps another view’s frame.


CADisplayLink: NSTimer的又一种选择

对于NSTimer, CADisplayLink更精确, 且它是和iphonescreen logic同步的, 每秒刷新60.

CADisplayLink * theTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(gameLogic)];

theTimer.frameInterval = 2;

[theTimer addToRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];

CADisplayLink认就是repeat, 直到invalidate为止.


applicationWillResignActive

applicationWillResignActive is called when the user is interrupted, such as when the user receives a phone call or text, or when the device is locked.


applicationDidBecomeActive

iPhone在中断(applicationWillResignActive将被调用)后回到程序将调用的函数.


applicationDidReceiveMemoryWarning 

内存低时会被调用. You should use the applicationDidReceiveMemoryWarning method to release any cached data your application may hold in the app delegate. 例如清楚web cache


viewDidUnload

view退出的时候调用到,相当于虚构函数.


Saving and Loading Game State

NSUserDefaults

NSString *kLivesKey = @"IVBrickerLives";

NSString *kScoreKey = @"IVBrickerScore";

- (void)saveGameState {

[[NSUserDefaults standardUserDefaults] setInteger:lives forKey:kLivesKey];

[[NSUserDefaults standardUserDefaults] setInteger:score forKey:kScoreKey];

}

- (void)loadGameState {

lives = [[NSUserDefaults standardUserDefaults] integerForKey:kLivesKey];

livesLabel.text = [NSString stringWithFormat:@"%d", lives];

score = [[NSUserDefaults standardUserDefaults] integerForKey:kScoreKey];

scoreLabel.text = [NSString stringWithFormat:@"%d", score];

}


XCode 一直停在Attaching to…的解决方法:

进入Orgnization->project->删除当前启动的项目


Handling Accelerometer Input

- (void) accelerometer:(UIAccelerometer *)accelerometer

didAccelerate:(UIAcceleration *)accel

{

//accel.x, accel.y, accel.z可以得到x, y, z;

}

- (void)viewDidLoad {

[super viewDidLoad];

UIAccelerometer *theAccel = [UIAccelerometer sharedAccelerometer];

theAccel.updateInterval = 1.0f / 30.0f; //设置更新间隔

theAccel.delegate = self; // 把当前view controlleraccelerometerdelegate.

//那么,这个加速器就会自动调用当前view controllerdidAccelerate函数.

// 当然,还必须首先声明UIAccelerometerDelegate:

// @interface IVBrickerViewController : UIViewController<UIAccelerometerDelegate>


Handling Touchscreen Input

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

UITouch *touch = [[event allTouches] anyObject];

[touch locationInView:touch.view].x; // the x of the touch point.

}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

UITouch *touch = [[event allTouches] anyObject];

[touch locationInView:touch.view].x //当前touch pointx分量

}


Timer

float theInterval = 1.0/30.0;

[NSTimer scheduledTimerWithTimeInterval:theInterval 

target:self //指明目标是当前view controller

selector:@selector(animteFun:) //每隔一个interval就会调用这个回调函数

userInfo:nil

repeats:YES];


CADisplayLink (QuartzCore.framework下有效)

CADisplayLink, 也可以周期性的调用某一个方法, NSTimer更精确.

CADisplayLink * theTimer = [CADisplayLink displayLinkWithTarget:self

selector:@selector(animateFun)];

// CADisplayLink defaults to running at 60 frames per second, 这里我们设置为2, 表明每2帧调//用一次回调函数.

// CADisplayLink is set to repeat until it is invalidated.

theTimer.frameInterval = 2;  

[theTimer addToRunLoop: [NSRunLoop currentRunLoop]

forMode: NSDefaultRunLoopMode];


Application Interruptions

- (void)applicationWillResignActive:(UIApplication *)application {

[viewController pauseGame];

[viewController saveGameState];

}

- (void)applicationDidBecomeActive:(UIApplication *)application {

[viewController loadGameState];

[viewController startPlaying];

}


Low Memory Warnings

applicationDidReceiveMemoryWarning:.

当内存比较低时,调用这个方法, 你可以在这里释放不用的内存.


Saving and Loading Game State

NSString *kLivesKey = @"IVBrickerLives";

NSString *kScoreKey = @"IVBrickerScore";

- (void)saveGameState {

[[NSUserDefaults standardUserDefaults] setInteger:lives forKey:kLivesKey];

[[NSUserDefaults standardUserDefaults] setInteger:score forKey:kScoreKey];

}

- (void)loadGameState {

lives = [[NSUserDefaults standardUserDefaults] integerForKey:kLivesKey];

livesLabel.text = [NSString stringWithFormat:@"%d", lives];

score = [[NSUserDefaults standardUserDefaults] integerForKey:kScoreKey];

scoreLabel.text = [NSString stringWithFormat:@"%d", score];

}


Managing Memory with a Custom Image Loader

static NSMutableDictionary *dict;

+ (UIImage*)loadImage:(NSString*)imageName

{

if (!dict) dict = [[NSMutableDictionary dictionary] retain];

UIImage* image = [dict objectForKey:imageName];

if (!image)

{

NSString* imagePath = [[NSBundle mainBundle]

pathForResource:imageName

ofType:nil];

image = [UIImage imageWithContentsOfFile:imagePath];

if (image)

{

[dict setObject:image forKey:imageName];

}

}

return image;

}

+ (void)releaseCache {

if (dict) {

[dict removeAllObjects];

}

}


Animation 

  1. 使用UIImageView Animation Properties

NSMutableArray *images = [NSMutableArray alloc] initWithCapacity: 30];

// load the images into the array

for (int i = 1; i <= 30; i++) {

NSString *imageName = [NSString stringWithFormat: @”animation1_f%0d.png”, i ];

UIImage *image = [ImageCache loadImage: imageName ];

[images addObject: image];

}

// set the animations property on the image view

imageView.animationImages = images; // 加入image array

[images release];

imageView.animationDuration = 1; //间隔时间

imageView.animationRepeatCount = 1; // 重复次数

[imageView startAnimating];


  1. 使用NSTimer
  2. 使用CADisplayLink


Quartz 2D Game

A Quart2D game loop: MVC model

Model: persist, records shared information on a common “blackboard,” while capturing external

changes to the UI (such as a finger touch or accelerometer movement).

Controller: update

View: render

Quartz 2D plays a heavy role in the views, and is only a bit player in the controller and model.


Quart 2D中的坐标系统,

左下角是(0, 0), OpenGL


Saving and Restoring the Context

// Get a graphics context, with no transformations

CGContextRef context = UIGraphicsGetCurrentContext();

得到当前viewcontext, context就像是a physical canvas, 可以被moved, rotated, shrunk, stretched. 想象一张很大的canvas, 你可以move, rotate, 使得能更方便的绘画.

绘画前,你要save context, 然后canvas做一定的matrix变换(实就是move,rotate等操作), 绘画完之后再restore context, 使得canvas回到原来的位置!


// Save context, This remembers the current state of the canvas for us

CGContextSaveGState(context)

// restore context

CGContextRestoreGState(context)


Quartz 2D describes the shifts of the canvas with matrix transformations. In 2D games, we’re

typically interested in affine transformations.

It’s important to follow one simple algorithm whenever drawing with Quartz 2D:

1. Save the context state.

2. Perform a single, combined matrix transformation.

3. Draw your graphics that need this transformation.

4. Restore the context.

 

例子:

- (void)drawRect:(CGRect)rect {

// Get a graphics context, saving its state

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSaveGState(context);

// Reset the transformation

// CGContextGetCTM得到CTM(the graphic context’s transformation matrix)

CGAffineTransform t0 = CGContextGetCTM(context);

t0 = CGAffineTransformInvert(t0); // 创建t0的逆矩

CGContextConcatCTM(context,t0); // 将已存在的矩阵与t0连接起来(乘法操作)

// Draw a green rectangle

CGContextBeginPath(context);

CGContextSetRGBFillColor(context, 0,1,0,1); // 指定我们要绘制的path颜色 rgba

// 增加一个rectangle, CGRectMake是一个macro

CGContextAddRect(context, CGRectMake(0,0,100,200)); // (x, y, width, height)

CGContextClosePath(context); // beginPath 对应

CGContextDrawPath(context,kCGPathFill); //绘画

CGContextRestoreGState(context); 

}


Changing to Landscape Orientation

[application setStatusBarOrientation: UIInterfaceOrientationLandscapeRight

animated:NO];

// Get the orientation of the device.

UIDeviceOrientation orient = [[UIDevice currentDevice] orientation];


若定CGFloat scale;

运行到Self.scale = 1.0f, 如果你自已定义了setScale函数, 调用到你自函数.

如果你只是写scale = 1.0f, 则不会调用setScale函数.


OpenGL Basics

Matrix Types

In OpenGL there are four main matrixes, called 

the model,  scene objects, 屏幕中的所有对象

the view,   camera position, 们的相机的位置和朝向

the viewport,  recorder, 相当于照片的大小和aspect ratio(宽高比).

the projection lens, 相当于相机上的镜头类型, 是透视投影还是水平投影, 们通过projection 阵来设置我们的视野区域.

Project阵又分为: perspective mode and orthographic mode.

perspective mode是透视投影, 远处的物体小, 处的物体大.

这里, 们需要定义一个view frustrum

例如: glFrustrum(left, right, bottom, top, near, far);

更方便的做法是使用gluPerspective(fovY, aspect, zNear, zFar). It allows you to specify the view frustrum as an aspect ratio and a viewing angle // 它不包含在openGL ES.

fovY: is the vertical angle of the field of view.

aspect ratio: is the width of the view over the height. (width / height)

zNear and zFar are the z positions of the near and far clipping planes.


orthographic mode是水平投影, 远近的物体一样大小.

例如: glMatrixMode(GL_PROJECTION);

glLoadIdentity();

glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);// (left, right, bottom, top, near, far);


Rendering Basics 

EAGLView中有一个CAEAGLLayer对象, CAEAGLLayer对象是真正在底层contextrender opengl scene.

+ (Class)layerClass {

return [CAEAGLLayer class];

} // This tells the UIView that its main layer is going to be a CAEAGLLayer.


//初始化函数

- (id)initWithFrame:(CGRect)rect {

if ((self = [super initWithFrame:rect])) {

// get the layer

CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;

//不要把最底层设置成transparency, opengliPhone上会对最底层实施优化,

//设成transparency会影响效率.

eaglLayer.opaque = YES;

// set the drawing properties on our layer

eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:

[NSNumber numberWithBool:NO],

//refers to how the memory is handled after it has been displayed. Generally, we do not //want to retain the backing buffer. Opengl不停在绘制frame, 保持当前frame的内存叫做//backing buffer. 通常情况下, 们都将进入新的frame,而不用保持当前frame, 即不用//retain the backing buffer.

kEAGLDrawablePropertyRetainedBacking,

//指定图片格式.RGBA8表示每一个pixel需要1 byte来保存r,g,ba的每个分量

//RGB565表示5 bits for red, 6 bits for green, 5 bits for blue.

//显然RGBA8表示的颜色更丰富.

kEAGLColorFormatRGBA8, 

kEAGLDrawablePropertyColorFormat, nil];

//们将使用OpenGL ES 1.1

context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];

if (!context || ![EAGLContext setCurrentContext:context]) {

[self release];

return nil;

}

}

return self;

}


Frame Buffers, Render Buffers, and Depth Buffers

Frame buffer: basically the big chunk of memory that will be used to hold the various bits of data needed to render a single frame.

Render buffer: is where the frame will be rendered before it is copied into the CAEAGLLayer backing buffer and ultimately ends up on the display.

Depth buffer: 使得屏幕中的物体有深度(哪个在前,哪个在后), 即在z方向上是有顺序的.

glEnable(GL_DEPTH_TEST) //2D中用不到

如果使用了USE_DEPTH_BUFFER, 将会block createFramebuffer方法.

- (BOOL)createFramebuffer {

glGenFramebuffersOES(1, &viewFramebuffer);

glGenRenderbuffersOES(1, &viewRenderbuffer);


glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);

glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);


[context renderbufferStorage:GL_RENDERBUFFER_OES

fromDrawable:(CAEAGLLayer*)self.layer];

glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,

GL_COLOR_ATTACHMENT0_OES,

GL_RENDERBUFFER_OES, viewRenderbuffer);

glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,

 GL_RENDERBUFFER_WIDTH_OES, &backingWidth);

glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, 

 GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);


if (USE_DEPTH_BUFFER) {

glGenRenderbuffersOES(1, &depthRenderbuffer);

glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);

glRenderbufferStorageOES(GL_RENDERBUFFER_OES, 

GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);

glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, 

 GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, 

 depthRenderbuffer);

}


if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != 

 GL_FRAMEBUFFER_COMPLETE_OES) {

NSLog(@"failed to make complete framebuffer object %x",

glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));

return NO;

}

return YES;

}


Render:

-(void)beginDraw

{

// make sure that you are drawing to the current context

[EAGLContext setCurrentContext:context];

glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);

// make sure we are in model matrix mode and clear the frame

glMatrixMode(GL_MODELVIEW);

glClear(GL_COLOR_BUFFER_BIT);

// set a clean transform

glLoadIdentity();

}

-(void)finishDraw

{

glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);

[context presentRenderbuffer:GL_RENDERBUFFER_OES];

}


How to Draw Stuff with OpenGL

绘制一个矩形 :

1 义顶点和颜色:

static CGFloat spinnySquareVertices[8] = {

-0.5f, -0.5f,

0.5f, -0.5f,

-0.5f, 0.5f,

0.5f, 0.5f,

};

static CGFloat spinnySquareColors[16] = {

1.0, 1.0, 0, 1.0,

0, 1.0, 1.0, 1.0,

0, 0, 0, 0,

1.0, 0, 1.0, 1.0,

};

2 绘制函数

-(void)render

// clear the matrix 保存当前矩

// glPushMatrix() takes the current state of the model matrix and pushes a copy // of it onto a stack, effectively saving it for later.

glPushMatrix();


glLoadIdentity();//so our objects start at 0,0,0

// move to my position

// glTranslate() is always relative to the current matrix position.因此我们要先

//load一个单位阵

glTranslatef(x, y, z);

// rotate

glRotatef(xRotation, 1.0f, 0.0f, 0.0f); // x轴旋转xRotation

glRotatef(yRotation, 0.0f, 1.0f, 0.0f);

glRotatef(zRotation, 0.0f, 0.0f, 1.0f);

//scale

glScalef(xScale, yScale, zScale);


// load arrays into the engine

//对于二维object, vertexSize = 2, 分别是x, y

// colorSize一直是4, 分别是r, g, b, a

glVertexPointer(vertexSize, GL_FLOAT, 0, spinnySquareVertices);

glEnableClientState(GL_VERTEX_ARRAY);//诉引擎vertex被定义了

glColorPointer(colorSize, GL_FLOAT, 0, spinnySquareColors);

glEnableClientState(GL_COLOR_ARRAY);

//render, 这里vertexCount =, renderStyle =GL_TRIANGLE_STRIP

glDrawArrays(renderStyle, 0, vertexCount);


//restore the matrix 恢复到我pushMatrix时保存的矩阵

// This means that whatever is happening to the engine outside our object, we won't //change it.

glPopMatrix();

}


iPhone, 你最高只能得到60fps.


如何设计gameLoop:

一种方法是

The simplest is to simply use an NSTimer that calls your game loop every 1/60 second. The upside of this method is that it is very easy. The downside is that if you take too long to render, you can get out of sync with the display refresh rate, and your frames per second will suffer.


更好的方法是:

A more complicated approach is to simply run the game loop as fast as possible. As soon as you are finished rendering a frame, you start rendering the next—you don't wait for any timers to fire or anything. This is generally how high-end 3D games work.


对于iPhone, Apple进了display link.

A display link is a hardware-triggered callback that will call a specified method on your object every time the screen refreshes. This object is called the CADisplayLink.

Eg

displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(gameLoop)];

[displayLink setFrameInterval:animationFrameInterval];

[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

这将会每1/60调用gameloop一次. IOS3.1以后的版本适用.


Adding Buttons

有两种方式添加控件:

1 use UIKit and simply lay them over your OpenGL view

   这种方式虽然简单, 但是效率较低. 所以在游戏中的控件尽量用openglrender, 如果是设置或其他界面的控件, 就用UIKit好了!

2 use OpenGL draw your own buttons


We will use GL_LINE_LOOP to draw the unfilled button and GL_TRIANGLE_STRIP to draw the filled one.


标的选取:

在定object时候,如何选取坐标是一个问题,

是以中心(0, 0),还是以左下角为中心点.


通常情况下,以中心(0,0)点比较好!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值