- Card Game Engine with UIKit
- Generic Card Game
- Card Game Internals - The Game Internals
- Card game animations - you are here
As you might have guessed we've finally reached animations.
Initially I was having an idea to provide an in-depth description with code examples and sample project. But it turned out to be hardly possible.
Those animations are useless without the overall screen view with collections views on it and their cards layout. And all this won't work without game presenter... and game.
So the only way to show some samples is to share the whole project, that I unfortunately cannot afford...
That's why I decided just to share ideas overview of how animations were implemented.
BTW it's been more than 2,5 years since I worked on that stuff. I've started to forget was going on there actually.
Flip cards animations
Flip animation starts with a method "setNeedsFlipAtIndexPath", that allows to mark cards that will be flipped on next cells reload. It's important to mention that flip flag is applied once and removed on cell reload.
In-place flip is usually applied when card changes its state from face down to face up.
Card ViewModel has
- Face up data: suit, number, etc.
- Face down image: the back side of the card that is simply an image asset
Card CollectionView Cell has:
- Card "data" related views (number label, suit image, etc.)
- Card overlay imageView on top of the "data" view
When we need to perform flip animation of the card we
- Make snapshot of "data" view into an image
- Use UIView.transition(...) with flip animation parameter to simply animate the change of the UIImageView's image. Here we change facedown image with snapshot of card views or vice versa.
After all it creates a cool illusion of flipping cards.
Move with flip
Works exactly the same with the only difference: we perform the flip during applying animated changes to the collection view.
- setNeedsFlipAtIndexPath when needed
- performBatchUpdates(..) of the collection view (basically, move item from one section to another)
Collection view's batch animated updates always trigger cellForItemAtIndexPath method call and reload the cell during the animation.
If we marked the item with setNeedsFlipAtIndexPath, it would flip at reload. That's way the move and the flip happen almost simultaneously.
Fast cards move animation
Game start cards layout animation
It looks like we populate the game collection view with cards.
I've got an animations controller, that overlays game field.
It makes snapshots of needed cells and performs all animations just with images placed as subviews on top of main game view. All without changing the collection view itself.
It doesn't even know, that collection view is somehow related to gambling. Just make snapshot of cell and move image view.
Initial game layout
- Collection view is reloaded with invisible cells.
- Already knowing each card cell frame, animations controller simply adds an imageView with a card facedown image and moves it from one place to another.
- When animation is finished, viewController receives callback and corresponding cell is reloaded as visible and with flip animation if needed.
Almost the same thing as initial game layout.
- Autocomplete game actions are generated and applied to the the game state without touching UI (game state becomes out of sync with UI)
- Animations controller makes snapshots of the required cells of source collection view according to actions
- Source collection view is reloaded (source collection view is synced with game state now and it is actually empty)
- Animations controller simply moves snapshots from one place to another.
- When animation is finished, viewController receives callback, target collection view is reloaded
- Game stat is fully synced with UI now
It's also done as an overlay on the game field view.
This time animation controller:
- Animates the fall and jump of the cards, using UIKit Dynamic physics engine
- Makes snapshots continuously of the whole screen by timer (simple timer or via display link timer, depending on desired frequency)
- Replaces the background image of the overlay view with the snapshot of the whole screen, creating effect of a trailing tail of cards.
Those were ancient times and I needed to support devices since iOS 8 😯😯😯 That's why many of UIKit Dynamics features were simply not available.
To implement the desired animations I've invented a system of anchors and swinging arms, that was handled by animation controller.
- Each anchor view's initial frame is taken from the collection view cell.
- Anchor views are invisible
- Dragging gesture handling applies coordinates changes to anchor views' frames
- Snapshot views initial frame sizes are taken from the collection view layout cells.
- Snapshots views are simply UIImageViews with snapshots of collection view cells
- Snapshot views are attached to Anchor views' centres via UIKit Dynamic UIAttachmentBehavior with fixed non-zero length. Representing swinging arms.
- UIKit Dynamic UIGravityBehavior is applied to Snapshot views.
- Y-dimension moves are cut out from snapshot views' UIAttachmentBehavior This is a way to obtain "sliding" behaviour that was not available for iOS 8.
- Length of every next swinging arm below is larger than the previous one. It allows to obtain higher amplitude and lower frequency of cards oscillation for more "snaky" look.
- UIAttachmentBehavior is stiff when we attach a view to a point. UIAttachmentBehavior is more loose, like spring, when we attach two views. That's the reason why I have two sets of views. Just for better look.
- Dragging gesture handling applies coordinates changes to anchor views' frames, and UIKit Dynamic translate these moves through its physical engine to attached Snapshot views.
Cascade drop animation
When dragging gesture handling receive cancel/end/fail event, cascade drop animation occurs.
- Animation controller immediately kills UIKit Dynamics behaviours, removing all attachments and making views freeze in current place.
- Animation controller starts animating moves of snapshot views to the target coordinates. With simple UIView.animate(...)
All the magic is just in delays between animations. Every next card animation is delayed a little bit more than the previous one, creating an illusion of cascade drop.
It turned out to be surprisingly responsive.
The most astonishing was simultaneous move of cards with flip animation right in collection view with performBatchUpdates.
Indeed, it allowed to move and flip cards incredibly fast without applying any additional throttling techniques. Collection views are my bros!
Even the weakest device of all times (iPad Mini 1) showed affordable fps rate. Not 60 all the time, but still rather enjoyable. On more or less up-to-date devices like iPhone 6 it was literally flying.
All the tuning was just in applying rasterization of layers and careful work with shadow paths. Shadows are expensive. Never forget.
UIKit dynamics worked like a charm. Highly recommended.