If you have a UIView
Imagine the view being full of text, animations, a cat photo, a gradient, or indeed any of the stuff a UIView can be full of.
It's easy to tilt it back to the left in 3D
or back to the right ..
(BTW I highly recommend the perfect OHCubeView
ohcubeview when you have to do that sort of thing.)
But I want to crease a view along a seam, and have one side going backwards to the left and the other side going backwards to the right ..
!
My first solution was simply: whatever the view is (the user is typing in text, a photo of a cat, whatever), just perfectly duplicate it, and then solve the problem in the obvious way.
That's inelegant, but does work.
the thing I want to fold like this, is ... an SKView with an emitter in it.
Particle emitters are of course random, and while two of the same one will look similar, it's not a real solution.
Essentially, if someone knows how to "copy" a UIView to another, well, view, or something ...
the problem would be solved.
For a problem like this in a game engine, say, you just throw a "virtual camera" (whatever the concept is in your favorite game engine) on the thing in question, and put the other half, or whatever, anywhere you want, but, I don't know the analogy of that in iOS!
Does anyone have an angle on this:
There's 2 approaches I could think of.
1) Bend Approach
You could bend your scene using an SKWarpGeometry. Semantically, you'd envision a 2 x 2 quadrant grid on your game scene, described by a 3 x 3 point data structure. You'd move the left and right edges in a little, and make them a little smaller. And you'd make the centre vertical edge a little longer.
Apple's documentation also mentions that essentially any type of node (including an SKEmitterNode), can be warped by putting it into an SKEffectNode, and applying the warp to that. See: https://developer.apple.com/documentation/spritekit/skwarpgeometrygrid/animate_the_warping_of_a_sprite?language=objc.
All we have to do is make an effect node be the root node of everything, and apply the warp to it. Now, this works, but as soon as there's an emitter in it, the warp starts spazzing in seizure-like convulsions. I regard this as a bug. I'll include the code anyway incase its fixed later on, because it's supposed to work according to the documentation.
let sourcePoints = [
// bottom row: left, center, right
vector_float2(0.0, 0.0),
vector_float2(0.5, 0.0),
vector_float2(1.0, 0.0),
// middle row: left, center, right
vector_float2(0.0, 0.5),
vector_float2(0.5, 0.5),
vector_float2(1.0, 0.5),
// top row: left, center, right
vector_float2(0.0, 1.0),
vector_float2(0.5, 1.0),
vector_float2(1.0, 1.0)
]
var destinationPoints = sourcePoints
// Bring lefts in, and make the edge shorter
destinationPoints[0] = vector_float2(0.1, 0.1)
destinationPoints[3] = vector_float2(0.1, 0.4)
destinationPoints[6] = vector_float2(0.1, 0.9)
// Bring rights in, and make the edge shorter
destinationPoints[2] = vector_float2(0.9, 0.1)
destinationPoints[5] = vector_float2(0.9, 0.4)
destinationPoints[8] = vector_float2(0.9, 0.9)
// Make the center edge longer
destinationPoints[1] = vector_float2(0.5, -0.2)
destinationPoints[4] = vector_float2(0.5, 0.7)
destinationPoints[7] = vector_float2(0.5, 1.2)
// Need to set the no warp geometry first
effectNode.warpGeometry = SKWarpGeometryGrid(columns: 2, rows: 2)
let newGeometry = SKWarpGeometryGrid(columns: 2, rows: 2,
sourcePositions: sourcePoints,
destinationPositions: destinationPoints)
effectNode.run(SKAction.warp(to: newGeometry, duration: 1.0)!)
Sources used:
https://www.hackingwithswift.com/example-code/games/how-to-warp-a-sprite-using-skwarpgeometrygrid
http://sound-of-silence.com/?page=blog&sort=recent&count=0
2) Live Copy Approach
This is simpler, there's far less code, and it's a lot more versatile; you can do things like in this Rayman Legends level https://youtu.be/7m5YQrucis8?t=1226. Also, it works.
Basically, we have our master SKView showing the master scene. We'll add a copy SKView showing a copy scene. The copy scene will have only one sprite node in it. In the master scene's update loop, we'll grab a flattened texture of the scene and assign it to the sprite node in the copy scene. An object that is the scene's delegate can tap into this update loop, so we'll make the view controller conform to SKSceneDelegate.
class Playground_VC: UIViewController, SKSceneDelegate {
var sprite: SKSpriteNode?
override func loadView() {
self.view = SKView()
}
var skView: SKView {
return self.view as! SKView
}
override func viewWillAppear(_ animated: Bool) {
let scene = SKScene(size: self.skView.bounds.size)
scene.backgroundColor = UIColor.clear
scene.delegate = self
self.skView.presentScene(scene)
let e = newEmitter()
scene.addChild(e)
e.position = self.skView.midPoint
self.createCopy()
}
func update(_ currentTime: TimeInterval, for scene: SKScene) {
// Grab the root texture and assign to the sprite that's in the copy scene
let t = self.skView.texture(from: self.skView.scene!)
self.sprite?.texture = t
}
func createCopy() {
// Make a new SKView that's 4 times smaller
var f = self.skView.bounds
f.size.height *= 0.4
f.size.width *= 0.4
let copy = SKView(frame: f)
copy.presentScene(SKScene(size: f.size))
self.view.addSubview(copy)
let sprite = SKSpriteNode(texture: nil, size: f.size)
copy.scene?.addChild(sprite)
self.sprite = sprite
sprite.position = CGPoint(x: f.size.width / 2, y: f.size.height / 2)
}
func newEmitter() -> SKEmitterNode {
return SKEmitterNode(fileNamed: "Particle.sks")!
}
}
Any manipulations you perform on the master SKView, like tilting it in 3D space, shouldn't affect the appearance of the copy, because the game scene is unaffected by those manipulations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With