问题
I have an UIBezierPath arrow that I found here. My goal is to pass the function "addArrows" a list of player arrays and draw arrows between them. This code works great... except when I call addArrows(gameScene: scene, from: [1, 3], to: [1, 3])
or addArrows(gameScene: scene, from: [2, 3], to: [2, 3])
. This should create arrows between players 1 and 3 (1st call), and 2 and 3 (2nd call). However, it is creating arrows between players 2 and 3 (1st call) and players 1 and 3 (2nd call). I have added print statements to the "addArrows" function and it is returning the locations (located in my constants struct) of players correctly. It works if it is fed an array of [1, 2, 3] and [1, 2, 3] or [1, 2] and [1, 2]. What exactly is happening here?
struct K {
static let stackPositions = [
// Player 1
1: CGPoint(x: (_screenW / 2), y: cardHeightConstant + cardHeight + 60),
// Player 2
2: CGPoint(x: (_screenW / 2), y: _screenH - cardHeightConstant - cardHeight - 60),
// Player 3
3: CGPoint(x: 30, y: _screenH / 2),
// Player 4
4: CGPoint(x: _screenW - 30, y: _screenH / 2)
]
}
// https://stackoverflow.com/questions/48625763/how-to-draw-a-directional-arrow-head
extension UIBezierPath {
func addArrow(start: CGPoint, end: CGPoint, pointerLineLength: CGFloat, arrowAngle: CGFloat) {
self.move(to: start)
self.addLine(to: end)
let startEndAngle = atan((end.y - start.y) / (end.x - start.x)) + ((end.x - start.x) < 0 ? CGFloat(Double.pi) : 0)
let arrowLine1 = CGPoint(x: end.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle + arrowAngle), y: end.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle + arrowAngle))
let arrowLine2 = CGPoint(x: end.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle - arrowAngle), y: end.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle - arrowAngle))
self.addLine(to: arrowLine1)
self.move(to: end)
self.addLine(to: arrowLine2)
}
}
func addArrows(gameScene: GameScene, from: [Int], to: [Int]) {
for fromPlayerNumber in from {
for toPlayerNumber in to {
guard let fromPos = K.stackPositions[fromPlayerNumber] else { return }
guard let toPos = K.stackPositions[toPlayerNumber] else { return }
if fromPos == toPos { continue }
print(fromPos.y)
print(toPos.y)
let arrow = UIBezierPath()
arrow.addArrow(start: CGPoint(x: fromPos.x, y: fromPos.y),
end: CGPoint(x: toPos.x, y: toPos.y),
pointerLineLength: 7,
arrowAngle: CGFloat(Double.pi / 4))
let arrowLayer = CAShapeLayer()
arrowLayer.strokeColor = UIColor.black.cgColor
arrowLayer.lineWidth = 1.0
arrowLayer.path = arrow.cgPath
arrowLayer.fillColor = UIColor.clear.cgColor
arrowLayer.lineJoin = CAShapeLayerLineJoin.round
arrowLayer.lineCap = CAShapeLayerLineCap.round
gameScene.view?.layer.addSublayer(arrowLayer)
}
}
}
Edit
I have forced the "From" location to be CGPoint(x: 0, y: 0)
. This is starting the arrow in the top left portion of the screen rather than all my other references which are starting in the bottom left of the screen. So, it appears that the arrow is using a different anchor point than the other objects in my scene. I tried setting arrowLayer.anchorPoint = CGPoint(x: 0, y: 0)
, but that didn't work...
回答1:
Here, the coordinate system was referencing the top left of the screen as opposed to the bottom left. In order to fix this, all I needed to do was "invert" the y coordinates of the start and ending y points. Fix below:
func addArrow(start: CGPoint, end: CGPoint, pointerLineLength: CGFloat, arrowAngle: CGFloat) {
// Adjust Y Directions Here! //
let adjStart = CGPoint(x: start.x, y: K._screenH - start.y)
let adjEnd = CGPoint(x: end.x, y: K._screenH - end.y)
self.move(to: adjStart)
self.addLine(to: adjEnd)
let startEndAngle = atan((adjEnd.y - adjStart.y) / (adjEnd.x - adjStart.x)) + ((adjEnd.x - adjStart.x) < 0 ? CGFloat(Double.pi) : 0)
let arrowLine1 = CGPoint(x: adjEnd.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle + arrowAngle),
y: adjEnd.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle + arrowAngle))
let arrowLine2 = CGPoint(x: adjEnd.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle - arrowAngle),
y: adjEnd.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle - arrowAngle))
self.addLine(to: arrowLine1)
self.move(to: adjEnd)
self.addLine(to: arrowLine2)
}
来源:https://stackoverflow.com/questions/59791882/swift-uibezierpath-different-coordinate-starting-point