I've added a Quad to the stage, which I have made draggable.
However when I touch the draggable object and start to drag it, the object is "centered" to my finger touching (the x and y coordinates of my finger) the screen? As if the center of the Quad snaps to my touch point??? So touch the corners of the Quad will make it "snap" to my touch point => moving slightly.
So what I really want to know is, is it possible to "grab/drag" an object without having it adjusting and centering to my actual touch point? Ie. so that I can drag the object touching one of the corners of the squared Quad object?
My code can be seen below:
public function Game() {
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
private function onAdded (e:Event):void {
var q:Quad = new Quad(200,200);
q.x = 100;
q.y = 100;
q.addEventListener(TouchEvent.TOUCH, touchHandler);
addChild(q);
}
private function touchHandler(e : TouchEvent) : void
{
var touch:Touch = e.getTouch(stage);
var position:Point = touch.getLocation(stage);
var target:Quad = e.target as Quad;
if(touch.phase == TouchPhase.MOVED ){
target.x = position.x - target.width/2;
target.y = position.y - target.height/2;
trace("x:" + target.x + " y:" + target.y)
}
}
Cheers
Of course you can. It's normal to snap, as you do this:
target.x = position.x - target.width/2;
target.y = position.y - target.height/2;
The thing to do here is to save the location at which the object was touched. This way you can calculate the difference between it's origin point and the actual point that you've touched it. Let's say you have a square of 100x100, your origin point is at 0x0, and you touch it at 50x50.
If you move your finger exactly one pixel (just for explanation), the object should actually move ONE pixel. Logic is this:
save touch point only once (50x50)
every time there is a movement, check location
make calculation: current location (51x51) - initial point (50x50) = difference (1x1)
move the object with the difference: 0x0 + 1x1 = 1x1, which means the object will move one pixel
Right. Using:
touch = event.getTouch(stage);
touchX = touch.globalX;
touchY = touch.globalY;
And calculating the offset (x and y) did the trick.
Related
I have a game where I'm moving square Blocks on a top layer overtop circles underneath, which are non-moveable. So when the dragging of a block ceases, I want to run a check or an if statement to see if the block I'm moving (myBlocks[objectDragging]) is within x amount of pixels of the center of my circle (myCircles[objectDragging]). objectDragging is just getting the tag of the image clicked. The matchable circle will have the same tag. Everything is working fine, I just cannot figure out how to check if the block I'm dropping (it's center point) is within so many pixels of the circles center point.
Some of what I'm working with:
var myBlocks = [UIImageView]()
var myCircles = [UIImageView]()
let objectDragging = recognizer.view?.tag
if myBlocks[objectDragging!].center.x == myCircles[objectDragging!].center.x {
...
} //this checks for an exact match of center.x where-as I want to check
//if the center.x for myBlocks[objectDragging!] is <= we'll say,
//25, pixels of the myCircles[objectDragging!].center.x
Discussion here to find distance between two CGPoints:
How to find the distance between two CG points?
per Lucius (answer 2)
You can use the hypot() or hypotf() function to calculate the
hypotenuse. Given two points p1 and p2:
CGFloat distance = hypotf(p1.x - p2.x, p1.y - p2.y);
sub in your myBlocks.center and myCircles.center for p1 and p2 and then
if distance < 25 {
...
}
I essentially want the "sprites" to collide when they stick together. However, I don't want the "joint" to be rigid; I essentially want the sprites to be able to move around as long as they are in contact with each other. Imagine two circles connected, and you can move one circle around the other, as long as it remains in contact.
I found this question: How to make one body stick to another moving object in SpriteKit and a lot of other resources that explain how to make sprites stick upon collision, but they all use SKJoints, which are rigid are not really flexible.
I guess another way to phrase it would be to say that I want the sprites to stick, but I want them to be able to "slide" on each other.
Well, I can think of one workaround, but this wouldn't work with non-normal polygons.
Sticking (pun unintended) with your circles example, what if you lock the position of the circle?
let circle1 = center circle
let circle2 = movable circle
Knowing the width of both circles, you can place in the update function that the position should be exactly the distance of:
((circle1.frame.width / 2) + (circle2.frame.width / 2))
If you're up to it, here's some code to help you on your way.
override func update(currentTime: CFTimeInterval) {
{
let distance = hypotf(Float(circle1.position.x - circle2.position.x), Float(circle1.position.y - circle2.position.y))
//calculate circle distances from each other
let radius = ((circle1.frame.width / 2) + (circle2.frame.width / 2))
//distance of circle positions
if distance != radius
{
//if distance is less or more than radius
let pointA = circle1.position
let pointB = circle2.position
let pointC = CGPointMake(pointB.x + 2, pointB.y)
let angle_ab = atan2(pointA.y - pointB.y, pointA.x - pointB.x)
let angle_cb = atan2(pointC.y - pointB.y, pointC.x - pointB.x)
let angle_abc = angle_ab - angle_cb
//get angle of circles from each other using atan2
let vectorx = cos(angle_abc)
let vectory = sin(angle_abc)
//convert angle into vectors
let x = circle1.position.x + radius * vectorx
let y = circle1.position.y + radius * vectory
//get new coordinates from vector, radius and center circle position
circle2.position = CGPointMake(x, y)
//set new position
}
}
Well you need to write code to make sure the movable circle, is well movable.
But, this should work.
I haven't tested this yet though, and I haven't even learned geometry let alone trig in school yet.
If I'm reading your question as you intended it, you can still use joints- just create actions with Inverse Kinematic constraints that allow rotation and translation around the contacting circles' joint.
https://developer.apple.com/library/prerelease/ios/documentation/SpriteKit/Reference/SKAction_Ref/index.html#//apple_ref/doc/uid/TP40013017-CH1-SW72
I have a subclass of SKNode which consists of a few sprites that make up a player. I would like the "camera" to center on this node (Always have the player in the center). Now before you down vote this for it being a duplicate, hear me out. The Apple documents suggest making the player node completely static, and instead moving around a camera node. However in my case I'm applying multiple properties of physics to my character, including velocity impulses. My first thought would be to just apply these impulses to the camera node itself, however this has become impossible due to the fact that the character has a small soft-body physics engine on it. I'm applying velocity to it like so:
player.primaryCircle.physicsBody!.velocity = CGVector(dx: player.primaryCircle.physicsBody!.velocity.dx+relVel.dx*rate, dy: player.primaryCircle.physicsBody!.velocity.dy+relVel.dy*rate)
I managed to get it to partially work with the following code:
override func didSimulatePhysics() {
self.player.position = player.primaryCircle.position
self.camera.position = player.position
centerOnNode(camera)
}
func centerOnNode(node: SKNode) {
let cameraPositionInScene: CGPoint = node.scene!.convertPoint(node.position, fromNode: node.parent!)
node.parent!.position = CGPoint(x:node.parent!.position.x - cameraPositionInScene.x, y:node.parent!.position.y - cameraPositionInScene.y)
}
However that didn't 100% work, as seen here: (It should be focused on the red circle)
http://gyazo.com/b78950e6cc15b60f390cd8bfd407ab56
As you can see, the world/map is moving, however it doesn't seem to be moving fast enough to center the player in the middle. (And note that the "Unamed" text is at a fixed spot on the screen -- That's why it seems to always be in the center)
I think this should still work with physics unless I am not truly understanding the question. We did something similar with our SKATiledMap with that Auto Follow Feature. What you need to do is make sure the player is added to a node you can move (usually a map) as a child and then in the update function you do something like this...(sorry it isn't in swift)
-(void)update
{
if (self.autoFollowNode)
{
self.position = CGPointMake(-self.autoFollowNode.position.x+self.scene.size.width/2, -self.autoFollowNode.position.y+self.scene.size.height/2);
//keep map from going off screen
CGPoint position = self.position;
if (position.x > 0)
position.x = 0;
if (position.y > 0)
position.y = 0;
if (position.y < -self.mapHeight*self.tileWidth+self.scene.size.height)
position.y = -self.mapHeight*self.tileWidth+self.scene.size.height;
if (position.x < -self.mapWidth*self.tileWidth+self.scene.size.width)
position.x = -self.mapWidth*self.tileWidth+self.scene.size.width;
self.position = CGPointMake((int)(position.x), (int)(position.y));
}
}
Map being the node that the player is added to. Hopefully that helps. Also here is the link to the git hub project we have been working on. https://github.com/SpriteKitAlliance/SKAToolKit
I currently have a large map that goes off the screen, because of this its coordinate system is very different from my other nodes. This has led me to a problem, because I'm needing to generate a random CGPoint within the bounds of this map, and then if that point is frame/on-screen I place a visible node there. However the check on wether or not the node is on screen continuously fails.
I'm checking if the node is in frame with the following code: CGRectContainsPoint(self.frame, values) (With values being the random CGPoint I generated). Now this is where my problem comes in, the coordinate system of the frame is completely different from the coordinate system of the map.
For example, in the picture below the ball with the arrows pointing to it is at coordinates (479, 402) in the scene's coordinates, but they are actually at (9691, 9753) in the map's coordinates.
I determined the coordinates using the touchesBegan event for those who are wondering. So basically, how do I convert that map coordinate system to one that will work for the frame?
Because as seen below, the dot is obviously in the frame however the CGRectContainsPoint always fails. I've tried doing scene.convertPoint(position, fromNode: map) but it didn't work.
Edit: (to clarify some things)
My view hierarchy looks something like this:
The map node goes off screen and is about 10,000x10,000 for size. (I have it as a scrolling type map). The origin (Or 0,0) for this node is in the bottom left corner, where the map starts, meaning the origin is offscreen. In the picture above, I'm near the top right part of the map. I'm generating a random CGPoint with the following code (Passing it the maps frame) as an extension to CGPoint:
static func randPoint(within: CGRect) -> CGPoint {
var point = within.origin
point.x += CGFloat(arc4random() % UInt32(within.size.width))
point.y += CGFloat(arc4random() % UInt32(within.size.height))
return point;
}
I then have the following code (Called in didMoveToView, note that I'm applying this to nodes I'm generating - I just left that code out). Where values is the random position.
let values = CGPoint.randPoint(map.totalFrame)
if !CGRectContainsPoint(self.frame, convertPointToView(scene!.convertPoint(values, fromNode: map))) {
color = UIColor.clearColor()
}
To make nodes that are off screen be invisible. (Since the user can scroll the map background). This always passes as true, making all nodes invisible, even though nodes are indeed within the frame (As seen in the picture above, where I commented out the clear color code).
If I understand your question correctly, you have an SKScene that contains an SKSpriteNode that is larger than the scene's view, and that you are randomly generating coordinates within that sprite's coordinate system that you want to map to the view.
You're on the right track with SKNode's convertPoint(_:fromNode:) (where your scene is the SKNode and your map is the fromNode). That should get you from the generated map coordinate to the scene coordinate. Next, convert that coordinate to the view's coordinate system using your scene's convertPointToView(_:). The point will be out of bounds if it is not in view.
Using a worldNode which includes a playerNode and having the camera center on this node, you can check on/off with this code:
float left = player.position.x - 700;
float right = player.position.x + 700;
float up = player.position.y + 450;
float down = player.position.y - 450;
if((object.position.x > left) && (object.position.x < right) && (object.position.y > down) && (object.position.y < up)) {
if((object.parent == nil) && (object.dead == false)) {
[worldNode addChild:object];
}
} else {
if(object.parent != nil) {
[object removeFromParent];
}
}
The numbers I used above are static. You can also make them dynamic:
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat screenHeight = screenRect.size.height;
Diving the screenWidth by 2 for left and right. Same for screenHeight.
I've been reading about AABB collision. I have a pretty rough implementation of it at the moment. I am now able to detect when two rectangles intersect on both the X and Y axis.
To calculate the depth of intersection on X, I basically do:
overlapX = (Game1.p[0].boundingBox.halfWidth + Game1.p[1].boundingBox.halfWidth)
- (Math.Abs(Game1.p[0].boundingBox.center.X - Game1.p[1].boundingBox.center.X));
Right now I am just testing the depth on X as I want to see where the problem is. I am not taking the direction, left or right, into account. I am just assuming the p[0] object is colliding on the left side of p[1].
Then for the actual movement and collision detection:
public void Move(float x, float y)
{
Position.X += x;
Position.Y += y;
overlapX = (Game1.p[0].boundingBox.halfWidth
+ Game1.p[1].boundingBox.halfWidth)
- (Math.Abs(Game1.p[0].boundingBox.center.X
- Game1.p[1].boundingBox.center.X));
overlapY = (Game1.p[0].boundingBox.halfHeight
+ Game1.p[1].boundingBox.halfHeight)
- (Game1.p[1].boundingBox.center.Y
- Game1.p[0].boundingBox.center.Y);
if (Collision.testAABBAABBX(
Game1.p[0].boundingBox, Game1.p[1].boundingBox) &&
Collision.testAABBAABBY(
Game1.p[0].boundingBox, Game1.p[1].boundingBox))
{
if (overlapX < overlapY) Position.X -= overlapX;
}
}
Now when I collide with p[1], overlapX reads 1. So position.X should -= 1. However, here is where the problem begins. Here is an image during the overlap. You can see that there is indeed an overlap shown by a vertical yellow line:
So my problem is that when I instruct the position.X to move back by the overlap value, it does not, at least not until I move the rectangle either up or down. If I do that, it magically corrects itself:
Any clues on what's going on? Maybe it's right in front of me and I'm not seeing it, but the response only seems to trigger after the next movement.
Let me know if more info is needed.
This happens because your collision resolution code is placed within your motion code, which doesn't seem to run unless you make a move.
The solution is to move your collision resolution code outside your Move method into a separate method that is called by Update every frame.