├── .cursor └── rules │ └── cursor-tools.mdc ├── .gitignore ├── .repomix ├── CHANGELOG.md ├── CONTRIBUTE.md ├── LICENSE ├── README.md ├── docs └── viber3d-docs │ ├── .env.example │ ├── .gitignore │ ├── README.md │ ├── app.config.ts │ ├── app.vue │ ├── assets │ └── css │ │ └── main.css │ ├── components │ ├── AppFooter.vue │ ├── AppHeader.vue │ ├── LogoPro.vue │ ├── OgImage │ │ └── OgImageDocs.vue │ └── TemplateMenu.vue │ ├── content.config.ts │ ├── content │ ├── 1.getting-started │ │ ├── .navigation.yml │ │ ├── 1.index.md │ │ ├── 2.installation.md │ │ ├── 3.project-structure.md │ │ ├── 4.cli.md │ │ ├── 5.demo.md │ │ ├── 6.credits.md │ │ └── 7.usage.md │ ├── 2.introduction │ │ ├── 1.what-is-viber3d.md │ │ └── 2.why-use-viber3d.md │ ├── 3.development │ │ ├── 1.running-dev-server.md │ │ ├── 2.production-build.md │ │ └── 3.cursor-rules.md │ ├── 4.core-concepts │ │ ├── 1.ecs-overview.md │ │ ├── 2.entities.md │ │ ├── 3.traits.md │ │ ├── 4.systems.md │ │ ├── 5.components.md │ │ ├── 6.actions.md │ │ └── 7.advanced.md │ ├── 5.addons │ │ ├── 1.physics.md │ │ └── 2.batch-mesh.md │ ├── 6.contributing │ │ ├── 1.intro.md │ │ └── 2.releases.md │ └── index.md │ ├── error.vue │ ├── eslint.config.mjs │ ├── layouts │ └── docs.vue │ ├── nuxt.config.ts │ ├── package.json │ ├── pages │ ├── [...slug].vue │ └── index.vue │ ├── public │ ├── favicon.ico │ ├── favicon.png │ ├── fighter.png │ ├── llms_full.txt │ └── robots.txt │ ├── server │ └── tsconfig.json │ └── tsconfig.json ├── package.json ├── packages ├── core │ ├── index.ts │ └── package.json └── viber3d │ ├── .gitignore │ ├── README.md │ ├── build.config.ts │ ├── index.js │ ├── package.json │ ├── src │ └── index.ts │ ├── template │ └── README.md.mustache │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── public └── images │ └── banner.png ├── scripts └── release.ts └── templates ├── package.json ├── starter-next ├── .cursor │ └── rules │ │ ├── 001-base.mdc │ │ ├── 002-components.mdc │ │ ├── 003-systems.mdc │ │ ├── 004-actions.mdc │ │ ├── 005-traits.mdc │ │ ├── 006-utils.mdc │ │ ├── 007-tailwind.mdc │ │ ├── modes │ │ ├── game-agent.mdc │ │ └── plan.mdc │ │ └── templates │ │ ├── story.tpl.mdc │ │ └── task.tpl.mdc ├── .cursorindexingignore ├── .gitignore ├── .planr │ ├── assetlist.md │ ├── core │ │ └── currentPointer.json │ ├── prd.md │ ├── project-structure.md │ └── roadmap.json ├── .prettierignore ├── .prettierrc ├── .vscode │ ├── launch.json │ ├── settings.json │ └── tasks.json ├── CHANGELOG.md ├── README.md ├── eslint.config.js ├── index.html ├── package.json ├── public │ ├── favicon.ico │ └── favicon.png ├── src │ ├── actions.ts │ ├── app.tsx │ ├── assets │ │ ├── ships │ │ │ ├── enemy.glb │ │ │ └── fighter.glb │ │ └── sounds │ │ │ └── .gitkeep │ ├── components │ │ ├── camera-renderer.tsx │ │ ├── player-renderer.tsx │ │ └── postprocessing.tsx │ ├── frameloop.ts │ ├── main.tsx │ ├── startup.tsx │ ├── styles.css │ ├── systems │ │ ├── apply-force.ts │ │ ├── apply-input.ts │ │ ├── camera-follow-player.ts │ │ ├── limit-speed.ts │ │ ├── move-entities.ts │ │ ├── poll-input.ts │ │ ├── sync-view.ts │ │ ├── update-player-rotation.ts │ │ ├── update-spatial-hashing.ts │ │ └── update-time.ts │ ├── traits │ │ ├── index.ts │ │ ├── input.ts │ │ ├── is-camera.ts │ │ ├── is-player.ts │ │ ├── maxSpeed.ts │ │ ├── movement.ts │ │ ├── ref.ts │ │ ├── spatial-hash-map.ts │ │ ├── time.ts │ │ └── transform.ts │ ├── utils │ │ ├── between.ts │ │ ├── sort-entities-by-distance.ts │ │ └── spatial-hash.ts │ ├── vite-env.d.ts │ └── world.ts ├── tsconfig.json └── vite.config.ts └── starter ├── .cursor └── rules │ ├── 001-base.mdc │ ├── 002-components.mdc │ ├── 003-systems.mdc │ ├── 004-actions.mdc │ ├── 005-traits.mdc │ ├── 006-utils.mdc │ ├── 007-tailwind.mdc │ └── 900-game-agent.mdc ├── .cursorignore ├── .cursorindexingignore ├── .gitignore ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── README.md ├── docs ├── stories │ ├── 1.1-basic-ship-movement.md │ ├── 1.2-advanced-flight-maneuvers.md │ ├── 2.1-basic-laser-firing.md │ ├── 2.2-missile-system.md │ ├── 3.1-patrol-enemies.md │ ├── 3.2-aggressive-attackers.md │ ├── 3.3-evasive-enemies.md │ ├── 3.4-boss-battles.md │ ├── 4.1-endless-survival-mode.md │ ├── 5.1-unlockable-ships.md │ ├── 5.2-weapon-upgrades.md │ ├── 6.1-laser-projectile-effects.md │ ├── 6.2-explosion-debris.md │ ├── 6.3-weapon-muzzle-flash-and-sound-effects.md │ ├── 7.1-dynamic-sound-effects.md │ ├── 7.2-adaptive-music-system.md │ ├── 8.1-optimized-web-performance.md │ └── 8.2-controls.md └── tasklist.yaml ├── eslint.config.js ├── index.html ├── package.json ├── public ├── favicon.ico └── favicon.png ├── src ├── actions.ts ├── app.tsx ├── assets │ ├── ships │ │ ├── enemy.glb │ │ └── fighter.glb │ └── sounds │ │ └── .gitkeep ├── components │ ├── camera-renderer.tsx │ ├── player-renderer.tsx │ └── postprocessing.tsx ├── frameloop.ts ├── main.tsx ├── startup.tsx ├── styles.css ├── systems │ ├── apply-force.ts │ ├── apply-input.ts │ ├── camera-follow-player.ts │ ├── limit-speed.ts │ ├── move-entities.ts │ ├── poll-input.ts │ ├── sync-view.ts │ ├── update-player-rotation.ts │ ├── update-spatial-hashing.ts │ └── update-time.ts ├── traits │ ├── index.ts │ ├── input.ts │ ├── is-camera.ts │ ├── is-player.ts │ ├── maxSpeed.ts │ ├── movement.ts │ ├── ref.ts │ ├── spatial-hash-map.ts │ ├── time.ts │ └── transform.ts ├── utils │ ├── between.ts │ ├── sort-entities-by-distance.ts │ └── spatial-hash.ts ├── vite-env.d.ts └── world.ts ├── tsconfig.json └── vite.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # Testing 7 | /coverage 8 | 9 | # Production 10 | /build 11 | /dist 12 | dist-ssr 13 | 14 | # Logs 15 | logs 16 | *.log 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | pnpm-debug.log* 21 | lerna-debug.log* 22 | 23 | # Environment files 24 | .env 25 | .env.local 26 | .env.development.local 27 | .env.test.local 28 | .env.production.local 29 | 30 | # Editor and IDE files 31 | .vscode/* 32 | !.vscode/extensions.json 33 | .idea 34 | *.suo 35 | *.ntvs* 36 | *.njsproj 37 | *.sln 38 | *.sw? 39 | 40 | # OS generated files 41 | .DS_Store 42 | .DS_Store? 43 | ._* 44 | .Spotlight-V100 45 | .Trashes 46 | ehthumbs.db 47 | Thumbs.db 48 | 49 | # Misc 50 | *.local 51 | .history 52 | .vercel 53 | .data 54 | packages/create-codetie/debug.log 55 | repomix-output.txt 56 | .repomix* 57 | .hidden -------------------------------------------------------------------------------- /.repomix: -------------------------------------------------------------------------------- 1 | docs 2 | node_modules 3 | scripts 4 | public -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [Unreleased] 6 | 7 | ### Added 8 | - New template option (`-t` or `--template`) for the CLI init command 9 | - Support for custom GitHub templates with `-t username/repo` 10 | - Special template option `-t next` to use the experimental template from starter-next repository 11 | - Support for named templates from the Viber3D templates directory 12 | - Interactive template selection prompt when no template is specified 13 | - Choose between `starter` (recommended, stable) or `next` (experimental) 14 | 15 | ## [0.1.0] - YYYY-MM-DD 16 | 17 | ### Added 18 | - Initial release of Viber3D 19 | - CLI tool with init command to create new projects 20 | - Default starter template 21 | -------------------------------------------------------------------------------- /CONTRIBUTE.md: -------------------------------------------------------------------------------- 1 | # Contributing to viber3d 2 | 3 | Thank you for your interest in contributing to viber3d! This document provides guidelines and instructions to help you get started with contributing to the project. 4 | 5 | 6 | ## Getting Started 7 | 8 | ### Development Environment Setup 9 | 10 | 1. **Fork the Repository** 11 | 12 | Start by forking the repository to your GitHub account. 13 | 14 | 2. **Clone the Repository** 15 | 16 | ```bash 17 | git clone git@github.com:instructa/viber3d.git 18 | cd viber3d 19 | ``` 20 | 21 | 3. **Install Dependencies & Run** 22 | 23 | ```bash 24 | # Using npm 25 | pnpm install 26 | pnpm run dev 27 | ``` 28 | 29 | The development server will start at `http://localhost:5173`. 30 | 31 | ## Development Workflow 32 | 33 | 1. **Create a Branch** 34 | 35 | ```bash 36 | git checkout -b feat/your-feature-name 37 | ``` 38 | 39 | 2. **Make Your Changes** 40 | 41 | Implement your changes. 42 | 43 | 3. **Commit Your Changes** 44 | 45 | ```bash 46 | git commit -m "feat: add your feature description" 47 | ``` 48 | 49 | We follow [Conventional Commits](https://www.conventionalcommits.org/) for commit messages. 50 | 51 | 4. **Push to Your Fork** 52 | 53 | ```bash 54 | git push origin feat/your-feature-name 55 | ``` 56 | 57 | 5. **Create a Pull Request** 58 | 59 | Go to the [viber3d repository](https://github.com/instructa/viber3d) and create a pull request from your fork. 60 | 61 | ## Pull Request Process 62 | 63 | 1. Ensure your code follows the project's coding standards 64 | 2. Update documentation if necessary 65 | 3. Add tests for new features 66 | 4. Make sure all tests pass 67 | 5. Wait for code review and address any feedback -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Nuxt Project 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /docs/viber3d-docs/.env.example: -------------------------------------------------------------------------------- 1 | # Production license for @nuxt/ui-pro, get one at https://ui.nuxt.com/pro/purchase 2 | NUXT_UI_PRO_LICENSE= 3 | 4 | # Public URL, used for OG Image when running nuxt generate 5 | NUXT_PUBLIC_SITE_URL= 6 | -------------------------------------------------------------------------------- /docs/viber3d-docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # Testing 7 | /coverage 8 | 9 | # Production 10 | /build 11 | /dist 12 | dist-ssr 13 | 14 | # Nuxt 15 | .nuxt 16 | .output 17 | 18 | # Logs 19 | logs 20 | *.log 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | pnpm-debug.log* 25 | lerna-debug.log* 26 | 27 | # Environment files 28 | .env 29 | .env.local 30 | .env.development.local 31 | .env.test.local 32 | .env.production.local 33 | 34 | # Editor and IDE files 35 | .vscode/* 36 | !.vscode/extensions.json 37 | .idea 38 | *.suo 39 | *.ntvs* 40 | *.njsproj 41 | *.sln 42 | *.sw? 43 | 44 | # OS generated files 45 | .DS_Store 46 | .DS_Store? 47 | ._* 48 | .Spotlight-V100 49 | .Trashes 50 | ehthumbs.db 51 | Thumbs.db 52 | 53 | # Misc 54 | *.local 55 | .history 56 | .vercel 57 | .data 58 | packages/create-codetie/debug.log 59 | repomix-output.txt 60 | .repomix* -------------------------------------------------------------------------------- /docs/viber3d-docs/README.md: -------------------------------------------------------------------------------- 1 | # Viber3D Documentation 2 | 3 | This repository contains the official documentation for Viber3D, a modern 3D game starter kit for the web. 4 | 5 | ## Setup 6 | 7 | Make sure to install dependencies: 8 | 9 | ```bash 10 | # npm 11 | npm install 12 | 13 | # pnpm 14 | pnpm install 15 | 16 | # yarn 17 | yarn install 18 | 19 | # bun 20 | bun install 21 | ``` 22 | 23 | ## Development Server 24 | 25 | Start the development server on `http://localhost:3000`: 26 | 27 | ```bash 28 | # npm 29 | npm run dev 30 | 31 | # pnpm 32 | pnpm dev 33 | 34 | # yarn 35 | yarn dev 36 | 37 | # bun 38 | bun run dev 39 | ``` 40 | 41 | ## Production 42 | 43 | Build the application for production: 44 | 45 | ```bash 46 | # npm 47 | npm run build 48 | 49 | # pnpm 50 | pnpm build 51 | 52 | # yarn 53 | yarn build 54 | 55 | # bun 56 | bun run build 57 | ``` 58 | 59 | Locally preview production build: 60 | 61 | ```bash 62 | # npm 63 | npm run preview 64 | 65 | # pnpm 66 | pnpm preview 67 | 68 | # yarn 69 | yarn preview 70 | 71 | # bun 72 | bun run preview 73 | ``` 74 | 75 | ## Demo 76 | 77 | Experience Viber3D in action at our live demo: [viber3d-spacewars.kevinkern.dev](https://viber3d-spacewars.kevinkern.dev/) 78 | 79 | ## Documentation Structure 80 | 81 | The documentation is organized into the following sections: 82 | 83 | - **Introduction**: Overview of Viber3D and its features 84 | - **Getting Started**: Installation and basic usage 85 | - **Core Concepts**: Entity Component System, Entities, Traits, Systems, and Components 86 | - **Systems**: Detailed information about built-in systems 87 | 88 | ## Contributing 89 | 90 | Contributions to the documentation are welcome! Please follow these steps: 91 | 92 | 1. Fork the repository 93 | 2. Create a feature branch 94 | 3. Make your changes 95 | 4. Submit a pull request 96 | 97 | ## License 98 | 99 | Viber3D and its documentation are licensed under the MIT License. 100 | -------------------------------------------------------------------------------- /docs/viber3d-docs/app.config.ts: -------------------------------------------------------------------------------- 1 | export default defineAppConfig({ 2 | ui: { 3 | colors: { 4 | primary: 'red', 5 | neutral: 'zinc' 6 | } 7 | }, 8 | uiPro: { 9 | footer: { 10 | slots: { 11 | root: 'border-t border-(--ui-border)', 12 | left: 'text-sm text-(--ui-text-muted)' 13 | } 14 | } 15 | }, 16 | seo: { 17 | siteName: 'Viber3D Documentation' 18 | }, 19 | header: { 20 | title: 'Viber3D', 21 | to: '/', 22 | logo: { 23 | alt: 'Viber3D Logo', 24 | light: '', 25 | dark: '' 26 | }, 27 | search: true, 28 | colorMode: true, 29 | links: [ 30 | { 31 | 'icon': 'i-simple-icons-github', 32 | 'to': 'https://github.com/instructa/viber3d', 33 | 'target': '_blank', 34 | 'aria-label': 'GitHub' 35 | } 36 | ] 37 | }, 38 | footer: { 39 | credits: `Copyright © ${new Date().getFullYear()} Viber3D | Made by`, 40 | colorMode: false, 41 | links: [ 42 | { 43 | 'icon': 'i-simple-icons-github', 44 | 'to': 'https://github.com/instructa/viber3d', 45 | 'target': '_blank', 46 | 'aria-label': 'Viber3D on GitHub' 47 | } 48 | ] 49 | }, 50 | toc: { 51 | title: 'Table of Contents', 52 | bottom: { 53 | title: 'Community', 54 | edit: 'https://github.com/instructa/viber3d/edit/main/docs', 55 | links: [ 56 | { 57 | icon: 'i-lucide-star', 58 | label: 'Star on GitHub', 59 | to: 'https://github.com/instructa/viber3d', 60 | target: '_blank' 61 | } 62 | ] 63 | } 64 | } 65 | }) 66 | -------------------------------------------------------------------------------- /docs/viber3d-docs/app.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 52 | -------------------------------------------------------------------------------- /docs/viber3d-docs/assets/css/main.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss" theme(static); 2 | @import "@nuxt/ui-pro"; 3 | 4 | @source "../../../content/**/*"; 5 | 6 | @theme static { 7 | --font-sans: 'Public Sans', sans-serif; 8 | 9 | --color-green-50: #EFFDF5; 10 | --color-green-100: #D9FBE8; 11 | --color-green-200: #B3F5D1; 12 | --color-green-300: #75EDAE; 13 | --color-green-400: #00DC82; 14 | --color-green-500: #00C16A; 15 | --color-green-600: #00A155; 16 | --color-green-700: #007F45; 17 | --color-green-800: #016538; 18 | --color-green-900: #0A5331; 19 | --color-green-950: #052E16; 20 | } 21 | -------------------------------------------------------------------------------- /docs/viber3d-docs/components/AppFooter.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 34 | -------------------------------------------------------------------------------- /docs/viber3d-docs/components/AppHeader.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 81 | -------------------------------------------------------------------------------- /docs/viber3d-docs/components/OgImage/OgImageDocs.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 77 | -------------------------------------------------------------------------------- /docs/viber3d-docs/components/TemplateMenu.vue: -------------------------------------------------------------------------------- 1 | 40 | -------------------------------------------------------------------------------- /docs/viber3d-docs/content.config.ts: -------------------------------------------------------------------------------- 1 | import { defineContentConfig, defineCollection, z } from '@nuxt/content' 2 | 3 | export default defineContentConfig({ 4 | collections: { 5 | landing: defineCollection({ 6 | type: 'page', 7 | source: 'index.md' 8 | }), 9 | docs: defineCollection({ 10 | type: 'page', 11 | source: { 12 | include: '**', 13 | exclude: ['index.md'] 14 | }, 15 | schema: z.object({ 16 | links: z.array(z.object({ 17 | label: z.string(), 18 | icon: z.string(), 19 | to: z.string(), 20 | target: z.string().optional() 21 | })).optional() 22 | }) 23 | }) 24 | } 25 | }) 26 | -------------------------------------------------------------------------------- /docs/viber3d-docs/content/1.getting-started/.navigation.yml: -------------------------------------------------------------------------------- 1 | title: Getting Started 2 | icon: false 3 | -------------------------------------------------------------------------------- /docs/viber3d-docs/content/1.getting-started/1.index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | description: Welcome to Viber3D - A Modern 3D Game Starter Kit for the Web 4 | navigation.icon: i-lucide-house 5 | --- 6 | 7 | Welcome to **Viber3D**, a powerful and modern 3D game starter kit designed for creating immersive web-based games and interactive experiences. Built with performance and developer experience in mind, Viber3D provides all the tools you need to bring your game ideas to life. 8 | 9 | ## Key Features 10 | 11 | Viber3D comes packed with features to help you create amazing 3D games: 12 | 13 | ::card-group 14 | 15 | ::card 16 | --- 17 | title: Physics Engine 18 | icon: i-lucide-box 19 | --- 20 | Built-in physics engine for realistic object interactions and collisions 21 | :: 22 | 23 | ::card 24 | --- 25 | title: Game Systems 26 | icon: i-lucide-gamepad-2 27 | --- 28 | Comprehensive systems for input handling, audio, networking, and more 29 | :: 30 | 31 | ::card 32 | --- 33 | title: Component Architecture 34 | icon: i-lucide-component 35 | --- 36 | Flexible entity-component architecture for building complex game objects 37 | :: 38 | 39 | ::card 40 | --- 41 | title: Performance 42 | icon: i-lucide-zap 43 | --- 44 | Optimized for modern browsers with efficient rendering and resource management 45 | :: 46 | 47 | ::card 48 | --- 49 | title: TypeScript Support 50 | icon: i-lucide-code 51 | --- 52 | Full TypeScript support for type-safe development 53 | :: 54 | 55 | ::card 56 | --- 57 | title: Asset Management 58 | icon: i-lucide-image 59 | --- 60 | Powerful asset loading and management system for 3D models, textures, and audio 61 | :: 62 | 63 | :: 64 | 65 | ## Core Concepts 66 | 67 | Viber3D is built around several core concepts that make game development intuitive and efficient: 68 | 69 | - **Entity Component System (ECS)**: A flexible, data-driven architecture for building game objects 70 | - **Scene Management**: Easy-to-use scene system for organizing your game worlds 71 | - **Asset Pipeline**: Efficient asset loading and management 72 | - **Input System**: Comprehensive input handling for keyboard, mouse, and touch 73 | - **Physics System**: Built-in physics engine for realistic interactions 74 | - **Rendering Pipeline**: Modern WebGL-based rendering (via React Three Fiber) with optimization features 75 | - **Audio System**: Spatial audio support for immersive game experiences 76 | - **Networking**: Built-in networking capabilities for multiplayer games 77 | - **Development Tools**: Debug tools and performance monitoring 78 | 79 | Ready to start building your game? Head to the [Installation](/getting-started/installation) guide to begin your journey with Viber3D. 80 | 81 | ## Demo 82 | 83 | See Viber3D in action at our live demo site: [viber3d-spacewars.kevinkern.dev](https://viber3d-spacewars.kevinkern.dev/) 84 | 85 | ## Credits 86 | 87 | Viber3D was created by [Kevin Kern](https://x.com/kregenrek) and is a project by [Instructa.ai](https://www.instructa.ai/). 88 | 89 | This kit is heavily inspired by and uses a lot of concepts from [Koota](https://github.com/krispya/koota), an elegant Entity Component System. Special thanks and credits to [Krispya](https://github.com/krispya) and [Brian](https://github.com/Ctrlmonster) for their amazing work. 90 | 91 | We also extend our gratitude to the teams behind React Three Fiber and the various packages it includes, as well as to the Nuxt team and UnJS team for their awesome tooling ecosystem. 92 | 93 | For a complete list of acknowledgments, please visit our [Credits](/getting-started/credits) page. 94 | -------------------------------------------------------------------------------- /docs/viber3d-docs/content/1.getting-started/2.installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Installation 3 | description: Get started with Viber3D game starter kit. 4 | navigation.icon: i-lucide-download 5 | --- 6 | 7 | ## Requirements 8 | 9 | Before installing Viber3D, make sure you have: 10 | 11 | - Node.js 16.x or later 12 | - A modern web browser with WebGL support 13 | - npm, yarn, or pnpm package manager 14 | 15 | ## Installation 16 | 17 | Run the script to create a new project: 18 | 19 | ```bash [Terminal] 20 | npx viber3d@latest init 21 | ``` 22 | 23 | ::video{src="https://a.storyblok.com/f/316774/x/0c8b8d92f8/install_viber_3d_720.mp4?cv=1741718543080" poster="" controls} 24 | :: 25 | 26 | ## Cursor AI Integration 27 | 28 | To enhance your development experience with Cursor AI, add our LLMs documentation: 29 | 30 | 1. Visit [viber3d.instructa.ai/llms_full.txt](https://viber3d.instructa.ai/llms_full.txt) 31 | 2. Add this documentation URL to Cursor AI's documentation settings 32 | 3. Cursor AI will now provide intelligent assistance specific to Viber3D development when you add `@Docs` and then search for the docs like `viber3d`. 33 | 34 | For detailed instructions on adding custom documentation to Cursor AI, visit our [tutorial](https://www.instructa.ai/en/blog/how-to-add-custom-documentation-in-cursor-ai). 35 | 36 | 37 | ## Next Steps 38 | 39 | Now that you have Viber3D installed, you can: 40 | 41 | 1. Check out the [Usage Guide](/getting-started/usage) to learn the basics 42 | 2. Explore the [Core Concepts](/core-concepts/ecs-overview) to understand the engine's architecture 43 | 3. Browse the [Systems](/core-concepts/systems) documentation to learn about specific features 44 | 4. Join our [GitHub Discussions](https://github.com/instructa/viber3d/discussions) for community support 45 | 46 | -------------------------------------------------------------------------------- /docs/viber3d-docs/content/1.getting-started/3.project-structure.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Project Structure 3 | description: Understanding the Viber3D project structure 4 | navigation.icon: i-lucide-folder-tree 5 | --- 6 | 7 | # Project Structure 8 | 9 | Below is an example project structure for a **Viber3D** game. Your actual folder names may differ, but these are the typical directories and files you'll see. 10 | 11 | ```bash 12 | viber3d/ 13 | ├── src 14 | │ ├── assets/ # 3D models, textures, images 15 | │ ├── components/ # React components for rendering 3D objects/UI 16 | │ ├── systems/ # ECS Systems for game logic updates 17 | │ ├── traits/ # ECS Traits (components) describing entity data 18 | │ ├── utils/ # Utility functions (math, sorting, spatial hashing) 19 | │ ├── actions.ts # Central actions to spawn or modify entities 20 | │ ├── app.tsx # Main React component (root of your 3D scene) 21 | │ ├── frameloop.ts # Main ECS update loop 22 | │ ├── index.css # Global CSS or Tailwind CSS (if used) 23 | │ ├── main.tsx # React app root, renders 24 | │ ├── startup.tsx # Startup logic (initial spawns, intervals) 25 | │ └── world.ts # Creates the ECS world with default traits 26 | ├── index.html # Basic HTML page with root div 27 | ├── package.json # Project dependencies and scripts 28 | └── tsconfig.json # TypeScript configuration 29 | ``` 30 | 31 | ## Key Directories 32 | 33 | **`/src/assets`** 34 | Contains static assets such as textures, images, 3D models (`.glb` or `.gltf`), or audio files. 35 | 36 | **`/src/components`** 37 | React components that render your entities or UI elements. For example: 38 | - `player-renderer.tsx` 39 | - `enemy-renderer.tsx` 40 | - `score-tracker.tsx` 41 | - `postprocessing.tsx` 42 | 43 | **`/src/systems`** 44 | ECS Systems that update game logic every frame. Examples: 45 | - `move-entities.ts` 46 | - `camera-follow-player.ts` 47 | - `handle-shooting.ts` 48 | - `update-bullet-collisions.ts` 49 | 50 | **`/src/traits`** 51 | Traits (a.k.a. ECS components) that store data about your entities, such as: 52 | - `is-player.ts` 53 | - `health.ts` 54 | - `movement.ts` 55 | - `bullet.ts` 56 | - `spatial-hash-map.ts` 57 | 58 | **`/src/utils`** 59 | Helper functions or classes, e.g.: 60 | - `spatial-hash.ts` 61 | - `sort-entities-by-distance.ts` 62 | - `between.ts` 63 | 64 | ## Key Files 65 | 66 | **`actions.ts`** 67 | Central location for spawning or modifying entities (e.g. `spawnPlayer`, `spawnBullet`). 68 | 69 | **`app.tsx`** 70 | Your main game component, often where you set up the Three.js canvas (`` from React Three Fiber) and add your top-level components like ``, ``, etc. 71 | 72 | **`frameloop.ts`** 73 | The main ECS update loop that orchestrates all the systems every frame. 74 | 75 | **`startup.tsx`** 76 | Initial spawns, intervals, or timed events (like adding more enemies every few seconds). 77 | 78 | **`world.ts`** 79 | Creates the ECS world (from `koota` or your chosen ECS library), defines default traits (`Time`, `SpatialHashMap`, etc.). -------------------------------------------------------------------------------- /docs/viber3d-docs/content/1.getting-started/4.cli.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CLI 3 | description: Viber3D CLI Reference 4 | navigation.icon: i-heroicons-command-line 5 | --- 6 | 7 | # Viber3D CLI 8 | 9 | The Viber3D CLI provides tools to help you create and manage your Viber3D projects. 10 | 11 | ## Installation 12 | 13 | You can use the Viber3D CLI without installing it by using `npx`: 14 | 15 | ```bash 16 | npx viber3d@latest [command] 17 | ``` 18 | 19 | ## Commands 20 | 21 | ### init 22 | 23 | Initialize a fresh Viber3D project. 24 | 25 | ```bash 26 | npx viber3d@latest init [dir] [options] 27 | ``` 28 | 29 | #### Arguments 30 | 31 | - `dir` - Project directory (optional) 32 | 33 | #### Options 34 | 35 | | Option | Alias | Description | Default | 36 | |--------|-------|-------------|---------| 37 | | `--name` | | Project name | | 38 | | `--template` | `-t` | Template name or GitHub repository | `starter` | 39 | | `--force` | `-f` | Override existing directory | `false` | 40 | | `--install` | | Install dependencies | `true` | 41 | | `--gitInit` | | Initialize git repository | | 42 | | `--packageManager` | | Package manager choice (npm, pnpm, yarn) | | 43 | | `--yes` | `-y` | Skip confirmation prompt | `false` | 44 | | `--defaults` | `-d` | Use default configuration | `false` | 45 | | `--silent` | `-s` | Mute output | `false` | 46 | | `--help` | `-h` | Display help for command | | 47 | | `--cwd` | `-c` | The working directory | Current directory | 48 | 49 | #### Template Options 50 | 51 | The `--template` (or `-t`) option allows you to specify which template to use: 52 | 53 | - **Default template**: If not specified, the CLI will prompt you to choose between: 54 | - `starter` (recommended, stable) - The default option 55 | - `next` (experimental) - Contains upcoming features and changes 56 | - **Named templates**: Use `-t ` to use a template from the Viber3D templates directory 57 | - **Next template**: Use `-t next` to use the experimental template from the starter-next repository 58 | - **GitHub repositories**: Use `-t username/repo` to use any GitHub repository as a template 59 | 60 | #### Examples 61 | 62 | Create a new project with the default template: 63 | ```bash 64 | npx viber3d@latest init my-game 65 | ``` 66 | 67 | Create a new project with the "next" template (for upcoming features): 68 | ```bash 69 | npx viber3d@latest init my-game -t next 70 | ``` 71 | 72 | Create a new project with a custom GitHub template: 73 | ```bash 74 | npx viber3d@latest init my-game -t username/repo 75 | ``` 76 | 77 | Create a new project with default settings (no prompts): 78 | ```bash 79 | npx viber3d@latest init my-game -y 80 | ``` 81 | 82 | ## Project Creation Process 83 | 84 | When you run the `init` command, the CLI will: 85 | 86 | 1. Ask for a project name (if not provided) 87 | 2. Check if the directory exists and prompt for action if it does 88 | 3. Prompt for template selection (if not specified and not using --yes or --defaults) 89 | 4. Download the specified template 90 | 5. Prompt for project details (description, author) 91 | 6. Process template files 92 | 7. Update package.json with project details 93 | 8. Install dependencies (unless `--install=false`) 94 | 9. Initialize git repository (if confirmed) 95 | 10. Display next steps 96 | 97 | ## Configuration 98 | 99 | The CLI uses the following configuration files: 100 | 101 | - `.gitignore` - Git ignore file 102 | - `.cursorignore` - Cursor ignore file 103 | - `.prettierrc` - Prettier configuration 104 | - `.prettierignore` - Prettier ignore file 105 | - `.eslintrc.js` - ESLint configuration 106 | 107 | These files are automatically created when you initialize a new project. -------------------------------------------------------------------------------- /docs/viber3d-docs/content/1.getting-started/5.demo.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Demo 3 | description: Experience Viber3D in action with our live demo 4 | navigation.icon: i-lucide-rocket 5 | --- 6 | 7 | # Viber3D Demo 8 | 9 | Experience Viber3D in action with our interactive demo. This showcase demonstrates the capabilities and features of the Viber3D framework in a real-world application. 10 | 11 | ## Live Demo 12 | 13 | Visit our live demo at: [viber3d-spacewars.kevinkern.dev](https://viber3d-spacewars.kevinkern.dev/) 14 | 15 | ## Demo Features 16 | 17 | The demo showcases several key features of Viber3D: 18 | 19 | ::card-group 20 | 21 | ::card 22 | --- 23 | title: 3D Rendering 24 | icon: i-lucide-rocket 25 | --- 26 | See the high-performance 3D rendering capabilities with dynamic lighting and effects 27 | :: 28 | 29 | ::card 30 | --- 31 | title: Physics Engine 32 | icon: i-lucide-atom 33 | --- 34 | Experience realistic physics interactions between game objects 35 | :: 36 | 37 | ::card 38 | --- 39 | title: Input Controls 40 | icon: i-lucide-gamepad 41 | --- 42 | Test the responsive input system with keyboard and mouse controls 43 | :: 44 | 45 | ::card 46 | --- 47 | title: Game Systems 48 | icon: i-lucide-cog 49 | --- 50 | Observe the Entity Component System in action with various game mechanics 51 | :: 52 | 53 | :: 54 | 55 | ## How to Use the Demo 56 | 57 | 1. **Navigate** to [viber3d-spacewars.kevinkern.dev](https://viber3d-spacewars.kevinkern.dev/) 58 | 2. **Explore** the 3D environment and interact with objects 59 | 3. **Test** different features and capabilities of the engine 60 | 4. **Observe** how various systems work together to create a cohesive experience 61 | 62 | ## Learning from the Demo 63 | 64 | The demo serves as both a showcase and a learning tool. As you interact with it, consider: 65 | 66 | - How different components interact with each other 67 | - How the physics system handles collisions and movement 68 | - How the rendering pipeline creates visual effects 69 | - How user input is translated into in-game actions 70 | 71 | ## Source Code 72 | 73 | Interested in how the demo was built? The complete source code is available in the Viber3D repository. You can use it as a reference for building your own games and applications with Viber3D. 74 | 75 | ## Next Steps 76 | 77 | After exploring the demo, you might want to: 78 | 79 | - Review the [Project Structure](/getting-started/project-structure) to understand how Viber3D projects are organized 80 | - Dive into the [Core Concepts](/core-concepts/ecs-overview) to learn more about the underlying architecture 81 | - Start building your own game with the knowledge you've gained -------------------------------------------------------------------------------- /docs/viber3d-docs/content/1.getting-started/6.credits.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Credits 3 | description: Special thanks to the individuals and projects that made Viber3D possible 4 | navigation.icon: i-lucide-star 5 | --- 6 | 7 | # Credits & Acknowledgments 8 | 9 | Viber3D was created by [Kevin Kern](https://x.com/kregenrek) and is a project by [Instructa.ai](https://www.instructa.ai/). 10 | 11 | Viber3D stands on the shoulders of giants. This project wouldn't be possible without the contributions of many talented individuals and the amazing open-source libraries they've created. 12 | 13 | ## Special Thanks 14 | 15 | ### Koota ECS 16 | 17 | This kit is heavily inspired by and uses a lot of concepts from [Koota](https://github.com/krispya/koota), an elegant Entity Component System for JavaScript and TypeScript. Koota provides the foundation for our game architecture, enabling flexible and performant game development. 18 | 19 | Special thanks and credits to: 20 | 21 | - **[Krispya](https://github.com/krispya)** - Creator of Koota 22 | - **[Brian](https://github.com/Ctrlmonster)** - Contributor and supporter 23 | 24 | ### Three.js 25 | 26 | At the core of our 3D rendering capabilities is [Three.js](https://threejs.org/), a powerful JavaScript 3D library that makes WebGL accessible and intuitive. We're deeply grateful to: 27 | 28 | - **[Ricardo Cabello (Mr.doob)](https://github.com/mrdoob)** - Creator and maintainer of Three.js 29 | - **The Three.js core team** - For their ongoing development and maintenance 30 | - **The Three.js community** - For their extensive examples, documentation, and support 31 | 32 | Three.js has revolutionized 3D on the web and made projects like Viber3D possible. 33 | 34 | ### React Three Fiber 35 | 36 | Viber3D leverages the power of [React Three Fiber](https://github.com/pmndrs/react-three-fiber), a React renderer for Three.js. We're grateful to the entire Poimandres collective for their incredible work on: 37 | 38 | - React Three Fiber 39 | - Drei 40 | - Zustand 41 | - and other related libraries 42 | 43 | ### Nuxt & UnJS 44 | 45 | Our documentation system and tooling are built on the excellent work of: 46 | 47 | - **[The Nuxt Team](https://nuxt.com/)** - For their innovative approach to web development and the Nuxt Content module that powers our documentation 48 | - **[The UnJS Team](https://unjs.io/)** - For their ecosystem of high-quality JavaScript tools that enable modern development workflows 49 | 50 | Key UnJS libraries we use include: 51 | - **Nitro** - For server functionality 52 | - **Unimport** - For auto-imports 53 | - **Unbuild** - For building packages 54 | - **and many more** - The entire UnJS ecosystem has been invaluable 55 | 56 | ## Additional Libraries 57 | 58 | Viber3D also benefits from many other open-source projects, including: 59 | 60 | - **TypeScript** - For type safety and developer experience 61 | - **Vite** - For fast, modern development tooling 62 | - **TailwindCSS** - For utility-first styling 63 | 64 | ## Community 65 | 66 | Finally, we'd like to thank the growing community of Viber3D users and contributors who provide feedback, report issues, and help improve the project. 67 | 68 | --- 69 | 70 | *If you've contributed to Viber3D and aren't listed here, please let us know so we can properly acknowledge your work!* -------------------------------------------------------------------------------- /docs/viber3d-docs/content/2.introduction/1.what-is-viber3d.md: -------------------------------------------------------------------------------- 1 | ## Key Features 2 | 3 | Viber3D leverages these key libraries and concepts: 4 | 5 | 1. **React**: A popular JavaScript library for building user interfaces. 6 | 2. **React Three Fiber**: React renderer for Three.js, making 3D in React a breeze. 7 | 3. **koota**: A small, ECS-like library for game state management (entities, traits, systems). 8 | 4. **Tailwind CSS**: A utility-first CSS framework for UI styling. -------------------------------------------------------------------------------- /docs/viber3d-docs/content/2.introduction/2.why-use-viber3d.md: -------------------------------------------------------------------------------- 1 | 2 | - **Quick Setup**: Quickly set up a 3D environment without the hassle of configuration. 3 | - **ECS Architecture**: Start coding with a flexible ECS approach for your game logic. 4 | - **React Integration**: Enjoy the expressiveness of React and react-three-fiber with minimal boilerplate. 5 | - **Scope Flexibility**: Ideal for making small to medium-scale 3D shooter or exploration games. 6 | 7 | ## Perfect For 8 | 9 | - Game developers looking to build browser-based 3D games 10 | - React developers wanting to explore 3D game development 11 | - Prototyping game ideas quickly 12 | - Learning ECS architecture in a practical context -------------------------------------------------------------------------------- /docs/viber3d-docs/content/3.development/1.running-dev-server.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Development Server 3 | description: This guide explains how to run and work with the Viber3D development server. 4 | --- 5 | 6 | ## Starting the Development Server 7 | 8 | To start the development server with hot reload: 9 | 10 | ```bash 11 | npm run dev 12 | ``` 13 | 14 | This will: 15 | - Start a local development server (by default at [http://localhost:5173](http://localhost:5173)) 16 | - Enable hot module replacement (HMR) 17 | - Provide real-time error feedback 18 | 19 | ## Accessing Your Game 20 | 21 | Once the server is running: 22 | - Open your browser to [http://localhost:5173](http://localhost:5173) 23 | - You should see your 3D scene 24 | - Any code changes automatically refresh (HMR) 25 | 26 | ## Development Features 27 | 28 | - **Hot Module Replacement (HMR)** 29 | Changes to React components update in real-time. 30 | 31 | - **Error Overlay** 32 | Compilation and runtime errors are shown in the browser. 33 | 34 | - **Dev Tools** 35 | Source maps, React Developer Tools, performance profiling. 36 | 37 | ## Troubleshooting 38 | 39 | If you encounter issues: 40 | 1. Check terminal for error messages 41 | 2. Verify port availability 42 | 3. Ensure all dependencies are installed 43 | 4. Clear browser cache if needed -------------------------------------------------------------------------------- /docs/viber3d-docs/content/3.development/2.production-build.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Production Build 3 | description: Learn how to create optimized production builds for your Viber3D game. 4 | --- 5 | 6 | ## Creating a Production Build 7 | 8 | ```bash 9 | npm run build 10 | ``` 11 | 12 | This command will: 13 | - Compile your TypeScript code 14 | - Bundle all assets 15 | - Optimize for production 16 | - Output static files in `dist/` 17 | 18 | ## Testing the Production Build 19 | 20 | ```bash 21 | npm run preview 22 | ``` 23 | 24 | This serves your built files at [http://localhost:4173](http://localhost:4173) by default. 25 | 26 | ## Optimizations 27 | 28 | - **Code Minification** 29 | JavaScript & CSS minified, dead code elimination, tree-shaking. 30 | 31 | - **Asset Optimization** 32 | Image compression, font subsetting, static asset fingerprinting. 33 | 34 | - **Performance** 35 | Code splitting, lazy loading components, caching headers. 36 | 37 | ## Deployment 38 | 39 | After building: 40 | - The `dist/` folder contains everything needed 41 | - Deploy to any static hosting service (Netlify, Vercel, etc.) 42 | - No server runtime required -------------------------------------------------------------------------------- /docs/viber3d-docs/content/6.contributing/1.intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Contributing 3 | description: How to contribute to Viber3D 4 | navigation.icon: i-heroicons-code-bracket 5 | --- 6 | 7 | # Contributing to Viber3D 8 | 9 | Thank you for your interest in contributing to Viber3D! This guide will help you understand how to contribute to the project. 10 | 11 | ## Project Structure 12 | 13 | Viber3D is organized as a monorepo with multiple packages: 14 | 15 | - `viber3d` - The main package published to npm 16 | 17 | ## Development Setup 18 | 19 | 1. Clone the repository: 20 | ```bash 21 | git clone https://github.com/instructa/viber3d.git 22 | cd viber3d 23 | ``` 24 | 25 | 2. Install dependencies: 26 | ```bash 27 | pnpm install 28 | ``` 29 | 30 | 3. Build the packages: 31 | ```bash 32 | pnpm build:packages 33 | ``` 34 | 35 | ## Pull Requests 36 | 37 | When submitting a pull request: 38 | 39 | 1. Fork the repository and create a new branch from `main` 40 | 2. Make your changes and ensure all tests pass 41 | 3. Submit a pull request to the `main` branch 42 | 4. Ensure your PR has a clear description of the changes and why they're needed 43 | 44 | For larger features, consider opening an issue first to discuss the approach. 45 | 46 | ## Templates 47 | 48 | Viber3D provides different templates for project initialization. These templates are stored in the `templates` directory of the repository. 49 | 50 | - The default template is the `starter` template 51 | - The `starter-next` template (accessed via the `next` branch) contains upcoming features and changes 52 | 53 | When developing new templates or making changes to existing ones, you can test them locally by using the `--template` flag with the CLI: 54 | 55 | ```bash 56 | npx viber3d@latest init my-game --template next 57 | ``` 58 | 59 | This will use the experimental template from `templates/starter-next` on the `next` branch. 60 | 61 | ## Coding Standards 62 | 63 | - Follow the existing code style and conventions 64 | - Write tests for new features 65 | - Update documentation for any changes to the API or functionality 66 | 67 | ## Commit Guidelines 68 | 69 | We follow conventional commit messages to make the commit history more readable and to automate versioning: 70 | 71 | - `feat:` - A new feature 72 | - `fix:` - A bug fix 73 | - `docs:` - Documentation changes 74 | - `style:` - Changes that do not affect the meaning of the code 75 | - `refactor:` - Code changes that neither fix a bug nor add a feature 76 | - `perf:` - Performance improvements 77 | - `test:` - Adding or correcting tests 78 | - `chore:` - Changes to the build process or auxiliary tools 79 | 80 | ## Need Help? 81 | 82 | If you need help with contributing, please open an issue on GitHub or reach out to the maintainers. 83 | -------------------------------------------------------------------------------- /docs/viber3d-docs/content/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | seo: 3 | title: Viber3D Documentation 4 | description: Viber3D is a powerful 3D game starter kit built for modern web browsers, enabling developers to create immersive 3D experiences with ease. 5 | --- 6 | 7 | ::u-page-hero 8 | --- 9 | orientation: horizontal 10 | --- 11 | :::prose-pre{filename="Terminal" code="npm install @instructa/viber3d"} 12 | ```bash 13 | npx viber3d@latest init 14 | ``` 15 | ::: 16 | 17 | #title 18 | Build 3D Games with Viber3D 19 | 20 | #description 21 | Viber3D is a modern, performant 3D game starter kit that makes it easy to create immersive web-based games and interactive experiences. 22 | 23 | #links 24 | :::u-button 25 | --- 26 | size: xl 27 | to: /getting-started 28 | trailing-icon: i-lucide-arrow-right 29 | --- 30 | Get started 31 | ::: 32 | 33 | :::u-button 34 | --- 35 | color: neutral 36 | icon: i-simple-icons-github 37 | size: xl 38 | target: _blank 39 | to: https://github.com/instructa/viber3d 40 | variant: subtle 41 | --- 42 | View on GitHub 43 | ::: 44 | :: 45 | 46 | ::u-page-section 47 | #title 48 | Complete 3D Game Starter Kit 49 | 50 | #features 51 | :::u-page-feature 52 | --- 53 | icon: i-lucide-box 54 | --- 55 | #title 56 | 3D Physics 57 | 58 | #description 59 | Built-in physics engine for realistic object interactions and collisions. 60 | ::: 61 | 62 | :::u-page-feature 63 | --- 64 | icon: i-lucide-gamepad-2 65 | --- 66 | #title 67 | Game Systems 68 | 69 | #description 70 | Comprehensive game systems including input handling, audio, and networking. 71 | ::: 72 | 73 | :::u-page-feature 74 | --- 75 | icon: i-lucide-sparkles 76 | --- 77 | #title 78 | Modern Architecture 79 | 80 | #description 81 | Built with modern web technologies for optimal performance. 82 | ::: 83 | 84 | :::u-page-feature 85 | --- 86 | icon: i-lucide-code 87 | --- 88 | #title 89 | TypeScript 90 | 91 | #description 92 | Fully typed development experience for better code quality. 93 | ::: 94 | 95 | :::u-page-feature 96 | --- 97 | icon: i-lucide-component 98 | --- 99 | #title 100 | Component System 101 | 102 | #description 103 | Flexible component-based architecture for building complex game objects. 104 | ::: 105 | 106 | :::u-page-feature 107 | --- 108 | icon: i-lucide-search 109 | --- 110 | #title 111 | Documentation 112 | 113 | #description 114 | Comprehensive docs with examples, tutorials, and references. 115 | ::: 116 | :: 117 | 118 | ::u-page-section 119 | :::u-page-c-t-a 120 | --- 121 | links: 122 | - label: Get Started 123 | to: /getting-started 124 | icon: i-lucide-play 125 | color: primary 126 | - label: GitHub 127 | to: https://github.com/instructa/viber3d 128 | trailingIcon: i-simple-icons-github 129 | target: _blank 130 | color: neutral 131 | variant: subtle 132 | description: Start building your 3D game today with Viber3D's powerful features and intuitive API. 133 | title: Create Amazing 3D Games! 134 | variant: subtle 135 | --- 136 | ::: 137 | :: 138 | -------------------------------------------------------------------------------- /docs/viber3d-docs/error.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 43 | -------------------------------------------------------------------------------- /docs/viber3d-docs/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import withNuxt from './.nuxt/eslint.config.mjs' 3 | 4 | export default withNuxt( 5 | // Your custom configs here 6 | ) 7 | -------------------------------------------------------------------------------- /docs/viber3d-docs/layouts/docs.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 23 | -------------------------------------------------------------------------------- /docs/viber3d-docs/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // Configuration for Viber3D documentation 2 | export default defineNuxtConfig({ 3 | modules: [ 4 | '@nuxt/eslint', 5 | '@nuxt/image', 6 | '@nuxt/ui-pro', 7 | '@nuxt/content', 8 | 'nuxt-og-image' 9 | ], 10 | 11 | devtools: { 12 | enabled: true 13 | }, 14 | 15 | css: ['~/assets/css/main.css'], 16 | 17 | content: { 18 | build: { 19 | markdown: { 20 | toc: { 21 | searchDepth: 1 22 | } 23 | } 24 | } 25 | }, 26 | 27 | future: { 28 | compatibilityVersion: 4 29 | }, 30 | 31 | compatibilityDate: '2025-03-11', 32 | 33 | nitro: { 34 | prerender: { 35 | routes: ['/'], 36 | crawlLinks: true 37 | } 38 | }, 39 | 40 | eslint: { 41 | config: { 42 | stylistic: { 43 | commaDangle: 'never', 44 | braceStyle: '1tbs' 45 | } 46 | } 47 | }, 48 | 49 | icon: { 50 | provider: 'iconify' 51 | } 52 | 53 | // llms: { 54 | // domain: 'https://viber3d.instructa.ai/', 55 | // title: 'Viber3D Documentation', 56 | // description: 57 | // 'Documentation for Viber3D - A modern 3D game starter kit for the web', 58 | // full: { 59 | // title: 'Viber3D Full Documentation', 60 | // description: 61 | // 'This is the full documentation for the Viber3D game engine' 62 | // }, 63 | // sections: [ 64 | // { 65 | // title: 'Getting Started', 66 | // contentCollection: 'docs', 67 | // contentFilters: [ 68 | // { field: 'path', operator: 'LIKE', value: '/getting-started%' } 69 | // ] 70 | // }, 71 | // { 72 | // title: 'Core Concepts', 73 | // contentCollection: 'docs', 74 | // contentFilters: [ 75 | // { field: 'path', operator: 'LIKE', value: '/core-concepts%' } 76 | // ] 77 | // }, 78 | // { 79 | // title: 'Development', 80 | // contentCollection: 'docs', 81 | // contentFilters: [ 82 | // { field: 'path', operator: 'LIKE', value: '/development%' } 83 | // ] 84 | // } 85 | // ] 86 | // } 87 | }) 88 | -------------------------------------------------------------------------------- /docs/viber3d-docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "viber3d-docs", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "build": "nuxt build", 7 | "dev": "nuxt dev", 8 | "generate": "nuxt generate", 9 | "preview": "nuxt preview", 10 | "postinstall": "nuxt prepare" 11 | }, 12 | "dependencies": { 13 | "@iconify-json/lucide": "^1.2.28", 14 | "@iconify-json/simple-icons": "^1.2.27", 15 | "@iconify-json/vscode-icons": "^1.2.16", 16 | "@nuxt/content": "^3.3.0", 17 | "@nuxt/image": "^1.9.0", 18 | "@nuxt/ui-pro": "3.0.0-beta.3", 19 | "nuxt": "^3.16.0", 20 | "nuxt-llms": "0.1.0", 21 | "nuxt-og-image": "^5.0.1" 22 | }, 23 | "devDependencies": { 24 | "@nuxt/eslint": "^1.1.0", 25 | "eslint": "^9.22.0", 26 | "tailwindcss": "^4.0.12", 27 | "typescript": "^5.8.2", 28 | "unist-util-visit": "^5.0.0", 29 | "vue-tsc": "^2.2.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docs/viber3d-docs/pages/[...slug].vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 105 | -------------------------------------------------------------------------------- /docs/viber3d-docs/pages/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 25 | -------------------------------------------------------------------------------- /docs/viber3d-docs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructa/viber3d/ad11dfd3ca621f1d3adb4412aa32ea8a12c053db/docs/viber3d-docs/public/favicon.ico -------------------------------------------------------------------------------- /docs/viber3d-docs/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructa/viber3d/ad11dfd3ca621f1d3adb4412aa32ea8a12c053db/docs/viber3d-docs/public/favicon.png -------------------------------------------------------------------------------- /docs/viber3d-docs/public/fighter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructa/viber3d/ad11dfd3ca621f1d3adb4412aa32ea8a12c053db/docs/viber3d-docs/public/fighter.png -------------------------------------------------------------------------------- /docs/viber3d-docs/public/robots.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/viber3d-docs/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /docs/viber3d-docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@viber3d/source", 3 | "private": true, 4 | "workspaces": [ 5 | "templates/*", 6 | "packages/*", 7 | "docs/*" 8 | ], 9 | "version": "0.0.6", 10 | "homepage": "https://codetie.ai", 11 | "engines": { 12 | "node": "^18.0.0 || >=20.0.0" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/instructa/viber3d" 17 | }, 18 | "license": "MIT", 19 | "scripts": { 20 | "build": "pnpm -r build", 21 | "build:packages": "pnpm -r --filter './packages/**' build", 22 | "build:templates": "pnpm -r --filter './templates/**' build", 23 | "build:docs": "pnpm -r --filter './docs/**' build", 24 | "dev:docs": "pnpm -r --filter './docs/**' dev", 25 | "clean": "npx clean-modules node_modules **/node_modules", 26 | "release": "tsx scripts/release.ts", 27 | "release:alpha": "tsx scripts/release.ts --alpha", 28 | "release:patch": "tsx scripts/release.ts patch", 29 | "release:minor": "tsx scripts/release.ts minor", 30 | "release:major": "tsx scripts/release.ts major", 31 | "release:patch:alpha": "tsx scripts/release.ts patch --alpha", 32 | "release:minor:alpha": "tsx scripts/release.ts minor --alpha", 33 | "release:major:alpha": "tsx scripts/release.ts major --alpha", 34 | "release:branch": "git checkout -b release/v$npm_package_version && git push origin release/v$npm_package_version && git tag v$npm_package_version && git push origin v$npm_package_version", 35 | "llms": "npx repomix docs/viber3d-docs/content --include '4.core-concepts/*.md,5.addons/*.md' --output docs/viber3d-docs/public/llms_full.txt" 36 | }, 37 | "keywords": [ 38 | "viber3d", 39 | "cli", 40 | "three", 41 | "threejs", 42 | "project-setup", 43 | "cursor", 44 | "cursor-ai", 45 | "react-three-fiber", 46 | "ai-coding", 47 | "instructa" 48 | ], 49 | "devDependencies": { 50 | "@types/node": "^22.13.10", 51 | "tsx": "^4.7.0", 52 | "typescript": "^5.8.2" 53 | }, 54 | "packageManager": "pnpm@10.6.2", 55 | "pnpm": { 56 | "onlyBuiltDependencies": [ 57 | "better-sqlite3" 58 | ], 59 | "overrides": {} 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/core/index.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'viber3d', 3 | version: '0.0.0', 4 | description: 5 | 'Viber3D is a framework for building 3D games with React Three Fiber', 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@viber3d/core", 3 | "version": "0.0.0", 4 | "description": "Core package for Viber3D", 5 | "type": "module", 6 | "main": "dist/index.mjs", 7 | "files": [ 8 | "dist", 9 | "template", 10 | "index.js" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/instructa/viber3d" 15 | }, 16 | "author": "Kevin Kern", 17 | "license": "MIT", 18 | "engines": { 19 | "node": ">=18.0.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/viber3d/.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /packages/viber3d/README.md: -------------------------------------------------------------------------------- 1 | # viber3d 2 | 3 |

4 | License 5 |

6 | 7 | CLI tool to create a new **viber3D** project - a modern starter kit for 3D browser games. 8 | 9 | ## Usage 10 | 11 | ```bash 12 | # Create a new viber3d project 13 | npx viber3d@latest init 14 | 15 | # Or specify a project name 16 | npx viber3d@latest init myGame 17 | ``` 18 | 19 | ## Features 20 | 21 | - 🔥 **React 19** with concurrent rendering 22 | - 🎮 **React Three Fiber** for declarative Three.js 23 | - 🏎️ **Vite** for fast development 24 | - 🎨 **TailwindCSS** for styling 25 | - 🧠 **Zustand** for state management 26 | - 🔋 **Physics** with React Three Rapier 27 | - 📏 **TypeScript** for type safety 28 | - 🤖 **AI Integration** with predefined rules 29 | 30 | ## CLI Options 31 | 32 | ```bash 33 | npx viber3d@latest init [project-name] [options] 34 | 35 | Options: 36 | --cwd, -c Working directory (default: current) 37 | --name Project name 38 | --force, -f Override existing directory 39 | --install Skip installing dependencies (default: true) 40 | --gitInit Initialize git repository 41 | --packageManager Package manager (npm, pnpm, yarn) 42 | --yes, -y Skip confirmation prompt 43 | --defaults, -d Use default configuration 44 | --silent, -s Mute output 45 | --help, -h Display help 46 | ``` 47 | 48 | ## Links 49 | 50 | - GitHub: [viber3d](https://github.com/instructa/viber3d) 51 | - Author: [Kevin Kern](https://x.com/kregenrek) 52 | -------------------------------------------------------------------------------- /packages/viber3d/build.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'unbuild' 2 | 3 | export default defineBuildConfig({ 4 | entries: ['src/index'], 5 | clean: true, 6 | rollup: { 7 | emitCJS: false, 8 | inlineDependencies: true, 9 | }, 10 | alias: { 11 | // Add any aliases if needed 12 | }, 13 | hooks: { 14 | 'build:done': () => { 15 | // Any post-build tasks 16 | }, 17 | }, 18 | }) 19 | -------------------------------------------------------------------------------- /packages/viber3d/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import "./dist/index.mjs"; -------------------------------------------------------------------------------- /packages/viber3d/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "viber3d", 3 | "version": "0.0.6", 4 | "description": "Create a new Viber3D project for building 3D games with React Three Fiber", 5 | "type": "module", 6 | "main": "dist/index.mjs", 7 | "files": [ 8 | "dist", 9 | "template", 10 | "index.js" 11 | ], 12 | "bin": { 13 | "viber3d": "index.js" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/instructa/viber3d" 18 | }, 19 | "keywords": [ 20 | "viber3d", 21 | "cli", 22 | "three", 23 | "threejs", 24 | "project-setup", 25 | "cursor", 26 | "cursor-ai", 27 | "react-three-fiber", 28 | "ai-coding", 29 | "instructa" 30 | ], 31 | "author": "Kevin Kern", 32 | "license": "MIT", 33 | "scripts": { 34 | "build": "unbuild", 35 | "dev": "unbuild --stub", 36 | "prepublishOnly": "npm run build", 37 | "release": "npm publish", 38 | "release:alpha": "npm version prerelease --preid=alpha && npm publish --tag alpha" 39 | }, 40 | "publishConfig": { 41 | "access": "public" 42 | }, 43 | "dependencies": { 44 | "citty": "^0.1.5", 45 | "consola": "^3.2.3", 46 | "fs-extra": "^11.2.0", 47 | "giget": "^2.0.0", 48 | "pathe": "^1.1.1", 49 | "scule": "^1.1.1" 50 | }, 51 | "devDependencies": { 52 | "@types/fs-extra": "^11.0.4", 53 | "@types/node": "^20.11.19", 54 | "typescript": "^5.3.3", 55 | "unbuild": "^2.0.0" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /packages/viber3d/template/README.md.mustache: -------------------------------------------------------------------------------- 1 | # {{PROJECT_NAME}} 2 | 3 | ![viber3d banner](/public/images/banner.png) 4 | 5 |

6 | React Version 7 | Three.js Version 8 | React Three Fiber Version 9 | Vite Version 10 | License 11 |

12 | 13 | **{{PROJECT_NAME}}** is a 3D web application built with viber3d, combining the power of React Three Fiber with the latest React 19 features. Created by {{AUTHOR_NAME}}. 14 | 15 | ## Description 16 | 17 | {{DESCRIPTION}} 18 | 19 | 20 | ## Documentation 21 | 22 | Docs: [viber3d.instructa.ai](https://viber3d.instructa.ai/) 23 | 24 | ## Getting Started 25 | 26 | ### Prerequisites 27 | 28 | - Node.js 18+ 29 | - npm or yarn 30 | 31 | ### Installation 32 | 33 | ```bash 34 | # Install dependencies 35 | npm install 36 | # or 37 | yarn install 38 | 39 | # Start development server 40 | npm run dev 41 | # or 42 | yarn dev 43 | ``` 44 | 45 | Visit `http://localhost:5173` to see your app in action. 46 | 47 | ## Resources 48 | 49 | - [React Three Fiber Documentation](https://docs.pmnd.rs/react-three-fiber) 50 | - [Drei Documentation](https://github.com/pmndrs/drei) 51 | - [Three.js Documentation](https://threejs.org/docs/) 52 | - [React Documentation](https://react.dev/) 53 | - [Vite Documentation](https://vitejs.dev/guide/) 54 | 55 | ## License 56 | 57 | This project is licensed under the MIT License - see the LICENSE file for details. 58 | 59 | --- 60 | 61 | Created with [viber3d](https://github.com/regenrek/viber3d) - {{CURRENT_YEAR}} -------------------------------------------------------------------------------- /packages/viber3d/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "esModuleInterop": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "outDir": "dist", 10 | "declaration": true, 11 | "resolveJsonModule": true 12 | }, 13 | "include": ["src/**/*"], 14 | "exclude": ["node_modules", "dist"] 15 | } 16 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'templates/*' 3 | - 'packages/*' 4 | - 'docs/*' 5 | ignoredBuiltDependencies: 6 | - better-sqlite3 7 | - esbuild 8 | onlyBuiltDependencies: 9 | - better-sqlite3 10 | catalog: 11 | '@biomejs/biome': ^1.9.4 12 | typescript: ^5.7.3 -------------------------------------------------------------------------------- /public/images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructa/viber3d/ad11dfd3ca621f1d3adb4412aa32ea8a12c053db/public/images/banner.png -------------------------------------------------------------------------------- /templates/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "viber3d-templates", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "Templates for Viber3D projects", 6 | "type": "module", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/instructa/viber3d" 10 | }, 11 | "keywords": [ 12 | "viber3d", 13 | "templates", 14 | "three", 15 | "threejs", 16 | "instructa" 17 | ], 18 | "author": "Kevin Kern", 19 | "license": "MIT" 20 | } 21 | -------------------------------------------------------------------------------- /templates/starter-next/.cursor/rules/002-components.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: React Three Fiber Component Rules 3 | globs: src/components/**/*.tsx 4 | alwaysApply: false 5 | --- 6 | 7 | # R3F Component Guidelines 8 | 9 | ## Core Principles 10 | 11 | - Use functional components for R3F with ECS data read via hooks. 12 | - Keep components strictly presentational. All game logic should reside in systems. 13 | - Use `useTraitEffect(entity, Trait, callback)` to reflect ECS changes on mesh references. 14 | 15 | ## Example Structure 16 | 17 | ```tsx 18 | function EnemyView({ entity }: { entity: Entity }) { 19 | const ref = useRef(null) 20 | 21 | // React to trait changes only for visual updates 22 | useTraitEffect(entity, Transform, (t) => { 23 | if (ref.current && t) { 24 | ref.current.position.copy(t.position) 25 | ref.current.rotation.copy(t.rotation) 26 | } 27 | }) 28 | 29 | return ( 30 | 31 | 32 | 33 | 34 | ) 35 | } 36 | ``` 37 | 38 | ## Asset Loading and Cleanup 39 | 40 | - Use React Three Fiber helpers like `useGLTF` or `useTexture` for loading models or textures. 41 | - Dispose geometry and materials in a cleanup function if creating many dynamic objects over time. 42 | 43 | ## Anti-Patterns 44 | 45 | - Do not place ECS or game logic in React state. 46 | - Do not mutate ECS traits directly from a component. Use systems or actions. 47 | - Do not rely on `useFrame` for core game logic; reserve it for minor visual effects only. 48 | 49 | -------------------------------------------------------------------------------- /templates/starter-next/.cursor/rules/003-systems.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: ECS System Rules 3 | globs: src/systems/*.ts 4 | alwaysApply: false 5 | --- 6 | 7 | ## System Requirements 8 | 9 | - Each system is a pure function that processes traits and updates ECS data. 10 | - Use `world.query(...)` to get relevant entities and keep queries minimal. 11 | - Include delta time when doing any time-based logic. 12 | - Avoid side effects beyond ECS data changes (no DOM or React state modifications). 13 | 14 | ## System Examples 15 | 16 | ```ts 17 | // Standard system pattern 18 | export function movementSystem(world: World) { 19 | const time = world.get(Time) 20 | if (!time) return 21 | world.query(Transform, Velocity).updateEach(([t, v]) => { 22 | t.position.addScaledVector(v.vector, time.delta) 23 | }) 24 | } 25 | ``` 26 | 27 | ## Advanced Usage 28 | 29 | - Partial Queries: Use `.select(...)` to fetch only the needed traits for performance. 30 | - Conditional Scheduling: Run certain systems only in specific game states (e.g. paused, menu, or combat). 31 | - Relationships: Use `relation()` if the system involves parent-child or entity-to-entity logic, rather than raw references. 32 | 33 | ## Performance Tips 34 | 35 | - Reuse math objects like `Vector3` or `Matrix4` rather than creating new ones every frame. 36 | - Remove or recycle entities that are no longer needed (e.g. off-screen, zero-health). 37 | - Prefer SoA for large numeric arrays to improve cache performance. 38 | - Consider running heavy systems at reduced frequencies instead of every frame. 39 | 40 | ## Do Not 41 | 42 | - Do not directly manipulate React components or the DOM from a system. 43 | - Do not skip delta time in time-based updates. 44 | - Do not implement camera or purely visual updates in systems; leave those to React or specialized visual hooks. -------------------------------------------------------------------------------- /templates/starter-next/.cursor/rules/004-actions.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Centralized ECS Actions 3 | globs: src/actions/*.ts 4 | alwaysApply: false 5 | --- 6 | 7 | # Actions Guidelines 8 | 9 | ## Purpose 10 | 11 | - Actions are centralized functions that modify the ECS world in predictable ways. 12 | - They keep spawn/remove/update logic out of components and systems, providing a single place for entity manipulation. 13 | 14 | ## Core Principles 15 | 16 | 1. Single Entry Points 17 | Write all creation, destruction, or major trait updates in these functions. 18 | 2. Pure ECS Focus 19 | Do not add external side effects or UI manipulation. 20 | 3. Descriptive Names 21 | Use clear names like `spawnEnemy()`, `applyDamage()`, or `destroyEntity()`. 22 | 23 | ## Recommended Usage 24 | 25 | ```ts 26 | import { createActions } from 'koota' 27 | import { IsEnemy, Transform, Health } from '../traits' 28 | 29 | export const actions = createActions((world) => ({ 30 | spawnEnemy: (position) => { 31 | return world.spawn( 32 | IsEnemy(), 33 | Transform({ position }), 34 | Health({ amount: 50 }) 35 | ) 36 | }, 37 | damageEntity: (entity, amount) => { 38 | if (entity.has(Health)) { 39 | entity.set(Health, (h) => { 40 | const newAmount = Math.max(0, h.amount - amount) 41 | if (newAmount <= 0) { 42 | actions.destroyEntity(entity) 43 | } 44 | return { ...h, amount: newAmount } 45 | }) 46 | } 47 | }, 48 | destroyEntity: (entity) => { 49 | entity.destroy() 50 | } 51 | })) 52 | ``` 53 | 54 | ## Integration with React 55 | 56 | - Use `useActions(actionsFile)` from `koota/react` to bind actions to the current `world`. 57 | - Keep React components free of direct ECS manipulation. Call these action functions instead. 58 | 59 | ```tsx 60 | function GameInit() { 61 | const { spawnEnemy } = useActions(actions) 62 | 63 | useEffect(() => { 64 | spawnEnemy({ x: 0, y: 5, z: 0 }) 65 | }, []) 66 | 67 | return null 68 | } 69 | ``` 70 | 71 | ## Testing Actions 72 | 73 | - Create a fresh `world` in each test and bind actions with `actions.bindTo(world)`. 74 | - Verify entity changes after calling each action. 75 | 76 | ```ts 77 | describe('actions', () => { 78 | let world 79 | let boundActions 80 | 81 | beforeEach(() => { 82 | world = createWorld() 83 | boundActions = actions.bindTo(world) 84 | }) 85 | 86 | it('spawnEnemy creates an enemy entity', () => { 87 | const ent = boundActions.spawnEnemy({ x: 0, y: 1, z: 0 }) 88 | expect(ent.has(IsEnemy)).toBe(true) 89 | }) 90 | }) 91 | ``` 92 | 93 | ## Avoid 94 | 95 | - Do not add direct DOM or React manipulations in actions. 96 | - Do not handle input events or animations here; keep it purely for ECS state changes. 97 | - Avoid side effects that do not relate to ECS data. Keep them simple and testable. -------------------------------------------------------------------------------- /templates/starter-next/.cursor/rules/005-traits.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Trait Module Rules 3 | globs: src/traits/**/*.ts 4 | alwaysApply: false 5 | --- 6 | 7 | # Trait Definitions 8 | 9 | ## Core Practices 10 | 11 | - Keep trait data minimal. Place logic in systems. 12 | - Use schema-based (SoA) traits for numeric or frequently-updated data. 13 | - Use callback-based (AoS) traits for complex or class objects (e.g. THREE.Mesh). 14 | - Use tag traits (with no fields) as flags to identify entities (like `IsPlayer`). 15 | 16 | ## Examples 17 | 18 | ```ts 19 | import { trait } from 'koota' 20 | import * as THREE from 'three' 21 | 22 | // Schema-based (SoA) 23 | export const Health = trait({ amount: 100, max: 100 }) 24 | 25 | // Callback-based (AoS) 26 | export const Transform = trait(() => ({ 27 | position: new THREE.Vector3(), 28 | rotation: new THREE.Euler() 29 | })) 30 | 31 | // Tag trait 32 | export const IsEnemy = trait() 33 | ``` 34 | 35 | ## Relationships 36 | 37 | - Use `relation()` when entities need references to each other. 38 | - Pass additional fields if you need extra data in the relationship. 39 | - Use `exclusive: true` or `autoRemoveTarget: true` if needed for parenting or single-target constraints. 40 | 41 | ```ts 42 | import { relation } from 'koota' 43 | 44 | // Simple relation with no fields 45 | export const ChildOf = relation() 46 | 47 | // Relationship with data 48 | export const Contains = relation({ store: { amount: 0 } }) 49 | ``` 50 | 51 | ## Best Practices 52 | 53 | - Store only essential data in traits. Do not add large logic blocks. 54 | - Check for existence before adding a trait: `if (!entity.has(Health)) entity.add(Health())`. 55 | - Prefer `entity.set(Trait, (prev) => ({ ...prev, ...changes }))` for updates so Koota can handle events. 56 | 57 | ## Anti-Patterns 58 | 59 | - Do not share the same mutable object across multiple entities (like a single shared Vector3). 60 | - Do not store React refs, DOM elements, or large external objects in traits. 61 | - Do not combine unrelated data in one trait. Keep them cohesive and small. -------------------------------------------------------------------------------- /templates/starter-next/.cursor/rules/006-utils.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Utility Function Patterns 3 | globs: src/utils/**/*.ts 4 | alwaysApply: false 5 | --- 6 | 7 | ## Koota Integration 8 | 9 | - Access entity traits with `entity.get(Trait)`. 10 | - Handle missing traits gracefully, using null checks when necessary. 11 | 12 | ## Three.js Usage 13 | 14 | - Reuse math objects like `Vector3` to reduce garbage collection. 15 | - Leverage built-in Three.js methods for transformations instead of reinventing them. 16 | 17 | ## Performance 18 | 19 | - Reuse arrays or buffers for spatial and geometry operations. 20 | - Implement spatial partitioning (quadtrees, hash grids) for large worlds. 21 | - Provide reset methods to avoid creating new data structures every frame. 22 | 23 | ## Math Utilities 24 | 25 | - Consolidate common math operations in concise functions. 26 | - Ensure edge cases are handled (e.g. clamping, bounding conditions). 27 | 28 | ## Documentation 29 | 30 | - Add clear comments and note any adapted third-party code. 31 | - Highlight performance considerations for complex or frequently used utilities. -------------------------------------------------------------------------------- /templates/starter-next/.cursor/rules/007-tailwind.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: "Tailwind CSS usage rules for styling (2025 best practices)" 3 | globs: src/components/**/*.tsx,*.css 4 | alwaysApply: false 5 | --- 6 | - Use Tailwind utility classes for consistent styling, with custom CSS only for special cases 7 | - Organize classes logically (layout, spacing, color, typography) 8 | - Use responsive and state variants (e.g., sm:, md:, lg:, hover:, focus:, dark:) in markup 9 | - Embrace Tailwind v4 features like container queries and CSS variables 10 | - Rely on Tailwind classes rather than inline styles or external CSS files for a unified design language -------------------------------------------------------------------------------- /templates/starter-next/.cursor/rules/modes/game-agent.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: false 5 | --- 6 | 7 | You are Levin, a specialized software development AI agent. Your primary goal is to fulfill all user stories. 8 | 9 | You always greet with a random "leetspeak" welcome message (only once)! 10 | 11 | CRITICAL RULES: 12 | - DON'T skip a step and wait until one step is 100% finished! 13 | - Server is already started. Never run `npm run dev` on your own. The development server is already started! 14 | 15 | Follow these guidelines: 16 | 17 | 1. first read [roadmap.json](mdc:.planr/roadmap.json) and learn about latest changes and use it as your memory. Allowed status for stories are todo or done. 18 | 19 | 2. If you haven't done so read all related cursor rules for the task. 20 | 21 | 3. Proceed with implementing the current story `.planr/stories/{{STORY-ID}}`, ensuring you address all acceptance criteria. 22 | 23 | 4. write all infos about updates, fixes, to the "Developer Notes" section in `.planr/stories/{{STORY-ID}}`. Do the same in [roadmap.json](mdc:.planr/roadmap.json) in `notes` but keep it for the 10 most important max. 24 | 25 | 5. ask if you should proceed wit the next story -------------------------------------------------------------------------------- /templates/starter-next/.cursor/rules/modes/plan.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: PLANNING MODE is used to create stories, specification and update project plans 3 | globs: 4 | alwaysApply: false 5 | --- 6 | 7 | You are Olena, a specialized product planner, requirements engineer AI agent. 8 | 9 | Rules: 10 | - Writing Style: No fluff, only the essential information—short as possible without losing details. 11 | 12 | You have two modes: 13 | 14 | 1. "init" - this mode initializes the project with all stories and the roadmap 15 | 2. "add" - this mode adds more stories to your project 16 | 17 | Your working directory and file structure: 18 | 19 | ``` 20 | [root] 21 | [.planr/] 22 | [stories/] # list of stories to work on 23 | {STORY-ID}.md 24 | roadmap.json # follow this step by step 25 | assetlist.json # models, sounds, textures 26 | prd.md # product requirements document 27 | ``` 28 | 29 | 30 | 31 | Your task is to create all stories bsed on the [prd.md](mdc:.planr/prd.md) file. 32 | 33 | STEPS: 34 | 1. Create a story using [story.tpl.mdc](mdc:.cursor/rules/templates/story.tpl.mdc) from [prd.md](mdc:.planr/prd.md) in the format `.planr/stories/{STORY-ID}.md` 35 | 2. Update [roadmap.json](mdc:.planr/roadmap.json) list using [task.tpl.mdc](mdc:.cursor/rules/templates/task.tpl.mdc) based on the story 36 | 3. Proceed with next story 37 | 38 | 39 | 40 | Your task is to create a new story based on the given input 41 | 42 | STEPS: 43 | 1. Create a new story using [story.tpl.mdc](mdc:.cursor/rules/templates/story.tpl.mdc) based on the given input in the format `.planr/stories/{STORY-ID}.md` 44 | 3. Update [roadmap.json](mdc:.planr/roadmap.json) list using [task.tpl.mdc](mdc:.cursor/rules/templates/task.tpl.mdc) based on the story 45 | 46 | -------------------------------------------------------------------------------- /templates/starter-next/.cursor/rules/templates/story.tpl.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: .planr/stories/**/*.md 4 | alwaysApply: false 5 | --- 6 | 7 | Use this template to create a new story for tracking in the `docs/stories` directory. 8 | 9 | # User Story: {{ID}} - {{TITLE}} 10 | 11 | ## Status: {{STATUS}} 12 | *(Valid values: TODO, IN PROGRESS, DONE)* 13 | 14 | ## Description: 15 | 16 | As a {{USER_TYPE}}, I want {{FEATURE}} so that {{REASON}}. 17 | 18 | ## Acceptance Criteria: 19 | 20 | - [ ] {{CRITERION_1}} 21 | - [ ] {{CRITERION_2}} 22 | - [ ] ... 23 | 24 | ## General Tasks: 25 | 26 | - [ ] All requirements from the plan are implemented 27 | - [ ] Code follows project style guidelines 28 | - [ ] Tests are written and passing 29 | - [ ] Documentation is updated 30 | - [ ] Performance considerations addressed 31 | - [ ] Security considerations addressed 32 | - [ ] Code has been reviewed 33 | 34 | ## Sub Tasks: 35 | 36 | - [ ] {{SUB_TASK_1}} - Status: {{SUB_TASK_1_STATUS}} 37 | 38 | ## Estimation: {{ESTIMATION}} story points 39 | *(Note: One step is 1 story point, which equals 1 day of work for a senior developer)* 40 | 41 | ## Developer Notes: 42 | *(Note: Add here important learnings, necessary fixes, all other devs need to know to proceed)* 43 | 44 | - {{NOTE_1}} 45 | - {{NOTE_2}} 46 | - ... -------------------------------------------------------------------------------- /templates/starter-next/.cursor/rules/templates/task.tpl.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: use this ALWAYS to create/update a task in json 3 | globs: .planr/roadmap.json 4 | alwaysApply: false 5 | --- 6 | 7 | Use the following JSON to add a task 8 | 9 | ```json 10 | { 11 | "id": "{{STORY-ID}}", 12 | "title": "Short descriptive title", 13 | "file": "docs/stories/{{STORY-ID}}.md", 14 | "status": "todo|done", 15 | "created": "YYYY-MM-DD", 16 | "updated": "YYYY-MM-DD", 17 | "notes": "add max 10 notes for other devs for documentation" 18 | } 19 | ``` -------------------------------------------------------------------------------- /templates/starter-next/.cursorindexingignore: -------------------------------------------------------------------------------- 1 | public/** 2 | .vscode -------------------------------------------------------------------------------- /templates/starter-next/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # Testing 7 | /coverage 8 | 9 | # Production 10 | /build 11 | /dist 12 | dist-ssr 13 | 14 | # Logs 15 | logs 16 | *.log 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | pnpm-debug.log* 21 | lerna-debug.log* 22 | 23 | # Environment files 24 | .env 25 | .env.local 26 | .env.development.local 27 | .env.test.local 28 | .env.production.local 29 | 30 | # Editor and IDE files 31 | .vscode/* 32 | !.vscode/extensions.json 33 | .idea 34 | *.suo 35 | *.ntvs* 36 | *.njsproj 37 | *.sln 38 | *.sw? 39 | 40 | # OS generated files 41 | .DS_Store 42 | .DS_Store? 43 | ._* 44 | .Spotlight-V100 45 | .Trashes 46 | ehthumbs.db 47 | Thumbs.db 48 | 49 | # Misc 50 | *.local 51 | .history 52 | .vercel 53 | .data 54 | packages/create-codetie/debug.log 55 | repomix-output.txt 56 | .repomix* -------------------------------------------------------------------------------- /templates/starter-next/.planr/assetlist.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructa/viber3d/ad11dfd3ca621f1d3adb4412aa32ea8a12c053db/templates/starter-next/.planr/assetlist.md -------------------------------------------------------------------------------- /templates/starter-next/.planr/core/currentPointer.json: -------------------------------------------------------------------------------- 1 | { 2 | "lastEdited": "2025-03-15T12:00:00Z", 3 | "plan": { 4 | "currentStoryId": "1.1", 5 | "nextAvailableStoryId": "1.2" 6 | }, 7 | "build": { 8 | "currentStoryId": "1.1" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /templates/starter-next/.planr/prd.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructa/viber3d/ad11dfd3ca621f1d3adb4412aa32ea8a12c053db/templates/starter-next/.planr/prd.md -------------------------------------------------------------------------------- /templates/starter-next/.planr/project-structure.md: -------------------------------------------------------------------------------- 1 | # Viber3D SpaceWars Project Structure 2 | 3 | This document outlines the core architecture and flow of the Viber3D SpaceWars project. 4 | 5 | ## Architecture Overview 6 | 7 | The project combines React Three Fiber (R3F) for 3D rendering with Koota ECS for game logic. This separation ensures clean architecture where: 8 | - React components handle purely visual aspects 9 | - ECS systems manage game logic and state 10 | - Traits (ECS components) store entity data 11 | - Actions provide a centralized way to modify the game state 12 | 13 | ## Core Components 14 | 15 | 1. **Game Loop** 16 | - Manages the main update cycle 17 | - Executes ECS systems in order 18 | - Handles timing and delta time 19 | 20 | 2. **World** 21 | - Contains all entities and their traits 22 | - Manages entity lifecycle 23 | - Provides query capabilities for systems 24 | 25 | 3. **Systems** 26 | - Pure functions that operate on entities 27 | - Update game logic based on traits 28 | - Run in a predetermined order each frame 29 | 30 | 4. **Traits** 31 | - Data containers for entities 32 | - Define entity capabilities and state 33 | - Used by systems for logic and by components for rendering 34 | 35 | 5. **React Components** 36 | - Handle 3D rendering using R3F 37 | - Subscribe to trait changes 38 | - Purely presentational, no game logic 39 | 40 | ## System Flow Diagram 41 | 42 | ```mermaid 43 | graph TB 44 | subgraph "Initialization" 45 | A[main.tsx] --> B[app.tsx] 46 | B --> C[world.ts] 47 | C --> D[startup.tsx] 48 | end 49 | 50 | subgraph "Game Loop" 51 | E[frameloop.ts] --> F[System Updates] 52 | F --> G[Physics System] 53 | F --> H[Combat System] 54 | F --> I[AI System] 55 | F --> J[Other Systems...] 56 | end 57 | 58 | subgraph "Rendering Pipeline" 59 | K[React Components] --> L[R3F Scene] 60 | L --> M[3D Models] 61 | L --> N[Effects] 62 | L --> O[UI Overlay] 63 | end 64 | 65 | subgraph "Entity Management" 66 | P[actions.ts] --> Q[Entity Creation] 67 | P --> R[Entity Modification] 68 | P --> S[Entity Destruction] 69 | end 70 | 71 | C --> E 72 | F --> K 73 | P --> C 74 | ``` 75 | 76 | ## Data Flow 77 | 78 | ```mermaid 79 | sequenceDiagram 80 | participant User 81 | participant Action 82 | participant World 83 | participant System 84 | participant Component 85 | 86 | User->>Action: Trigger game action 87 | Action->>World: Modify entity/traits 88 | World->>System: Update cycle 89 | System->>World: Apply logic changes 90 | World->>Component: Notify trait changes 91 | Component->>User: Update visual representation 92 | ``` 93 | 94 | ## Key Interactions 95 | 96 | 1. User input triggers actions 97 | 2. Actions modify the ECS world state 98 | 3. Systems process the changes during the game loop 99 | 4. React components reflect the updated state visually 100 | 5. The cycle continues with new input or autonomous systems 101 | 102 | ## Best Practices 103 | 104 | 1. Keep React components purely presentational 105 | 2. Handle all game logic in systems 106 | 3. Use actions for state modifications 107 | 4. Keep traits minimal and focused 108 | 5. Maintain clear separation between ECS and React -------------------------------------------------------------------------------- /templates/starter-next/.planr/roadmap.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructa/viber3d/ad11dfd3ca621f1d3adb4412aa32ea8a12c053db/templates/starter-next/.planr/roadmap.json -------------------------------------------------------------------------------- /templates/starter-next/.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | coverage/ 3 | node_modules/ 4 | .yarn/ 5 | *.gltf 6 | *.mdx -------------------------------------------------------------------------------- /templates/starter-next/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 110, 3 | "tabWidth": 2, 4 | "useTabs": true, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "es5", 8 | "bracketSpacing": true, 9 | "jsxBracketSameLine": false, 10 | "arrowParens": "always", 11 | "proseWrap": "preserve" 12 | } 13 | -------------------------------------------------------------------------------- /templates/starter-next/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch Development Server", 6 | "type": "node", 7 | "request": "launch", 8 | "preLaunchTask": "Start Development Server", 9 | "presentation": { 10 | "hidden": false, 11 | "group": "", 12 | "order": 1 13 | }, 14 | "internalConsoleOptions": "neverOpen", 15 | "serverReadyAction": { 16 | "pattern": "Local:\\s+(https?://\\S+|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+:[0-9]+)", 17 | "uriFormat": "%s", 18 | "action": "openExternally" 19 | }, 20 | "runtimeExecutable": "node", 21 | "runtimeArgs": ["--version"], 22 | "skipFiles": ["/**"] 23 | }, 24 | { 25 | "name": "Stop Development Server", 26 | "type": "node", 27 | "request": "launch", 28 | "preLaunchTask": "Stop Development Server", 29 | "presentation": { 30 | "hidden": false, 31 | "group": "", 32 | "order": 2 33 | }, 34 | "internalConsoleOptions": "neverOpen", 35 | "serverReadyAction": null, 36 | "runtimeExecutable": "node", 37 | "runtimeArgs": ["--version"], 38 | "skipFiles": ["/**"] 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /templates/starter-next/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "activitusbar.views": [ 3 | { 4 | "name": "task.Start Development Server", 5 | "codicon": "play", 6 | "tooltip": "Start Development Server" 7 | }, 8 | { 9 | "name": "task.Stop Development Server", 10 | "codicon": "stop", 11 | "tooltip": "Stop Development Server" 12 | }, 13 | { 14 | "name": "task.Clean", 15 | "codicon": "sync", 16 | "tooltip": "Clean" 17 | } 18 | ], 19 | "activitusbar.activeColour": "statusBar.foreground", 20 | "activitusbar.inactiveColour": "activityBar.inactiveForeground", 21 | "activitusbar.toggleSidebar": true, 22 | "activitusbar.priority": 99999, 23 | "activitusbar.alignment": "Right", 24 | "editor.defaultFormatter": "esbenp.prettier-vscode", 25 | "editor.formatOnSave": true, 26 | "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, 27 | "[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, 28 | "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" } 29 | } 30 | -------------------------------------------------------------------------------- /templates/starter-next/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Start Development Server", 6 | "type": "shell", 7 | "command": "npm", 8 | "args": ["run", "dev"], 9 | "group": { 10 | "kind": "build", 11 | "isDefault": true 12 | }, 13 | "presentation": { 14 | "reveal": "always", 15 | "panel": "new", 16 | "focus": true 17 | }, 18 | "isBackground": true, 19 | "problemMatcher": { 20 | "pattern": { 21 | "regexp": "^.*$", 22 | "file": 1, 23 | "location": 2, 24 | "message": 3 25 | }, 26 | "background": { 27 | "activeOnStart": true, 28 | "beginsPattern": "^.*$", 29 | "endsPattern": "^.*Local:.+(https?://\\S+|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+:[0-9]+).*$" 30 | } 31 | }, 32 | "detail": "Start viber3d" 33 | }, 34 | { 35 | "label": "Stop Development Server", 36 | "type": "shell", 37 | "command": "echo 'Stopping development server...' && kill $(lsof -t -i:5173) 2>/dev/null || true", 38 | "presentation": { 39 | "reveal": "always", 40 | "panel": "new", 41 | "focus": true 42 | }, 43 | "problemMatcher": [], 44 | "detail": "Force stop the running development server without confirmation" 45 | }, 46 | { 47 | "label": "Clean", 48 | "type": "shell", 49 | "command": "${command:npm-script.showScriptExplorer}", 50 | "script": "clean", 51 | "presentation": { 52 | "reveal": "always", 53 | "panel": "new" 54 | }, 55 | "problemMatcher": [], 56 | "detail": "Clean node_modules and reinstall dependencies" 57 | } 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /templates/starter-next/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to the Starfighter 4.0 project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ### Added 11 | - ECS architecture implementation with Koota 12 | - Improved TypeScript type safety across all components 13 | - New React Three Fiber component structure 14 | - Enhanced system-based game logic separation 15 | 16 | ### Changed 17 | - Migrated game logic to ECS systems 18 | - Refactored components to be purely presentational 19 | - Updated project structure to follow new architecture patterns 20 | 21 | ## [4.0.0] - 2024-03-20 22 | 23 | ### Added 24 | - Initial project setup with React, TypeScript, and React Three Fiber 25 | - Basic 3D scene rendering with component 26 | - Player spaceship model and controls 27 | - Enemy spacecraft with basic AI behavior 28 | - Collision detection system 29 | - Weapon systems (lasers, missiles) 30 | - Particle effects for explosions and engine trails 31 | - Basic HUD with player health, score, and weapon status 32 | - Background starfield and nebula effects 33 | - Game state management with Zustand 34 | - Sound effects and background music 35 | - Mobile-responsive controls 36 | - Performance optimizations for different device capabilities 37 | 38 | ### Changed 39 | - Improved spaceship movement physics 40 | - Enhanced lighting and shadow effects 41 | - Optimized asset loading for faster startup 42 | 43 | ### Fixed 44 | - Collision detection edge cases 45 | - Memory leaks in particle effect system 46 | - Performance issues on lower-end devices 47 | - "Cannot read properties of undefined (reading 'add')" error in player component 48 | 49 | ## [0.1.0] - 2023-12-15 50 | 51 | ### Added 52 | - Project initialization 53 | - Basic development environment setup 54 | - Core game engine architecture 55 | - Initial concept art and 3D models 56 | 57 | [Unreleased]: https://github.com/yourusername/starfighter-2.0/compare/v4.0.0...HEAD 58 | [4.0.0]: https://github.com/yourusername/starfighter-2.0/compare/v0.1.0...v4.0.0 59 | [0.1.0]: https://github.com/yourusername/starfighter-2.0/releases/tag/v0.1.0 60 | -------------------------------------------------------------------------------- /templates/starter-next/README.md: -------------------------------------------------------------------------------- 1 | # viber3d 2 | 3 | ![viber3d banner](/public/images/banner.png) 4 |

5 | License 6 | Stars 7 |

8 | 9 | **viber3D** - a modern starter kit for 3D browser games. 10 | 11 | --- 12 | 13 | ## ⚠️ Important Note 14 | 15 | **Do not install this package directly!** Instead, use the CLI tool to create a new project: 16 | 17 | ```bash 18 | npx viber3d@latest my-project 19 | ``` 20 | 21 | This ensures you get the complete starter kit with all necessary configurations and dependencies properly set up. 22 | 23 | --- -------------------------------------------------------------------------------- /templates/starter-next/eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js'; 2 | import globals from 'globals'; 3 | import reactHooks from 'eslint-plugin-react-hooks'; 4 | import reactRefresh from 'eslint-plugin-react-refresh'; 5 | import tseslint from 'typescript-eslint'; 6 | 7 | export default tseslint.config( 8 | { ignores: ['dist'] }, 9 | { 10 | extends: [js.configs.recommended, ...tseslint.configs.recommended], 11 | files: ['**/*.{ts,tsx}'], 12 | languageOptions: { 13 | ecmaVersion: 2020, 14 | globals: globals.browser, 15 | }, 16 | plugins: { 17 | 'react-hooks': reactHooks, 18 | 'react-refresh': reactRefresh, 19 | }, 20 | rules: { 21 | ...reactHooks.configs.recommended.rules, 22 | 'react-refresh/only-export-components': [ 23 | 'warn', 24 | { allowConstantExport: true }, 25 | ], 26 | }, 27 | } 28 | ); 29 | -------------------------------------------------------------------------------- /templates/starter-next/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Viber3D 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /templates/starter-next/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "viber3d-starter", 3 | "version": "0.0.0", 4 | "private": true, 5 | "description": "Starter template for Viber3D projects", 6 | "type": "module", 7 | "scripts": { 8 | "dev": "vite", 9 | "build": "vite build", 10 | "lint": "eslint .", 11 | "clean": "npx rimraf node_modules && npm install", 12 | "preview": "vite preview" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/instructa/viber3d" 17 | }, 18 | "keywords": [ 19 | "viber3d", 20 | "cli", 21 | "three", 22 | "threejs", 23 | "project-setup", 24 | "cursor", 25 | "cursor-ai", 26 | "ai-coding", 27 | "instructa" 28 | ], 29 | "author": "Kevin Kern", 30 | "license": "MIT", 31 | "dependencies": { 32 | "koota": "^0.2.3", 33 | "@react-three/drei": "^9.120.8", 34 | "@react-three/fiber": "^8.17.12", 35 | "@react-three/rapier": "^1.5.0", 36 | "@tailwindcss/vite": "^4.0.9", 37 | "@react-three/postprocessing": "^2.16.6", 38 | "leva": "^0.10.0", 39 | "react": "^18.3.1", 40 | "react-animated-counter": "^1.7.9", 41 | "react-dom": "^18.3.1", 42 | "tailwindcss": "^4.0.9", 43 | "three": "^0.173.0", 44 | "typescript-eslint": "^8.25.0" 45 | }, 46 | "devDependencies": { 47 | "@eslint/js": "^9.21.0", 48 | "@types/react": "^18.3.18", 49 | "@types/react-dom": "^18.3.5", 50 | "@types/three": "^0.173.0", 51 | "@vitejs/plugin-react": "^4.3.4", 52 | "eslint": "^9.21.0", 53 | "eslint-plugin-react": "^7.37.4", 54 | "eslint-plugin-react-hooks": "^5.1.0", 55 | "globals": "^16.0.0", 56 | "typescript": "^5.7.3", 57 | "@vitejs/plugin-react-swc": "^3.7.2", 58 | "eslint-plugin-react-refresh": "^0.4.18", 59 | "typescript-eslint": "^8.20.0", 60 | "vite": "^6.2.0" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /templates/starter-next/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructa/viber3d/ad11dfd3ca621f1d3adb4412aa32ea8a12c053db/templates/starter-next/public/favicon.ico -------------------------------------------------------------------------------- /templates/starter-next/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructa/viber3d/ad11dfd3ca621f1d3adb4412aa32ea8a12c053db/templates/starter-next/public/favicon.png -------------------------------------------------------------------------------- /templates/starter-next/src/actions.ts: -------------------------------------------------------------------------------- 1 | import { createActions } from 'koota'; 2 | import * as THREE from 'three'; 3 | import { IsPlayer, Transform, IsCamera } from './traits'; 4 | 5 | export const actions = createActions((world) => ({ 6 | spawnPlayer: () => world.spawn(IsPlayer, Transform), 7 | spawnCamera: (position: [number, number, number]) => { 8 | return world.spawn(Transform({ position: new THREE.Vector3(...position) }), IsCamera); 9 | }, 10 | })); 11 | -------------------------------------------------------------------------------- /templates/starter-next/src/app.tsx: -------------------------------------------------------------------------------- 1 | import { Canvas } from '@react-three/fiber'; 2 | import { CameraRenderer } from './components/camera-renderer'; 3 | import { PlayerRenderer } from './components/player-renderer'; 4 | import { GameLoop } from './frameloop'; 5 | import { Startup } from './startup'; 6 | import { Color } from 'three'; 7 | 8 | export function App() { 9 | return ( 10 | <> 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /templates/starter-next/src/assets/ships/enemy.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructa/viber3d/ad11dfd3ca621f1d3adb4412aa32ea8a12c053db/templates/starter-next/src/assets/ships/enemy.glb -------------------------------------------------------------------------------- /templates/starter-next/src/assets/ships/fighter.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructa/viber3d/ad11dfd3ca621f1d3adb4412aa32ea8a12c053db/templates/starter-next/src/assets/ships/fighter.glb -------------------------------------------------------------------------------- /templates/starter-next/src/assets/sounds/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructa/viber3d/ad11dfd3ca621f1d3adb4412aa32ea8a12c053db/templates/starter-next/src/assets/sounds/.gitkeep -------------------------------------------------------------------------------- /templates/starter-next/src/components/camera-renderer.tsx: -------------------------------------------------------------------------------- 1 | import { useQueryFirst } from 'koota/react'; 2 | import { IsCamera, Ref, Transform } from '../traits'; 3 | import { PerspectiveCamera } from '@react-three/drei'; 4 | import { Entity } from 'koota'; 5 | import { ComponentRef, useCallback } from 'react'; 6 | 7 | function CameraView({ entity }: { entity: Entity }) { 8 | const setInitial = useCallback( 9 | (camera: ComponentRef | null) => { 10 | if (!camera) return; 11 | entity.add(Ref(camera)); 12 | }, 13 | [entity] 14 | ); 15 | 16 | return ; 17 | } 18 | 19 | export function CameraRenderer() { 20 | const camera = useQueryFirst(IsCamera, Transform); 21 | if (!camera) return null; 22 | return ; 23 | } 24 | -------------------------------------------------------------------------------- /templates/starter-next/src/components/player-renderer.tsx: -------------------------------------------------------------------------------- 1 | import { useGLTF } from '@react-three/drei'; 2 | import { Entity } from 'koota'; 3 | import { useQueryFirst } from 'koota/react'; 4 | import { useRef, MutableRefObject, useCallback } from 'react'; 5 | import * as THREE from 'three'; 6 | import { Group } from 'three'; 7 | import src from '../assets/ships/fighter.glb?url'; 8 | import { IsPlayer, Transform, Ref } from '../traits'; 9 | 10 | export function PlayerView({ entity }: { entity: Entity }) { 11 | const { scene } = useGLTF(src); 12 | const groupRef = useRef(null) as MutableRefObject; 13 | 14 | // Set up initial state with useCallback 15 | const setInitial = useCallback( 16 | (group: Group | null) => { 17 | if (!group) return; 18 | groupRef.current = group; 19 | 20 | // Initialize with default position at origin 21 | entity.add(Ref(scene)); 22 | if (!entity.has(Transform)) { 23 | entity.set(Transform, { 24 | position: new THREE.Vector3(0, 0, 0), 25 | rotation: new THREE.Euler(0, 0, 0), 26 | scale: new THREE.Vector3(1, 1, 1), 27 | }); 28 | } 29 | }, 30 | [entity, scene] 31 | ); 32 | 33 | return ( 34 | 35 | 36 | 37 | ); 38 | } 39 | 40 | // Query for the first player entity and render it 41 | export function PlayerRenderer() { 42 | const player = useQueryFirst(IsPlayer, Transform); 43 | return player ? : null; 44 | } 45 | -------------------------------------------------------------------------------- /templates/starter-next/src/components/postprocessing.tsx: -------------------------------------------------------------------------------- 1 | import { Bloom, EffectComposer, SMAA } from '@react-three/postprocessing'; 2 | 3 | export function PostProcessing() { 4 | return ( 5 | 6 | 13 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /templates/starter-next/src/frameloop.ts: -------------------------------------------------------------------------------- 1 | import { useFrame } from '@react-three/fiber'; 2 | import { useWorld } from 'koota/react'; 3 | import { syncView } from './systems/sync-view'; 4 | import { updateTime } from './systems/update-time'; 5 | import { updatePlayerRotation } from './systems/update-player-rotation'; 6 | 7 | export function GameLoop() { 8 | const world = useWorld(); 9 | 10 | useFrame(() => { 11 | // Start 12 | updateTime(world); 13 | 14 | // Update game state 15 | updatePlayerRotation(world); 16 | 17 | // Sync view state 18 | syncView(world); 19 | }); 20 | 21 | return null; 22 | } 23 | -------------------------------------------------------------------------------- /templates/starter-next/src/main.tsx: -------------------------------------------------------------------------------- 1 | // src/main.tsx 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom/client'; 4 | import './styles.css'; 5 | import { App } from './app'; 6 | import { WorldProvider } from 'koota/react'; 7 | import { world } from './world'; 8 | 9 | // Create root & render 10 | ReactDOM.createRoot(document.getElementById('root')!).render( 11 | 12 | 13 | 14 | 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /templates/starter-next/src/startup.tsx: -------------------------------------------------------------------------------- 1 | import { useFrame } from '@react-three/fiber'; 2 | import { useActions, useWorld } from 'koota/react'; 3 | import { useEffect } from 'react'; 4 | import { actions } from './actions'; 5 | import { updateSpatialHashing } from './systems/update-spatial-hashing'; 6 | 7 | export function Startup({ 8 | initialCameraPosition = [0, 0, 50], 9 | }: { 10 | initialCameraPosition?: [number, number, number]; 11 | }) { 12 | const { spawnPlayer, spawnCamera } = useActions(actions); 13 | const world = useWorld(); 14 | 15 | useEffect(() => { 16 | // Spawn camera 17 | spawnCamera(initialCameraPosition); 18 | 19 | // Spawn player (without movement) 20 | const player = spawnPlayer(); 21 | 22 | return () => { 23 | player.destroy(); 24 | }; 25 | }, [spawnPlayer, spawnCamera, initialCameraPosition]); 26 | 27 | useFrame(() => { 28 | updateSpatialHashing(world); 29 | }); 30 | 31 | return null; 32 | } 33 | -------------------------------------------------------------------------------- /templates/starter-next/src/styles.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | * { 4 | box-sizing: border-box; 5 | } 6 | 7 | html, 8 | body, 9 | #root { 10 | margin: 0; 11 | padding: 0; 12 | width: 100%; 13 | height: 100%; 14 | overflow: hidden; 15 | } 16 | 17 | body { 18 | background-color: #000; 19 | } 20 | -------------------------------------------------------------------------------- /templates/starter-next/src/systems/apply-force.ts: -------------------------------------------------------------------------------- 1 | import { World } from 'koota'; 2 | import { Movement } from '../traits'; 3 | 4 | export function applyForce(world: World) { 5 | world.query(Movement).updateEach(([{ force, velocity }]) => { 6 | velocity.add(force); 7 | 8 | // Damp force 9 | if (force.length() > 0.01) { 10 | force.multiplyScalar(1 - 0.1); 11 | } else { 12 | force.setScalar(0); 13 | } 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /templates/starter-next/src/systems/apply-input.ts: -------------------------------------------------------------------------------- 1 | import { World } from 'koota'; 2 | import { Input, Movement, Time, Transform } from '../traits'; 3 | import * as THREE from 'three'; 4 | 5 | const MOUSE_SENSITIVITY = 0.005; // Reduced sensitivity to minimize direct rotation 6 | 7 | /** 8 | * convertInputToMovement: 9 | * Applies mouse pitch & yaw, forward & strafe thrust, 10 | * brake damping, and optional boost factor. 11 | */ 12 | export function convertInputToMovement(world: World) { 13 | const { delta } = world.get(Time)!; 14 | 15 | world.query(Input, Transform, Movement).updateEach(([input, transform, movement]) => { 16 | const { velocity, thrust } = movement; 17 | 18 | transform.rotation.x -= input.mouseDelta.y * MOUSE_SENSITIVITY; 19 | transform.rotation.y -= input.mouseDelta.x * MOUSE_SENSITIVITY; 20 | 21 | // Apply roll based on Q/R keys 22 | const ROLL_SPEED = 3.0 * delta; // Adjust roll speed as needed 23 | if (input.roll !== 0) { 24 | transform.rotation.z += input.roll * ROLL_SPEED; 25 | } 26 | 27 | // (No clamp => full freedom to loop or look up/down as in No Man's Sky) 28 | 29 | // 2) Compute local directions 30 | const forwardDir = new THREE.Vector3(0, 0, -1).applyEuler(transform.rotation); 31 | const strafeDir = new THREE.Vector3(1, 0, 0).applyEuler(transform.rotation); 32 | 33 | // 3) Determine thrust force 34 | const boostFactor = input.boost ? 2 : 1; 35 | const thrustForce = thrust * delta * 100 * boostFactor; 36 | 37 | // Forward/back 38 | velocity.addScaledVector(forwardDir, input.forward * thrustForce); 39 | 40 | // Strafe left/right 41 | velocity.addScaledVector(strafeDir, input.strafe * thrustForce); 42 | 43 | // 4) Brake 44 | if (input.brake) { 45 | // Slight damping each frame if S is pressed 46 | velocity.multiplyScalar(0.98); 47 | } 48 | }); 49 | } 50 | -------------------------------------------------------------------------------- /templates/starter-next/src/systems/camera-follow-player.ts: -------------------------------------------------------------------------------- 1 | import { World } from 'koota'; 2 | import { IsCamera, IsPlayer, Transform } from '../traits'; 3 | import * as THREE from 'three'; 4 | 5 | // Camera configuration for space game feel 6 | const CAMERA_CONFIG = { 7 | // Base offset from player (slightly above and behind) 8 | offset: new THREE.Vector3(0, 2, 8), 9 | // How far the camera looks ahead of the player's movement 10 | lookAheadDistance: 10, 11 | // How much the camera rotates with the player 12 | rotationInfluence: 0.7, 13 | // Damping factors for smooth transitions 14 | positionDamping: 0.05, 15 | rotationDamping: 0.08, 16 | // Limits for camera movement 17 | minDistance: 5, 18 | maxDistance: 12, 19 | }; 20 | 21 | export const cameraFollowPlayer = (world: World) => { 22 | const player = world.queryFirst(IsPlayer, Transform); 23 | if (!player) return; 24 | 25 | const playerTransform = player.get(Transform)!; 26 | 27 | // Calculate the desired camera position 28 | const offsetRotated = CAMERA_CONFIG.offset.clone().applyEuler( 29 | new THREE.Euler( 30 | playerTransform.rotation.x * CAMERA_CONFIG.rotationInfluence, 31 | playerTransform.rotation.y * CAMERA_CONFIG.rotationInfluence, 32 | 0 // Don't roll with the player 33 | ) 34 | ); 35 | 36 | world.query(IsCamera, Transform).updateEach(([cameraTransform]) => { 37 | // Calculate target position with look-ahead 38 | const playerForward = new THREE.Vector3(0, 0, -1) 39 | .applyEuler(playerTransform.rotation) 40 | .multiplyScalar(CAMERA_CONFIG.lookAheadDistance); 41 | 42 | const targetPosition = new THREE.Vector3().copy(playerTransform.position).add(offsetRotated); 43 | 44 | // Calculate look-at point (ahead of the player) 45 | const lookAtPoint = new THREE.Vector3().copy(playerTransform.position).add(playerForward); 46 | 47 | // Smoothly move camera position 48 | cameraTransform.position.lerp(targetPosition, CAMERA_CONFIG.positionDamping); 49 | 50 | // Calculate and apply camera rotation 51 | const targetRotation = new THREE.Quaternion().setFromRotationMatrix( 52 | new THREE.Matrix4().lookAt(cameraTransform.position, lookAtPoint, new THREE.Vector3(0, 1, 0)) 53 | ); 54 | 55 | // Apply smooth rotation using quaternion slerp 56 | const currentQuat = new THREE.Quaternion().setFromEuler(cameraTransform.rotation); 57 | currentQuat.slerp(targetRotation, CAMERA_CONFIG.rotationDamping); 58 | cameraTransform.rotation.setFromQuaternion(currentQuat); 59 | 60 | // Ensure camera stays within distance limits 61 | const distanceToPlayer = cameraTransform.position.distanceTo(playerTransform.position); 62 | if (distanceToPlayer < CAMERA_CONFIG.minDistance || distanceToPlayer > CAMERA_CONFIG.maxDistance) { 63 | const idealDistance = THREE.MathUtils.clamp( 64 | distanceToPlayer, 65 | CAMERA_CONFIG.minDistance, 66 | CAMERA_CONFIG.maxDistance 67 | ); 68 | const direction = cameraTransform.position 69 | .clone() 70 | .sub(playerTransform.position) 71 | .normalize() 72 | .multiplyScalar(idealDistance); 73 | cameraTransform.position.copy(playerTransform.position).add(direction); 74 | } 75 | }); 76 | }; 77 | -------------------------------------------------------------------------------- /templates/starter-next/src/systems/limit-speed.ts: -------------------------------------------------------------------------------- 1 | import { World } from 'koota'; 2 | import { Movement, MaxSpeed } from '../traits'; 3 | 4 | export function limitSpeed(world: World) { 5 | // Query the relevant entities 6 | world.query(Movement, MaxSpeed).updateEach(([{ velocity }, { maxSpeed }]) => { 7 | velocity.clampLength(0, maxSpeed) 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /templates/starter-next/src/systems/move-entities.ts: -------------------------------------------------------------------------------- 1 | import { World } from 'koota'; 2 | import { Transform, Movement, Time } from '../traits'; 3 | 4 | export function moveEntities(world: World) { 5 | // Get the delta time from the world clock 6 | const { delta } = world.get(Time)!; 7 | 8 | // Query the relevant entities 9 | const results = world.query(Transform, Movement); 10 | 11 | // Update the data of each entity 12 | results.updateEach(([{ position }, { velocity, damping }]) => { 13 | // Move the position by the velocity for a slice of time 14 | position.x += velocity.x * delta; 15 | position.y += velocity.y * delta; 16 | position.z += velocity.z * delta; 17 | 18 | // Damp the velocity 19 | velocity.multiplyScalar(damping); 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /templates/starter-next/src/systems/sync-view.ts: -------------------------------------------------------------------------------- 1 | import { World } from 'koota'; 2 | import { Transform, Ref } from '../traits'; 3 | 4 | export function syncView(world: World) { 5 | world.query(Transform, Ref).updateEach(([transform, view]) => { 6 | view.position.copy(transform.position); 7 | view.rotation.copy(transform.rotation); 8 | view.scale.copy(transform.scale); 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /templates/starter-next/src/systems/update-player-rotation.ts: -------------------------------------------------------------------------------- 1 | import { World } from 'koota'; 2 | import { Transform, IsPlayer, Time } from '../traits'; 3 | 4 | export const updatePlayerRotation = (world: World) => { 5 | const { delta } = world.get(Time)!; 6 | 7 | world.query(IsPlayer, Transform).updateEach(([transform]) => { 8 | // Use delta for frame-independent rotation 9 | transform.rotation.y += delta * 0.5; 10 | }); 11 | }; 12 | -------------------------------------------------------------------------------- /templates/starter-next/src/systems/update-spatial-hashing.ts: -------------------------------------------------------------------------------- 1 | import { createRemoved, World } from 'koota'; 2 | import { SpatialHashMap, Transform } from '../traits'; 3 | 4 | const Removed = createRemoved(); 5 | 6 | export const updateSpatialHashing = (world: World) => { 7 | const spatialHashMap = world.get(SpatialHashMap); 8 | 9 | // Add entities to the spatial hash map 10 | world.query(Transform).updateEach(([{ position }], entity) => { 11 | spatialHashMap!.setEntity(entity, position.x, position.y, position.z); 12 | }); 13 | 14 | // Remove entities from the spatial hash map 15 | world.query(Removed(Transform)).forEach((entity) => { 16 | spatialHashMap!.removeEntity(entity); 17 | }); 18 | }; 19 | -------------------------------------------------------------------------------- /templates/starter-next/src/systems/update-time.ts: -------------------------------------------------------------------------------- 1 | import { World } from 'koota'; 2 | import { Time } from '../traits'; 3 | 4 | export function updateTime(world: World) { 5 | const time = world.get(Time)!; 6 | 7 | if (time.current === 0) time.current = performance.now(); 8 | 9 | const now = performance.now(); 10 | const delta = now - time.current; 11 | 12 | time.delta = Math.min(delta / 1000, 1 / 30); 13 | time.current = now; 14 | 15 | world.set(Time, time); 16 | } 17 | -------------------------------------------------------------------------------- /templates/starter-next/src/traits/index.ts: -------------------------------------------------------------------------------- 1 | export * from './input'; 2 | export * from './is-player'; 3 | export * from './is-camera'; 4 | export * from './movement'; 5 | export * from './transform'; 6 | export * from './time'; 7 | export * from './spatial-hash-map'; 8 | export * from './ref'; 9 | export * from './maxSpeed'; 10 | -------------------------------------------------------------------------------- /templates/starter-next/src/traits/input.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | import * as THREE from 'three'; 3 | 4 | /** 5 | * Input trait for free-roam flight: 6 | * - forward: +1 (W) or 0 (none) 7 | * - strafe: +1 (D), -1 (A), or 0 8 | * - boost: true when Space is held 9 | * - brake: true when S is held 10 | * - mouseDelta: frame-by-frame mouse movement (x=Yaw, y=Pitch) 11 | * - roll: +1 (R), -1 (Q), or 0 for rolling the ship 12 | */ 13 | export const Input = trait({ 14 | forward: 0, 15 | strafe: 0, 16 | boost: false, 17 | brake: false, 18 | roll: 0, // +1 for roll right (R), -1 for roll left (Q) 19 | mouseDelta: () => new THREE.Vector2(), 20 | }); 21 | 22 | // export const Input = trait(() => new THREE.Vector2()); 23 | -------------------------------------------------------------------------------- /templates/starter-next/src/traits/is-camera.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | 3 | export const IsCamera = trait(); 4 | -------------------------------------------------------------------------------- /templates/starter-next/src/traits/is-player.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | 3 | export const IsPlayer = trait(); 4 | -------------------------------------------------------------------------------- /templates/starter-next/src/traits/maxSpeed.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | 3 | export const MaxSpeed = trait({ 4 | maxSpeed: 1, 5 | }); -------------------------------------------------------------------------------- /templates/starter-next/src/traits/movement.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | import * as THREE from 'three'; 3 | 4 | export const Movement = trait({ 5 | velocity: () => new THREE.Vector3(), 6 | thrust: 1, 7 | damping: 0.95, 8 | force: () => new THREE.Vector3(), 9 | }); 10 | -------------------------------------------------------------------------------- /templates/starter-next/src/traits/ref.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | import * as THREE from 'three'; 3 | 4 | export const Ref = trait(() => new THREE.Object3D()); 5 | -------------------------------------------------------------------------------- /templates/starter-next/src/traits/spatial-hash-map.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | import { SpatialHashMap as SpatialHashMapImpl } from '../utils/spatial-hash'; 3 | 4 | export const SpatialHashMap = trait(() => new SpatialHashMapImpl(50)); 5 | -------------------------------------------------------------------------------- /templates/starter-next/src/traits/time.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | 3 | export const Time = trait({ delta: 0, current: 0 }); 4 | -------------------------------------------------------------------------------- /templates/starter-next/src/traits/transform.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | import * as THREE from 'three'; 3 | 4 | // A transform just like CSS! 5 | export const Transform = trait({ 6 | position: () => new THREE.Vector3(), 7 | rotation: () => new THREE.Euler(), 8 | scale: () => new THREE.Vector3(1, 1, 1), 9 | }); 10 | -------------------------------------------------------------------------------- /templates/starter-next/src/utils/between.ts: -------------------------------------------------------------------------------- 1 | export function between(min: number, max: number): number { 2 | return Math.random() * (max - min) + min; 3 | } 4 | -------------------------------------------------------------------------------- /templates/starter-next/src/utils/sort-entities-by-distance.ts: -------------------------------------------------------------------------------- 1 | import { Entity } from 'koota'; 2 | import * as THREE from 'three'; 3 | import { Transform } from '../traits'; 4 | 5 | export const sortEntitiesByDistance = (position: THREE.Vector3, entities: Entity[]) => { 6 | return [...entities].sort((a, b) => { 7 | const transformA = a.get(Transform); 8 | const transformB = b.get(Transform); 9 | 10 | if (!transformA || !transformB) return 0; 11 | 12 | const distanceA = position.distanceTo(transformA.position); 13 | const distanceB = position.distanceTo(transformB.position); 14 | 15 | return distanceA - distanceB; 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /templates/starter-next/src/utils/spatial-hash.ts: -------------------------------------------------------------------------------- 1 | // Originally by Hendrik Mans: https://github.com/hmans/miniplex/blob/main/apps/demo/src/systems/SpatialHashingSystem.tsx 2 | 3 | import { Entity } from 'koota'; 4 | 5 | type Cell = Set; 6 | 7 | export class SpatialHashMap { 8 | protected cells = new Map(); 9 | protected entityToCell = new Map(); 10 | 11 | constructor(public cellSize: number) {} 12 | 13 | setEntity(entity: Entity, x: number, y: number, z: number) { 14 | const cell = this.getCell(x, y, z); 15 | 16 | /* Remove from previous hash if known */ 17 | const oldCell = this.entityToCell.get(entity); 18 | 19 | if (oldCell) { 20 | /* If hash didn't change, do nothing */ 21 | if (oldCell === cell) return; 22 | 23 | /* Remove from previous hash */ 24 | oldCell.delete(entity); 25 | } 26 | 27 | cell.add(entity); 28 | this.entityToCell.set(entity, cell); 29 | } 30 | 31 | removeEntity(entity: Entity) { 32 | const cell = this.entityToCell.get(entity); 33 | cell?.delete(entity); 34 | this.entityToCell.delete(entity); 35 | } 36 | 37 | getNearbyEntities( 38 | x: number, 39 | y: number, 40 | z: number, 41 | radius: number, 42 | entities: Entity[] = [], 43 | maxEntities = Infinity 44 | ) { 45 | let count = 0; 46 | entities.length = 0; 47 | 48 | // Calculate the cell coordinates that contain the sphere defined by radius 49 | const minCellX = Math.floor((x - radius) / this.cellSize); 50 | const maxCellX = Math.floor((x + radius) / this.cellSize); 51 | const minCellY = Math.floor((y - radius) / this.cellSize); 52 | const maxCellY = Math.floor((y + radius) / this.cellSize); 53 | const minCellZ = Math.floor((z - radius) / this.cellSize); 54 | const maxCellZ = Math.floor((z + radius) / this.cellSize); 55 | 56 | // Iterate through all cells that might contain entities within the radius 57 | for (let cx = minCellX; cx <= maxCellX; cx++) { 58 | for (let cy = minCellY; cy <= maxCellY; cy++) { 59 | for (let cz = minCellZ; cz <= maxCellZ; cz++) { 60 | const cell = this.getCell( 61 | cx * this.cellSize, 62 | cy * this.cellSize, 63 | cz * this.cellSize 64 | ); 65 | 66 | for (const entity of cell) { 67 | entities.push(entity); 68 | count++; 69 | 70 | if (count >= maxEntities) return entities; 71 | } 72 | } 73 | } 74 | } 75 | 76 | return entities; 77 | } 78 | 79 | reset() { 80 | this.cells.clear(); 81 | this.entityToCell.clear(); 82 | } 83 | 84 | protected getCell(x: number, y: number, z: number) { 85 | const hash = this.calculateHash(x, y, z, this.cellSize); 86 | 87 | if (!this.cells.has(hash)) { 88 | this.cells.set(hash, new Set()); 89 | } 90 | 91 | return this.cells.get(hash)!; 92 | } 93 | 94 | protected calculateHash(x: number, y: number, z: number, cellSize: number) { 95 | const hx = Math.floor(x / cellSize); 96 | const hy = Math.floor(y / cellSize); 97 | const hz = Math.floor(z / cellSize); 98 | 99 | return `${hx}:${hy}:${hz}`; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /templates/starter-next/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /templates/starter-next/src/world.ts: -------------------------------------------------------------------------------- 1 | import { createWorld } from 'koota'; 2 | import { SpatialHashMap, Time } from './traits'; 3 | 4 | export const world = createWorld(Time, SpatialHashMap); 5 | -------------------------------------------------------------------------------- /templates/starter-next/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 5 | "target": "ES2020", 6 | "useDefineForClassFields": true, 7 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 8 | "module": "ESNext", 9 | "skipLibCheck": true, 10 | 11 | /* Bundler mode */ 12 | "moduleResolution": "Bundler", 13 | "allowImportingTsExtensions": true, 14 | "isolatedModules": true, 15 | "moduleDetection": "force", 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | 19 | /* Linting */ 20 | "strict": true, 21 | "noUnusedLocals": true, 22 | "noUnusedParameters": true, 23 | "noFallthroughCasesInSwitch": true 24 | }, 25 | "include": ["src"] 26 | } 27 | -------------------------------------------------------------------------------- /templates/starter-next/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import react from '@vitejs/plugin-react-swc'; 3 | import tailwindcss from '@tailwindcss/vite'; 4 | 5 | export default defineConfig({ 6 | plugins: [react(), tailwindcss()], 7 | }); 8 | -------------------------------------------------------------------------------- /templates/starter/.cursor/rules/001-base.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Viber3D Coding Guidelines 3 | globs: *,**/* 4 | alwaysApply: true 5 | --- 6 | 7 | # R3F + Koota ECS Base Rules 8 | 9 | ## Core Architecture 10 | 11 | - Use Koota ECS for all game logic. 12 | - Keep React components purely presentational. 13 | - Do not mix ECS logic and React rendering. 14 | 15 | # Instructions 16 | 17 | 1. Always use codebase_search with target_directories="src" first to find existing core files 18 | 2. Always check existing system files' purposes before creating new ones with similar functionality 19 | 3. Always check how core objects (World, Entity, etc.) are instantiated in the existing codebase 20 | 4. Always list the cursor rules you're using this is for debugging purpose 21 | 22 | 23 | ## Directory Structure 24 | ``` 25 | viber3d/ 26 | ├── src 27 | │ ├── assets/ # 3D models, textures, images 28 | │ ├── components/ # React components for rendering 3D objects/UI 29 | │ ├── systems/ # ECS Systems for game logic updates 30 | │ ├── traits/ # ECS Traits (components) describing entity data 31 | │ ├── utils/ # Utility functions (math, sorting, spatial hashing) 32 | │ ├── actions.ts # Central actions to spawn or modify entities 33 | │ ├── app.tsx # Main React component (root of your 3D scene) 34 | │ ├── frameloop.ts # Main ECS update loop 35 | │ ├── index.css # Global CSS or Tailwind CSS (if used) 36 | │ ├── main.tsx # React app root, renders 37 | │ ├── startup.tsx # Startup logic (initial spawns, intervals) 38 | │ └── world.ts # Creates the ECS world with default traits 39 | ├── index.html # Basic HTML page with root div 40 | ├── package.json # Project dependencies and scripts 41 | └── tsconfig.json # TypeScript configuration 42 | ``` 43 | 44 | ## Important Guidelines 45 | 46 | 1. Systems must be stateless functions that query traits and update ECS data. 47 | 2. Components only render visuals; use `useTraitEffect` to reflect trait changes. 48 | 3. Traits hold data; avoid logic in them. Keep them minimal and typed. 49 | 4. Relationships (`relation()`) can be used for parent-child or referencing entities. 50 | 5. SoA vs AoS: Use schema-based traits for large numeric data, callback-based traits for complex objects. 51 | 6. Advanced Queries: Use partial selection (`.select(...)`) or specialized operators (`Not`, `Or`, `createAdded`, etc.) when needed. 52 | 7. Scheduling: If you have multiple systems, consider a scheduling approach so only relevant systems run in different states (e.g. paused vs. active). 53 | 8. If we have linter/typescript errors try to not be that restrictive with types. 54 | 55 | ## Anti-Patterns 56 | 57 | - Do not place game logic in React hooks (e.g. `useFrame`), except for purely visual effects. 58 | - Avoid storing ECS-related state in React state. 59 | - Never mutate traits inside components. Use systems or actions instead. 60 | - Do not initialize or destroy ECS entities directly in components (use an action or system). 61 | 62 | ## Steps 63 | 64 | 1. When implementing any ECS feature, always follow this order: 65 | 2. Define all traits first 66 | 3. Document trait dependencies (which traits need each other) 67 | 4. Create a spawn function that bundles ALL required traits together 68 | 5. Only then implement the systems that operate on these traits 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /templates/starter/.cursor/rules/002-components.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: React Three Fiber Component Rules 3 | globs: src/components/**/*.tsx 4 | alwaysApply: false 5 | --- 6 | 7 | # R3F Component Guidelines 8 | 9 | ## Core Principles 10 | 11 | - Use functional components for R3F with ECS data read via hooks. 12 | - Keep components strictly presentational. All game logic should reside in systems. 13 | - Use `useTraitEffect(entity, Trait, callback)` to reflect ECS changes on mesh references. 14 | 15 | ## Example Structure 16 | 17 | ```tsx 18 | function EnemyView({ entity }: { entity: Entity }) { 19 | const ref = useRef(null) 20 | 21 | // React to trait changes only for visual updates 22 | useTraitEffect(entity, Transform, (t) => { 23 | if (ref.current && t) { 24 | ref.current.position.copy(t.position) 25 | ref.current.rotation.copy(t.rotation) 26 | } 27 | }) 28 | 29 | return ( 30 | 31 | 32 | 33 | 34 | ) 35 | } 36 | ``` 37 | 38 | ## Asset Loading and Cleanup 39 | 40 | - Use React Three Fiber helpers like `useGLTF` or `useTexture` for loading models or textures. 41 | - Dispose geometry and materials in a cleanup function if creating many dynamic objects over time. 42 | 43 | ## Anti-Patterns 44 | 45 | - Do not place ECS or game logic in React state. 46 | - Do not mutate ECS traits directly from a component. Use systems or actions. 47 | - Do not rely on `useFrame` for core game logic; reserve it for minor visual effects only. 48 | 49 | -------------------------------------------------------------------------------- /templates/starter/.cursor/rules/003-systems.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: ECS System Rules 3 | globs: src/systems/*.ts 4 | alwaysApply: false 5 | --- 6 | 7 | ## System Requirements 8 | 9 | - Each system is a pure function that processes traits and updates ECS data. 10 | - Use `world.query(...)` to get relevant entities and keep queries minimal. 11 | - Include delta time when doing any time-based logic. 12 | - Avoid side effects beyond ECS data changes (no DOM or React state modifications). 13 | 14 | ## System Examples 15 | 16 | ```ts 17 | // Standard system pattern 18 | export function movementSystem(world: World) { 19 | const time = world.get(Time) 20 | if (!time) return 21 | world.query(Transform, Velocity).updateEach(([t, v]) => { 22 | t.position.addScaledVector(v.vector, time.delta) 23 | }) 24 | } 25 | ``` 26 | 27 | ## Advanced Usage 28 | 29 | - Partial Queries: Use `.select(...)` to fetch only the needed traits for performance. 30 | - Conditional Scheduling: Run certain systems only in specific game states (e.g. paused, menu, or combat). 31 | - Relationships: Use `relation()` if the system involves parent-child or entity-to-entity logic, rather than raw references. 32 | 33 | ## Performance Tips 34 | 35 | - Reuse math objects like `Vector3` or `Matrix4` rather than creating new ones every frame. 36 | - Remove or recycle entities that are no longer needed (e.g. off-screen, zero-health). 37 | - Prefer SoA for large numeric arrays to improve cache performance. 38 | - Consider running heavy systems at reduced frequencies instead of every frame. 39 | 40 | ## Do Not 41 | 42 | - Do not directly manipulate React components or the DOM from a system. 43 | - Do not skip delta time in time-based updates. 44 | - Do not implement camera or purely visual updates in systems; leave those to React or specialized visual hooks. -------------------------------------------------------------------------------- /templates/starter/.cursor/rules/004-actions.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Centralized ECS Actions 3 | globs: src/actions/*.ts 4 | alwaysApply: false 5 | --- 6 | 7 | # Actions Guidelines 8 | 9 | ## Purpose 10 | 11 | - Actions are centralized functions that modify the ECS world in predictable ways. 12 | - They keep spawn/remove/update logic out of components and systems, providing a single place for entity manipulation. 13 | 14 | ## Core Principles 15 | 16 | 1. Single Entry Points 17 | Write all creation, destruction, or major trait updates in these functions. 18 | 2. Pure ECS Focus 19 | Do not add external side effects or UI manipulation. 20 | 3. Descriptive Names 21 | Use clear names like `spawnEnemy()`, `applyDamage()`, or `destroyEntity()`. 22 | 23 | ## Recommended Usage 24 | 25 | ```ts 26 | import { createActions } from 'koota' 27 | import { IsEnemy, Transform, Health } from '../traits' 28 | 29 | export const actions = createActions((world) => ({ 30 | spawnEnemy: (position) => { 31 | return world.spawn( 32 | IsEnemy(), 33 | Transform({ position }), 34 | Health({ amount: 50 }) 35 | ) 36 | }, 37 | damageEntity: (entity, amount) => { 38 | if (entity.has(Health)) { 39 | entity.set(Health, (h) => { 40 | const newAmount = Math.max(0, h.amount - amount) 41 | if (newAmount <= 0) { 42 | actions.destroyEntity(entity) 43 | } 44 | return { ...h, amount: newAmount } 45 | }) 46 | } 47 | }, 48 | destroyEntity: (entity) => { 49 | entity.destroy() 50 | } 51 | })) 52 | ``` 53 | 54 | ## Integration with React 55 | 56 | - Use `useActions(actionsFile)` from `koota/react` to bind actions to the current `world`. 57 | - Keep React components free of direct ECS manipulation. Call these action functions instead. 58 | 59 | ```tsx 60 | function GameInit() { 61 | const { spawnEnemy } = useActions(actions) 62 | 63 | useEffect(() => { 64 | spawnEnemy({ x: 0, y: 5, z: 0 }) 65 | }, []) 66 | 67 | return null 68 | } 69 | ``` 70 | 71 | ## Testing Actions 72 | 73 | - Create a fresh `world` in each test and bind actions with `actions.bindTo(world)`. 74 | - Verify entity changes after calling each action. 75 | 76 | ```ts 77 | describe('actions', () => { 78 | let world 79 | let boundActions 80 | 81 | beforeEach(() => { 82 | world = createWorld() 83 | boundActions = actions.bindTo(world) 84 | }) 85 | 86 | it('spawnEnemy creates an enemy entity', () => { 87 | const ent = boundActions.spawnEnemy({ x: 0, y: 1, z: 0 }) 88 | expect(ent.has(IsEnemy)).toBe(true) 89 | }) 90 | }) 91 | ``` 92 | 93 | ## Avoid 94 | 95 | - Do not add direct DOM or React manipulations in actions. 96 | - Do not handle input events or animations here; keep it purely for ECS state changes. 97 | - Avoid side effects that do not relate to ECS data. Keep them simple and testable. -------------------------------------------------------------------------------- /templates/starter/.cursor/rules/005-traits.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Trait Module Rules 3 | globs: src/traits/**/*.ts 4 | alwaysApply: false 5 | --- 6 | 7 | # Trait Definitions 8 | 9 | ## Core Practices 10 | 11 | - Keep trait data minimal. Place logic in systems. 12 | - Use schema-based (SoA) traits for numeric or frequently-updated data. 13 | - Use callback-based (AoS) traits for complex or class objects (e.g. THREE.Mesh). 14 | - Use tag traits (with no fields) as flags to identify entities (like `IsPlayer`). 15 | 16 | ## Examples 17 | 18 | ```ts 19 | import { trait } from 'koota' 20 | import * as THREE from 'three' 21 | 22 | // Schema-based (SoA) 23 | export const Health = trait({ amount: 100, max: 100 }) 24 | 25 | // Callback-based (AoS) 26 | export const Transform = trait(() => ({ 27 | position: new THREE.Vector3(), 28 | rotation: new THREE.Euler() 29 | })) 30 | 31 | // Tag trait 32 | export const IsEnemy = trait() 33 | ``` 34 | 35 | ## Relationships 36 | 37 | - Use `relation()` when entities need references to each other. 38 | - Pass additional fields if you need extra data in the relationship. 39 | - Use `exclusive: true` or `autoRemoveTarget: true` if needed for parenting or single-target constraints. 40 | 41 | ```ts 42 | import { relation } from 'koota' 43 | 44 | // Simple relation with no fields 45 | export const ChildOf = relation() 46 | 47 | // Relationship with data 48 | export const Contains = relation({ store: { amount: 0 } }) 49 | ``` 50 | 51 | ## Best Practices 52 | 53 | - Store only essential data in traits. Do not add large logic blocks. 54 | - Check for existence before adding a trait: `if (!entity.has(Health)) entity.add(Health())`. 55 | - Prefer `entity.set(Trait, (prev) => ({ ...prev, ...changes }))` for updates so Koota can handle events. 56 | 57 | ## Anti-Patterns 58 | 59 | - Do not share the same mutable object across multiple entities (like a single shared Vector3). 60 | - Do not store React refs, DOM elements, or large external objects in traits. 61 | - Do not combine unrelated data in one trait. Keep them cohesive and small. -------------------------------------------------------------------------------- /templates/starter/.cursor/rules/006-utils.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Utility Function Patterns 3 | globs: src/utils/**/*.ts 4 | alwaysApply: false 5 | --- 6 | 7 | ## Koota Integration 8 | 9 | - Access entity traits with `entity.get(Trait)`. 10 | - Handle missing traits gracefully, using null checks when necessary. 11 | 12 | ## Three.js Usage 13 | 14 | - Reuse math objects like `Vector3` to reduce garbage collection. 15 | - Leverage built-in Three.js methods for transformations instead of reinventing them. 16 | 17 | ## Performance 18 | 19 | - Reuse arrays or buffers for spatial and geometry operations. 20 | - Implement spatial partitioning (quadtrees, hash grids) for large worlds. 21 | - Provide reset methods to avoid creating new data structures every frame. 22 | 23 | ## Math Utilities 24 | 25 | - Consolidate common math operations in concise functions. 26 | - Ensure edge cases are handled (e.g. clamping, bounding conditions). 27 | 28 | ## Documentation 29 | 30 | - Add clear comments and note any adapted third-party code. 31 | - Highlight performance considerations for complex or frequently used utilities. -------------------------------------------------------------------------------- /templates/starter/.cursor/rules/007-tailwind.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: "Tailwind CSS usage rules for styling (2025 best practices)" 3 | globs: src/components/**/*.tsx,*.css 4 | alwaysApply: false 5 | --- 6 | - Use Tailwind utility classes for consistent styling, with custom CSS only for special cases 7 | - Organize classes logically (layout, spacing, color, typography) 8 | - Use responsive and state variants (e.g., sm:, md:, lg:, hover:, focus:, dark:) in markup 9 | - Embrace Tailwind v4 features like container queries and CSS variables 10 | - Rely on Tailwind classes rather than inline styles or external CSS files for a unified design language -------------------------------------------------------------------------------- /templates/starter/.cursor/rules/900-game-agent.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Levin Builder Agent 3 | globs: 4 | alwaysApply: false 5 | --- 6 | 7 | Levin AI Agent Instructions 8 | 9 | You always greet with a random "leetspeak" welcome message (only once)! 10 | 11 | You are Levin, a specialized software development AI agent. Your primary goal is to fulfill all user stories. 12 | 13 | RULES FOR THE AGENT: 14 | - If a prompt or request specifies certain behaviors, languages, or output formats, you must obey them without deviation. 15 | - Do not include explanations, reasoning, or filler text unless explicitly instructed. Stick strictly to the requested output. 16 | - If multiple steps or sub-requests are given, address them in the specified order. Provide answers in the exact format or sequence requested. 17 | - Pay close attention to all stated constraints (e.g., language choice, performance goals, coding style). Do not ignore any requirement or best practice stated. 18 | - Only produce output relevant to the question or instructions. Do not add features, code, or details beyond what is explicitly asked. 19 | - Deliver the response in a minimal yet complete form. Avoid unnecessary verbosity and tangential remarks. 20 | - If the prompt requests a specific output format (e.g., a fenced code block, bullet points, JSON), follow that format exactly. 21 | - If a prompt includes a pre-seeded answer structure (e.g., starts a code block), continue within that structure without introducing extra text outside it. 22 | - If the request is ambiguous, you may ask clarifying questions (if instructions allow). Otherwise, state briefly that more information is needed. 23 | - When generating or modifying code, adhere to best practices for clarity, maintainability, and efficiency, as appropriate to the specified language or framework. 24 | - Do not generate or include private data (API keys, secrets) unless explicitly provided in context. If the user requests something unsafe or disallowed, refuse or provide a safe alternative per policy. 25 | 26 | CRITICAL RULES: 27 | - DON'T skip a step and wait until one step is 100% finished! 28 | - Server is already started. Never start the server. 29 | 30 | Follow these guidelines: 31 | 32 | 1. first open [tasklist.yaml](mdc:docs/tasklist.yaml) and learn about latest changes and use it as your memory. Allowed status for stories are todo or done. 33 | 34 | 2. Proceed with implementing each story in `docs/stories/` sequence, ensuring you address all acceptance criteria. 35 | 36 | 3. implement each story 100% without any errors. 37 | 38 | 4. check if you add all critical systems to the game loop 39 | 40 | 5. write all infos about updates, fixes to a new option in the current story so that the next dev knows what happened in [tasklist.yaml](mdc:docs/tasklist.yaml) 41 | 42 | 6. ask if you should proceed wit the next task 43 | 44 | -------------------------------------------------------------------------------- /templates/starter/.cursorignore: -------------------------------------------------------------------------------- 1 | scripts -------------------------------------------------------------------------------- /templates/starter/.cursorindexingignore: -------------------------------------------------------------------------------- 1 | public/*/** 2 | .vscode 3 | -------------------------------------------------------------------------------- /templates/starter/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # Testing 7 | /coverage 8 | 9 | # Production 10 | /build 11 | /dist 12 | dist-ssr 13 | 14 | # Logs 15 | logs 16 | *.log 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | pnpm-debug.log* 21 | lerna-debug.log* 22 | 23 | # Environment files 24 | .env 25 | .env.local 26 | .env.development.local 27 | .env.test.local 28 | .env.production.local 29 | 30 | # Editor and IDE files 31 | !.vscode/extensions.json 32 | .idea 33 | *.suo 34 | *.ntvs* 35 | *.njsproj 36 | *.sln 37 | *.sw? 38 | 39 | # OS generated files 40 | .DS_Store 41 | .DS_Store? 42 | ._* 43 | .Spotlight-V100 44 | .Trashes 45 | ehthumbs.db 46 | Thumbs.db 47 | 48 | # Misc 49 | *.local 50 | .history 51 | .vercel 52 | .data 53 | packages/create-codetie/debug.log 54 | repomix-output.txt 55 | .repomix* -------------------------------------------------------------------------------- /templates/starter/.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | coverage/ 3 | node_modules/ 4 | .yarn/ 5 | *.gltf 6 | *.mdx -------------------------------------------------------------------------------- /templates/starter/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 110, 3 | "tabWidth": 2, 4 | "useTabs": true, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "es5", 8 | "bracketSpacing": true, 9 | "jsxBracketSameLine": false, 10 | "arrowParens": "always", 11 | "proseWrap": "preserve" 12 | } 13 | -------------------------------------------------------------------------------- /templates/starter/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to the Starfighter 4.0 project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ### Added 11 | - ECS architecture implementation with Koota 12 | - Improved TypeScript type safety across all components 13 | - New React Three Fiber component structure 14 | - Enhanced system-based game logic separation 15 | 16 | ### Changed 17 | - Migrated game logic to ECS systems 18 | - Refactored components to be purely presentational 19 | - Updated project structure to follow new architecture patterns 20 | 21 | ## [4.0.0] - 2024-03-20 22 | 23 | ### Added 24 | - Initial project setup with React, TypeScript, and React Three Fiber 25 | - Basic 3D scene rendering with component 26 | - Player spaceship model and controls 27 | - Enemy spacecraft with basic AI behavior 28 | - Collision detection system 29 | - Weapon systems (lasers, missiles) 30 | - Particle effects for explosions and engine trails 31 | - Basic HUD with player health, score, and weapon status 32 | - Background starfield and nebula effects 33 | - Game state management with Zustand 34 | - Sound effects and background music 35 | - Mobile-responsive controls 36 | - Performance optimizations for different device capabilities 37 | 38 | ### Changed 39 | - Improved spaceship movement physics 40 | - Enhanced lighting and shadow effects 41 | - Optimized asset loading for faster startup 42 | 43 | ### Fixed 44 | - Collision detection edge cases 45 | - Memory leaks in particle effect system 46 | - Performance issues on lower-end devices 47 | - "Cannot read properties of undefined (reading 'add')" error in player component 48 | 49 | ## [0.1.0] - 2023-12-15 50 | 51 | ### Added 52 | - Project initialization 53 | - Basic development environment setup 54 | - Core game engine architecture 55 | - Initial concept art and 3D models 56 | 57 | [Unreleased]: https://github.com/yourusername/starfighter-2.0/compare/v4.0.0...HEAD 58 | [4.0.0]: https://github.com/yourusername/starfighter-2.0/compare/v0.1.0...v4.0.0 59 | [0.1.0]: https://github.com/yourusername/starfighter-2.0/releases/tag/v0.1.0 60 | -------------------------------------------------------------------------------- /templates/starter/README.md: -------------------------------------------------------------------------------- 1 | # viber3d 2 | 3 | ![viber3d banner](/public/images/banner.png) 4 |

5 | License 6 | Stars 7 |

8 | 9 | **viber3D** - a modern starter kit for 3D browser games. 10 | 11 | --- 12 | 13 | ## ⚠️ Important Note 14 | 15 | **Do not install this package directly!** Instead, use the CLI tool to create a new project: 16 | 17 | ```bash 18 | npx viber3d@latest my-project 19 | ``` 20 | 21 | This ensures you get the complete starter kit with all necessary configurations and dependencies properly set up. 22 | 23 | --- -------------------------------------------------------------------------------- /templates/starter/docs/stories/1.1-basic-ship-movement.md: -------------------------------------------------------------------------------- 1 | # Story: Basic Ship Movement 2 | 3 | ## User Story 4 | **As a player,** 5 | I want to **freely control my spaceship in all directions** (yaw, pitch, roll, strafe, throttle) 6 | So that I can navigate space in a natural and responsive way. 7 | 8 | ## Acceptance Criteria 9 | 10 | ### Controls 11 | - **W/S** → Increase/Decrease thrust 12 | - **A/D** → Strafe left/right 13 | - **Mouse movement** → Pitch and yaw rotation 14 | - **Q/E** → Roll left/right 15 | - **Space** → Boost forward (short burst) 16 | - **Shift** → Brake 17 | 18 | ### Movement Behaviors 19 | - Movement should be **inertia-based** (conserving momentum) but with a slight auto-stabilization to prevent infinite drifting 20 | - Thrust accelerates gradually, while braking slows the ship quickly 21 | - Camera smoothly follows behind the ship with slight lag for a cinematic feel 22 | 23 | ## Technical Implementation Notes 24 | - Implement physics-based movement using Three.js/React Three Fiber 25 | - Create ship controller class that handles input and translates to movement 26 | - Add damping coefficient for auto-stabilization 27 | - Implement camera follow logic with smooth transitions 28 | - Add visual feedback for thrust (engine glow/particles) 29 | 30 | ## Definition of Done 31 | - Player can control the ship in all 6 degrees of freedom 32 | - Movement feels smooth and responsive 33 | - Inertia is preserved while still being controllable 34 | - Camera follows ship appropriately without causing motion sickness 35 | - HUD displays current speed and direction 36 | 37 | ## Dependencies 38 | - Ship 3D model assets 39 | - Basic space environment setup 40 | - Input handling system -------------------------------------------------------------------------------- /templates/starter/docs/stories/1.2-advanced-flight-maneuvers.md: -------------------------------------------------------------------------------- 1 | # Story: Advanced Flight Maneuvers 2 | 3 | ## User Story 4 | **As a player,** 5 | I want to **perform evasive maneuvers** (barrel rolls, sudden brakes, quick turns) 6 | So that I can dodge enemy fire and outmaneuver opponents. 7 | 8 | ## Acceptance Criteria 9 | 10 | ### Maneuvers 11 | - **Barrel Roll** → Press **Q or E** to quickly spin the ship, providing temporary invulnerability 12 | - **Quick Turn** → Press a directional key and a movement key together to execute a sharp turn 13 | - **Drift Mode** → Let go of thrust and manually rotate while drifting for advanced positioning 14 | 15 | ### Behavior 16 | - Barrel rolls should complete a full 360° rotation in under 1 second 17 | - Quick turns should rapidly change ship orientation while maintaining some momentum 18 | - Drift mode should allow the ship to continue on its trajectory while the player can reorient 19 | 20 | ## Technical Implementation Notes 21 | - Implement special maneuver state machine with cooldowns 22 | - Add visual effects for each maneuver (motion blur, trail effects) 23 | - Create temporary invulnerability frames during barrel roll 24 | - Implement easing functions for smooth maneuver animations 25 | - Add audio cues for maneuver activation 26 | 27 | ## Definition of Done 28 | - All three special maneuvers can be triggered and performed 29 | - Maneuvers provide tactical advantages in combat 30 | - Animations are smooth and convey the sense of movement 31 | - Visual effects clearly indicate when a maneuver is being performed 32 | - Cooldown system prevents maneuver spamming 33 | 34 | ## Dependencies 35 | - Basic ship movement (Story 1.1) 36 | - Animation system 37 | - VFX framework -------------------------------------------------------------------------------- /templates/starter/docs/stories/2.1-basic-laser-firing.md: -------------------------------------------------------------------------------- 1 | # Story: Basic Laser Firing 2 | 3 | ## User Story 4 | **As a player,** 5 | I want to **fire energy lasers at enemies** 6 | So that I can damage and destroy them in space combat. 7 | 8 | ## Acceptance Criteria 9 | 10 | ### Weapons System 11 | - Left Mouse Button fires **rapid laser shots** 12 | - Lasers **overheat** if fired continuously for too long 13 | - Visual indicator shows current heat level of weapons 14 | - Cooling period required if weapon overheats completely 15 | 16 | ### Visual and Audio 17 | - **Visuals**: Bright energy bolts with subtle glow and trail effect 18 | - **Sound FX**: Distinct firing sound + impact sound when hitting an enemy 19 | - Muzzle flash effect at ship weapon hardpoints 20 | - Hit effects on enemy ships (sparks, shield ripple) 21 | 22 | ## Technical Implementation Notes 23 | - Implement raycast-based weapon system for hitscan lasers 24 | - Create heat management system with variables for: 25 | - Heat generation per shot 26 | - Cooling rate 27 | - Maximum heat capacity 28 | - Design particle effects for laser bolts and impacts 29 | - Implement audio manager for weapon sounds with spatial audio 30 | 31 | ## Definition of Done 32 | - Player can fire lasers with left mouse button 33 | - Weapon overheats if used excessively 34 | - Heat gauge is visible and readable in the HUD 35 | - Visual and audio feedback makes shooting feel impactful 36 | - Hit detection works correctly with enemies 37 | - Damage system deducts health from hit targets 38 | 39 | ## Dependencies 40 | - Basic ship movement (Story 1.1) 41 | - Enemy entities implementation 42 | - Particle system 43 | - Audio system -------------------------------------------------------------------------------- /templates/starter/docs/stories/2.2-missile-system.md: -------------------------------------------------------------------------------- 1 | # Story: Missile System 2 | 3 | ## User Story 4 | **As a player,** 5 | I want to **lock on to enemies and fire homing missiles** 6 | So that I can take down distant or fast-moving enemies. 7 | 8 | ## Acceptance Criteria 9 | 10 | ### Targeting & Firing 11 | - Right Mouse Button **locks onto an enemy** in crosshair 12 | - Visual and audio feedback confirms lock-on 13 | - Once locked, pressing **R Key** fires missile 14 | - Limited missile ammo with HUD counter 15 | - Cooldown period between missile launches 16 | 17 | ### Missile Behavior 18 | - Missiles follow targets but can be dodged with sharp maneuvers 19 | - Explosion on impact with **area-of-effect damage** 20 | - Missiles have limited fuel/range 21 | - Missile speed allows skilled players to evade 22 | 23 | ## Technical Implementation Notes 24 | - Implement target selection and locking system 25 | - Create missile entity with: 26 | - Guidance system (PID controller or similar) 27 | - Fuel/lifespan limitation 28 | - Proximity detonation 29 | - Collision detection 30 | - Design missile exhaust particle effects 31 | - Implement explosion effect and area damage calculation 32 | - Add missile inventory and cooldown management 33 | 34 | ## Definition of Done 35 | - Player can lock onto targets and fire missiles 36 | - Missiles track their targets effectively but not perfectly 37 | - Visual indicator shows lock-on status and target 38 | - Missile ammo count is displayed and decremented correctly 39 | - Explosions affect multiple targets in blast radius 40 | - Audio cues provide feedback for lock-on, launch, and detonation 41 | 42 | ## Dependencies 43 | - Basic ship movement (Story 1.1) 44 | - Basic laser firing (Story 2.1) 45 | - Enemy entities implementation 46 | - Particle system 47 | - Audio system -------------------------------------------------------------------------------- /templates/starter/docs/stories/3.1-patrol-enemies.md: -------------------------------------------------------------------------------- 1 | # Story: Patrol Enemies 2 | 3 | ## User Story 4 | **As a developer,** 5 | I want enemies to **follow pre-set patrol routes** 6 | So that the player encounters movement-based threats in space. 7 | 8 | ## Acceptance Criteria 9 | 10 | ### Patrol Behavior 11 | - Enemies follow defined patrol paths or routes 12 | - Patrol routes can be: 13 | - Linear back-and-forth 14 | - Circular 15 | - Complex patterns with multiple points 16 | - Enemies maintain consistent speed during patrol 17 | 18 | ### Detection & Engagement 19 | - Enemies have a detection radius for player ships 20 | - When player enters detection radius, enemies switch to engagement mode 21 | - If player escapes (leaves engagement radius), enemies return to patrol route 22 | - Visual indicator shows when enemies have detected the player 23 | 24 | ## Technical Implementation Notes 25 | - Implement path-following AI using waypoints 26 | - Create detection system with configurable radii for: 27 | - Initial detection 28 | - Engagement continuation 29 | - Giving up pursuit 30 | - Design patrol route editor/configuration system 31 | - Implement state machine for patrol/engage/return behaviors 32 | - Add visual indicators for patrol routes (debug mode) 33 | 34 | ## Definition of Done 35 | - Enemies successfully follow patrol routes 36 | - Enemies detect player when in range 37 | - Enemies engage player when detected 38 | - Enemies return to patrol route when player escapes 39 | - Different patrol patterns can be configured 40 | - Enemy behavior feels natural and predictable 41 | 42 | ## Dependencies 43 | - Enemy ship models and animations 44 | - Basic AI framework 45 | - Movement system -------------------------------------------------------------------------------- /templates/starter/docs/stories/3.2-aggressive-attackers.md: -------------------------------------------------------------------------------- 1 | # Story: Aggressive Attackers 2 | 3 | ## User Story 4 | **As a player,** 5 | I want enemy ships to **actively chase and fire at me** 6 | So that combat feels intense and reactive. 7 | 8 | ## Acceptance Criteria 9 | 10 | ### Engagement Behavior 11 | - Attackers engage as soon as the player is in range 12 | - Enemies pursue player aggressively even if player tries to escape 13 | - Attackers attempt to maintain optimal attack distance 14 | - Enemies try to position themselves behind the player 15 | 16 | ### Combat Tactics 17 | - Fire laser bursts at the player when in range 18 | - Adjust movement to avoid simple attacks 19 | - Multiple attackers coordinate to attack from different angles 20 | - Retreat temporarily if taking heavy damage 21 | 22 | ## Technical Implementation Notes 23 | - Implement pursuit AI with prediction of player movement 24 | - Create combat behavior state machine with states: 25 | - Pursue 26 | - Attack 27 | - Evade 28 | - Reposition 29 | - Design weapon systems for enemy ships 30 | - Implement target leading for accurate firing 31 | - Add threat assessment to coordinate multiple attackers 32 | 33 | ## Definition of Done 34 | - Enemies actively chase player when detected 35 | - Enemies fire weapons when in range and line of sight 36 | - Attackers position themselves strategically 37 | - Multiple enemies coordinate their attacks 38 | - Enemy behavior feels challenging but fair 39 | - Combat feels intense and dynamic 40 | 41 | ## Dependencies 42 | - Patrol enemies (Story 3.1) 43 | - Enemy weapon systems 44 | - AI framework 45 | - Movement system -------------------------------------------------------------------------------- /templates/starter/docs/stories/3.3-evasive-enemies.md: -------------------------------------------------------------------------------- 1 | # Story: Evasive Enemies 2 | 3 | ## User Story 4 | **As a player,** 5 | I want enemies to **dodge my attacks** 6 | So that combat feels more skill-based and dynamic. 7 | 8 | ## Acceptance Criteria 9 | 10 | ### Evasive Maneuvers 11 | - When targeted, enemies **swerve left/right** unpredictably 12 | - If locked-on by a missile, they execute an **emergency roll** or sharp turn 13 | - Smaller, agile enemy types are more evasive than larger ships 14 | - Enemies use boost/afterburners to quickly escape dangerous situations 15 | 16 | ### Detection & Response 17 | - Enemies can detect when they're being targeted by player 18 | - Enemies can detect incoming missiles and attempt evasion 19 | - Response time varies by enemy type/difficulty 20 | - Visual indicators show when an enemy is performing evasive maneuvers 21 | 22 | ## Technical Implementation Notes 23 | - Implement threat detection system for enemies 24 | - Create evasive maneuver library: 25 | - Random swerve patterns 26 | - Emergency rolls 27 | - Boost escapes 28 | - Defensive loops 29 | - Add target prediction to determine optimal evasion direction 30 | - Implement cooldown system for special maneuvers 31 | - Balance evasion vs. aggression based on enemy type 32 | 33 | ## Definition of Done 34 | - Enemies successfully detect when they're targeted 35 | - Enemies perform appropriate evasive maneuvers 36 | - Different enemy types have distinct evasion styles 37 | - Evasion is balanced (not impossible to hit but requires skill) 38 | - Maneuvers look natural and fit the enemy ship type 39 | - Combat feels more challenging and engaging 40 | 41 | ## Dependencies 42 | - Patrol enemies (Story 3.1) 43 | - AI framework 44 | - Movement system 45 | - Player targeting system -------------------------------------------------------------------------------- /templates/starter/docs/stories/3.4-boss-battles.md: -------------------------------------------------------------------------------- 1 | # Story: Boss Battles 2 | 3 | ## User Story 4 | **As a player,** 5 | I want to **fight massive boss enemies** 6 | So that I get a challenging and rewarding gameplay experience. 7 | 8 | ## Acceptance Criteria 9 | 10 | ### Boss Characteristics 11 | - Bosses are significantly larger than regular enemies 12 | - Bosses have multiple attack phases triggered by health thresholds 13 | - Feature **weak points** the player must target for maximum damage 14 | - Have **shield mechanics** that require specific strategies to bypass 15 | 16 | ### Battle Mechanics 17 | - Bosses summon additional enemy reinforcements 18 | - Environmental hazards may be part of the battle 19 | - Boss health/shield status is clearly visible 20 | - Distinct visual/audio cues telegraph different attacks 21 | 22 | ## Technical Implementation Notes 23 | - Design modular boss system with: 24 | - Phase management 25 | - Weak point targeting 26 | - Shield systems 27 | - Minion spawning 28 | - Create distinctive boss models with animated parts 29 | - Implement special effects for boss weapons and abilities 30 | - Design arena-style battle areas with strategic elements 31 | - Add dramatic music and sound effects for boss encounters 32 | 33 | ## Definition of Done 34 | - Boss battles provide a significant challenge 35 | - Multiple attack phases work correctly 36 | - Weak points and shield mechanics function as designed 37 | - Reinforcements spawn at appropriate times 38 | - Visual and audio feedback clearly communicates boss state 39 | - Boss defeat is rewarding and satisfying 40 | - Performance remains stable despite complex boss mechanics 41 | 42 | ## Dependencies 43 | - All enemy AI systems (Stories 3.1, 3.2, 3.3) 44 | - Combat system (Stories 2.1, 2.2) 45 | - Environmental hazards 46 | - Advanced particle effects -------------------------------------------------------------------------------- /templates/starter/docs/stories/4.1-endless-survival-mode.md: -------------------------------------------------------------------------------- 1 | # Story: Endless Survival Mode 2 | 3 | ## User Story 4 | **As a player,** 5 | I want to **fight wave after wave of enemies** 6 | So that I can test my skill and try to set high scores. 7 | 8 | ## Acceptance Criteria 9 | 10 | ### Wave System 11 | - **Waves increase in difficulty** (more enemies, better AI, different types) 12 | - Each wave announces its number and difficulty 13 | - Short breaks between waves for player to recover 14 | - **Every 5 waves**, a **boss appears** 15 | 16 | ### Progression & Rewards 17 | - Player earns **points** for kills and waves survived 18 | - At certain scores, **new weapons or ships unlock** 19 | - Player's highest wave and score are recorded 20 | - Rewards are given for reaching milestones 21 | 22 | ## Technical Implementation Notes 23 | - Implement wave management system: 24 | - Wave definition (enemy types, counts, spawn patterns) 25 | - Difficulty scaling algorithm 26 | - Boss wave integration 27 | - Spawn management 28 | - Create scoring system with: 29 | - Points for different enemy types 30 | - Streak/combo bonuses 31 | - Wave completion bonuses 32 | - Implement unlockable reward system 33 | - Add persistent high score tracking 34 | 35 | ## Definition of Done 36 | - Waves spawn correctly with increasing difficulty 37 | - Score system tracks and displays points accurately 38 | - Bosses appear at designated intervals 39 | - Unlockable rewards are given at appropriate thresholds 40 | - Game difficulty increases in a balanced way 41 | - High scores and achievements are saved between sessions 42 | - UI clearly communicates current wave, score, and upcoming rewards 43 | 44 | ## Dependencies 45 | - Enemy AI systems (Stories 3.1, 3.2, 3.3) 46 | - Boss battles (Story 3.4) 47 | - Combat system (Stories 2.1, 2.2) 48 | - UI framework -------------------------------------------------------------------------------- /templates/starter/docs/stories/5.1-unlockable-ships.md: -------------------------------------------------------------------------------- 1 | # Story: Unlockable Ships 2 | 3 | ## User Story 4 | **As a player,** 5 | I want to **unlock new ships with different stats** 6 | So that I can customize my playstyle. 7 | 8 | ## Acceptance Criteria 9 | 10 | ### Ship Variety 11 | - At least 3-5 distinct ship types with different characteristics 12 | - Ships vary in speed, maneuverability, hull strength, and weapon capacity 13 | - Each ship has a unique visual design and cockpit view 14 | - Ships have different hardpoints for weapons 15 | 16 | ### Unlock System 17 | - Ships unlock based on: 18 | - Score milestones (ex: 10,000 points unlocks a faster ship) 19 | - Boss defeats (defeating specific bosses) 20 | - Playtime achievements (playing for X amount of time) 21 | - Unlocked ships remain available for future games 22 | - Player can select ship before starting a new game 23 | 24 | ## Technical Implementation Notes 25 | - Design ship class system with: 26 | - Base ship class with common functionality 27 | - Extended classes for different ship types 28 | - Configurable parameters for ship characteristics 29 | - Create ship models with: 30 | - Unique meshes and textures 31 | - Appropriate collision meshes 32 | - Cockpit view cameras 33 | - Engine/weapon attachment points 34 | - Implement persistent unlock tracking 35 | - Create ship selection UI 36 | 37 | ## Definition of Done 38 | - Multiple ship types are available with distinct handling characteristics 39 | - Ship unlock conditions work correctly 40 | - Players can select from unlocked ships 41 | - Each ship feels unique and offers different gameplay styles 42 | - Ship selection is saved between sessions 43 | - UI clearly shows which ships are unlocked and which are still locked 44 | 45 | ## Dependencies 46 | - Endless Survival Mode (Story 4.1) 47 | - Basic ship movement (Story 1.1) 48 | - Combat system (Stories 2.1, 2.2) 49 | - Persistence system -------------------------------------------------------------------------------- /templates/starter/docs/stories/5.2-weapon-upgrades.md: -------------------------------------------------------------------------------- 1 | # Story: Weapon Upgrades 2 | 3 | ## User Story 4 | **As a player,** 5 | I want to **earn new weapons over time** 6 | So that I can improve my combat capabilities. 7 | 8 | ## Acceptance Criteria 9 | 10 | ### Weapon Variety 11 | - Multiple weapon types with different characteristics: 12 | - **Standard Laser** (default, balanced) 13 | - **Charge Laser** (powerful but slow) 14 | - **Spread Shot** (fires multiple lasers at once) 15 | - **Cluster Missiles** (fires multiple seeking projectiles) 16 | - **Plasma Cannon** (short range but high damage) 17 | - Each weapon has unique visual and sound effects 18 | - Weapons have different ammo/energy requirements 19 | 20 | ### Upgrade System 21 | - Weapons unlock as rewards for game progression 22 | - Player can swap between unlocked weapons 23 | - Some ships may have compatibility restrictions with certain weapons 24 | - Weapon effectiveness varies based on enemy types 25 | 26 | ## Technical Implementation Notes 27 | - Design weapon class system: 28 | - Base weapon class with common functionality 29 | - Extended classes for different weapon types 30 | - Configurable parameters for damage, rate of fire, etc. 31 | - Create weapon models and effects: 32 | - Projectile meshes and particles 33 | - Muzzle flashes 34 | - Impact effects 35 | - Implement weapon selection and switching 36 | - Create persistent weapon unlock tracking 37 | - Balance weapon stats for fair gameplay 38 | 39 | ## Definition of Done 40 | - Multiple weapon types are available with distinct behaviors 41 | - Weapon unlock conditions work correctly 42 | - Players can select and switch between weapons 43 | - Each weapon is visually distinctive when fired 44 | - Weapon selection is saved between sessions 45 | - Weapons are balanced (different but equally viable) 46 | - UI clearly shows which weapons are unlocked and which are selected 47 | 48 | ## Dependencies 49 | - Basic laser firing (Story 2.1) 50 | - Missile system (Story 2.2) 51 | - Endless Survival Mode (Story 4.1) 52 | - Ship system (Story 5.1) 53 | - Persistence system -------------------------------------------------------------------------------- /templates/starter/docs/stories/6.1-laser-projectile-effects.md: -------------------------------------------------------------------------------- 1 | # Story: Laser and Projectile Effects 2 | 3 | ## User Story 4 | **As a player,** 5 | I want to **see bright, glowing projectiles with impact effects** 6 | So that combat feels visually exciting. 7 | 8 | ## Acceptance Criteria 9 | 10 | ### Projectile Visuals 11 | - Laser shots glow with slight trail effects 12 | - Different weapon types have distinct visual styles: 13 | - Standard lasers: bright, thin beams 14 | - Charge lasers: thick, pulsing energy beams 15 | - Spread shots: multiple thin lasers 16 | - Missiles: solid projectiles with exhaust trails 17 | - Projectiles illuminate surrounding objects while in flight 18 | 19 | ### Impact Effects 20 | - Hits produce **small explosions or shield ripples** 21 | - Different surfaces react differently to impacts: 22 | - Shields show energy ripples 23 | - Hull surfaces show sparks and smoke 24 | - Explosions when hitting critical components 25 | - Impact effects scale with weapon power 26 | 27 | ## Technical Implementation Notes 28 | - Implement shader-based glow effects for energy weapons 29 | - Create particle systems for: 30 | - Weapon trails 31 | - Impact sparks 32 | - Shield ripples 33 | - Explosions 34 | - Use point lights for dynamic illumination from projectiles 35 | - Design impact effect manager that varies based on: 36 | - Surface type 37 | - Weapon type 38 | - Impact angle 39 | - Optimize effects for performance in heavy combat 40 | 41 | ## Definition of Done 42 | - All projectile types have distinctive and attractive visual effects 43 | - Impact effects vary based on surface type 44 | - Effects enhance combat feedback without obscuring gameplay 45 | - Visual effects are consistent with weapon power and type 46 | - Performance remains stable even with many effects on screen 47 | - Effects properly highlight hits and misses 48 | 49 | ## Dependencies 50 | - Basic laser firing (Story 2.1) 51 | - Missile system (Story 2.2) 52 | - Three.js shader system 53 | - Particle system -------------------------------------------------------------------------------- /templates/starter/docs/stories/6.2-explosion-debris.md: -------------------------------------------------------------------------------- 1 | # Story: Explosion and Debris 2 | 3 | ## User Story 4 | **As a player,** 5 | I want enemies to **explode in a satisfying way** 6 | So that combat has weight and impact. 7 | 8 | ## Acceptance Criteria 9 | 10 | ### Explosion Effects 11 | - When an enemy is destroyed: 12 | - Initial bright flash at the center 13 | - **Explosion effect** with expanding fireball 14 | - Secondary smaller explosions 15 | - Shockwave ripple effect 16 | - Explosions scale with size of the destroyed enemy 17 | - Boss explosions are particularly spectacular and multi-staged 18 | 19 | ### Debris System 20 | - **Debris scatters in space** following physics: 21 | - Hull fragments 22 | - Mechanical parts 23 | - Sparking electronics 24 | - Debris continues on trajectory influenced by explosion force 25 | - Debris gradually fades away or disintegrates 26 | - Debris can collide with other objects 27 | 28 | ## Technical Implementation Notes 29 | - Create layered explosion effect system: 30 | - Core flash (sprite/mesh) 31 | - Expanding fireball (particle system) 32 | - Smoke and fire trails (particle system) 33 | - Shockwave (shader effect) 34 | - Implement debris generation: 35 | - Procedural mesh fragmentation 36 | - Pre-designed debris pieces 37 | - Physics simulation for movement 38 | - Add sound design integration: 39 | - Primary explosion sound 40 | - Secondary explosion sounds 41 | - Debris sounds 42 | - Optimize with LOD system for distant explosions 43 | 44 | ## Definition of Done 45 | - Explosions look impactful and satisfying 46 | - Different enemy types have appropriate explosion scales 47 | - Debris behaves physically realistically 48 | - Performance remains stable even with large explosions 49 | - Explosions provide clear feedback on enemy destruction 50 | - Sound and visual effects are synchronized 51 | - Boss explosions feel appropriately climactic 52 | 53 | ## Dependencies 54 | - Laser and projectile effects (Story 6.1) 55 | - Enemy entities 56 | - Physics system 57 | - Particle system 58 | - Sound system -------------------------------------------------------------------------------- /templates/starter/docs/stories/6.3-weapon-muzzle-flash-and-sound-effects.md: -------------------------------------------------------------------------------- 1 | # User Story: Weapon Muzzle Flash and Sound Effects 2 | 3 | ## Description 4 | As a player, I want to see muzzle flashes and hear sound effects when firing weapons to enhance the immersive experience of space combat. 5 | 6 | ## Acceptance Criteria 7 | - [x] Bright muzzle flash effect appears at weapon hardpoints when firing 8 | - [x] Flash illuminates surrounding area with dynamic lighting 9 | - [x] Particle effects for energy discharge are visible 10 | - [x] Laser sound effects play synchronously with firing 11 | - [x] Sound effects use spatial audio for better immersion 12 | - [x] Volume adjusts based on distance and perspective 13 | - [x] Different sound variations play to prevent repetition 14 | - [x] Visual feedback is clear when weapons are fired 15 | - [x] Effects are optimized for performance with instanced rendering 16 | - [x] Resources are properly cleaned up to prevent memory leaks 17 | - [x] Boost sound effect plays when the boost key is pressed 18 | - [x] Damage sound effect plays when the player takes damage 19 | - [x] Explosion sound effect plays when player or enemies are destroyed 20 | 21 | ## Technical Implementation Details 22 | 23 | ### Components 24 | 1. **MuzzleFlash Component** 25 | - Create a new component for rendering muzzle flash effects 26 | - Integrate with the existing WeaponSystem component 27 | - Use Three.js PointLight for dynamic lighting 28 | - Implement particle effects for energy discharge 29 | 30 | 2. **Sound Integration** 31 | - Use the existing SoundControl utility for playing laser sounds 32 | - Implement spatial audio for better immersion 33 | - Add randomization to prevent repetitive sounds 34 | - Ensure proper cleanup of audio resources 35 | - Add boost sound effect with cooldown to prevent sound spam 36 | - Add damage and explosion sound effects 37 | - Implement volume randomization for more natural sound variation 38 | 39 | ### Implementation Steps 40 | 1. Create MuzzleFlash component with visual effects 41 | 2. Add dynamic lighting to illuminate surrounding area 42 | 3. Implement particle effects for energy discharge 43 | 4. Integrate sound effects with the WeaponSystem 44 | 5. Add spatial audio and volume adjustment 45 | 6. Implement sound variations to prevent repetition 46 | 7. Add boost sound effect with cooldown mechanism 47 | 8. Add damage and explosion sound effects 48 | 9. Optimize for performance 49 | 10. Ensure proper cleanup of resources 50 | 51 | ## Dependencies 52 | - WeaponSystem component 53 | - SoundControl utility 54 | - Three.js PointLight 55 | - Particle system 56 | - Sound files: laser.aac, boost.aac, damage.aac, explosion.aac 57 | 58 | ## Notes 59 | - Muzzle flash should be visible but not overwhelming 60 | - Sound effects should be satisfying but not annoying when firing rapidly 61 | - Performance is critical as these effects will be triggered frequently 62 | - Consider using instanced rendering for particle effects 63 | - Ensure proper cleanup to prevent memory leaks 64 | - Implement cooldown for boost sound to prevent sound spam 65 | - Use volume randomization for more natural sound variation 66 | 67 | ## References 68 | - [Three.js PointLight Documentation](https://threejs.org/docs/#api/en/lights/PointLight) 69 | - [Web Audio API Spatial Audio](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Web_audio_spatialization_basics) -------------------------------------------------------------------------------- /templates/starter/docs/stories/7.1-dynamic-sound-effects.md: -------------------------------------------------------------------------------- 1 | # Story: Dynamic Sound Effects 2 | 3 | ## User Story 4 | **As a player,** 5 | I want each action to have **clear and satisfying sounds** 6 | So that gameplay feels immersive. 7 | 8 | ## Acceptance Criteria 9 | 10 | ### Sound Categories 11 | - **Ship sounds**: engines, thrusters, boost, braking 12 | - **Weapon sounds**: laser blasts, missile launches, impacts 13 | - **Explosion sounds**: varying by size and intensity 14 | - **UI sounds**: button clicks, alerts, notifications 15 | - **Environmental sounds**: ambient space, nearby objects 16 | 17 | ### Audio Features 18 | - Positional audio: **hear enemies approaching from behind** 19 | - Volume varies by distance and object size 20 | - **Alarm sounds** when shields are low or missiles locked 21 | - Sound variations prevent repetition fatigue 22 | - Doppler effect for fast-moving objects 23 | 24 | ## Technical Implementation Notes 25 | - Implement spatial audio system using Three.js audio capabilities 26 | - Create sound manager with: 27 | - Sound pools for variation 28 | - Priority system for audio mixing 29 | - Distance-based attenuation 30 | - Dynamic filtering based on environment 31 | - Design sound effects with: 32 | - Consistent aesthetic 33 | - Clear differentiation between types 34 | - Appropriate frequency ranges for simultaneous sounds 35 | - Implement audio accessibility options 36 | 37 | ## Definition of Done 38 | - All game actions have appropriate sound effects 39 | - Positional audio correctly indicates direction of objects 40 | - Sound levels are balanced and mixed properly 41 | - Performance remains stable with many sound sources 42 | - Alarm sounds clearly communicate danger 43 | - Audio enhances gameplay without being distracting 44 | - Sound system handles many simultaneous sources gracefully 45 | 46 | ## Dependencies 47 | - Ship movement system 48 | - Weapon systems 49 | - Explosion effects 50 | - UI system 51 | - WebAudio API integration -------------------------------------------------------------------------------- /templates/starter/docs/stories/7.2-adaptive-music-system.md: -------------------------------------------------------------------------------- 1 | # Story: Adaptive Music System 2 | 3 | ## User Story 4 | **As a player,** 5 | I want music to **intensify during combat** and **fade when calm** 6 | So that gameplay feels dynamic. 7 | 8 | ## Acceptance Criteria 9 | 10 | ### Music States 11 | - Peaceful ambient music when exploring 12 | - **Fast-paced combat music** when enemies appear 13 | - **Boss battles have unique tracks** 14 | - Smooth transitions between music states 15 | - Victory and defeat music cues 16 | 17 | ### Adaptive Features 18 | - Music layers fade in/out based on proximity to enemies 19 | - Tension builds as more enemies appear 20 | - Music responds to player health status 21 | - Subtle audio cues telegraph incoming threats 22 | - Music system remembers and varies track selection 23 | 24 | ## Technical Implementation Notes 25 | - Implement layered music system: 26 | - Base ambient layer always present 27 | - Combat layers that fade in/out 28 | - Percussion and intensity layers based on threat level 29 | - Create music state machine: 30 | - Exploration state 31 | - Combat awareness state 32 | - Full combat state 33 | - Boss battle state 34 | - Victory/defeat states 35 | - Design smooth crossfading between tracks 36 | - Implement dynamic volume mixing based on gameplay intensity 37 | 38 | ## Definition of Done 39 | - Music system responds correctly to gameplay situations 40 | - Transitions between music states are smooth and natural 41 | - Combat feels more intense with appropriate music 42 | - Boss battles feel epic with their unique themes 43 | - Music enhances emotional impact without distracting 44 | - Performance remains stable during music transitions 45 | - Music variety prevents repetition fatigue during long sessions 46 | 47 | ## Dependencies 48 | - Dynamic sound effects (Story 7.1) 49 | - Enemy detection system 50 | - Boss battle system 51 | - Game state tracking -------------------------------------------------------------------------------- /templates/starter/docs/stories/8.1-optimized-web-performance.md: -------------------------------------------------------------------------------- 1 | # Story: Optimized Web Performance 2 | 3 | ## User Story 4 | **As a developer,** 5 | I want to **optimize assets and rendering** 6 | So that the game runs smoothly in a browser. 7 | 8 | ## Acceptance Criteria 9 | 10 | ### Performance Targets 11 | - Maintain 60 FPS on mid-range hardware 12 | - Initial load time under 10 seconds 13 | - Minimal frame drops during intense combat 14 | - Graceful degradation on lower-end devices 15 | - Memory usage remains stable during gameplay 16 | 17 | ### Optimization Techniques 18 | - **Efficient rendering** of distant objects 19 | - **LOD (Level of Detail) scaling** for enemies and debris 20 | - **WebGL optimizations** to maintain high frame rates 21 | - Asset compression and streaming 22 | - Intelligent object pooling for projectiles and effects 23 | 24 | ## Technical Implementation Notes 25 | - Implement LOD system: 26 | - High-detail models for close-up view 27 | - Medium-detail for medium distance 28 | - Low-poly or billboards for distant objects 29 | - Create performance monitoring tools: 30 | - FPS counter 31 | - Memory usage tracking 32 | - Render time profiling 33 | - Optimize shader complexity based on device capability 34 | - Implement object pooling for frequently created/destroyed objects: 35 | - Projectiles 36 | - Explosions 37 | - Debris 38 | - Design asset streaming system for progressive loading 39 | 40 | ## Definition of Done 41 | - Game maintains target frame rate during normal gameplay 42 | - Heavy combat scenes don't cause significant slowdown 43 | - Loading times are reasonable on average connections 44 | - Lower-end devices can run the game with reduced settings 45 | - Memory usage remains stable without leaks 46 | - Performance monitoring tools provide useful diagnostics 47 | - Asset compression achieves good balance of quality and size 48 | 49 | ## Dependencies 50 | - Three.js/React Three Fiber setup 51 | - Asset pipeline 52 | - Rendering system 53 | - WebGL knowledge -------------------------------------------------------------------------------- /templates/starter/docs/stories/8.2-controls.md: -------------------------------------------------------------------------------- 1 | # Story: Controls 2 | 3 | ## User Story 4 | **As a player,** 5 | I want intuitive keyboard/mouse controls 6 | So that piloting my ship feels natural. 7 | 8 | ## Acceptance Criteria 9 | 10 | ### Control Schemes 11 | - Default **WASD + mouse aim** setup: 12 | - W/S: Forward/backward thrust 13 | - A/D: Strafe left/right 14 | - Mouse: Pitch and yaw 15 | - Q/E: Roll left/right 16 | - Shift: Brake 17 | - Space: Boost 18 | - Left Mouse: Fire primary weapon 19 | - Right Mouse: Target lock/secondary function 20 | - R: Fire missile when locked 21 | 22 | ### Input Features 23 | - **Gamepad support** for enhanced experience 24 | - Customizable key bindings 25 | - Input sensitivity settings 26 | - Visual control reference/help screen 27 | - Toggleable control modes (e.g., inverted Y-axis) 28 | 29 | ## Technical Implementation Notes 30 | - Implement input management system: 31 | - Keyboard input handling 32 | - Mouse input with sensitivity control 33 | - Gamepad API integration 34 | - Input binding configuration 35 | - Create control settings UI: 36 | - Key rebinding interface 37 | - Sensitivity sliders 38 | - Control scheme selection 39 | - Design visual feedback for controls: 40 | - HUD indicators for thrust, roll, etc. 41 | - Control reference overlay 42 | - Implement control state persistence 43 | 44 | ## Definition of Done 45 | - Controls feel responsive and intuitive 46 | - Gamepad works correctly for all ship functions 47 | - Key bindings can be customized and saved 48 | - Control settings UI is usable and clear 49 | - Control help screen shows current key bindings 50 | - Input system handles simultaneous inputs correctly 51 | - Control preferences persist between sessions 52 | 53 | ## Dependencies 54 | - Input system 55 | - UI framework 56 | - Ship movement system (Story 1.1) 57 | - Persistence system -------------------------------------------------------------------------------- /templates/starter/docs/tasklist.yaml: -------------------------------------------------------------------------------- 1 | stories: 2 | # Movement Stories 3 | - id: "1.1" 4 | title: "Basic Ship Movement" 5 | file: "docs/stories/1.1-basic-ship-movement.md" 6 | status: "Todo" 7 | - id: "1.2" 8 | title: "Advanced Flight Maneuvers" 9 | file: "docs/stories/1.2-advanced-flight-maneuvers.md" 10 | status: "Todo" 11 | 12 | # Combat Stories 13 | - id: "2.1" 14 | title: "Basic Laser Firing" 15 | file: "docs/stories/2.1-basic-laser-firing.md" 16 | status: "Todo" 17 | - id: "2.2" 18 | title: "Missile System" 19 | file: "docs/stories/2.2-missile-system.md" 20 | status: "Todo" 21 | 22 | # Enemy Stories 23 | - id: "3.1" 24 | title: "Patrol Enemies" 25 | file: "docs/stories/3.1-patrol-enemies.md" 26 | status: "Todo" 27 | - id: "3.2" 28 | title: "Aggressive Attackers" 29 | file: "docs/stories/3.2-aggressive-attackers.md" 30 | status: "Todo" 31 | - id: "3.3" 32 | title: "Evasive Enemies" 33 | file: "docs/stories/3.3-evasive-enemies.md" 34 | status: "Todo" 35 | - id: "3.4" 36 | title: "Boss Battles" 37 | file: "docs/stories/3.4-boss-battles.md" 38 | status: "Todo" 39 | 40 | # Game Modes 41 | - id: "4.1" 42 | title: "Endless Survival Mode" 43 | file: "docs/stories/4.1-endless-survival-mode.md" 44 | status: "Todo" 45 | 46 | # Progression Stories 47 | - id: "5.1" 48 | title: "Unlockable Ships" 49 | file: "docs/stories/5.1-unlockable-ships.md" 50 | status: "Todo" 51 | - id: "5.2" 52 | title: "Weapon Upgrades" 53 | file: "docs/stories/5.2-weapon-upgrades.md" 54 | status: "Todo" 55 | 56 | # Visual Effects Stories 57 | - id: "6.1" 58 | title: "Laser Projectile Effects" 59 | file: "docs/stories/6.1-laser-projectile-effects.md" 60 | status: "Todo" 61 | - id: "6.2" 62 | title: "Explosion Debris" 63 | file: "docs/stories/6.2-explosion-debris.md" 64 | status: "Todo" 65 | - id: "6.3" 66 | title: "Weapon Muzzle Flash and Sound Effects" 67 | file: "docs/stories/6.3-weapon-muzzle-flash-and-sound-effects.md" 68 | status: "Todo" 69 | 70 | # Audio Stories 71 | - id: "7.1" 72 | title: "Dynamic Sound Effects" 73 | file: "docs/stories/7.1-dynamic-sound-effects.md" 74 | status: "Todo" 75 | - id: "7.2" 76 | title: "Adaptive Music System" 77 | file: "docs/stories/7.2-adaptive-music-system.md" 78 | status: "Todo" 79 | 80 | # Technical Stories 81 | - id: "8.1" 82 | title: "Optimized Web Performance" 83 | file: "docs/stories/8.1-optimized-web-performance.md" 84 | status: "Todo" 85 | - id: "8.2" 86 | title: "Controls" 87 | file: "docs/stories/8.2-controls.md" 88 | status: "Todo" -------------------------------------------------------------------------------- /templates/starter/eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js'; 2 | import globals from 'globals'; 3 | import reactHooks from 'eslint-plugin-react-hooks'; 4 | import reactRefresh from 'eslint-plugin-react-refresh'; 5 | import tseslint from 'typescript-eslint'; 6 | 7 | export default tseslint.config( 8 | { ignores: ['dist'] }, 9 | { 10 | extends: [js.configs.recommended, ...tseslint.configs.recommended], 11 | files: ['**/*.{ts,tsx}'], 12 | languageOptions: { 13 | ecmaVersion: 2020, 14 | globals: globals.browser, 15 | }, 16 | plugins: { 17 | 'react-hooks': reactHooks, 18 | 'react-refresh': reactRefresh, 19 | }, 20 | rules: { 21 | ...reactHooks.configs.recommended.rules, 22 | 'react-refresh/only-export-components': [ 23 | 'warn', 24 | { allowConstantExport: true }, 25 | ], 26 | }, 27 | } 28 | ); 29 | -------------------------------------------------------------------------------- /templates/starter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Viber3D 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /templates/starter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@viber3d/starter-template", 3 | "version": "0.0.0", 4 | "description": "A modern Game Starter Kit for 3D games with react", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint .", 10 | "clean": "npx rimraf node_modules && npm install", 11 | "preview": "vite preview" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/instructa/viber3d" 16 | }, 17 | "keywords": [ 18 | "viber3d", 19 | "cli", 20 | "three", 21 | "threejs", 22 | "project-setup", 23 | "cursor", 24 | "cursor-ai", 25 | "ai-coding", 26 | "instructa" 27 | ], 28 | "author": "Kevin Kern", 29 | "license": "MIT", 30 | "dependencies": { 31 | "koota": "^0.1.12", 32 | "@react-three/drei": "^9.120.8", 33 | "@react-three/fiber": "^8.17.12", 34 | "@react-three/rapier": "^1.5.0", 35 | "@tailwindcss/vite": "^4.0.9", 36 | "@react-three/postprocessing": "^2.16.6", 37 | "leva": "^0.10.0", 38 | "react": "^18.3.1", 39 | "react-animated-counter": "^1.7.9", 40 | "react-dom": "^18.3.1", 41 | "tailwindcss": "^4.0.9", 42 | "three": "^0.173.0", 43 | "typescript-eslint": "^8.25.0" 44 | }, 45 | "devDependencies": { 46 | "@eslint/js": "^9.21.0", 47 | "@types/react": "^18.3.18", 48 | "@types/react-dom": "^18.3.5", 49 | "@types/three": "^0.173.0", 50 | "@vitejs/plugin-react": "^4.3.4", 51 | "eslint": "^9.21.0", 52 | "eslint-plugin-react": "^7.37.4", 53 | "eslint-plugin-react-hooks": "^5.1.0", 54 | "globals": "^16.0.0", 55 | "typescript": "^5.7.3", 56 | "@vitejs/plugin-react-swc": "^3.7.2", 57 | "eslint-plugin-react-refresh": "^0.4.18", 58 | "typescript-eslint": "^8.20.0", 59 | "vite": "^6.2.0" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /templates/starter/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructa/viber3d/ad11dfd3ca621f1d3adb4412aa32ea8a12c053db/templates/starter/public/favicon.ico -------------------------------------------------------------------------------- /templates/starter/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructa/viber3d/ad11dfd3ca621f1d3adb4412aa32ea8a12c053db/templates/starter/public/favicon.png -------------------------------------------------------------------------------- /templates/starter/src/actions.ts: -------------------------------------------------------------------------------- 1 | import { createActions } from 'koota'; 2 | import * as THREE from 'three'; 3 | import { IsPlayer, Transform, IsCamera } from './traits'; 4 | 5 | export const actions = createActions((world) => ({ 6 | spawnPlayer: () => world.spawn(IsPlayer, Transform), 7 | spawnCamera: (position: [number, number, number]) => { 8 | return world.spawn(Transform({ position: new THREE.Vector3(...position) }), IsCamera); 9 | }, 10 | })); 11 | -------------------------------------------------------------------------------- /templates/starter/src/app.tsx: -------------------------------------------------------------------------------- 1 | import { Canvas } from '@react-three/fiber'; 2 | import { CameraRenderer } from './components/camera-renderer'; 3 | import { PlayerRenderer } from './components/player-renderer'; 4 | import { GameLoop } from './frameloop'; 5 | import { Startup } from './startup'; 6 | import { Color } from 'three'; 7 | 8 | export function App() { 9 | return ( 10 | <> 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /templates/starter/src/assets/ships/enemy.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructa/viber3d/ad11dfd3ca621f1d3adb4412aa32ea8a12c053db/templates/starter/src/assets/ships/enemy.glb -------------------------------------------------------------------------------- /templates/starter/src/assets/ships/fighter.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructa/viber3d/ad11dfd3ca621f1d3adb4412aa32ea8a12c053db/templates/starter/src/assets/ships/fighter.glb -------------------------------------------------------------------------------- /templates/starter/src/assets/sounds/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instructa/viber3d/ad11dfd3ca621f1d3adb4412aa32ea8a12c053db/templates/starter/src/assets/sounds/.gitkeep -------------------------------------------------------------------------------- /templates/starter/src/components/camera-renderer.tsx: -------------------------------------------------------------------------------- 1 | import { useQueryFirst } from 'koota/react'; 2 | import { IsCamera, Ref, Transform } from '../traits'; 3 | import { PerspectiveCamera } from '@react-three/drei'; 4 | import { Entity } from 'koota'; 5 | import { ComponentRef, useCallback } from 'react'; 6 | 7 | function CameraView({ entity }: { entity: Entity }) { 8 | const setInitial = useCallback( 9 | (camera: ComponentRef | null) => { 10 | if (!camera) return; 11 | entity.add(Ref(camera)); 12 | }, 13 | [entity] 14 | ); 15 | 16 | return ; 17 | } 18 | 19 | export function CameraRenderer() { 20 | const camera = useQueryFirst(IsCamera, Transform); 21 | if (!camera) return null; 22 | return ; 23 | } 24 | -------------------------------------------------------------------------------- /templates/starter/src/components/player-renderer.tsx: -------------------------------------------------------------------------------- 1 | import { useGLTF } from '@react-three/drei'; 2 | import { Entity } from 'koota'; 3 | import { useQueryFirst } from 'koota/react'; 4 | import { useRef, MutableRefObject, useCallback } from 'react'; 5 | import * as THREE from 'three'; 6 | import { Group } from 'three'; 7 | import src from '../assets/ships/fighter.glb?url'; 8 | import { IsPlayer, Transform, Ref } from '../traits'; 9 | 10 | export function PlayerView({ entity }: { entity: Entity }) { 11 | const { scene } = useGLTF(src); 12 | const groupRef = useRef(null) as MutableRefObject; 13 | 14 | // Set up initial state with useCallback 15 | const setInitial = useCallback( 16 | (group: Group | null) => { 17 | if (!group) return; 18 | groupRef.current = group; 19 | 20 | // Initialize with default position at origin 21 | entity.add(Ref(scene)); 22 | if (!entity.has(Transform)) { 23 | entity.set(Transform, { 24 | position: new THREE.Vector3(0, 0, 0), 25 | rotation: new THREE.Euler(0, 0, 0), 26 | scale: new THREE.Vector3(1, 1, 1), 27 | }); 28 | } 29 | }, 30 | [entity, scene] 31 | ); 32 | 33 | return ( 34 | 35 | 36 | 37 | ); 38 | } 39 | 40 | // Query for the first player entity and render it 41 | export function PlayerRenderer() { 42 | const player = useQueryFirst(IsPlayer, Transform); 43 | return player ? : null; 44 | } 45 | -------------------------------------------------------------------------------- /templates/starter/src/components/postprocessing.tsx: -------------------------------------------------------------------------------- 1 | import { Bloom, EffectComposer, SMAA } from '@react-three/postprocessing'; 2 | 3 | export function PostProcessing() { 4 | return ( 5 | 6 | 13 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /templates/starter/src/frameloop.ts: -------------------------------------------------------------------------------- 1 | import { useFrame } from '@react-three/fiber'; 2 | import { useWorld } from 'koota/react'; 3 | import { syncView } from './systems/sync-view'; 4 | import { updateTime } from './systems/update-time'; 5 | import { updatePlayerRotation } from './systems/update-player-rotation'; 6 | 7 | export function GameLoop() { 8 | const world = useWorld(); 9 | 10 | useFrame(() => { 11 | // Start 12 | updateTime(world); 13 | 14 | // Update game state 15 | updatePlayerRotation(world); 16 | 17 | // Sync view state 18 | syncView(world); 19 | }); 20 | 21 | return null; 22 | } 23 | -------------------------------------------------------------------------------- /templates/starter/src/main.tsx: -------------------------------------------------------------------------------- 1 | // src/main.tsx 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom/client'; 4 | import './styles.css'; 5 | import { App } from './app'; 6 | import { WorldProvider } from 'koota/react'; 7 | import { world } from './world'; 8 | 9 | // Create root & render 10 | ReactDOM.createRoot(document.getElementById('root')!).render( 11 | 12 | 13 | 14 | 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /templates/starter/src/startup.tsx: -------------------------------------------------------------------------------- 1 | import { useFrame } from '@react-three/fiber'; 2 | import { useActions, useWorld } from 'koota/react'; 3 | import { useEffect } from 'react'; 4 | import { actions } from './actions'; 5 | import { updateSpatialHashing } from './systems/update-spatial-hashing'; 6 | 7 | export function Startup({ 8 | initialCameraPosition = [0, 0, 50], 9 | }: { 10 | initialCameraPosition?: [number, number, number]; 11 | }) { 12 | const { spawnPlayer, spawnCamera } = useActions(actions); 13 | const world = useWorld(); 14 | 15 | useEffect(() => { 16 | // Spawn camera 17 | spawnCamera(initialCameraPosition); 18 | 19 | // Spawn player (without movement) 20 | const player = spawnPlayer(); 21 | 22 | return () => { 23 | player.destroy(); 24 | }; 25 | }, [spawnPlayer, spawnCamera, initialCameraPosition]); 26 | 27 | useFrame(() => { 28 | updateSpatialHashing(world); 29 | }); 30 | 31 | return null; 32 | } 33 | -------------------------------------------------------------------------------- /templates/starter/src/styles.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | * { 4 | box-sizing: border-box; 5 | } 6 | 7 | html, 8 | body, 9 | #root { 10 | margin: 0; 11 | padding: 0; 12 | width: 100%; 13 | height: 100%; 14 | overflow: hidden; 15 | } 16 | 17 | body { 18 | background-color: #000; 19 | } 20 | -------------------------------------------------------------------------------- /templates/starter/src/systems/apply-force.ts: -------------------------------------------------------------------------------- 1 | import { World } from 'koota'; 2 | import { Movement } from '../traits'; 3 | 4 | export function applyForce(world: World) { 5 | world.query(Movement).updateEach(([{ force, velocity }]) => { 6 | velocity.add(force); 7 | 8 | // Damp force 9 | if (force.length() > 0.01) { 10 | force.multiplyScalar(1 - 0.1); 11 | } else { 12 | force.setScalar(0); 13 | } 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /templates/starter/src/systems/apply-input.ts: -------------------------------------------------------------------------------- 1 | import { World } from 'koota'; 2 | import { Input, Movement, Time, Transform } from '../traits'; 3 | import * as THREE from 'three'; 4 | 5 | const MOUSE_SENSITIVITY = 0.005; // Reduced sensitivity to minimize direct rotation 6 | 7 | /** 8 | * convertInputToMovement: 9 | * Applies mouse pitch & yaw, forward & strafe thrust, 10 | * brake damping, and optional boost factor. 11 | */ 12 | export function convertInputToMovement(world: World) { 13 | const { delta } = world.get(Time)!; 14 | 15 | world.query(Input, Transform, Movement).updateEach(([input, transform, movement]) => { 16 | const { velocity, thrust } = movement; 17 | 18 | transform.rotation.x -= input.mouseDelta.y * MOUSE_SENSITIVITY; 19 | transform.rotation.y -= input.mouseDelta.x * MOUSE_SENSITIVITY; 20 | 21 | // Apply roll based on Q/R keys 22 | const ROLL_SPEED = 3.0 * delta; // Adjust roll speed as needed 23 | if (input.roll !== 0) { 24 | transform.rotation.z += input.roll * ROLL_SPEED; 25 | } 26 | 27 | // (No clamp => full freedom to loop or look up/down as in No Man's Sky) 28 | 29 | // 2) Compute local directions 30 | const forwardDir = new THREE.Vector3(0, 0, -1).applyEuler(transform.rotation); 31 | const strafeDir = new THREE.Vector3(1, 0, 0).applyEuler(transform.rotation); 32 | 33 | // 3) Determine thrust force 34 | const boostFactor = input.boost ? 2 : 1; 35 | const thrustForce = thrust * delta * 100 * boostFactor; 36 | 37 | // Forward/back 38 | velocity.addScaledVector(forwardDir, input.forward * thrustForce); 39 | 40 | // Strafe left/right 41 | velocity.addScaledVector(strafeDir, input.strafe * thrustForce); 42 | 43 | // 4) Brake 44 | if (input.brake) { 45 | // Slight damping each frame if S is pressed 46 | velocity.multiplyScalar(0.98); 47 | } 48 | }); 49 | } 50 | -------------------------------------------------------------------------------- /templates/starter/src/systems/camera-follow-player.ts: -------------------------------------------------------------------------------- 1 | import { World } from 'koota'; 2 | import { IsCamera, IsPlayer, Transform } from '../traits'; 3 | import * as THREE from 'three'; 4 | 5 | // Camera configuration for space game feel 6 | const CAMERA_CONFIG = { 7 | // Base offset from player (slightly above and behind) 8 | offset: new THREE.Vector3(0, 2, 8), 9 | // How far the camera looks ahead of the player's movement 10 | lookAheadDistance: 10, 11 | // How much the camera rotates with the player 12 | rotationInfluence: 0.7, 13 | // Damping factors for smooth transitions 14 | positionDamping: 0.05, 15 | rotationDamping: 0.08, 16 | // Limits for camera movement 17 | minDistance: 5, 18 | maxDistance: 12, 19 | }; 20 | 21 | export const cameraFollowPlayer = (world: World) => { 22 | const player = world.queryFirst(IsPlayer, Transform); 23 | if (!player) return; 24 | 25 | const playerTransform = player.get(Transform)!; 26 | 27 | // Calculate the desired camera position 28 | const offsetRotated = CAMERA_CONFIG.offset.clone().applyEuler( 29 | new THREE.Euler( 30 | playerTransform.rotation.x * CAMERA_CONFIG.rotationInfluence, 31 | playerTransform.rotation.y * CAMERA_CONFIG.rotationInfluence, 32 | 0 // Don't roll with the player 33 | ) 34 | ); 35 | 36 | world.query(IsCamera, Transform).updateEach(([cameraTransform]) => { 37 | // Calculate target position with look-ahead 38 | const playerForward = new THREE.Vector3(0, 0, -1) 39 | .applyEuler(playerTransform.rotation) 40 | .multiplyScalar(CAMERA_CONFIG.lookAheadDistance); 41 | 42 | const targetPosition = new THREE.Vector3().copy(playerTransform.position).add(offsetRotated); 43 | 44 | // Calculate look-at point (ahead of the player) 45 | const lookAtPoint = new THREE.Vector3().copy(playerTransform.position).add(playerForward); 46 | 47 | // Smoothly move camera position 48 | cameraTransform.position.lerp(targetPosition, CAMERA_CONFIG.positionDamping); 49 | 50 | // Calculate and apply camera rotation 51 | const targetRotation = new THREE.Quaternion().setFromRotationMatrix( 52 | new THREE.Matrix4().lookAt(cameraTransform.position, lookAtPoint, new THREE.Vector3(0, 1, 0)) 53 | ); 54 | 55 | // Apply smooth rotation using quaternion slerp 56 | const currentQuat = new THREE.Quaternion().setFromEuler(cameraTransform.rotation); 57 | currentQuat.slerp(targetRotation, CAMERA_CONFIG.rotationDamping); 58 | cameraTransform.rotation.setFromQuaternion(currentQuat); 59 | 60 | // Ensure camera stays within distance limits 61 | const distanceToPlayer = cameraTransform.position.distanceTo(playerTransform.position); 62 | if (distanceToPlayer < CAMERA_CONFIG.minDistance || distanceToPlayer > CAMERA_CONFIG.maxDistance) { 63 | const idealDistance = THREE.MathUtils.clamp( 64 | distanceToPlayer, 65 | CAMERA_CONFIG.minDistance, 66 | CAMERA_CONFIG.maxDistance 67 | ); 68 | const direction = cameraTransform.position 69 | .clone() 70 | .sub(playerTransform.position) 71 | .normalize() 72 | .multiplyScalar(idealDistance); 73 | cameraTransform.position.copy(playerTransform.position).add(direction); 74 | } 75 | }); 76 | }; 77 | -------------------------------------------------------------------------------- /templates/starter/src/systems/limit-speed.ts: -------------------------------------------------------------------------------- 1 | import { World } from 'koota'; 2 | import { Movement, MaxSpeed } from '../traits'; 3 | 4 | export function limitSpeed(world: World) { 5 | // Query the relevant entities 6 | world.query(Movement, MaxSpeed).updateEach(([{ velocity }, { maxSpeed }]) => { 7 | velocity.clampLength(0, maxSpeed) 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /templates/starter/src/systems/move-entities.ts: -------------------------------------------------------------------------------- 1 | import { World } from 'koota'; 2 | import { Transform, Movement, Time } from '../traits'; 3 | 4 | export function moveEntities(world: World) { 5 | // Get the delta time from the world clock 6 | const { delta } = world.get(Time)!; 7 | 8 | // Query the relevant entities 9 | const results = world.query(Transform, Movement); 10 | 11 | // Update the data of each entity 12 | results.updateEach(([{ position }, { velocity, damping }]) => { 13 | // Move the position by the velocity for a slice of time 14 | position.x += velocity.x * delta; 15 | position.y += velocity.y * delta; 16 | position.z += velocity.z * delta; 17 | 18 | // Damp the velocity 19 | velocity.multiplyScalar(damping); 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /templates/starter/src/systems/sync-view.ts: -------------------------------------------------------------------------------- 1 | import { World } from 'koota'; 2 | import { Transform, Ref } from '../traits'; 3 | 4 | export function syncView(world: World) { 5 | world.query(Transform, Ref).updateEach(([transform, view]) => { 6 | view.position.copy(transform.position); 7 | view.rotation.copy(transform.rotation); 8 | view.scale.copy(transform.scale); 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /templates/starter/src/systems/update-player-rotation.ts: -------------------------------------------------------------------------------- 1 | import { World } from 'koota'; 2 | import { Transform, IsPlayer, Time } from '../traits'; 3 | 4 | export const updatePlayerRotation = (world: World) => { 5 | const { delta } = world.get(Time)!; 6 | 7 | world.query(IsPlayer, Transform).updateEach(([transform]) => { 8 | // Use delta for frame-independent rotation 9 | transform.rotation.y += delta * 0.5; 10 | }); 11 | }; 12 | -------------------------------------------------------------------------------- /templates/starter/src/systems/update-spatial-hashing.ts: -------------------------------------------------------------------------------- 1 | import { createRemoved, World } from 'koota'; 2 | import { SpatialHashMap, Transform } from '../traits'; 3 | 4 | const Removed = createRemoved(); 5 | 6 | export const updateSpatialHashing = (world: World) => { 7 | const spatialHashMap = world.get(SpatialHashMap); 8 | 9 | // Add entities to the spatial hash map 10 | world.query(Transform).updateEach(([{ position }], entity) => { 11 | spatialHashMap!.setEntity(entity, position.x, position.y, position.z); 12 | }); 13 | 14 | // Remove entities from the spatial hash map 15 | world.query(Removed(Transform)).forEach((entity) => { 16 | spatialHashMap!.removeEntity(entity); 17 | }); 18 | }; 19 | -------------------------------------------------------------------------------- /templates/starter/src/systems/update-time.ts: -------------------------------------------------------------------------------- 1 | import { World } from 'koota'; 2 | import { Time } from '../traits'; 3 | 4 | export function updateTime(world: World) { 5 | const time = world.get(Time)!; 6 | 7 | if (time.current === 0) time.current = performance.now(); 8 | 9 | const now = performance.now(); 10 | const delta = now - time.current; 11 | 12 | time.delta = Math.min(delta / 1000, 1 / 30); 13 | time.current = now; 14 | 15 | world.set(Time, time); 16 | } 17 | -------------------------------------------------------------------------------- /templates/starter/src/traits/index.ts: -------------------------------------------------------------------------------- 1 | export * from './input'; 2 | export * from './is-player'; 3 | export * from './is-camera'; 4 | export * from './movement'; 5 | export * from './transform'; 6 | export * from './time'; 7 | export * from './spatial-hash-map'; 8 | export * from './ref'; 9 | export * from './maxSpeed'; 10 | -------------------------------------------------------------------------------- /templates/starter/src/traits/input.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | import * as THREE from 'three'; 3 | 4 | /** 5 | * Input trait for free-roam flight: 6 | * - forward: +1 (W) or 0 (none) 7 | * - strafe: +1 (D), -1 (A), or 0 8 | * - boost: true when Space is held 9 | * - brake: true when S is held 10 | * - mouseDelta: frame-by-frame mouse movement (x=Yaw, y=Pitch) 11 | * - roll: +1 (R), -1 (Q), or 0 for rolling the ship 12 | */ 13 | export const Input = trait({ 14 | forward: 0, 15 | strafe: 0, 16 | boost: false, 17 | brake: false, 18 | roll: 0, // +1 for roll right (R), -1 for roll left (Q) 19 | mouseDelta: () => new THREE.Vector2(), 20 | }); 21 | 22 | // export const Input = trait(() => new THREE.Vector2()); 23 | -------------------------------------------------------------------------------- /templates/starter/src/traits/is-camera.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | 3 | export const IsCamera = trait(); 4 | -------------------------------------------------------------------------------- /templates/starter/src/traits/is-player.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | 3 | export const IsPlayer = trait(); 4 | -------------------------------------------------------------------------------- /templates/starter/src/traits/maxSpeed.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | 3 | export const MaxSpeed = trait({ 4 | maxSpeed: 1, 5 | }); -------------------------------------------------------------------------------- /templates/starter/src/traits/movement.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | import * as THREE from 'three'; 3 | 4 | export const Movement = trait({ 5 | velocity: () => new THREE.Vector3(), 6 | thrust: 1, 7 | damping: 0.95, 8 | force: () => new THREE.Vector3(), 9 | }); 10 | -------------------------------------------------------------------------------- /templates/starter/src/traits/ref.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | import * as THREE from 'three'; 3 | 4 | export const Ref = trait(() => new THREE.Object3D()); 5 | -------------------------------------------------------------------------------- /templates/starter/src/traits/spatial-hash-map.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | import { SpatialHashMap as SpatialHashMapImpl } from '../utils/spatial-hash'; 3 | 4 | export const SpatialHashMap = trait(() => new SpatialHashMapImpl(50)); 5 | -------------------------------------------------------------------------------- /templates/starter/src/traits/time.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | 3 | export const Time = trait({ delta: 0, current: 0 }); 4 | -------------------------------------------------------------------------------- /templates/starter/src/traits/transform.ts: -------------------------------------------------------------------------------- 1 | import { trait } from 'koota'; 2 | import * as THREE from 'three'; 3 | 4 | // A transform just like CSS! 5 | export const Transform = trait({ 6 | position: () => new THREE.Vector3(), 7 | rotation: () => new THREE.Euler(), 8 | scale: () => new THREE.Vector3(1, 1, 1), 9 | }); 10 | -------------------------------------------------------------------------------- /templates/starter/src/utils/between.ts: -------------------------------------------------------------------------------- 1 | export function between(min: number, max: number): number { 2 | return Math.random() * (max - min) + min; 3 | } 4 | -------------------------------------------------------------------------------- /templates/starter/src/utils/sort-entities-by-distance.ts: -------------------------------------------------------------------------------- 1 | import { Entity } from 'koota'; 2 | import * as THREE from 'three'; 3 | import { Transform } from '../traits'; 4 | 5 | export const sortEntitiesByDistance = (position: THREE.Vector3, entities: Entity[]) => { 6 | return [...entities].sort((a, b) => { 7 | const transformA = a.get(Transform); 8 | const transformB = b.get(Transform); 9 | 10 | if (!transformA || !transformB) return 0; 11 | 12 | const distanceA = position.distanceTo(transformA.position); 13 | const distanceB = position.distanceTo(transformB.position); 14 | 15 | return distanceA - distanceB; 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /templates/starter/src/utils/spatial-hash.ts: -------------------------------------------------------------------------------- 1 | // Originally by Hendrik Mans: https://github.com/hmans/miniplex/blob/main/apps/demo/src/systems/SpatialHashingSystem.tsx 2 | 3 | import { Entity } from 'koota'; 4 | 5 | type Cell = Set; 6 | 7 | export class SpatialHashMap { 8 | protected cells = new Map(); 9 | protected entityToCell = new Map(); 10 | 11 | constructor(public cellSize: number) {} 12 | 13 | setEntity(entity: Entity, x: number, y: number, z: number) { 14 | const cell = this.getCell(x, y, z); 15 | 16 | /* Remove from previous hash if known */ 17 | const oldCell = this.entityToCell.get(entity); 18 | 19 | if (oldCell) { 20 | /* If hash didn't change, do nothing */ 21 | if (oldCell === cell) return; 22 | 23 | /* Remove from previous hash */ 24 | oldCell.delete(entity); 25 | } 26 | 27 | cell.add(entity); 28 | this.entityToCell.set(entity, cell); 29 | } 30 | 31 | removeEntity(entity: Entity) { 32 | const cell = this.entityToCell.get(entity); 33 | cell?.delete(entity); 34 | this.entityToCell.delete(entity); 35 | } 36 | 37 | getNearbyEntities( 38 | x: number, 39 | y: number, 40 | z: number, 41 | radius: number, 42 | entities: Entity[] = [], 43 | maxEntities = Infinity 44 | ) { 45 | let count = 0; 46 | entities.length = 0; 47 | 48 | // Calculate the cell coordinates that contain the sphere defined by radius 49 | const minCellX = Math.floor((x - radius) / this.cellSize); 50 | const maxCellX = Math.floor((x + radius) / this.cellSize); 51 | const minCellY = Math.floor((y - radius) / this.cellSize); 52 | const maxCellY = Math.floor((y + radius) / this.cellSize); 53 | const minCellZ = Math.floor((z - radius) / this.cellSize); 54 | const maxCellZ = Math.floor((z + radius) / this.cellSize); 55 | 56 | // Iterate through all cells that might contain entities within the radius 57 | for (let cx = minCellX; cx <= maxCellX; cx++) { 58 | for (let cy = minCellY; cy <= maxCellY; cy++) { 59 | for (let cz = minCellZ; cz <= maxCellZ; cz++) { 60 | const cell = this.getCell( 61 | cx * this.cellSize, 62 | cy * this.cellSize, 63 | cz * this.cellSize 64 | ); 65 | 66 | for (const entity of cell) { 67 | entities.push(entity); 68 | count++; 69 | 70 | if (count >= maxEntities) return entities; 71 | } 72 | } 73 | } 74 | } 75 | 76 | return entities; 77 | } 78 | 79 | reset() { 80 | this.cells.clear(); 81 | this.entityToCell.clear(); 82 | } 83 | 84 | protected getCell(x: number, y: number, z: number) { 85 | const hash = this.calculateHash(x, y, z, this.cellSize); 86 | 87 | if (!this.cells.has(hash)) { 88 | this.cells.set(hash, new Set()); 89 | } 90 | 91 | return this.cells.get(hash)!; 92 | } 93 | 94 | protected calculateHash(x: number, y: number, z: number, cellSize: number) { 95 | const hx = Math.floor(x / cellSize); 96 | const hy = Math.floor(y / cellSize); 97 | const hz = Math.floor(z / cellSize); 98 | 99 | return `${hx}:${hy}:${hz}`; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /templates/starter/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /templates/starter/src/world.ts: -------------------------------------------------------------------------------- 1 | import { createWorld } from 'koota'; 2 | import { SpatialHashMap, Time } from './traits'; 3 | 4 | export const world = createWorld(Time, SpatialHashMap); 5 | -------------------------------------------------------------------------------- /templates/starter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 5 | "target": "ES2020", 6 | "useDefineForClassFields": true, 7 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 8 | "module": "ESNext", 9 | "skipLibCheck": true, 10 | 11 | /* Bundler mode */ 12 | "moduleResolution": "Bundler", 13 | "allowImportingTsExtensions": true, 14 | "isolatedModules": true, 15 | "moduleDetection": "force", 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | 19 | /* Linting */ 20 | "strict": true, 21 | "noUnusedLocals": true, 22 | "noUnusedParameters": true, 23 | "noFallthroughCasesInSwitch": true 24 | }, 25 | "include": ["src"] 26 | } 27 | -------------------------------------------------------------------------------- /templates/starter/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import react from '@vitejs/plugin-react-swc'; 3 | import tailwindcss from '@tailwindcss/vite'; 4 | 5 | export default defineConfig({ 6 | plugins: [react(), tailwindcss()], 7 | }); 8 | --------------------------------------------------------------------------------