SKTexture: Error loading image resource: "Tile_-4412407809" - swift

I'm trying to find out what is causing this Tile_xxxxx name to be some sort of long random number in this code.
SKTexture: Error loading image resource: "Tile_-4412407809"
The code related to it is in this tutorial: https://www.raywenderlich.com/54-how-to-make-a-game-like-candy-crush-with-spritekit-and-swift-part-2
The Tile name is set in the swift file GameScene.swift line 144.
let name = String(format: "Tile_%ld", value)
and it's probably best if I put the code from the file in here. Something is setting "value" to something it can't use. But what? Where is it getting hashValue from
var value = topLeft.hashValue
import SpriteKit
import GameplayKit
class GameScene: SKScene {
// Sound FX
let swapSound = SKAction.playSoundFileNamed("Chomp.wav", waitForCompletion: false)
let invalidSwapSound = SKAction.playSoundFileNamed("Error.wav", waitForCompletion: false)
let matchSound = SKAction.playSoundFileNamed("Ka-Ching.wav", waitForCompletion: false)
let fallingCookieSound = SKAction.playSoundFileNamed("Scrape.wav", waitForCompletion: false)
let addCookieSound = SKAction.playSoundFileNamed("Drip.wav", waitForCompletion: false)
var level: Level!
let tilesLayer = SKNode()
let cropLayer = SKCropNode()
let maskLayer = SKNode()
let tileWidth: CGFloat = 32.0
let tileHeight: CGFloat = 36.0
let gameLayer = SKNode()
let cookiesLayer = SKNode()
var swipeHandler: ((Swap) -> Void)?
private var swipeFromColumn: Int?
private var swipeFromRow: Int?
private var selectionSprite = SKSpriteNode()
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder) is not used in this app")
}
override init(size: CGSize) {
super.init(size: size)
anchorPoint = CGPoint(x: 0.5, y: 0.5)
// This is the Witches background image
let background = SKSpriteNode(imageNamed: "Background")
background.size = size
addChild(background)
addChild(gameLayer)
gameLayer.isHidden = true
let layerPosition = CGPoint(
x: -tileWidth * CGFloat(numColumns) / 2,
y: -tileHeight * CGFloat(numRows) / 2)
tilesLayer.position = layerPosition
maskLayer.position = layerPosition
cropLayer.maskNode = maskLayer
gameLayer.addChild(tilesLayer)
gameLayer.addChild(cropLayer)
cookiesLayer.position = layerPosition
cropLayer.addChild(cookiesLayer)
let _ = SKLabelNode(fontNamed: "GillSans-BoldItalic")
}
func addSprites(for cookies: Set<Cookie>) {
for cookie in cookies {
let sprite = SKSpriteNode(imageNamed: cookie.cookieType.spriteName)
sprite.size = CGSize(width: tileWidth, height: tileHeight)
sprite.position = pointFor(column: cookie.column, row: cookie.row)
cookiesLayer.addChild(sprite)
cookie.sprite = sprite
// Give each cookie sprite a small, random delay. Then fade them in.
sprite.alpha = 0
sprite.xScale = 0.5
sprite.yScale = 0.5
sprite.run(
SKAction.sequence([
SKAction.wait(forDuration: 0.25, withRange: 0.5),
SKAction.group([
SKAction.fadeIn(withDuration: 0.25),
SKAction.scale(to: 1.0, duration: 0.25)
])
]))
}
}
func addTiles() {
for row in 0..<numRows {
for column in 0..<numColumns {
if level.tileAt(column: column, row: row) != nil {
let tileNode = SKSpriteNode(imageNamed: "MaskTile")
tileNode.size = CGSize(width: tileWidth, height: tileHeight)
tileNode.position = pointFor(column: column, row: row)
maskLayer.addChild(tileNode)
}
}
}
for row in 0...numRows {
for column in 0...numColumns {
let topLeft = (column > 0) && (row < numRows)
&& level.tileAt(column: column - 1, row: row) != nil
let bottomLeft = (column > 0) && (row > 0)
&& level.tileAt(column: column - 1, row: row - 1) != nil
let topRight = (column < numColumns) && (row < numRows)
&& level.tileAt(column: column, row: row) != nil
let bottomRight = (column < numColumns) && (row > 0)
&& level.tileAt(column: column, row: row - 1) != nil
var value = topLeft.hashValue
value = value | topRight.hashValue << 1
value = value | bottomLeft.hashValue << 2
value = value | bottomRight.hashValue << 3
// Values 0 (no tiles), 6 and 9 (two opposite tiles) are not drawn.
if value != 0 && value != 6 && value != 9 {
let name = String(format: "Tile_%ld", value)
//print(name)
let tileNode = SKSpriteNode(imageNamed: name)
tileNode.size = CGSize(width: tileWidth, height: tileHeight)
var point = pointFor(column: column, row: row)
point.x -= tileWidth / 2
point.y -= tileHeight / 2
tileNode.position = point
tilesLayer.addChild(tileNode)
}
}
}
}

Fixed.
Change
var value = topLeft.hashable
value = value | topRight.hashable << 1
value = value | bottomLeft.hashable << 2
value = value | bottomRight.hashable << 3
to
var value = (topLeft ? 1 : 0)
value = value | (topRight ? 1 : 0) << 1
value = value | (bottomLeft ? 1 : 0) << 2
value = value | (bottomRight ? 1 : 0) << 3

Related

Allow PieChartView to hide value line for tiny slices in Swift

I'm building a pie chart by chart iOS framework. I'm able to hide the value and label when the the slice is tiny but I can't hide the value line for tiny slice.
If I add this code on setDataCount()
set.valueLineWidth = 0.0
It will hide all the value line. How to hide it by the size of slice?
#IBOutlet weak var myChart: PieChartView!
var valueColors = [UIColor]()
var dataEntries = [PieChartDataEntry]()
var record = [Record]()
var category = [String]()
var categoryTotal : [Double] = []
var categoryArray : [String] = []
func setDataCount() {
var totalValue = 0.0
for a in categoryTotal {
totalValue += a
}
UserDefaults.standard.set(totalValue, forKey: "totalValue")
valueAndColor()
let set = PieChartDataSet(values: dataEntries, label: nil)
set.colors = valueColors
set.valueLinePart1OffsetPercentage = 0.8
set.valueLinePart1Length = 0.2
set.valueLinePart2Length = 0.4
set.xValuePosition = .outsideSlice
set.yValuePosition = .outsideSlice
set.selectionShift = 0.0
let data = PieChartData(dataSet: set)
let Formatter:ChartFormatter = ChartFormatter()
data.setValueFormatter(Formatter)
data.setValueFont(.systemFont(ofSize: 11, weight: .light))
data.setValueTextColor(.black)
myChart.data = data
myChart.highlightValues(nil)
}
func valueAndColor(){
for i in 0..<categoryArray.count{
let dataEntry = PieChartDataEntry(value: categoryTotal[i], label: categoryArray[i % categoryArray.count])
dataEntries.append(dataEntry)
//I'm using this code to hide the label
let value = categoryTotal[i]
let total = UserDefaults.standard.double(forKey: "totalValue")
var valueToUse = value/total * 100
valueToUse = Double(round(10*valueToUse)/10)
let minNumber = 10.0
if(valueToUse < minNumber) {
dataEntries[i].label = ""
}else{
dataEntries[i].label = categoryArray[i % categoryArray.count]
}
if categoryArray[i] == "吃喝" {
valueColors.append(UIColor.yellow)
}...
}
I'm using this code to hide the value %
public class ChartFormatter: NSObject, IValueFormatter{
public func stringForValue(_ value: Double, entry: ChartDataEntry, dataSetIndex: Int, viewPortHandler: ViewPortHandler?) -> String {
let total = UserDefaults.standard.double(forKey: "totalValue")
var valueToUse = value/total * 100
valueToUse = Double(round(10*valueToUse)/10)
let minNumber = 10.0
if(valueToUse < minNumber) {
return ""
}
else {
let pFormatter = NumberFormatter()
pFormatter.numberStyle = .percent
pFormatter.maximumFractionDigits = 1
pFormatter.multiplier = 1
pFormatter.percentSymbol = " %"
let hideValue = pFormatter.string(from: NSNumber(value: value))
return String(hideValue ?? "0")
}
}
}
Inside the file PieChartRenderer change from this:
if dataSet.valueLineColor != nil
{
context.setStrokeColor(dataSet.valueLineColor!.cgColor)
context.setLineWidth(dataSet.valueLineWidth)
context.move(to: CGPoint(x: pt0.x, y: pt0.y))
context.addLine(to: CGPoint(x: pt1.x, y: pt1.y))
context.addLine(to: CGPoint(x: pt2.x, y: pt2.y))
context.drawPath(using: CGPathDrawingMode.stroke)
}
to this:
if dataSet.valueLineColor != nil
{
if(valueText == "") {
context.setStrokeColor(UIColor.clear.cgColor)
}
else {
context.setStrokeColor(dataSet.valueLineColor!.cgColor)
}
context.setLineWidth(dataSet.valueLineWidth)
context.move(to: CGPoint(x: pt0.x, y: pt0.y))
context.addLine(to: CGPoint(x: pt1.x, y: pt1.y))
context.addLine(to: CGPoint(x: pt2.x, y: pt2.y))
context.drawPath(using: CGPathDrawingMode.stroke)
}
The change basically checks if the valueText is the empty string, and if so it changes the linecolor to a clear color.

How to make sprites follow a random pattern within a circle?

I am makeing a game in which I want that the enemies move following a random pattern within a circle. I already made that the enemies spawn randomly in all the sides of the screen, but the problem is that I dont know how to make the enemies move following a random pattern within a circle just like the image.
class GameScene: SKScene, SKPhysicsContactDelegate {
var circuloPrincipal = SKSpriteNode(imageNamed: "circulo")
var enemigoTimer = NSTimer()
}
override func didMoveToView(view: SKView) {
circuloPrincipal.size = CGSize(width: 225, height: 225)
circuloPrincipal.position = CGPoint(x: frame.width / 2, y: frame.height / 2)
circuloPrincipal.color = colorAzul
circuloPrincipal.colorBlendFactor = 1.0
circuloPrincipal.name = "circuloPrincipal"
circuloPrincipal.zPosition = 1.0
self.addChild(circuloPrincipal)
override func touchesBegan(touches: Set, withEvent event: UIEvent?) {
enemigoTimer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: Selector("enemigos"), userInfo: nil, repeats: true)
}
func enemigos() {
let enemigo = SKSpriteNode(imageNamed: "enemigo")
enemigo.size = CGSize(width: 25, height: 25)
enemigo.zPosition = 2.0
enemigo.name = "enemigo"
let posisionRandom = arc4random() % 4
switch posisionRandom {
case 0:
enemigo.position.x = 0
let posisionY = arc4random_uniform(UInt32(frame.size.height))
enemigo.position.y = CGFloat(posisionY)
self.addChild(enemigo)
break
case 1:
enemigo.position.y = 0
let posisionX = arc4random_uniform(UInt32(frame.size.width))
enemigo.position.x = CGFloat(posisionX)
self.addChild(enemigo)
break
case 2:
enemigo.position.y = frame.size.height
let posisionX = arc4random_uniform(UInt32(frame.size.width))
enemigo.position.x = CGFloat(posisionX)
self.addChild(enemigo)
break
case 3:
enemigo.position.x = frame.size.width
let posisionY = arc4random_uniform(UInt32(frame.size.height))
enemigo.position.y = CGFloat(posisionY)
self.addChild(enemigo)
break
default:
break
}
enemigo.runAction(SKAction.moveTo(circuloPrincipal.position, duration: 1.4))
}
Try to add this code:
let randomY = CGFloat(Int.random(-Int(circuloPrincipal.frame.height/2)...Int(circuloPrincipal.frame.height/2)))
let randomX = CGFloat(Int.random(-Int(circuloPrincipal.frame.width/2)...Int(circuloPrincipal.frame.width/2)))
let slopeToCirculoPrincipal = (enemigo.position.y - circuloPrincipal.position.y + randomY ) / (enemigo.position.x - circuloPrincipal.position.x + randomX)
let constant = enemigo.position.y - slopeToCirculoPrincipal * enemigo.position.x
let finalX : CGFloat = enemigo.position.x < circuloPrincipal.position.x ? 1500.0 : -1500.0 // Set it to somewhere outside screen size
let finalY = constant + slopeToCirculoPrincipal * finalX
let distance = (enemigo.position.y - finalY) * (enemigo.position.y - finalY) + (enemigo.position.x - finalX) * (enemigo.position.x - finalX)
let enemigoSpeed : CGFloat = 100.0
let timeToCoverDistance = sqrt(distance) / enemigoSpeed
let moveAction = SKAction.moveTo(CGPointMake(finalX, finalY), duration: NSTimeInterval(timeToCoverDistance))
let removeAction = SKAction.runBlock { () -> Void in
enemigo.removeFromParent()
}
enemigo.runAction(SKAction.sequence([moveAction,removeAction]))
Instead of:
enemigo.runAction(SKAction.moveTo(circuloPrincipal.position, duration: 1.4))
Also you need to put this extension somewhere in your project:
extension Int
{
static func random(range: Range<Int> ) -> Int
{
var offset = 0
if range.startIndex < 0 // allow negative ranges
{
offset = abs(range.startIndex)
}
let mini = UInt32(range.startIndex + offset)
let maxi = UInt32(range.endIndex + offset)
return Int(mini + arc4random_uniform(maxi - mini)) - offset
}
}

spritekit collision detection inconsistency (failing to get position of both nodes)

Within didBeginContact function I'm successfully capturing the collision between a horizontal grid line and a circle.
At the collision I can successfully get the position of the circle(head).
if let headBody = contact.bodyB.node as? SnakeBodyUnit {
print("head position ", headBody.position)
}
This successfully prints out:
head position (87.536979675293, 267.116882324219)
Now if I try to examine the horizontal line:
if let tripRow = contact.bodyA.node as? HeadTripper {
print("row position ", tripRow.position)
}
I get the following error:
po tripRow
error: :1:1: error: use of unresolved identifier 'tripRow'
tripRow
Here is the condition where this detection fires:
if (contact.bodyA.categoryBitMask == ColliderType.TripRow && contact.bodyB.categoryBitMask == ColliderType.Head) {
//contact successfully fires
}
The HeadTripper class and the SnakeBodyUnit class are both SKNode classes.
Here is the SnakeBodyUnit class:
import SpriteKit
class SnakeBodyUnit : SKNode {
var bodyDir: Direction?
var id = -1
var staticIDref = -1
var partX:CGFloat = -1
var partY:CGFloat = -1
var bodyT = -1
var staticTurn: TurnCrumb?
var turnRequested = 0
var requestedTurn: Direction?
var unitHolder: SKSpriteNode?
init(size: CGSize, bodyType: Int) {
super.init()
bodyT = bodyType
let reducedSize = size.width
unitHolder = SKSpriteNode()
unitHolder!.size = CGSize(width: (size.width), height: (size.height))
if (bodyT == 0) {
//head
unitHolder!.color = UIColor.clearColor()
//unitHolder!.position = CGPoint(x:xPos, y:yPos);
let shape = SKShapeNode(circleOfRadius: reducedSize/2)
shape.fillColor = UIColor.whiteColor()
shape.strokeColor = UIColor.whiteColor()
shape.position = CGPoint(x: reducedSize/2, y: reducedSize/2)
unitHolder!.addChild(shape)
self.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(reducedSize, reducedSize), center: CGPointMake(reducedSize/2, reducedSize/2))
self.physicsBody?.affectedByGravity = false
self.physicsBody?.categoryBitMask = ColliderType.Head
self.physicsBody?.dynamic = false
self.physicsBody?.contactTestBitMask = ColliderType.TripRow | ColliderType.TripColumn | ColliderType.Food | ColliderType.Body | ColliderType.WallLeft | ColliderType.WallRight | ColliderType.WallBottom | ColliderType.WallTop | ColliderType.Canvas
} else if (bodyT == 1) {
//body part
self.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(reducedSize, reducedSize), center: CGPointMake(reducedSize/2, reducedSize/2))
self.physicsBody?.affectedByGravity = false
self.physicsBody?.categoryBitMask = ColliderType.Body
self.physicsBody!.collisionBitMask = 0
self.physicsBody?.dynamic = true
self.physicsBody?.contactTestBitMask = ColliderType.WallLeft | ColliderType.WallRight | ColliderType.WallBottom | ColliderType.WallTop | ColliderType.BodyStatic
} else if (bodyT == 2) {
//static corner
self.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(reducedSize/2, reducedSize/2), center: CGPointMake(reducedSize/2, reducedSize/2))
self.physicsBody?.affectedByGravity = false
self.physicsBody?.categoryBitMask = ColliderType.BodyStatic
self.physicsBody!.collisionBitMask = 0
self.physicsBody?.dynamic = true
}
unitHolder!.anchorPoint = CGPoint(x: 0, y: 0)
self.addChild(unitHolder!)
}
func updateColor(color: UIColor) {
unitHolder!.color = color
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//fatalError("init coder not implemented")
}
}
Here is the HeadTripper class:
import SpriteKit
class HeadTripper : SKNode {
var id = -1
var rowTripLine: SKSpriteNode?
var colTripLine: SKSpriteNode?
init(size: CGSize, rowOrCol: Int) {
//init(size: CGSize, xPos: Double, yPos: Double, rowOrCol: Int) {
super.init()
//row 0
//col 1
if (rowOrCol == 0) {
//row
rowTripLine = SKSpriteNode()
rowTripLine!.color = UIColor.redColor()
//rowTripLine!.position = CGPoint(x: 0, y: yPos);
rowTripLine!.size = CGSize(width: Int(size.width), height: 1)
//borderLeft!.physicsBody = SKPhysicsBody(rectangleOfSize: size, center: CGPointMake(reducedSize/2, reducedSize/2))
rowTripLine!.physicsBody = SKPhysicsBody(rectangleOfSize: rowTripLine!.size, center: CGPointMake(rowTripLine!.size.width/2, rowTripLine!.size.height/2))
rowTripLine!.physicsBody?.affectedByGravity = false
rowTripLine!.physicsBody?.categoryBitMask = ColliderType.TripRow
rowTripLine!.physicsBody!.collisionBitMask = 0
rowTripLine!.physicsBody?.dynamic = true
rowTripLine!.anchorPoint = CGPoint(x: 0, y: 0)
self.addChild(rowTripLine!)
} else {
//col
colTripLine = SKSpriteNode()
colTripLine!.color = UIColor.redColor()
//colTripLine!.position = CGPoint(x: xPos, y: 0);
colTripLine!.size = CGSize(width: 1, height: Int(size.height))
//borderLeft!.physicsBody = SKPhysicsBody(rectangleOfSize: size, center: CGPointMake(reducedSize/2, reducedSize/2))
colTripLine!.physicsBody = SKPhysicsBody(rectangleOfSize: colTripLine!.size, center: CGPointMake(colTripLine!.size.width/2, colTripLine!.size.height/2))
colTripLine!.physicsBody?.affectedByGravity = false
colTripLine!.physicsBody?.categoryBitMask = ColliderType.TripColumn
colTripLine!.physicsBody!.collisionBitMask = 0
colTripLine!.physicsBody?.dynamic = true
colTripLine!.anchorPoint = CGPoint(x: 0, y: 0)
self.addChild(colTripLine!)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init coder not implemented")
}
}
I generate the HeadTripper lines like this in the main scene:
for var i = 0; i < rowCount; i++ {
let rowTrip = HeadTripper(size: size, rowOrCol: 0)
rowTrip.id = i
rowTrip.position.x = 0
rowTrip.position.y = CGFloat(yVal)
self.addChild(rowTrip)
rowLines.append(CGFloat(rowTrip.position.y))
CGPathMoveToPoint(linePath, nil, 0, CGFloat(yVal))
CGPathAddLineToPoint(linePath, nil, CGFloat(theGrid.screenWidth), CGFloat(yVal))
yVal += (theGrid.snakeHigh + yValPad)
}
Here is how the collider types are setup:
struct ColliderType {
static let Head: UInt32 = 0
static let Food: UInt32 = 0b1
static let Body: UInt32 = 0b10
static let BodyStatic: UInt32 = 0b100
static let WallLeft: UInt32 = 0b1000
static let WallBottom: UInt32 = 0b10000
static let WallRight: UInt32 = 0b100000
static let WallTop: UInt32 = 0b1000000
static let None: UInt32 = 0b10000000
static let Canvas: UInt32 = 0b100000000
static let TripColumn: UInt32 = 0b1000000000
static let TripRow: UInt32 = 0b10000000000
}
The tripRow let setup happens on line 310. On line 309 I can see the contact.bodyA:
0x000000012758db20
{
NSObject = {
isa = 0x000000012758db20
}
_representedObject = 0x00000001275765a0
_field = 0x0000000000000000
_dynamicType = 2
_world = 0x0000000127761b90
_joints = 0x0000000127576270 "0 values"
_inUse = true
_shapeType = 2
_radius = 0
_edgeRadius = 0.0010000000474974513
_mask = 0x0000000000000000
_isPinned = false
_allowsRotation = true
_postStepBlock = 0x000000019e666c70
}
I can also see the contact.bodyA.node:
0x00000001275765a0
{
UIKit.UIResponder = {...}
}
Line 311 never gets called because its in the if condition that isn't passing.
At line 313 I attempt to look at tripRow and get this:
(lldb) po tripRow
error: <EXPR>:1:1: error: use of unresolved identifier 'tripRow'
tripRow
It sounds like issue is related to the children within HeadTripper. I have named the children "row" and "col" respectively.
print("body A name ", contact.bodyA.node?.name)
let tripRow = contact.bodyA.node as? SKSpriteNode
This gives a name of "row". Also tripRow now prints out as:
(lldb) po tripRow
0x0000000155e737c0
{
SpriteKit.SKNode = {...}
}
I think this is very close. The problem still exists that the position is not accurate. The y value should be a number like 54 and it is zero:
(lldb) po tripRow?.position
(x = 0, y = 0)
This was resolved by having just 1 SKSpriteNode referenced in the HeadTripper SKNode class instead of 2.

Procedural Level Generation With Cellular Automaton In Swift

Is there a easy way to create a procedural level with a cellular automaton in swift/SpriteKit(library?)? I want to create a 'cave' with 11 fields in the height and 22 width. These should be randomly created and every field without a wall should be reached.
I just found a documentation using Objective-C, which I am not familiar with. I spend quite some time trying to understand the code and follow the example without success.
PS: If there is an easier way I appreciate some algorithms
I made a Playground where you can experiment
//: Playground - noun: a place where people can play
import UIKit
import SpriteKit
import XCPlayground
class Cave {
var cellmap:[[Bool]]
let chanceToStartAlive = 35
let deathLimit = 3
let birthLimit = 4
var xCell = 40 // number of cell in x axes
var yCell = 20 // number of cell in y axes
var wCell = 20 // cell width
var hCell = 20 // cell height
init(){
cellmap = Array(count:yCell, repeatedValue:
Array(count:xCell, repeatedValue:false))
cellmap = self.initialiseMap(xCell, yIndex:yCell)
}
func initialiseMap(xIndex:Int, yIndex:Int) -> [[Bool]]{
var map:[[Bool]] = Array(count:yIndex, repeatedValue:
Array(count:xIndex, repeatedValue:false))
for y in 0...(yIndex - 1) {
for x in 0...(xIndex - 1) {
let diceRoll = Int(arc4random_uniform(100))
if diceRoll < chanceToStartAlive {
map[y][x] = true
} else {
map[y][x] = false
}
}
}
return map
}
func addSprite(scene:SKScene){
for (indexY, row) in cellmap.enumerate(){
for (indexX, isWall) in row.enumerate(){
if isWall {
let wall = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: wCell, height: hCell))
wall.position = CGPoint(x: (indexX * wCell) + (wCell / 2) , y: (indexY * hCell) + (hCell / 2) )
scene.addChild(wall)
}
}
}
}
func countAliveNeighbours(x:Int, y:Int) -> Int{
var count = 0
var neighbour_x = 0
var neighbour_y = 0
for i in -1...1 {
for j in -1...1 {
neighbour_x = x + j
neighbour_y = y + i
if(i == 0 && j == 0){
} else if(neighbour_x < 0 || neighbour_y < 0 || neighbour_y >= cellmap.count || neighbour_x >= cellmap[0].count){
count = count + 1
} else if(cellmap[neighbour_y][neighbour_x]){
count = count + 1
}
}
}
return count
}
func applyRules(){
var newMap:[[Bool]] = Array(count:yCell, repeatedValue:
Array(count:xCell, repeatedValue:false))
for y in 0...(cellmap.count - 1) {
for x in 0...(cellmap[0].count - 1) {
let nbs = countAliveNeighbours( x, y: y);
if(cellmap[y][x]){
if(nbs < deathLimit){
newMap[y][x] = false;
}
else{
newMap[y][x] = true;
}
} else{
if(nbs > birthLimit){
newMap[y][x] = true;
}
else{
newMap[y][x] = false;
}
}
}
}
cellmap = newMap
}
}
let view:SKView = SKView(frame: CGRectMake(0, 0, 1024, 768))
XCPShowView("Live View", view: view)
let scene:SKScene = SKScene(size: CGSizeMake(1024, 768))
scene.scaleMode = SKSceneScaleMode.AspectFit
let aCave = Cave()
aCave.applyRules()
aCave.applyRules()
aCave.addSprite(scene)
view.presentScene(scene)
Updated the playground code for Xcode 8 and Swift 3. I swapped the X and Y cell count since you will likely see the view in a "portrait" orientation.
Remember to open the Assistant Editor to view the results. It also takes a little while to execute, so give it a couple of minutes to run the algorithm.
//: Playground - noun: a place where people can play
import UIKit
import SpriteKit
import XCPlayground
import PlaygroundSupport
class Cave {
var cellmap:[[Bool]]
let chanceToStartAlive = 35
let deathLimit = 3
let birthLimit = 4
var xCell = 20 // number of cell in x axes
var yCell = 40 // number of cell in y axes
var wCell = 20 // cell width
var hCell = 20 // cell height
init(){
cellmap = Array(repeating:
Array(repeating:false, count:xCell), count:yCell)
cellmap = self.initialiseMap(xIndex: xCell, yIndex:yCell)
}
func initialiseMap(xIndex:Int, yIndex:Int) -> [[Bool]]{
var map:[[Bool]] = Array(repeating:
Array(repeating:false, count:xIndex), count:yIndex)
for y in 0...(yIndex - 1) {
for x in 0...(xIndex - 1) {
let diceRoll = Int(arc4random_uniform(100))
if diceRoll < chanceToStartAlive {
map[y][x] = true
} else {
map[y][x] = false
}
}
}
return map
}
func addSprite(scene:SKScene){
for (indexY, row) in cellmap.enumerated(){
for (indexX, isWall) in row.enumerated(){
if isWall {
let wall = SKSpriteNode(color: UIColor.red, size: CGSize(width: wCell, height: hCell))
wall.position = CGPoint(x: (indexX * wCell) + (wCell / 2) , y: (indexY * hCell) + (hCell / 2) )
scene.addChild(wall)
}
}
}
}
func countAliveNeighbours(x:Int, y:Int) -> Int{
var count = 0
var neighbour_x = 0
var neighbour_y = 0
for i in -1...1 {
for j in -1...1 {
neighbour_x = x + j
neighbour_y = y + i
if(i == 0 && j == 0){
} else if(neighbour_x < 0 || neighbour_y < 0 || neighbour_y >= cellmap.count || neighbour_x >= cellmap[0].count){
count = count + 1
} else if(cellmap[neighbour_y][neighbour_x]){
count = count + 1
}
}
}
return count
}
func applyRules(){
var newMap:[[Bool]] = Array(repeating:
Array(repeating:false, count:xCell), count:yCell)
for y in 0...(cellmap.count - 1) {
for x in 0...(cellmap[0].count - 1) {
let nbs = countAliveNeighbours( x: x, y: y);
if(cellmap[y][x]){
if(nbs < deathLimit){
newMap[y][x] = false;
}
else{
newMap[y][x] = true;
}
} else{
if(nbs > birthLimit){
newMap[y][x] = true;
}
else{
newMap[y][x] = false;
}
}
}
}
cellmap = newMap
}
}
let view:SKView = SKView(frame: CGRect(x: 0, y: 0, width: 768, height: 1024))
let scene:SKScene = SKScene(size: CGSize(width: 768, height: 1024))
scene.scaleMode = SKSceneScaleMode.aspectFit
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = view
let aCave = Cave()
aCave.applyRules()
aCave.applyRules()
aCave.addSprite(scene: scene)
view.presentScene(scene)

SKEmitterNode iOS 8 vs iOS 9 How to get the same result?

I have a game similar to fruit ninja using Swift -> SpriteKit. Everything is working fine on iOS 8 but on iOS 9 SKEmitterNode is having a bit strange behavior. This is what I get for my blade effect on both:
func emitterNodeWithColor(color:UIColor)->SKEmitterNode {
let emitterNode:SKEmitterNode = SKEmitterNode()
emitterNode.particleTexture = SKTexture(imageNamed: "spark.png")
emitterNode.particleBirthRate = 3000
emitterNode.particleLifetime = 0.2
emitterNode.particleLifetimeRange = 0
emitterNode.particlePositionRange = CGVectorMake(0.0, 0.0)
emitterNode.particleSpeed = 0.0
emitterNode.particleSpeedRange = 0.0
emitterNode.particleAlpha = 0.8
emitterNode.particleAlphaRange = 0.2
emitterNode.particleAlphaSpeed = -0.45
emitterNode.particleScale = 0.5
emitterNode.particleScaleRange = 0.001
emitterNode.particleScaleSpeed = -1
emitterNode.particleRotation = 0
emitterNode.particleRotationRange = 0
emitterNode.particleRotationSpeed = 0
emitterNode.particleColorBlendFactor = 1
emitterNode.particleColorBlendFactorRange = 0
emitterNode.particleColorBlendFactorSpeed = 0
emitterNode.particleColor = color
emitterNode.particleBlendMode = SKBlendMode.Add
return emitterNode
}
let emitter:SKEmitterNode = emitterNodeWithColor(color)
emitter.targetNode = target
emitter.zPosition = 0
tip.addChild(emitter)
This is the method I am using with all the options. It is the same for both but the result is different. Any ideas how can I make the effect in iOS 9 to be the same as iOS 8 ?
I'm facing the exact same issue in my project.
The emitter's performance is low in iOS9 (Metal version not finished?), so Apple shut off the interpolation of the drawing to get back the performance a little (The drawing rate is limited to 60 fps, anything between two frames is not rendered).
My solution is to implement the tail myself, which is simple:
class TailNode: SKSpriteNode {
var tailTexture: SKTexture!
var tailSize: CGSize! = CGSizeMake(30, 30)
var tailColor: SKColor!
var tailBlendMode: SKBlendMode!
var initialAlpha: CGFloat = 0.6
var initialScale: CGFloat = 0
var finalScale: CGFloat = 1
var particleLife: NSTimeInterval = 0.1
var running: Bool = false
var particleAction: SKAction!
var lastParticle: SKSpriteNode?
var battleScene: BattleScene {
return self.scene as! BattleScene
}
convenience init(tailTexture: SKTexture, tailSize: CGSize, tailColor: SKColor, tailBlendMode: SKBlendMode, initialAlpha: CGFloat, initialScale: CGFloat, finalScale: CGFloat, particleLife: NSTimeInterval) {
self.init(texture: nil, color: SKColor.whiteColor(), size: CGSize(width: 0, height: 0))
self.tailTexture = tailTexture
self.tailSize = tailSize
self.tailColor = tailColor
self.tailBlendMode = tailBlendMode
self.initialAlpha = initialAlpha
self.initialScale = initialScale
self.finalScale = finalScale
self.particleLife = particleLife
let fadeAction = SKAction.fadeAlphaTo(0, duration: particleLife)
let scaleAction = SKAction.scaleTo(finalScale, duration: particleLife)
let removeAction = SKAction.removeFromParent()
self.particleAction = SKAction.sequence([SKAction.group([fadeAction, scaleAction]), removeAction])
}
func updateWithTimeSinceLastUpdate(interval: NSTimeInterval) {
if running {
let particlePosition = battleScene.convertPoint(battleScene.convertPoint(self.position, fromNode: self.parent!),
toNode:battleScene.worldLayers[.UnderCharacter]!)
if lastParticle == nil || lastParticle!.parent == nil {
lastParticle = nil
} else {
let lastPosition = lastParticle!.position
let x = lastPosition.x + (particlePosition.x - lastPosition.x)*0.5
let y = lastPosition.y + (particlePosition.y - lastPosition.y)*0.5
newParticleAtPosition(CGPointMake(x, y), withDelay: interval*0.5)
}
lastParticle = newParticleAtPosition(particlePosition, withDelay: interval)
}
}
func newParticleAtPosition(position: CGPoint, withDelay delay: NSTimeInterval) -> SKSpriteNode {
let myParticle = SKSpriteNode(texture: tailTexture, color: tailColor, size: tailSize)
myParticle.colorBlendFactor = 1
myParticle.blendMode = tailBlendMode
myParticle.alpha = initialAlpha
myParticle.setScale(initialScale)
myParticle.position = position
battleScene.addNode(myParticle, atWorldLayer: .UnderCharacter)
myParticle.runAction(SKAction.sequence([SKAction.waitForDuration(delay), particleAction]))
return myParticle
}
}