Coordinate system in swift ios is wrong - ios

I am trying to position a sprite at a point :
class GameScene: SKScene {
let player = SKSpriteNode(imageNamed: "Koopa_walk_1.png")
override func didMove(to view: SKView) {
player.position = CGPoint(x: 0, y: 0)
self.addChild(player)
print(koopa.get_x())
}
}
But for some reason my sprite appears in more or less the middle of the screen :
Edit :
This is the original image (260px by 320px) :
I expected to see the image appear in the top left because it's coordinates are (0, 0)

The coordinate system of SpriteKit is cartesian, with an origin default of the middle of the screen, when using the starting template in Apple's Xcode.
This template sets the origin to the centre of the screen by using an origin setting of (0.5, 0.5)
To have a top left origin, you're going to need set this to (0, 1), and then invert Y values, to negative values.

Related

MGLUserLocationAnnotationView subclass does not take the pitch perspective of the camera

I have styled the user location icon according to these docs:
https://docs.mapbox.com/ios/maps/examples/user-location-annotation/
It works, but although I worked with camera and pitch, it is displayed in two dimensions. How can i make it that it is in the right perspective of the camera and the pitch effect works?
I added MGLMapCamera with this code:
func mapViewDidFinishLoadingMap(_ mapView: MGLMapView) {
// Wait for the map to load before initiating the first camera movement.
mapView.camera = MGLMapCamera(lookingAtCenter: mapView.centerCoordinate, altitude: 200, pitch: 50, heading: 0)
}
Isn't there a camera option mode like gps for Android?
https://docs.mapbox.com/android/maps/examples/location-component-camera-options/
If you want the annotation view to tilt with the map view, you can use the MGLAnnotationView:scalesWithViewingDistance property. This determines whether the annotation view grows and shrinks as the distance between the viewpoint and the annotation view changes on a tilted map.
When the value of this property is YES and the map is tilted, the annotation view appears smaller if it is towards the top of the view (closer to the horizon) and larger if it is towards the bottom of the view (closer to the viewpoint).
I found a solution.
As far as I can see, there is no way to set this with an option. So I solved it manually:
First, I create an image:
private func setupLayers() {
if arrow == nil {
arrow = CALayer()
let myImage = UIImage(named: "arrow")?.cgImage
arrow.bounds = CGRect(x: 0, y: 0, width: size, height: size)
arrow.contents = myImage
layer.addSublayer(arrow)
}
}
Then I use MGLRadiansFromDegrees to convert from radians to degrees and transform the image with every update:
override func update() {
// Convert Optional to Double
let camerapitch = Double(mapView?.camera.pitch ?? 0)
let pitch: CGFloat = MGLRadiansFromDegrees(camerapitch)
layer.transform = CATransform3DMakeRotation(pitch, 1, 0, 0)
}

how to set anchorPoint of view to 0.5 , 0.5 to get view in centre of screen? (Not scene)

I want to make my game full screen not with the black bars on both sides and bottom. so I found this code for that and it works for scene and now it's on full screen but the assets are not correctly positioned. Because of the view's coordinates are different (don't know why maybe because of previously I tried to set all for safeAreaLayoutGuide but then I get black bars). So is there any way to set it back to 0.5 0.5 . Otherwise I will have to set back anchorPoint of scene to what ever is view's anchorPoint and set all assets in my sks file according to that.
Here is the code from GameViewController :
class GameViewController: UIViewController {
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if let view = self.view as! SKView? {
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
let scene = HomeScene(size: view.bounds.size)
scene.scaleMode = .aspectFill
view.presentScene(scene)
}
}
override func viewDidLoad() {
super.viewDidLoad()
print("---")
print("∙ \(type(of: self))")
print("---")
}
}
this code from HomeScene :
let rectangle1 = SKShapeNode(rect: (self.view?.bounds)!)
rectangle1.strokeColor = .white
rectangle1.lineWidth = 20
addChild(rectangle1)
// Set (0, 0) as the centre of the screen
scene?.anchorPoint = CGPoint(x: 0.5, y: 0.5)
let rectangle = SKShapeNode(rect: self.frame)
rectangle.strokeColor = .green
rectangle.lineWidth = 20
addChild(rectangle)
This is how it looks the scene is at 0.5, 0.5 but, view is above that green is scene and white is view and button in middle of the view :
Thank you.
You could fix the problem with a different anchorpoint, or you could position the white 'view' node differently in the scene.
Information about the coordinate system (and anchor points): apple documentation
The default anchor position is (0.5, 0.5) according to the documentation. To put the white view in the bottom left: subtract half of the width and height of the x and y. Note that the line width also affects the positioning. Position the white view to the bottom left:
let bottomLeft: CGPoint = CGPoint(x: -self.size.width/2 + rectangle1.lineWidth/2,
y: -self.size.height/2 + rectangle1.lineWidth/2)
rectangle1.position = bottomLeft
To center the white view in the green view, subtract the width and height of rectangle1 (again, note the line width)
let center: CGPoint = CGPoint(x: -rectangle1.frame.size.width/2 + rectangle1.lineWidth/2,
y: -rectangle1.frame.size.height/2 + rectangle1.lineWidth/2)
rectangle1.position = center
Note that your green view is above the white view. Set the z position of the white view to some positive number to arrange it above the green view
rectangle1.zPosition = 1
If you were loading your HomeScene from a .sks file, the anchorPoint was set by xcode to (0.5, 0.5) as shown in my image.
But when you are trying to create a custom class without loading it from file (.sks), you will not have the properties set like here. So you need to set the manually.
let scene = HomeScene(size: view.bounds.size)
// at this point, you HomeScene has the anchorPoint set to (0.0, 0.0)
// lets set it back to (0.5, 0.5) as we want
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
scene.scaleMode = .aspectFill
view.presentScene(scene)

How to change SpriteKit's coordinate system

All SKSpriteNodes have their origin, also called anchorPoint, if I'm not wrong, in their middle. I want to translate that origin to be in the middle of the top line of the node. I've tried the following inside the init() method, but it doesn't work:
self.anchorPoint = self.anchorPoint.applying(
CGAffineTransform.init(translationX: 0, y: self.frame.size.height/2)
)
PS: I'm using this to simplify the following:
I've created an array of SpriteNodes, which all have are in the new CGPoint.zero position, then, i'll have to apply this on them:
for i in 0..<arr.count {
arr[i].position.y += self.size.height*CGFloat((i+1)/(arr.count+1))
}
Here is the code:
import SpriteKit
class SKLayer: SKSpriteNode {
init(inside scene: SKScene, withLabels texts: [String]) {
super.init(texture: nil, color: .clear, size: scene.size)
self.anchorPoint = CGPoint(x: 0.5, y: 1)
converted(texts).forEach(self.addChild(_:))
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.position = .zero
}
private func converted(_ text: [String]) -> [SKNode] {
var arr = [SKLabelNode]()
text.forEach {
arr.append(SKLabelNode(text: $0))
}
for i in 0..<arr.count {
arr[i].position.y = CGFloat(i) * arr[0].frame.height
}
return arr
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
}
class GameScene: SKScene {
override func didMove(to view: SKView) {
self.addChild(
SKLayer(inside: self, withLabels: ["Welcome", "Hello", "Bye", "Help"])
)
}
}
Here's the pic of what the code compiles to:
I want the nodes to go from up to down, not the other way around. Iow, I wanna see the "Welcome" Label at the top, then the "texT" one, then the others.
According to the docs,
This is how anchor points work:
Anchor points are specified in the unit coordinate system, shown in the following illustration. The unit coordinate system places the origin at the bottom left corner of the frame and (1,1) at the top right corner of the frame. A sprite’s anchor point defaults to (0.5,0.5), which corresponds to the center of the frame.
From this image, we can see that to set the anchor point to the middle of the top border, you need to set it to (0.5, 1).
You tried to transform the anchor point to self.frame.size / 2 which is probably a number larger than 1. Well, according to the docs, the range of (0, 0) - (1, 1) covers the whole frame of the sprite node. If you set it to a value larger than (1, 1), the anchor point will be outside of the frame of the node.

Nodes not appearing

So I'm trying to get a Node in xcode to appear, but it doesnt... here's the code.
import SpriteKit
import GameplayKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
let levelLabelNode = SKLabelNode(fontNamed: "Arial")
levelLabelNode.text = "Level"
levelLabelNode.fontSize = 30
levelLabelNode.fontColor = SKColor.green
levelLabelNode.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height*0.75)
self.addChild(levelLabelNode)
}
}
This had me stuck for a while. The coordinate system of the programme (assuming you are running from the default) has point (0, 0) at the centre of the screen. self.frame.size.width refers to the entire width of the screen. Therefore, positioning a node at self.frame.size.width/2 would position a node at the rightmost edge of the screen. If you want to position your node centred horizontally and 3/4 up the screen try:
levelLabelNode.position = CGPoint(x: 0, y: self.frame.height/4)
Hope this helped!
Remember: The coordinate system starts with (0, 0) in the centre and self.frame.size.width is the entire width of the screen.

SKSpriteNode not scalling properly

I'm developing a small game using Swift & SpriteKit. When I add a SKSpriteNode for Restart button, it doesn't scale properly.
The size of Restart button is 100px in height and width. If I don't set scale , it covers the whole screen and make the screen appear white. I figured out that if I setScale to 0.005 only than it appears on screen, but not in proper size.
import Foundation
import SpriteKit
class EndScene: SKScene {
var restartBtn = SKSpriteNode()
override func didMoveToView(view: SKView) {
background()
restartGame()
}
func restartGame() {
restartBtn = SKSpriteNode(imageNamed: "restartBtn")
restartBtn.setScale(0.005)
restartBtn.position = CGPoint(x: self.size.width / 2, y: self.size.height / 4)
restartBtn.zPosition = 1
self.addChild(restartBtn)
}
func background() {
let bkg = SKSpriteNode(imageNamed: "Background")
bkg.size = self.frame.size
bkg.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
bkg.zPosition = -2
self.addChild(bkg)
}
}
Here is the output of this code,
Restart Button Output
UPDATE
I put scene!.scaleMode = .AspectFill right inside didMoveToView function and it helped in rendering the shape of the SpriteNode properly. But still I have to setScale(0.001) to make the size of Restart button fit in the screen. Can anyone assist me what line of code I'm still missing?
Instead of using .setScale try using restartBtn.size = CGSize(Width: 50, Height: 50)
This uses the Sprite Kit's resize formula.
I ran into this as well. It happened when I forgot to specify the size of the scene. Instead of calling initWithSize I just wrote init and the scene started to scale down all nodes by x1000. Nodes were visible, but they required a scale of 0.001

Resources