我有一个简单的将篮球扔到篮筐的比赛,但我想加一个影响球的风。
我做了一个功能:
func setWind() {
let multi = CGFloat(50)
let rnd = CGFloat(arc4random_uniform(UInt32(10))) - 5
windLbl.text = "Wind: \(rnd)"
wind = rnd * multi
}
并且还启用了使用碰撞Bitmask启动篮球的功能
func launch(x inX: CGFloat, y: CGFloat) {
......
// Change Collision Bitmask
let wait = SKAction.wait(forDuration: c.airTime / 2)
let changeCollision = SKAction.run({
self.basketball.physicsBody?.collisionBitMask = self.netFront
self.basketball.zPosition = self.bg.zPosition + 2
})
// ADD WIND!!
let windWait = SKAction.wait(forDuration: c.airTime / 4)
let push = SKAction.applyImpulse(CGVector(dx: wind, dy: 0), duration: 1)
basketball.run(SKAction.sequence([windWait, push]))
basketball.run(SKAction.sequence([wait,changeCollision]))
// Wait & reset
let wait4 = SKAction.wait(forDuration: 4)
let reset = SKAction.run({
self.setWind()
})
self.run(SKAction.sequence([wait4,reset]))
}
然而,我试图在几个地方召唤一个功能,但风不影响篮球。我错过了什么?
代码在这里:
override func didMove(to view: SKView) {
windLbl = (childNode(withName: "//windLbl") as? SKLabelNode)!
backLabel = childNode(withName: "//backLabel") as? SKLabelNode
scoreLabel = childNode(withName: "//scoreLabel") as? SKLabelNode
basketball = childNode(withName: "//basketball") as? SKSpriteNode
scoreZone = childNode(withName: "//scoreZone") as? SKSpriteNode
// make sure we get notified of any collisions, including pass-through (for scoreZone)
physicsWorld.contactDelegate = self
resetBasketBall()
setWind()
}
// put the basketball at the bottom of the screen,
func resetBasketBall() {
// reset score state
if !collidedWithScoreZone {
recordScore()
score = 0
scoreLabel.text = "0"
}
collidedWithScoreZone = false
shouldDetectScoreCollision = false
// the first shot is always at x=0
var randomX = 0.0
if score > 0 {
// x varies between -300 and +300
randomX = -300.0 + Double(arc4random_uniform(600))
// randomness increases over first 3 shots
if score < 4 {
randomX = randomX / (4.0 - Double(score))
}
}
basketball.position = CGPoint(x: randomX, y: yBasketballStart)
// not moving
basketball.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
// ball isn't affected by the physics simulation at all
basketball.physicsBody?.isDynamic = false
// stop any running scale animation
basketball.removeAction(forKey: "scale")
// make basketball bigger
basketball.xScale = 2 * basketballScale
basketball.yScale = 2 * basketballScale
// not recording touch events yet
isShooting = false
// nuke any leftover recorded drag points
throwPoints = [CGPoint]()
//
// Things that will be changed after the ball moves above the net
//
// put ball's Z location it in front of the net
basketball.zPosition = inFrontZ
// don't collide with anything
basketball.physicsBody?.collisionBitMask = 0
}
func touchDown(atPoint pos: CGPoint, atTime t: TimeInterval) {
// have to start inside the basketball
guard basketball.contains(pos) else {
return
}
isShooting = true
addDrag(point: pos)
}
func addDrag(point: CGPoint) {
throwPoints.append(point)
}
func recordScore() {
if score > 0 {
// add the score before wiping it out
MenuScene.scores.add(score: score)
}
}
// what to do when the user taps the "<Back" label
func goBack() {
// record the current score if it's non-zero
recordScore()
// Load the TitleScene
if let view = self.view, let scene = MenuScene(fileNamed: "MenuScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
let reveal = SKTransition.moveIn(with: .left, duration: kSceneTransitionDelay)
// Present the scene
view.presentScene(scene, transition:reveal)
}
}
func touchMoved(atPoint pos: CGPoint, atTime t: TimeInterval) {
guard isShooting else {
return
}
addDrag(point: pos)
}
func touchUp(atPoint pos : CGPoint, atTime t: TimeInterval) {
// check for touching the "< Back" label
if touchWasHandledByLabel(atPoint: pos,
withDict: [ backLabel : { self.goBack() } ]) {
return
}
// nothing else to do unless we are shooting
guard isShooting else {
return
}
// add the new point tothe list of drag points
addDrag(point: pos)
// need to move your finger to move the ball
guard throwPoints.count > 1 else {
resetBasketBall()
return
}
// calculate the dx / dy vector for the ball
var dx = CGFloat(0.0)
var dy = dx
// average the last few drags to get a vector
for i in 1 ..< throwPoints.count {
// CGFloat.native is a Double
dx += throwPoints[i].x - throwPoints[i-1].x
dy += throwPoints[i].y - throwPoints[i-1].y
}
// average them
dx /= CGFloat(throwPoints.count)
dy /= CGFloat(throwPoints.count)
// we always want dy to be at basketballYImpulse, so find out what the appropriate dx value is
let yFactor = basketballYImpulse / dy
dx *= yFactor
// launch the basketball
launch(x: dx, y: basketballYImpulse)
}
func launch(x inX: CGFloat, y: CGFloat) {
// make a 1200-magnitude vector that points just above the net
var netVector = CGVector(dx: scoreZone.position.x - basketball.position.x,
dy: scoreZone.position.y + 1200 - basketball.position.y)
let netVectorYFactor = basketballYImpulse / netVector.dy
netVector = netVector * netVectorYFactor
// limit magnitude of x such that the max angle is 45º
var x = inX
if abs(x) > abs(y) {
x = abs(y)*(x/abs(x))
print("Updated x: \(inX) -> \(x)")
}
// allow the ball to be affected by the physics simulation
basketball.physicsBody?.isDynamic = true
// average out 2 x the user vector with 1 x the netVector to give the user a bit of a helping hand
let userThrowVector = CGVector(dx: x, dy: y)
// weight of helping hand is 1/3
let ballVector = (userThrowVector*2 + netVector) * 0.333
// give the basketball a kick in the given direction
basketball.physicsBody?.velocity = ballVector
// shrink the basketball down to size soon after it's launched - name this action
// so that it can be cancelled if the ball flies offscreen
basketball.run(SKAction.scale(to: basketballScale, duration: 0.66), withKey: "scale")
// The wind label
// Change Collision Bitmask
let wait = SKAction.wait(forDuration: c.airTime / 2)
let changeCollision = SKAction.run({
self.basketball.physicsBody?.collisionBitMask = self.netFront
self.basketball.zPosition = self.bg.zPosition + 2
})
// ADD WIND!!
let windWait = SKAction.wait(forDuration: c.airTime / 4)
let push = SKAction.applyImpulse(CGVector(dx: wind, dy: 0), duration: 1)
basketball.run(SKAction.sequence([windWait, push]))
basketball.run(SKAction.sequence([wait,changeCollision]))
// Wait & reset
let wait4 = SKAction.wait(forDuration: 4)
let reset = SKAction.run({
self.setWind()
})
self.run(SKAction.sequence([wait4,reset]))
}
// Fetch a new wind speed and store it for use on the ball
func setWind() {
let multi = CGFloat(50)
let rnd = CGFloat(arc4random_uniform(UInt32(10))) - 5
windLbl.text = "Wind: \(rnd)"
wind = rnd * multi
}
func didBegin(_ contact: SKPhysicsContact) {
// check for the ball contacting the scoreZone
guard let ballBody = basketball.physicsBody, let scoreBody = scoreZone.physicsBody else {
return
}
// it doesn't matter who touches who, so just use array "contains" to handle both cases
let bodies = [contact.bodyA, contact.bodyB]
if bodies.contains(ballBody) && bodies.contains(scoreBody) && shouldDetectScoreCollision {
addScore()
}
}
func addScore() {
score += 1
scoreLabel.text = "\(score)"
shouldDetectScoreCollision = false
collidedWithScoreZone = true
}
// overridden touch methods to call our touch method for each touch
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { touchDown(atPoint: t.location(in: self), atTime: t.timestamp) }
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { touchMoved(atPoint: t.location(in: self), atTime: t.timestamp) }
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { touchUp(atPoint: t.location(in: self), atTime: t.timestamp) }
}
// called each tick
override func update(_ currentTime: TimeInterval) {
// check to see if the ball is past the net
let halfBall = basketball.size.height/2.0
let ballClearHeight = basketball.position.y - halfBall
if !shouldDetectScoreCollision && ballClearHeight.native > basketballHoopHeight {
// allow colliding with the hoop
basketball.physicsBody?.collisionBitMask = hoopCategory
// put the ball behind the net in Z
basketball.zPosition = droppingZ
shouldDetectScoreCollision = true
}
// check for basketball leaving screen in (almost) any direction
if basketball.position.y + halfBall < self.frame.minY
|| basketball.position.x + halfBall < self.frame.minX
|| basketball.position.x - halfBall > self.frame.maxX {
resetBasketBall()
}
}