我正在使用Xcode 9.4创建一个非常简单的游戏。当用户启动该应用程序时,他/她可以移动一个网来捕捉根据http get请求定义的路径从屏幕顶部飞到底部的苍蝇。
该应用程序可在模拟器中完美运行。
我已将该应用上传到应用商店进行测试。当用户通过TestFlight下载该应用程序时,它在首次使用时可以完美运行。但是,如果他们关闭应用程序并重新启动,则不会出现苍蝇。否则,场景将按预期渲染。如果他们随后删除了该应用程序,然后重新安装并启动它,则苍蝇会再次出现。但是同样,如果他们关闭该应用程序并重新启动它,那将不会成功。
我已经拖网了,没有找到任何指导。有什么建议吗?
import SpriteKit
func +(left: CGPoint, right: CGPoint) -> CGPoint {
return CGPoint(x: left.x + right.x, y: left.y + right.y)
}
func -(left: CGPoint, right: CGPoint) -> CGPoint {
return CGPoint(x: left.x - right.x, y: left.y - right.y)
}
func *(point: CGPoint, scalar: CGFloat) -> CGPoint {
return CGPoint(x: point.x * scalar, y: point.y * scalar)
}
func /(point: CGPoint, scalar: CGFloat) -> CGPoint {
return CGPoint(x: point.x / scalar, y: point.y / scalar)
}
#if !(arch(x86_64) || arch(arm64))
func sqrt(a: CGFloat) -> CGFloat {
return CGFloat(sqrtf(Float(a)))
}
#endif
extension CGPoint {
func length() -> CGFloat {
return sqrt(x*x + y*y)
}
func normalized() -> CGPoint {
return self / length()
}
}
class GameScene: SKScene {
struct PhysicsCategory {
static let none : UInt32 = 0
static let all : UInt32 = UInt32.max
static let fly : UInt32 = 0b1
static let net: UInt32 = 0b10
}
let net = SKSpriteNode(imageNamed: "net")
var fliesAdded = 0
var fliesCaught = 0
let maxFlies = 10
var level = CGFloat(1.0)
var gameKey: String?
var lives = 3
var levelCounter = 0
override func didMove(to view: SKView) {
backgroundColor = SKColor.white
// Set up the properties of the net
net.scale(to: CGSize.init(width: net.size.width/10, height: net.size.height/10))
net.physicsBody = SKPhysicsBody(rectangleOf: net.size)
net.physicsBody?.isDynamic = false
net.physicsBody?.categoryBitMask = PhysicsCategory.net
net.physicsBody?.contactTestBitMask = PhysicsCategory.fly
net.physicsBody?.collisionBitMask = PhysicsCategory.none
net.position = CGPoint(x: size.width * 0.5, y: size.height * 0.1)
// 4
addChild(net)
physicsWorld.gravity = .zero
physicsWorld.contactDelegate = self
self.isPaused = false
run(SKAction.run(addFly))
let backgroundMusic = SKAudioNode(fileNamed: "background-music-aac.caf")
backgroundMusic.autoplayLooped = true
addChild(backgroundMusic)
// Add a label for the score
let scoreLabel = SKLabelNode()
scoreLabel.fontColor = UIColor.black
scoreLabel.fontSize = 65
scoreLabel.name = "scoreLabel"
scoreLabel.position = CGPoint(x: size.width/2, y: size.height/2);
scoreLabel.text = "0";
self.addChild(scoreLabel)
// Add a label for the level
let levelLabel = SKLabelNode()
levelLabel.fontColor = UIColor.black
levelLabel.fontSize = 32.5
levelLabel.name = "levelLabel"
levelLabel.text = "level \(Int(level))"
levelLabel.horizontalAlignmentMode = .left
levelLabel.verticalAlignmentMode = .top
levelLabel.position = CGPoint(x: 0, y: self.size.height)
self.addChild(levelLabel)
// Add a label for the lives
let livesLabel = SKLabelNode()
livesLabel.fontColor = UIColor.black
livesLabel.fontSize = 32.5
livesLabel.name = "livesLabel"
livesLabel.text = "3/3 lives"
livesLabel.horizontalAlignmentMode = .right
livesLabel.verticalAlignmentMode = .top
livesLabel.position = CGPoint(x:self.size.width, y:self.size.height)
self.addChild(livesLabel)
// Get the starting level
let urlComp = NSURLComponents(string: "---")!
let params = [URLQueryItem(name: "UserName", value: (UIApplication.shared.delegate as! AppDelegate).userName)]
urlComp.queryItems = params
var request = URLRequest(url: urlComp.url!)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
do {
let result = try JSONDecoder().decode(LevelResponseObject.self, from: data!)
let userHighestLevel = Int(result.level) ?? 1
if userHighestLevel > 5 {
self.level = CGFloat(userHighestLevel) - 5.0
}
else {
self.level = CGFloat(userHighestLevel)
}
if let levelLabel = self.childNode(withName: "levelLabel") as? SKLabelNode {
levelLabel.text = "level \(Int(self.level))"
}
} catch {
print(error)
}
}
task.resume()
}
func touchDown(atPoint pos : CGPoint) {
//net.position = CGPoint(x: pos.x, y: size.height * 0.1)
}
func touchMoved(toPoint pos : CGPoint) {
//net.position = CGPoint(x: pos.x, y: size.height * 0.1)
}
func touchUp(atPoint pos : CGPoint) {
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let touchLocation = touch.location(in: self)
net.position = CGPoint(x: touchLocation.x, y: size.height * 0.1)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
// MARK: Private functions
func addFly() {
let origin = (UIApplication.shared.delegate as! AppDelegate).dataOrigin
let urlComp = NSURLComponents(string: "---")!
let params = [URLQueryItem(name: "Origin", value: origin)]
urlComp.queryItems = params
var request = URLRequest(url: urlComp.url!)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
do {
let result = try JSONDecoder().decode(ResponseObject.self, from: data!)
let rows = result.items[0].Data.components(separatedBy: ";")
let firstPoint = Double(rows[0]) ?? 0.0
//let xCoords = rows.map{ CGFloat(NSString(string: $0).doubleValue - firstPoint)*self.size.width/157.4*2 + self.size.width/2 }
let xCoords = rows.map{ self.size.width/(1+exp(-(CGFloat(NSString(string: $0).doubleValue - firstPoint)/10)))} // increasing the denominator in the exponential will make the relationship more linear, decreasing it will exaggerate moves near to the center
self.gameKey = result.items[0].Key
self.fliesAdded += 1
// Create sprite
print("New Fly")
print("Level \(self.level)")
let fly = SKSpriteNode(imageNamed: "fly")
fly.scale(to: CGSize.init(width: self.size.width/20, height: fly.size.height/fly.size.width*self.size.width/20))
fly.physicsBody = SKPhysicsBody(rectangleOf: fly.size)
fly.physicsBody?.isDynamic = true
fly.physicsBody?.categoryBitMask = PhysicsCategory.fly
fly.physicsBody?.contactTestBitMask = PhysicsCategory.none
fly.physicsBody?.collisionBitMask = PhysicsCategory.none
// Position the fly slightly off-screen along the top edge,
// in the middle of the x axis
fly.position = CGPoint(x: self.size.width/2, y: self.size.height + fly.size.height/2)
// Add the fly to the scene
self.addChild(fly)
var flightSequence: [SKAction] = []
var currentY = fly.position.y
var currentX = fly.position.x
let yDistanceToTravel = currentY - (self.net.position.y + self.net.size.height/2)
let yIncrement = yDistanceToTravel / CGFloat(xCoords.count - 1)
var duration = CGFloat(8.28)/CGFloat(xCoords.count - 1)
duration = duration/(1.0 + self.level/10)
// Create a sequence of moves for the fly
for targetX in xCoords {
// Set the parameters for the move
let targetY = currentY - yIncrement
let flyTurn = SKAction.rotate(toAngle: -atan((targetX-currentX)/(targetY-currentY)), duration: 0.0, shortestUnitArc: true)
flightSequence.append(flyTurn)
let flyMovement = SKAction.move(to: CGPoint(x: targetX, y: targetY), duration: TimeInterval(duration))
flightSequence.append(flyMovement)
currentY = targetY
currentX = targetX
}
// After the fly passes the net send it vertically down
let flyTurn = SKAction.rotate(toAngle: 0.0, duration: 0.0, shortestUnitArc: true)
flightSequence.append(flyTurn)
let flyMovement = SKAction.move(to: CGPoint(x: currentX, y: -fly.size.height/2), duration: TimeInterval(duration))
flightSequence.append(flyMovement)
// Remove the fly after it passes the net and end the game
let flightOver = SKAction.run() { [weak self] in
guard let `self` = self else { return }
if (self.gameKey != nil){
self.recordDistance(fly: fly, net: self.net, gameKey: self.gameKey!, level: self.level, win: false)
}
fly.removeFromParent()
self.gameOver()
}
flightSequence.append(flightOver)
fly.run(SKAction.sequence(flightSequence))
} catch {
print(error)
}
}
task.resume()
}
func gameOver() {
if (lives == 1) {
let reveal = SKTransition.flipHorizontal(withDuration: 0.5)
let scoreScene = ScoreScene(size: self.size, score: fliesCaught, level: Int(level))
view?.presentScene(scoreScene, transition: reveal)
}
else {
lives = lives - 1
if let livesLabel = self.childNode(withName: "livesLabel") as? SKLabelNode {
livesLabel.text = "\(lives)/3 lives"
}
addFly()
}
}
func flyCaught(fly: SKSpriteNode, net: SKSpriteNode, gameKey: String?) {
if (gameKey != nil) {
recordDistance(fly: fly, net: net, gameKey: gameKey!, level: self.level, win: true)
}
fly.removeFromParent()
fliesCaught += 1
//print("Score = \(fliesCaught)")
if let scoreLabel = self.childNode(withName: "scoreLabel") as? SKLabelNode {
scoreLabel.text = "\(fliesCaught)"
}
if levelCounter == 4 {
levelCounter = 0
level = level + 1.0
if let levelLabel = self.childNode(withName: "levelLabel") as? SKLabelNode {
levelLabel.text = "level \(Int(level))"
}
}
else {
levelCounter = levelCounter + 1
}
addFly()
}
func recordDistance(fly: SKSpriteNode, net: SKSpriteNode, gameKey: String, level: CGFloat, win: Bool) {
let distance = fly.position.x - net.position.x
//print("Distance to center of net = \(distance)")
// prepare json data
let json: [String: Any] = ["UserName": (UIApplication.shared.delegate as! AppDelegate).userName,
"DataKey": gameKey,
"Level": Int(level),
"Win": win,
"DistanceToCenter": distance,
"ScreenWidth": self.size.width]
let jsonData = try? JSONSerialization.data(withJSONObject: json)
// create post request
let url = URL(string: "---")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
// insert json data to the request
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let _ = data, error == nil else { // check for fundamental networking error
print("error=\(String(describing: error))")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(String(describing: response))")
}
}
task.resume()
}
}
extension GameScene: SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
// 1
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if ((firstBody.categoryBitMask & PhysicsCategory.fly != 0) &&
(secondBody.categoryBitMask & PhysicsCategory.net != 0)) {
if let fly = firstBody.node as? SKSpriteNode,
let net = secondBody.node as? SKSpriteNode {
//print("Collision with net")
if fly.position.y > net.position.y + net.size.height/2 {
//print("Fly Caught")
flyCaught(fly: fly, net: net, gameKey: gameKey)
}
}
}
}
}
struct ResponseObject: Decodable {
let items: [RouteItem]
}
struct RouteItem: Decodable {
let Key: String
let Origin: String
let Data: String
}
struct LevelResponseObject: Decodable {
let level: String
}