I took the Apple pill!

Started by iWasAdam, May 29, 2024, 12:52:18

Previous topic - Next topic

iWasAdam

Here's a few thoughts on my recent transfer from Blitz to pure xcode programming.

I'm not using objectiveC but swift as the coding language, with metal as the render system. I am starting from scratch...

and here's what I've found so far...

1. xcode (like visual studio) is so far ahead of blitz editors it hurts. you want debugging, realtime variable tracking, memory monitoring, visual frame buffers. it's all there

2. Because I am using metal as the visual part and NOT SwiftUI, you have to deal with lots of (nasty) stuff like getting metal working, shaders, etc. and that is before you can show anything onscreen - this is not for the beginner. It's deep and unfriendly, but need for my purposes

3. There are no event structures - well there are but they are obtuse and hidden, so if you are used to that sort of programming there is a lot to learn and unlearn

4. keyboard have there own codes. you will need to work out how to remap to ascii if you want that. my personal view would be to avoid it if you don't need that sort of low level stuff - use SwiftUI instead if possible

5. it's highly typed and will complain as you enter text. this is both good and bad. the good is code becomes much more readable. the bad is when starting - everything will cause an error - hehehehehe

6. using native code is amazing. SIMD, compute, etc. Whatever hardware you want access to it's just there - you only have to figure out how to do it

6a. You have to know what you want - figure out how to get it, then work out what is the right way to do it. You will use google and youtube and books and online help and every resources you can think of. It's mess but you will get there...

7. Learning is more vertical than hill based. It's a real climb and not pleasant at all :( But the rewards are great when you get them.

8. Swift is a bit like BlitzBasic and C++ went out on a rager. On one level it's basic then jumps to being c++ and then something in between. if you are good with both then swift will be fine. you will just have the apple way of doing things to worry about


So would I recommend it?
Yes if you want much closer access to apple hardware, ios and macos are sort of seamless.
No for games - I would potentially go for Godot or Unreal as they are much friendlier things

Why am I doing it?
I need direct access to audio hardware this means I need to do things the apple way.

Doesn't that mean you can't code for windows?
Absolutely. The moment you have a narrow band of users - you use what is the simplest and best platform. Plus I hate windows - lol

And here's a pic of the (very slow) progress:


It looks simple but was not simple to do. There a single shader dealing with the visuals, bitmaps, etc. the 3 page icons are actually a single sprites but rendered with the programmable shader.

Next up will be keyboard input, mouse etc, then direct audio to multiple outs....

Xerra

I'm betting Swift has changed a lot but I remember me and Morpheus working with it for a year or so, back in 2015/16. It was pretty new then but it was obvious that Apple were wanting to make it their golden child. I think I wrote maybe two very basic games using it, and I think the screen building system built into Xcode for the graphics stuff. 

It's a good language, and, like Adam said, very C and Blitz like. Obviously ideal for X-Code, even though you can use just plain old C with that as well. I don't know enough about it for using to make bigger, and better games though. I'd still prefer to use Gamemaker to hide away some of the grunt stuff that a game would need if you weren't using an engine/framework to develop it. 

I believe the main motivation for not sticking with it back then was because it wouldn't build for Windows at the time, although perhaps it does now....

Here's how Swift code looked back in 2016. It's one of the source files for a simple mini-game I was adding to the game I worked on at the time. Syntax could well be a lot different now. It was eight years ago, so I barely recognise any of it. 

Looks like there were at least twenty source files I was working with, including files that had two versions based on compiler directives, so I could build the game for both iPad and iMac. Swift is a very powerful system to develop with, but a lot to learn. Even looking at all this code I believe I barely touched on it.

Code: BASIC
//
//  BonusScene.swift
//  Infection
//
//  Created by Tony Brice on 18/02/2016.
//  Copyright © 2016 Dexterity Design. All rights reserved.
//

// This part will be the bonus game where you can select one of 9 large circles for a
// score boost. A break from the gameplay section while retaining the one tap gameplay idea.

import SpriteKit

class bonusScene: SKScene {
    
    var myGameData:GameData!
    
    var exploding:Bool!
    var sceneExit:Bool!
    var temp:Bool!
    var bonusSelected:Bool!
    
    var score:Int!
    var nodeCounter:Int!
    var XP:Int!
    var YP:Int!
    var bonusNumber:Int!
    var bonusCounter:Int!
    
    var location:CGPoint!
    
    var radius:CGFloat!
    
    var bonus:SKSpriteNode!
    var continueButton:SKSpriteNode!

    var bonusLabel:SKLabelNode!
    var scoreLabel:SKLabelNode!
    
    let bonusScores = [0, 10, 100, 500, 1000, 1500, 2000, 2500, 5000]
    var bonusArray = [0, 10, 100, 500, 1000, 1500, 2000, 2500, 5000]
    
    let nodeX = [300, 500, 700]
    let nodeY = [200, 400, 600]
    
    ///MARK: Move to view.
    override func didMoveToView(view: SKView) {
        backgroundColor = SKColor.blackColor()
        #if DEBUG
            print ("Bonus scene is active.")
        #endif
        let screenSize = self.frame
        
        myGameData = GameData()
        score = Variables.score
        sceneExit = false       // Flag so that only one bonus node can be chosen.
        
        #if os(iOS)
            continueButton = SKSpriteNode(imageNamed: "IOS_Continue")
        #endif
        
        #if os(OSX)
            continueButton = SKSpriteNode(imageNamed: "OSX_Continue")
        #endif
        continueButton.position = CGPoint(x: screenSize.width * 0.50, y: screenSize.height * 0.10)
        continueButton.zPosition = 3
        self.addChild(continueButton)
        continueButton.hidden = true
        
        bonusLabel = SKLabelNode(fontNamed: "Chewy")
        bonusLabel.fontSize = 25
        bonusLabel.zPosition = 3
        bonusLabel.position = CGPoint(x: screenSize.width * 0.5, y: screenSize.height * 0.95)
        bonusLabel.fontColor = SKColor.greenColor()
        bonusLabel.text = ("*** Select a bonus score token ***")
        self.addChild(bonusLabel)

        scoreLabel = SKLabelNode(fontNamed: "Chewy")
        scoreLabel.fontSize = 25
        scoreLabel.zPosition = 4
        scoreLabel.fontColor = SKColor.yellowColor()
        scoreLabel.text = ("")
        scoreLabel.position = CGPoint(x: screenSize.width * 0.5, y: screenSize.height * 0.95)
        self.addChild(scoreLabel)
        scoreLabel.hidden = true
        
        nodeCounter = -1
        for YP in 0..<3 {
            for XP in 0..<3 {
                nodeCounter = nodeCounter+1
                location = CGPoint(x: nodeX[XP], y: nodeY[YP])
                drawBonusCircle(location , node: nodeCounter)
            }
        }
        random()
        #if DEBUG
            // seeing this sequence means you can work out which bonus scores the most.
            print("Number sequence \(bonusArray)")
        #endif
    }
    
    ///MARK: IOS input
    #if os(iOS)
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    // Called when a touch begins
    let touchLocation = touches.first?.locationInNode(self)
    buttonHandler(touchLocation!)
    }
    #endif
    
    // MARK: OSX Mouse Inputs.
    #if os(OSX)
    override func mouseDown(theEvent: NSEvent) {
        // Called when a mouse click begins
        let clickLocation = theEvent.locationInNode(self)
        buttonHandler(clickLocation)
    }
    #endif
    
    func buttonHandler(location:CGPoint) {
        let touchedNode = self.nodeAtPoint(location)
        if sceneExit == true {
            let myScene = gameScene(size: self.size)
            let transition = SKTransition.doorsCloseVerticalWithDuration(0.7)
            myScene.scaleMode = SKSceneScaleMode.AspectFill
            self.scene!.view?.presentScene(myScene, transition: transition)
        }
        
//        #if DEBUG
//            print ("Object: \(touchedNode.name) touched.")
//        #endif
//        if (touchedNode.name == "Exit") {
//            let myScene = gameScene(size: self.size)
//            let transition = SKTransition.doorsCloseVerticalWithDuration(0.7)
//            myScene.scaleMode = SKSceneScaleMode.AspectFill
//            self.scene!.view?.presentScene(myScene, transition: transition)
//        }
        
        if sceneExit == false {
            if let name = touchedNode.name {
                // Convert the touched node (named 0 to 8) into an integer so we can work out its score figure.
                sceneExit = true    // Ensure we can't select more than one bonus and that exit selected can't crash the game.
                continueButton.hidden = false
                bonusNumber = Int(name)
                Variables.score = Variables.score + bonusArray[bonusNumber]
                
                //let nodeX = [300, 500, 700]
                //let nodeY = [200, 400, 600]
                
                // Work out Y position of selected node.
                YP = 2
                if bonusNumber < 6 {
                    YP = 1
                }
                if bonusNumber < 3 {
                    YP = 0
                }
                
                // Work out X position of selected node. Temp code just to get working. 
                // but the or operator || wont work with an Int. There must be a neater way to do this.
                XP = 0
                if bonusNumber == 1 {
                    XP = 1
                }
                if bonusNumber == 2 {
                    XP = 2
                }
                if bonusNumber == 3 {
                    XP = 0
                }
                if bonusNumber == 4 {
                    XP = 1
                }
                if bonusNumber == 5 {
                    XP = 2
                }
                if bonusNumber == 6 {
                    XP = 0
                }
                if bonusNumber == 7 {
                    XP = 1
                }
                if bonusNumber == 8 {
                    XP = 2
                }
                let location = CGPoint(x: nodeX[XP], y: nodeY[YP])
                drawColourCircle(location , node: bonusNumber)
                scoreLabel.hidden = false
                scoreLabel.position = CGPoint(x: nodeX[XP], y: nodeY[YP])
                scoreLabel.text = ("\(bonusArray[bonusNumber])")
                #if DEBUG
                    print("Number:\(bonusNumber) & your score increased by:\(bonusArray[bonusNumber]) to \(Variables.score)")
                #endif
            }
        }
    }
    
    func random() {
        // This function will set up the bonus array with a score for each token.
        // How it works is a random number is chosen from 0 to 8 and then the counter starts from there loading the
        // bonusArray array with what's in the bonusScores array at that location. If it goes beyond 8 then it resets to location
        // 0 and carries on loading the array from there. Hence the scores will always be in order but, without knowing what the
        // starting counter was, player won't know this or be able to work it out as they can only select one bonus and the
        // locations for the scores changes each time.
        
        bonusCounter = Int(arc4random_uniform(9)) // random number 0 - 8. This is the node that is going to start with 0 bonus score.
        for temp in 0..<9 {
            bonusArray [temp] = bonusScores[bonusCounter]
            bonusCounter = bonusCounter + 1
            if bonusCounter > 8 {
                bonusCounter = 0
            }
        }
    }
    
    func drawBonusCircle(location:CGPoint, node:Int) {
        // Here we draw our 9 selectable nodes for the bonus score selector.
        let myBonusCircle:BonusCircle = BonusCircle(Radius: 10, Position: location, Exploding: true, ID: node)
        self.addChild(myBonusCircle)
        
    }
    
    func drawColourCircle(location:CGPoint, node:Int) {
        // Here we draw our selected bonus circle a different colour. We set exploding flag to false so the entity class knows.
        let myColourCircle:BonusCircle = BonusCircle(Radius: 10, Position: location, Exploding: false, ID: node)
        self.addChild(myColourCircle)
    }
    
    override func update(currentTime: CFTimeInterval) {
        /* Called before each frame is rendered */
        // Change colour or something here on the selectable tokens.
        self.enumerateChildNodesWithName("*") { // Checks everything so may need edit.
            node, stop in
            if let currentBonusCircle = node as? BonusCircle {
                currentBonusCircle.update()
            }
        }
    }
}
M2 Pro Mac mini - 16GB 512 SSD
ACER Nitro 5 15.6" Gaming Laptop - Intel® Core™ i7, RTX 3050, 1 TB SSD
Vic 20 - 3.5k 1mhz 6502

Latest game - https://xerra.itch.io/revenge-of-the-quadra
Blog: http://xerra.co.uk
Itch.IO: https://xerra.itch.io/

iWasAdam

mmmm, very interesting code, especially the compiler directives and input handling. I can certainly read and understand it all - so I'm not doing too badly.

I notice you're using SpriteKit (This is an apple 2d framework based with sprites), I looked at that but it didn't play nice with Metal (in the way I wanted).

For those that are interested in the source, here's some insights:
'let' is sort of the swift version of const. you can't change it
'var' is a variable that can be changed. you can give it a type or it will decide a type for you
you can cast a type E.G. let number = UInt16(33)

Jayenkai

Good luck.  It certainly looks scary when you first look at it, and especially coming from the Blitz's it took me a while to wrap my head around the methodology of Swift/ObjC, but ..  it's alright.  It's manageable.
And once I'd written a bunch of "SetColor/SetScale/DrawText/DrawImg/DrawRect,etc" commands, everything got that little bit easier to handle for my BASIC mangled brain to cope with.

I did all this back in 2010, trying to target iOS, and then promptly forgot it all once Monkey let me do it much easier!
Had to recently relearn it all, in the hope of getting back into the iOS AppStore and managed to get about 25% of the way there before the whole Euro vs Apple kerfuffle happened, at which point (only a couple of weeks into the project) I shrugged my shoulders and gave up.
A shame.  But I wasn't jumping through an extra myriad of complex hoops, and throwing my home address out for the world to see.

But good luck with this.  It'll definitely be worth the effort.  If for no other reason than you'll have learned your way around a new language!   Have fun :)
"Load, Next List"

iWasAdam

Yep, I'm at the reinventing ui stuff, colors, etc too ;)

It's all good fun thought lol


Xerra

Yeah, some of that code is even scary to me now. In all honesty, Morpheus had a much better handle on it than I actually did. I used to re-use sections he'd done for other areas to get the basic stuff being handled and feel my way through afterwards.

We were using spritekit because that, I think, gave us the facility to lay out all the static screens within xcode with our sprite elements and then just control them with references from the source code. As i recall, we had something like a seperate code file for each screen that was used in the game pretty much. A lot of it is going to be so out of date now, that it'd need a complete rewrite to replicate what i was doing back then. This source file, for example, was just laying out 9 circles and randomising a bonus score behind each of them, for the player to use as a simple mini-game between levels of the main game. I think I had a couple of other simple little games I was going to add in, had I finished the project.

One specific thing I do remember about Swift coding at the time was using a line of code with a ? like this example:

Code: BASIC
self.scene!.view?.presentScene(myScene, transition: transition)

I never quite got exactly what that was doing but apparently using the ? as a suffix on the view was to basically test and make sure it existed before actually carrying it out. I think this was so you could have that line trigger your error handler, perhaps. 

You're welcome to have a look at a few more of the source files from the project, if you like. But I suspect it's almost useless as a learning reference now. One day I might actually try and get it built again on the old iPad out of curiosity, as I still have all the assets and original project. xCode is so powerful that it could probably automatically update it for me now.
M2 Pro Mac mini - 16GB 512 SSD
ACER Nitro 5 15.6" Gaming Laptop - Intel® Core™ i7, RTX 3050, 1 TB SSD
Vic 20 - 3.5k 1mhz 6502

Latest game - https://xerra.itch.io/revenge-of-the-quadra
Blog: http://xerra.co.uk
Itch.IO: https://xerra.itch.io/


iWasAdam

I see racoons (not dead people). Maybe they are dead? whooo knoooows.... <grins>




iWasAdam

well I've now got text entry working and need to parse the text, and so begins relearning tokens and parsing and all things compiler related.

Currently I've got the basic tokoniser working (see pic). it will report errors when you press enter and also properly tokens the string too \o/

What is interesting is this feels like the 80's computers command line, and the current tokenising would work well to convert into pcode and run as basic!

Pst, it's also native macos M code, so play very nice and removes all the issues I was having before with monkey and the like!

iWasAdam


Alienhead

#10
-- deleted --