Octopuskit
2D ECS game engine in 100% Swift + SwiftUI for iOS, macOS, tvOS
Install / Use
/learn @InvadingOctopus/OctopuskitREADME
OctopusKit 
A 2D game engine based on ECS and written in 100% Swift for iOS, macOS, tvOS and visionOS.
[!CAUTION] 😔 This project is no longer updated by the sole maintainer.
🌟 I have moved to Godot. Check out [Comedot][comedot] ← my components-based framework for 2D games in Godot!
[!IMPORTANT] ❕ OctopusKit requires [OctopusCore][octopuscore]. Non-game functionality was split into a separate repository for use in general apps. For the last standalone version, see 4.0.0-beta-5
If you've tried making a game in Swift while sticking to the official APIs, this may be for you! OctopusKit wraps and extends Apple's frameworks:
• GameplayKit for a flexible Entity-Component-System architecture to dynamically compose game behavior.
• SpriteKit for 2D graphics, physics and GPU shaders.
• SwiftUI for quickly designing fluid, scalable HUDs with a declarative syntax.
• Metal to ensure the best native performance under the hood.
• OS-independent components let you handle mouse/touch or keyboard/gamepad input with the same code, and compile natively for iOS + macOS without needing Catalyst.

OctopusKit is a constant work in progress and I'm still learning as I go, so it may change rapidly without maintaining backwards compatibility or updating the documentation.
This project is the result of my attempts to make games in pure Swift. I fell in love with the language but couldn't find any engines that supported it or had the kind of architecture that I found intuitive, so I started making my own.
Feedback welcome! – ShinryakuTako
Examples
🚀 Eager to dive in? Add OctopusKit as a Swift Package Manager dependency to a SwiftUI project, and use the [QuickStart template][quickstart] (which also serves as a little demo.)
🎨 Using with SwiftUI
import SwiftUI
import OctopusKit
struct ContentView: View {
// The coordinator object manages your game's scenes and global state.
@StateObject var gameCoordinator = OKGameCoordinator(states: [
MainMenu(),
Lobby(),
Gameplay() ])
var body: some View {
// The container view combines SpriteKit with SwiftUI,
// and presents the coordinator's current scene.
OKContainerView()
.environmentObject(gameCoordinator)
.statusBar(hidden: true)
}
}
👾 Creating an animated sprite
var character = OKEntity(components: [
// Start with a blank texture.
NodeComponent(node: SKSpriteNode(color: .clear, size: CGSize(widthAndHeight: 42))),
// Load texture resources.
TextureDictionaryComponent(atlasName: "PlayerCharacter"),
// Animate the sprite with textures whose names begin with the specified prefix.
TextureAnimationComponent(initialAnimationTexturePrefix: "Idle") ])
🕹 Adding player control
// Add a component to the scene that will be updated with input events.
// Other components that handle player input will query this component.
// This lets us handle asynchronous events in sync with the frame-update cycle.
// A shared event stream is more efficient than forwarding events to every entity.
// PointerEventComponent is an OS-agnostic component for touch or mouse input.
let sharedPointerEventComponent = PointerEventComponent()
scene.entity?.addComponent(sharedPointerEventComponent)
character.addComponents([
// A relay component adds a reference to a component from another entity,
// and also fulfills the dependencies of other components in this entity.
RelayComponent(for: sharedPointerEventComponent),
// This component checks the entity's PointerEventComponent (provided here by a relay)
// and syncs the entity's position to the touch or mouse location in every frame.
PointerControlledPositioningComponent() ])
🕹 Dynamically removing player control or changing to a different input method
character.removeComponent(ofType: PointerControlledPositioningComponent.self)
character.addComponents([
// Add a physics body to the sprite.
PhysicsComponent(),
RelayComponent(for: sharedKeyboardEventComponent),
// Apply a force to the body based on keyboard input in each frame.
KeyboardControlledForceComponent() ])
🧩 A custom game-specific component
class AngryEnemyComponent: OKComponent, RequiresUpdatesPerFrame {
override func didAddToEntity(withNode node: SKNode) {
node.colorTint = .angryMonster
}
override func update(deltaTime seconds: TimeInterval) {
guard let behaviorComponent = coComponent(EnemyBehaviorComponent.self) else { return }
behaviorComponent.regenerateHP()
behaviorComponent.chasePlayerWithExtraFervor()
}
override func willRemoveFromEntity(withNode node: SKNode) {
node.colorTint = .mildlyInconveniencedMonster
}
}
🛠 Using a custom closure to change the animation based on player movement
// Add a component that executes the supplied closure every frame.
character.addComponent(RepeatingClosureComponent { component in
// Check if the entity of this component has the required dependencies at runtime.
// This approach allows dynamic behavior modification instead of halting the game.
if let physicsBody = component.coComponent(PhysicsComponent.self)?.physicsBody,
let animationComponent = component.coComponent(TextureAnimationComponent.self)
{
// Change the animation depending on whether the body is stationary or mobile.
animationComponent.textureDictionaryPrefix = physicsBody.isResting ? "Idle" : "Moving"
}
})
// This behavior could be better encapsulated in a custom component,
// with many different game-specific animations depending on many conditions.
🎎 Loading a scene built in the Xcode Scene Editor and creating multiple entities from sprites identified by a shared name
// Load a ".sks" file as a child node.
if let editorScene = SKReferenceNode(fileNamed: "EditorScene.sks") {
scene.addChild(editorScene)
}
// Search the entire tree for all nodes named "Turret",
// and give them properties of "tower defense" turrets,
// and make them independently draggable by the player.
for turretNode in scene["//Turret"] {
// Create a new entity for each node found.
scene.addEntity(OKEntity(components: [
NodeComponent(node: turretNode),
RelayComponent(for: sharedPointerEventComponent),
// Hypothetical game-specific components.
HealthComponent(),
AttackComponent(),
MonsterTargetingComponent(),
// Track the first touch or mouse drag that begins inside the sprite.
NodePointerStateComponent(),
// Let the player select and drag a specific sprite.
// This differs from the PointerControlledPositioningComponent in a previous example,
// which repositions nodes regardless of where the pointer began.
PointerControlledDraggingComponent() ]))
}
// Once the first monster wave starts, you could replace PointerControlledDraggingComponent
// with PointerControlledShootingComponent to make the turrets immovable but manually-fired.
Overview
OctopusKit uses an ["Entity-Component-System"][entity–component–system] architecture, where:
-
🎬 A game is organized into States such as MainMenu, Playing and Paused. Each state is associated with a SwiftUI view which displays the user interface, and a SpriteKit Scene that presents the gameplay for that state using Entities, Components and Systems.
You can divide your game into as many or as few states as you want. e.g. A single "PlayState" which also handles the main menu, pausing, cutscenes etc.
States, Scenes, and SwiftUI views may have many-to-many relationships that may change during runtime.
-
👾 Entities are simply collections of Components. They contain no logic, except for convenience constructors which initialize groups of related components.
-
🧩 Components (which could also be called Behaviors, Effects, Features, or Traits) are the core concept in OctopusKit, containing the properties as well as the logic* which make up each visual or abstract element of the game. A component runs its code when it's added to an entity, when a frame is updated, and/or when it's removed from an entity. Components may query their entity for other components and affect each other's behavior to form dynamic dependencies during runtime. The engine comes with a library of customizable components for graphics, gameplay, physics etc.
-
⛓ Systems are simply collections of components of a specific class. They don't perform any logic*, but they're arranged by a Scene in an array to execute components from all entities in a deterministic order every frame, so that components which rely on other components are updated after their dependencies.
* These definitions may differ from other engines, like Unity, where all the logic is contained within systems.
-
🎛 User Interface elements like buttons, lists and HUDs are designed in SwiftUI. This allows fluid animations, sharp text, vector shapes, live previews, automatic data-driven updates, and over 1,500 high-quality icons from Apple's [SF Symbols.][sf-symbols]
See the [Architecture documentation][architec
Related Skills
node-connect
343.3kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
92.1kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
343.3kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
343.3kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
