I'm new to Cocoa and looking for a little advice for an application from experienced Cocoa-ers.
I'm building a basic OmniGraffle-style app where objects are drawn/dragged onto a canvas. After the objects are on the canvas, they can be selected to modify their properties (fill color, stroke color/width, etc.), be resized, moved around to a new position, etc.
To get warmed up, I've written a basic drawing app that creates objects (circles, rectangles, etc.) as drawn by the mouse on a custom NSView, adds the objects to an NSArray collection, and renders the contents of the collection into the view. I could continue in this vein, but I'm going to have to add support for detecting object selection, resolving z-indexing, focus highlighting, drag handles, etc. with all the associated rendering. Also, rendering every object on each cycle seems terribly wasteful.
It seems like a better approach would be to drop lightweight view objects onto a canvas that were able to detect mouse events on themselves, draw themselves and their focus rings, and so forth. However, while NSView seems like an object with these properties, I see a lot of chatter on the web about it being a heavyweight component with a lot of baggage. I've stumbled across NSCells and have read up on them, but I'm not sure if they are the right alternative.
Any suggestions? If you can nudge me in the right direction I'd greatly appreciate it.
First rule of optimization: Don't do it first.
A custom NSView per shape sounds about right to me. Whether you'll want different subclasses for different shapes will be up to you; I'd start out with a single generic shape-view class and shapes able to describe themselves as Bézier paths, but don't be too strict about holding to that—change it if it'd make it easier. Just implement it however it makes sense to you.
Then, once you've got it working, profile it. Make as many shapes as you can. Then make more. High-poly-count shapes. Intersections. Fills, strokes, shadows, and gradients. You probably should create a separate document for each stressor. Notice just at the user level what's slow. Then, run your app under Instruments and look into why it's slow.
Maybe views will turn out to be the wrong solution. Don't forget to look into CALayers. But don't rule anything out as slow until you've tried it and measured it.
Related
I'm working on a 3rd person game built on Three.js where the user has free orbital control of the camera.
To enhance player experience, I'd like to use an outline on the active character that the user is controlling (there can be more characters in the scene), but only show the outline when (a part) of the player model is occluded/behind something.
Example:
The effect I want to create is shown on the far right (Stencil: 1), but instead of an opaque effect, I just want the outline as shown on the other models. There should be no outline on the visible parts of the player character, but only when the character is either partially behind another object or completely.
Now, the outline effect itself isn't really a problem. There are more than enough resources/examples/tutorials/whatnot about that. The part where I'm stuck is how to combine something like this with the occluding part. There is also the part about performance. If at all possible, I would really like to avoid rendering my entire scene multiple times for performance reasons.
Thanks in advance!
Using threejs R88 / 89-dev.
As an exercise, I decided to write a SimCity (original) clone in Swift for OSX. I started the project using SpriteKit, originally having each tile as an instance of SKSpriteNode and swapping the texture of each node when that tile changed. This caused terrible performance, so I switched the drawing over to regular Cocoa windows, implementing drawRect to draw NSImages at the correct tile position. This solution worked well until I needed to implement animated tiles which refresh very quickly.
From here, I went back to the first approach, this time using a texture atlas to reduce the amount of draws needed, however, swapping textures of nodes that need to be animated was still very slow and had a huge detrimental effect on frame rate.
I'm attempting to display a 44x44 tile map where each tile is 16x16 pixels. I know here must be an efficient (or perhaps more correct way) to do this. This leads to my question:
Is there an efficient way to support 1500+ nodes in SpriteKit and which are animated through changing their textures? More importantly, am I taking the wrong approach by using SpriteKit and SKSpriteNode for each tile in the map (even if I only redraw the dirty ones)? Would another approach (perhaps, OpenGL?) be better?
Any help would be greatly appreciated. I'd be happy to provide code samples, but I'm not sure how relevant/helpful they would be for this question.
Edit
Here are some links to relevant drawing code and images to demonstrate the issue:
Screenshot:
When the player clicks on the small map, the center position of the large map changes. An event is fired from the small map the central engine powering the game which is then forwarded to listeners. The code that gets executed on the large map the change all of the textures can be found here:
https://github.com/chrisbenincasa/Swiftopolis/blob/drawing-performance/Swiftopolis/GameScene.swift#L489
That code uses tileImages which is a wrapper around a Texture Atlas that is generated at runtime.
https://github.com/chrisbenincasa/Swiftopolis/blob/drawing-performance/Swiftopolis/TileImages.swift
Please excuse the messiness of the code -- I made an alternate branch for this investigation and haven't cleaned up a lot of residual code that has been hanging around from pervious iterations.
I don't know if this will "answer" your question, but may help.
SpriteKit will likely be able to handle what you need but you need to look at different optimizations for SpriteKit and more so your game logic.
SpriteKit. Creating a .atlas is by far one of the best things you can do and will help keep your draw calls down. Also as I learned the hard way keep a pointer to your SKTextures as long as you need them and only generate the ones you needs. For instance don't create textureWithImageNamed#"myImage" every time you need a texture for myImage instead keep reusing a texture and store it in a dictionary. Also skView.ignoresSiblingOrder = YES; helps a bunch but you have to manage your own zPosition on all the sprites.
Game logic. Updating every tile every loop is going to be very expensive. You will want to look at a better way to do that. keeping smaller arrays or maybe doing logic (model) updates on a background thread.
I currently have a project you can look into if you want called Old Frank. I have a map that is 75 x 75 with 32px by 32px tiles that may be stacked 2 tall. I have both Mac and iOS target so you could in theory blow up the scene size and see how the performance holds up. Not saying there isn't optimization work to be done (it is a work in progress), but I feel it might help get you pointed in the right direction at least.
Hope that helps.
I'm evaluating canvas libraries, and my needs are:
I want to make it easy to build nice looking buttons that move
around and on which I can easily capture events. Button drawing
helpers would be cool
I'll be building a system for others to use to create animated
scenes combining moving test, images, and sound. I won't ever be
drawing complex shapes myself, the most I might be drawing is
buttons around some text.
I do not want to be totally insulated from the low level machinery
of the per-frame drawing callback. Helped along sure, but
I'm going to be syncing with Web Audio API stuff and want to keep
access to super tight timing control
I'm comfortable with pretty low level scripting of animation, would rather not have it be something that changes Canvas into some
totally different paradigm, but not sure on this point
needs to work well for touch on iOs
I'd ideally like to be using one with good docs and a high truck number. The state of Canvas libs reminds me of the state of JS libs
10 years ago, and I'd rather not invest in something that doesn't
have an actual "team" behind it. Truck number == 1 worries me.
You flagged KineticJS, so I can say a little bit about how that would work.
1) It's a great tool for tracking shapes on a canvas, capturing clicks, and moving them around. It's easy to place an image on any shape, but I would use another program to make those images.
2) Even if you don't do a lot beyond buttons, KineticJS provides some nice features for manipulating the canvas, and I'm sure you'd use a lot of them in making tools for others.
3) KineticJS provides an animation object that repeatedly calls the draw() method for you. You define your draw method in order to create animations.
4) It's more of a wrapper around canvas. You work with a Stage and Layers, but there is still a lot of transparency to the canvas itself, and you can always do direct manipulation as well.
5) You can capture a broad range of events including "touch", "click", etc. It's easy to treat them the same when appropriate or differently if you need to. Furthermore, you can simply mark shapes as "draggable" and it handles all that appropriately.
6) Kinetic has had spectacular documentation and examples, but in looking now, the tutorials seem to be missing from http://kineticjs.com/ and I can't find them elsewhere. That's minorly worrisome, but the docs are still there and my guess is that they'll be back up soon since KineticJS is still under active development.
I'll weigh in on #1:
Nice looking buttons:
Hands-down...use Adobe Illustrator to create a set of button vector images (.svg).
If you need low level control over the button design at run-time then convert the Illustrator images to canvas drawing commands with this great plugin from Mike Swanson:
http://blog.mikeswanson.com/post/29634279264/ai2canvas.
The key here is that canvas will scale the vector button for you so you're always getting a professional, polished look both on a small mobile screen and a large desktop screen.
You could use canvas to build each part of a button from scratch, but don't reinvent the wheel.
A good animation library is Greensock. It also helps you build timelines (kind of like Flash timelines).
http://www.greensock.com/gsap-js/
As to canvas libraries, check out Stackoverflow's sister site that offers software recommendations:
http://softwarerecs.stackexchange.com
Good luck with your project!
in my app I want to have a light table to sort photos. Basically it's just a huge view with lots of photos in it and you can drag the photos around. Photos can overlap, they don't fall into a grid like in iPhoto.
So every photo needs to respond to mouse events. Do I make every photo into its own view? Or are views too expensive to create? I want to easily support 100+ photos or more.
Photos need to be in layers as well so I can change the stacking order. Do I use CoreAnimation for this?
I don't need finished source code just some pointers and general ideas. I will (try to) figure out the implementation myself.
Fwiw, I target 10.5+, I use Obj-C 2.0 and garbage collection.
Thanks in advance!
You should definitely use CALayer objects. Using a set of NSImageView subviews will very quickly become unmanageable performance-wise, especially if you have more than 100 images on screen. If you don't want to use Core Animation for some reason, you'd be much better off creating a single custom view and handling all the image drawing and hit testing yourself. This will be more efficient than instantiating many NSImageView objects.
However, Core Animation layers will give orders of magnitude improvement in performance over this approach, as each layer is buffered in the GPU so you can drag the layers around with virtually zero cost, and you only need to draw each image once rather than every time anything in the view changes. Core Animation will also handle layer stacking for you.
Have a look at the excellent CocoaSlides sample code which demonstrates a very similar application to what you describe, including hit testing and simple animation.
The simplest method is to use NSImageViews. You can create a subclass that can be easily dragged scaled and rotated. A more complex but visually superior option would be to use Core Animation layers (CALayer).
As long as you maintain the photo representations as distinct objects (so you can manipulate individually) they will use quite a chunk of memory, no matter how you represent them. If you provide all the data available in the photos each one could take several megs. You probably will want to actually reduce the image's display quality i.e. size in pixels, fidelity etc except when the particular photo is being worked on in detail.
Remember, you don't have to treat the photos like the physical objects they mimic. You simply have to create the illusion of physical objects in the interface. We're theater stage designers, not architects. As long as you data model model remains rigorous to the task at hand, the interface can engage in all kinds of illusions for the benefit of the user.
My current plan is to draw the rectangles by subclassing NSView, but that seems like a very inefficient way for what I'm trying to do, which is to draw a bunch of fixed, non-overlapping rectangles that changes colors once in a while. Is there a better way? Thanks.
You can try using CALayers, kind of like this: http://theocacao.com/document.page/555.
If they're all the same color or image, you may find a single CGLayer more efficient. The purpose of that API is drawing the same thing many times.
On the other hand, if the rectangles move independently or have different colors or images on them, Core Animation is definitely the way to go.
Core Animation would be a great technology for a game, but if you want to stick with NSView for the time being you could create a class similar to NSCell that the gameboard view uses to implement positioning and drawing. This would work in a similar way as many Cocoa control classes, which use a single cell (with different values) to draw multiple items inside a view.
Keep in mind that using individual NSView objects may very well be more than fast enough, but regardless of any speed differences this strategy allows you to separate the logic in a way that makes sense.