+1 for the answers so far, but I just don't like SpriteKit. I don't understand sprites that are apparently also objects and worse, are automatically drawn somehow. I need to see code. I need to see code that I actually wrote, or at least actually called the library function. Otherwise I feel like I don't have complete control over what is being drawn, when it's changed, when the object disappears even tho the object may not have been destroyed, etc. As a rule of thumb, a fully functional language such as Swift can do this without having to learn another language within a language (wizard or SpriteKit UI or something), and it's the way I've been programming for years.
Therefore I decided to answer my own question in the hopes that other programmers similar to me will get off to the right start.
Who am I? A C# programmer who's made many games/apps/simulations in XNA and MonoGame using Visual Studio. Up until a few days ago, I had never touched a Mac or iOS languages. I love C# and thought iOS programming in Objective-C would be similar. Boy was I wrong. And regarding other options, why use SpriteKit or SceneKit when you can just use the native draw functions? (called Quartz I believe, but that threw me off. Quartz is just the native Swift drawing library as far as I can tell.) That would be like learning another language within a language when you're already trying to learn Objective-C or Swift.
If that's who you are, then this answer is for you:
Use this tutorial to setup your new iOS game and learn how to draw. In particular, you want a "Single View App" (not "Game"!). The tutorial makes a new class called Draw2D that has the main drawing function. It shows how to draw primitives such as lines, and at the end it shows you how to draw pictures. Violla! No SpriteKit wizards necessary that you have no idea about anyway! Pretend the Draw2D class is your "main" class and put your main variables and object lists/arrays in there. This will make it very easy to reference all objects, which are created and stored in arrays or lists.
Absolutely use the Swift language! Do not use Objective-C! (This will force you to target iOS versions no earlier than 7, but that's definitely old enough to let a lot of phones run your game). Swift is native iOS code and similar to C# but with different arrangements in the syntax. Objective-C is basically stone-age C hacked to unreadable dangers. Just from looking at it, I'm pretty sure you can blow your own legs off by going out-of-bounds during file writes or array traverses...just like plain C. Yes this is my opinion, but if you're reading this answer, you're hopefully like me.
Learn what ! and ? do. Not even I am 100% sure yet, but basically, Swift does not allow objects to be null (nil) by default---unless you explicitly say they can with a '?' at the end of the variable name at instance declaration. Same thing with functions returning an object. Use '!' to force the language to not do any safe nil checking when accessing the object. For some reason the compiler is gonna require this on certain accesses to your object. Just be safe and always manually do
if (MyObj != nil) first.
You need to set up a NSTimer to automatically call your draw function every x seconds. This is very important! Don't skip this huge paragraph! Find the code here or just copy this:
var timer = NSTimer.scheduledTimerWithTimeInterval(0.033, target: self, selector: "draw_now", userInfo: nil, repeats: true). Make sure the timer is a part of Draw2D. See here's the thing. For some reason, the custom UIView we created in the tutorial only gets called once or twice at the beginning, so we need a timer to call it again and again. I wanted 30 fps so I used 0.033 for the interval (so it will get called once every 0.033 seconds). But there's a trick to this. The timer needs to call a selector; it's the string parameter. But don't make this your default drawRect() function! Instead, make a new function called
draw_now() like I did, so the NSTimer parameter should be "draw_now", no parentheses, and in draw_now() (a function of Draw2D) put this sole line of code:
[setNeedsDisplay()]. Type that exactly, including the brackets. That's the trick! That code somehow forces the UIView to redraw itself by calling the
drawRect() function. I have no idea why, but if you set up your timer to call drawRect() like I mistakenly did at first, it just doesn't work. drawRect() gets called again and again, and you can check that in the debugger, but things just don't get drawn for mysterious reasons having to do with bizarre and ungainly things called contexts. Anyway, you have to call a function with a single line of code:
[setNeedsDisplay()]. And that code goes and does magical things to make the system call drawRect() with the right context. Many thanks to stackoverflow at this link for that code.
In my main draw function (drawRect()), the first thing I did was call
master_update(), which is my own function I made. It basically loops through all the arrays of objects and calls their individual update functions. The point is, with this setup, the timer effectively runs the update loop and the draw loop, which are therefore in sync. I always want in-sync updates and draws in my games, though you may want it differently. Personally I've never found a reason to use variable draw rates, and keeping it in sync makes things so much simpler.
Access the touch functions so you can control what happens when a user touches the screen. Go to this youtube video and jump to 2:40, where he begins overriding the
touchesBegan function. Make sure this overridden func is in your Draw2D class.
Each class needs its own constructor, and Swift is picky about it. I had trouble with this because Swift allows many weird types, such as
delegate, and others. The one foolproof constructor I found was
required init?(parameter1: parametertype1, parameter2: parametertype2, etc). All my classes used this as the one and only constructor that the compiler did not complain about. Even Draw2D had this same single constructor, but it needed a weird parameter:
required init?(coder aDecoder: NSCoder). Not sure what that was all about. However, your constructor may need to call
super.init() at the end, which may have its own parameters too. I think the super thing is for subclasses that inherit parent classes, so you have to call the parent's constructor too. This is apparently necessary before using
self (which would be
this in C#) in your constructor.
Go here and learn about drawing points. Not pixels. Points. They are different things in the Mac world. This is not how we do it on desktop PCs, but so far I haven't had much trouble. Apparently it's pretty useful to draw consistently to different devices with different screen resolutions. It gave me a fairly straightforward way to just scale everything to fit the screen, which is the quick and dirty way I wanted to support multiple devices for my first iOS game. By the way, you can do this with
var screen_width = UIScreen.mainScreen().fixedCoordinateSpace.bounds.width, which gives you the width of the actual screen in points. (Unfortunately, this will force you to target iOS 8 and above, which means less iPhones will support your app. I have not yet found a different way to do this while staying compatible with iOS 7. But according to this, it's not much to worry about.) Then if you want a 20x12 grid, each square will be
Now you are finally ready to program 99% the same way you did in XNA and MonoGame! All your objects have their own
draw() functions, and in the main update you just iterate through each object in your array and call
MyObj.update(); Same thing with the draw.
Keep in mind I'm using a Mac Book running Yosemite 10.10.5 on Xcode 7 with the Swift Language. Who knows what could happen, if anything, on later versions.