What method do I have to use for calculating the distance that CGPoint travelled from its old position to new position?
var point: CGPoint = CGPointMake(x:50.0, y:50.0)
...and there is a method for dragging a point with LMB:
func mouseDragged(event: NSEvent) {
var pointDragged = self.convertPoint(event.locationInWindow, fromView: nil)
}
Use Pythagorean theorem and mouseLocation = event.mouseLocation() where event in your case is of type NSEvent.
let point1: CGPoint = CGPoint(x: 2.0, y: 9.0)
let point2: CGPoint = CGPoint(x: 4.0, y: 13.0)
let xDist = (point1.x - point2.x)
let yDist = (point1.y - point2.y)
let distance = sqrt((xDist * xDist) + (yDist * yDist))
print(distance)
point1 would be your starting position and you can obtain the position where the user clicked - point2, from the NSEvent in your mouseDragged function:
mouseLocation = event.mouseLocation()
point2 = CGPointMake(mouseLocation.x, mouseLocation.y)
Related
I want to use a CGVector for a SKAction.move, which moves one SKSpriteNode towards another SKSpriteNode.
I want to use this code:
let point: CGPoint = CGPoint(x: CGFloat(arc4random_uniform(UInt32(size.width))), y: CGFloat(arc4random_uniform(UInt32(size.height))))
let object: SKSpriteNode = SKSpriteNode(color: NSColor.red, size: CGSize(width: 10, height: 10))
object.position = CGPoint(x: 50, y: 50)
addChild(object)
object.run(SKAction.move(by: CGVector(), duration: 2.5)) // <- Vector from `object.position` to `point`
You need to use vector math
so looking at your code you want to use something like this:
let P1 = point
let P2 = object.position
//Find offset
let offset = P1 - P2
//Set X and Y distance to move
let distX = CGFloat(offset.x)
let distY = CGFloat(offset.y)
//set vector
let vec = CGVector(dx: distX, dy: distY)
//Set action
object.run(SKAction.move(by: vec, duration: 2.4)
Let me know how you get on
I m trying to create an effect that dice thrown by hand when I tapped to screen or shake. in my reference code there is rotation effect so that dice faces changes randomly. Im trying to make this code while dice is rotating it must be fall. I mean it will fall from a predefined coordinate y to coordinate y 0. Any idea will be apriciated.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
let touchLocation = touch.location(in: sceneView)
let results = sceneView.hitTest(touchLocation, types: .existingPlaneUsingExtent)
if let hitResult = results.first{
let diceScene = SCNScene(named: "art.scnassets/diceCollada.scn")!
if let diceNode = diceScene.rootNode.childNode(withName: "Dice", recursively: false){
diceNode.position = SCNVector3(
hitResult.worldTransform.columns.3.x,
hitResult.worldTransform.columns.3.y + diceNode.boundingSphere.radius,
hitResult.worldTransform.columns.3.z)
let randomX = Float(arc4random_uniform(4) + 1) * (Float.pi/2)
let randomZ = Float(arc4random_uniform(4) + 1) * (Float.pi/2)
diceNode.runAction(
SCNAction.rotateBy(x: CGFloat(randomX * 3),
y: 0,
z: CGFloat(randomZ * 3),
duration: 0.5)
)
sceneView.scene.rootNode.addChildNode(diceNode)
}
print(hitResult)
}
}
}
I created this game in Spite Kit in Swift. I create a ship that I can determine its location be touching above or underneath the location of the ship. The ship will move towards the Y axis where I placed my finger. However at this point the ship will only move by a specific amount, in this case 30 or -30. How can I rewrite this code so the ship will keep on moving until my release of my finger?
Thanks!
func addShip() {
// Initializing spaceship node
ship = SKSpriteNode(imageNamed: "spaceship")
ship.setScale(0.45)
ship.zRotation = CGFloat(-M_PI/2)
// Adding SpriteKit physics body for collision detection
ship.physicsBody = SKPhysicsBody(rectangleOfSize: ship.size)
ship.physicsBody?.categoryBitMask = UInt32(shipCategory)
ship.physicsBody?.affectedByGravity = false
ship.physicsBody?.dynamic = true
ship.physicsBody?.contactTestBitMask = UInt32(obstacleCategory)
//NEWLY ADDED
ship.physicsBody?.contactTestBitMask = coinCategory
ship.physicsBody?.collisionBitMask = 0
ship.name = "ship"
ship.position = CGPointMake(120, 160)
self.addChild(ship)
actionMoveUp = SKAction.moveByX(0, y: 30, duration: 0.2)
actionMoveDown = SKAction.moveByX(0, y: -30, duration: 0.2)
}
i don't like to use SKActions for these types of things because they're really meant for one off simple animations.. However, it may be appropriate depending your game's requirements..
if you insist on using SKActions then instead of doing moveBy, why don't you just use moveTo utilizing touch location? The only thing you'd have to do is come up with a ratio based on the distance your sprite needs to travel so that your animation duration is correct. example:
let curPoint = self.sprite.position
let destinationPoint = touch.position
let diffPoint = CGPoint(
x: curPoint.x - destinationPoint.x,
y: curPoint.y - destinationPoint.y
)
let distance = sqrt(pow(diffPoint.x, 2) + pow(diffPoint.y, 2))
let durationRatio = distance / CGFloat(600)
let moveAction = SKAction.moveTo(point, duration: NSTimeInterval(durationRatio))
self.sprite.runAction(moveAction, withKey: "move")
then when you want to cancel the movement. in touchesEnded you do
self.sprite.removeActionForKey("move")
using physics velocity
let curPoint = CGPoint(x: 10, y: 10)
let destinationPoint = CGPoint(x: 100, y: 100)
let diffPoint = CGPoint(
x: curPoint.x - destinationPoint.x,
y: curPoint.y - destinationPoint.y
)
let distance = sqrt(pow(diffPoint.x, 2) + pow(diffPoint.y, 2))
let normalizedVector = CGPoint(
x: diffPoint.x / distance,
y: diffPoint.y / distance
)
let speed = CGFloat(100)
let velocity = CGVector(
dx: normalizedVector.x * speed,
dy: normalizedVector.y * speed
)
self.sprite.physicsBody!.velocity = velocity
in touchesEnded you do
self.sprite.physicsBody!.velocity = CGVector(dx: 0.0, dy: 0.0)
I’m trying to drag a chess piece with the mouse/trackpad using Scene Kit. All objects (board and pieces) are children of the root node, loaded from a Collada file.
I found a helpful description of the process elsewhere on Stack Overflow. Using that description I wrote the initial version of the code below. My problem is the disparity between the click coordinates and the piece node position — their coordinates are different orders of magnitude. I remain unclear on how to match them up — put them in the “same universe”. I’ve tried a number of suggestions from the Apple forums along with flights of fancy of my own.
Here’s my current attempt, mostly reverted back to the original version based on the link above, along with logged coordinate values along the way. The result is that dragging a chess piece causes it to abruptly jump off screen:
- (NSPoint)
viewPointForEvent: (NSEvent *) event_
{
NSPoint windowPoint = [event_ locationInWindow];
NSPoint viewPoint = [self.view convertPoint: windowPoint
fromView: nil];
return viewPoint;
}
- (SCNHitTestResult *)
hitTestResultForEvent: (NSEvent *) event_
{
NSPoint viewPoint = [self viewPointForEvent: event_];
CGPoint cgPoint = CGPointMake (viewPoint.x, viewPoint.y);
NSArray * points = [(SCNView *) self.view hitTest: cgPoint
options: #{}];
return points.firstObject;
}
- (void)
mouseDown: (NSEvent *) theEvent
{
SCNHitTestResult * result = [self hitTestResultForEvent: theEvent];
SCNVector3 clickWorldCoordinates = result.worldCoordinates;
log output: clickWorldCoordinates x 208.124578, y -12827.223365, z 3163.659073
SCNVector3 screenCoordinates = [(SCNView *) self.view projectPoint: clickWorldCoordinates];
log output: screenCoordinates x 245.128906, y 149.335938, z 0.985565
// save the z coordinate for use in mouseDragged
mouseDownClickOnObjectZCoordinate = screenCoordinates.z;
selectedPiece = result.node; // save selected piece for use in mouseDragged
SCNVector3 piecePosition = selectedPiece.position;
log output: piecePosition x -18.200000, y 6.483060, z 2.350000
offsetOfMouseClickFromPiece.x = clickWorldCoordinates.x - piecePosition.x;
offsetOfMouseClickFromPiece.y = clickWorldCoordinates.y - piecePosition.y;
offsetOfMouseClickFromPiece.z = clickWorldCoordinates.z - piecePosition.z;
log output: offsetOfMouseClickFromPiece x 226.324578, y -12833.706425, z 3161.309073
}
- (void)
mouseDragged: (NSEvent *) theEvent;
{
NSPoint viewClickPoint = [self viewPointForEvent: theEvent];
SCNVector3 clickCoordinates;
clickCoordinates.x = viewClickPoint.x;
clickCoordinates.y = viewClickPoint.y;
clickCoordinates.z = mouseDownClickOnObjectZCoordinate;
log output: clickCoordinates x 246.128906, y 0.000000, z 0.985565
log output: pieceWorldTransform =
m11 = 242.15889219510001, m12 = -0.000045609300002524833, m13 = -0.00000721691076126, m14 = 0,
m21 = 0.0000072168760805499971, m22 = -0.000039452697396149999, m23 = 242.15890446329999, m24 = 0,
m31 = -0.000045609300002524833, m32 = -242.15889219510001, m33 = -0.000039452676995750002, m34 = 0,
m41 = -4268.2349924762348, m42 = -12724.050221935429, m43 = 4852.6652710104272, m44 = 1)
SCNVector3 newPiecePosition;
newPiecePosition.x = offsetOfMouseClickFromPiece.x + clickCoordinates.x;
newPiecePosition.y = offsetOfMouseClickFromPiece.y + clickCoordinates.y;
newPiecePosition.z = offsetOfMouseClickFromPiece.z + clickCoordinates.z;
log output: newPiecePosition x 472.453484, y -12833.706425, z 3162.294639
selectedPiece.position = newPiecePosition;
}
Up to this point, I’ve gotten a lot of interesting and useful comments and advice. But I’ve realized that to move forward, I’m probably going to need a working code sample which shows the secret sauce which allows clicks and vectors to exist in the “same universe”.
You don't need to "play around" to find the origin. You can do code like this:
let originZ = sceneView.projectPoint(SCNVector3Zero).z
let viewLocation = /* Location in SCNView coordinate system */
let viewLocationWithOriginZ = SCNVector3(
x: Float(viewLocation.x), // X & Y are in view coordinate system
y: Float(viewLocation.y),
z: originZ // Z is in scene coordinate system
)
var position = sceneView.unprojectPoint(viewLocationWithOriginZ)
/* "position" is in the scene's coordinate system with z of 0 */
You have to use the method unprojectPoint. The key is to play around with z-values to find the correct depth of your scene relative to your click point (at least that's how I understood it). I had a similar problem trying to do touch-dragging on iOS. This is what I did to solve it:
let convertedTranslation = scnView.unprojectPoint(SCNVector3Make(Float(translation.x), Float(translation.y), 1.0))
Notice that the z-value for the method is 1. From the documentation, you can see that it can be a value between -1 and 1. -1 and 0 did not work for me, but 1 did.
I followed a guide about dragging multiple images on screen with gesture recognizers however i have a problem. I want the images to be dragged inside the bounds of the screen which maybe iPad or iPhone. The code i am using estimates the image's center so half of the image is getting off screen when reaching the bounds. Could you please help me with this?
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
CGPoint translation = [recognizer translationInView:self.view];
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
if (recognizer.state == UIGestureRecognizerStateEnded) {
CGPoint velocity = [recognizer velocityInView:self.view];
CGFloat magnitude = sqrtf((velocity.x * velocity.x) + (velocity.y * velocity.y));
CGFloat slideMult = magnitude / 200; //original divider 200
NSLog(#"magnitude: %f, slideMult: %f", magnitude, slideMult);
float slideFactor = 0.1 * slideMult /4; // Increase for more of a slide (original doesn't have a divider)
CGPoint finalPoint = CGPointMake(recognizer.view.center.x + (velocity.x * slideFactor),
recognizer.view.center.y + (velocity.y * slideFactor));
finalPoint.x = MIN(MAX(finalPoint.x, 0), self.view.bounds.size.width);
finalPoint.y = MIN(MAX(finalPoint.y, 0), self.view.bounds.size.height);
[UIView animateWithDuration:slideFactor*2 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
recognizer.view.center = finalPoint;
} completion:nil];
}
}
Well, i managed to find out the solution to my problem. I divided the image width and height by two. In my case the results were 100 and 70. The rest can be seen in code box :
finalPoint.x = MIN(MAX(finalPoint.x, 70), self.view.bounds.size.width-70);
finalPoint.y = MIN(MAX(finalPoint.y, 100), self.view.bounds.size.height-100);