
Um mecanismo de jogo 2D baseado em CEs e escrito em 100% Swift para iOS, macOS, TVOS e Visionos.
Cuidado
? Este projeto não é mais atualizado pelo único mantenedor.
? Eu me mudei para Godot. Confira comedot ← minha estrutura baseada em componentes para jogos 2D em Godot!
Importante
❕ Octopuskit requer Octopuscore. A funcionalidade não-jogo foi dividida em um repositório separado para uso em aplicativos gerais. Para a última versão independente, consulte 4.0.0-beta-5
Se você tentou fazer um jogo em Swift enquanto mantém as APIs oficiais, isso pode ser para você! Octopuskit envolve e estende as estruturas da Apple:
• GamePlayKit para uma arquitetura flexível do sistema de componentes de entidade para compor dinamicamente o comportamento do jogo.
• SpriteKit para gráficos 2D, física e shaders de GPU.
• Swiftui para projetar rapidamente HUDs escaláveis com uma sintaxe declarativa.
• Metal para garantir o melhor desempenho nativo sob o capô.
• Componentes independentes do OS permitem lidar com a entrada de mouse/toque ou teclado/gamepad com o mesmo código e compilar nativamente para iOS + macOS sem precisar de catalisador.

Octopuskit é um trabalho constante em andamento e ainda estou aprendendo à medida que avançar, para que isso possa mudar rapidamente sem manter a compatibilidade com versões anteriores ou atualizar a documentação.
Este projeto é o resultado de minhas tentativas de fazer jogos em Pure Swift. Eu me apaixonei pelo idioma, mas não consegui encontrar motores que o apoiassem ou tivessem o tipo de arquitetura que achei intuitiva, então comecei a fazer o meu.
Feedback bem -vindo! - Shinryakutako
Ansioso para mergulhar? Adicione o Octopuskit como uma dependência do Swift Package Manager a um projeto Swiftui e use o modelo Quickstart (que também serve como uma pequena demonstração.)
? Usando com 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 )
}
}? Criando um sprite animado
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 " ) ] )? Adicionando controle do jogador
// 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 ( ) ] )? Removendo dinamicamente o controle do jogador ou mudando para um método de entrada diferente
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 ( ) ] )? Um componente personalizado específico
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
}
}? Usando um fechamento personalizado para alterar a animação com base no movimento do jogador
// 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.? Carregando uma cena construída no editor de cenas Xcode e criando várias entidades a partir de sprites identificados por um nome compartilhado
// 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.Octopuskit usa uma arquitetura "entidade-componente-sistema", onde:
? Um jogo é organizado em estados como Mainmenu , jogando e parado . Cada estado está associado a uma visualização Swiftui que exibe a interface do usuário e uma cena SpriteKit que apresenta a jogabilidade para esse estado usando entidades , componentes e sistemas .
Você pode dividir seu jogo em quantos estados quiser. Por exemplo, um único "PlayState" que também lida com o menu principal, pausando, cenas etc.
Estados, cenas e visualizações Swiftui podem ter relacionamentos muitos para muitos que podem mudar durante o tempo de execução.
? As entidades são simplesmente coleções de componentes . Eles não contêm lógica, exceto os construtores de conveniência que inicializam grupos de componentes relacionados.
? Os componentes (que também podem ser chamados de comportamentos, efeitos, recursos ou características) são o conceito central no Octopuskit, contendo as propriedades e a lógica* que compõem cada elemento visual ou abstrato do jogo. Um componente executa seu código quando é adicionado a uma entidade, quando um quadro é atualizado e/ou quando é removido de uma entidade. Os componentes podem consultar sua entidade para outros componentes e afetar o comportamento um do outro para formar dependências dinâmicas durante o tempo de execução. O motor vem com uma biblioteca de componentes personalizáveis para gráficos, jogabilidade, física etc.
⛓ Os sistemas são simplesmente coleções de componentes de uma classe específica. Eles não realizam nenhuma lógica*, mas são organizados por uma cena em uma matriz para executar componentes de todas as entidades em uma ordem determinística em todos os quadros, para que os componentes que dependam de outros componentes sejam atualizados após suas dependências.
* Essas definições podem diferir de outros motores, como a unidade, onde toda a lógica está contida nos sistemas.
? Os elementos da interface do usuário , como botões, listas e HUDs, são projetados no SwiftUi . Isso permite animações fluidas, texto nítido, formas vetoriais, visualizações ao vivo, atualizações automáticas orientadas a dados e mais de 1.500 ícones de alta qualidade dos símbolos SF da Apple.
Consulte a documentação da arquitetura para obter um detalhamento detalhado da hierarquia de objetos.
Seu fluxo de trabalho principal será escrever classes de componentes para cada "parte" dos gráficos e da jogabilidade e, em seguida, combiná -los para criar entidades que aparecem na tela ou entidades abstratas que lidam com dados no "back -end".
Por exemplo, uma parte do parallaxbackground que contém um componente do clouds , um componente de colinas e um componente de árvores ou uma graça de jogos contendo um componente mundial e um multiplayersyncComponent .
Desempenho: Embora os benchmarks extensos ainda não tenham sido feitos, o OK pode exibir mais de 5000 sprites em um iPhone XS a 60 quadros por segundo ; Cada sprite representado por uma entidade com vários componentes sendo atualizados em todos os quadros e respondendo à entrada de toque.
Personalizado para Swift : Swift, Swift, Swift! A estrutura deve seguir as diretrizes estabelecidas para o design da API SWIFT. Tudo deve fazer sentido dentro de Swift e fluir perfeitamente com idiomas rápidos o máximo possível.
Vitamina 2D : No momento, o OK é principalmente uma estrutura para jogos 2D, mas não impede que você use tecnologias como cenário ou vistas de metal de baixo nível e pode ser usado para aplicativos que não sejam de jogo.
Ombros de ettinas : o motor aproveita o spritekit, o jogo, o Swiftui e outras tecnologias fornecidas pela Apple. Não deve tentar "combatê -los", substituí -los ou escondê -los atrás de muitas abstrações.
Ok é implementado principalmente por meio de subclasses e extensões personalizadas de classes SpriteKit e GamePlaykit, sem "obscurecê -las" ou bloquear você de interagir com as classes base. Isso permite que você adote essa estrutura de forma incremental e permite integrar seu jogo com as ferramentas Xcode IDE, como o editor de cenas, sempre que possível.
O acoplamento apertado com as APIs da Apple também garante que seu jogo seja à prova de futuro; Sempre que a Apple melhora essas estruturas, o Octopuskit e seus jogos também devem obter alguns benefícios "de graça". Por exemplo, quando o Metal foi introduzido, o SpriteKit foi atualizado para usar automaticamente o Metal em vez do OpenGL sob o capô, dando a muitos jogos existentes um impulso de desempenho. (WWDC 2016, Sessão 610)
O código vem primeiro : ok é principalmente um motor "programático"; Quase tudo é feito no código. Isso também ajuda no controle da fonte. O editor de cenas do Xcode é relegado ao status de "cidadão de segunda classe" por causa de sua incompletude e bugs (em maio de 2018, Xcode 9.4), mas é suportado onde quer que seja conveniente. Veja o próximo ponto.
Você pode projetar layouts/maquetes de alto nível no editor de cenas, usando nós de espaço reservado com nomes (identificadores.) Você pode criar entidades desses nós e adicionar componentes a eles no código.
Agora, com o Swiftui, a programação para plataformas da Apple está indo para um foco no código, em vez de editores visuais de qualquer maneira.
Customizabilidade e flexibilidade : o motor se esforça para ser flexível e oferece a liberdade de estruturar seu jogo de várias maneiras. Como você tem acesso total ao código -fonte do mecanismo, você pode modificar ou estender qualquer coisa para atender às necessidades exatas de cada projeto.
Você pode usar qualquer uma das seguintes abordagens para construir suas cenas, em ordem de suporte ao motor:
- Execute a criação e colocação dos nós principalmente no código. Use o editor de cena do Xcode com pouca frequência, para projetar e visualizar alguns elementos individuais, como entidades com posições específicas etc., não cenas inteiras e use
SKReferenceNodepara carregá -las no código.
- Use o Xcode Scene Editor como seu ponto de partida, para criar cenas de modelo que possam ser carregadas como instâncias de
SKReferenceNodede nível superior de uma cena daOKScene. Essa abordagem permite um pouco de design visual e visualização "Wysiwyg".
- Crie uma cena quase inteiramente no editor de cenas do Xcode, adicionando quaisquer componentes, ações, órgãos de física, gráficos e texturas de navegação etc. no IDE.
Defina a classe personalizada da cena comoOKSceneou uma subclasse dela. Carregue a cena ligando paraOKViewController.loadAndPresentScene(fileNamed:withTransition:), por exemplo, durante odidEnter.from(_:)Evento de umOKGameState.
- Você não precisa usar nenhuma das arquiteturas e padrões sugeridos aqui; Você não precisa usar estados de jogo, e seus objetos de jogo nem precisam herdar de nenhuma aula OK. Você pode usar sua própria arquitetura e apenas usar OK para alguns métodos auxiliares etc., mantendo apenas o que você precisa dessa estrutura e excluindo o restante da compilação.
Autqueção : você não precisa fazer o download ou acompanhar outras bibliotecas de terceiros se seu próprio projeto não exigir; Tudo o que o OK usa está dentro das estruturas OK ou da Apple, por isso vem totalmente utilizável pronta para uso.
Leia o Guia do QuickStart and Uso. Você precisará de Xcode 12, iOS 14 e MacOS Big Sur (embora OK possa funcionar em versões mais antigas com algumas modificações manuais.)
Nível de habilidade: intermediário : embora OK não seja apresentado de uma forma projetada para iniciantes absolutos, principalmente porque estou com preguiça de escrever documentação da etapa zero, também não é material de nível "avançado"; Se você leu o Swift Language Book e tentou fazer um jogo Spritekit no Xcode, está pronto para usar OK!
Você também deve ler sobre os padrões "Composição sobre herança" e "Entidade -Componente -Sistema" se você ainda não estiver familiarizado com esses conceitos, embora a implementação de OK seja diferente do que você espera.
Veja também os tutoriais da Apple para Swiftui.
Para uma visão geral detalhada da arquitetura do motor, consulte a arquitetura.
Preso? Veja Dicas e solução de problemas.
Quer saber se algo foi feito intencionalmente do jeito que é ou por quê? Convenções de codificação e decisões de design podem ter uma explicação.
Deseja acompanhar o que está por vir ou ajudar com o desenvolvimento de recursos ausentes? Veja o TODO & ROADMAP.
Colaboradores e apoiadores ❤︎
Este projeto pode ser chamado de Octopuskit, "OK" ou "Okio" (para "Octopuskit, invadindo o polvo"), mas "iook" parece estranho.
A nomeação é uma combinação de inspiração de empresas como Rogue Amoeba, o domínio .IO e o anime Shinryaku! Ika Musume .
O espaço antes do último ]) na seção Exemplos é para clareza. :)
Licença: Apache 2.0
Incorpora shaders de Shaderkit © Paul Hudson, licenciado sob licença do MIT (consulte Cabeçalhos em arquivos relevantes).
Diga -me como tudo é incrível ou terrível: discórdia, twitter ou ?ctopus?it@?nvading?ctopus.ⓘⓞ
No entanto, eu raramente verifico isso, então a melhor maneira de fazer uma pergunta pode ser abrindo um problema no repositório do GitHub.
Apoie meu estilo de vida decadente para que eu possa me concentrar em fazer coisas inseguras: meu patreon
Este projeto não é afiliado de forma alguma com a Apple.
Octopuskit © 2023 Invading Octopus • Apache License 2.0