I am working on a GameScene and I set up some nodes in the scene editor, since I want to place them visually in the level.
The nodes have all names going from "platform1" to "platform5". Depending on the level there are more or less platform nodes.
when the level is loaded I want to enumerate over all nodes with the title like "platform*" and put them into an array.
For now I use enumerateChildNodesWithName but I dont get the code in the block working correct.
This is what I have so far:
enumerateChildNodesWithName("//platform*", usingBlock: {node, _ in
if let platform = node as? PlatformNode {
print(platform.name)
}
})
And it prints out following error message:
CUICatalog: Invalid Request: requesting subtype without specifying
idiom
But the platform name isn't printed out.
Any ideas how to achieve this?
My next goal would be to out every single platform into an array, so I can access the properties of every platform though the array.
Does someone have a helping hand?
Thanks in advance...
If you add the platforms to an SKNode container in the scene editor, they are automatically added to the container's children array (in the order they are added). You can then access the platforms with
if let platforms = childNodeWithName("platforms") {
for platform in platforms.children {
print ("\(platform.name)")
}
}
To add the platforms to an SKNode in the scene editor, add an SKNode (listed as an Empty) to the editor, set the SKNode's name appropriately (e.g., platforms), and set the parent of each platform by setting platform's parent property in the editor (see image below).
Your search string is not correct. You can do it like this:
class PlatformNode:SKSpriteNode{
}
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
let p1 = PlatformNode()
p1.name = "platform1"
let p2 = PlatformNode()
p2.name = "platform2"
let p3 = PlatformNode()
p3.name = "platform3"
let p4 = PlatformNode()
p4.name = "platform4"
let p5 = PlatformNode()
p5.name = "platform5"
addChild(p1)
addChild(p2)
addChild(p3)
addChild(p4)
addChild(p5)
enumerateChildNodesWithName("platform[1-3]", usingBlock: {node, _ in
if let platform = node as? PlatformNode {
print(platform.name)
}
})
}
}
This will give you all the platforms named platform1,platform2 and platform3. To retrieve nodes named platform4 and platform5, you will change search string to this:
"platform[1-5]"
Related
I have Core Data setup in my application so that when there is no internet connection the app will save data locally. Once a connection is established it will trigger online-mode and then I need to send that data that I have stored inside Core Data up to my database.
My question is how do you turn an entity into a dictionary such as this:
<Response: 0x1c0486860> (entity: AQ; id: 0xd000000000580000 <x-coredata://D6656875-7954-486F-8C35-9DBF3CC64E34/AQ/p22> ; data: {
amparexPin = 3196;
dateFull = "\"10/5/2018\"";
finalScore = 34;
hasTech = No;
intervention = Before;
responseValues = "(\n 31,\n 99,\n 82,\n 150,\n 123,\n 66,\n 103,\n 125,\n 0,\n 14\n)";
timeStamp = "\"12:47\"";
who = "Luke";
})
into this:
amparexPin: 5123
timeStamp: 10:30
hasTech: No
intervention: Before
Basically a dictionary, I am trying to perform the same operation on each set of data on each entity. I know it sounds overly complicated but its quite imperative that each entity go through the same filter/function to then send its data up to a database. Any help on this would be awesome!
I see two ways to go here. The first is to have a protocol with some "encode" function, toDictionary() -> [String, Any?] that each managed object class implements in an extension and then call this function on each object before sending it.
The advantage of this way is that you get more precise control of each mapping between the entity and the dictionary, the disadvantage is that you need to implement it for each entity in your core data model.
The other way is to make use of NSEntityDescription and KVC to extract all values in one function. This class holds a dictionary of all attributes, attributesByName that could be used to extract all values using key-value coding. Depending on if you need to map the data type of the values as well you can get that from the NSAttributeDescription. (If you need to deal with relationships and more there is also propertiesByName).
A simplified example. (Written directly here so no guarantee it compiles)
static func convertToDictionary(object: NSManagedObject) -> [String, Any?] {
let entity = object.entity
let attributes = entity.attributesByName
var result: [String: Any?]
for key in attributes.keys {
let value = object.valueForKey: key)
result[key] = value
}
}
you can use this function
func objectAsDictionary(obj:YourObject) -> [String: String] {
var object: [String: String] = [String: String]()
object["amparexPin"] = "\(obj.amparexPin)"
object["timeStamp"] = "\(obj.timeStamp)"
object["hasTech"] = "\(obj.hasTech)"
object["intervention"] = "\(obj.intervention)"
return object
}
I have currently made a game featuring one player. I also made a character screen where the user can choose which character he/she wants to play with. How do I make it so that a certain high score unlocks a certain character, and allows the user to equip this character to use in the game?
Right now my player has his own swift file that defines all the properties of him:
import SpriteKit
class Player: SKSpriteNode, GameSprite {
var initialSize = CGSize(width:150, height: 90)
var textureAtlas: SKTextureAtlas = SKTextureAtlas(named: "Rupert")
let maxFlyingForce: CGFloat = 80000
let maxHeight: CGFloat = 900
var health:Int = 1
var invulnerable = false
var damaged = false
var damageAnimation = SKAction()
var dieAnimation = SKAction()
var forwardVelocity: CGFloat = 190
var powerAnimation = SKAction()
init() {
super.init(texture:nil, color: .clear, size: initialSize)
createAnimations()
self.run(soarAnimation, withKey: "soarAnimation")
let bodyTexture = textureAtlas.textureNamed("pug3")
self.physicsBody = SKPhysicsBody(texture: bodyTexture, size: self.size)
self.physicsBody?.linearDamping = 0.9
self.physicsBody?.mass = 10
self.physicsBody?.allowsRotation = false
self.physicsBody?.categoryBitMask = PhysicsCategory.rupert.rawValue
self.physicsBody?.contactTestBitMask = PhysicsCategory.enemy.rawValue | PhysicsCategory.treat.rawValue | PhysicsCategory.winky.rawValue | PhysicsCategory.ground.rawValue
func createAnimations() {
let rotateUpAction = SKAction.rotate(toAngle: 0.75, duration: 0.475)
rotateUpAction.timingMode = .easeOut
let rotateDownAction = SKAction.rotate(toAngle: 0, duration: 0.475)
rotateDownAction.timingMode = .easeIn
let flyFrames: [SKTexture] = [
textureAtlas.textureNamed("pug1"),
textureAtlas.textureNamed("pug2"),
textureAtlas.textureNamed("pug3"),
textureAtlas.textureNamed("pug4"),
textureAtlas.textureNamed("pug3"),
textureAtlas.textureNamed("pug2")
]
let flyAction = SKAction.animate(with:flyFrames, timePerFrame: 0.07)
flyAnimation = SKAction.group([SKAction.repeatForever(flyAction), rotateUpAction])
let soarFrames:[SKTexture] = [textureAtlas.textureNamed("pug5")]
let soarAction = SKAction.animate(with: soarFrames, timePerFrame: 1)
soarAnimation = SKAction.group([SKAction.repeatForever(soarAction), rotateDownAction])
This is not all the code but you get the point.
I then say: let player = Player() in my Gamescene file which essentially attaches all the attributes in the player file to my player that will be seen in the Gamescene. Even if I am able to replace the initial player with a certain different player, there are so many animations that I don't know how to replace everything at once. I want to set a condition that spans over both the gamescene class and the player class so that it can just sub out certain images for other ones and keep the same actions.
Thank you for any help!
Here are some techniques you can use for making things like this more manageable:
Have a naming convention for your character images and/or sprite sheets, so that you can pass in a name to your Player() constructor. Then, instead of loading texturenamed("pug3"), you load up texturenamed("\(playerName)3"). If the only difference between your characters are the sprite sheets, this is actually all you need on the Player end.
If your characters are more complex, with differences beyond just the images, like being larger or having more health, then you will probably want to go with a more data-oriented approach. There are a couple of approaches to this, but a handy one is to read your texture names, hitbox sizes, health levels, etc., out of a .plist file instead of hard-coding them. Then just pass in the name of the .plist file to load for the character you want. Then, to create a new character, you just create a new .plist file. Another approach would be to create a "character definition" struct that you could pass to the Player constructor that contains the information you need to construct the player (this, in turn, could be loaded from a .plist file, as well, but you could also hard-code them or save them directly using codable serialization).
If neither of the above approaches are sufficient, say if you need different behavior between characters, you could always go the subclassing route - pull the various parts and pieces out into functions, and then override those functions to add the specific functionality you need for more complex characters.
I have some user comments stored in a database (parse-server) that I would like to would like to display on my viewController's viewDidLoad(). I can easily pull the comment objects as follows:
super.viewDidLoad()
func query(){
let commentsQuery = PFQuery(className: "Comments")
commentsQuery.whereKey("objectId", equalTo: detailDisclosureKey)
commentsQuery.findObjectsInBackground { (objectss, error) in
if let objects = objectss{
if objects.count == 1{
for object in objects{
self.unOrderedComments.append(object)
}
}
}
}
}
This query dumps all of the of the comments in the unOrederedComments array. Each comment is added to the database with a createdAt property automatically being added relating the exact time of its creation. This property is a string with (as an example) the form: "2017-08-13T19:31:47.776Z" (the Z at the end is at the end of every string... not exactly sure why its there but its constant). Now, each new comment is added in order to the top of database and thus any queried result should be in order regardless. However, I would like to make sure of this by reordering it if necessary. My general thought process is to use .sorted, but I cannot figure out how to apply this to my situation
func orderComments(unOrderComments: [PFObject]) -> [PFObject]{
let orderedEventComments = unOrderedEventComments.sorted(by: { (<#PFObject#>, <#PFObject#>) -> Bool in
//code
})
}
This is the generic set up but I cannot, despite looking up several examples online figure out what to put in the <#PFObject#>'s and in the //code. I want to order them based on the "createdAt" property but this is not achieved via dot notation and instead requires PFObject["createdAt"] and using this notation keeps leading to error. I feel as so though I may need to set up a custom predicate but I do not know how to do this.
I was in the same situation, what I did was to first create an array of structs with the data I downloaded where I turned the string createdAt into a Date, then used this function:
dataArrayOrdered = unOrderedArray.sorted(by: { $0.date.compare($1.date) == .orderedAscending})
(.date being the stored Date inside my array of strcuts)
Try this code, notice that I assumed you have a variable name called ["Comments"] inside your Parse database, so replace if necessary. Also, I realised that createdAt it's in Date format, so there was no need to change it from String to Date, chek if it works the same for you, if it doesn't refer to this: Swift convert string to date.
struct Comment {
var date = Date()
var comment = String()
}
var unOrderedComments: [Comment] = []
var orderedComments = [Comment]()
override func viewDidLoad() {
super.viewDidLoad()
query()
}
func query(){
let commentsQuery = PFQuery(className: "Comments")
commentsQuery.findObjectsInBackground { (objectss, error) in
if let objects = objectss{
if objects.count >= 1{
for object in objects{
let newElement = Comment(date: object.createdAt!, comment: object["Comments"] as! String)
self.unOrderedComments.append(newElement)
print(self.unOrderedComments)
}
}
self.orderedComments = self.unOrderedComments.sorted(by: { $0.date.compare($1.date) == .orderedAscending})
print(self.orderedComments)
}
}
}
I have searched for a while but couldn't find reasonable answer for this. I want to add/remove objects in one array to make effect in 2nd array which points to first array.
class Person
{
var name:String = ""
}
var arr1:[Person] = [Person]()
let p1 = Person()
p1.name = "Umair"
let p2 = Person()
p2.name = "Ali"
arr1.append(p1)
arr1.append(p2)
var arr2 = arr1
print("\(arr1.count)") //"2\n"
print("\(arr2.count)") //"2\n"
arr1.removeFirst()
print("\(arr1.count)") //"1\n"
print("\(arr2.count)") //"2\n"
Why changing arr1 does not affect arr2. Please help me out to accomplish this.
Arrays are value types. When we copy them, each copy is independent of the other. This is a virtue in Swift. What you are trying to do requires references so that effects on one can be seen by others. Try this code. Create a class (reference type) containing your data. Now changes to the container can be seen in the other.
class Person
{
var name: String
init(_ name: String) {
self.name = name
}
}
let p1 = Person("Umair")
let p2 = Person("Ali")
class Container {
var people = [Person]()
init(people: [Person]) {
self.people = people
}
}
let arr1 = Container(people: [p1, p2])
let arr2 = arr1
print(arr1.people)
print(arr2.people)
arr1.people.removeFirst()
print(arr1.people)
print(arr2.people)
Even if you're using Swift, you can still use NSArray.
Per Apple's documentation,
NSArray is an object representing a static ordered collection, for use instead of an Array constant in cases that require reference semantics.
The only downside is you'll have to import Foundation. This isn't a problem if you're creating an iOS or Mac app, as you're depending on it already.
Goal: Randomly select a member of a class containing people, and display the information in an alert.
Here is what I am doing: I have a class called Entrepreneurs. Inside the class are variables for name, networth etc. I have instantiated the class with several Entrepreneurs as follows:
//Class for entrepreneurs
class Entrepreneurs {
var name:String?
var netWorth = (0.0, "")
var company:String?
var summary: [String]
var age: Int?
init() {
name = ""
company = ""
summary = [""]
age = 1;
}
}
let markZuckerBerg = Entrepreneurs()
markZuckerBerg.name = "Mark Zuckerberg"
markZuckerBerg.age = 19
markZuckerBerg.company = "Facebook"
markZuckerBerg.netWorth = (35.7, "Billion")
I have several instantiations (more than 5) and I now want to randomly access a member from the Entrepreneur class and display its properties.
I know I will need an Array to hold the class members but I don't think adding each member of the class in an array is the most efficient way to go about it, since I plan on adding hundreds of entrepreneurs eventually.
Any Suggestions?
(Side question: is this the best way to structure such a problem? In other words, If my goal is to have a list of entrepreneurs with information about them, and I want them to display randomly on the screen of a phone as an alert, is creating a class of entrepreneurs the best way?)
Seems to me like you want to randomly select objects from a group of them. To do this you need an array and a random number between 0 and the amount of elements in the array.
Code
let array: [Entrepreneurs] = [a,b,c,...]
let random: Int = Int(arc4random_uniform(UInt32(array.count)))
let selection: Entrepreneurs = array[random]
As far as showing them through and alert I need more details on the question.