1

I want to make an SKView I can use as a factory to make SKShapeNodes and "render" them to textures.

But I can't find how I would initialise such a thing, and am having no luck, at all.

How do I make a standalone SKView for this purpose?

Or is there a better way to do this that avoids using the gamescene?

Here's my FUTILE Effort at making a factory, this complains that texture(from: ) is ambiguous. I have no idea what that means.

import SpriteKit

class Make: SKView{

static func circle() -> SKSpriteNode {
    let myShapeNode = SKShapeNode(circleOfRadius: 100)
    myShapeNode.fillColor = SKColor.lightGray
    myShapeNode.strokeColor = SKColor.gray
    let tex = texture(from: myShapeNode)
    return SKSpriteNode(texture: tex)
    }

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
    }   
}

Update

After more futile time on google, I tried searching for initialisation of a UIView, and found and added this piece of code, that initialises to a frame that seems imaginary... but it works! I don't know why... but I can't use it as a factory method, only as an instance method, this way:

import Foundation
import SpriteKit

class Make: SKView{

    // added randomly found UIView initialisation "code"...
    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    func circle() -> SKSpriteNode {
        let myShapeNode = SKShapeNode(circleOfRadius: 100)
        myShapeNode.fillColor = SKColor.lightGray
        myShapeNode.strokeColor = SKColor.gray
        let tex = texture(from: myShapeNode)
        return SKSpriteNode(texture: tex)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
halfer
  • 19,824
  • 17
  • 99
  • 186
Confused
  • 6,048
  • 6
  • 34
  • 75
  • I don't think this makes sense to derive this from SKView. If you have a base class for your GameScene, it would be more appropriate to have it be a method there. – Mobile Ben Oct 17 '16 at 20:56

2 Answers2

1

It's not quite clear what you mean by "how I would initialise such a thing". How is dependent upon technique and what the factory does overall. Some factories do not need external input for initialization while others do. Here is are a couple of really crude examples. These are perhaps overly simplistic and have not been tested at all.

One uses the concept of a static class whilst the other is a singleton. None of this is to serve as "this is better than that". Nor is it to open Pandora's Box on which is better, etc. There are other threads which go over those details. Rather they are just a couple of approaches to get you going.

Here is a static class version

// static class version
class SpriteFactory1 {
    private static let view:SKView = SKView()

    static func spriteFromShape(shape:SKShapeNode) -> SKSpriteNode? {
        guard let tex = view.texture(from:shape) else {
            return nil
        }

        return SKSpriteNode(texture:tex)
    }
}

Here is a singleton version. Adding your circle code from above ...

// singleton version
class SpriteFactory2 {
    static let sharedInstance = SpriteFactory2()

    private let view:SKView = SKView()

    func spriteFromShape(shape:SKShapeNode) -> SKSpriteNode? {
        guard let tex = view.texture(from:shape) else {
            return nil
        }

        return SKSpriteNode(texture:tex)
    }

    func circle() -> SKSpriteNode {
        let myShapeNode = SKShapeNode(circleOfRadius: 100)
        myShapeNode.fillColor = SKColor.lightGray
        myShapeNode.strokeColor = SKColor.gray
        let tex = texture(from: myShapeNode)
        return SKSpriteNode(texture: tex)
    }
}

You'll also note the code is more or less the same. However in the case of the static, the class variables must be static as well.

Usage would look something like:

// Test shape
let shape = SKShapeNode()
// Pretend shape is constructed as desired

let sprite1 = SpriteFactory1.spriteFromShape(shape: shape)    
let sprite2 = SpriteFactory2.sharedInstance.spriteFromShape(shape: shape)
let circle = SpriteFactory2.sharedInstance.circle()
Mobile Ben
  • 7,121
  • 1
  • 27
  • 43
  • 1
    You do not need to pass in a view, just creating an SKView() is more than enough to create textures. This is useful for when you want to create your sprites before you even have a scene in a view. – Knight0fDragon Oct 17 '16 at 17:09
  • I saw somewhere, a few days ago and cannot find now, someone subclassing SKView and using it as a factory to make SKShapeNodes, and that's what I'm trying to do. But I can't figure out how to subclass an SKView. Your approaches seem to be doing something that confuses me further, in that I don't even get what they're doing. Seemingly acknowledging the existence of the game's SKView, I suppose, but my conceptualisation skills are about as honed as a nose guard's belt. – Confused Oct 17 '16 at 19:21
  • Yes, @Knight0fDragon, that's what I think I saw done. How do I initialise that SKView? I have tried everything I can think of, which isn't much, but nothing is even close. More red mist, mostly. – Confused Oct 17 '16 at 19:23
  • @Knight0fDragon, thanks for the info. I didn't know that. I mistakenly assumed that Apple put it on `SKView` as an instance method due to some dependency. I read the docs and they clarify it there. I've updated the code to just create the view internally, which actually simplifies things! – Mobile Ben Oct 17 '16 at 20:12
  • @Confused, I've updated the code based on what KnightOfDragon said. It simplifies the code and is probably easier to follow. You should not be trying to subclass an `SKView` for the factory. The example I gave was not inheriting from anything. You're encountering one of the challenges of learning development which is that there are a lot of details to understand. And often, getting a new technique only confuses the matter. Note you also don't necessarily need a factory, if you don't want. You could have the method be on the scene (if that makes it easier to understand). – Mobile Ben Oct 17 '16 at 20:16
  • I'm making LOTS of different buttons. And for the sake of sanity, and organisation, want to have a nice messy factory off somewhere, and call into it to make various things from it, when necessary and as desired. You're right, though, the enormity of the number of ways things can relate to each other is doing my head in. – Confused Oct 17 '16 at 20:57
  • I've actually gotten my SKView subclass factory partially working. I can call my factory methods (in the SKView subclass) from within the gameScene, but would prefer to be able to call into it from the SKSpriteNode subclasses become the buttons (have touchesbegun functionality, etc). But haven't figured out how to do that, yet. What is wrong with subclassing SKView to get at the rendering of textures? – Confused Oct 17 '16 at 21:00
  • What I don't understand about your approach is much. I don't get what type these are, nor what they're doing when they say "private static let view:SKView = SKView()" or private let view:SKView = SKView() in the second one. I'm completely at a loss when trying to conceive of what you're actually doing, and how your way even manages to enable the use of "let tex = texture(from: myShapeNode)" – Confused Oct 17 '16 at 21:03
  • Take a look right now, I added your circle code to the singleton factory. I am assuming you perhaps understand using singleton a bit more than static class so added it there. RE: subclassing SKView. A "simple" test of subclassing is the "is a" question. Is the factory an SKView? No. Really it "has an" SKView by nature of needing it to get the texture. – Mobile Ben Oct 17 '16 at 21:03
  • Ignore SpriteFactory1. Just use SpriteFactory2 (which is a singleton). And BTW, you can just implement it and step through the debugger. It may be easier to understand that way. – Mobile Ben Oct 17 '16 at 21:04
  • I have to apologise, I don't know how to step through the debugger. But agree, wholeheartedly, Singleton makes a lot more sense than whatever static class does. I've tried to make static classes, functions and properties all over the place, and had no end of trouble with "ambiguity" and other issues from the warnings of Xcode. – Confused Oct 17 '16 at 21:07
  • On the "has a" vs "is a" thing... to my way of thinking, I need a SKView to access the power to render to a texture. So, to my way of thinking, it is an SKView for the sole purpose of making sprites. Is this illogical? Wrong headed? The fact that you can seemingly make an instance of an SKView inside of a class of no apparent type is doing my head in. – Confused Oct 17 '16 at 21:09
  • And, further confusion, mentally I think I prefer the construct and conception of a Static (class methods, class properties not instance properties or methods), but the mechanics of using statics do my head in. Is it fair to say that using Static means EVERYTHING inside that class that wants to be used must be static? And that this somewhat mitigates the need for initialisation in a more normal sense, because the Class is never turned into an instance, only used as a storehouse of methods and properties for universal use? – Confused Oct 17 '16 at 21:12
  • 1. Swift classes don't need to be subclasses of anything. I forgot about this. Mental roadblock one – Confused Oct 17 '16 at 21:19
  • 2. I had no idea you could simply make an instance of an SKView and store it inside any class. Revelation. I thought the only way to use this was to be it. – Confused Oct 17 '16 at 21:20
  • 3. Storing of that SKView in a Static Class as a private instance means that it's there... but must be CALLED on. – Confused Oct 17 '16 at 21:20
  • 4. here's the big revelation... in your 1st example "view.texture(from:shape)" === I don't know how to bold that in comments, but this call to texture ON/WITHIN the instance of the view... this is the biggest single mental relief point and lifted the entire confusion, and helped me see the whole 3 of the other 3 points above. THANK YOU!!!! – Confused Oct 17 '16 at 21:22
  • So now I have a "hidden away" place to store all my button design and building experiments (very messy code, and tonnes of it, iterations galore), and a perfectly elegant to way to create from this factory Static Class, like this, from anywhere in the project: "let button = SpriteFactory1.circle()" which is absolutely ideal both in terms of clarity and conception. THANK YOU!!! – Confused Oct 17 '16 at 21:24
  • In both those case, the actual methods to do stuff are identical. The difference is the initialization/usage of statics for the data members. Check this out for a bit more reading ;). http://stackoverflow.com/questions/519520/difference-between-static-class-and-singleton-pattern – Mobile Ben Oct 17 '16 at 22:27
1

(Posted on behalf of the OP).

Problem solved!

For those of you that understand coding better than I, the following commentary is not for you. It's for all the fools and aspiring tools, like me.

Thanks to the wondrous patience and mine of knowledge that is @MobileBen, I am able to do exactly as desired, ie have a factory for creating SKShapeNodes from anywhere in the project, via an SKView instance in a Static Class of no type, whose only purpose is to be this factory.

  1. A class can have no superclass or inheritance in Swift. This means it's possible to make perfectly isolated Class files that have no purpose other than to store functionality and properties for universal access within a project.

  2. This is somewhat considered "wrong" in OOP programming, but from a conception and usage point, and the urgency of getting things done neatly and easily to find and read, it's invaluable, in my NAIVE opinion.

  3. Despite SKView being an intimidatingly high class within the UIKit world, it's possible to simply make an instance of an otherwise unused and unrecognised SKView within a static class for the purposes of exploiting its functionality

  4. The resulting usage is elegant and simple, thanks to MobileBen, like this:

a factory, with completely incorrect use of capitals for the naming, so I can instantly see it in code:

import SpriteKit

class MAKE {

private static let view:SKView = SKView()

static func circle(radius: CGFloat) -> SKSpriteNode {
    let myShapeNode = SKShapeNode(circleOfRadius: radius)
    myShapeNode.fillColor = SKColor.lightGray
    myShapeNode.strokeColor = SKColor.gray
    let tex = view.texture(from: myShapeNode)
    return SKSpriteNode(texture: tex)
    }
}

With this in the project, from within just about any other part of the project, the following creates a circle and stores it in myCircle:

let myCircle = MAKE.circle(radius: 50)
Community
  • 1
  • 1
halfer
  • 19,824
  • 17
  • 99
  • 186