Details of program:
by looking at this picture(http://i.stack.imgur.com/QOZ53.png), what I'm trying to do is have the spaceship circle the planet. I achieved this by making a line node and changes its anchor point to the top and then position it to the centre of the planet which then upon impact of the spaceship and planet, the line is angled towards the ship in which then the ship is removed from the view and added to the line node and together they rotate around the planet. (hopefully that makes sense)
Problem:
the problem is after adding the ship to the line node, the coordinates of the spaceship get all weird, I'm not sure what's happening but if you take a look at the console logs, the x position and y position are totally off. The first "I'm touched" is activated at the beginning to make the spaceship go, the initial starting point of the ship is [100,100], once it comes in contact with a planet the "WE'RE TOUCHING" is activated and the position of the ship is reasonable [116.000015258789, 162.0]. The ship then starts to circle the planet but when i call "I'm touched" to get more info on the ships position, its totally off [4.1961669921875e-05, -59.0515747070312 ]. At this point I have no idea what's happening, for more info you can look at the code and console logs.
Cosnole Log:
I'm Touched
ship angle in degrees: 72.9146667862848
PLAYER POSITION X: 100.0 Y: 100.0
*PLAYER ROTATION: 1.27260100841522
WE'RE TOUCHING
PLANET POSITION X: 116.000015258789 Y: 162.0
PLAYER POSITION X: 101.464706420898 Y: 104.765426635742
ANGLE OF HANDLE RADIANS: -1.81949883426405 DEGRESS: -104.249604032303
*PLAYER ROTATION: 1.27260100841522
I'm Touched
ship angle in degrees: 10.6651491436781
PLAYER POSITION X: 7.33137130737305e-05 Y: -59.0515937805176
*PLAYER ROTATION: 0.186141967773438
I'm Touched
ship angle in degrees: 10.6651491436781
PLAYER POSITION X: 4.1961669921875e-05 Y: -59.0515747070312
*PLAYER ROTATION: 0.186141967773438
I'm Touched
ship angle in degrees: 10.6651491436781
PLAYER POSITION X: 3.4332275390625e-05 Y: -59.0515594482422
*PLAYER ROTATION: 0.186141967773438
I'm Touched
ship angle in degrees: 10.6651491436781
PLAYER POSITION X: 2.6702880859375e-05 Y: -59.0515365600586
*PLAYER ROTATION: 0.186141967773438
I'm Touched
ship angle in degrees: 10.6651491436781
PLAYER POSITION X: 3.0517578125e-05 Y: -59.0515327453613
*PLAYER ROTATION: 0.186141967773438
This is the collision code:
func didBeginContact(contact: SKPhysicsContact) {
if contact.bodyA.categoryBitMask == planetGroup || contact.bodyB.categoryBitMask == planetGroup {
print(" **WE'RE TOUCHING** ")
moving = false
touching = true
let degrees = 45.0
let radians = degrees * M_PI / 180.0
var rotate = SKAction.rotateByAngle(CGFloat(radians), duration: 0.5)
var repeat = SKAction.repeatActionForever(rotate)
playerShip.runAction(repeat, withKey: "rotate")
playerShip.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
planetNode = contact.bodyA.node as! SKSpriteNode
planetX = planetNode.position.x
planetY = planetNode.position.y
playerX = playerShip.position.x
playerY = playerShip.position.y
var angleOfAnchor = AngleBetweenPoints(planetNode.position, endPoint: playerShip.position)
var three60 = 360 * CGFloat(M_PI) / 180.0
var nintey = 90 * CGFloat(M_PI) / 180.0
var inDegree = angleOfAnchor * 180.0 / CGFloat(M_PI)
var shipPlanetDistance = SDistanceBetweenPoints(planetNode.position, p2: playerShip.position)
line = SKSpriteNode(color: UIColor.blackColor(), size: CGSize(width: 2, height: planetNode.size.height))
line.anchorPoint = CGPoint(x: 0.5, y: 1)
line.position = CGPoint(x: planetX, y: planetY)
// this sets the angle of the line when the ship and planet collide
line.zRotation = -(three60 - nintey - angleOfAnchor)
self.addChild(line)
playerShip.removeFromParent()
angle.runAction(repeat, withKey: "rotate")
line.addChild(playerShip)
print("*PLANET POSITION* X: \(planetX) Y: \(planetY) \r *PLAYER POSITION* X: \(playerX) Y: \(playerY) \r *ANGLE OF HANDLE* RADIANS: \(angleOfAnchor) DEGRESS: \(inDegree) *PLAYER ROTATION: \(playerShip.zRotation)")
}
}
Screen Touch Code:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
print(" I'm Touched ")
playerX = playerShip.position.x
playerY = playerShip.position.y
var radians:CGFloat = playerShip.zRotation
var degrees = radians * 180.0 / CGFloat(M_PI)
var dx = cos(radians)
var dy = sin(radians)
print(" ship angle in degrees: \(degrees) ")
//playerShip.removeFromParent()
//self.addChild(playerShip)
//playerShip.position = CGPoint(x: playerX, y: playerY)
// playerShip.physicsBody?.angularVelocity = 0 // forgot what this does
playerShip.removeActionForKey("rotate")
if moving == true {
playerShip.physicsBody?.velocity = CGVector(dx: 100*dx, dy: 100*dy)// speed of direction
}
print("*PLAYER POSITION* X: \(playerX) Y: \(playerY) *PLAYER ROTATION: \(playerShip.zRotation)")
}
Related
I am trying to get my sprite to move and rotate to the location of my touch and follow as I move my finger around the screen. The movement part is working fine but I can't seem to get it to rotate. My moveAndRotate function is below.
func moveAndRotate(spriteNode: SKSpriteNode, toPosition position: CGPoint) {
let angle = atan2(position.y - player.position.y, position.x - player.position.x)
let rotateAction = SKAction.rotate(toAngle: angle - -(CGFloat(Double.pi / 2)), duration: 0.05, shortestUnitArc: true)
player.run(rotateAction)
let offsetX = position.x - player.position.x
let offsetY = position.y - player.position.y
let normal = simd_normalize(simd_double2(x: Double(offsetX), y: Double(offsetY)))
velocity = CGVector(dx: CGFloat(normal.x) * movePointsPerSecond, dy: CGFloat(normal.y) * movePointsPerSecond)
}
The duration was set to o.5 on the rotateAction, change it to 0 and it works fine.
I am developing an iPad spaceship (first person) game with 6 DOF (6 degrees of freedom) with SceneKit and the accelerometer. I have the accelerometer working, but how can I move the spaceship forward in the direction of the angle of the accelerometer (yaw, roll, pitch)? I use a button for the forward direction. I can't find it on google or something. I hope someone knows how to do this.
Here is the code, where cubeNode is a test (in the 3D game cubeNode is cameraNode).
func updateMotionControl() {
motion.getAccelerometerData(interval: 0.1) { (x,y,z) in
self.motionForce = SCNVector3(x: Float(x) * 0.05, y: Float(y) * 0.05,
z: Float(z) * 0.05)
}
let currentAttitude = motion.attitude
let roll = Float(currentAttitude.roll)
let pitch = Float(currentAttitude.pitch)
let yaw = Float(currentAttitude.yaw)
cubeNode.eulerAngles = SCNVector3(roll, yaw, pitch)
}
I have this code, but it still won't work, what is wrong?
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
cameraNode.eulerAngles = SCNVector3Make(Float(rot.roll + M_PI/2) * 2, -Float(rot.yaw) * 3, -Float(rot.pitch * 5))
let z = cos(rot.pitch)*sin(rot.yaw)
let y = sin(rot.pitch)
let x = cos(rot.pitch)*cos(rot.yaw)
let view = SCNVector3(x: Float(x), y : Float(y), z : Float(z))
cameraNode.position = cameraNode.position + (view * 0.1)
//cameraNode.physicsBody?.velocity = -view
}
Here are the prints for a few x, y en z:
X:
0.996798277375072,
0.996753286621399,
0.996748721234948,
0.996776960253164,
0.996809519516764,
0.996816599311414,
0.996818969166521,
0.996777228814986
Z:
0.0683379467811582,
0.0678748719764882,
0.0678313394347644,
0.0683611603110574,
0.0686781724979699,
0.0684588783890975,
0.0461781969805951,
0.0478922538280758,
0.0485691959442941,
0.0482168862439498,
0.0474367474311863
Y:
0.0437050318386564,
0.0434850468949368,
0.0432268234343706,
0.0431581009944219,
0.0433707104009568,
0.0436728563252332,
0.044357103165837,
0.0449712895273139,
0.0364369979497931,
0.0353335950917769
I'm making a SpriteKit game. I have six cannons that I've made rotate back and fourth. I want to shoot cannonballs from each cannon that sync with the rotation of each cannon. I want a duration of one second between each cannonball.
So basically: A cannon that is in constant rotation is shooting cannonballs in the same direction as the rotation.
For the cannons i'm using an extension:
extension CGFloat {
func degreesToRadians() -> CGFloat {
return self * CGFloat.pi / 180
}
}
I'm gonna put the code for just one cannon, since I should be able to figure out how to adjust one of the cannonball movements to the others. Here is one:
func createCannons() {
let cannonLeftBottom = SKSpriteNode(imageNamed: "Cannon")
cannonLeftBottom.anchorPoint = CGPoint(x: 0.5, y: 0.5)
cannonLeftBottom.position = CGPoint(x: -325, y: -420)
cannonLeftBottom.zPosition = 4
cannonLeftBottom.setScale(0.4)
cannonLeftBottom.zRotation = CGFloat(65).degreesToRadians()
let rotateLB = SKAction.rotate(byAngle:
CGFloat(-65).degreesToRadians(), duration: 2)
let rotateBackLB = SKAction.rotate(byAngle:
CGFloat(65).degreesToRadians(), duration: 2)
let repeatRotationLB =
SKAction.repeatForever(SKAction.sequence([rotateLB,rotateBackLB]))
cannonLeftBottom.run(repeatRotationLB)
self.addChild(cannonLeftBottom)
}
Here is my function for the cannonball:
func createBalls() {
let cannonBallLB = SKSpriteNode(imageNamed: "Ball")
cannonBallLB.name = "CannonBall"
cannonBallLB.position = CGPoint(x: -325, y: -420)
cannonBallLB.physicsBody = SKPhysicsBody(circleOfRadius:
cannonBallLB.size.height / 2)
cannonBallLB.physicsBody?.affectedByGravity = false
cannonBallLB.zPosition = 3
cannonBallLB.setScale(0.1)
self.addChild(cannonBallLB)
}
THX!
You need to convert from Polar Coordinates to Rectangular Coordinates.
You do this by using sin and cos
E.G.
let speed = 100 //This would mean move 100 points per second
let force = CGVector(dx:cos(cannon.zRotation) * speed,dy:sin(cannon.zRotation) * speed)
cannonBall.applyForce(force)
Note: Now unless they changed this, force used to be in units of points, if they fixed it to units of meters, then you need to divide your speed by 150, since 150 points = 1 meter in Spritekit
I try to build 2D - top down game, and I have player (SKSpriteNode) he can move and rotate, and I want to shoot two bullets from him.
I use this code to shoot:
func setBullet(player: Player, bullet: Int)
{
let weaponPosition = scene!.convertPoint(player.weapon.position, fromNode: player)
var xPos, yPos: CGFloat!
let sinus = sin(player.zRotation)
let cosinus = cos(player.zRotation)
if bullet == 1
{
xPos = converted.x - sinus * player.size.height / 2
yPos = converted.y + cosinus * player.size.height / 2
}
else if bullet == 2
{
xPos = weaponPosition.x - sinus * player.size.height / 2
yPos = weaponPosition.y + cosinus * player.size.height / 2
}
position = CGPoint(x: xPos, y: yPos)
physicsBody!.applyImpulse(CGVector(dx: -sinus * normalSpeed, dy: cosinus * normalSpeed))
}
But, i do not know how to correctly set the position...
I try to make something like this
(Green dots - this is a bullets). Can anyone help me please!
Shooting multiple bullets in the same direction is fairly straightforward. The key is to determine the bullets' initial positions and direction vectors when the character is rotated.
You can calculate a bullet's initial position within the scene by
let point = node.convertPoint(weapon.position, toNode: self)
where node is the character, weapon.position is non-rotated position of a gun, and self is the scene.
Typically, a bullet moves to the right, CGVector(dx:1, dy:0), or up, CGVector (dx:0, dy:1), when the character is not rotated. You can calculate the direction of the impulse to apply to the bullet's physics body by rotating the vector by the character's zRotation with
func rotate(vector:CGVector, angle:CGFloat) -> CGVector {
let rotatedX = vector.dx * cos(angle) - vector.dy * sin(angle)
let rotatedY = vector.dx * sin(angle) + vector.dy * cos(angle)
return CGVector(dx: rotatedX, dy: rotatedY)
}
Here's an example of how to shoot two bullets from a rotated character:
struct Weapon {
var position:CGPoint
}
class GameScene: SKScene {
let sprite = SKSpriteNode(imageNamed:"Spaceship")
let dualGuns = [Weapon(position: CGPoint(x: -15, y: 15)), Weapon(position: CGPoint(x: 15, y: 15))]
let singleGun = [Weapon(position: CGPoint(x: 0, y: 15))]
let numGuns = 1
// If your character faces up where zRotation == 0, offset by pi
let rotationOffset = CGFloat(M_PI_2)
override func didMoveToView(view: SKView) {
scaleMode = .ResizeFill
sprite.position = view.center
sprite.size = CGSizeMake(25, 25)
self.addChild(sprite)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let _ = touches.first {
let action = SKAction.rotateByAngle(CGFloat(M_PI_4/2), duration:1)
sprite.runAction(action) {
[weak self] in
if let scene = self {
switch (scene.numGuns) {
case 1:
for weapon in scene.singleGun {
scene.shoot(weapon: weapon, from: scene.sprite)
}
case 2:
for weapon in scene.dualGuns {
scene.shoot(weapon: weapon, from: scene.sprite)
}
default:
break
}
}
}
}
}
func shoot(weapon weapon:Weapon, from node:SKNode) {
// Convert the position from the character's coordinate system to scene coodinates
let converted = node.convertPoint(weapon.position, toNode: self)
// Determine the direction of the bullet based on the character's rotation
let vector = rotate(CGVector(dx: 0.25, dy: 0), angle:node.zRotation+rotationOffset)
// Create a bullet with a physics body
let bullet = SKSpriteNode(color: SKColor.blueColor(), size: CGSizeMake(4,4))
bullet.physicsBody = SKPhysicsBody(circleOfRadius: 2)
bullet.physicsBody?.affectedByGravity = false
bullet.position = CGPointMake(converted.x, converted.y)
addChild(bullet)
bullet.physicsBody?.applyImpulse(vector)
}
// Rotates a point (or vector) about the z-axis
func rotate(vector:CGVector, angle:CGFloat) -> CGVector {
let rotatedX = vector.dx * cos(angle) - vector.dy * sin(angle)
let rotatedY = vector.dx * sin(angle) + vector.dy * cos(angle)
return CGVector(dx: rotatedX, dy: rotatedY)
}
}
Suppose your player is a circle maked with SKShapeNode or SKSpriteNode.
Both of them have the frame property:
let f = player.frame
So, the first bullet can be in this position:
let firstBullet.position = CGPointMake(player.position.x-(f.width/2),player.position.y)
let secondBullet.position = CGPointMake(player.position.x+(f.width/2),player.position.y)
To know it during rotation do:
let firstBulletXPos = firstBullet.position.x - sinus * bullet.size.height / 2
let firstBulletYPos = firstBullet.position.y + cosinus * bullet.size.height / 2
let secondBulletXPos = secondBullet.position.x - sinus * bullet.size.height / 2
let secondBulletYPos = secondBullet.position.y + cosinus * bullet.size.height / 2
Details of program: by looking at this picture(http://i.stack.imgur.com/QOZ53.png), what I'm trying to do is have the spaceship circle the planet. I achieved this by making a line node and changes its anchor point to the top and then position it to the centre of the planet which then upon impact of the spaceship and planet, the line is angled towards the ship in which then the ship is removed from the view and added to the line node as a child and together they rotate around the planet, as the ship rotates around the the planet, the ship it self also rotates on the spot (hopefully that makes sense)
Problem: the problem is im not getting the correct zRotation value of the ship. Right now I got the code to draw another ship at the location where it was tapped and set the zRotation of that image to the zRotation of the ship but i keep getting different angles. (refer to picture). Is this because I have ship added to line node as a child? Im running a rotating animation on both the line and the ship. In the picture, the line is rotating around the planet counter clockwise dragging the ship along with it and the ship itself is also rotating counter clockwise on the spot. In the picture the left ship; the one that's touching the line is the one thats rotating, the ship on the right is just drawn to angle of the rotating ship but by looking at the picture you can see the angle of the ship is pretty opposite of the one on the left. Why is this so? what I noticed is that when the ship is at the bottom half of the planet, the angles are fine but when it comes to the top half, the angles are kinda opposite(refer to picture)
Picture
Console Log:
I'm Touched -
ship angle in degrees: 83.6418545381942
PLAYER POSITION X: 100.0 Y: 100.0
PLAYER ZROTATION: 1.45982575416565
WE'RE TOUCHING -
PLANET POSITION X*: 120.0 Y: 230.000015258789
PLAYER-SHIP POSITION X: 107.998710632324 Y: 171.783294677734
ANGLE OF LINE RADIANS*: -1.77409685090117 DEGRESS: -101.648262004087
PLAYER-SHIP ROTATION: 1.57079637050629
I'm Touched -
ship angle in degrees: 314.660859137531
TEMP POS X: 136.535125732422 Y: 287.094879150391
TEMP ZROTATION: 5.491868019104
PLAYER POSITION X: 136.535125732422 Y: 287.094879150391
PLAYER ZROTATION: 5.491868019104
Collision Code:
func didBeginContact(contact: SKPhysicsContact) {
if contact.bodyA.categoryBitMask == planetGroup || contact.bodyB.categoryBitMask == planetGroup {
print(" WE'RE TOUCHING ")
moving = true
touching = true
let degrees = 45.0
let radians = degrees * M_PI / 180.0
//rotate Line
var rotateLine = SKAction.rotateByAngle(CGFloat(radians), duration: 0.5)
var repeatLine = SKAction.repeatActionForever(rotateLine)
//rotates Ship
var rotateShip = SKAction.rotateByAngle(CGFloat(radians), duration: 0.4)
var repeatShip = SKAction.repeatActionForever(rotateShip)
playerShip.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
planetNode = contact.bodyA.node as! SKSpriteNode
planetX = planetNode.position.x
planetY = planetNode.position.y
playerX = playerShip.position.x
playerY = playerShip.position.y
var angleOfAnchor = AngleBetweenPoints(planetNode.position, endPoint: playerShip.position)
var three60 = 360 * CGFloat(M_PI) / 180.0
var nintey = 90 * CGFloat(M_PI) / 180.0
var inDegree = angleOfAnchor * 180.0 / CGFloat(M_PI)
var shipPlanetDistance = SDistanceBetweenPoints(planetNode.position, p2: playerShip.position)
line = SKSpriteNode(color: UIColor.blackColor(), size: CGSize(width: 2, height: planetNode.size.height))
line.anchorPoint = CGPoint(x: 0.5, y: 1)
line.position = CGPoint(x: planetX, y: planetY)
line.zRotation = -(three60 - nintey - angleOfAnchor)
self.addChild(line)
tempShip = playerShip
playerShip.removeFromParent()
line.runAction(repeatLine, withKey: "rotateLine")
//playerShip.position = CGPoint(x: playerX, y: playerY)
line.addChild(playerShip)
playerShip.zRotation = (90 * CGFloat(M_PI) / 180.0)
playerShip.runAction(repeatShip, withKey: "rotateShip")
print("*PLANET POSITION* X: \(planetX) Y: \(planetY) \r *PLAYER-SHIP POSITION* X: \(playerX) Y: \(playerY) \r *ANGLE OF LINE* RADIANS: \(angleOfAnchor) DEGRESS: \(inDegree) *PLAYER-SHIP ROTATION: \(playerShip.zRotation)")
}
}
Screen Touch Code:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
print(" I'm Touched ")
var radians:CGFloat = playerShip.zRotation
var degrees = radians * 180.0 / CGFloat(M_PI)
var dx = cos(radians)
var dy = sin(radians)
print(" ship angle in degrees: \(degrees) ")
var tempAngle = playerShip.zRotation
var shipPosition = convertPoint(playerShip.position, fromNode: line)
playerX = shipPosition.x
playerY = shipPosition.y
if startMove == true {
playerShip.removeActionForKey("rotateShip")
playerShip.physicsBody?.velocity = CGVector(dx: 100*dx, dy: 100*dy)// speed of direction
startMove = false
}
if moving == true{
//playerShip.removeActionForKey("rotateShip")
//playerShip.removeFromParent()
//self.addChild(playerShip)
var radians:CGFloat = playerShip.zRotation
var degrees = radians * 180.0 / CGFloat(M_PI)
var dx = cos(radians)
var dy = sin(radians)
print(" ship angle in degrees: \(degrees) ")
//playerShip.zRotation = tempShip.zRotation
//playerShip.position = CGPoint(x: playerX, y: playerY)
//playerShip.physicsBody?.velocity = CGVector(dx: 100*dx, dy: 100*dy)// speed of direction
// this is the ship that gets drawn
var temp = SKSpriteNode(imageNamed: "img/ship/2.png")
temp.position = CGPoint(x: playerX, y: playerY)
temp.zRotation = playerShip.zRotation
self.addChild(temp)
//moving = false
print("*TEMP POS* X: \(temp.position.x) Y: \(temp.position.y) *TEMP ZROTATION*: \(temp.zRotation)")
}
print("*PLAYER POSITION* X: \(playerX) Y: \(playerY) *PLAYER ZROTATION: \(playerShip.zRotation)")
}
I managed to solve this problem, I added the line.zrotation with the playerShip.zrotation and it ended up working perfectly