I'm developing an app showing a 3D human body - in the SceneKit editor, I've set up the camera pointing to the model.
As I did set sceneView.allowsCameraControl = true, the user can move around the object, zoom in and zoom out with simple gestures.
In order to move vertically, I've implemented an UISlider - I don't want the user to pan vertically with two finger gestures.
In the slider action, I do the following:
#IBAction func sliderDidMove(_ sender: UISlider) {
cameraNode = sceneView.scene?.rootNode.childNode(withName: "camera", recursively: true)
let vert = 1.5*(0.5 - sender.value)
cameraNode?.position.y = posY + vert
}
This works fine when I change the slider position just after loading my ViewController. But as soon as I rotate or zoom the model by gestures, the slider no longer shows effect - thus I can no longer change the position of my camera node programmatically.
Has anyone an idea how I can fix this?
Related
I've got a UIView laying right above all others. This UIView has a UIPanGestureRecognizer to drag it around of the screen. If the User stopped panning, the View moves to a calculated Position which is the left screen bounds or the right ones. This works quite good but now I want to consider the velocity the View moved around the screen and when the user lifts his Finger off the screen, the UIView should not stop immediately to move to the endpoint, instead it should do it with a curve.
Edit 1
I think I expressed myself wrongly. I meant that the View should move the direction the user dragged it to but then it should make a curve and move to the desired position. Think of it like gravity. The user will drag the View with a speed from the left bottom corner to the middle of the screen and lifts immediately his finger. Then the View should not stop, else it should move in the last direction und slowly move to the desired location, which is in this case the right bottom corner.
This is the code I already have:
private void DidPan()
{
CGPoint translation = PanGesture.TranslationInView(this.Superview);
CGPoint velocity = PanGesture.VelocityInView(this.Superview);
if (PanGesture.State == UIGestureRecognizerState.Changed)
{
this.Center = new CGPoint(lastLocation.X + translation.X, lastLocation.Y + translation.Y);
}
else if (PanGesture.State == UIGestureRecognizerState.Ended)
{
// Calculates the new position the view will be moving to
var newLocation = new CGPoint(SetX(Superview.Bounds), SetY(Superview.Bounds));
// Animate it to the desired location
UIView.Animate(0.3, () =>
{
this.Center = newLocation;
}, null);
}
}
As you can see we already have the velocity but I don't know how to continue from here. Hopefully someone has the right starting point from here and could help me.
Thanks a lot!
I think UIKit Dynamics can meet your requirement.
refer to this Blog : https://blog.xamarin.com/drag-drop-and-snap-with-uikit-dynamics/
If I am using the default camera control in SceneKit, is there a way to disable the two fingered pan function and leave the others as they are?
I want users to be able to rotate and zoom an object, but not move it at all.
The sceneView already has a UIPanGestureRecognizer attached.
If you modify its maximumNumberOfTouches to be 1, you can disable the pan gesture.
for reco in sceneView.gestureRecognizers! {
if let panReco = reco as? UIPanGestureRecognizer {
panReco.maximumNumberOfTouches = 1
}
}
No. You'll have to make your own camera and use gesture recognizers.
I am currently working a spriteKit 2D game using Objective C. It's a tile based game similar to superMario. I am having a problem with my Camera.
I added a camera that is following my player, but when the player gets closer to the edges of the scene, the camera goes out of bounds. Setting up the camera was easy.
-(void)didMoveToView:(SKView *)view {
//add Camera
mainCamera = [SKCameraNode node];
self.camera = mainCamera;
}
then in :
-(void)update:(NSTimeInterval)currentTime {
[super update: currentTime];
mainCamera.position = avatar.position;
}
However i need to add Constraints so that the camera stays within the container of the scene.
Add a physics body to the camera, make it a rectangle the size of the scene.
Since you are doing your update of the camera in the beginning of the update, the physics engine will move the camera prior to being drawn.
make sure you set the restitution to 0 to avoid bouncing
So I'm hoping to learn about making a scrollview of icons (kinda like a menu) in Spritekit, using Swift. I can't find any good resources or tutorials that don't charge.
I have my basic app set up with a ViewController and my scene.
I'm hoping to have a scrollview that's say 2 or 3 times longer than the height of the screen, where I can scroll up and down and view different icons.
I hope this would be a good implementation, so I can programmatically set all the icons using x/y coordinates.
My viewdidLoad:
override func didMoveToView(view: SKView) {
/* Setup your scene here */
addChild(worldNode)
//create Scrollview
//for loop to adding icons from top to bottom of scrollview
//name sprites
//use touch location so allow sprites to be clickable
//will just adjust alpha of icon for testing purposes later
}
This is really all I'm looking at achieving at the moment, if you can imagine, something like this:
Some of the questions that I have seen here seem to go in far more detail than I imagine I need, and are too complex for me at this stage, or they are not in swift...
How can I include a scrollview?
Thank you in advance :)
If your entire UI is a scene built with SpriteKit, you may want to consider building it as if it were a scrolling scene with a camera. Here's an example in a gameplay context
Basically it could work like this:
var sceneCam: SKCameraNode! //declare your camera variable in your scene class
Then, in your didMoveToView function:
sceneCam = SKCameraNode() //initialize your camera
//scaleAsPoint lets you zoom the camera in and out
sceneCam.scaleAsPoint = CGPoint(x: 0.25, y: 0.25)
camera = sceneCam //set the scene's camera
addChild(sceneCam) //add camera to scene
//position the camera on the scene.
sceneCam.position = CGPoint(x: frame.center.x, y: frame.center.y)
Now, in your touchesMoved gesture handler, when you detect the pan, you can adjust the camera position to match the pan gesture translation.
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.anyObject() as UITouch
let positionInScene = touch.locationInNode(self)
let previousPosition = touch.previousLocationInNode(self)
let translation = CGPoint(x: positionInScene.x - previousPosition.x, y: positionInScene.y - previousPosition.y)
sceneCam.position = CGPoint(x: sceneCam.position.x - translation.x, y: sceneCam.position.y - translation.y)
}
The other crucial part of the setup is just to make your scene the right size for your menu. This is a similar concept to your content size on a scrollview. You might also have some work to do to implement paging or similar UIScrollView features if you desire those. But if you just need a simple scrolling view, you could do it with a camera.
If you ONLY want UIKit elements on this view, it should probably just be it's own VC without a scene. You could even create this menu view controller and place it in a container on your game scene - that way you'd be using only UIKit components in your menu vc, but you can still use it in a SpriteKit setting.
I have a UIView in my ViewController that users can move around the screen, rotate and zoom in and out. All with UIGestureRecognizers.
The problem I have is that the UIView contains a UILabel and after zooming the label is very blurry.
Here is the code for the zoom:
#IBAction func handlePinch(recognizer : UIPinchGestureRecognizer) {
recognizer.view!.transform = CGAffineTransformScale(recognizer.view!.transform, recognizer.scale, recognizer.scale)
recognizer.scale = 1
}
Also, when the user leaves the screen I am saving the new location and transformation of the UIView like this:
func saveViewValues() {
var userDefaults:NSUserDefaults = NSUserDefaults.standardUserDefaults()
userDefaults.setObject(NSStringFromCGPoint(self.theView.center), forKey:kViewCenter)
userDefaults.setObject(NSStringFromCGAffineTransform(self.theView.transform), forKey:kViewTransform)
userDefaults.synchronize()
}
This way when I reload the page I can use these values to reset the UIView to how the user last had it.
All works fine however the UILabel is very blurry if the user zooms in on the view.
Is there a way I can make the text crisp, even when zoomed? And how do I save this value like the others so that it can be put back afterwards and be crisp straight away?
Hope this makes sense.