Can GKObstacleGraph be saved to a file and loaded from there ?
I cant find anything on this.
I would love to save and load precalculated graphs for my levels.
I have tried so far
NSArray * obstacles = [SKNode obstaclesFromNodePhysicsBodies:arrayOfBodies];
_graph = [GKObstacleGraph graphWithObstacles:obstacles bufferRadius:[(BaseUnit *)[_units firstObject] size].width/2];
[NSKeyedArchiver archiveRootObject:_graph toFile:#"/Users/roma/Desktop/myGraph.graph"];
But this is what I got:
-[GKObstacleGraph encodeWithCoder:]: unrecognized selector sent to instance 0x6180000432d0
GKObstacleGraph is a subclass of GKGraph, which (as of macOS 10.12, iOS 10, and tvOS 10) declares conformance to the NSCoding protocol. That means you can serialize one to data or a file (and deserialize to create an instance from a file) using NSKeyedArchiver (and NSKeyedUnarchiver) just like you can for any other object that supports NSCoding.
For general info on archiving (which applies to any NSCoding-compatible class), see Apple's Archives and Serializations Programming Guide.
Also, in Xcode 8 (when deploying to macOS 10.12, iOS 10, or tvOS 10), you can create GKGraphs in a visual editor to go along with your SpriteKit scenes. When you do that, you use the GKScene class to load both the SpriteKit scene and the GK objects (which can include not just pathfinding graphs, but also entity/component info) from the .sks file Xcode writes.
In older OS versions, the GKGraph family doesn't support NSCoding. However, all the information you need to reconstruct a GKObstacleGraph is publicly accessible. So you could implement your own serialization by reading a graph's buffer radius and list of obstacles, and reading each obstacle's list of vertices. Write that info to a file however you like... then when you want to reconstruct a graph, create GKPolygonObstacles from the vertices you saved, and create a new GKObstacleGraph from those obstacles and your saved buffer radius.
Related
I've been working on a IOS mobile game and I wanted to take an object,duplicate it, and have the copies move all over the screen. I've looked through Google to find things relevant to this but they were all in Objective C or just didn't have what I was looking for. I want to know how to do this in Swift and SpriteKit.
If you are working with SKSpriteNode you can copy it and all it's current properties with:
let copiedNode = nodeToCopy.copy() as! SKSpriteNode
You will still need need to add copiedNode to your scene. copiedNode will also continue to run any actions that nodeToCopy was running. You can cancel them with copiedNode.removeAllActions().
Note that the documentation for the protocol NSCopying reads exactly:
Protocol
NSCopying
A protocol that objects adopt to provide functional
copies of themselves.
The exact meaning of “copy” can vary from class to class, but a copy
must be a functionally independent object with values identical to the
original at the time the copy was made...
Indeed, in the case of SKSpriteNode, Apple have interpreted that idea so that the copy() function "spawns" another instance of the item, exactly as in any game engine.
(So, for SKSpriteNode copy() works identically to the sense of Instantiate in Unity, say.)
As mogelbuster points out below, there is nowhere in the Apple documentation that they state "The spawn command in Apple is copy()" but in fact they have interpreted this "The exact meaning of “copy” can vary from class to class" in exactly that way for SKNode, since indeed it's a game engine and it's the only meaningful sense of copy there.
It's worth noting that the most completely typical way to work in games is: for your say rocketShips, you would have one "model" of your rocketShip, say modelRocketShip. The model simply sits offscreen, or is perhaps marked as invisible or inactive. You never use the model in the game, it just sits there. When you spawn rocketShips, you just dupe the model. (So in Apple that's modelRocketShip.copy() and then set the position etc.)
You can define a function to create and return a sprite :
func createSprite()->SKSpriteNode{
let sprite = SKSpriteNode(...)//Use the init function in the SKSpriteNode class
//Add some code to define the sprite's property
return sprite
}
And call this function to get some sprites that have the same property:
let spriteOne = createSprite()
let spriteTwo = createSprite()
Then you can add different SKActions to each of them so that they can behave differently.
Once you have multiple SKSpriteNodes, you can also control them by using EnumerateChildNodesWithName (assuming all your nodes have the same name) to go through all of them do do what you want in the update() function.
On a more advanced level, you could subclass SKSpriteNode and incorporate your own behaviour in your custom class.
I'm using SpriteKit .sks file Can I make a sprite in .sks into an instance of subclass of SKSpriteNode?
This is the init method in my subclass:
init(imageNamed: String) {
let blockTexture = SKTexture(imageNamed: imageNamed)
super.init(texture: blockTexture, color: nil, size: blockTexture.size())
}
In GameScene.swift I can create an instance like this:
var myObj = Block(imageNamed: "Block")
My question is how can I relate this instance with .sks file?
I tried this line of code but it doesn't work.
myObj = childNodeWithName("block1") as Block
Any help?
Thanks.
There are a couple of issues here to address...
How .sks loading works
When you load a .sks file, SpriteKit instantiates everything within using NSKeyedUnarchiver. This means that all the nodes inside are loaded as whatever base classes they were specified as by Xcode when it created the .sks file — SKSpriteNode for sprites with texture art, SKLabelNode for text, SKFieldNode for physics fields, etc. And Xcode doesn't currently provide an option for setting custom classes for the nodes inside a .sks file.
(The one exception for this is changing the runtime class of the scene itself — the top level container for everything in the .sks file. And that's only because of the custom SKNode.unarchiveFromFile implementation provided for you in the project template. Its technique for changing classes at load time works when you have one and only one instance of a particular class in an archive — good for SKScene, not so good for the many nodes in a scene.)
How casting works
When you write something like:
myObj = childNodeWithName("block1") as Block
You're telling the compiler something like: "Hey, you know that thing you got from childNodeWithName? All you know is that it's an SKNode, but I know it's really a Block, so please let me call Block methods on it." (And the compiler says, "Okay, whatever.")
But then at run time, that thing you got had better really be a Block, or your app will crash because you tried to do something Blocky with something that's not a Block. And, per the bit about .sks loading above, that thing isn't and can't be a Block — Xcode doesn't know how to put Blocks into a .sks file. So you can't get a Block out of it, so your app is guaranteed to crash.
Workarounds
So, if you can't put custom classes into a .sks file, what can you do? It depends a bit on what exactly you're trying to accomplish. But there's a good trick that might also be good game/app design in general: use the .sks file for general layout and configuration, and use a second pass to bring in things that need custom behavior.
For example, if you're building a level in a 2D platform game, you wouldn't really want to have the .sks file contain an instance of your Plumber class even if you could — that class probably has lots of details about how tall or fat the guy is, how high he jumps, the shape of his mustache, etc, and you don't want to have to set those up again every time you make a new level, much less have them saved again in each level's .sks file. Instead, the only thing you really need to know in each level file is the position he starts at. So, drag out an "Empty Node" in Xcode, and at load time, replace that node with an instance of your Plumber class, like so:
let spawnPoint = childNodeWithName("spawnPoint")
let player = Plumber()
player.position = spawnPoint.position
addChild(player)
spawnPoint.removeFromParent()
If you have more configuration details that you want to set in the .sks file, you might consider automating that process.
Make a method that does the above node-swapping trick. (Call it something like replaceNode(_:withNode:).)
Make an initializer for your custom class that takes a SKNode or SKSpriteNode, and have it set all its inherited properties (or at least the ones you care about, like color and texture) from that node.
Use enumerateChildNodesWithName:usingBlock: with a search pattern to find all the nodes in your scene with a certain kind of name, and replace them with a new node created using your initializer. Something like:
enumerateChildNodesWithName("//brick_[0-9]*") { node, stop in
self.replaceNode(node, withNode: BrickBlock(node))
}
enumerateChildNodesWithName("//question_[0-9]*") { node, stop in
self.replaceNode(node, withNode: QuestionBlock(node))
}
I have a class that loads the drawing code for a bunch of graphics. It goes something sort of like this:
if (type == RabbitGraphic)
{
//a whole bunch of drawing code gets loaded into an object
}
else if (type == FrogGraphic)...
This file is getting quite long with the more graphics I've added to it and the compilation / loading the file are taking a while. I'm wondering, is there a way I can split these graphics into separate files without having to create a new object? IE is there some mechanism I can do like:
if (type == RabbitGraphic)
{
load file that has rabbit graphic code
}
Why are you trying to avoid creating a new object? It sounds to me like more OOP is what is called for here. You should have individual classes that implement the various bits of graphics code specific to each type, optionally with a parent class that implements common functionality.
I would place all of your graphics in a DB and load them through the network - much easier to maintain and doesn't impact the ever so precious disk space on mobile devices. Check out the heroku tutorial and run with it. At the end of the day, its a great design for app stack to client layers and its free to develop!
Images and Apps in iOS/ any endpoint that can do HTTP
I think you might be looking for objective-c categories. The gist of it is you can declare interface and implementations like this:
#interface MyMainClass
#end
Then in another file, usually called MyMainClass+OtherStuff.h
#interface MyMainClass (OtherStuff)
#end
Similarly with #implementation's. The syntax allows you to do just what you're looking for: to group related methods into separate modules without spanning classes.
I'm having some troubles trying to save a singleton object to the iPhone disk.
The object is a collection of 2 arrays, which contain the faved posts and faced jobs.
Basically --> Favorites = arrayOfFavedPosts + arrayOfFavedJobs
Now I am trying to save the Favorites object, so that the once faved posts or jobs can be read from the disk.
my Faves.m file.
Error:
2012-04-26 14:56:56.957 FirstDesign_test2[666:10403] -[Post encodeWithCoder:]: unrecognized selector sent to instance 0x6c74a80
This error fires up when I call the saveToDisk method when I add a new object to one of the arrays...
Some help would be really appreciated!
Thanks in Advance
You need to adopt and implement the NSCoding protocol on your Post class (and presumably on your Job class).
NSCoding Protocol Reference
Archives and Serializations Programming Guide: Encoding and Decoding Objects
I want to create a game, that will use a level system. So i want to store my levels and to be able to change them during the game (to save the state). So i decided to use XML for storing levels. I found NSXmlParser class for reading from XML, but i can't find a writer to save the level state. In my game the level state and the level are very similar ( i have a lot of movable objects), so i don't wan't to store the level state data separated from the level it belongs. The problem is that i can't find a way to easily modify XML files on iPhone. Maybe i'm using a bad approach.
If you throw the data in an NSDictionary, you could do (with caveats):
[myDictionary writeToFile:pathToPlist atomically:YES];
Try the open source XML stream writer for iOS:
Written in Objective-C, a single .h. and .m file
One #protocol for namespace support and one for without
Example:
// allocate serializer
XMLWriter* xmlWriter = [[XMLWriter alloc]init];
// start writing XML elements
[xmlWriter writeStartElement:#"Root"];
[xmlWriter writeCharacters:#"Text content for root element"];
[xmlWriter writeEndElement];
// get the resulting XML string
NSString* xml = [xmlWriter toString];
This produces the following XML string:
<Root>Text content for root element</Root>
I would recommend using KissXML. The author started in a similar situation as you and created an NSXML compatible API wrapper around libxml. He discusses the options and decisions here on his blog.
You can use the C libary libxml2 to read and write XML. Here's a quick intro: Cocoa Samurai: Getting Some XML Love with libXML2.
However, have you considered using CoreData or implementing the NSCoding protocol? NSCoding would be easier to add to existing classes.