Core Animation Sample Code: NanoLife
NanoLife is a simple Core Animation project with a single layer-backed view and a collection of sublayers. Each sublayer has a glowing sphere and moves along a random path, giving the impression of a microscopic lifeforms (or maybe fireflies?).You can switch to fullscreen mode by pressing any key, and switch back again by pressing a key again. The impressive bits about this is the very small amount of code used to achieve the effect, and the incredible scalability. The base layer count is 80 for artsy reasons, but I'm sure you could get away with a lot more.
The image size, opacity, and animation path are all random, so it's a new show each time. If you click hold the mouse button, the lifeforms will all be instinctively attracted to the mouse pointer. Once you've gathered them all up, you can drag them to a new location and release them for nice expansion effect.
For extra fun, try dragging them to different points outside of the window bounds and releasing them (for example, off the bottom edge of the window but horizontally centered).
This project is Leopard-only and has examples of:
- Core Animation
- Core Image filters
- Conversion between NSImage, CIImage, and CGImageRef
- Full screen mode for NSView
I did not use NS_BUILD_32_LIKE_64 for the project, because I wanted to avoid situations where somebody would copy-and-paste the code and see build errors in another project.
This project also has a simpler source usage terms than the BSD-ish license I've used in the past. The new license reads:
The purpose of this code is to provide an example of how to
write Mac software.
You may use this code for any purpose, though I ask you receive
permission before re-using any substantial portion of it in a
tutorial of your own. For info: theocacao at mac dot com.
An acknowledgement in release notes of your software is always
appreciated, but not required.
I hope that makes life easier for everyone. It also occurred to me that some people might want to run these little apps without dealing with Xcode, so I'm including a pre-built, double-clickable version too.
NanoLife Downloads: Xcode 3.0 Project / Pre-built App
Enjoy.
Core Animation Sample Code: NanoLife
Posted Feb 21, 2008 — 36 comments below
Posted Feb 21, 2008 — 36 comments below
Jonathan Grynspan — Feb 22, 08 5536
Seth Willits — Feb 22, 08 5537
Jason — Feb 22, 08 5538
Joachim Bengtsson — Feb 22, 08 5539
Moitah — Feb 22, 08 5540
ian — Feb 22, 08 5542
Chris Ryland — Feb 22, 08 5543
Nit-pick: these days, shouldn't the _cmd in NSLog() (-glowingSphereImageWithScaleFactor:) be wrapped in a sel_getName(_cmd)?
Bob Peterson — Feb 22, 08 5544
Jason — Feb 22, 08 5545
// stop animating everything and move all the sphere layers so that // they're directly under the mouse pointer. NSArray* sublayers = self.containerLayerForSpheres.sublayers; for ( CALayer* layer in sublayers) { [layer removeAllAnimations]; layer.position = cgMousePointInView; }
This snippet shows that what you are seeing is on purpose
Phil — Feb 22, 08 5547
I'm running it on a MacBook Pro 2.33Ghz with 2000 spheres whizzing around the place without any real noticeable slowdown.
Scott Stevenson — Feb 22, 08 5548
I noticed that too. Not sure offhand why it does that -- probably something simple. It was designed to not animate during -mouseDragged (as the comments in the code note), but it should always animate for normal -mouseDown.
@Moitah: Please, please... Make a screensaver out of it!
I thought about that, yeah. :)
@Chris Ryland: Nit-pick: these days, shouldn't the _cmd in NSLog() (-glowingSphereImageWithScaleFactor:) be wrapped in a sel_getName(_cmd)?
Sounds reasonable to me, although that _cmd stuff is just generated by TextMate's Objective-C bundle when I use the "log" tab trigger. I didn't write it out by hand.
@Bob Peterson: The pre-built app reliable freezes my Dual G5
Freezes the whole machine or just the app? If it freezes the machine, you should file a bug.
Jussi — Feb 22, 08 5549
I got a spinning cursor, for a few minutes nothing else seemed to respond but the mouse cursor -> reboot.
John C. Randolph — Feb 22, 08 5551
-jcr
Chris L — Feb 23, 08 5552
Works fine on a Mac mini with the G4 and Radeon 9200.
Adrian Bool — Feb 23, 08 5553
// create container layer for spheres
CALayer* sphereContainer = [CALayer layer];
sphereContainer.name = @"sphereContainer";
[mainLayer addSublayer:sphereContainer];
self.containerLayerForSpheres = sphereContainer;
As opposed to,
// create container layer for spheres
self.containerLayerForSpheres = [CALayer layer];
self.containerLayerForSpheres.name = @"sphereContainer";
[mainLayer addSublayer:self.containerLayerForSpheres];
You perform a similar action with sphereCount as well. I'm learning Cocoa and would love to know if the second form is significantly less efficient or if there is some other possible problem. Cheers!
Scott Stevenson — Feb 23, 08 5554
Every time you do either one of these:
self.containerLayerForSpheres [self containerLayerForSpheres]
... there's a cost for the method call. It's very small in this case, but if you're making several hundreds or thousand calls like this, it's probably better to get a local reference to the object so you don't have to keep calling the method.
It's also much easier to read "sphereContainer" than the longer "self.containerLayerForSpheres". It's important that instance variables and methods have very descriptive names, but I think it's less important for local variables because their purpose is clear from the context (a variable name like "sc" is not clear enough, though).
A third reason is that it's typically better to "set up" an object locally before setting it as an instance variable with the setter method, because you might write a setter that does some post-processing, such as creating a cached version of the object. It's better to give the setter the complete, finished object.
Patrick — Feb 23, 08 5555
kenneth — Feb 23, 08 5556
I turned this into a screensaver because it's totally cool and someone here requested it... i hope this is OK with you Scott, otherwise I'll take it down.
http://www.seoxys.com/nanolifesaver/
Kenneth
Scott Stevenson — Feb 23, 08 5558
I just emailed you -- I think it's great. I made it my screensaver. My first request is a slider in the options sheet to control the number of spheres.
ACoolie — Feb 24, 08 5560
To play around with it I added a window to change core/glow colors, change the background, and add/remove spheres.
I think I had to write more glue code for all the outlets than I did to actually do the CA stuff.
Changing sphere colors or adding a lot of them takes ~15 seconds each time on my PowerMac G5. Changing the gradient on the other hand is realtime.
Pic and Source. Someone might learn from it:
http://rapidshare.com/files/94640992/NanoLife-Colors.zip.html
http://img91.imageshack.us/img91/5425/picture1bh7.png
PS. I love it when you do things like this. Really encourages people to try it out for themselves.
Gav — Feb 24, 08 5561
I'm still at the beginner level of Cocoa/Objective-C, but it's stuff like this that gives me concrete milestones to work towards.
John Muir — Feb 24, 08 5562
Scott Stevenson — Feb 24, 08 5563
This is probably because a separate image is generated for each layer. You could change the code to only generate a single full-size image and resize the layer instead. If you do this, set kCAGravityResizeAspect for the contentsGravity property on the layer, so that the image scales with the layer.
The speed difference is nearly nonexistent for the original version of NanoLife, but it would be very noticeable for hundred/thousands of layers or if you're regenerating them frequently.
kenneth — Feb 24, 08 5564
http://www.seoxys.com/nanolifesaver/
As soon as I have some free time I'll try to optimize the CA code so it only generates one image that gets resized... Tends to lag with 500+ layers (and it takes extremely long to load too).
Moitah — Feb 25, 08 5567
David — Mar 02, 08 5585
Your response to Bob Peterson was file a bug, but the link is to this article. Did you mean file a bug with Apple? I don't think I have enough information to file a useful bug report.
Scott Stevenson — Mar 02, 08 5586
I wish I knew what to say about that, but I don't have the same hardware to test on.
Your response to Bob Peterson was file a bug, but the link is to this article. Did you mean file a bug with Apple?
Yes, typo on my part -- which is fixed now.
I don't think I have enough information to file a useful bug report
Actually you do. No application (especially something as simple as this) should hang the entire machine. You can file a bug with your system profile and attach the Xcode project for NanoLife (or just point to the url). That gives them plenty to work with.
This is all up to you, of course. It's just there's nothing I can personally do since I don't have the same hardware.
Peter — Mar 05, 08 5592
Scott, I'm curios as to why they animate towards the cursor when you click. From reading the code I would expect the behavior stated above.
Interestingly if I replace this line
CGPoint cgMousePointInView = NSPointToCGPoint(mousePointInView);
with
CGPoint cgMousePointInView = CGPointMake(0., 0.);
It always jumps immediately to the origin. Now, if you change the coordinates of the point to something like (10, 50) the first time it will animate to the point but any subsequent clicks will cause it to jump directly there.
How come it animates when you remove all animations and just set it's position?
Scott Stevenson — Mar 05, 08 5594
In this case, "remove animations" means "stop all animations which are currently in progress". When you set the "position" on the layer, you're changing the value of an animatable property which can trigger a new animation.
I haven't looked at the code to see why clicking in the same place twice only causes an animation the first time, but I suspect it's because the "random movement" animation is explicitly added -- it's not the result of animating a "position" property change. In other words, the layer may still consider itself to be at the location of the original mouse click, so it sees no reason to animate.
This is just a guess, though. I'd have to go back and look. Notably, Nano Spores does not have this issue.
Patrick Geiller — Mar 05, 08 5595
I've looked :) - on mouseup, Nanolife sets a path with mousedown's point as first point, where Nanospores sets the layer position to a new point, then sets a path from here.
Thanks for the code, btw !
Jeff — Mar 14, 08 5646
Steve White — Jun 28, 08 6112
Screenshot, Xcode Project
Facepalm — Feb 28, 09 6614
Free Videos — Mar 11, 09 6622
celik kapi — Jan 11, 10 7091
çelik kapı aksesuarından, çelik kapı müşterisi de herhangi
bir tüketiciden daha fazlası olduğunun bilincindeyiz.
Çelik kapı, taşınmaz sınıfına giren demirbaş sayılan ayrıcalıklı bir üründür.
Business PowerPoint — Jan 15, 10 7109
This article gives the light in which we can observe the reality. This is very nice one and gives in-depth information. Thanks for this nice article.
<a href="http://www.bizplancorner.com/resources/12/powerpoint_presentation.aspx">Business PowerPoint Presentation</a>