I have made a game where the basic idea is that a player sprite has to either collide with or avoid falling enemy sprites depending on the letter assigned to the enemy. The problem is that when my player makes contact with a sprite, the surrounding enemies skip forward significantly. This makes the game too hard to play because there isn't enough time for the player to move out of the way of oncoming enemy sprites. This happens on the device and the simulator.
To see what I mean, look at this brief video. You can see the lag at 00:06 and 00:013: https://www.dropbox.com/s/8pp66baxc9uhy26/SimulatorScreenSnapz002.mov?dl=0
Here is my code for player and enemy contact:
class GameplaySceneClass: SKScene, SKPhysicsContactDelegate {
private var player:Player?
private var center = CGFloat()
private var canMove = false, moveLeft = false
private var itemController = ItemController()
private var scoreLabel: SKLabelNode?
private var wordLabel: SKLabelNode?
private var score = 0
private var theWord = ""
private var vowelPressed = false
let correct = SoundSFX("correct.wav")
let wrong = SoundSFX("nomatch.wav")
let explosion = SoundSFX("explosion.wav")
var arrayOfStrings: [String]?
var theCheckedWord = ""
var currentCount = 0
var itemsArray = [String]()
var partialExists = false
var characterScore = 0
var onePointLetters = [12, 14, 18, 19, 20]
var twoPointLetters = [4, 7]
var threePointLetters = [2, 3, 13, 16]
var fourPointLetters = [6, 8, 22, 23, 25]
var fivePointLetters = [11]
var eightPointLetters = [10, 24]
var tenPointLetters = [17, 26]
var letter = 0
var scoreArray = [Int]()
var matchingTerms = [String]()
var gravity:CGFloat = -0.35
var result = CGSize()
let synth = AVSpeechSynthesizer()
var myUtterance = AVSpeechUtterance(string:"")
override func didMove(to view: SKView) {
SoundEngine.shared.backgroundMusicVolume = 1.0
SoundEngine.shared.playBackgroundMusic("Jungle Audio-1.m4a", loop: true)
initializeGame()
}
override func update(_ currentTime: TimeInterval) {
managePlayer()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location (in: self)
if atPoint(location).name == "LetterA" {
print ("Letter A pressed")
vowelPressed = true
theWord = theWord + "A"
wordLabel?.text = theWord
if theWord.characters.count >= 3 {
checkWord()
}
vowelPressed = false
}
else if atPoint(location).name == "LetterE" {
print ("Letter E pressed")
vowelPressed = true
theWord = theWord + "E"
wordLabel?.text = theWord
if theWord.characters.count >= 3 {
checkWord()
}
vowelPressed = false
}
else if atPoint(location).name == "LetterI" {
print ("Letter I pressed")
vowelPressed = true
theWord = theWord + "I"
wordLabel?.text = theWord
if theWord.characters.count >= 3 {
checkWord()
}
vowelPressed = false
}
else if atPoint(location).name == "LetterO" {
print ("Letter O pressed")
vowelPressed = true
theWord = theWord + "O"
wordLabel?.text = theWord
if theWord.characters.count >= 3 {
checkWord()
}
vowelPressed = false
}
else if atPoint(location).name == "LetterU" {
print ("Letter U pressed")
vowelPressed = true
theWord = theWord + "U"
wordLabel?.text = theWord
if theWord.characters.count >= 3 {
checkWord()
}
vowelPressed = false
}
else if atPoint(location).name == "Pause" {
showPauseAlert()
}
else if atPoint(location).name == "delete" {
theWord = ""
theCheckedWord = ""
wordLabel?.text = theWord
}
else {
if location.x > center && !vowelPressed {
moveLeft = false
}
else if location.x < center && !vowelPressed {
moveLeft = true
}
canMove = true
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
canMove = false
}
func didBegin(_ contact: SKPhysicsContact) {
if !gamePaused {
var firstBody = SKPhysicsBody()
var secondBody = SKPhysicsBody()
if contact.bodyA.node?.name == "Player" {
firstBody = contact.bodyA
secondBody = contact.bodyB
}
else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if firstBody.node?.name == "Player" && secondBody.node?.name == "Tile" {
letter = secondBody.node?.userData?.value(forKey:"key") as! Int
print ("The value of letter is \(letter)")
var letterCode = String(describing: letter)
var letterValue = Int(letterCode)
var finalLetterValue = letterValue!+64
var ASCIICode = Character(UnicodeScalar(finalLetterValue)!)
var letterString = String(describing:ASCIICode)
theWord = theWord + letterString
print (theWord)
if onePointLetters.contains(letter) {
characterScore += 1
}
else if twoPointLetters.contains(letter) {
characterScore += 2
}
else if threePointLetters.contains(letter) {
characterScore += 3
}
else if fourPointLetters.contains(letter) {
characterScore += 4
}
else if fivePointLetters.contains(letter) {
characterScore += 5
}
else if eightPointLetters.contains(letter) {
characterScore += 8
}
else if tenPointLetters.contains(letter) {
characterScore += 10
}
wordLabel?.text = theWord
theCheckedWord = theWord.lowercased()
print ("The checked word is \(theCheckedWord)")
checkWord()
secondBody.node?.removeFromParent()
}
if firstBody.node?.name == "Player" && secondBody.node?.name == "Bomb" {
explosion.play()
firstBody.node?.removeFromParent()
secondBody.node?.removeFromParent()
theWord = ""
score = 0
scoreLabel?.text = String(score)
var delayTimer = SKAction.wait(forDuration:1)
run (delayTimer)
restartGame()
}
}
}
private func initializeGame() {
do {
// This solution assumes you've got the file in your bundle
if let path = Bundle.main.path(forResource: "en", ofType: "txt"){
let data = try String(contentsOfFile:path, encoding: String.Encoding.utf8)
arrayOfStrings = data.components(separatedBy: "\n")
}
} catch let err as NSError {
// do something with Error
print(err)
}
physicsWorld.contactDelegate = self
physicsWorld.gravity = CGVector(dx:0,dy:gravity)
player = childNode(withName: "Player") as? Player!
player?.initializePlayer()
player?.position = CGPoint(x:0, y: -420)
scoreLabel = childNode(withName: "ScoreLabel") as? SKLabelNode!
scoreLabel?.text = String(score)
wordLabel = childNode(withName:"WordLabel") as? SKLabelNode!
wordLabel?.text = ""
center = self.frame.size.width/self.frame.size.height
var spawnBlocks = SKAction.repeatForever(SKAction.sequence([SKAction.wait(forDuration:1),SKAction.run(spawnItems)]))
var removeBlocks = SKAction.repeatForever(SKAction.sequence([SKAction.wait(forDuration:1),SKAction.run(removeItems)]))
if gamePaused == true {
self.scene?.isPaused = true
}
self.run(spawnBlocks)
self.run(removeBlocks)
}
private func checkWord() {
theCheckedWord = theWord.lowercased()
if (arrayOfStrings?.contains(theCheckedWord))! {
print ("Yes! \(theCheckedWord) is a word")
correct.play()
print ("The current value of score is \(score)")
score += characterScore
print ("Current gravity setting is \(gravity)")
scoreLabel?.text = String(score)
characterScore = 0
matchingTerms = (arrayOfStrings?.filter({$0.hasPrefix(theCheckedWord)
}))!
if matchingTerms.count == 1 {
characterScore = 0
print ("Current gravity setting is \(gravity)")
theWord = ""
theCheckedWord = ""
wordLabel?.text = ""
}
}
else if !(arrayOfStrings?.contains(theCheckedWord))! {
matchingTerms = (arrayOfStrings?.filter({$0.hasPrefix(theCheckedWord)
}))!
if matchingTerms.count == 0 {
wrong.play()
characterScore = 0
if score >= 5 {
score -= 5
}
theWord = ""
theCheckedWord = ""
wordLabel?.text = ""
}
}
}
Here is my code for the enemies:
import SpriteKit
struct ColliderType {
static let PLAYER: UInt32 = 0
static let TILE_AND_BOMB: UInt32 = 1;
}
public var bounds = UIScreen.main.bounds
public var width = bounds.size.width
public var height = bounds.size.height
class ItemController {
private var minX = CGFloat(-(width/2)), maxX = CGFloat(width/2)
private var vowelNumbers = [1, 5, 9, 15, 21]
func spawnItems() -> SKSpriteNode {
let item: SKSpriteNode?
if Int(randomBetweenNumbers(firstNum: 0, secondNum: 10)) > 7 {
item = SKSpriteNode(imageNamed: "Bomb")
item!.name = "Bomb"
item!.setScale(0.6)
item!.physicsBody = SKPhysicsBody(circleOfRadius: item!.size.height / 2)
print ("The value of width is \(width)")
} else {
var num = Int(randomBetweenNumbers(firstNum: 1, secondNum: 26))
if vowelNumbers.contains(num) {
num = num + 1
}
item = SKSpriteNode(imageNamed: "Tile \(num)")
item?.userData = NSMutableDictionary()
item!.name = "Tile"
item!.userData!.setValue(num, forKey:"key")
item!.zRotation = 0
item!.setScale(0.9)
item!.physicsBody = SKPhysicsBody(circleOfRadius: item!.size.height / 2)
if gamePaused == true {
item!.isPaused = true
}
else {
item!.isPaused = false
}
}
item!.physicsBody?.categoryBitMask = ColliderType.TILE_AND_BOMB;
item!.physicsBody?.isDynamic = true
item!.zPosition = 3
item!.anchorPoint = CGPoint(x: 0.5, y: 0.5)
item!.position.x = randomBetweenNumbers(firstNum: minX, secondNum: maxX)
item!.position.y = 600
return item!
}
func randomBetweenNumbers(firstNum: CGFloat, secondNum: CGFloat) -> CGFloat {
return CGFloat(arc4random()) / CGFloat(UINT32_MAX) * abs(firstNum - secondNum) + min(firstNum, secondNum);
}
}
And here is my code for the player:
import SpriteKit
class Player: SKSpriteNode {
private var minX = CGFloat(-300), maxX = CGFloat(300)
func initializePlayer() {
name = "Player"
physicsBody = SKPhysicsBody(circleOfRadius: size.height/2)
physicsBody?.affectedByGravity = false
physicsBody?.isDynamic = false
physicsBody?.categoryBitMask = ColliderType.PLAYER
physicsBody?.contactTestBitMask = ColliderType.TILE_AND_BOMB
}
func move(left:Bool){
if left {
position.x -= 15
if position.x < minX {
position.x = minX
}
}
else {
position.x += 15
if position.x > maxX {
position.x = maxX
}
}
}
}
arrayOfStrings
? – nathangitterskView.showsDrawsCount = true
to see how many draw calls are required for your scene to be rendered (and post that number here). – Whirlwind