Void Galaxia Only for Apple TV

Void Galaxia is a Shoot-em-up game for Apple TV®, using Apple’s XCode® IDE, Swift™ programming language, and the SpriteKit API. I wrote it to get familiar with Swift and Apple’s App development process. The app is FREE and only available on the App Store for Apple TV devices.

Development Details

Player Movement

The player ship movement is based on the Rotation code by Batu Orhanalp. This code uses the gravity variable in Apple TV’s remote motion property; then it calculates rotational delta of its current and previous gravity.

The game uses this rotation calculation to determine how fast the ship moves, with a maximum speed being at a right angle (90 degrees) from center, with an 8 degree “dead zone” in the center of the range, where the ship does not move at all.

Due to the difficulty of precision movement with the motion controls, I added a brake button to freeze the ship in place. This allows a player to make precise movements more easily and evade in-between enemy bullets.

Enemy Movement

Enemies use a sequence of SpriteKit’s SKActions to determine how they move. I built an array of a custom class called MovePoint, which contains:

    • the point to move to,
    • the time delay before each movement,
    • either the time it takes to get to that point or the speed in which it moves,
    • whether it speeds up or slows down, and
    • whether it moves in a straight or curved line.

The setMovement() function contains three (3) variables:

    • a static movement done once,
    • a (possible) repeating movement, and,
    • if there is no repeat, whether or not the enemy dies at the end (for enemies that leave the screen and don’t come back).

Originally I used “move here” points to decide how the enemies move, but I switched to “move by this much” points. This way it’s easier to write certain movement patterns (a straight horizontal line always being x: ###, y: 0) instead of having to remember the y value of the enemy, at that point.

Player and Enemy Hit-Boxes

For the player ship, enemy ships, and bullets, I use the physics body code with matching category/collision bit masks – one (1) for the player ship and anything that can hit it, and two (2) for the player bullets and enemy ships – which determine collisions.

I disabled all gravity/collision effects of the physics bodies since they’re unneeded. While everything else has a physics body matching their sprite size, I made the physics body of the player ship tiny on purpose. This allows more difficult enemy patterns, without making it impossible.

Player Targeting System

The player should be able to focus more on avoiding enemies and not worry too much about returning enemy fire. So I made a crosshair, that’s controlled with the trackpad on the Apple TV Remote, to lock-on to enemies and makes the player’s ship shoot automatically. For this, I used of SKPhysicsbody again. The crosshair has the same bit mask as enemies, and – if not locked on or moving – it checks if any enemies are overlapping with the physics body of the crosshair, and can “lock-on” to an enemy if it is.

I added a “priority” variable on enemies, where the crosshair will lock-on to enemies, with the lower priority value first. This is to make it easy to target the first and closest enemy in a line, and then move down the line as they all die.

At first, the physics body of the crosshair (the range where it can find enemies) was about the same size as the crosshair itself. This was too small, didn’t catch enough area around the crosshair, and made it hard to focus on some enemies. That defeats the purpose of the lock-on function in the first place. I began experimenting some with a “laser targeting” system, where the ship can shoot out a laser that finds enemies. Eventually, I decided to simply triple the size of the crosshair’s physics body, which allows it to catch enemies from much farther away.

Enemy Shooting System

As the main point of the game is to have enemies with ridiculously complex shot patterns, I went through a very large number of revisions before I was happy with how it worked.

First: the bullets themselves. I created a protocol called “Shootable,” which lays the groundwork functions and ensures that bullets have everything they need to work properly. It’s more efficient because it allows classes outside to work with a generic “shootable” class, that doesn’t need to worry about how the different types of bullets work.

One step up from the bullets is the Volley class. This contains a group of bullets and any information pertaining to how the Volley is fired. Specifically:

    • how many times it is fired,
    • the delay before/after,
    • the time between shots, and
    • differences in angle between each round.

Next is the ShotSequence class. This is a double array of Volleys, used as a sequence of groups. Each inner array is fired simultaneously, then after the group is finished, it moves onto the next array.

Finally, the shot sequence is added to an object on-screen. This is either a gun on an enemy ship (which repeatedly shoots the sequence on a timer), or it’s a bullet (which causes the sequence to fire from the bullet’s position after a similar timer).

I created all of these in order to make the very last portion of the final boss possible, where a single bullet is fired which then shoots various different waves.

Menus

I made the menus using Xcode’s SpriteKit scene builder. For text, I used the VCR OSD Mono font, a downloadable, royalty-free font. The menu controls I made with UIKit/SpriteKit’s UIGestureRecognizers.

For best UX gameplay, I created a screen (appears when the game boots), showing the player how to hold the remote sideways, since the menu controls that way.

Sounds

I composed all of the sound effects with either GarageBand or BFXR.net (a retro sound effects creation website). For GarageBand, I used the magical-8-bit plugin freeware from YMCK.net and a Nektar Impact GX61 USB Keyboard. On BFXR.net, I manipulated sounds using their synthesizer. Sounds are played within the game by SKAction playSoundFileNamed().

I composed the music, also using GarageBand and the USB keyboard. The music is played by a global AVAudio player because it needs to be persistent between scenes. During the between scene transitions (fade-ins, etc.), both scenes are technically present. In order to have the music stop at the beginning of the transition and play at the end, music needs to stop, then choose the next song, on the incoming scene’s didMove(toView) and play on the outgoing scenes willMove(fromView).

Sprites/Images

I drew all sprites and images using the Aseprite sprite-drawing program. I’m red-green color deficient (AKA color-blind), so I chose mostly primary colors. Since the game background is dark, most of the colors needed to be quite light. I made a quick doodle for most of the images before using the sprite program.

Bugs, Glitches, & Performance

There were quite a few bugs and errors. One of the more annoying bugs was that the collision box for laser-type projectiles became very inaccurate. Re-drawing the collision box each frame instead of simply stretching it fixed the problem, and it doesn’t seem to have affected performance that badly.

On performance, the update function – which is called 60 times a second at its best – had to be limited to look through the # of objects onscreen only once. I fixed this by creating the Updateable protocol. Anything that needs to be altered for each update has this protocol added to it. Each GameScene calls update on each object “as? Updateable” using Swift’s optionals ensuring that only the objects that actually have the function calls it.

Another issue was the “muzzle flash” particle effect – every time the player ship would shoot, a small particle effect would appear to give the effect of its guns shooting. The whole thing wound up being fairly convoluted. In order to allow a 2nd particle effect to appear before the first one was finished, it had to be copied and added again. This resulted in several particle effects being copied, added, and removed constantly. Eventually I removed it due to the performance hit and tricky coding required to make it work properly.

In order to find the performance issues, I used the profiling tools supplied by Xcode, while running the App.

Extensions made for convenience

I extended a few classes in Swift and SpriteKit to make my own code easier to write.

The first is SKSpriteNode. I created an initializer that takes a UIImage. Xcode’s autofill lets you add pictures from the assets folders as UIImages. These UIImages appear in the Xcode workspace as the picture itself instead of text. It’s almost surprising that this isn’t a part of the class to begin with.

The other is an extension to various number classes to make common conversions easier. In Double, .toRadians, and .toDegrees variables to make angle conversions easier. I used a .cgfloat variable in Double, a .double variable in CGFloat, and both variables in the Int class make conversions between types smoother and less cluttered, as well as allowing Xcode’s autocomplete to handle most of it.

© 2017 Void Galaxia | Vianki, LLC | Developed by Zachary Garbarino. All Rights Reserved.

Apple, the Apple logo, Apple TV,  and XCode are trademarks of Apple Inc., registered in the U.S. and other countries. App Store is a service mark of Apple Inc., registered in the U.S. and other countries. Swift is a trademark or registered trademark of Cisco in the U.S. and other countries and is used under license. Other product and company names mentioned herein may be trademarks of their respective companies.