So I am working on a Breakout app in swift. I currently have a ball, which is a UIView with a cornerRadius = 20.0 to emulate a ball. I also have a paddle, which is another UIView with a smaller cornerRadius = 5.0. I have programmatically made nine red views which are each 50x50 units large. I have collision and motion mechanics for my ball, paddle, and block elements.`var dynamicAnimator: UIDynamicAnimator!
var pushBehavior: UIPushBehavior!
var collisionBehavior: UICollisionBehavior!
var ballDynamicBehavior: UIDynamicItemBehavior!
var paddleDynamicBehavior: UIDynamicItemBehavior!
var blockBehaviors: UIDynamicItemBehavior!
My issue here, is that the ball collides with the blocks, but I don't know how to detect whether or not the ball hit the block, but I do know how to make the views appear and disappear (give the view a backgroundcolor matching the View's color, and remove it from the blockBehaviors. Basically, I want to know how to detect when two views collide via. function or something else.
It would also be awesome if I could also add multiple levels,lol.
A UICollisionBehavior needs a delegate that adopts the UICollisionBehaviorDelegate protocol. This delegate has a method collisionBehavior that is called whenever a collision is detected.
For example:
var collisionBehavior: UICollisionBehavior! // create a UICollisionBehavior as you have done
collisionBehavior.addItem(ball) // add your items to it
collisionBehavior.addItem(block) // (faster to do this in the init step with `items` argument)
collisionBehavior.collisionDelegate = myDelegate // give it a delegate which adopts UICollisionBehaviorDelegate
dynamicAnimator.addBehavior(collisionBehavior) // add the behavior to your animator
Then implement func collisionBehavior for your delegate class. Often people just use the UIViewController itself as the delegate, so the above line would read collisionBehavior.collisionDelegate = self.
See "Making objects respond to collisions" here for a good and short tutorial: http://www.raywenderlich.com/76147/uikit-dynamics-tutorial-swift.
Related
I'm making a maze app, that generates a new maze after one is completed. Obviously, I can turn the walls (each having their own physics and sprite stored in their spot on an array) invisible by doing what I did in my code wall[location].isHidden = true ,but I couldn't find a way to do the same with the physics body bound to the wall, creating an invisible wall. Is there a simple way to do that without setting it to nil?
Just to clarify, it's not that I don't want it to be dynamic, I just want the entire thing to not exist while that maze is being solved.
Also the only reason I wouldn't want to set it to nil is because I don't know how to restore its old physics body in an easy way.
The preferred way would be to physically remove your wall from your node tree. This will increase performance since the system does not have to check unnecessary nodes.
Just make sure that your wall is being retained when you remove it so that you can readd it when needed.
wall[location].removeFromParent()
...
scene.addChild(wall[location])
Sure
just set
isDynamic = false
wall.physicsBody?.collisionBitMask = 0
wall.physicsBody?.contactTestBitMask = 0
This pretty much isolates the SKPhysicsBody from the rest of the system. Do you have any SKJoints connected to your nodes with the bodies ? This can be awkward if you do.
or you can set it to nil
wall.physicsBody? = nil
then just make a method to set it again when your ready
func addPhysics() {
wall.physicsBody = SKPhysicsBody(circleOfRadius: 5)
wall.isDynamic = true
wall.physicsBody?.collisionBitMask = whatever
wall.physicsBody?.collisionBitMask = whatever
wall.physicsBody?.contactTestBitMask = whatever
}
If you do set to nil and then restore it make sure you do this at either the start or the end of the gameLoop.
In either of these override methods in the gameScene
override func update(_ currentTime: TimeInterval) {
}
override func didFinishUpdate() {
}
Hope this helps
You already have an answer, but another approach might be to subclass SKSpriteNode with one extra property called inactivePhysicsBody (or similar) which is an 'SKPhysicsBody'.
When you want to 'turn off' the physics for that node, set the inactivePhysicsBody property to physicsBody and then set physicsBody to nil. You can then easily restore the physics body when necessary.
Or you could add methods to the new class to do the same:
wall.activatePhysicsBody()
wall.inactPhysicsBody()
Give the object that owns the sprite an 'extra' reference to the sprite's physics body, e.g.:
init(sprite: SKSpriteNode) {
self.sprite = sprite
self.physicsBody = sprite.physicsBody
super.init()
}
Then you can set self.sprite.physicsBody to either self.physicsBody or nil to turn the object's physics on / off. Personally I'd avoid subclassing here but YMMV.
Say you have a simple square UIView sitting in the view of a view controller:
#IBOutlet var redSquare: UIView!
Imagine it can move up/down left/right slightly. TBC it simply stays in the same totally normal 3D plane, it remains an ordinary "square of red" on the glass screen; it does not even rotate.
In iOS (1) you can access the motion manager along the lines...
let mm: CMMotionManager = CMMotionManager()
mm.startAccelerometerUpdates()
and you can (2) make an object "be attached to a spring" along the lines...
sumthin.physicsBody.dynamic = true
sumthin.physicsBody.affectedByGravity = false
sumthin.physicsBody.mass = 1
finally (3) you can apply force, to an object, based on the accelerometer with something like...
f = mm.accelerometerData
sumthin.physicsBody.applyForce(
CGVectorMake(CGFloat(f.acceleration.x), CGFloat(f.acceleration.y)))
GOAL: As you shake the phone around ...
the red square will simply "wibble" up/down, left-right a bit (perhaps 10 points or so, just to be clear); when you stop moving your hand it will settle as if on a spring back to the original position.
How do you do this, with an ordinary UIView?
I am trying to make an app where you can use Stickers like on Snapchat and Instagram. It fully worked to find a technique, that adds the images, but now I want that if you swipe the object around the object changes its position (I also want to make the scale / rotate function).
My code looks like this:
#objc func StickerLaden() {
for i in 0 ..< alleSticker.count {
let imageView = UIImageView(image: alleSticker[i])
imageView.frame = CGRect(x: StickerXScale[i], y:StickerYScale[i], width: StickerScale[i], height: StickerScale[i])
ImageViewsSticker.append(imageView)
ImageView.addSubview(imageView)
imageView.isUserInteractionEnabled = true
let aSelector : Selector = "SlideFunc"
let slideGesture = UISwipeGestureRecognizer(target: self, action: aSelector)
imageView.addGestureRecognizer(slideGesture)
}
}
func SlideFunc(fromPoint:CGPoint, toPoint: CGPoint) {
}
Here are the high-level steps you need to take:
Add one UIPanGestureRecognizer to the parent view that has the images on it.
Implement UIGestureRecognizerDelegate methods to keep track of the user touching and releasing the screen.
On first touch, loop through all your images and call image.frame.contains(touchPoint). Add all images that are under the touch point to an array.
Loop through the list of touched images and calculate the distance of the touch point to the center of the image. Chose the image whose center is closest to the touched point.
Move the chosen image to the top of the view stack. [You now have selected an image and made it visible.]
Next, when you receive pan events, change the frame of the chosen image accordingly.
Once the user releases the screen, reset any state variables you may have, so that you can start again when the next touch is done.
The above will give you a nicely working pan solution. It's a good amount of things you need to sort out, but it's not very difficult.
As I said in my comment, scale and rotate are very tricky. I advise you to forget that for a bit and first implement other parts of your app.
I’m trying to animate specific parts of a SCNScene object in SceneKit (in my case I want to animate fingers of a hand). I import the .dae (COLLADA) file easily from Blender with the respective bones to generate articulation on the model.
override func viewDidLoad() {
super.viewDidLoad()
var scene = SCNScene(named: "hand.dae")!
sceneView.scene = scene
sceneView.allowsCameraControl = true
sceneView.autoenablesDefaultLighting = true
sceneView.backgroundColor = UIColor.lightGrayColor()
}
My goal is to animate those bones on iOS with user generated values between 0 and 1. Imagine a UISlider where you scroll back and forth and see the specific finger move depending on the value of the slider.
This is needed animation screenshot
Image with the animation pretended
I’ve tried animate the model by calling an animation file like the Apple’s Fox example:
private var indexFingerAnimation: CAAnimation!
indexFingerAnimation = CAAnimation.animationWithSceneNamed(“move_index_finger.dae”)
indexFingerAnimation = false
indexFingerAnimation = 0.3
indexFingerAnimation = 0.3
indexFingerAnimation = Float.infinity
The problem is that’s a Global animation instead of just the index finger. Besides it’s always a ‘pre-defined’ animation instead of an animation controlled by user input. Ultimately I want to mix animations (e.g. move index finger and thumb at the same time revealing gestures)
Is this possible? I’m struggling because I can’t figure out how to manipulate specific parts of the mesh. I’m starting to study MetalKit but it’s not clear to me that’s the solution.
Any help would be really appreciated.
I have never tried two animations at the same time
but I can rotate SCNNode in dae file with two or more animate
You must set pivot point and group them together
I'm new to IOS and Swift, so I've started by porting Apple's Accelerometer example code to Swift.
This was all quite straightforward. Since the Accelerometer API has been deprecated, I used Core Motion instead, and it works just fine. I also switched to a storyboard.
The problem I have is that my layer delegate is only rarely called. It will go for a few minutes and never get called, and then it will get called 40 times a second, and then go back to not being called. If I context switch, the delegate will get called, and one of the sublayers will be displayed, but there are 32 sublayers, and I've yet to see them all get drawn. What's drawn seems to be fine - the problem is just getting the delegate to actually get called when I call setNeedsDisplay(), and getting all of the sublayers to get drawn.
I've checked to be sure that each sublayer has the correct bounds and frame dimensions, and I've checked to make sure that setNeedsDisplay() gets called after each accelerometer point is acquired.
If I attach an instrument, I see that the frame rate is usually zero, but occasionally it will be some higher number.
My guess is that the run loop isn't cycling. There's actually nothing in the run loop, and I'm not sure where to put one. In the ViewDidLoad delegate, I set up an update rate for the accelerometer, and call a function that updates the sublayers in the view. Everything else is event driven, so I don't know what I'd do with a run loop.
I've tried creating CALayers, and adding them as sublayers. I've also tried making the GraphViewSegment class a UIView, so it has it's own layer.
The version that's written in Objective C works perfectly reliably.
The way that this application works, is that acceleration values show up on the left side of the screen, and scroll to the right. To make it efficient, new acceleration values are written into a small sublayer that holds a graph for 32 time values. When it's full, that whole sublayer is just moved a pixel at a time to the right, and a new (or recycled) segment takes its place at the left side of the screen.
Here's the code that moves unchanged segments to the right by a pixel:
for s: GraphViewSegment in self.segments {
var position = s.layer.position
position.x += 1.0;
s.layer.position = position;
//s.layer.hidden = false
s.layer.setNeedsDisplay()
}
I don't think that the setNeedsDisplay is strictly necessary here, since it's called for the layer when the segment at the left gets a new line segment.
Here's how new layers are added:
public func addSegment() -> GraphViewSegment {
// Create a new segment and add it to the segments array.
var segment = GraphViewSegment(coder: self.coder)
// We add it at the front of the array because -recycleSegment expects the oldest segment
// to be at the end of the array. As long as we always insert the youngest segment at the front
// this will be true.
self.segments.insert(segment, atIndex: 0)
// this is now a weak reference
// Ensure that newly added segment layers are placed after the text view's layer so that the text view
// always renders above the segment layer.
self.layer.insertSublayer(segment.layer, below: self.text.layer)
// Position it properly (see the comment for kSegmentInitialPosition)
segment.layer.position = kSegmentInitialPosition;
//println("New segment added")
self.layer.setNeedsDisplay()
segment.layer.setNeedsDisplay()
return segment;
}
At this point I'm pretty confused. I've tried calling setNeedsDisplay all over the place, including the owning UIView. I've tried making the sublayers UIViews, and I've tried making them not be UIViews. No matter what I do, the behavior is always the same.
Everything is set up in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
pause.possibleTitles?.setByAddingObjectsFromArray([kLocalizedPause, kLocalizedResume])
isPaused = false
useAdaptive = false
self.changeFilter(LowpassFilter)
var accelerometerQueue = NSOperationQueue()
motionManager.accelerometerUpdateInterval = 1.0 / kUpdateFrequency
motionManager.startAccelerometerUpdatesToQueue(accelerometerQueue,
withHandler:
{(accelerometerData: CMAccelerometerData!, error: NSError!) -> Void in
self.accelerometer(accelerometerData)})
unfiltered.isAccessibilityElement = true
unfiltered.accessibilityLabel = "unfiltered graph"
filtered.isAccessibilityElement = true
filtered.accessibilityLabel = "filtered graph"
}
func accelerometer (accelerometerData: CMAccelerometerData!) {
if (!isPaused) {
let acceleration: CMAcceleration = accelerometerData.acceleration
filter.addAcceleration(acceleration)
unfiltered!.addPoint(acceleration.x, y: acceleration.y, z: acceleration.z)
filtered!.addPoint(filter.x, y: filter.y, z: filter.z)
//unfiltered.setNeedsDisplay()
}
}
Any idea?
I quite like Swift as a language - it takes the best parts of Java and C#, and adds some nice syntactic sugar. But this is driving me spare! I'm sure it's some little thing that I've overlooked, but I can't figure out what.
Since you've created a new NSOperationQueue for your accelerometer updates handler, everything that handler calls is also running in a separate queue, sequestered from the main run loop. I'd suggest either running that handler on the main thread NSOperationQueue.mainQueue() or moving anything that could update the UI back to the main thread via a block on the main queue:
NSOperationQueue.mainQueue().addOperationWithBlock {
// do UI stuff here
}