2012年6月 的存档

Drawrect with CGBitmapContext is too slow

2012年6月29日 没有评论

So I’ve got a basic drawing app in the process that allows me to draw lines. I draw to an off screen bitmap then present the image in drawRect. It works but its way too slow, updating about half a second after you’ve drawn it with your finger. I took the code and adapted it from this tutorial, http://www.youtube.com/watch?v=UfWeMIL-Nu8&feature=relmfu , as you can see in the comments people are also saying its too slow but the guy hasn’t responded.

So how can I speed it up? or is there a better way to do it? any pointers will be appreciated.

Heres the code in my DrawView.m.

-(id)initWithCoder:(NSCoder *)aDecoder {
     if ((self=[super initWithCoder:aDecoder])) {
         [self setUpBuffer];

     return self;

-(void)setUpBuffer {

     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

     offscreenBuffer = CGBitmapContextCreate(NULL, self.bounds.size.width, self.bounds.size.height, 8, self.bounds.size.width*4, colorSpace, kCGImageAlphaPremultipliedLast);

     CGContextTranslateCTM(offscreenBuffer, 0, self.bounds.size.height);
     CGContextScaleCTM(offscreenBuffer, 1.0, -1.0);

-(void)drawToBuffer:(CGPoint)coordA :(CGPoint)coordB :(UIColor *)penColor :(int)thickness {

     CGContextMoveToPoint(offscreenBuffer, coordA.x,coordA.y);
     CGContextAddLineToPoint(offscreenBuffer, coordB.x,coordB.y);
     CGContextSetLineWidth(offscreenBuffer, thickness);
     CGContextSetLineCap(offscreenBuffer, kCGLineCapRound);
     CGContextSetStrokeColorWithColor(offscreenBuffer, [penColor CGColor]);


- (void)drawRect:(CGRect)rect {
    CGImageRef cgImage = CGBitmapContextCreateImage(offscreenBuffer);
    UIImage *image =[[UIImage alloc] initWithCGImage:cgImage];
    [image drawInRect:self.bounds];


Works perfectly on the simulator but not device, I imagine that’s something to do with processor speed.

I’m using ARC.

I tried to fix your code, however as you only seem to have posted half of it I couldn’t get it working (Copy+pasting code results in lots of errors, let alone start performance tuning it).

However there are some tips you can use to VASTLY improve performance.

The first, and probably most noticeably, is -setNeedsDisplayInRect: rather then -setNeedsDisplay. This will mean that it only redraws the little rect that changed. For an iPad 3 with 1024*768*4 pixels that is a lot of work. Reducing that down to about 20*20 or less for each frame will massively improve performance.

CGRect rect;
rect.origin.x = minimum(coordA.x, coordB.x) - (thickness * 0.5);
rect.size.width = (maximum(coordA.x, coordB.x) + (thickness * 0.5)) - rect.origin.x;
rect.origin.y = minimum(coordA.y, coordB.y) - (thickness * 0.5);
rect.size.height = (maximum(coordA.y, coordB.y) + (thickness * 0.5)) - rect.origin.y;
[self setNeedsDisplayInRect:rect];

Another big improvement you could make is to only draw the CGPath for this current touch (which you do). However you then draw that saved/cached image in the draw rect. So, again, it is redrawn each frame. A better approach is to have the draw view being transparent and then to use a UIImageView behind that. UIImageView is the best way to display images on iOS.

- DrawView (1 finger)
- BackgroundView (the image of the old touches)

The draw view would itself then only ever draw the current touch only the part that changes each time. When the user lifts their finger you can cache that to a UIImage, draw that over the current background/cache UIImageView’s image and set the imageView.image to the new image.

That final bit when combining the images involves drawing 2 full screen images into an off screen CGContext and so will cause lag if done on the main thread, instead this should be done in a background thread and then the result pushed back to the main thread.

* touch starts *
- DrawView : draw current touch
* touch ends *
- 'background thread' : combine backgroundView.image and DrawView.drawRect
    * thread finished *
    send resulting UIImage to main queue and set backgroundView.image to it;
    Clear DrawView's current path that is now in the cache;

All of this combined can make a very smooth 60fps drawing app. However, views are not updated as quickly as we’d like so the drawing when moving the figure faster looks jagged. This can be improved by using UIBezierPath’s instead of CGPaths.

CGPoint lastPoint = [touch previousLocationInView:self];
CGPoint mid = midPoint(currentPoint, lastPoint);
-[UIBezierPath addQuadCurveToPoint:mid controlPoint:lastPoint];

The reason it is slow is because every frame you are creating a bitmap and trying to draw that.

You asked for better ways of doing it? Have you looked at the apple sample code for a drawing app on iOS? If you don’t like that, then you can always use cocos2d which provides a CCRenderTexture class (and sample code).

Currently, you are using a method which you already know is not efficient.

With this approach I suppose you should consider using background thread for all hard work of image rendering and main thread for UI updates only, i. e.

__block UIImage *__imageBuffer = nil;

- (UIImage *)drawSomeImage

    // draw image with CoreGraphics

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();


    return image;

- (void)updateUI
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // prepare image on background thread

        __imageBuffer = [self drawSomeImage];

        dispatch_async(dispatch_get_main_queue(), ^{

            // calling drawRect with prepared image

            [self setNeedsDisplay];


- (void)drawRect
    // draw image buffer on current context

    [__imageBuffer drawInRect:self.bounds];

I am omitting some details for making the optimization more clear. Even better to switch to UIImageView. This way you could get rid from critically important - (void)drawDect method and update image property of the UIImageView when the image is ready.

Well I think you need to change your logic. You may get some very good idea with the help of this link
and if you think that you have no time to make understanding then you may go directly to this code https://github.com/levinunnink/Smooth-Line-View :) I hop this will help you a lot.

Use CgLayer for caching your paths, read the docs, Its best for optimization.

I did something exactly like this. Check out the Pixelate app on AppStore. In order to draw , I used tiles in my code. After all , when you toch the screen and draw something you need to re-draw the entire image which is a very heavy operation. If you like the way Pixelate is moving , here’s how I did it:

1)Split my image in n x m tiles. That was so I can change those values and obtain bigger/smaller tiles. In the worst case scenario (the user taps at the intersection of 4 tiles) you have to re-draw those 4 tiles. Not the entire image.

2) Make a 3 dimensional matrix in which I was storring the pixel information of each tile. So matrix[0][0][0] was the red value ( each pixel has a RGB or RGBA value depending if you are using pngs or jpgs) of the first pixel of the first tile.

3) Get the location the user pressed and calculate the tiles that need to be modified.

4) Modify the values in the matrix and update the tiles that need to update.

NOTE: This most certainly isn’t the best option. It’s just an alternative. I mentioned it because I think it is close to what you have right now. And it worked for me on an iPhone 3GS. If you are targeting >= iPhone 4 , you should be more than ok.



Whatever the method u’ve suggested is way too inefficient, because creating the image every time you move the finger is inappropriate.

If its just paths that you need to draw, then have a CGMutablePathref as a member variable,
and in draw rect just move to the specified point using CGPath functions.

And more importantly, while refreshing the view, call setNeedsDisplayInRect passing only the area that you need to draw. Hope it will work for you.

分类: cocos2d, stackoverflow精选 标签:

Unity3D学习 愤怒的小鸟之Play界面(四)

2012年6月28日 没有评论

目标:给Play界面添加一个个性化的Play按钮Unity3D学习 愤怒的小鸟之Play界面(四)


1. 创建一个GUISkin, Assets—>Create—>GUI Skin, 然后在Custom Styles添加你想要的按钮图片。

2. 接下来我们写代码,添加这个个性化的按钮,我先上代码了。

#pragma strict

var customSkin : GUISkin;

function Start () {


function Update () {

function OnGUI () {
	GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, 
		Vector3(Screen.width / 800.0, Screen.height / 600.0, 1));
	GUI.skin = customSkin;
	if (GUI.Button(Rect(250, 225, 300, 150), "", "PlayButton")) {

代码很简单,就是在使用Button之前,将系统的skin换成自定义的skin,然后在GUI.Button函数添加第三个参数,就是custom styles里自定义样式。

代码很简单,就是在使用Button之前,将系统的skin换成自定义的skin,然后在GUI.Button函数添加第三个参数,就是custom styles里自定义样式。


分类: unity3d 标签:

Unity3D学习 愤怒的小鸟之Play界面(三)

2012年6月28日 没有评论


Unity3D学习 愤怒的小鸟之Play界面(三)                  Unity3D学习 愤怒的小鸟之Play界面(三)



#pragma strict

var myCursor : Texture2D;
var myClickCursor : Texture2D;
var cursorWidth : float;
var cursorHeight : float;

private var isClicked : boolean = false;

function Start () {
	Screen.showCursor = false;

function Update () {
	if (Input.GetMouseButton(0)) 
		isClicked = true;

		isClicked = false;

function OnGUI () {
	var mousePos = Input.mousePosition;
	if (isClicked)
		GUI.DrawTexture(Rect(mousePos.x - cursorWidth / 2, Screen.height - mousePos.y - cursorHeight / 2,
			cursorWidth, cursorHeight), myClickCursor);
		GUI.DrawTexture(Rect(mousePos.x - cursorWidth / 2, Screen.height - mousePos.y - cursorHeight / 2,
			cursorWidth, cursorHeight), myCursor);


OK, 试试效果吧!

分类: unity3d 标签:

Unity3D学习 愤怒的小鸟之Play界面(二)

2012年6月27日 没有评论


Unity3D学习 愤怒的小鸟之Play界面(二)

1. 利用GUITexture搭建背景。



2. 写脚本使背景循环移动




#pragma strict

var moveSpeed : float = 0.1;

function Start () {


function Update () {
	transform.Translate(Vector3.left * moveSpeed * Time.deltaTime);
	if (transform.position.x <= -1) {
		transform.position.x = 1;


源码下载地址: http://download.csdn.net/detail/ariel7321/4395457

分类: unity3d 标签:

Unity3D学习 愤怒的小鸟之Logo界面(一)

2012年6月26日 没有评论




Unity3D学习 愤怒的小鸟之Logo界面(一)

1.  建一个空Project,将屏幕分辨率改为800*600.

File—>Build Settings…—->PC and Mac Standalone—>Player Settings…—>Resolution and Presentation

2. 创建一个GUI Texture,重命名为Logo,然后Logo图片放到这个GUI Texture上,修改其参数如下:

Unity3D学习 愤怒的小鸟之Logo界面(一)
Unity3D学习 愤怒的小鸟之Logo界面(一)
Unity3D学习 愤怒的小鸟之Logo界面(一)

Unity3D学习 愤怒的小鸟之Logo界面(一)

3. 创建一个Scene,命名为PlayScene。然后把Scene都加入到build中

Unity3D学习 愤怒的小鸟之Logo界面(一)

4. 我们来写个脚本实现界面切换功能。

#pragma strict

var timeInterval : int = 4;
var level : int;

private var timeUpdate : float;

function Start () {


function Update () {
	timeUpdate += Time.deltaTime;
	if (timeUpdate > timeInterval) {


5. 将这个脚本赋给LogoScene中的camera,将Level值改为1.


分类: unity3d 标签:

Detect touch Cocos2d-x

2012年6月21日 没有评论

I’m using Cocos2d-x and trying to detect touches in my HelloWorld project. Though I’m having no luck.


class HelloWorld : public CCLayer{

    CCSpriteBatchNode * _batchNode;
    CCSprite *_turkey;
    virtual void ccTouchesBegan(cocos2d::CCSet* touches, cocos2d::CCEvent* event);


void HelloWorld::ccTouchesBegan(cocos2d::CCSet* touches, cocos2d::CCEvent* event){

but the thing is that when I click the screen ‘this’ never shows up in the log. What am i missing here?



Im using this tutorial.

You have to register with CCTouchDispatcher in order to receive touches:

Write this in your init() method in order to receive touches:

CCTouchDispatcher::sharedDispatcher()->addStandardDelegate(this, 0);

Also I recommend you to receive touch event via targeted touch delegate methods:

virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent);

In order these methods to be called you have to register with touch dispatcher a bit different:

CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0, true);


In new cocos version CCTouchDispatcher is located in CCDirector:

It should look something like this:

CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true);

So something super simple, just added


to my init(); function.

'this' never shows up in the log

hints You might be using a different version of Cocos2D library. Please go to cocos2d.h on your project and confirm. (the sample was written on 1.0.1). If you are on a different version, (guessing) you might have to use different ccTouchesBegan signature and/or fix more than just setIsTouchEnabled to make it work. I just downloaded the sample, and the ccTouchesBegan call works perfect – without any changes.

this->setTouchEnabled(true); works better than CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0, true);unfortunately my ccTouchMoved is not picking anything up… :(

分类: cocos2d, stackoverflow精选 标签:

cocos2d 2.0-rc2: end the director and restart

2012年6月15日 没有评论

I have a cocos2d powered game that uses UIKit menues, so I only use the framework for one viewcontroller, which is the game itself. Also, it only has one scene. Since cocos2d 2.0 the director itself is a UIViewController subclass, so I just push it in my MenuViewController when the user taps a start button:

-(void)startGameButtonPressed {

    CCDirectorIOS* director = (CCDirectorIOS *) [CCDirector sharedDirector];

    // Create an CCGLView with a RGB565 color buffer, and a depth buffer of 0-bits
    self.glView = [CCGLView viewWithFrame:CGRectMake(0, 0, 480, 320)
                              pixelFormat:kEAGLColorFormatRGB565    //kEAGLColorFormatRGBA8
                              depthFormat:0 //GL_DEPTH_COMPONENT24_OES

    // attach the openglView to the director
    [director setView:glView];
    [director runWithScene:[GameLayer scene]];
    [director setDelegate:(id <CCDirectorDelegate>) [[UIApplication sharedApplication] delegate]];
    [self.navigationController pushViewController:director animated:YES];

This works fine for the first time the method is called, when the user starts the first game. When the game is over, I call [[CCDirector sharedDirector] end].

Most of the director setup is done in the appDelegate (it’s taken unchanged from the default Cocos2d template). I only put the CCGLView as a retained property into my MenuViewController, because otherwise the app crashes when [[CCDirector sharedDirector] end] is called and the CCGLView is not retained. I think that might be a cocos2d bug. In [[CCDirector sharedDirector] end] the framework calls [self setView:nil], but it still tries to access the view later on (probably on another thread).

The problem now is that on the second call of my method above (when the user wants to start another game from the menu), startGameButtonPressed, the director gets pushed but the screen remains black. The game is running and responding, I just don’t see anything. Can someone please help me out with that?

OK, I had the same problem and I was able to “fix it”.

When you set the CCGLView and [director setView:], even if you pop the controller the scene still exists. the only thing that happens is that the scene gets stopped.

So in order to have the “restart” working, you have to check if there is already a running scene (even if it’s stopped, and instead of runWithScene: you use replaceScene:.

Here is my code so you can see:

- (void)setupCocos2D {
    CCGLView *glView = [CCGLView viewWithFrame:CGRectMake(0, 0, 320, 480)
                               pixelFormat:kEAGLColorFormatRGB565   //kEAGLColorFormatRGBA8
                               depthFormat:0    //GL_DEPTH_COMPONENT24_OES

if(![director_ runningScene]){
    [director_ setView:glView]; // SET THE DIRECTOR VIEW
    if( ! [director_ enableRetinaDisplay:YES] ) // ENABLE RETINA
        CCLOG(@"Retina Display Not supported");

    [director_ runWithScene:[HelloWorldLayer scene]]; // RUN THE SCENE

} else {
    [director_ startAnimation];
    [director_ replaceScene:[HelloWorldLayer scene]];

[director_ setDelegate:(id <CCDirectorDelegate>) [[UIApplication sharedApplication] delegate]];

[self.view addSubview:[director_ view]];


Hope this code will help you, because I spent a whole day trying to figure this out.
It may not be the correct way or even the prettiest way, but it’s working :)

Also, please not that if you POP the COCOS2D scene, you don’t have to [[CCDirector sharedDirector] end] as the animation will be stopped when the view is dealloc/removed.

I have spent several days looking for information relating to this, and will share with you my own experience. I am also trying to create a game that loads into a UITableViewController, from which the CCDirector is loaded when a cell is touched. This is a Game Center turn-based game, hence the design (think Words With Friends). The best approach I have found so far for this is as follows (note I’m working in 2.0 – where CCDirector is a UIViewController subclass):

In AppDelegate.h, create a new ivar to hold the CCGLView that is created from the template code. Then assign the CCGLView created in didFinishLaunching to your new ivar. This allows the director to reuse the originally created view instead of trying to recreate it every time you reload the CCDirector, which seems to cause all sorts of weird issues in my experience.

You also want to create a new method in AppDelegate called -setupDirector or something of the like where you will, well, setup the director. This should be called each time you are recreating the CCDirector. I have posted my version below. Note my ivar for the CCGLView is called “GLView”.

- (void)setupDirector {
if (![CCDirector sharedDirector]) {
    CCLOG(@"Calling setupDirector");

    director_ = (CCDirectorIOS*) [CCDirector sharedDirector];

    director_.wantsFullScreenLayout = YES;

    // Display FSP and SPF
    [director_ setDisplayStats:NO];

    // set FPS at 60
    [director_ setAnimationInterval:1.0/60];

    // attach the openglView to the director
    [director_ setView:GLView];

    // for rotation and other messages
    [director_ setDelegate:self];

    // 2D projection
    [director_ setProjection:kCCDirectorProjection2D];
    //  [director setProjection:kCCDirectorProjection3D];

    // Enables High Res mode (Retina Display) on iPhone 4 and maintains low res on all other devices
    if( ! [director_ enableRetinaDisplay:YES] )
        CCLOG(@"Retina Display Not supported");

    // Default texture format for PNG/BMP/TIFF/JPEG/GIF images
    // It can be RGBA8888, RGBA4444, RGB5_A1, RGB565
    // You can change anytime.
    [CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];

    // If the 1st suffix is not found and if fallback is enabled then fallback suffixes are going to searched. If none is found, it will try with the name without suffix.
    // On iPad HD  : "-ipadhd", "-ipad",  "-hd"
    // On iPad     : "-ipad", "-hd"
    // On iPhone HD: "-hd"
    CCFileUtils *sharedFileUtils = [CCFileUtils sharedFileUtils];
    [sharedFileUtils setEnableFallbackSuffixes:NO];             // Default: NO. No fallback suffixes are going to be used
    [sharedFileUtils setiPhoneRetinaDisplaySuffix:@"-hd"];      // Default on iPhone RetinaDisplay is "-hd"
    [sharedFileUtils setiPadSuffix:@"-ipad"];                   // Default on iPad is "ipad"
    [sharedFileUtils setiPadRetinaDisplaySuffix:@"-ipadhd"];    // Default on iPad RetinaDisplay is "-ipadhd"

    // Assume that PVR images have premultiplied alpha
    [CCTexture2D PVRImagesHavePremultipliedAlpha:YES];

In addition, you will want to make a couple changes to the way the template loads up the view controllers. Normally, cocos2D sets up the navigation controller with director_ as the root view controller. Here, you want to alloc and init your menu view controller and add THAT instead of director_:

// Create a Navigation Controller with the Director
gamesTVC_ = [[GamesTableViewController alloc] initWithStyle:UITableViewStyleGrouped];
navController_ = [[UINavigationController alloc] initWithRootViewController:gamesTVC_];
navController_.navigationBarHidden = NO;

Everything else in didFinishLaunching can remain the same. Now, in your menuViewController in your startGameButtonPressed method, you will call the newly created setupDirector method on your app instance, which is referenced by calling:

AppController *app = (AppController *)[[UIApplication sharedApplication] delegate];
if ([CCDirector sharedDirector].runningScene) {
    [[CCDirectorIOS sharedDirector] end];
[app setupDirector];
[app.navController pushViewController:app.director animated:YES];

I include a check to make sure the CCDirector isn’t still running, and if it is, end it. In your game layer, when the time comes that you want to pop the view controller, you will simply call it like so:

AppController *app = (AppController *)[[UIApplication sharedApplication] delegate];
[app.navController popViewControllerAnimated:YES];
[[CCDirector sharedDirector] end];

This flow should allow you to freely use a navigation controller to push your game scene with CCDirector, and pop that view controller when you want to go back to your UIKit-based main menu. I hope this helps, as I have spent a lot of frustrating time trying to get this right for my own game.

What works well is to just call startAnimation and stopAnimation in the director but keep the cocos2d view around and just re-use it.

Any attempts to shut down cocos2d and its OpenGL view and re-initializing it later on will cause more or less issues, because it really hasn’t been tested well enough. Other than that cocos2d works just fine with UIKit, it just has the same issues any other OpenGL ES app has when mixing it with UIKit views.

In my experience, Cocos doesn’t really support ending and resuming, it acts like it’s done and pretty much shuts itself down.

There are two things you can try, the first one (and it just came to my mind) is to call CC_DIRECTOR_INIT (may not be the exact name) after the director ended, and before you want to start it again.

The second one is to edit the director source code, and modify the end method so that it leaves cocos in a usable state (stop it from releasing the view and purging the cache, etc). Alternatively to this, you can modify the start method so that it makes sure Cocos is in a usable state before starting.

Sadly, Cocos doesn’t make it easy for us to use UIKit+Cocos, but with luck with it.

分类: stackoverflow精选, unity3d 标签:

Cocos2d 2.0 – 3 numbers on the bottom left

2012年6月4日 没有评论

I have 3 numbers on the bottom left part of the screen on my Cocos2D 2.0 project:


60 is probably FPS and what about the other two? As I remember, previous versions of Cocos had just the FPS number.

Any clues? thanks

82    <-- number of draw calls
0.016 <-- time it took to render the frame, here: 1.0/60.0 = 60 fps
60.0  <-- frames per second

The first number (82) is the number of draw calls (which is fairly high). Typically each node that renders something on the screen (sprites, labels, particle fx, etc) increases that number by one. Draw calls are expensive, so it is very important to keep that number down. One way to do so is by batching draw calls – cocos2d v3 does this automatically.

The time it took to render a frame, in seconds. Since you need to draw a new frame every 0.016666666 seconds in order to achieve 60 frames per second (1/60 = 0,0166…) it’s just the inverse of the framerate.

The last number is the number of frames per second aka framerate aka fps. This value, like the previous one, is averaged over several frames so that it doesn’t fluctuate as much.

Note that iOS devices always have VSynch (vertical synchronization) on. A game can render a frame every 0.0166 seconds – if every frame takes 0.017 seconds to compute, the framerate is effectively halved to 30 fps. You can only have fps in concrete steps: 60, 30, 20, 15, 12, 10 …

Since the fps display is averaged over a couple frames it hides this fact. So if the display stats show 45 fps would be a sequence of frames where every other frame took longer than 0.0166 seconds. In fps numbers the individual fps of most recent frames would have been: 60, 30, 60, 30, 60, 30.

The top number is the number of sprites in your CCLayer, etc..

The middle is the FPS’s milliseconds.

The bottom is of course your FPS! :)

分类: cocos2d, stackoverflow精选 标签: