아이폰어플개발정보2010. 10. 23. 18:46
[아이폰 앱 개발] iphone crash log 위치 on the MAC 및 로그까는 symbolicatecrash프로그램 및 설치하기 명령어

IPhone Crash Logs

Sometimes programs crash. This annoys users and developers alike. Users are frustrated because they cannot use crashing software, developers are frustrated because they have to hunt bugs instead of doing something creative and rewarding. How do we communicate if an iPhone application crashes?

I’ll start with a disclaimer. I’m not sure whether the information provided in this post is covered by iPhone Developer Program NDA or not. If it is, the post will be removed. Secondly, this post is a result of googling, so I haven’t invented anything new here.

Working with crash logs typically involves certain interaction between developers and users, unless they are automagically sent to the developer. First of all, the user should get the crash log and send it to the developer, who should examine it, find the bug and fix it.

iPhone OS and Mac OS X are remarkably similar architectures. Both store crash logs to help identify crashing bugs. The difference between the two is how users retrieve them. On Mac OS X every user has unrestricted access to crash logs related to the applications she runs. The iPhone does not even have a file browser. What to do? iTunes comes to the rescue.

Whenever you synchronize your iPhone or iPod Touch, all the crash logs are transferred to your computer. Here are their locations:

  • Mac OS X:~/Library/Logs/CrashReporter/MobileDevice/<DEVICE_NAME>
  • Windows XPC:\Documents and Settings\<USERNAME>\Application Data\Apple computer\Logs\CrashReporter/<DEVICE_NAME>
  • Windows VistaC:\Users\<USERNAME>\AppData\Roaming\Apple computer\Logs\CrashReporter/MobileDevice/<DEVICE_NAME>

The log file names start with application name and have the extension “crash”. They are just plain text files and can be sent by e-mail in original or zipped form, or even copy-pasted into your e-mail program.

The second part is trickier. Both Apple and common sense suggest that all AppStore binaries are shipped with stripped symbols. If you ever saw a crash log like this, read on:


까보면 아래처럼 나옮.. 저 밑에 어플리케이션하나 설치하면 크래쉬로그를 인간이 이해할 수 있는 

메모리에서 돌아가던 콘트롤명이나 생성한 객체들이나 이벤트들이 보이게됨.


Thread 0 Crashed:
0   libobjc.A.dylib           0x300c87ec 0x300bb000 + 55276
1   MobileLines               0x00006434 0x1000 + 21556
2   MobileLines               0x000064c2 0x1000 + 21698
3   UIKit                     0x30a740ac 0x30a54000 + 131244
4   UIKit                     0x30a66110 0x30a54000 + 74000
5   UIKit                     0x30a6565c 0x30a54000 + 71260
6   GraphicsServices          0x3169b0b4 0x31696000 + 20660
7   GraphicsServices          0x3169d818 0x31696000 + 30744
8   IOMobileFramebuffer       0x31f3e8f8 0x31f3d000 + 6392
9   com.apple.framework.IOKit 0x30f342b8 0x30f30000 + 17080
10  CoreFoundation            0x3025ced4 0x30229000 + 212692
11  CoreFoundation            0x3025bed6 0x30229000 + 208598
12  CoreFoundation            0x3025b584 0x30229000 + 206212
13  GraphicsServices          0x316998e4 0x31696000 + 14564
14  UIKit                     0x30a5e308 0x30a54000 + 41736
15  UIKit                     0x30a671dc 0x30a54000 + 78300
16  MobileLines               0x00002090 0x1000 + 4240
17  MobileLines               0x0000202c 0x1000 + 4140

In a nutshell, it contains function addresses and offsets instead of function names and line numbers. The structure is obvious, but, to be honest, I don’t know what “MobileLines 0×00006434 0×1000 + 21556″ is, even though I have all the source code. Thanks to Apple Developer Tools and to Craig Hockenberry who wrote about it, we have a perfect solution called symbolicatecrash.


위의 크래쉬로그를 가독성있게 분석할 수 있도록하는 툴


/usr/local/bin/으로 위의 프로그램 옮기고

$ sudo cp /Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Plug-ins/iPhoneRemoteDevice.xcodeplugin/Contents/Resources/symbolicatecrash /usr/local/bin/

위의 명령어로 설치하고

$ symbolicatecrash report.crash MobileLines.app.dSYM > report-with-symbols.crash

통해  크래쉬로그를 가독성있도록 바꿈


I copied it to /usr/local/bin/ so that I can run it whenever I want without trying to remember its original location (you may prefer a symbolic link):
$ sudo cp /Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Plug-ins/iPhoneRemoteDevice.xcodeplugin/Contents/Resources/symbolicatecrash /usr/local/bin/

Running this script with the -h option provides the minimal help:

$ symbolicatecrash -h
usage:

    symbolicatecrash [-Ah] LOGFILE [SYMBOL_PATH ...]

    Symbolicates a crashdump LOGFILE which may be "-" to refer
  to stdin. By default, all heuristics will be employed
  in an attempt to symbolicate all addresses. Additional
  symbol files can be found under specified directories.

Options:
    -A  Only symbolicate the application, not libraries
    -h  Display this message
    -v  Verbose

To add symbols to the crash log you need the dSYM file generated by the linker when you compiled your application for AppStore. In other words, when you build for AppStore you should keep the dSYM package in a safe place backed up by Time Machine. This is very important. You should keep a copy of the dSYM for each version of your application ever shipped. If you have the package, translating code offsets to function names with line numbers has never been easier:

$ symbolicatecrash report.crash MobileLines.app.dSYM > report-with-symbols.crash

Here is the result:


Thread 0 Crashed:
0   libobjc.A.dylib           0x300c87ec objc_msgSend + 20
1   MobileLines               0x00006434 -[BoardView setSelectedPiece:] (BoardView.m:321)
2   MobileLines               0x000064c2 -[BoardView touchesBegan:withEvent:] (BoardView.m:349)
3   UIKit                     0x30a740ac -[UIWindow sendEvent:] + 264
4   UIKit                     0x30a66110 -[UIApplication sendEvent:] + 248
5   UIKit                     0x30a6565c _UIApplicationHandleEvent + 4088
6   GraphicsServices          0x3169b0b4 PurpleEventCallback + 428
7   GraphicsServices          0x3169d818 HeartbeatVBLCallback + 152
8   IOMobileFramebuffer       0x31f3e8f8 IOMobileFramebufferNotifyFunc + 124
9   com.apple.framework.IOKit 0x30f342b8 IODispatchCalloutFromCFMessage + 304
10  CoreFoundation            0x3025ced4 __CFMachPortPerform + 72
11  CoreFoundation            0x3025bed6 CFRunLoopRunSpecific + 2364
12  CoreFoundation            0x3025b584 CFRunLoopRunInMode + 44
13  GraphicsServices          0x316998e4 GSEventRunModal + 268
14  UIKit                     0x30a5e308 -[UIApplication _run] + 404
15  UIKit                     0x30a671dc UIApplicationMain + 1064
16  MobileLines               0x00002090 main (main.m:16)
17  MobileLines               0x0000202c start + 44

Now, this is much better. Happy debugging!

Other useful references:

http://www.anoshkin.net/blog/2008/09/09/iphone-crash-logs/
    Posted by 오늘마감

    댓글을 달아 주세요

    아이폰어플개발정보2010. 10. 16. 23:18
    [아이폰 앱 개발] Exploring iPhone Graphics Part 1

    Exploring iPhone Graphics Part 1

    April 9th, 2008 Posted in Software DevelopmentiPhone


    The series of articles is going to discuss creating graphics on the iPhone using the SDK. Part 1 will start out with the basics by drawing some simple 2D graphics. In later articles I plan to get into animation, using the accelerometers and maybe even a little OpenGL.

    The sample application for this article is very simple. It is just a Cocoa Touch application with a single view. The view’s drawRectmethod contains all of the code. Again, this first article is very introductory.

    All drawing is done directly onto a graphics context. For this example we are drawing on the current UI graphics context. You can also create your own graphics context and draw into it but this article won’t be getting into that process.

    The first step is to get the graphics context and clear it. Technically since we are only really rendering this scene once and nothing is changing I don’t need to clear it but I will anyway.

    
    1
    2
    3
    4
    5
    
    
    - (void)drawRect:(CGRect)rect
    {
        // Get the graphics context and clear it
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        CGContextClearRect(ctx, rect);
    

    The UIGraphicsGetCurrentContext function returns the current graphics context for thisUIView object. The CGContextRef value returned from this call is only valid until the drawRect method returns. You should not try to save the value in a variable and use it later.

    CGContextClearRect does just what it says. It clears a rectangle. It actually makes the entire rectangle black. I haven’t found a way to clear to a different color.

    The first object we are going to draw is a solid square colored red.

    
    1
    2
    3
    
    
        // Draw a red solid square
        CGContextSetRGBFillColor(ctx, 255, 0, 0, 1);
        CGContextFillRect(ctx, CGRectMake(10, 10, 50, 50));
    

    The CGContextSetRGBFillColor function is used to set the current fill color for the context. You give it values for red, green, blue and alpha. There are other ways to set the fill color but this is the easiest I’ve found. We’ll talk about the alpha value later.

    You pass CGContextFillRect a rectangle created with CGRectMake and it will draw your rectangle using the current fill color. Nothing to it. Very simple stuff.

    This code creates a solid circle colored green.

    
    1
    2
    3
    
    
        // Draw a green solid circle
        CGContextSetRGBFillColor(ctx, 0, 255, 0, 1);
        CGContextFillEllipseInRect(ctx, CGRectMake(100, 100, 25, 25));
    

    Again we use CGContextSetRBGFillColor to set the current fill color.CGContextFillEllipseInRect is passed a rectangle and will draw an ellipse within the bounds of the rectangle. We are giving it a square so our ellipse will end up being a circle.

    The following code will draw the outline of a circle in blue:

    
    1
    2
    3
    
    
        // Draw a blue hollow circle
        CGContextSetRGBStrokeColor(ctx, 0, 0, 255, 1);
        CGContextStrokeEllipseInRect(ctx, CGRectMake(200, 200, 50, 50));
    

    This time we use CGContextSetRGBStrokeColor instead of CGContextSetRGBFillColor. The current stroke color is the color used to draw stroke type graphics.CGContextStrokeEllipseInRect is just like CGContextFillEllipseInRect except it draws a hollow circle instead of a filled circle.

    This code draws the outline of square in yellow.

    
    1
    2
    3
    
    
        // Draw a yellow hollow rectangle
        CGContextSetRGBStrokeColor(ctx, 255, 255, 0, 1);
        CGContextStrokeRect(ctx, CGRectMake(195, 195, 60, 60));
    

    This code constructs a triangle from a set of three lines.

    
    1
    2
    3
    4
    5
    6
    
    
        // Draw a purple triangle with using lines
        CGContextSetRGBStrokeColor(ctx, 255, 0, 255, 1);
        CGPoint points[6] = { CGPointMake(100, 200), CGPointMake(150, 250),
                              CGPointMake(150, 250), CGPointMake(50, 250),
                              CGPointMake(50, 250), CGPointMake(100, 200) };
        CGContextStrokeLineSegments(ctx, points, 6);
    

    The CGContextStrokeLineSegments function is passed an array of points and will draw lines between each pair of points.

    The following code will draw the text string “TrailsintheSand.com”:

    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    
        // Draw the text TrailsintheSand.com in light blue
        char* text = "TrailsintheSand.com";
        CGContextSelectFont(ctx, "Helvetica", 24.0, kCGEncodingMacRoman);
        CGContextSetTextDrawingMode(ctx, kCGTextFill);
        CGContextSetRGBFillColor(ctx, 0, 255, 255, 1);
     
        CGAffineTransform xform = CGAffineTransformMake(
                1.0,  0.0,
                0.0, -1.0,
                0.0,  0.0);
        CGContextSetTextMatrix(ctx, xform);
     
        CGContextShowTextAtPoint(ctx, 10, 300, text, strlen(text));
    

    When drawing text we need a font. In this case we are selecting a 24 point Helvetica font into the current context with the CGContextSelectFont function.

    CGContextSetTextDrawingMode is used to set how the text will be drawn. In this case it is set to kCGTextFill which means the text will be filled with the current fill color. You can draw the text outlined using kCGTextStroke or both filled and outlined with kCGTextFillStroke.

    Since we are using kCGTextFill we need to set a fill color.

    CGContextShowTextAtPoint draws the text at the specified x,y coordinate within the view. Notice that it takes a C style null terminated string.

    You’ll notice that I skipped the call to CGAffineTransformMake function call. If you run this code without it your text will be drawn up-side down but in the correct position on the screen. The CGAffineTransformMake function creates a transformation matrix and theCGContextSetTextMatrix will cause this matrix to be applied to all text drawn on the context. The matrix above will cause to text to be flipped so it is right-side up. It seems strange to me that the default would be upside down. Apparently fonts are stored with a different coordinate system. You can also use transformation matricies to do things like rotate and scale your text.

    Now we are going to draw a filled gray transparent rectangle over some of the other graphics that we’ve already drawn.

    
    1
    2
    3
    
    
        // Draw a transparent filled circle over other objects
        CGContextSetRGBFillColor(ctx, 200, 200, 200, 0.5);
        CGContextFillEllipseInRect(ctx, CGRectMake(100, 200, 150, 150));
    

    The last parameter passed to CGContextSetRGBFillColor is the alpha value and controls the transparency of what is drawn. This value ranges from 0 to 1 with 0 being completely transparent and 1 being completely opaque.

    The last graphics example is drawing an image.

    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    
        // Load image from applicaiton bundle
        NSString* imageFileName = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"iphone_sdk.png"];
        CGDataProviderRef provider = CGDataProviderCreateWithFilename([imageFileName UTF8String]);
        CGImageRef image = CGImageCreateWithPNGDataProvider(provider, NULL, true, kCGRenderingIntentDefault);
        CGDataProviderRelease(provider);
     
        // Draw image
        CGContextDrawImage(ctx, CGRectMake(200, 0, 100, 100), image);
        CGImageRelease(image);
    }
    

    The image we are going to draw has been added as a resouce to the project (just like a source file) and will be compiled into the application bundle. We get a reference to the bundle by calling the mainBundle: method of NSBundle. Then we get the resourcePath by calling the resourcePath: method on the mainBundle. This returns an NSString object that we call stringByAppendingPathComponent: on passing it the file name to get the full path to the file name.

    Now that we have the file name we create a CGDataProviderRef object using theCGDataProviderCreateWithFilename function.

    Now we can get the actual CGImageRef object that we’ll use to draw the image. TheCGImageCreateWithPNGDataProvider function takes our provider and turns it into an image.

    We are done with the provider so we call CGDataProviderRelease to release its memory and resources.

    Finally we can call the CGContextDrawImage function to actually draw the image on the graphics context. We pass it a rectangle that the image will be drawn into. The image will be automatically resized to fit the rectangle with no regard to the image aspect ratio.

    Lastly we use CGImageRelease to free up the image.

    The only problem with this code is that the image will be drawn up-side down. I assume this is the same reason fonts default to being drawn up-side down. I’ll talk about how to correct this in the next article.

    Screenshot:
    The next article in this series will go into more detail on drawing images.


    http://trailsinthesand.com/exploring-iphone-graphics-part-1

    Posted by 오늘마감

    댓글을 달아 주세요

    아이폰어플개발정보2010. 10. 16. 20:16
    [아이폰 앱 개발] OpenGL ES for iPhone : Part 3 with Accelerometer control

    OpenGL ES for iPhone : Part 3 with Accelerometer control

    In this part 3, we will add the accelerometer control to move the position of ellipse object that we have created in part 2 of the Tutorial.



    1) UIAccelerometerDelegate
    We need to add the UIAccelerometerDelegate protocol to the EAGLView and implement the accelerometer: didAccelerate: method as below


    @interface EAGLView : UIView <UIAccelerometerDelegate>

    - (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration


    We need to configure and start the accelerometer in the setupView method

    [[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / kAccelerometerFrequency)];
    [[UIAccelerometer sharedAccelerometer] setDelegate:self];


    2) Accelerometer values
    Inside the accelerometer: didAccelerate: method, we add a low-pass filter in the accelerometer values. This low-pass filter codes are sourced from the GLGravity Sample Code from Apple.

    //Use a basic low-pass filter in the accelerometer values
    accel[0] = acceleration.x * kFilteringFactor + accel[0] * (1.0 - kFilteringFactor);
    accel[1] = acceleration.y * kFilteringFactor + accel[1] * (1.0 - kFilteringFactor);
    accel[2] = acceleration.z * kFilteringFactor + accel[2] * (1.0 - kFilteringFactor);


    The meaning of accelerometer values:

    acceleration.x = Roll. It corresponds to roll, or rotation around the axis that runs from your home button to your earpiece. Values vary from 1.0 (rolled all the way to the right) to -1.0 (rolled all the way to the left).

    acceleration.y = Pitch. Place your iPhone on the table and mentally draw a horizontal line about half-way down the screen. That's the axis around which the Y value rotates. Values go from 1.0 (the headphone jack straight down) to -1.0 (the headphone jack straight up).

    acceleration.z = Face up/face down. It refers to whether your iPhone is face up (-1.0) or face down (1.0). When placed on it side, either the side with the volume controls and ringer switch, or the side directly opposite, the Z value equates to 0.0.

    3) Control on movement of the ellipse is using the variables moveX and moveY and the ellipse position will be changed according to acceleration.x (that is accel[0]) and acceleration.y (that is accel[1]) values that passed from the Accelerometer control after the low-pass filter. The larger the absolute value of acceleration.x/acceleration.y, the greater for the magnitude for the value of moveX/moveY and thus the faster the ellipse will change its position to that direction. As the object should not move beyond the screen view, the ellipseData.pos.x and ellipseData.pos.y values will be governed by the boundaries of the screen.

     ellipseData.pos.x += moveX;
     if (accel[0] > -0.1 & accel[0] < 0.1 ) {
       moveX = 0.0f;
     }
     else {
      moveX = 10.0f * accel[0];
     }

     ellipseData.pos.y += moveY;
     if (accel[1] > -0.1 & accel[1] < 0.1 ) {
       moveY = 0.0f;
     }
     else {
       moveY = -10.0f * accel[1];
     }


    4) Conditional compilation code for the iPhone Simulator and on-screen debug info
    As iPhone Simulator does not have Accelerometer control, we have added the code that will change the ellipse position inside this compiler directive, so that the ellipse will keep moving on the iPhone Simulator.
      #if TARGET_IPHONE_SIMULATOR 

    Moroever, we have added a UILabel to the code so that we can read the Accelerometer values while we debug the program on actual device. This UILabel can be disabled using this define directive.
      #undef DEBUGSCREEN

    5) The source codes are here, you just need to create a new project from OpenGL ES Application template of XCode and copy the source codes of EAGLView.h and EAGLView.m from below and paste them for Build & Go in XCode. The accelerometer control can only be tested on actual device.



    EAGLView.h Select all

    // EAGLView.h
    // OpenGL ES Tutorial - Part 3 by javacom


    // To enable Debug NSLog, add GCC_PREPROCESSOR_DEFINITIONS DEBUGON in Project Settings for Debug Build Only and replace NSLog() with DEBUGLOG()
    #ifdef DEBUGON
    #define DEBUGLOG if (DEBUGON) NSLog
    #else
    #define DEBUGLOG
    #endif

    #define DEBUGSCREEN

    #import <UIKit/UIKit.h>
    #import <OpenGLES/EAGL.h>
    #import <OpenGLES/ES1/gl.h>
    #import <OpenGLES/ES1/glext.h>

    typedef struct
    {
    BOOL rotstop; // stop self rotation
    BOOL touchInside; // finger tap inside of the object ?
    BOOL scalestart; // start to scale the obejct ?
    CGPoint pos; // position of the object on the screen
    CGPoint startTouchPosition; // Start Touch Position
    CGPoint currentTouchPosition; // Current Touch Position
    GLfloat pinchDistance; // distance between two fingers pinch
    GLfloat pinchDistanceShown; // distance that have shown on screen
    GLfloat scale; // OpenGL scale factor of the object
    GLfloat rotation; // OpenGL rotation factor of the object
    GLfloat rotspeed; // control rotation speed of the object
    } ObjectData;

    /*
    This class wraps the CAEAGLLayer from CoreAnimation into a convenient UIView subclass.
    The view content is basically an EAGL surface you render your OpenGL scene into.
    Note that setting the view non-opaque will only work if the EAGL surface has an alpha channel.
    */
    @interface EAGLView : UIView {

    @private
    /* The pixel dimensions of the backbuffer */
    GLint backingWidth;
    GLint backingHeight;

    EAGLContext *context;

    /* OpenGL names for the renderbuffer and framebuffers used to render to this view */
    GLuint viewRenderbuffer, viewFramebuffer;

    /* OpenGL name for the depth buffer that is attached to viewFramebuffer, if it exists (0 if it does not exist) */
    GLuint depthRenderbuffer;

    NSTimer *animationTimer;
    NSTimeInterval animationInterval;

    @public
    ObjectData squareData;
    ObjectData ellipseData;
    GLfloat ellipseVertices[720];
    CGFloat initialDistance;
    UIAccelerationValue accel[3];
    GLfloat moveX, moveY;
    #ifdef DEBUGSCREEN
    UILabel *textView;
    #endif
    }

    @property NSTimeInterval animationInterval;

    @property (nonatomic) ObjectData squareData;
    @property (nonatomic) ObjectData ellipseData;
    @property CGFloat initialDistance;
    #ifdef DEBUGSCREEN
    @property (nonatomic, assign) UILabel *textView;
    #endif

    - (void)startAnimation;
    - (void)stopAnimation;
    - (void)drawView;
    - (void)setupView;

    @end


    EAGLView.m Select all

    // EAGLView.m
    // OpenGL ES Tutorial - Part 3 by javacom
    //
    #import <QuartzCore/QuartzCore.h>
    #import <OpenGLES/EAGLDrawable.h>

    #import "EAGLView.h"

    #include <math.h>

    // Macros
    #define degreesToRadians(__ANGLE__) (M_PI * (__ANGLE__) / 180.0)
    #define radiansToDegrees(__ANGLE__) (180.0 * (__ANGLE__) / M_PI)

    CGFloat distanceBetweenPoints (CGPoint first, CGPoint second) {
    CGFloat deltaX = second.x - first.x;
    CGFloat deltaY = second.y - first.y;
    return sqrt(deltaX*deltaX + deltaY*deltaY );
    };

    CGFloat angleBetweenPoints(CGPoint first, CGPoint second) {
    // atan((top - bottom)/(right - left))
    CGFloat rads = atan((second.y - first.y) / (first.x - second.x));
    return radiansToDegrees(rads);
    }

    CGFloat angleBetweenLines(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) {

    CGFloat a = line1End.x - line1Start.x;
    CGFloat b = line1End.y - line1Start.y;
    CGFloat c = line2End.x - line2Start.x;
    CGFloat d = line2End.y - line2Start.y;

    CGFloat rads = acos(((a*c) + (b*d)) / ((sqrt(a*a + b*b)) * (sqrt(c*c + d*d))));

    return radiansToDegrees(rads);
    }

    #define USE_DEPTH_BUFFER 0

    // CONSTANTS
    #define kMinimumTouchLength 30
    #define kMaximumScale 7.0f
    #define kMinimumPinchDelta 15
    #define kAccelerometerFrequency 100.0 // Hz
    #define kFilteringFactor 0.1


    // A class extension to declare private methods
    @interface EAGLView ()

    @property (nonatomic, retain) EAGLContext *context;
    @property (nonatomic, assign) NSTimer *animationTimer;

    - (BOOL) createFramebuffer;
    - (void) destroyFramebuffer;

    @end


    @implementation EAGLView

    @synthesize context;
    @synthesize animationTimer;
    @synthesize animationInterval;
    @synthesize squareData;
    @synthesize ellipseData;
    @synthesize initialDistance;
    #ifdef DEBUGSCREEN
    @synthesize textView;
    #endif

    // You must implement this method
    + (Class)layerClass {
    return [CAEAGLLayer class];
    }


    //The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
    - (id)initWithCoder:(NSCoder*)coder {

    if ((self = [super initWithCoder:coder])) {

    // Get the layer
    CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;

    eaglLayer.opaque = YES;
    eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
    [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];

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

    if (!context || ![EAGLContext setCurrentContext:context]) {
    [self release];
    return nil;
    }

    animationInterval = 1.0 / 60.0;
    [self setupView];
    }
    return self;
    }

    // These are four methods touchesBegan, touchesMoved, touchesEnded, touchesCancelled and use to notify about touches and gestures

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /*
    NSUInteger numTaps = [[touches anyObject] tapCount]; // number of taps
    NSUInteger numTouches = [touches count]; // number of touches
    */
    UITouch *touch = [[touches allObjects] objectAtIndex:0];

    DEBUGLOG(@"TouchBegan event counts = %d ",[[event touchesForView:self] count]);
    DEBUGLOG(@"TouchBegan tounches counts = %d ",[touches count]);
    if ([touches count]== 2) {
    NSArray *twoTouches = [touches allObjects];
    UITouch *first = [twoTouches objectAtIndex:0];
    UITouch *second = [twoTouches objectAtIndex:1];
    initialDistance = distanceBetweenPoints([first locationInView:self], [second locationInView:self]);
    squareData.rotstop = YES;
    squareData.touchInside = NO;
    }
    else if ([touches count]==[[event touchesForView:self] count] & [[event touchesForView:self] count] == 1) {
    squareData.startTouchPosition = [touch locationInView:self];
    if (distanceBetweenPoints([touch locationInView:self], squareData.pos) <= kMinimumTouchLength * squareData.scale) {
    DEBUGLOG(@"Square Touch at %.2f, %.2f ",squareData.pos.x,squareData.pos.y);
    squareData.rotstop = YES;
    squareData.touchInside = YES;
    }
    }

    }

    - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [[touches allObjects] objectAtIndex:0];
    squareData.currentTouchPosition = [touch locationInView:self];
    if ([touches count]== 2) {
    NSArray *twoTouches = [touches allObjects];
    UITouch *first = [twoTouches objectAtIndex:0];
    UITouch *second = [twoTouches objectAtIndex:1];

    // Calculate the distance bewtween the two fingers(touches) to determine the pinch distance
    CGFloat currentDistance = distanceBetweenPoints([first locationInView:self], [second locationInView:self]);

    squareData.rotstop = YES;
    squareData.touchInside = NO;

    if (initialDistance == 0.0f)
    initialDistance = currentDistance;
    if (currentDistance - initialDistance > kMinimumPinchDelta) {
    squareData.pinchDistance = currentDistance - initialDistance;
    squareData.scalestart = YES;
    DEBUGLOG(@"Outward Pinch %.2f", squareData.pinchDistance);
    }
    else if (initialDistance - currentDistance > kMinimumPinchDelta) {
    squareData.pinchDistance = currentDistance - initialDistance;
    squareData.scalestart = YES;
    DEBUGLOG(@"Inward Pinch %.2f", squareData.pinchDistance);
    }
    }
    else if ([touches count]==[[event touchesForView:self] count] & [[event touchesForView:self] count] == 1) {
    if (squareData.touchInside) {
    // Only move the square to new position when touchBegan is inside the square
    squareData.pos.x = [touch locationInView:self].x;
    squareData.pos.y = [touch locationInView:self].y;
    DEBUGLOG(@"Square Move to %.2f, %.2f ",squareData.pos.x,squareData.pos.y);
    squareData.rotstop = YES;
    }
    }
    }


    - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    if ([touches count] == [[event touchesForView:self] count]) {
    initialDistance = squareData.pinchDistanceShown = squareData.pinchDistance = 0.0f;
    squareData.rotstop = squareData.touchInside = squareData.scalestart = NO;
    DEBUGLOG(@"touchesEnded, all fingers up");
    }
    else {
    initialDistance = squareData.pinchDistanceShown = squareData.pinchDistance = 0.0f;
    squareData.scalestart = NO;
    DEBUGLOG(@"touchesEnded");
    }
    }


    - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    initialDistance = squareData.pinchDistanceShown = squareData.pinchDistance = 0.0f;
    squareData.rotstop = squareData.touchInside = squareData.scalestart = NO;
    DEBUGLOG(@"touchesCancelled");
    }

    - (void)setupView { // new method for intialisation of variables and states

    // Enable Multi Touch of the view
    self.multipleTouchEnabled = YES;

    //Configure and start accelerometer
    [[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / kAccelerometerFrequency)];
    [[UIAccelerometer sharedAccelerometer] setDelegate:self];
    #if TARGET_IPHONE_SIMULATOR
    moveX = 2.0f;
    moveY = 3.0f;
    #else
    moveX = 0.0f;
    moveY = 0.0f;
    #endif

    #ifdef DEBUGSCREEN
    UIColor *bgColor = [[UIColor alloc] initWithWhite:1.0f alpha:0.0f];
    textView = [[UILabel alloc] initWithFrame:CGRectMake(10.0f, 350.0f, 300.0f, 96.0f)];
    textView.text = [NSString stringWithFormat:@"-Accelerometer Data-"];
    textView.textAlignment = UITextAlignmentLeft;
    [textView setNumberOfLines:4];
    textView.backgroundColor = bgColor;
    textView.font = [UIFont fontWithName:@"Arial" size:18];
    [self addSubview:textView];
    [self bringSubviewToFront:textView];
    #endif


    // Initialise square data
    squareData.rotation = squareData.pinchDistance = squareData.pinchDistanceShown = 0.0f;
    ellipseData.rotation = 0.0f;
    squareData.scale = 1.0f;
    squareData.rotstop = squareData.touchInside = squareData.scalestart = NO;
    squareData.pos.x = 160.0f;
    squareData.pos.y = 240.0f;
    squareData.pinchDistance = 0.0f;
    squareData.rotspeed = 1.0f;

    // Initialise ellipse data
    ellipseData.rotation = 0.0f;
    ellipseData.rotstop = ellipseData.touchInside = ellipseData.scalestart = NO;
    ellipseData.pos.x = 160.0f;
    ellipseData.pos.y = 100.0f;
    ellipseData.rotspeed = -4.0f;

    // calculate the vertices of ellipse
    const GLfloat xradius = 35.0f;
    const GLfloat yradius = 25.0f;
    for (int i = 0; i < 720; i+=2) {
    ellipseVertices[i] = (cos(degreesToRadians(i)) * xradius) + 0.0f;
    ellipseVertices[i+1] = (sin(degreesToRadians(i)) * yradius) + 0.0f;
    // DEBUGLOG(@"ellipseVertices[v%d] %.1f, %.1f",i, ellipseVertices[i], ellipseVertices[i+1]);
    }

    // setup the projection matrix
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    // Setup Orthographic Projection for the 320 x 480 of the iPhone screen
    glOrthof(0.0f, 320.0f, 480.0f, 0.0f, -1.0f, 1.0f);
    glMatrixMode(GL_MODELVIEW);

    }

    - (void)drawView {

    // Define the square vertices
    const GLfloat squareVertices[] = {
    -20.0f, -20.0f,
    20.0f, -20.0f,
    -20.0f, 20.0f,
    20.0f, 20.0f,
    };

    // Define the colors of the square vertices
    const GLubyte squareColors[] = {
    255, 255, 0, 255,
    0, 255, 255, 255,
    0, 0, 0, 0,
    255, 0, 255, 255,
    };


    // Define the colors of the ellipse vertices
    const GLubyte ellipseColors[] = {
    233, 85, 85, 255,
    233, 85, 85, 255,
    233, 85, 85, 255,
    233, 85, 85, 255,
    233, 85, 85, 255,
    };


    [EAGLContext setCurrentContext:context];
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
    glViewport(0, 0, backingWidth, backingHeight);

    // Clear background color
    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    // draw the square
    glLoadIdentity();
    glTranslatef(squareData.pos.x, squareData.pos.y, 0.0f);
    glRotatef(squareData.rotation, 0.0f, 0.0f, 1.0f);
    glScalef(squareData.scale, squareData.scale, 1.0f);
    glVertexPointer(2, GL_FLOAT, 0, squareVertices);
    glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors);
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    // draw the ellipse
    glLoadIdentity();
    glTranslatef(ellipseData.pos.x, ellipseData.pos.y, 0.0f);
    glRotatef(ellipseData.rotation, 0.0f, 0.0f, 1.0f);
    glVertexPointer(2, GL_FLOAT, 0, ellipseVertices);
    glColorPointer(4, GL_UNSIGNED_BYTE, 0, ellipseColors);
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 360); // the ellipse has 360 vertices

    // control the square rotation
    if (!squareData.rotstop) {
    squareData.rotation += squareData.rotspeed;
    if(squareData.rotation > 360.0f)
    squareData.rotation -= 360.0f;
    else if(squareData.rotation < -360.0f)
    squareData.rotation += 360.0f;
    }

    // control the ellipse rotation
    if (!ellipseData.rotstop) {
    ellipseData.rotation += ellipseData.rotspeed;
    if(ellipseData.rotation > 360.0f)
    ellipseData.rotation -= 360.0f;
    else if(ellipseData.rotation < -360.0f)
    ellipseData.rotation += 360.0f;
    }

    // control the square scaling
    if (squareData.scalestart && squareData.scale <= kMaximumScale) {
    GLfloat pinchDelta = squareData.pinchDistance - squareData.pinchDistanceShown;
    if (squareData.pinchDistance != 0.0f) {
    squareData.scale += pinchDelta/30;
    squareData.pinchDistanceShown = squareData.pinchDistance;
    if (squareData.scale >= kMaximumScale) {
    squareData.scale = kMaximumScale;
    squareData.pinchDistanceShown = squareData.pinchDistance = 0.0f;
    squareData.scalestart = NO;
    } else if (squareData.scale <= 1.0f) {
    squareData.scale = 1.0f;
    squareData.pinchDistanceShown = squareData.pinchDistance = 0.0f;
    squareData.scalestart = NO;
    }
    DEBUGLOG(@"scale is %.2f",squareData.scale);
    }
    }

    // control the ellipse movement
    #if TARGET_IPHONE_SIMULATOR
    ellipseData.pos.x += moveX;
    if (ellipseData.pos.x >= 290.f) {
    moveX = -2.0f;
    }
    else if (ellipseData.pos.x <= 30.f) {
    moveX = 2.0f;
    }

    ellipseData.pos.y += moveY;
    if (ellipseData.pos.y >= 450.f) {
    moveY = -1.5f;
    }
    else if (ellipseData.pos.y <= 55.f) {
    moveY = 3.5f;
    }
    #else
    ellipseData.pos.x += moveX;
    if (accel[0] > -0.1 & accel[0] < 0.1 ) {
    moveX = 0.0f;
    }
    else {
    moveX = 10.0f * accel[0];
    }

    ellipseData.pos.y += moveY;
    if (accel[1] > -0.1 & accel[1] < 0.1 ) {
    moveY = 0.0f;
    }
    else {
    moveY = -10.0f * accel[1];
    }
    #endif
    if (ellipseData.pos.x >= 290.f) {
    ellipseData.pos.x = 290.0f;
    }
    else if (ellipseData.pos.x <= 30.f) {
    ellipseData.pos.x = 30.0f;
    }
    if (ellipseData.pos.y >= 450.f) {
    ellipseData.pos.y = 450.0f;
    }
    else if (ellipseData.pos.y <= 55.f) {
    ellipseData.pos.y = 55.0f;
    }


    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
    }

    - (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration
    {
    /*
    The meaning of acceleration values for firmware 2.x
    acceleration.x = Roll. It corresponds to roll, or rotation around the axis that runs from your home button to your earpiece.
    Values vary from 1.0 (rolled all the way to the right) to -1.0 (rolled all the way to the left).

    acceleration.y = Pitch. Place your iPhone on the table and mentally draw a horizontal line about half-way down the screen.
    That's the axis around which the Y value rotates.
    Values go from 1.0 (the headphone jack straight down) to -1.0 (the headphone jack straight up).

    acceleration.z = Face up/face down.
    It refers to whether your iPhone is face up (-1.0) or face down (1.0).
    When placed on it side, either the side with the volume controls and ringer switch, or the side directly opposite
    , the Z value equates to 0.0.
    */

    //Use a basic low-pass filter in the accelerometer values
    accel[0] = acceleration.x * kFilteringFactor + accel[0] * (1.0 - kFilteringFactor);
    accel[1] = acceleration.y * kFilteringFactor + accel[1] * (1.0 - kFilteringFactor);
    accel[2] = acceleration.z * kFilteringFactor + accel[2] * (1.0 - kFilteringFactor);

    #ifdef DEBUGSCREEN
    textView.text = [NSString stringWithFormat:
    @"X (roll, %4.1f%%): %f\nY (pitch %4.1f%%): %f\nZ (%4.1f%%) : %f",
    100.0 - (accel[0] + 1.0) * 50.0, accel[0],
    100.0 - (accel[1] + 1.0) * 50.0, accel[1],
    100.0 - (accel[2] + 1.0) * 50.0, accel[2]
    ];
    #endif
    }

    - (void)layoutSubviews {
    [EAGLContext setCurrentContext:context];
    [self destroyFramebuffer];
    [self createFramebuffer];
    [self drawView];
    }


    - (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) {
    DEBUGLOG(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
    return NO;
    }

    return YES;
    }


    - (void)destroyFramebuffer {

    glDeleteFramebuffersOES(1, &viewFramebuffer);
    viewFramebuffer = 0;
    glDeleteRenderbuffersOES(1, &viewRenderbuffer);
    viewRenderbuffer = 0;

    if(depthRenderbuffer) {
    glDeleteRenderbuffersOES(1, &depthRenderbuffer);
    depthRenderbuffer = 0;
    }
    }


    - (void)startAnimation {
    self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];
    }


    - (void)stopAnimation {
    self.animationTimer = nil;
    }


    - (void)setAnimationTimer:(NSTimer *)newTimer {
    [animationTimer invalidate];
    animationTimer = newTimer;
    }


    - (void)setAnimationInterval:(NSTimeInterval)interval {

    animationInterval = interval;
    if (animationTimer) {
    [self stopAnimation];
    [self startAnimation];
    }
    }


    - (void)dealloc {

    [self stopAnimation];

    if ([EAGLContext currentContext] == context) {
    [EAGLContext setCurrentContext:nil];
    }

    [context release];
    [super dealloc];
    }

    @end

    .http://iphonesdkdev.blogspot.com/2009/04/opengl-es-for-iphone-part-3-with.html
    .
    Posted by 오늘마감

    댓글을 달아 주세요

    아이폰어플개발정보2010. 10. 15. 06:10
    [아이폰 앱 개발] iPad for iPhone Developers 101: UIPopoverController Tutorial

    iPad for iPhone Developers 101: UIPopoverController Tutorial

    http://www.raywenderlich.com/1056/ipad-for-iphone-developers-101-uipopovercontroller-tutorial

    This is the second part of a three part series to help get iPhone Developers up-to-speed with iPad development by focusing on three of the most interesting new classes (at least to me): UISplitView, UIPopoverController, and Custom Input Views.

    In the first part of the series, we made an app with a split view that displays a list of monsters on the left side, and details on the selected monster on the right side.

    In this part, we’re going to try out popovers view with a simple example: we’ll add a popover to let the user select from a list of colors to change the color of the monster’s name.

    We’ll start out with where we left off the project last time, so grab a copy if you don’t have it already.

    Creating our Color Picker

    Let’s start by creating the view that we’ll use to let the user pick between a list of colors. We’ll make this a simple table view with a list of color names.

    So go to “File\New File…”, pick “UIViewController subclass”, and make sure “Targeted for iPad” and “UITableViewController subclass” are checked but “With XIB for user interface” is NOT checked, and click Next. Name the class ColorPickerController, and click Finish.

    Then replace ColorPickerController.h with the following:

    
    @protocol ColorPickerDelegate
    - (void)colorSelected:(NSString *)color;
    @end
     
     
    @interface ColorPickerController : UITableViewController {
        NSMutableArray *_colors;
        id<ColorPickerDelegate> _delegate;
    }
     
    @property (nonatomic, retain) NSMutableArray *colors;
    @property (nonatomic, assign) id<ColorPickerDelegate> delegate;
     
    @end
    

    Here we declare a delegate so that this class can notify another class when a user selects a color. We then declare two variables/properties: one for the list of colors to display, and one to store the delegate itself.

    Then make the following changes to ColorPickerController.m:

    
    // Under @implementation
    @synthesize colors = _colors;
    @synthesize delegate = _delegate;
     
    // Add viewDidLoad like the following:
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.clearsSelectionOnViewWillAppear = NO;
        self.contentSizeForViewInPopover = CGSizeMake(150.0, 140.0);
        self.colors = [NSMutableArray array];
        [_colors addObject:@"Red"];
        [_colors addObject:@"Green"];
        [_colors addObject:@"Blue"];
    }
     
    // in numberOfSectionsInTableView:
    return 1;
     
    // in numberOfRowsInSection:
    return [_colors count];
     
    // In cellForRowAtIndexPath, under configure the cell:
    NSString *color = [_colors objectAtIndex:indexPath.row];
    cell.textLabel.text = color;
     
    // In didSelectRowAtIndexPath:
    if (_delegate != nil) {
        NSString *color = [_colors objectAtIndex:indexPath.row];
        [_delegate colorSelected:color];
    }
     
    // In dealloc
    self.colors = nil;
    self.delegate = nil;
    

    Most of this should be normal table view stuff except for the following line:

    
    self.contentSizeForViewInPopover = CGSizeMake(150.0, 140.0);
    

    This line sets the size of how large the popover should be when it is displayed. If you do not add this line, by default the popover will be the entire height of the screen (which is usually too large).

    Displaying the Picker

    Believe it or not, that was the hardest part. Now to display the picker, all we need to do is add a button to our toolbar, and a little bit of code to display it and handle the selection.

    So first, let’s add the button. Open up RightViewController.xib and add a Bar Button Item to the toolbar. Set the title of the button “Set Color”.

    Now let’s declare a method for the button to trigger in RightViewController.h and declare a few variables we’ll need in a minute:

    
    // Up top, under #import
    #import "ColorPickerController.h"
     
    // Modfiy class declaration
    @interface RightViewController : UIViewController <MonsterSelectionDelegate,  
        UISplitViewControllerDelegate, ColorPickerDelegate> {
     
    // Inside class
    ColorPickerController *_colorPicker;
    UIPopoverController *_colorPickerPopover;
     
    // In property section
    @property (nonatomic, retain) ColorPickerController *colorPicker;
    @property (nonatomic, retain) UIPopoverController *colorPickerPopover;
     
    - (IBAction)setColorButtonTapped:(id)sender;
    

    Before we forget, go ahead and connect the action method to the Bar Button Item in Interface Builder by control-dragging from the Bar Button Item to File’s Owner and connecting to the “setColorButtonTapped” outlet.

    Then let’s finish by making the required changes to RightViewController.m:

    
    // In synthesize section
    @synthesize colorPicker = _colorPicker;
    @synthesize colorPickerPopover = _colorPickerPopover;
     
    // In dealloc
    self.colorPicker = nil;
    self.colorPickerPopover = nil;
     
    // Add to end of file
    - (void)colorSelected:(NSString *)color {
        if ([color compare:@"Red"] == NSOrderedSame) {
            _nameLabel.textColor = [UIColor redColor];
        } else if ([color compare:@"Green"] == NSOrderedSame) {
            _nameLabel.textColor = [UIColor greenColor];
        } else if ([color compare:@"Blue"] == NSOrderedSame){
            _nameLabel.textColor = [UIColor blueColor];
        }
        [self.colorPickerPopover dismissPopoverAnimated:YES];
    }
     
    - (IBAction)setColorButtonTapped:(id)sender {
        if (_colorPicker == nil) {
            self.colorPicker = [[[ColorPickerController alloc] 
                initWithStyle:UITableViewStylePlain] autorelease];
            _colorPicker.delegate = self;
            self.colorPickerPopover = [[[UIPopoverController alloc] 
                initWithContentViewController:_colorPicker] autorelease];               
        }
        [self.colorPickerPopover presentPopoverFromBarButtonItem:sender 
            permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
    }
    

    Ok let’s explain this a bit. All popovers are is a “wrapper” around an existing view controller that “floats” it in a certain spot and possibly displays an arrow showing what the popover is related to. You can see this in setColorButtonTapped – we create our color picker, and then wrap it with a popover controller.

    Then we call a method on the popover controller to display it in the view. We use the helper function presentPopoverFromBarButtonItem to display the popover.

    When the user is done, they can tap anywhere outside the popover to dismiss it automatically. However if they select a color, we also want it to be dismissed, so we call the dismissPopoverAnimated method to get rid of the popover on-demand (as well as setting the color appropriately).

    And that’s it! Compile and run and when you tap the “Set Color” bar button item, you should see a popover like the following that changes the label color:

    You will find yourself using popovers quite a bit in places where users need to edit a field or toggle a setting, rather than the iPhone style where you navigate to the next level in a UINavigationController. They call this “flattening the hierarchy” in the iPad docs.

    Show Me the Code!

    Here’s a copy of all of the code we’ve developed so far.

    Check out the next part of the series, where we cover how to use custom input views on the iPad!

    Posted by 오늘마감

    댓글을 달아 주세요

    아이폰어플개발정보2010. 10. 12. 05:41
    Exploring iPhone Graphics Part 1

    Exploring iPhone Graphics Part 1

    April 9th, 2008 Posted in Software DevelopmentiPhone


    The series of articles is going to discuss creating graphics on the iPhone using the SDK. Part 1 will start out with the basics by drawing some simple 2D graphics. In later articles I plan to get into animation, using the accelerometers and maybe even a little OpenGL.

    The sample application for this article is very simple. It is just a Cocoa Touch application with a single view. The view’s drawRectmethod contains all of the code. Again, this first article is very introductory.

    All drawing is done directly onto a graphics context. For this example we are drawing on the current UI graphics context. You can also create your own graphics context and draw into it but this article won’t be getting into that process.

    The first step is to get the graphics context and clear it. Technically since we are only really rendering this scene once and nothing is changing I don’t need to clear it but I will anyway.

    
    1 2 3 4 5 
    
    
    - (void)drawRect:(CGRect)rect { // Get the graphics context and clear it CGContextRef ctx = UIGraphicsGetCurrentContext(); CGContextClearRect(ctx, rect);
    

    The UIGraphicsGetCurrentContext function returns the current graphics context for thisUIView object. The CGContextRef value returned from this call is only valid until the drawRect method returns. You should not try to save the value in a variable and use it later.

    CGContextClearRect does just what it says. It clears a rectangle. It actually makes the entire rectangle black. I haven’t found a way to clear to a different color.

    The first object we are going to draw is a solid square colored red.

    
    1 2 3 
    
    
     // Draw a red solid square CGContextSetRGBFillColor(ctx, 255, 0, 0, 1); CGContextFillRect(ctx, CGRectMake(10, 10, 50, 50));
    

    The CGContextSetRGBFillColor function is used to set the current fill color for the context. You give it values for red, green, blue and alpha. There are other ways to set the fill color but this is the easiest I’ve found. We’ll talk about the alpha value later.

    You pass CGContextFillRect a rectangle created with CGRectMake and it will draw your rectangle using the current fill color. Nothing to it. Very simple stuff.

    This code creates a solid circle colored green.

    
    1 2 3 
    
    
     // Draw a green solid circle CGContextSetRGBFillColor(ctx, 0, 255, 0, 1); CGContextFillEllipseInRect(ctx, CGRectMake(100, 100, 25, 25));
    

    Again we use CGContextSetRBGFillColor to set the current fill color.CGContextFillEllipseInRect is passed a rectangle and will draw an ellipse within the bounds of the rectangle. We are giving it a square so our ellipse will end up being a circle.

    The following code will draw the outline of a circle in blue:

    
    1 2 3 
    
    
     // Draw a blue hollow circle CGContextSetRGBStrokeColor(ctx, 0, 0, 255, 1); CGContextStrokeEllipseInRect(ctx, CGRectMake(200, 200, 50, 50));
    

    This time we use CGContextSetRGBStrokeColor instead of CGContextSetRGBFillColor. The current stroke color is the color used to draw stroke type graphics.CGContextStrokeEllipseInRect is just like CGContextFillEllipseInRect except it draws a hollow circle instead of a filled circle.

    This code draws the outline of square in yellow.

    
    1 2 3 
    
    
     // Draw a yellow hollow rectangle CGContextSetRGBStrokeColor(ctx, 255, 255, 0, 1); CGContextStrokeRect(ctx, CGRectMake(195, 195, 60, 60));
    

    This code constructs a triangle from a set of three lines.

    
    1 2 3 4 5 6 
    
    
     // Draw a purple triangle with using lines CGContextSetRGBStrokeColor(ctx, 255, 0, 255, 1); CGPoint points[6] = { CGPointMake(100, 200), CGPointMake(150, 250), CGPointMake(150, 250), CGPointMake(50, 250), CGPointMake(50, 250), CGPointMake(100, 200) }; CGContextStrokeLineSegments(ctx, points, 6);
    

    The CGContextStrokeLineSegments function is passed an array of points and will draw lines between each pair of points.

    The following code will draw the text string “TrailsintheSand.com”:

    
    1 2 3 4 5 6 7 8 9 10 11 12 13 
    
    
     // Draw the text TrailsintheSand.com in light blue char* text = "TrailsintheSand.com"; CGContextSelectFont(ctx, "Helvetica", 24.0, kCGEncodingMacRoman); CGContextSetTextDrawingMode(ctx, kCGTextFill); CGContextSetRGBFillColor(ctx, 0, 255, 255, 1);   CGAffineTransform xform = CGAffineTransformMake( 1.0, 0.0, 0.0, -1.0, 0.0, 0.0); CGContextSetTextMatrix(ctx, xform);   CGContextShowTextAtPoint(ctx, 10, 300, text, strlen(text));
    

    When drawing text we need a font. In this case we are selecting a 24 point Helvetica font into the current context with the CGContextSelectFont function.

    CGContextSetTextDrawingMode is used to set how the text will be drawn. In this case it is set to kCGTextFill which means the text will be filled with the current fill color. You can draw the text outlined using kCGTextStroke or both filled and outlined with kCGTextFillStroke.

    Since we are using kCGTextFill we need to set a fill color.

    CGContextShowTextAtPoint draws the text at the specified x,y coordinate within the view. Notice that it takes a C style null terminated string.

    You’ll notice that I skipped the call to CGAffineTransformMake function call. If you run this code without it your text will be drawn up-side down but in the correct position on the screen. The CGAffineTransformMake function creates a transformation matrix and theCGContextSetTextMatrix will cause this matrix to be applied to all text drawn on the context. The matrix above will cause to text to be flipped so it is right-side up. It seems strange to me that the default would be upside down. Apparently fonts are stored with a different coordinate system. You can also use transformation matricies to do things like rotate and scale your text.

    Now we are going to draw a filled gray transparent rectangle over some of the other graphics that we’ve already drawn.

    
    1 2 3 
    
    
     // Draw a transparent filled circle over other objects CGContextSetRGBFillColor(ctx, 200, 200, 200, 0.5); CGContextFillEllipseInRect(ctx, CGRectMake(100, 200, 150, 150));
    

    The last parameter passed to CGContextSetRGBFillColor is the alpha value and controls the transparency of what is drawn. This value ranges from 0 to 1 with 0 being completely transparent and 1 being completely opaque.

    The last graphics example is drawing an image.

    
    1 2 3 4 5 6 7 8 9 10 
    
    
     // Load image from applicaiton bundle NSString* imageFileName = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"iphone_sdk.png"]; CGDataProviderRef provider = CGDataProviderCreateWithFilename([imageFileName UTF8String]); CGImageRef image = CGImageCreateWithPNGDataProvider(provider, NULL, true, kCGRenderingIntentDefault); CGDataProviderRelease(provider);   // Draw image CGContextDrawImage(ctx, CGRectMake(200, 0, 100, 100), image); CGImageRelease(image); }
    

    The image we are going to draw has been added as a resouce to the project (just like a source file) and will be compiled into the application bundle. We get a reference to the bundle by calling the mainBundle: method of NSBundle. Then we get the resourcePath by calling the resourcePath: method on the mainBundle. This returns an NSString object that we call stringByAppendingPathComponent: on passing it the file name to get the full path to the file name.

    Now that we have the file name we create a CGDataProviderRef object using theCGDataProviderCreateWithFilename function.

    Now we can get the actual CGImageRef object that we’ll use to draw the image. TheCGImageCreateWithPNGDataProvider function takes our provider and turns it into an image.

    We are done with the provider so we call CGDataProviderRelease to release its memory and resources.

    Finally we can call the CGContextDrawImage function to actually draw the image on the graphics context. We pass it a rectangle that the image will be drawn into. The image will be automatically resized to fit the rectangle with no regard to the image aspect ratio.

    Lastly we use CGImageRelease to free up the image.

    The only problem with this code is that the image will be drawn up-side down. I assume this is the same reason fonts default to being drawn up-side down. I’ll talk about how to correct this in the next article.

    Screenshot:
    The next article in this series will go into more detail on drawing images.


    http://trailsinthesand.com/exploring-iphone-graphics-part-1

    Posted by 오늘마감

    댓글을 달아 주세요

    아이폰어플개발정보2010. 10. 12. 05:36
    iPad for iPhone Developers 101: UIPopoverController Tutorial

    iPad for iPhone Developers 101: UIPopoverController Tutorial

    http://www.raywenderlich.com/1056/ipad-for-iphone-developers-101-uipopovercontroller-tutorial

    This is the second part of a three part series to help get iPhone Developers up-to-speed with iPad development by focusing on three of the most interesting new classes (at least to me): UISplitView, UIPopoverController, and Custom Input Views.

    In the first part of the series, we made an app with a split view that displays a list of monsters on the left side, and details on the selected monster on the right side.

    In this part, we’re going to try out popovers view with a simple example: we’ll add a popover to let the user select from a list of colors to change the color of the monster’s name.

    We’ll start out with where we left off the project last time, so grab a copy if you don’t have it already.

    Creating our Color Picker

    Let’s start by creating the view that we’ll use to let the user pick between a list of colors. We’ll make this a simple table view with a list of color names.

    So go to “File\New File…”, pick “UIViewController subclass”, and make sure “Targeted for iPad” and “UITableViewController subclass” are checked but “With XIB for user interface” is NOT checked, and click Next. Name the class ColorPickerController, and click Finish.

    Then replace ColorPickerController.h with the following:

    
    @protocol ColorPickerDelegate - (void)colorSelected:(NSString *)color; @end     @interface ColorPickerController : UITableViewController { NSMutableArray *_colors; id<ColorPickerDelegate> _delegate; }   @property (nonatomic, retain) NSMutableArray *colors; @property (nonatomic, assign) id<ColorPickerDelegate> delegate;   @end
    

    Here we declare a delegate so that this class can notify another class when a user selects a color. We then declare two variables/properties: one for the list of colors to display, and one to store the delegate itself.

    Then make the following changes to ColorPickerController.m:

    
    // Under @implementation @synthesize colors = _colors; @synthesize delegate = _delegate;   // Add viewDidLoad like the following: - (void)viewDidLoad { [super viewDidLoad]; self.clearsSelectionOnViewWillAppear = NO; self.contentSizeForViewInPopover = CGSizeMake(150.0, 140.0); self.colors = [NSMutableArray array]; [_colors addObject:@"Red"]; [_colors addObject:@"Green"]; [_colors addObject:@"Blue"]; }   // in numberOfSectionsInTableView: return 1;   // in numberOfRowsInSection: return [_colors count];   // In cellForRowAtIndexPath, under configure the cell: NSString *color = [_colors objectAtIndex:indexPath.row]; cell.textLabel.text = color;   // In didSelectRowAtIndexPath: if (_delegate != nil) { NSString *color = [_colors objectAtIndex:indexPath.row]; [_delegate colorSelected:color]; }   // In dealloc self.colors = nil; self.delegate = nil;
    

    Most of this should be normal table view stuff except for the following line:

    
    self.contentSizeForViewInPopover = CGSizeMake(150.0, 140.0);
    

    This line sets the size of how large the popover should be when it is displayed. If you do not add this line, by default the popover will be the entire height of the screen (which is usually too large).

    Displaying the Picker

    Believe it or not, that was the hardest part. Now to display the picker, all we need to do is add a button to our toolbar, and a little bit of code to display it and handle the selection.

    So first, let’s add the button. Open up RightViewController.xib and add a Bar Button Item to the toolbar. Set the title of the button “Set Color”.

    Now let’s declare a method for the button to trigger in RightViewController.h and declare a few variables we’ll need in a minute:

    
    // Up top, under #import #import "ColorPickerController.h"   // Modfiy class declaration @interface RightViewController : UIViewController <MonsterSelectionDelegate, UISplitViewControllerDelegate, ColorPickerDelegate> {   // Inside class ColorPickerController *_colorPicker; UIPopoverController *_colorPickerPopover;   // In property section @property (nonatomic, retain) ColorPickerController *colorPicker; @property (nonatomic, retain) UIPopoverController *colorPickerPopover;   - (IBAction)setColorButtonTapped:(id)sender;
    

    Before we forget, go ahead and connect the action method to the Bar Button Item in Interface Builder by control-dragging from the Bar Button Item to File’s Owner and connecting to the “setColorButtonTapped” outlet.

    Then let’s finish by making the required changes to RightViewController.m:

    
    // In synthesize section @synthesize colorPicker = _colorPicker; @synthesize colorPickerPopover = _colorPickerPopover;   // In dealloc self.colorPicker = nil; self.colorPickerPopover = nil;   // Add to end of file - (void)colorSelected:(NSString *)color { if ([color compare:@"Red"] == NSOrderedSame) { _nameLabel.textColor = [UIColor redColor]; } else if ([color compare:@"Green"] == NSOrderedSame) { _nameLabel.textColor = [UIColor greenColor]; } else if ([color compare:@"Blue"] == NSOrderedSame){ _nameLabel.textColor = [UIColor blueColor]; } [self.colorPickerPopover dismissPopoverAnimated:YES]; }   - (IBAction)setColorButtonTapped:(id)sender { if (_colorPicker == nil) { self.colorPicker = [[[ColorPickerController alloc] initWithStyle:UITableViewStylePlain] autorelease]; _colorPicker.delegate = self; self.colorPickerPopover = [[[UIPopoverController alloc] initWithContentViewController:_colorPicker] autorelease]; } [self.colorPickerPopover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; }
    

    Ok let’s explain this a bit. All popovers are is a “wrapper” around an existing view controller that “floats” it in a certain spot and possibly displays an arrow showing what the popover is related to. You can see this in setColorButtonTapped – we create our color picker, and then wrap it with a popover controller.

    Then we call a method on the popover controller to display it in the view. We use the helper function presentPopoverFromBarButtonItem to display the popover.

    When the user is done, they can tap anywhere outside the popover to dismiss it automatically. However if they select a color, we also want it to be dismissed, so we call the dismissPopoverAnimated method to get rid of the popover on-demand (as well as setting the color appropriately).

    And that’s it! Compile and run and when you tap the “Set Color” bar button item, you should see a popover like the following that changes the label color:

    You will find yourself using popovers quite a bit in places where users need to edit a field or toggle a setting, rather than the iPhone style where you navigate to the next level in a UINavigationController. They call this “flattening the hierarchy” in the iPad docs.

    Show Me the Code!

    Here’s a copy of all of the code we’ve developed so far.

    Check out the next part of the series, where we cover how to use custom input views on the iPad!

    Posted by 오늘마감

    댓글을 달아 주세요

    아이폰어플개발정보2010. 10. 12. 05:27
    Prevent The iPhone From Sleeping

    Code Snippet: Prevent The iPhone From Sleeping

    If you're new here, you may want to subscribe to my RSS feed. Thanks for visiting!

    The code below will prevent the iPhone from dimming its screen and ultimately going to sleep. Use it wisely as you don’t want your application becoming notorious for being a battery hog 

    [UIApplication sharedApplication].idleTimerDisabled = YES;

    밧데리를 엄청먹게되니 적절하게 적용
    Posted by 오늘마감

    댓글을 달아 주세요

    아이폰어플개발정보2010. 10. 12. 05:09
    [cocos2D] 프로젝트에, OpenGL ES와 iphone framework이 동시에 있을때 좌표
    OpenGL ES의 함수인  glViewport등 gl붙은 함수들이 모여있는 renderScene함수속에서 호출되는 함수상의 x y좌표 0.0은 수학적 좌표로 좌측하단에 있다. 심지어 이 함수 내부에서 콜되는 sprite의 함수도 만찮가지

    - (void)renderScene {
        
    // If OpenGL has not yet been initialised then go and initialise it
    if(!glInitialised) {
    [selfinitOpenGL];
    }
        
    // Set the current EAGLContext and bind to the framebuffer.This will direct all OGL commands to the
    // framebuffer and the associated renderbuffer attachment which is where our scene will be rendered
    [EAGLContextsetCurrentContext:context];
        glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
        
    // Define the viewport.Changing the settings for the viewport can allow you to scale the viewport
    // as well as the dimensions etc and so I'm setting it for each frame in case we want to change it
    glViewport(0, 0, screenBounds.size.width , screenBounds.size.height);

    // Clear the screen.If we are going to draw a background image then this clear is not necessary
    // as drawing the background image will destroy the previous image
    glClear(GL_COLOR_BUFFER_BIT);

    // Setup how the images are to be blended when rendered.This could be changed at different points during your
    // render process if you wanted to apply different effects
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


    [font1drawStringAt:CGPointMake(messageX, 100) text:message];
    //[anim renderAtPoint:CGPointMake(70, 100)];
    //[animThunder renderAtPoint:CGPointMake(180, 150)];
    [animrenderAtPoint:CGPointMake(70, 5)];
    [animThunderrenderAtPoint:CGPointMake(2, 180)];



    // Bind to the renderbuffer and then present this image to the current context
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
        [contextpresentRenderbuffer:GL_RENDERBUFFER_OES];
    }

    아이폰 API함수의 좌표는 우측하단이다
    아래의 함수상에서 호출되는 함수들의 좌표 x,y 는 우측상단이 0,0이다.


    -(void)BallMoving{




    float x = fireBall.center.x;
    float y = fireBall.center.y;

    fireBall.center = CGPointMake(fireBall.center.x + pos.x, fireBall.center.y + pos.y);
    //
    /
    //방향전환만한다.
    if (fireBall.center.x > 320 || fireBall.center.x < 0){

    pos.x = -pos.x;
    //찌그러뜨림


    ....
    }

    Posted by 오늘마감

    댓글을 달아 주세요

    아이폰어플개발정보2010. 10. 12. 05:00
    [cocos2D] Using Box2D Physics Engine with Cocos2D iPhone

    Starting work on my new project, I’ve found Box2D to be a far superior physics engine than chipmunk. It is more mature, the API more flexible, and it seems to even perform faster. However, the cocos2d code for it was rather space, so here is a sort of helper file I had to create to make it work with my game.

    Keep in mind this game is in progress and I’ve only been using this for a few days, so it may have issues that might pop up later on. It seems to be working very well for now though, with 40-50 objects on screen moving around with ~40 FPS.

    Here is the header

    // // Box2DEngine.h // // Created by EricH on 7/22/09. // Copyright 2009 __MyCompanyName__. All rights reserved. //   #import "Box2D.h"   // made this an extern constant to avoid obj c lookup overhead extern b2World *bb_world;   @interface Box2DEngine : CocosNode { }   + (Box2DEngine *)sharedSingleton; - (void)createWorld:(CGSize)size; - (void)deleteWorld; - (void)runSimulation; @end

    The .mm file

    // // Box2DEngine.mm // // Created by EricH on 7/22/09. // Copyright 2009 __MyCompanyName__. All rights reserved. //   #import "HookActor.h"     #import "Box2DEngine.h" #import "SuperBox2DActor.h"   b2World* bb_world;   const float32 timeStep = 1.0f / 60.0f; const int32 velocityIterations = 10; const int32 positionIterations = 10;   #define MAX_NUM_COLLISIONS 2048 // TODO make sure this buffer is the right size b2ContactResult contactResultCache[MAX_NUM_COLLISIONS]; int32 contactResultCount = 0;   class MyContactListener : public b2ContactListener { public: void Add(const b2ContactPoint* point) { }   void Persist(const b2ContactPoint* point) { }   void Remove(const b2ContactPoint* point) { }   void Result(const b2ContactResult* point) { // TODO we are making a deep copy of every contact point here // check the box2d contact masks to make sure we minimize unwanted contact results contactResultCache[contactResultCount++] = *point; } };   void handleCachedContactResults() { b2ContactResult contactResult; for(int i = 0; i < contactResultCount; i++) { #ifdef BBDEBUG if(contactResultCount >= MAX_NUM_COLLISIONS) { NSLog(@"RAN OUT OF BUFFER"); assert(NO); } #endif contactResult = contactResultCache[i]; SuperBox2DActor* actorOne = (SuperBox2DActor*)contactResult.shape1->GetBody()->GetUserData(); SuperBox2DActor* actorTwo = (SuperBox2DActor*)contactResult.shape2->GetBody()->GetUserData(); if(!actorOne.isDead && !actorTwo.isDead) { [actorOne collisionResultOne:&contactResult withActor:actorTwo]; [actorTwo collisionResultTwo:&contactResult withActor:actorOne]; } }   // clear the collision cache contactResultCount = 0; }   void removeDeadActors() { b2Body* node = bb_world->GetBodyList(); while (node) { b2Body* b = node; node = node->GetNext();   SuperBox2DActor* actor = (SuperBox2DActor*)b->GetUserData(); if (actor.isDead) {   // remove from physics engine bb_world->DestroyBody(b);   // remove from game engine (cocos2d) [[StandardGameController sharedSingleton] removeGameActor:actor]; } } }     @implementation Box2DEngine + (Box2DEngine*)sharedSingleton { static Box2DEngine* sharedSingleton; if (!sharedSingleton) sharedSingleton = [[Box2DEngine alloc] init];   return sharedSingleton; }   - (void)createWorld:(CGSize)size { b2AABB worldAABB; worldAABB.lowerBound.Set(0, 0); worldAABB.upperBound.Set(size.width * BOX2D_SCALE_FACTOR_INVERSE, size.height * BOX2D_SCALE_FACTOR_INVERSE); // TODO this shouldnt be inverse?   b2Vec2 gravity(0.0f, -30.0f); bool doSleep = true;   bb_world = new b2World(worldAABB, gravity, doSleep); bb_world->SetContactListener(new MyContactListener()); }   - (void)deleteWorld { [self unschedule:@selector(step:)]; delete bb_world; bb_world = NULL; }   - (void)runSimulation { [self schedule:@selector(step:)]; }   - (void)step:(ccTime)dt {   // step the world bb_world->Step(dt, velocityIterations, positionIterations); // TODO do i use timestep or dt here?   //BBLog(@"Num of contact results is %d",contactResultCount); // do stuff with collisions handleCachedContactResults();   // remove all actors that are marked as dead removeDeadActors();   // update cocosnode positions for (b2Body* b = bb_world->GetBodyList(); b; b = b->GetNext()) { if (b->GetUserData() != NULL) { SuperBox2DActor *actor = (SuperBox2DActor*)b->GetUserData(); b2Vec2 position = b->GetPosition(); actor.position = b2toCGPoint(position); actor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle()); //NSLog(@"obj %@ %4.2f %4.2f\n",actor, position.x, position.y); } }   }   @end

    Update: This code is obsolete now. You can just do

    Label *messageLabel = [Label labelWithString:message dimensions:CGSizeMake(380, 120) alignment:UITextAlignmentCenter fontName:@"your_custom_font" fontSize:26];

    by using the new FontManager class. For example, run this once in your app delegate when your program first loads

    [[FontManager sharedManager] loadFont:@"your_custom_font"];

    It can take a long NSString and create multiple labels without breaking up a word. I’ll eventually use this for my help screen, replacing the current 6 different 480×320 png images that I load for each one  .

    The code is simple, but hopefully it might save somebody the time it took me to write it. I had to look up some very basic elements of ObjC here, so if there is a much easier way to do this, please let me know but don’t make too much fun of me.

    You can easily switch out the BitmapFontAtlas for just a normal Label and it would work just fine.

    (void) setTipString:(NSString*)str {   NSInteger lineChars = 0; BOOL isSpace = NO; NSInteger index = 0; NSInteger numLines = 0;   NSMutableString *line = [NSMutableString stringWithCapacity:LINE_LENGTH];   while (index <= [str length]) { if(index == [str length]) { BitmapFontAtlas *tip = [[BitmapFontAtlas bitmapFontAtlasWithString:[NSString stringWithString:line] fntFile:@"text.fnt" alignment:UITextAlignmentLeft] retain]; [tip setPosition: cpv(30,210 - 20 * numLines)]; [self addChild:tip]; return; }     NSString *tmp = [str substringWithRange:NSMakeRange(index, 1)]; [line appendString:tmp];   if([tmp isEqual:@" "]) isSpace = YES; else isSpace = NO;   if(lineChars >= LINE_LENGTH && isSpace) { BitmapFontAtlas *tip = [[BitmapFontAtlas bitmapFontAtlasWithString:[NSString stringWithString:line] fntFile:@"text.fnt" alignment:UITextAlignmentLeft] retain]; [tip setPosition: cpv(30,210 - 20 * numLines)]; [self addChild:tip]; lineChars = -1; [line setString:@""]; numLines++; } lineChars++; index++; } }
    Posted by 오늘마감

    댓글을 달아 주세요

    아이폰어플개발정보2010. 9. 15. 07:58
    Using Box2D Physics Engine with Cocos2D iPhone

    Starting work on my new project, I’ve found Box2D to be a far superior physics engine than chipmunk. It is more mature, the API more flexible, and it seems to even perform faster. However, the cocos2d code for it was rather space, so here is a sort of helper file I had to create to make it work with my game.

    Keep in mind this game is in progress and I’ve only been using this for a few days, so it may have issues that might pop up later on. It seems to be working very well for now though, with 40-50 objects on screen moving around with ~40 FPS.

    Here is the header

    // // Box2DEngine.h // // Created by EricH on 7/22/09. // Copyright 2009 __MyCompanyName__. All rights reserved. //   #import "Box2D.h"   // made this an extern constant to avoid obj c lookup overhead extern b2World *bb_world;   @interface Box2DEngine : CocosNode { }   + (Box2DEngine *)sharedSingleton; - (void)createWorld:(CGSize)size; - (void)deleteWorld; - (void)runSimulation; @end

    The .mm file

    // // Box2DEngine.mm // // Created by EricH on 7/22/09. // Copyright 2009 __MyCompanyName__. All rights reserved. //   #import "HookActor.h"     #import "Box2DEngine.h" #import "SuperBox2DActor.h"   b2World* bb_world;   const float32 timeStep = 1.0f / 60.0f; const int32 velocityIterations = 10; const int32 positionIterations = 10;   #define MAX_NUM_COLLISIONS 2048 // TODO make sure this buffer is the right size b2ContactResult contactResultCache[MAX_NUM_COLLISIONS]; int32 contactResultCount = 0;   class MyContactListener : public b2ContactListener { public: void Add(const b2ContactPoint* point) { }   void Persist(const b2ContactPoint* point) { }   void Remove(const b2ContactPoint* point) { }   void Result(const b2ContactResult* point) { // TODO we are making a deep copy of every contact point here // check the box2d contact masks to make sure we minimize unwanted contact results contactResultCache[contactResultCount++] = *point; } };   void handleCachedContactResults() { b2ContactResult contactResult; for(int i = 0; i < contactResultCount; i++) { #ifdef BBDEBUG if(contactResultCount >= MAX_NUM_COLLISIONS) { NSLog(@"RAN OUT OF BUFFER"); assert(NO); } #endif contactResult = contactResultCache[i]; SuperBox2DActor* actorOne = (SuperBox2DActor*)contactResult.shape1->GetBody()->GetUserData(); SuperBox2DActor* actorTwo = (SuperBox2DActor*)contactResult.shape2->GetBody()->GetUserData(); if(!actorOne.isDead && !actorTwo.isDead) { [actorOne collisionResultOne:&contactResult withActor:actorTwo]; [actorTwo collisionResultTwo:&contactResult withActor:actorOne]; } }   // clear the collision cache contactResultCount = 0; }   void removeDeadActors() { b2Body* node = bb_world->GetBodyList(); while (node) { b2Body* b = node; node = node->GetNext();   SuperBox2DActor* actor = (SuperBox2DActor*)b->GetUserData(); if (actor.isDead) {   // remove from physics engine bb_world->DestroyBody(b);   // remove from game engine (cocos2d) [[StandardGameController sharedSingleton] removeGameActor:actor]; } } }     @implementation Box2DEngine + (Box2DEngine*)sharedSingleton { static Box2DEngine* sharedSingleton; if (!sharedSingleton) sharedSingleton = [[Box2DEngine alloc] init];   return sharedSingleton; }   - (void)createWorld:(CGSize)size { b2AABB worldAABB; worldAABB.lowerBound.Set(0, 0); worldAABB.upperBound.Set(size.width * BOX2D_SCALE_FACTOR_INVERSE, size.height * BOX2D_SCALE_FACTOR_INVERSE); // TODO this shouldnt be inverse?   b2Vec2 gravity(0.0f, -30.0f); bool doSleep = true;   bb_world = new b2World(worldAABB, gravity, doSleep); bb_world->SetContactListener(new MyContactListener()); }   - (void)deleteWorld { [self unschedule:@selector(step:)]; delete bb_world; bb_world = NULL; }   - (void)runSimulation { [self schedule:@selector(step:)]; }   - (void)step:(ccTime)dt {   // step the world bb_world->Step(dt, velocityIterations, positionIterations); // TODO do i use timestep or dt here?   //BBLog(@"Num of contact results is %d",contactResultCount); // do stuff with collisions handleCachedContactResults();   // remove all actors that are marked as dead removeDeadActors();   // update cocosnode positions for (b2Body* b = bb_world->GetBodyList(); b; b = b->GetNext()) { if (b->GetUserData() != NULL) { SuperBox2DActor *actor = (SuperBox2DActor*)b->GetUserData(); b2Vec2 position = b->GetPosition(); actor.position = b2toCGPoint(position); actor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle()); //NSLog(@"obj %@ %4.2f %4.2f\n",actor, position.x, position.y); } }   }   @end

    Update: This code is obsolete now. You can just do

    Label *messageLabel = [Label labelWithString:message dimensions:CGSizeMake(380, 120) alignment:UITextAlignmentCenter fontName:@"your_custom_font" fontSize:26];

    by using the new FontManager class. For example, run this once in your app delegate when your program first loads

    [[FontManager sharedManager] loadFont:@"your_custom_font"];

    It can take a long NSString and create multiple labels without breaking up a word. I’ll eventually use this for my help screen, replacing the current 6 different 480×320 png images that I load for each one  .

    The code is simple, but hopefully it might save somebody the time it took me to write it. I had to look up some very basic elements of ObjC here, so if there is a much easier way to do this, please let me know but don’t make too much fun of me.

    You can easily switch out the BitmapFontAtlas for just a normal Label and it would work just fine.

    (void) setTipString:(NSString*)str {   NSInteger lineChars = 0; BOOL isSpace = NO; NSInteger index = 0; NSInteger numLines = 0;   NSMutableString *line = [NSMutableString stringWithCapacity:LINE_LENGTH];   while (index <= [str length]) { if(index == [str length]) { BitmapFontAtlas *tip = [[BitmapFontAtlas bitmapFontAtlasWithString:[NSString stringWithString:line] fntFile:@"text.fnt" alignment:UITextAlignmentLeft] retain]; [tip setPosition: cpv(30,210 - 20 * numLines)]; [self addChild:tip]; return; }     NSString *tmp = [str substringWithRange:NSMakeRange(index, 1)]; [line appendString:tmp];   if([tmp isEqual:@" "]) isSpace = YES; else isSpace = NO;   if(lineChars >= LINE_LENGTH && isSpace) { BitmapFontAtlas *tip = [[BitmapFontAtlas bitmapFontAtlasWithString:[NSString stringWithString:line] fntFile:@"text.fnt" alignment:UITextAlignmentLeft] retain]; [tip setPosition: cpv(30,210 - 20 * numLines)]; [self addChild:tip]; lineChars = -1; [line setString:@""]; numLines++; } lineChars++; index++; } }

    Posted by 오늘마감

    댓글을 달아 주세요