diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js index 4a484e33..feadd7f8 100644 --- a/docs/.vitepress/config.js +++ b/docs/.vitepress/config.js @@ -74,7 +74,8 @@ module.exports = { text: "Recipes", collapsed: false, items: [ - { text: "Display Save Progress", link: "/guide/recipes/save-progress" } + { text: "Display Save Progress", link: "/guide/recipes/save-progress" }, + { text: "Display Particle Effect", link: "/guide/recipes/particles" } ] }, { diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css index 4ce8865d..36982a15 100644 --- a/docs/.vitepress/theme/custom.css +++ b/docs/.vitepress/theme/custom.css @@ -13,7 +13,16 @@ body { } .custom-block.warning { - background-color: #EBCB8B; + background-color: #3B4252; + border-color: #EBCB8B; +} + +.custom-block.warning > .custom-block-title { + color: #EBCB8B; +} + +.custom-block.warning > :not(.custom-block-title) { + color: var(--vp-custom-block-info-text); } .vp-doc tr:nth-child(2n), diff --git a/docs/guide/advanced-concepts/nodes.md b/docs/guide/advanced-concepts/nodes.md index 10736fff..44342d42 100644 --- a/docs/guide/advanced-concepts/nodes.md +++ b/docs/guide/advanced-concepts/nodes.md @@ -1,7 +1,37 @@ # Nodes -Every feature that is rendered in the DOM should have a `Node` component within it, which registers itself to the closest `Context` component (typically within the`Layer`'s component) and tracks the bounding rect (both size and position) of the DOM element. You can then search for a feature's unique `id` property within `layer.nodes` to get access to the DOM element for that feature, if it currently exists. +Features rendered in the DOM should include a `Node` component, which registers itself to the nearest `Context` component (usually within the `Layer`'s component) and tracks the bounding rect (both size and position) of the DOM element. Access the DOM element for a feature via its unique `id` property within `layer.nodes`, provided it currently exists. -This can be used for features with more complex displays, such as particle effects positioned relative to another feature, or drawing links between different nodes. +This is useful for features with complex displays, such as particle effects positioned relative to another feature or drawing links between different nodes. To illustrate this, let's look at a complete example of using `layer.nodes` to get a node's bounding rect and then placing a particle effect using it. Here's an example from Kronos: -The bounding rect that will typically be kept up to date and react to things like nodes changing size, or moving because of the window resizing or feature's showing or hiding. However, there are ocassional situations where it may become out of sync, so it's recommended to only use the node system for visual effects, where any glitches will be relatively minor. +```ts +const particlesEmitter = ref(particles.addEmitter(element.particlesConfig)); +const updateParticleEffect = async ([shouldEmit, rect, boundingRect]: [ + boolean, + DOMRect | undefined, + DOMRect | undefined +]) => { + const emitter = await particlesEmitter.value; + emitter.emit = isGaining && rect != undefined && boundingRect != undefined; + if (emitter.emit && !emitter.destroyed) { + emitter.cleanup(); + emitter.updateOwnerPos( + rect.x + rect.width / 2 - boundingRect.x, + rect.y + rect.height / 2 - boundingRect.y + ); + emitter.resetPositionTracking(); + } +}; +watch( + [ + () => Decimal.gt(actualGain.value, 0), + () => layer.nodes.value[name]?.rect, + particles.boundingRect + ], + updateParticleEffect +) +``` + +In this example the particle effects will update whenever the window resizes, the feature's bounding rect changes, or the particle effect is supposed to turn on/off. By watching for other relevant properties you can ensure even more complex situations are accounted for. + +The bounding rect is usually kept up-to-date and responsive to changes such as nodes resizing, moving due to window resizing, or features being shown or hidden. However, occasional situations may cause it to be out of sync. Therefore, it's recommended to use the node system for visual effects only, where any glitches have minimal impact. diff --git a/docs/guide/getting-started/setup.md b/docs/guide/getting-started/setup.md index 99a95c9e..60450348 100644 --- a/docs/guide/getting-started/setup.md +++ b/docs/guide/getting-started/setup.md @@ -1,61 +1,63 @@ # Setting Up -Profectus requires a node development environment in order to work on a project. If you are comfortable with the command line, it is recommended to use a local development environment. +Profectus requires a Node.js development environment for working on a project. If you are comfortable with the command line, a local development environment is recommended. ## Local Development -You will require the following tools for local development: +For local development, you will need the following tools: - [git](https://git-scm.com/downloads) - [node](https://nodejs.org/en/download/) -Create a new project from the [Profectus repository](https://github.com/profectus-engine/Profectus) via the "Use this template" button. You can then copy the link for the repository to clone it locally. +Create a new project from the [Profectus repository](https://github.com/profectus-engine/Profectus) by clicking the "Use this template" button. Then, clone the repository locally using the provided link. ::: info -Since the repository is a template repository, you can easily create multiple projects from one repository. However, it does make updating an existing project to a newer version of Profectus more difficult. Consider [updating Profectus](./updating.md) _before_ you start developing, to avoid the issue with unrelated histories. +The template repository allows easy creation of multiple projects from one repository. However, updating an existing project to a newer version of Profectus can be challenging. Consider [updating Profectus](https://chat.openai.com/updating.md) _before_ starting development to avoid issues with unrelated histories. ::: -It's recommended to create a new git branch for development, so you can push your changes without it affecting the live build. This allows you to continue working with smaller commits, and only release new versions when you're actually ready to. The github workflow will automatically rebuild the page whenever you push to the `main` branch. +It's recommended to create a new Git branch for development, allowing you to push changes without affecting the live build. The GitHub workflow will automatically rebuild the page when you push to the `main` branch. -The next step is to install Profectus' dependencies. This is as simple as running `npm install`. +Next, install Profectus' dependencies by running `npm install`. Run `npm run serve` to start a local server hosting your project. The site will automatically reload as you modify files. -You can now run `npm run serve` to start a local server that will host your project so you can work on it. As you change files the site will automatically reload them. - -Also, you might consider following the steps to [update Profectus](./updating.md) before you start working, as it'll make updating in the future easier without needing to worry about unrelated histories. +Also, follow the steps to [update Profectus](https://chat.openai.com/updating.md) before starting to make future updates easier without worrying about unrelated histories. ### Deploying -If you're using git, deploying is as easy as pushing your changes to the `main` branch. In a couple minutes the site will be updated fully automatically. If you'd like to see progress on it, or look at any errors that happened, you can do so from the actions tab on your repository. +Using Git, the repository's workflow action automates deployment. However, you need to grant write permissions for the action in the repository settings. Go to Actions, General, Workflow permissions, and select "Read and write permissions". + +![workflow permissions](./workflow-perms.png) + +To deploy, push changes to the main branch. The site will be updated automatically in a few minutes. Check progress or errors from the Actions tab on your repository. ![actions button](./actionsbutton.png) -Before github knows to actually host the generated site, you'll have to enable github pages in the repo settings. This just means selecting the branch to use - `gh-pages`. You will only need to perform this step once. This will automatically start another github action to deploy the website. +Enable GitHub Pages in the repo settings to host the generated site. Select the `gh-pages` branch. Perform this step once. This will automatically start another GitHub action to deploy the website. ![github pages](./gh-pages.png) -Once the action completes, your project should be available at `https://.github.io//`. For example, the TMT Demo project hosted at https://github.com/profectus-engine/TMT-Demo is available at https://profectus-engine.github.io/TMT-Demo/. +Upon action completion, your project should be available at `https://.github.io//`. For example, the TMT Demo project hosted at https://github.com/profectus-engine/TMT-Demo is available at https://profectus-engine.github.io/TMT-Demo/. ### Visual Studio Code Setup -If you don't already have a preferred IDE, Profectus is currently developed in [Visual Studio Code](https://code.visualstudio.com) and is known to work well with that IDE in particular. +If you don't have a preferred IDE, Profectus is developed in [Visual Studio Code](https://code.visualstudio.com) and is known to work well with it. Recommendations: - Use [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471) for proper type analysis - Turn off `.value` autocomplete by running the `Preferences: Open Settings` command and setting `volar.autoCompleteRefs` to `false` -- Install the [Vitest VS Code extension](https://marketplace.visualstudio.com/items?itemName=ZixuanChen.vitest-explorer&ssr=false#qna) for running and debugging unit tests (if you plan on working on the engine itself) +- Install the [Vitest VS Code extension](https://marketplace.visualstudio.com/items?itemName=ZixuanChen.vitest-explorer&ssr=false#qna) for running and debugging unit tests (if working on the engine itself) ## Replit -As an alternative to local development, you may use [replit](https://replit.com), which will automatically set up your development environment for you and also host your project itself. +As an alternative to local development, you may use [Replit](https://replit.com). Replit sets up your development and hosts your project. -However, on the free plan you will be limited by the amount of resources you can use, and the program will need to start up occasionally. +On the free plan, you'll face limitations, and the program may need occasional startups. -To create a Profectus project on replit, all you have to do is click this button: +To create a Profectus project on Replit, all you have to do is click this button: [![Run on Repl.it](https://repl.it/badge/github/profectus-engine/Profectus)](https://repl.it/github/profectus-engine/Profectus) -You can then start the developing by clicking the "Run" button at the top of the screen. This will also make the project run publicly, so you could consider it as automatically deployed. This does mean you cannot have a separate development environment from your production environment, however. +Click the "Run" button at the top of the screen to start development. This will also make the project publicly accessible, essentially auto-deploying it. However, this means you cannot separate your development and production environments. ## Glitch -[Glitch](https://glitch.com) is a site similar to replit, with much the same pros and cons. To create a projectus project on glitch, select "New Project", "Import from GitHub", and then type in `profectus-engine/Profectus`. The new project will be created and should be automatically configured and ready to go. +[Glitch](https://glitch.com) is a site similar to Replit, with many of the same pros and cons. To create a Profectus project on Glitch, select "New Project", "Import from GitHub", and enter `profectus-engine/Profectus`. The new project will be automatically configured and ready to go. diff --git a/docs/guide/getting-started/workflow-perms.png b/docs/guide/getting-started/workflow-perms.png new file mode 100644 index 00000000..b5295eb7 Binary files /dev/null and b/docs/guide/getting-started/workflow-perms.png differ diff --git a/docs/guide/recipes/particles.md b/docs/guide/recipes/particles.md new file mode 100644 index 00000000..fec753a0 --- /dev/null +++ b/docs/guide/recipes/particles.md @@ -0,0 +1,77 @@ +# Particles + +This is a more complete example from the Kronos example used in the [nodes](../advanced-concepts/nodes) docs. You will design a particle effect, make it appear on another feature, and make it adapt to the game state. + +Let's start with designing the particle effect. Profectus uses pixi-particles, and there's an online particle effect editor for it [here](https://pixijs.io/pixi-particles-editor/). However, it will return an older format of the particle effect emitter options, so you'll need to convert it like so: + +```ts +import myParticleEffect from "myParticleEffect.json"; +import { upgradeConfig } from "@pixi/particle-emitter" + +const particleEffect = upgradeConfig(myParticleEffect); +``` + +Next we need to create the particles feature and render it. We'll also want to track the bounding rect of the particle effects. We'll consider the next step: + +```ts +const particles = createParticles(() => ({ + fullscreen: false, + zIndex: -1, + boundingRect: ref(null), + onContainerResized(boundingRect) { + this.boundingRect.value = boundingRect; + } +})); +``` + +You'll note this adds a bounding rect and updates it's based on the boundingRect set to non-null. The next step will be creating the emitter. Now we can pull in the Kronos example, which make a particle effect that appears when actualGain > 0. + +```ts +const particlesEmitter = ref(particles.addEmitter(element.particlesConfig)); +const updateParticleEffect = async ([shouldEmit, rect, boundingRect]: [ + boolean, + DOMRect | undefined, + DOMRect | undefined +]) => { + const emitter = await particlesEmitter.value; + emitter.emit = isGaining && rect != undefined && boundingRect != undefined; + if (emitter.emit && !emitter.destroyed) { + emitter.cleanup(); + emitter.updateOwnerPos( + rect.x + rect.width / 2 - boundingRect.x, + rect.y + rect.height / 2 - boundingRect.y + ); + emitter.resetPositionTracking(); + } +}; +watch( + [ + () => Decimal.gt(actualGain.value, 0), + () => layer.nodes.value[name]?.rect, + particles.boundingRect + ], + updateParticleEffect +) +``` + +You'll note this checks regularly whether the boundingRect exists. If you ensure all the references to things are being watched, youy can make complex situations for various emitters. + +If you're hot reloading, it may be required to re-load the particle effect. Here's example code from Kronos. + +```ts +const refreshParticleEffect = () => { + particlesEmitter.value + .then(e => e.destroy()) + .then( + () => + (particlesEmitter.value = particles.addEmitter(element.particlesConfig)) + ) + .then(() => + updateParticleEffect([ + Decimal.gt(actualGain.value, 0), + layer.nodes.value[name]?.rect, + particles.boundingRect.value + ]) + ); +}; +```