-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update the remain choo choo train tutorial steps
- Loading branch information
1 parent
f22133e
commit 0f6133f
Showing
24 changed files
with
819 additions
and
159 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,25 @@ | ||
# Choo Choo Train | ||
# Onboard the Choo Choo Train! | ||
|
||
Welcome to the Choo Choo Train workshop! | ||
|
||
We are going to handcraft a cute little scene of a train moving through a landscape at night. We will solely be using the [Graphics](https://pixijs.com/guides/components/graphics) API to draw out the whole scene. In this tutorial, we will be exploring a handful of methods it provides to draw a variety of shapes. For the full list of methods, please check out the Graphics [documentation](https://pixijs.download/release/docs/PIXI.Graphics.html). | ||
|
||
Please go through the tutorial steps at your own pace and challenge yourself using the editor on the right hand side. Here PixiJS has already been included as guided under the [Getting Started](/guides/basics/getting-started#loading-pixijs) section. Let's start off by creation a PixiJS application, initialize it, add its canvas to the DOM, and preload the required assets ahead of the subsequent steps. | ||
|
||
We will be using an asynchronous immediately invoked function expression ([IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE)), but you are free to switch to use promises instead. | ||
|
||
## Application Setup | ||
|
||
Let's create the application outside of the IIFE just so that it can be referenced across other functions declared outside. The initialization and appending the application's canvas will be done from within the `setup` function which is called inside the IIFE. | ||
|
||
```javascript | ||
async function setup() | ||
{ | ||
await app.init({ background: '#021f4b', resizeTo: window }); | ||
document.body.appendChild(app.canvas); | ||
} | ||
``` | ||
|
||
At this point, you should see the preview filled with an empty light blue background. | ||
|
||
When you are ready, proceed to the next exercise using the _Next >_ button below, or feel free to skip to any exercise using the drop-down menu on the top right hand corner of the card. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
# You did it! | ||
|
||
Congratulations! Hope you enjoyed the journey. Feel free to head back to the gallery and explore other tutorials. | ||
Congratulations, hope you enjoyed the journey! Now you are an expert on the Graphics API. Make sure to explore other features that the API has to offer on the official [documentation](https://pixijs.download/release/docs/PIXI.Graphics.html), like the ability to cut shapes out from existing ones, advance lines and curves, using gradients or textures for fill and stroke - just to list a few. | ||
|
||
Feel free to head back to the gallery and explore other tutorials. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,30 @@ | ||
# Adding Stars | ||
# Adding Stars | ||
|
||
Let's start with the sky! It's a little plain and boring right now, so how about adding some stars to it? | ||
|
||
Because we will be drawing many different elements on the remaining steps, let's separate the building of each element into its own function to be called from within the main IIFE. Here, the `addStars` function has been set up for you to fill out. | ||
|
||
Graphics API has a built-in `star(x, y, points, radius, innerRadius?, rotation?)` method for this with the ability to specify number of star points, its rotation, radius and even inner radius if you prefer it with a hollow. | ||
|
||
Here, we will use a for-loop to create a number of 5-point stars with randomized radius, rotation and deterministically randomized positions across the whole scene. Let create 20 scattered stars with a radius size between 2 - 5 units to start under a single Graphics instance. After drawing out the individual invisible shape, we can then use the `fill(style)` method to color it in, specifying the color and the opacity calculated from the percentage of random radius to the max radius. | ||
|
||
> _**TIPS:** The Graphics API methods (with a few exceptions) return back the Graphics instance so it can be used for chained as you will see in the future steps_ | ||
```javascript | ||
const starCount = 20; | ||
const graphics = new Graphics(); | ||
|
||
for (let index = 0; index < starCount; index++) | ||
{ | ||
const x = (index * 0.78695 * app.screen.width) % app.screen.width; | ||
const y = (index * 0.9382 * app.screen.height) % app.screen.height; | ||
const radius = 2 + Math.random() * 3; | ||
const rotation = Math.random() * Math.PI * 2; | ||
|
||
graphics.star(x, y, 5, radius, 0, rotation).fill({ color: 0xffdf00, alpha: radius / 5 }); | ||
} | ||
|
||
app.stage.addChild(graphics); | ||
``` | ||
|
||
Now we have a starry sky! But let's take it a little further to lighten up our sky even more on the next step. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,31 @@ | ||
# Adding Moon | ||
# Adding Moon | ||
|
||
For the moon crescent, we will cheat a little bit with an existing moon SVG code here: | ||
|
||
```svg | ||
<svg width="111" height="126" viewBox="0 0 111 126" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||
<path fill-rule="evenodd" clip-rule="evenodd" | ||
d="M9.99794 104.751C44.7207 104.751 72.869 76.6028 72.869 41.8801C72.869 25.9516 66.9455 | ||
11.4065 57.1812 0.327637C87.3034 4.98731 110.363 31.0291 110.363 62.4566C110.363 97.1793 | ||
82.2144 125.328 47.4917 125.328C28.6975 125.328 11.8294 117.081 0.308472 104.009C3.46679 | ||
104.498 6.70276 104.751 9.99794 104.751Z" fill="#FFDF00"/> | ||
<path fill-rule="evenodd" clip-rule="evenodd" | ||
d="M57.4922 0.682129C75.7709 10.9731 88 29.7256 88 51.1529C88 83.6533 59.8656 110 25.16 | ||
110C16.9934 110 9.19067 108.541 2.03273 105.887C1.44552 105.272 0.870627 104.646 0.308472 | ||
104.008C3.46679 104.497 6.70276 104.75 9.99794 104.75C44.7207 104.75 72.869 76.6018 72.869 | ||
41.8791C72.869 26.1203 67.0711 11.7158 57.4922 0.682129Z" fill="#DEC61A"/> | ||
</svg> | ||
``` | ||
|
||
Graphics API also has a built-in `svg(svgString)` method for drawing vector graphics using SVG data. Have a go at it on the set up `addMoon` function. | ||
|
||
```javascript | ||
const svg = `-- SVG DATA STRING --` | ||
const graphics = new Graphics().svg(svg); | ||
|
||
graphics.x = app.screen.width / 2 + 100; | ||
graphics.y = app.screen.height / 8; | ||
app.stage.addChild(graphics); | ||
``` | ||
|
||
Think the sky is enough, let's us now proceed to add some landscape elements! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,104 @@ | ||
# Adding Mountains | ||
# Adding Mountains | ||
|
||
For the background let's put up some mountains, shall we? We will also animate them to the left to give an impression that the scene is moving rightwards. | ||
|
||
## Create Mountain Groups | ||
|
||
Since we are moving the mountains to the left, they will eventually go off the screen and at the same time leaving empty spaces to the right. To fix this, we will be looping them back to the right of the scene once they go out of the scene view. In order to make the loop seamless, we will be making 2 mountain groups where each covers the whole scene. Then we will offset one group off the screen to the right. This is so that the second group and slowly filling in the screen from the right as the first group moving off the screen to the left before looping back to be offscreen to the right of the second group and repeating the process. | ||
|
||
Let start by filling in the logic for creating a mountain group in the `createMountainGroup()` function which will return a Graphics instance of a mountain group. We will use this to create the 2 group instances later. | ||
|
||
Here, we are using a single Graphics instance for a group of mountains. Taking into account the screen dimension we can draw out 3 mountains with different heights and colors. In this case, we will imagine the Graphics instance as a pen and for each of the mountain we move the pen to the starting position using Graphics API's `moveTo(x, y)` method and then contour out the mountain arc using `bezierCurveTo(cx1, cy1, cx2, cy2, x, y)` method, where [`cx`, `cy`] positions are control point coordinates for the curve going from where it was to the [`x`, `y`] position. Again, we then need to fill the resulted shape with `fill(style)`. | ||
|
||
> _**TIPS:** In this case, we do not have to connect the end point to the starting point as the Graphics' context will automatically infer a closed shape by doing so for the fill. | ||
```javascript | ||
const graphics = new Graphics(); | ||
const width = app.screen.width / 2; | ||
const startY = app.screen.height; | ||
const startXLeft = 0; | ||
const startXMiddle = Number(app.screen.width) / 4; | ||
const startXRight = app.screen.width / 2; | ||
const heightLeft = app.screen.height / 2; | ||
const heightMiddle = (app.screen.height * 4) / 5; | ||
const heightRight = (app.screen.height * 2) / 3; | ||
const colorLeft = 0xc1c0c2; | ||
const colorMiddle = 0x7e818f; | ||
const colorRight = 0x8c919f; | ||
|
||
graphics | ||
// Draw the middle mountain | ||
.moveTo(startXMiddle, startY) | ||
.bezierCurveTo( | ||
startXMiddle + width / 2, | ||
startY - heightMiddle, | ||
startXMiddle + width / 2, | ||
startY - heightMiddle, | ||
startXMiddle + width, | ||
startY, | ||
) | ||
.fill({ color: colorMiddle }) | ||
|
||
// Draw the left mountain | ||
.moveTo(startXLeft, startY) | ||
.bezierCurveTo( | ||
startXLeft + width / 2, | ||
startY - heightLeft, | ||
startXLeft + width / 2, | ||
startY - heightLeft, | ||
startXLeft + width, | ||
startY, | ||
) | ||
.fill({ color: colorLeft }) | ||
|
||
// Draw the right mountain | ||
.moveTo(startXRight, startY) | ||
.bezierCurveTo( | ||
startXRight + width / 2, | ||
startY - heightRight, | ||
startXRight + width / 2, | ||
startY - heightRight, | ||
startXRight + width, | ||
startY, | ||
) | ||
.fill({ color: colorRight }); | ||
|
||
return graphics; | ||
``` | ||
|
||
## Set Up Mountain Groups | ||
|
||
With the `createMountainGroup()` helper function, we can then create 2 instances of the mountain group and offset one of them off the screen to the right. | ||
|
||
```javascript | ||
const group1 = createMountainGroup(); | ||
const group2 = createMountainGroup(); | ||
|
||
group2.x = app.screen.width; | ||
app.stage.addChild(group1, group2); | ||
``` | ||
|
||
You should now see a single group of mountains covering the whole scene. | ||
|
||
## Animate Mountains | ||
|
||
Using the application's ticker, we can add a callback function which will reposition the mountain groups every ticker update, creating a continuous animation. The callback function will be supplied with the Ticker object in which time-related data can be inferred like the `deltaTime` that we will be using to calculate the distance for the mountain to move consistently. Remember to reposition the groups when they moved completely off the screen. | ||
|
||
```javascript | ||
app.ticker.add((time) => | ||
{ | ||
const dx = time.deltaTime * 0.5; | ||
|
||
group1.x -= dx; | ||
group2.x -= dx; | ||
|
||
if (group1.x <= -app.screen.width) | ||
{ | ||
group1.x += app.screen.width * 2; | ||
} | ||
if (group2.x <= -app.screen.width) | ||
{ | ||
group2.x += app.screen.width * 2; | ||
} | ||
}); | ||
``` |
Oops, something went wrong.