SKNode跟随另一个SKNode在SKConstraint边界内

3
我正在尝试使用SpriteKit模拟一个眼睛。
当用户手指在屏幕上移动时,眼睛的瞳孔会跟随手指移动,但必须保持在眼睛的范围内。
我曾尝试使用SKConstraint来解决这个问题,但未成功。 编辑 我的想法是对瞳孔应用SKConstraints以限制其边界为眼睛。任何触摸(例如touchesMoved()等)都将以SKAction.moveTo()的形式应用于瞳孔,SpriteKit负责维护瞳孔在眼睛范围内。
let touchPoint = CGPoint()
SKAction.moveTo( touchPoint, duration: 2)

视频的代码可供使用:https://gist.github.com/anonymous/f2356e07d1ac0e67c25b1940662d72cb

Eyeball video demo

一张图片胜过千言万语......

想象瞳孔是小的、白色的、填充的圆圈。蓝色框模拟用户在屏幕上移动手指。

理想情况下,瞳孔会跟随蓝色框在屏幕上移动,并沿着黄色圆圈定义的路径移动。

iOS 10 | Swift 3 | Xcode 8


你从未编写任何代码来移动瞳孔,但它看起来正在按照你的意愿进行。 - Knight0fDragon
@Knight0fDragon 正确。我稍微编辑了答案,以使其更加清晰。 - Alex
请在您实际进行此操作的代码中发布它,这仍然不够有帮助。 - Knight0fDragon
此外,您不想执行 moveTo 操作,因为事情可能会变得非常复杂。您可能会有多个操作相互冲突。相反,只需将节点移动到触摸位置,约束条件应将其移回。 - Knight0fDragon
2个回答

5

不必限制距离,您可以使用方向约束来旋转节点以面向触摸位置。通过将带有x偏移量的“瞳孔”节点添加到被约束的节点上,瞳孔将朝着触摸点移动。以下是如何实现的示例:

let follow = SKSpriteNode(color:.blue, size:CGSize(width:10,height:10))
let pupil = SKShapeNode(circleOfRadius: 10)
let container = SKNode()
let maxDistance:CGFloat = 25

override func didMove(to view: SKView) {
    addChild(container)
    // Add the pupil at an offset
    pupil.position = CGPoint(x: maxDistance, y: 0)
    container.addChild(pupil)

    // Node that shows the touch point
    addChild(follow)

    // Add an orientation constraint to the container
    let range = SKRange(constantValue: 0)        
    let constraint = SKConstraint.orient(to: follow, offset: range)
    container.constraints = [constraint]
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    for t in touches {
        let location = t.location(in: self)
        follow.position = location
        adjustPupil(location: location)
    }
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    for t in touches {
        let location = t.location(in: self)
        follow.position = location
        adjustPupil(location: location)
    }
 }

// Adjust the position of the pupil within the iris
func adjustPupil(location:CGPoint) {
    let dx = location.x - container.position.x
    let dy = location.y - container.position.y
    let distance = sqrt(dx*dx + dy*dy)
    let x = min(distance, maxDistance)
    pupil.position = CGPoint(x:x, y:0)
}

enter image description here


1
这意味着瞳孔永远无法进入眼球中心,这只是给楼主的一个提示。 - Knight0fDragon
此时你最好回到距离约束,因为这正是你正在做的事情,哈哈。 - Knight0fDragon
你如何在不使用SKAction(参见上面的评论)或向update方法添加代码的情况下实现距离约束? - 0x141E
每次应用约束时,会在节点位置和目标节点位置之间投射一条线。计算两点之间的距离,如果它超出了指定范围,节点将沿着这条线被推或拉,直到它位于范围内。这是来自苹果文档的内容。 - Knight0fDragon
1
@Fluidity 快速录屏,然后我只需使用在线网站将mov转换为png。 - Knight0fDragon
显示剩余10条评论

1

在我发布答案之前,我希望等待回应,但是我和@0x141E就约束条件的工作方式进行了辩论,所以这里提供代码。使用它来找出你错在哪里。

import SpriteKit


class GameScene: SKScene {
    static let pupilRadius : CGFloat = 30
    static let eyeRadius : CGFloat = 100

    let follow = SKSpriteNode(color:.blue, size:CGSize(width:10,height:10))
    let pupil = SKShapeNode(circleOfRadius: pupilRadius)

    override func didMove(to view: SKView) {
        let eye = SKShapeNode(circleOfRadius: GameScene.eyeRadius)
        eye.strokeColor = .white
        eye.fillColor = .white
        addChild(eye)

        pupil.position = CGPoint(x: 0, y: 0)
        pupil.fillColor = .blue
        eye.addChild(pupil)

        addChild(follow)

        pupil.constraints = [SKConstraint.distance(SKRange(lowerLimit: 0, upperLimit: GameScene.eyeRadius-GameScene.pupilRadius), to: eye)]

    }

    func moveFollowerAndPupil(_ location:CGPoint){
        follow.position = location
        pupil.position = convert(location, to: pupil.parent!)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        touches.forEach({moveFollowerAndPupil($0.location(in: self))})
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        touches.forEach({moveFollowerAndPupil($0.location(in: self))})
    }
}

正如您所看到的,我没有使用平方根,希望苹果也聪明到不需要使用它,这意味着理论上应该比手动计算距离公式更快。

enter image description here


我以为你会让瞳孔平滑地从A点移动到B点,而不是一步跳到那里。 - 0x141E
谁说过这件事了? - Knight0fDragon
OP打算使用一个moveTo动作。 - 0x141E
已经告诉了OP我不建议这样做,这取决于他想要实现什么结果,这会很麻烦。 - Knight0fDragon
请不要随便点踩,如果你要点踩,请礼貌地解释一下原因。本文作者没有提供足够的信息来解决他的问题,这段代码只是为了向0x141E演示正确的距离约束方法。一旦作者解释清楚了他的代码缺失(我认为与他没有移动瞳孔有关),我就可以提供正确的答案。 - Knight0fDragon
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接