├── bin ├── .DS_Store ├── spriteUtilities.js └── spriteUtilities.js.map ├── README.md └── src └── spriteUtilities.js /bin/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kittykatattack/spriteUtilities/HEAD/bin/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Sprite Utilities for Pixi (v3.0.11) 2 | ===================== 3 | 4 | This repository contains a bunch of useful functions for creating 5 | [Pixi](https://github.com/GoodBoyDigital/pixi.js/) sprites and making them easier to work with. 6 | 7 | (Important! Sprite Utilities targets Pixi v3.0.11, which is the most stable version of Pixi, and is the only version I can recommend using. This library will eventually be upgraded for Pixi v4 when the v4 branch matures.) 8 | 9 | [Setting up](#settingup)
10 | [sprite: Quickly make any Sprite or MovieClip](#sprite)
11 | [tilingSprite: Make a tiling sprite](#tilingSprite)
12 | [filmstrip: Turn any tileset PNG into a texture array](#filmstrip)
13 | [frames: Capture a subset of frames from a PNG tileset](#frames)
14 | [frame: Capture a single rectangular area inside PNG image or tileset](#frame)
15 | [frameSeries: Captures a sequence of numbered frame ids from a textureatlas](#frameSeries)
16 | [text: Make a text sprite](#text)
17 | [bitmaptext: Make a BitmapText sprite](#bitmaptext)
18 | [rectangle: Draw a rectangle](#rectangle)
19 | [circle: Draw a circle](#circle)
20 | [line: Draw a line](#line)
21 | [grid: Create a grid of sprites](#grid)
22 | [group: Group sprites](#group)
23 | [batch: Create a particle container](#batch)
24 | [shoot: A method for easily shooting bullet sprites](#shoot)
25 | [shake: Make a sprite shake](#shake)
26 | [remove: Remove a sprite or array of sprites from its parent](#batch)
27 | [color: Convert a HTML or RGBA color to a Hex color code](#color)
28 | 29 | 30 | Setting up and initializing `SpriteUtilities` 31 | ------------------------------------------- 32 | 33 | Create a new instance of `SpriteUtilities` like this: 34 | ```js 35 | let u = new SpriteUtilities(PIXI); 36 | ``` 37 | Supply a reference to `PIXI` as the optional argument in the 38 | constructor. (If you don't supply it, `SpriteUtilites` will look for a 39 | global `PIXI` object and alert you with an error if it can't find it.) 40 | 41 | You can now access the `SpriteUtilites` instance and all its 42 | methods using the variable reference `u`. 43 | 44 | 45 | The `sprite` function 46 | ------------------- 47 | 48 | Use the universal `sprite` function to make any kind of Pixi sprite. 49 | ```js 50 | let anySprite = u.sprite(frameTextures, xPosition, yPosition); 51 | ``` 52 | The first argument, `frameTextures` can be any of the following: 53 | 54 | - A single PNG image string. 55 | - A Pixi `Texture` object. 56 | - An array of texture atlas frame ids. 57 | - An array of single PNG image strings. 58 | - An array of Pixi `Texture` objects. 59 | 60 | You can essentially throw anything at it, and it will give you back a sprite that works as it should, 61 | depending on the kind of texture information you've supplied. That 62 | means you can use the `sprite` function 63 | as your one-stop-shop for creating any kind of sprite. Forget about 64 | using Pixi's `Sprite` and `MovieClip` 65 | classes to make sprites and just use the `sprite` function for everything! 66 | 67 | If you supply the `sprite` function with an array, it will return a 68 | `MovieClip` sprite but with a bonus 69 | **state player** built into it. The state player is just a collection of 4 properties and methods 70 | that make it easy to control sprite animation states. Here they are: 71 | 72 | 1. `fps`: A property to set the precise animation speed, as 73 | frames-per-second. Its default value is 12. The `fps` is not linked to 74 | the renderer's fps, and that means you can have sprite animations 75 | playing at speeds that are independent of the game or application 76 | speed. `anySprite.fps = 6`. 77 | 78 | 2. `playAnimation`: A method to play the sprite's 79 | animation.`anySprite.playAnimation()`. You can supply it with start and end frame values if you want to play a sub-set of frames. Here's how: `anySprite.playAnimation([startFrame, endFrame])` The animation will play in a loop, by default, unless you set the sprite's `loop` property value to `false`. 80 | 81 | 3. `stopAnimation`: A method that stops the sprite's animation at the 82 | current frame. `anySprite.stopAnimation()`. 83 | 84 | 4. `show`: A method that displays a specific frame number. 85 | `anySprite.show(frameNumber)`. 86 | 87 | 5. `animating`: A Boolean property that will be `true` if the 88 | animation is playing and `false` if it isn't. 89 | 90 | 91 | `tilingSprite` 92 | ------------- 93 | 94 | Create a sprite with an image that you can tile across its surface. The first argument is 95 | the source for the tile image. You can use ordinary images, 96 | texture atlas frames, or an array of image sources if you want to 97 | tiling sprite with multiple frames. The second and third arguments 98 | are the sprite's width and height, which determine the entire area that 99 | the tile pattern should fill. You can optionally supply the x and y 100 | position as the fourth and fifth arguments. 101 | ```js 102 | let anySprite = u.tilingSprite("images/tile.png", 128, 128); 103 | ``` 104 | 105 | 106 | `filmstrip` 107 | ---------- 108 | 109 | Use the`filmstrip` function to automatically turn a tileset PNG image 110 | into an array of textures that you can use to make a sprite. 111 | ```js 112 | u.filmstrip("anyTilesetImage.png", frameWidth, frameHeight, optionalPadding); 113 | ``` 114 | Supply `filmstrip` with the tileset image name and the width and 115 | height of each frame. If there's padding around each frame, supply the 116 | padding amount, in pixels. `filmstrip` returns an array of frames that 117 | you can use to make an animated `MovieClip` sprite. 118 | Here's how you could use `filmstrip` with the universal `sprite` 119 | function to quickly make a sprite with multiple frames: 120 | ```js 121 | let textures = u.filmstrip("tileset.png", 32, 32); 122 | let anySprite = u.sprite(textures); 123 | ``` 124 | The `filmstrip` function automatically loads every frame from a tileset image into the sprite. 125 | 126 | 127 | `frames` 128 | ------- 129 | 130 | But what if you only want to use a sub-set of frames from the tileset, 131 | not all of them? Use another utility function called `frames`. The 132 | `frames` function takes 4 arguments: the texture, a 2D array of x/y 133 | frame position coordinates, and the width and height of each frame. 134 | Here's how you could use the frames function to create a sprite. 135 | ```js 136 | let textures = u.frames( 137 | "tileset.png", //The tileset image 138 | [[0,0],[32,0],[64,0]], //A 2D array of x/y frame coordianates 139 | 32, 32 //The width and height of each frame 140 | ); 141 | let anySprite = u.sprite(textures); 142 | ``` 143 | Use the `frames` function whenever you need to create a sprite using 144 | selected frames from a larger tileset PNG image. 145 | 146 | 147 | `frame` 148 | ------- 149 | 150 | Use the `frame` function if you just want to create a texture using a smaller 151 | rectangular section of a larger image. The `frame` function takes 152 | four arguments: the image, the sub-image x position, the sub-image y 153 | position, and the sub-image's width and height. 154 | ``` 155 | u.frame("image.png", x, y, width, height) 156 | ``` 157 | Here's how you could make a sprite using a sub-image of a larger 158 | image. 159 | ```js 160 | let texture = u.frame("tileset.png", 64, 128, 32, 32); 161 | let anySprite = u.sprite(texture); 162 | ``` 163 | Use the `frame` function to 164 | [blit](https://en.wikipedia.org/wiki/Bit_blit) a smaller image from bigger image. 165 | 166 | 167 | `frameSeries` 168 | ------------ 169 | 170 | If you've loaded a texture atlas and want a sequence of numbered frame 171 | ids to create an animated sprite, use the `frameSeries` function. 172 | Imagine that you have frames in a texture atlas with the following id 173 | names: 174 | ```js 175 | frame0.png 176 | frame1.png 177 | frame2.png 178 | ``` 179 | To create a sprite in Pixi using these frames, you would ordinarily 180 | write some code using Pixi's `MovieClip` class 181 | (`PIXI.extras.MovieClip`) that looks something like this: 182 | ```js 183 | let frameTextures = ["frame0.png", "frame1.png", "frame2.png"]; 184 | let anySprite = MovieClip.fromFrames(frameTextures); 185 | ``` 186 | You now have a sprite with 3 frames that you can control. That's not too painful, but what if you 187 | had 100 animation frames? You definitely don't want to manually type in 188 | 100 frame id's into an array. Instead, use the `frameSeries` function. 189 | 190 | `frameSeries` takes four arguments: the start frame sequence number, 191 | the end frame sequence number, the optional base file name, and the optional file extension. 192 | You could use the `frameSeries` function to create the sprite from the 193 | texture atlas frame ids like this: 194 | ``` 195 | let frameTextures = u.frameSeries(0, 2, "frame", ".png"); 196 | let anySprite = u.sprite(frameTextures); 197 | ``` 198 | If you had 100 animation frames, your code might look like this: 199 | ``` 200 | let frameTextures = u.frameSeries(0, 99, "frame", ".png"); 201 | let anySprite = u.sprite(frameTextures); 202 | ``` 203 | That's much better! 204 | 205 | 206 | `text` 207 | ------ 208 | 209 | Use the `text` method to quickly create a text sprite. 210 | ```js 211 | let messgae = u.text("Hello!", "32px Futura", "black", xPosition, yPosition); 212 | ``` 213 | Only the first argument, the text you want to display, is required. 214 | The second argument is the font size and family. You can use any 215 | system font, or a font from a loaded font file. The thrid argument is the 216 | fill color. Text colors can provided as RGBA, HLSA, hexadecimal, 217 | or HTML color string values, such as “blue” or “green.” The last 218 | arguments are the text's x and y position. 219 | 220 | You can add any additional Pixi text properties by setting the text sprite's 221 | `style` property. 222 | ```js 223 | message.style = {fill: "black", font: "16px Helvetica"}; 224 | ``` 225 | Check out the [full list of Pixi Text properties](http://pixijs.github.io/docs/PIXI.Text.html) to find out 226 | which styles you can apply. 227 | 228 | To change the text display at any time, use the text's `content` property. 229 | ```js 230 | message.content = "Updated text!"; 231 | ``` 232 | 233 | 234 | `bitmapText` 235 | ----------- 236 | 237 | Bitmap text is text that is rendered using images for the letter 238 | shapes. (Ordinary font files just contain instructions about how 239 | your computer should draw the font shapes.) Bitmap fonts need to load the 240 | image and data files containing the letter shapes, but they tend to 241 | display more reliably across different platforms. 242 | 243 | The `bitmapText` method lets you quickly create a bitmap text sprite, 244 | like this: 245 | ```js 246 | let message = u.bitmapText("Hello!", "42px disko", align, tint, xPosition, yPosition); 247 | ``` 248 | Only the first argument, the text to display, is required. The second 249 | argument is the font size and family. 250 | 251 | The third argument is the alignment, which determines how the text 252 | should be displayed if it appears on more than one line. Alignment 253 | values can be any of these three strings: "left", "right" or "center". 254 | 255 | The fourth argument, tint, is the color that the font should be 256 | tinted. This can be any RGBA, HLASA, Hex, or HTML string color value. 257 | 258 | Finally, the last two values are the text's x and y position values. 259 | 260 | 261 | `rectangle` 262 | ---------- 263 | 264 | The `rectangle` method lets you quickly draw a rectangle. 265 | ```js 266 | u.rectangle( 267 | width, height, fillStyle, strokeStyle, lineWidth, xPosition, yPosition 268 | ); 269 | ``` 270 | `width` and `height` are the size, in pixels, of the rectangle. 271 | They're the only two arguments that are required. 272 | `fillStyle` is color for the inside fill color of the 273 | rectangle, and `strokeStyle` is the color code for the outline. (You 274 | can use hex colors, RGBA colors, or even any [HTML color names](http://www.w3schools.com/html/html_colornames.asp), like "blue" or "pink".) 275 | `lineWidth` determines how thick, in pixels, the rectangle's outline 276 | should be. (The default value is 0, which means the rectangle will have 277 | no outline.) The last two values are the rectangle's x and y 278 | position. 279 | 280 | Here's how to use the `rectangle` method to create a green square 281 | with a 2 pixel wide pink outline: 282 | ```js 283 | let square = u.rectangle(64, 64, "seaGreen", "hotPink", 2); 284 | ``` 285 | (Because the last two arguments, x and y, haven't been provided, the 286 | rectangle will have default x and y values of 0.) 287 | 288 | Rectangles also have `strokeStyle`, `lineStyle` and `lineWidth` properties 289 | that you can change at any time. 290 | 291 | 292 | `circle` 293 | ------- 294 | 295 | Use the `circle` method to draw a circle. 296 | ```js 297 | u.circle(diameter, fillStyle, strokeStyle, lineWidth, xPosition, yPosition) 298 | ``` 299 | The arguments are similar to the `rectangle` method's arguments, 300 | except that the fist one is the diameter, in pixels, of the circle you 301 | want to draw. Here's how to draw a blue circle with a diameter of 64 302 | pixels and a purple outline 3 pixels wide. 303 | ```js 304 | let ball = u.circle(64, "powderBlue", "plum", 3); 305 | ``` 306 | A circle's x and y position is anchored to the top left corner of an 307 | invisible rectangular bounding box that is surrounding the circle. To 308 | set the x and y position to the center of the circle, use the 309 | `anchor.set` method: 310 | ```js 311 | ball.anchor.set(0.5, 0.5); 312 | ``` 313 | This sets the `x` and `y` positions to `0.5`, which means "the 314 | positions that are at half the circle's width and height". In other 315 | words, its center. 316 | 317 | Circles have `fillStyle`, `strokeStyle`, `diameter` and `radius` properties that 318 | you can access and change later if you need to. 319 | 320 | 321 | `line` 322 | ----- 323 | The `line` method lets you quickly draw a straight line. 324 | ```js 325 | u.line(color, width, ax, ay, bx, by) 326 | ``` 327 | The `color` should be a hexadecimal color value. (Just as with 328 | rectangles or cirlces, you 329 | can use hex colors, RGBA colors, or HTML color name strings.) The last four arguments define the line's start and end points. `ax` 330 | and `ay` are it's start point; `bx` and `by` are it's end points. 331 | Here's how to create red line, 3 pixels wide, with a start x/y point 332 | of 64 and and an end x/y point of 128. 333 | 334 | ```js 335 | let diagonal = u.line(0xff0000, 3, 64, 64, 128, 128); 336 | ``` 337 | (Yes, as you can see above, you can use a hex color code with 338 | rectangles, circles or lines if you want to!) 339 | 340 | You can change the start and end points at any time. Here's how you 341 | set the line's end point to an x position of 100 and a y position of 342 | 90. 343 | ```js 344 | diagonal.bx = 100; 345 | diagonal.by = 90; 346 | ``` 347 | The line will be re-drawn to these coordinates as soon as you set 348 | them. 349 | 350 | Lines have `ax`, `ay`, `bx`, `by`, `strokeStyle` and `width` 351 | properties that you can acess and change. 352 | 353 | 354 | `grid` 355 | ----- 356 | 357 | `grid` is a very useful method that plots a grid of sprites for you. It 358 | returns a Pixi container object and fills it with a grid of sprites - 359 | any kind of sprite you need. Here's an example of how to use it to 360 | plot a 5 by 4 grid of black circles. 361 | 362 | ```js 363 | let circles = u.grid( 364 | 5, //The number of columns 365 | 4, //The number of rows 366 | 48, //The width of each cell 367 | 48, //The height of each cell 368 | true, //Should the sprite be centered in the cell? 369 | 0, //The sprite's xOffset from the left of the cell 370 | 0, //The sprite's yOffset from the top of the cell 371 | 372 | //A function that describes how to make each peg in the grid. 373 | //A random diameter and color are selected for each one 374 | () => { 375 | let ball = u.circle(24, 0x000000); 376 | return ball; 377 | }, 378 | 379 | //Run any optional extra code after each ball is made 380 | () => console.log("extra!") 381 | ); 382 | 383 | ``` 384 | The `grid` method returns a Pixi `Container` object called `circles`. All the sprites 385 | inside each cell of the grid are children of that `circles` Container. 386 | Because it’s a Container, you can manipulate the entire grid just like any 387 | other sprite. That means you can set its `x` and `y` position 388 | values to move the grid around the canvas. (The default x/y position 389 | is 0.) 390 | 391 | You can access the individual sprites in the grid through the 392 | Container's `children` array property. 393 | ```js 394 | circles.children 395 | ``` 396 | Just loop through the `children` array to set or access any properties 397 | of sprites in the grid. 398 | 399 | 400 | `group` 401 | ------ 402 | 403 | A quick way to make a Pixi `Container` and add sprites to it. Just supply 404 | the `group` method with a single spirte, or a list of sprites, and it 405 | will return a container with those sprites as its children. 406 | ``` 407 | let container = u.group(spriteOne, spriteTwo, spriteThree); 408 | ``` 409 | You can alternatively create an empty group, and add sprites to it as you 410 | need to using `addChild`, like this: 411 | ``` 412 | let container = u.group(); 413 | container.addChild(anySprite); 414 | ``` 415 | 416 | 417 | `batch` 418 | ------- 419 | 420 | A quick way to create a Pixi `ParticleContainer`. 421 | ``` 422 | let particleContainer = u.batch(); 423 | particleContainer.addChild(anySprite); 424 | ``` 425 | You can optionally create the batch with two arguments: the maximum 426 | number of sprites the container can inclue and the `ParticleContainer` 427 | object's `options`. Here's how to create a particle container with a 428 | maximum number of 20,000 sprites, and all the options set to `true` 429 | ```js 430 | let particleContainer = u.batch(20000, {rotation: true, alpha: true, scale: true, uvs: true}); 431 | ``` 432 | The default size is 15,000. So, if you have to contain more sprites, 433 | set it to a higher number. The `options` argument is an object with 434 | five Boolean properties that you can set: `scale`, `position`, 435 | `rotation`, `alpha`, and `uvs`. The default value for position is `true`, but all 436 | the others are set to `false`. 437 | 438 | 439 | `shoot` 440 | ------- 441 | 442 | The `shoot` methods let you create bullet sprites at any position on 443 | another sprite. 444 | 445 | The first step to making bullets is to create an array to store the 446 | new bullet sprites that you’re going to make: 447 | ```js 448 | let bullets = []; 449 | ``` 450 | You also need a sprite that's going to do the shooting. 451 | ```js 452 | let tank = u.sprite("tank.png"); 453 | ``` 454 | If you want your tank sprite to rotate around its center, then 455 | you'll want to center its x/y anchor point, like this: 456 | ```js 457 | tank.anchor.set(0.5, 0,5); 458 | ``` 459 | Next, use the `shoot` method to create bullet sprites. 460 | ```js 461 | u.shoot( 462 | tank, //The sprite that will be shooting 463 | tank.rotation, //The angle at which to shoot 464 | 32, //The x point on the tank where the bullet should start 465 | 0, //The y point on the tank where the bullet should start 466 | stage, //The container you want to add the bullet to 467 | 7, //The bullet's speed (pixels per frame) 468 | bullets, //The array used to store the bullets 469 | 470 | //A function that returns the sprite that should 471 | //be used to make each bullet 472 | () => g.circle(8, "red") 473 | ); 474 | 475 | ``` 476 | The 3rd and 4th arguments are the local x and y points on the tank 477 | where you want the bullets to start from. The 5th argument is the Pixi 478 | container that you want to add the bullets to. The 7th argument is the 479 | array that you want to add each bullet to. 480 | 481 | The most important argument is the last one: 482 | ```js 483 | () => u.circle(8, "red") 484 | ``` 485 | That’s a function that creates and returns the kind of sprite you want 486 | to use as a bullet. In this case, it’s a red circle 8 pixels in diameter. 487 | You can use any of the sprite-creation methods from this Sprite Utilities library, 488 | any standard Pixi sprite creation methods, or use your own custom function that 489 | creates and returns a sprite. 490 | 491 | All the bullet sprites that the `shoot` method creates are added to 492 | the `bullets` array, so just loop though the sprites in that array to 493 | check for collisions with other sprites. 494 | 495 | 496 | `shake` 497 | ------- 498 | 499 | Use the `shake` method to make a sprite shake or create a screen-shake 500 | effect. Here's how to use it: 501 | ```js 502 | u.shake(spriteToShake, magnitude, angular?); 503 | ``` 504 | The `shake` method’s first argument is the sprite, and the second is the shake 505 | magnitude in radians. The third argument is a Boolean that when `true` means 506 | the shaking should be angular around the sprite’s center point. 507 | 508 | Here's how you could make a sprite called `screen` shake around its center with a 509 | magnitude of 0.05. 510 | ```js 511 | u.shake(gameScene, 0.05, true); 512 | ``` 513 | You can alternatively make the shaking happen up and down on the x/y plane. 514 | Just set the second argument to a number, in pixels, that determines the 515 | maximum amount by which the sprite should shake. Then set the third 516 | argument to `false`, to disable angular shaking. 517 | ```js 518 | shake(gameScene, 16, false); 519 | ``` 520 | Which shaking style you prefer is entirely up to you. 521 | 522 | `shake` is an animation effect, and you won't see it unless run the 523 | SpriteUtilities `update` method inside a game loop. Here's how: 524 | ```js 525 | function gameLoop() { 526 | requestAnimationFrame(gameLoop); 527 | 528 | //Update the SpriteUtilities library each frame 529 | u.update(); 530 | } 531 | ``` 532 | The `update` method takes care animating the shake effect for you. 533 | 534 | 535 | `remove` 536 | ------- 537 | `remove` is a global convenience method that will remove any sprite, or an 538 | argument list of sprites, from its parent. 539 | ``` 540 | u.remove(spriteOne, spriteTwo, spriteThree); 541 | ``` 542 | This is really useful because you never need to know what the sprite's 543 | parent is. 544 | 545 | You can also remove a whole array of sprites from their parents, like 546 | this: 547 | ```js 548 | u.remove(arrayOfSprites); 549 | ``` 550 | Easy! 551 | 552 | 553 | `color`: Convert HTML and RGBA colors to Hexadecimal 554 | ------------------------------------------------------------ 555 | 556 | Do you like Pixi, but don't like Hexadecimal color codes? Use 557 | `color` to convert any ordinary HTML color string name (like 558 | "blue" or "green",) or any RGBA value to its equivalent Hex code. 559 | ```js 560 | let hexColor = u.color("darkSeaGreen"); 561 | ``` 562 | Now just use `hexColor` wherever Pixi asks for a color code. Yes, all 563 | of the [HTML color string names](http://www.w3schools.com/html/html_colornames.asp) are supported. 564 | 565 | 566 | 567 | 568 | 569 | -------------------------------------------------------------------------------- /src/spriteUtilities.js: -------------------------------------------------------------------------------- 1 | class SpriteUtilities{ 2 | constructor(renderingEngine = PIXI) { 3 | if (renderingEngine === undefined) throw new Error("Please supply a reference to PIXI in the SpriteUtilities constructor before using spriteUtilities.js"); 4 | 5 | //Find out which rendering engine is being used (the default is Pixi) 6 | this.renderer = ""; 7 | 8 | //If the `renderingEngine` is Pixi, set up Pixi object aliases 9 | if (renderingEngine.ParticleContainer && renderingEngine.Sprite) { 10 | this.renderer = "pixi"; 11 | this.Container = renderingEngine.Container; 12 | this.ParticleContainer = renderingEngine.ParticleContainer; 13 | this.TextureCache = renderingEngine.utils.TextureCache; 14 | this.Texture = renderingEngine.Texture; 15 | this.Rectangle = renderingEngine.Rectangle; 16 | this.MovieClip = renderingEngine.extras.MovieClip; 17 | this.BitmapText = renderingEngine.extras.BitmapText; 18 | this.Sprite = renderingEngine.Sprite; 19 | this.TilingSprite = renderingEngine.extras.TilingSprite; 20 | this.Graphics = renderingEngine.Graphics; 21 | this.Text = renderingEngine.Text; 22 | 23 | //An array to store all the shaking sprites 24 | this.shakingSprites = []; 25 | } 26 | } 27 | 28 | update() { 29 | if (this.shakingSprites.length > 0) { 30 | for(let i = this.shakingSprites.length - 1; i >= 0; i--) { 31 | let shakingSprite = this.shakingSprites[i]; 32 | if (shakingSprite.updateShake) shakingSprite.updateShake(); 33 | } 34 | } 35 | } 36 | 37 | sprite(source, x = 0, y = 0, tiling = false, width, height) { 38 | 39 | let o, texture; 40 | 41 | //Create a sprite if the `source` is a string 42 | if (typeof source === "string") { 43 | 44 | //Access the texture in the cache if it's there 45 | if (this.TextureCache[source]) { 46 | texture = this.TextureCache[source]; 47 | } 48 | 49 | //If it's not is the cache, load it from the source file 50 | else { 51 | texture = this.Texture.fromImage(source); 52 | } 53 | 54 | //If the texture was created, make the o 55 | if (texture) { 56 | 57 | //If `tiling` is `false`, make a regular `Sprite` 58 | if (!tiling) { 59 | o = new this.Sprite(texture); 60 | } 61 | 62 | //If `tiling` is `true` make a `TilingSprite` 63 | else { 64 | o = new this.TilingSprite(texture, width, height); 65 | } 66 | } 67 | //But if the source still can't be found, alert the user 68 | else { 69 | throw new Error(`${source} cannot be found`); 70 | } 71 | } 72 | 73 | //Create a o if the `source` is a texture 74 | else if (source instanceof this.Texture) { 75 | if (!tiling) { 76 | o = new this.Sprite(source); 77 | } else { 78 | o = new this.TilingSprite(source, width, height); 79 | } 80 | } 81 | 82 | //Create a `MovieClip` o if the `source` is an array 83 | else if (source instanceof Array) { 84 | 85 | //Is it an array of frame ids or textures? 86 | if (typeof source[0] === "string") { 87 | 88 | //They're strings, but are they pre-existing texture or 89 | //paths to image files? 90 | //Check to see if the first element matches a texture in the 91 | //cache 92 | if (this.TextureCache[source[0]]) { 93 | 94 | //It does, so it's an array of frame ids 95 | o = this.MovieClip.fromFrames(source); 96 | } else { 97 | 98 | //It's not already in the cache, so let's load it 99 | o = this.MovieClip.fromImages(source); 100 | } 101 | } 102 | 103 | //If the `source` isn't an array of strings, check whether 104 | //it's an array of textures 105 | else if (source[0] instanceof this.Texture) { 106 | 107 | //Yes, it's an array of textures. 108 | //Use them to make a MovieClip o 109 | o = new this.MovieClip(source); 110 | } 111 | } 112 | 113 | //If the sprite was successfully created, intialize it 114 | if (o) { 115 | 116 | //Position the sprite 117 | o.x = x; 118 | o.y = y; 119 | 120 | //Set optional width and height 121 | if (width) o.width = width; 122 | if (height) o.height = height; 123 | 124 | //If the sprite is a MovieClip, add a state player so that 125 | //it's easier to control 126 | if (o instanceof this.MovieClip) this.addStatePlayer(o); 127 | 128 | //Assign the sprite 129 | return o; 130 | } 131 | } 132 | 133 | addStatePlayer(sprite) { 134 | 135 | let frameCounter = 0, 136 | numberOfFrames = 0, 137 | startFrame = 0, 138 | endFrame = 0, 139 | timerInterval = undefined; 140 | 141 | //The `show` function (to display static states) 142 | function show(frameNumber) { 143 | 144 | //Reset any possible previous animations 145 | reset(); 146 | 147 | //Find the new state on the sprite 148 | sprite.gotoAndStop(frameNumber); 149 | } 150 | 151 | //The `stop` function stops the animation at the current frame 152 | function stopAnimation() { 153 | reset(); 154 | sprite.gotoAndStop(sprite.currentFrame); 155 | } 156 | 157 | //The `playSequence` function, to play a sequence of frames 158 | function playAnimation(sequenceArray) { 159 | 160 | //Reset any possible previous animations 161 | reset(); 162 | 163 | //Figure out how many frames there are in the range 164 | if (!sequenceArray) { 165 | startFrame = 0; 166 | endFrame = sprite.totalFrames - 1; 167 | } else { 168 | startFrame = sequenceArray[0]; 169 | endFrame = sequenceArray[1]; 170 | } 171 | 172 | //Calculate the number of frames 173 | numberOfFrames = endFrame - startFrame; 174 | 175 | //Compensate for two edge cases: 176 | //1. If the `startFrame` happens to be `0` 177 | /* 178 | if (startFrame === 0) { 179 | numberOfFrames += 1; 180 | frameCounter += 1; 181 | } 182 | */ 183 | 184 | //2. If only a two-frame sequence was provided 185 | /* 186 | if(numberOfFrames === 1) { 187 | numberOfFrames = 2; 188 | frameCounter += 1; 189 | } 190 | */ 191 | 192 | //Calculate the frame rate. Set the default fps to 12 193 | if (!sprite.fps) sprite.fps = 12; 194 | let frameRate = 1000 / sprite.fps; 195 | 196 | //Set the sprite to the starting frame 197 | sprite.gotoAndStop(startFrame); 198 | 199 | //Set the `frameCounter` to the first frame 200 | frameCounter = 1; 201 | 202 | //If the state isn't already `playing`, start it 203 | if (!sprite.animating) { 204 | timerInterval = setInterval(advanceFrame.bind(this), frameRate); 205 | sprite.animating = true; 206 | } 207 | } 208 | 209 | //`advanceFrame` is called by `setInterval` to display the next frame 210 | //in the sequence based on the `frameRate`. When the frame sequence 211 | //reaches the end, it will either stop or loop 212 | function advanceFrame() { 213 | 214 | //Advance the frame if `frameCounter` is less than 215 | //the state's total frames 216 | if (frameCounter < numberOfFrames + 1) { 217 | 218 | //Advance the frame 219 | sprite.gotoAndStop(sprite.currentFrame + 1); 220 | 221 | //Update the frame counter 222 | frameCounter += 1; 223 | 224 | //If we've reached the last frame and `loop` 225 | //is `true`, then start from the first frame again 226 | } else { 227 | if (sprite.loop) { 228 | sprite.gotoAndStop(startFrame); 229 | frameCounter = 1; 230 | } 231 | } 232 | } 233 | 234 | function reset() { 235 | 236 | //Reset `sprite.playing` to `false`, set the `frameCounter` to 0, //and clear the `timerInterval` 237 | if (timerInterval !== undefined && sprite.animating === true) { 238 | sprite.animating = false; 239 | frameCounter = 0; 240 | startFrame = 0; 241 | endFrame = 0; 242 | numberOfFrames = 0; 243 | clearInterval(timerInterval); 244 | } 245 | } 246 | 247 | //Add the `show`, `play`, `stop`, and `playSequence` methods to the sprite 248 | sprite.show = show; 249 | sprite.stopAnimation = stopAnimation; 250 | sprite.playAnimation = playAnimation; 251 | } 252 | 253 | //`tilingSpirte` lets you quickly create Pixi tiling sprites 254 | tilingSprite(source, width, height, x, y) { 255 | if (width === undefined) { 256 | throw new Error("Please define a width as your second argument for the tiling sprite"); 257 | } 258 | if (height === undefined) { 259 | throw new Error("Please define a height as your third argument for the tiling sprite"); 260 | } 261 | let o = this.sprite(source, x, y, true, width, height); 262 | 263 | //Add `tileX`, `tileY`, `tileScaleX` and `tileScaleY` properties 264 | Object.defineProperties(o, { 265 | "tileX": { 266 | get() { 267 | return o.tilePosition.x; 268 | }, 269 | set(value) { 270 | o.tilePosition.x = value; 271 | }, 272 | enumerable: true, configurable: true 273 | }, 274 | "tileY": { 275 | get() { 276 | return o.tilePosition.y; 277 | }, 278 | set(value) { 279 | o.tilePosition.y = value; 280 | }, 281 | enumerable: true, configurable: true 282 | }, 283 | "tileScaleX": { 284 | get() { 285 | return o.tileScale.x; 286 | }, 287 | set(value) { 288 | o.tileScale.x = value; 289 | }, 290 | enumerable: true, configurable: true 291 | }, 292 | "tileScaleY": { 293 | get() { 294 | return o.tileScale.y; 295 | }, 296 | set(value) { 297 | o.tileScale.y = value; 298 | }, 299 | enumerable: true, configurable: true 300 | }, 301 | }); 302 | 303 | return o 304 | } 305 | 306 | filmstrip( 307 | texture, 308 | frameWidth, 309 | frameHeight, 310 | spacing = 0 311 | ) { 312 | 313 | //An array to store the x/y positions of the frames 314 | let positions = []; 315 | 316 | //Find the width and height of the texture 317 | let textureWidth = this.TextureCache[texture].width, 318 | textureHeight = this.TextureCache[texture].height; 319 | 320 | //Find out how many columns and rows there are 321 | let columns = textureWidth / frameWidth, 322 | rows = textureHeight / frameHeight; 323 | 324 | //Find the total number of frames 325 | let numberOfFrames = columns * rows; 326 | 327 | for (let i = 0; i < numberOfFrames; i++) { 328 | 329 | //Find the correct row and column for each frame 330 | //and figure out its x and y position 331 | let x = (i % columns) * frameWidth, 332 | y = Math.floor(i / columns) * frameHeight; 333 | 334 | //Compensate for any optional spacing (padding) around the tiles if 335 | //there is any. This bit of code accumlates the spacing offsets from the 336 | //left side of the tileset and adds them to the current tile's position 337 | if (spacing > 0) { 338 | x += spacing + (spacing * i % columns); 339 | y += spacing + (spacing * Math.floor(i / columns)); 340 | } 341 | 342 | //Add the x and y value of each frame to the `positions` array 343 | positions.push([x, y]); 344 | } 345 | 346 | //Return the frames 347 | return this.frames(texture, positions, frameWidth, frameHeight); 348 | } 349 | 350 | //Make a texture from a frame in another texture or image 351 | frame(source, x, y, width, height) { 352 | 353 | let texture, imageFrame; 354 | 355 | //If the source is a string, it's either a texture in the 356 | //cache or an image file 357 | if (typeof source === "string") { 358 | if (this.TextureCache[source]) { 359 | texture = new this.Texture(this.TextureCache[source]); 360 | } 361 | } 362 | 363 | //If the `source` is a texture, use it 364 | else if (source instanceof this.Texture) { 365 | texture = new this.Texture(source); 366 | } 367 | if (!texture) { 368 | throw new Error(`Please load the ${source} texture into the cache.`); 369 | } else { 370 | 371 | //Make a rectangle the size of the sub-image 372 | imageFrame = new this.Rectangle(x, y, width, height); 373 | texture.frame = imageFrame; 374 | return texture; 375 | } 376 | } 377 | 378 | //Make an array of textures from a 2D array of frame x and y coordinates in 379 | //texture 380 | frames(source, coordinates, frameWidth, frameHeight) { 381 | 382 | let baseTexture, textures; 383 | 384 | //If the source is a string, it's either a texture in the 385 | //cache or an image file 386 | if (typeof source === "string") { 387 | if (this.TextureCache[source]) { 388 | baseTexture = new this.Texture(this.TextureCache[source]); 389 | } 390 | } 391 | //If the `source` is a texture, use it 392 | else if (source instanceof this.Texture) { 393 | baseTexture = new this.Texture(source); 394 | } 395 | if (!baseTexture) { 396 | throw new Error(`Please load the ${source} texture into the cache.`); 397 | } else { 398 | let textures = coordinates.map((position) => { 399 | let x = position[0], 400 | y = position[1]; 401 | let imageFrame = new this.Rectangle(x, y, frameWidth, frameHeight); 402 | let frameTexture = new this.Texture(baseTexture); 403 | frameTexture.frame = imageFrame; 404 | return frameTexture 405 | }); 406 | return textures; 407 | } 408 | } 409 | 410 | frameSeries(startNumber = 0, endNumber = 1, baseName = "", extension = "") { 411 | 412 | //Create an array to store the frame names 413 | let frames = []; 414 | 415 | for (let i = startNumber; i < endNumber + 1; i++) { 416 | let frame = this.TextureCache[`${baseName + i + extension}`]; 417 | frames.push(frame); 418 | } 419 | return frames; 420 | } 421 | 422 | /* Text creation */ 423 | 424 | //The`text` method is a quick way to create a Pixi Text sprite 425 | text(content = "message", font = "16px sans", fillStyle = "red", x = 0, y = 0) { 426 | 427 | //Create a Pixi Sprite object 428 | let message = new this.Text(content, {font: font, fill: fillStyle}); 429 | message.x = x; 430 | message.y = y; 431 | 432 | //Add a `_text` property with a getter/setter 433 | message._content = content; 434 | Object.defineProperty(message, "content", { 435 | get() { 436 | return this._content; 437 | }, 438 | set(value) { 439 | this._content = value; 440 | this.text = value; 441 | }, 442 | enumerable: true, configurable: true 443 | }); 444 | 445 | //Return the text object 446 | return message; 447 | } 448 | 449 | //The`bitmapText` method lets you create bitmap text 450 | bitmapText(content = "message", font, align, tint, x = 0, y = 0) { 451 | 452 | //Create a Pixi Sprite object 453 | let message = new this.BitmapText(content, {font: font, align: align, tint: tint}); 454 | message.x = x; 455 | message.y = y; 456 | 457 | //Add a `_text` property with a getter/setter 458 | message._content = content; 459 | Object.defineProperty(message, "content", { 460 | get() { 461 | return this._content; 462 | }, 463 | set(value) { 464 | this._content = value; 465 | this.text = value; 466 | }, 467 | enumerable: true, configurable: true 468 | }); 469 | 470 | //Return the text object 471 | return message; 472 | } 473 | 474 | /* Shapes and lines */ 475 | 476 | //Rectangle 477 | rectangle( 478 | width = 32, 479 | height = 32, 480 | fillStyle = 0xFF3300, 481 | strokeStyle = 0x0033CC, 482 | lineWidth = 0, 483 | x = 0, 484 | y = 0 485 | ){ 486 | 487 | let o = new this.Graphics(); 488 | o._sprite = undefined; 489 | o._width = width; 490 | o._height = height; 491 | o._fillStyle = this.color(fillStyle); 492 | o._strokeStyle = this.color(strokeStyle); 493 | o._lineWidth = lineWidth; 494 | 495 | //Draw the rectangle 496 | let draw = (width, height, fillStyle, strokeStyle, lineWidth) => { 497 | o.clear(); 498 | o.beginFill(fillStyle); 499 | if (lineWidth > 0) { 500 | o.lineStyle(lineWidth, strokeStyle, 1); 501 | } 502 | o.drawRect(0, 0, width, height); 503 | o.endFill(); 504 | }; 505 | 506 | //Draw the line and capture the sprite that the `draw` function 507 | //returns 508 | draw(o._width, o._height, o._fillStyle, o._strokeStyle, o._lineWidth); 509 | 510 | //Generate a texture from the rectangle 511 | let texture = o.generateTexture(); 512 | 513 | //Use the texture to create a sprite 514 | let sprite = new this.Sprite(texture); 515 | 516 | //Position the sprite 517 | sprite.x = x; 518 | sprite.y = y; 519 | 520 | //Add getters and setters to the sprite 521 | let self = this; 522 | Object.defineProperties(sprite, { 523 | "fillStyle": { 524 | get() { 525 | return o._fillStyle; 526 | }, 527 | set(value) { 528 | o._fillStyle = self.color(value); 529 | 530 | //Draw the new rectangle 531 | draw(o._width, o._height, o._fillStyle, o._strokeStyle, o._lineWidth, o._x, o._y); 532 | 533 | //Generate a new texture and set it as the sprite's texture 534 | let texture = o.generateTexture(); 535 | o._sprite.texture = texture; 536 | }, 537 | enumerable: true, configurable: true 538 | }, 539 | "strokeStyle": { 540 | get() { 541 | return o._strokeStyle; 542 | }, 543 | set(value) { 544 | o._strokeStyle = self.color(value); 545 | 546 | //Draw the new rectangle 547 | draw(o._width, o._height, o._fillStyle, o._strokeStyle, o._lineWidth, o._x, o._y); 548 | 549 | //Generate a new texture and set it as the sprite's texture 550 | let texture = o.generateTexture(); 551 | o._sprite.texture = texture; 552 | }, 553 | enumerable: true, configurable: true 554 | }, 555 | "lineWidth": { 556 | get() { 557 | return o._lineWidth; 558 | }, 559 | set(value) { 560 | o._lineWidth = value; 561 | 562 | //Draw the new rectangle 563 | draw(o._width, o._height, o._fillStyle, o._strokeStyle, o._lineWidth, o._x, o._y); 564 | 565 | //Generate a new texture and set it as the sprite's texture 566 | let texture = o.generateTexture(); 567 | o._sprite.texture = texture; 568 | }, 569 | enumerable: true, configurable: true 570 | } 571 | }); 572 | 573 | //Get a local reference to the sprite so that we can 574 | //change the rectangle properties later using the getters/setters 575 | o._sprite = sprite; 576 | 577 | //Return the sprite 578 | return sprite; 579 | } 580 | 581 | //Circle 582 | circle( 583 | diameter = 32, 584 | fillStyle = 0xFF3300, 585 | strokeStyle = 0x0033CC, 586 | lineWidth = 0, 587 | x = 0, 588 | y = 0 589 | ){ 590 | 591 | let o = new this.Graphics(); 592 | o._diameter = diameter; 593 | o._fillStyle = this.color(fillStyle); 594 | o._strokeStyle = this.color(strokeStyle); 595 | o._lineWidth = lineWidth; 596 | 597 | //Draw the circle 598 | let draw = (diameter, fillStyle, strokeStyle, lineWidth) => { 599 | o.clear(); 600 | o.beginFill(fillStyle); 601 | if (lineWidth > 0) { 602 | o.lineStyle(lineWidth, strokeStyle, 1); 603 | } 604 | o.drawCircle(0, 0, diameter / 2); 605 | o.endFill(); 606 | }; 607 | 608 | //Draw the cirlce 609 | draw(o._diameter, o._fillStyle, o._strokeStyle, o._lineWidth); 610 | 611 | //Generate a texture from the rectangle 612 | let texture = o.generateTexture(); 613 | 614 | //Use the texture to create a sprite 615 | let sprite = new this.Sprite(texture); 616 | 617 | //Position the sprite 618 | sprite.x = x; 619 | sprite.y = y; 620 | 621 | //Add getters and setters to the sprite 622 | let self = this; 623 | Object.defineProperties(sprite, { 624 | "fillStyle": { 625 | get() { 626 | return o._fillStyle; 627 | }, 628 | set(value) { 629 | o._fillStyle = self.color(value); 630 | 631 | //Draw the cirlce 632 | draw(o._diameter, o._fillStyle, o._strokeStyle, o._lineWidth); 633 | 634 | //Generate a new texture and set it as the sprite's texture 635 | let texture = o.generateTexture(); 636 | o._sprite.texture = texture; 637 | }, 638 | enumerable: true, configurable: true 639 | }, 640 | "strokeStyle": { 641 | get() { 642 | return o._strokeStyle; 643 | }, 644 | set(value) { 645 | o._strokeStyle = self.color(value); 646 | 647 | //Draw the cirlce 648 | draw(o._diameter, o._fillStyle, o._strokeStyle, o._lineWidth); 649 | 650 | //Generate a new texture and set it as the sprite's texture 651 | let texture = o.generateTexture(); 652 | o._sprite.texture = texture; 653 | }, 654 | enumerable: true, configurable: true 655 | }, 656 | "diameter": { 657 | get() { 658 | return o._diameter; 659 | }, 660 | set(value) { 661 | o._lineWidth = 10; 662 | 663 | //Draw the cirlce 664 | draw(o._diameter, o._fillStyle, o._strokeStyle, o._lineWidth); 665 | 666 | //Generate a new texture and set it as the sprite's texture 667 | let texture = o.generateTexture(); 668 | o._sprite.texture = texture; 669 | }, 670 | enumerable: true, configurable: true 671 | }, 672 | "radius": { 673 | get() { 674 | return o._diameter / 2; 675 | }, 676 | set(value) { 677 | 678 | //Draw the cirlce 679 | draw(value * 2, o._fillStyle, o._strokeStyle, o._lineWidth); 680 | 681 | //Generate a new texture and set it as the sprite's texture 682 | let texture = o.generateTexture(); 683 | o._sprite.texture = texture; 684 | }, 685 | enumerable: true, configurable: true 686 | }, 687 | }); 688 | //Get a local reference to the sprite so that we can 689 | //change the circle properties later using the getters/setters 690 | o._sprite = sprite; 691 | 692 | //Return the sprite 693 | return sprite; 694 | } 695 | 696 | //Line 697 | line( 698 | strokeStyle = 0x000000, 699 | lineWidth = 1, 700 | ax = 0, 701 | ay = 0, 702 | bx = 32, 703 | by = 32 704 | ){ 705 | 706 | //Create the line object 707 | let o = new this.Graphics(); 708 | 709 | //Private properties 710 | o._strokeStyle = this.color(strokeStyle); 711 | o._width = lineWidth; 712 | o._ax = ax; 713 | o._ay = ay; 714 | o._bx = bx; 715 | o._by = by; 716 | 717 | //A helper function that draws the line 718 | let draw = (strokeStyle, lineWidth, ax, ay, bx, by) => { 719 | o.clear(); 720 | o.lineStyle(lineWidth, strokeStyle, 1); 721 | o.moveTo(ax, ay); 722 | o.lineTo(bx, by); 723 | }; 724 | 725 | //Draw the line 726 | draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by); 727 | 728 | //Define getters and setters that redefine the line's start and 729 | //end points and re-draws it if they change 730 | let self = this; 731 | Object.defineProperties(o, { 732 | "ax": { 733 | get() { 734 | return o._ax; 735 | }, 736 | set(value) { 737 | o._ax = value; 738 | draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by); 739 | }, 740 | enumerable: true, configurable: true 741 | }, 742 | "ay": { 743 | get() { 744 | return o._ay; 745 | }, 746 | set(value) { 747 | o._ay = value; 748 | draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by); 749 | }, 750 | enumerable: true, configurable: true 751 | }, 752 | "bx": { 753 | get() { 754 | return o._bx; 755 | }, 756 | set(value) { 757 | o._bx = value; 758 | draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by); 759 | }, 760 | enumerable: true, configurable: true 761 | }, 762 | "by": { 763 | get() { 764 | return o._by; 765 | }, 766 | set(value) { 767 | o._by = value; 768 | draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by); 769 | }, 770 | enumerable: true, configurable: true 771 | }, 772 | "strokeStyle": { 773 | get() { 774 | return o._strokeStyle; 775 | }, 776 | set(value) { 777 | o._strokeStyle = self.color(value); 778 | 779 | //Draw the line 780 | draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by); 781 | }, 782 | enumerable: true, configurable: true 783 | }, 784 | "width": { 785 | get() { 786 | return o._width; 787 | }, 788 | set(value) { 789 | o._width = value; 790 | 791 | //Draw the line 792 | draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by); 793 | }, 794 | enumerable: true, configurable: true 795 | } 796 | }); 797 | 798 | //Return the line 799 | return o; 800 | } 801 | 802 | /* Compound sprites */ 803 | 804 | //Use `grid` to create a grid of sprites 805 | grid( 806 | columns = 0, rows = 0, cellWidth = 32, cellHeight = 32, 807 | centerCell = false, xOffset = 0, yOffset = 0, 808 | makeSprite = undefined, 809 | extra = undefined 810 | ){ 811 | 812 | //Create an empty group called `container`. This `container` 813 | //group is what the function returns back to the main program. 814 | //All the sprites in the grid cells will be added 815 | //as children to this container 816 | let container = new this.Container(); 817 | 818 | //The `create` method plots the grid 819 | 820 | let createGrid = () => { 821 | 822 | //Figure out the number of cells in the grid 823 | let length = columns * rows; 824 | 825 | //Create a sprite for each cell 826 | for(let i = 0; i < length; i++) { 827 | 828 | //Figure out the sprite's x/y placement in the grid 829 | let x = (i % columns) * cellWidth, 830 | y = Math.floor(i / columns) * cellHeight; 831 | 832 | //Use the `makeSprite` function supplied in the constructor 833 | //to make a sprite for the grid cell 834 | let sprite = makeSprite(); 835 | 836 | //Add the sprite to the `container` 837 | container.addChild(sprite); 838 | 839 | //Should the sprite be centered in the cell? 840 | 841 | //No, it shouldn't be centered 842 | if (!centerCell) { 843 | sprite.x = x + xOffset; 844 | sprite.y = y + yOffset; 845 | } 846 | 847 | //Yes, it should be centered 848 | else { 849 | sprite.x 850 | = x + (cellWidth / 2) 851 | - (sprite.width / 2) + xOffset; 852 | sprite.y 853 | = y + (cellHeight / 2) 854 | - (sprite.width / 2) + yOffset; 855 | } 856 | 857 | //Run any optional extra code. This calls the 858 | //`extra` function supplied by the constructor 859 | if (extra) extra(sprite); 860 | } 861 | }; 862 | 863 | //Run the `createGrid` method 864 | createGrid(); 865 | 866 | //Return the `container` group back to the main program 867 | return container; 868 | } 869 | 870 | //Use `shoot` to create bullet sprites 871 | shoot( 872 | shooter, angle, x, y, container, bulletSpeed, bulletArray, bulletSprite 873 | ) { 874 | 875 | //Make a new sprite using the user-supplied `bulletSprite` function 876 | let bullet = bulletSprite(); 877 | 878 | //Set the bullet's anchor point to its center 879 | bullet.anchor.set(0.5, 0.5); 880 | 881 | //Temporarily add the bullet to the shooter 882 | //so that we can position it relative to the 883 | //shooter's position 884 | shooter.addChild(bullet); 885 | bullet.x = x; 886 | bullet.y = y; 887 | 888 | //Find the bullet's global coordinates so that we can use 889 | //them to position the bullet on the new parent container 890 | let tempGx = bullet.getGlobalPosition().x, 891 | tempGy = bullet.getGlobalPosition().y; 892 | 893 | //Add the bullet to the new parent container using 894 | //the new global coordinates 895 | container.addChild(bullet); 896 | bullet.x = tempGx; 897 | bullet.y = tempGy; 898 | 899 | //Set the bullet's velocity 900 | bullet.vx = Math.cos(angle) * bulletSpeed; 901 | bullet.vy = Math.sin(angle) * bulletSpeed; 902 | 903 | //Push the bullet into the `bulletArray` 904 | bulletArray.push(bullet); 905 | } 906 | 907 | /* 908 | grid 909 | ---- 910 | 911 | Helps you to automatically create a grid of sprites. `grid` returns a 912 | `group` sprite object that contains a sprite for every cell in the 913 | grid. You can define the rows and columns in the grid, whether or 914 | not the sprites should be centered inside each cell, or what their offset from the 915 | top left corner of each cell should be. Supply a function that 916 | returns the sprite that you want to make for each cell. You can 917 | supply an optional final function that runs any extra code after 918 | each sprite has been created. Here's the format for creating a grid: 919 | 920 | gridGroup = grid( 921 | 922 | //Set the grid's properties 923 | columns, rows, cellWidth, cellHeight, 924 | areSpirtesCentered?, xOffset, yOffset, 925 | 926 | //A function that returns a sprite 927 | () => g.circle(16, "blue"), 928 | 929 | //An optional final function that runs some extra code 930 | () => console.log("extra!") 931 | ); 932 | */ 933 | 934 | grid( 935 | columns = 0, rows = 0, cellWidth = 32, cellHeight = 32, 936 | centerCell = false, xOffset = 0, yOffset = 0, 937 | makeSprite = undefined, 938 | extra = undefined 939 | ){ 940 | 941 | //Create an empty group called `container`. This `container` 942 | //group is what the function returns back to the main program. 943 | //All the sprites in the grid cells will be added 944 | //as children to this container 945 | let container = this.group(); 946 | 947 | //The `create` method plots the grid 948 | let createGrid = () => { 949 | 950 | //Figure out the number of cells in the grid 951 | let length = columns * rows; 952 | 953 | //Create a sprite for each cell 954 | for(let i = 0; i < length; i++) { 955 | 956 | //Figure out the sprite's x/y placement in the grid 957 | let x = (i % columns) * cellWidth, 958 | y = Math.floor(i / columns) * cellHeight; 959 | 960 | //Use the `makeSprite` function supplied in the constructor 961 | //to make a sprite for the grid cell 962 | let sprite = makeSprite(); 963 | 964 | //Add the sprite to the `container` 965 | container.addChild(sprite); 966 | 967 | //Should the sprite be centered in the cell? 968 | 969 | //No, it shouldn't be centered 970 | if (!centerCell) { 971 | sprite.x = x + xOffset; 972 | sprite.y = y + yOffset; 973 | } 974 | 975 | //Yes, it should be centered 976 | else { 977 | sprite.x 978 | = x + (cellWidth / 2) 979 | - sprite.halfWidth + xOffset; 980 | sprite.y 981 | = y + (cellHeight / 2) 982 | - sprite.halfHeight + yOffset; 983 | } 984 | 985 | //Run any optional extra code. This calls the 986 | //`extra` function supplied by the constructor 987 | if (extra) extra(sprite); 988 | } 989 | }; 990 | 991 | //Run the `createGrid` method 992 | createGrid(); 993 | 994 | //Return the `container` group back to the main program 995 | return container; 996 | } 997 | 998 | /* 999 | shake 1000 | ----- 1001 | 1002 | Used to create a shaking effect, like a screen shake 1003 | */ 1004 | 1005 | shake(sprite, magnitude = 16, angular = false) { 1006 | 1007 | //Get a reference to this current object so that 1008 | //it's easy to maintain scope in the nested sub-functions 1009 | let self = this; 1010 | 1011 | //A counter to count the number of shakes 1012 | let counter = 1; 1013 | 1014 | //The total number of shakes (there will be 1 shake per frame) 1015 | let numberOfShakes = 10; 1016 | 1017 | //Capture the sprite's position and angle so you can 1018 | //restore them after the shaking has finished 1019 | let startX = sprite.x, 1020 | startY = sprite.y, 1021 | startAngle = sprite.rotation; 1022 | 1023 | //Divide the magnitude into 10 units so that you can 1024 | //reduce the amount of shake by 10 percent each frame 1025 | let magnitudeUnit = magnitude / numberOfShakes; 1026 | 1027 | //The `randomInt` helper function 1028 | let randomInt = (min, max) => { 1029 | return Math.floor(Math.random() * (max - min + 1)) + min; 1030 | }; 1031 | 1032 | //Add the sprite to the `shakingSprites` array if it 1033 | //isn't already there 1034 | if(self.shakingSprites.indexOf(sprite) === -1) { 1035 | 1036 | self.shakingSprites.push(sprite); 1037 | 1038 | //Add an `updateShake` method to the sprite. 1039 | //The `updateShake` method will be called each frame 1040 | //in the game loop. The shake effect type can be either 1041 | //up and down (x/y shaking) or angular (rotational shaking). 1042 | sprite.updateShake = () => { 1043 | if(angular) { 1044 | angularShake(); 1045 | } else { 1046 | upAndDownShake(); 1047 | } 1048 | }; 1049 | } 1050 | 1051 | //The `upAndDownShake` function 1052 | function upAndDownShake() { 1053 | 1054 | //Shake the sprite while the `counter` is less than 1055 | //the `numberOfShakes` 1056 | if (counter < numberOfShakes) { 1057 | 1058 | //Reset the sprite's position at the start of each shake 1059 | sprite.x = startX; 1060 | sprite.y = startY; 1061 | 1062 | //Reduce the magnitude 1063 | magnitude -= magnitudeUnit; 1064 | 1065 | //Randomly change the sprite's position 1066 | sprite.x += randomInt(-magnitude, magnitude); 1067 | sprite.y += randomInt(-magnitude, magnitude); 1068 | 1069 | //Add 1 to the counter 1070 | counter += 1; 1071 | } 1072 | 1073 | //When the shaking is finished, restore the sprite to its original 1074 | //position and remove it from the `shakingSprites` array 1075 | if (counter >= numberOfShakes) { 1076 | sprite.x = startX; 1077 | sprite.y = startY; 1078 | self.shakingSprites.splice(self.shakingSprites.indexOf(sprite), 1); 1079 | } 1080 | } 1081 | 1082 | //The `angularShake` function 1083 | //First set the initial tilt angle to the right (+1) 1084 | let tiltAngle = 1; 1085 | 1086 | function angularShake() { 1087 | if (counter < numberOfShakes) { 1088 | 1089 | //Reset the sprite's rotation 1090 | sprite.rotation = startAngle; 1091 | 1092 | //Reduce the magnitude 1093 | magnitude -= magnitudeUnit; 1094 | 1095 | //Rotate the sprite left or right, depending on the direction, 1096 | //by an amount in radians that matches the magnitude 1097 | sprite.rotation = magnitude * tiltAngle; 1098 | counter += 1; 1099 | 1100 | //Reverse the tilt angle so that the sprite is tilted 1101 | //in the opposite direction for the next shake 1102 | tiltAngle *= -1; 1103 | } 1104 | 1105 | //When the shaking is finished, reset the sprite's angle and 1106 | //remove it from the `shakingSprites` array 1107 | if (counter >= numberOfShakes) { 1108 | sprite.rotation = startAngle; 1109 | self.shakingSprites.splice(self.shakingSprites.indexOf(sprite), 1); 1110 | } 1111 | } 1112 | } 1113 | 1114 | /* 1115 | _getCenter 1116 | ---------- 1117 | 1118 | A utility that finds the center point of the sprite. If it's anchor point is the 1119 | sprite's top left corner, then the center is calculated from that point. 1120 | If the anchor point has been shifted, then the anchor x/y point is used as the sprite's center 1121 | */ 1122 | 1123 | _getCenter(o, dimension, axis) { 1124 | if (o.anchor !== undefined) { 1125 | if (o.anchor[axis] !== 0) { 1126 | return 0; 1127 | } else { 1128 | return dimension / 2; 1129 | } 1130 | } else { 1131 | return dimension; 1132 | } 1133 | } 1134 | 1135 | 1136 | 1137 | /* Groups */ 1138 | 1139 | //Group sprites into a container 1140 | group(...sprites) { 1141 | let container = new this.Container(); 1142 | sprites.forEach(sprite => { 1143 | container.addChild(sprite); 1144 | }); 1145 | return container; 1146 | } 1147 | 1148 | //Use the `batch` method to create a ParticleContainer 1149 | batch(size = 15000, options = {rotation: true, alpha: true, scale: true, uvs: true}) { 1150 | let o = new this.ParticleContainer(size, options); 1151 | return o; 1152 | } 1153 | 1154 | //`remove` is a global convenience method that will 1155 | //remove any sprite, or an argument list of sprites, from its parent. 1156 | remove(...sprites) { 1157 | 1158 | //Remove sprites that's aren't in an array 1159 | if (!(sprites[0] instanceof Array)) { 1160 | if (sprites.length > 1) { 1161 | sprites.forEach(sprite => { 1162 | sprite.parent.removeChild(sprite); 1163 | }); 1164 | } else { 1165 | sprites[0].parent.removeChild(sprites[0]); 1166 | } 1167 | } 1168 | 1169 | //Remove sprites in an array of sprites 1170 | else { 1171 | let spritesArray = sprites[0]; 1172 | if (spritesArray.length > 0) { 1173 | for (let i = spritesArray.length - 1; i >= 0; i--) { 1174 | let sprite = spritesArray[i]; 1175 | sprite.parent.removeChild(sprite); 1176 | spritesArray.splice(spritesArray.indexOf(sprite), 1); 1177 | } 1178 | } 1179 | } 1180 | } 1181 | 1182 | /* Color conversion */ 1183 | //From: http://stackoverflow.com/questions/1573053/javascript-function-to-convert-color-names-to-hex-codes 1184 | //Utilities to convert HTML color string names to hexadecimal codes 1185 | 1186 | colorToRGBA(color) { 1187 | // Returns the color as an array of [r, g, b, a] -- all range from 0 - 255 1188 | // color must be a valid canvas fillStyle. This will cover most anything 1189 | // you'd want to use. 1190 | // Examples: 1191 | // colorToRGBA('red') # [255, 0, 0, 255] 1192 | // colorToRGBA('#f00') # [255, 0, 0, 255] 1193 | var cvs, ctx; 1194 | cvs = document.createElement('canvas'); 1195 | cvs.height = 1; 1196 | cvs.width = 1; 1197 | ctx = cvs.getContext('2d'); 1198 | ctx.fillStyle = color; 1199 | ctx.fillRect(0, 0, 1, 1); 1200 | let data = ctx.getImageData(0, 0, 1, 1).data; 1201 | return data; 1202 | } 1203 | 1204 | byteToHex(num) { 1205 | // Turns a number (0-255) into a 2-character hex number (00-ff) 1206 | return ('0'+num.toString(16)).slice(-2); 1207 | } 1208 | 1209 | colorToHex(color) { 1210 | // Convert any CSS color to a hex representation 1211 | // Examples: 1212 | // colorToHex('red') # '#ff0000' 1213 | // colorToHex('rgb(255, 0, 0)') # '#ff0000' 1214 | var rgba, hex; 1215 | rgba = this.colorToRGBA(color); 1216 | hex = [0,1,2].map( 1217 | idx => this.byteToHex(rgba[idx]) 1218 | ).join(''); 1219 | return "0x" + hex; 1220 | } 1221 | 1222 | //A function to find out if the user entered a number (a hex color 1223 | //code) or a string (an HTML color string) 1224 | color(value) { 1225 | 1226 | //Check if it's a number 1227 | if(!isNaN(value)){ 1228 | 1229 | //Yes, it is a number, so just return it 1230 | return value; 1231 | } 1232 | 1233 | //No it's not a number, so it must be a string 1234 | else { 1235 | 1236 | return parseInt(this.colorToHex(value)); 1237 | /* 1238 | 1239 | //Find out what kind of color string it is. 1240 | //Let's first grab the first character of the string 1241 | let firstCharacter = value.charAt(0); 1242 | 1243 | //If the first character is a "#" or a number, then 1244 | //we know it must be a RGBA color 1245 | if (firstCharacter === "#") { 1246 | console.log("first character: " + value.charAt(0)) 1247 | } 1248 | */ 1249 | } 1250 | 1251 | /* 1252 | //Find out if the first character in the string is a number 1253 | if (!isNaN(parseInt(string.charAt(0)))) { 1254 | 1255 | //It's not, so convert it to a hex code 1256 | return colorToHex(string); 1257 | 1258 | //The use input a number, so it must be a hex code. Just return it 1259 | } else { 1260 | 1261 | return string; 1262 | } 1263 | 1264 | */ 1265 | 1266 | } 1267 | 1268 | } 1269 | 1270 | 1271 | 1272 | -------------------------------------------------------------------------------- /bin/spriteUtilities.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 4 | 5 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6 | 7 | var SpriteUtilities = (function () { 8 | function SpriteUtilities() { 9 | var renderingEngine = arguments.length <= 0 || arguments[0] === undefined ? PIXI : arguments[0]; 10 | 11 | _classCallCheck(this, SpriteUtilities); 12 | 13 | if (renderingEngine === undefined) throw new Error("Please supply a reference to PIXI in the SpriteUtilities constructor before using spriteUtilities.js"); 14 | 15 | //Find out which rendering engine is being used (the default is Pixi) 16 | this.renderer = ""; 17 | 18 | //If the `renderingEngine` is Pixi, set up Pixi object aliases 19 | if (renderingEngine.ParticleContainer && renderingEngine.Sprite) { 20 | this.renderer = "pixi"; 21 | this.Container = renderingEngine.Container; 22 | this.ParticleContainer = renderingEngine.ParticleContainer; 23 | this.TextureCache = renderingEngine.utils.TextureCache; 24 | this.Texture = renderingEngine.Texture; 25 | this.Rectangle = renderingEngine.Rectangle; 26 | this.MovieClip = renderingEngine.extras.MovieClip; 27 | this.BitmapText = renderingEngine.extras.BitmapText; 28 | this.Sprite = renderingEngine.Sprite; 29 | this.TilingSprite = renderingEngine.extras.TilingSprite; 30 | this.Graphics = renderingEngine.Graphics; 31 | this.Text = renderingEngine.Text; 32 | 33 | //An array to store all the shaking sprites 34 | this.shakingSprites = []; 35 | } 36 | } 37 | 38 | _createClass(SpriteUtilities, [{ 39 | key: "update", 40 | value: function update() { 41 | if (this.shakingSprites.length > 0) { 42 | for (var i = this.shakingSprites.length - 1; i >= 0; i--) { 43 | var shakingSprite = this.shakingSprites[i]; 44 | if (shakingSprite.updateShake) shakingSprite.updateShake(); 45 | } 46 | } 47 | } 48 | }, { 49 | key: "sprite", 50 | value: function sprite(source) { 51 | var x = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1]; 52 | var y = arguments.length <= 2 || arguments[2] === undefined ? 0 : arguments[2]; 53 | var tiling = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3]; 54 | var width = arguments[4]; 55 | var height = arguments[5]; 56 | 57 | var o = undefined, 58 | texture = undefined; 59 | 60 | //Create a sprite if the `source` is a string 61 | if (typeof source === "string") { 62 | 63 | //Access the texture in the cache if it's there 64 | if (this.TextureCache[source]) { 65 | texture = this.TextureCache[source]; 66 | } 67 | 68 | //If it's not is the cache, load it from the source file 69 | else { 70 | texture = this.Texture.fromImage(source); 71 | } 72 | 73 | //If the texture was created, make the o 74 | if (texture) { 75 | 76 | //If `tiling` is `false`, make a regular `Sprite` 77 | if (!tiling) { 78 | o = new this.Sprite(texture); 79 | } 80 | 81 | //If `tiling` is `true` make a `TilingSprite` 82 | else { 83 | o = new this.TilingSprite(texture, width, height); 84 | } 85 | } 86 | //But if the source still can't be found, alert the user 87 | else { 88 | throw new Error(source + " cannot be found"); 89 | } 90 | } 91 | 92 | //Create a o if the `source` is a texture 93 | else if (source instanceof this.Texture) { 94 | if (!tiling) { 95 | o = new this.Sprite(source); 96 | } else { 97 | o = new this.TilingSprite(source, width, height); 98 | } 99 | } 100 | 101 | //Create a `MovieClip` o if the `source` is an array 102 | else if (source instanceof Array) { 103 | 104 | //Is it an array of frame ids or textures? 105 | if (typeof source[0] === "string") { 106 | 107 | //They're strings, but are they pre-existing texture or 108 | //paths to image files? 109 | //Check to see if the first element matches a texture in the 110 | //cache 111 | if (this.TextureCache[source[0]]) { 112 | 113 | //It does, so it's an array of frame ids 114 | o = this.MovieClip.fromFrames(source); 115 | } else { 116 | 117 | //It's not already in the cache, so let's load it 118 | o = this.MovieClip.fromImages(source); 119 | } 120 | } 121 | 122 | //If the `source` isn't an array of strings, check whether 123 | //it's an array of textures 124 | else if (source[0] instanceof this.Texture) { 125 | 126 | //Yes, it's an array of textures. 127 | //Use them to make a MovieClip o 128 | o = new this.MovieClip(source); 129 | } 130 | } 131 | 132 | //If the sprite was successfully created, intialize it 133 | if (o) { 134 | 135 | //Position the sprite 136 | o.x = x; 137 | o.y = y; 138 | 139 | //Set optional width and height 140 | if (width) o.width = width; 141 | if (height) o.height = height; 142 | 143 | //If the sprite is a MovieClip, add a state player so that 144 | //it's easier to control 145 | if (o instanceof this.MovieClip) this.addStatePlayer(o); 146 | 147 | //Assign the sprite 148 | return o; 149 | } 150 | } 151 | }, { 152 | key: "addStatePlayer", 153 | value: function addStatePlayer(sprite) { 154 | 155 | var frameCounter = 0, 156 | numberOfFrames = 0, 157 | startFrame = 0, 158 | endFrame = 0, 159 | timerInterval = undefined; 160 | 161 | //The `show` function (to display static states) 162 | function show(frameNumber) { 163 | 164 | //Reset any possible previous animations 165 | reset(); 166 | 167 | //Find the new state on the sprite 168 | sprite.gotoAndStop(frameNumber); 169 | } 170 | 171 | //The `stop` function stops the animation at the current frame 172 | function stopAnimation() { 173 | reset(); 174 | sprite.gotoAndStop(sprite.currentFrame); 175 | } 176 | 177 | //The `playSequence` function, to play a sequence of frames 178 | function playAnimation(sequenceArray) { 179 | 180 | //Reset any possible previous animations 181 | reset(); 182 | 183 | //Figure out how many frames there are in the range 184 | if (!sequenceArray) { 185 | startFrame = 0; 186 | endFrame = sprite.totalFrames - 1; 187 | } else { 188 | startFrame = sequenceArray[0]; 189 | endFrame = sequenceArray[1]; 190 | } 191 | 192 | //Calculate the number of frames 193 | numberOfFrames = endFrame - startFrame; 194 | 195 | //Compensate for two edge cases: 196 | //1. If the `startFrame` happens to be `0` 197 | /* 198 | if (startFrame === 0) { 199 | numberOfFrames += 1; 200 | frameCounter += 1; 201 | } 202 | */ 203 | 204 | //2. If only a two-frame sequence was provided 205 | /* 206 | if(numberOfFrames === 1) { 207 | numberOfFrames = 2; 208 | frameCounter += 1; 209 | } 210 | */ 211 | 212 | //Calculate the frame rate. Set the default fps to 12 213 | if (!sprite.fps) sprite.fps = 12; 214 | var frameRate = 1000 / sprite.fps; 215 | 216 | //Set the sprite to the starting frame 217 | sprite.gotoAndStop(startFrame); 218 | 219 | //Set the `frameCounter` to the first frame 220 | frameCounter = 1; 221 | 222 | //If the state isn't already `playing`, start it 223 | if (!sprite.animating) { 224 | timerInterval = setInterval(advanceFrame.bind(this), frameRate); 225 | sprite.animating = true; 226 | } 227 | } 228 | 229 | //`advanceFrame` is called by `setInterval` to display the next frame 230 | //in the sequence based on the `frameRate`. When the frame sequence 231 | //reaches the end, it will either stop or loop 232 | function advanceFrame() { 233 | 234 | //Advance the frame if `frameCounter` is less than 235 | //the state's total frames 236 | if (frameCounter < numberOfFrames + 1) { 237 | 238 | //Advance the frame 239 | sprite.gotoAndStop(sprite.currentFrame + 1); 240 | 241 | //Update the frame counter 242 | frameCounter += 1; 243 | 244 | //If we've reached the last frame and `loop` 245 | //is `true`, then start from the first frame again 246 | } else { 247 | if (sprite.loop) { 248 | sprite.gotoAndStop(startFrame); 249 | frameCounter = 1; 250 | } 251 | } 252 | } 253 | 254 | function reset() { 255 | 256 | //Reset `sprite.playing` to `false`, set the `frameCounter` to 0, //and clear the `timerInterval` 257 | if (timerInterval !== undefined && sprite.animating === true) { 258 | sprite.animating = false; 259 | frameCounter = 0; 260 | startFrame = 0; 261 | endFrame = 0; 262 | numberOfFrames = 0; 263 | clearInterval(timerInterval); 264 | } 265 | } 266 | 267 | //Add the `show`, `play`, `stop`, and `playSequence` methods to the sprite 268 | sprite.show = show; 269 | sprite.stopAnimation = stopAnimation; 270 | sprite.playAnimation = playAnimation; 271 | } 272 | 273 | //`tilingSpirte` lets you quickly create Pixi tiling sprites 274 | 275 | }, { 276 | key: "tilingSprite", 277 | value: function tilingSprite(source, width, height, x, y) { 278 | if (width === undefined) { 279 | throw new Error("Please define a width as your second argument for the tiling sprite"); 280 | } 281 | if (height === undefined) { 282 | throw new Error("Please define a height as your third argument for the tiling sprite"); 283 | } 284 | var o = this.sprite(source, x, y, true, width, height); 285 | 286 | //Add `tileX`, `tileY`, `tileScaleX` and `tileScaleY` properties 287 | Object.defineProperties(o, { 288 | "tileX": { 289 | get: function get() { 290 | return o.tilePosition.x; 291 | }, 292 | set: function set(value) { 293 | o.tilePosition.x = value; 294 | }, 295 | 296 | enumerable: true, configurable: true 297 | }, 298 | "tileY": { 299 | get: function get() { 300 | return o.tilePosition.y; 301 | }, 302 | set: function set(value) { 303 | o.tilePosition.y = value; 304 | }, 305 | 306 | enumerable: true, configurable: true 307 | }, 308 | "tileScaleX": { 309 | get: function get() { 310 | return o.tileScale.x; 311 | }, 312 | set: function set(value) { 313 | o.tileScale.x = value; 314 | }, 315 | 316 | enumerable: true, configurable: true 317 | }, 318 | "tileScaleY": { 319 | get: function get() { 320 | return o.tileScale.y; 321 | }, 322 | set: function set(value) { 323 | o.tileScale.y = value; 324 | }, 325 | 326 | enumerable: true, configurable: true 327 | } 328 | }); 329 | 330 | return o; 331 | } 332 | }, { 333 | key: "filmstrip", 334 | value: function filmstrip(texture, frameWidth, frameHeight) { 335 | var spacing = arguments.length <= 3 || arguments[3] === undefined ? 0 : arguments[3]; 336 | 337 | //An array to store the x/y positions of the frames 338 | var positions = []; 339 | 340 | //Find the width and height of the texture 341 | var textureWidth = this.TextureCache[texture].width, 342 | textureHeight = this.TextureCache[texture].height; 343 | 344 | //Find out how many columns and rows there are 345 | var columns = textureWidth / frameWidth, 346 | rows = textureHeight / frameHeight; 347 | 348 | //Find the total number of frames 349 | var numberOfFrames = columns * rows; 350 | 351 | for (var i = 0; i < numberOfFrames; i++) { 352 | 353 | //Find the correct row and column for each frame 354 | //and figure out its x and y position 355 | var x = i % columns * frameWidth, 356 | y = Math.floor(i / columns) * frameHeight; 357 | 358 | //Compensate for any optional spacing (padding) around the tiles if 359 | //there is any. This bit of code accumlates the spacing offsets from the 360 | //left side of the tileset and adds them to the current tile's position 361 | if (spacing > 0) { 362 | x += spacing + spacing * i % columns; 363 | y += spacing + spacing * Math.floor(i / columns); 364 | } 365 | 366 | //Add the x and y value of each frame to the `positions` array 367 | positions.push([x, y]); 368 | } 369 | 370 | //Return the frames 371 | return this.frames(texture, positions, frameWidth, frameHeight); 372 | } 373 | 374 | //Make a texture from a frame in another texture or image 375 | 376 | }, { 377 | key: "frame", 378 | value: function frame(source, x, y, width, height) { 379 | 380 | var texture = undefined, 381 | imageFrame = undefined; 382 | 383 | //If the source is a string, it's either a texture in the 384 | //cache or an image file 385 | if (typeof source === "string") { 386 | if (this.TextureCache[source]) { 387 | texture = new this.Texture(this.TextureCache[source]); 388 | } 389 | } 390 | 391 | //If the `source` is a texture, use it 392 | else if (source instanceof this.Texture) { 393 | texture = new this.Texture(source); 394 | } 395 | if (!texture) { 396 | throw new Error("Please load the " + source + " texture into the cache."); 397 | } else { 398 | 399 | //Make a rectangle the size of the sub-image 400 | imageFrame = new this.Rectangle(x, y, width, height); 401 | texture.frame = imageFrame; 402 | return texture; 403 | } 404 | } 405 | 406 | //Make an array of textures from a 2D array of frame x and y coordinates in 407 | //texture 408 | 409 | }, { 410 | key: "frames", 411 | value: function frames(source, coordinates, frameWidth, frameHeight) { 412 | var _this = this; 413 | 414 | var baseTexture = undefined, 415 | textures = undefined; 416 | 417 | //If the source is a string, it's either a texture in the 418 | //cache or an image file 419 | if (typeof source === "string") { 420 | if (this.TextureCache[source]) { 421 | baseTexture = new this.Texture(this.TextureCache[source]); 422 | } 423 | } 424 | //If the `source` is a texture, use it 425 | else if (source instanceof this.Texture) { 426 | baseTexture = new this.Texture(source); 427 | } 428 | if (!baseTexture) { 429 | throw new Error("Please load the " + source + " texture into the cache."); 430 | } else { 431 | var _textures = coordinates.map(function (position) { 432 | var x = position[0], 433 | y = position[1]; 434 | var imageFrame = new _this.Rectangle(x, y, frameWidth, frameHeight); 435 | var frameTexture = new _this.Texture(baseTexture); 436 | frameTexture.frame = imageFrame; 437 | return frameTexture; 438 | }); 439 | return _textures; 440 | } 441 | } 442 | }, { 443 | key: "frameSeries", 444 | value: function frameSeries() { 445 | var startNumber = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0]; 446 | var endNumber = arguments.length <= 1 || arguments[1] === undefined ? 1 : arguments[1]; 447 | var baseName = arguments.length <= 2 || arguments[2] === undefined ? "" : arguments[2]; 448 | var extension = arguments.length <= 3 || arguments[3] === undefined ? "" : arguments[3]; 449 | 450 | //Create an array to store the frame names 451 | var frames = []; 452 | 453 | for (var i = startNumber; i < endNumber + 1; i++) { 454 | var frame = this.TextureCache["" + (baseName + i + extension)]; 455 | frames.push(frame); 456 | } 457 | return frames; 458 | } 459 | 460 | /* Text creation */ 461 | 462 | //The`text` method is a quick way to create a Pixi Text sprite 463 | 464 | }, { 465 | key: "text", 466 | value: function text() { 467 | var content = arguments.length <= 0 || arguments[0] === undefined ? "message" : arguments[0]; 468 | var font = arguments.length <= 1 || arguments[1] === undefined ? "16px sans" : arguments[1]; 469 | var fillStyle = arguments.length <= 2 || arguments[2] === undefined ? "red" : arguments[2]; 470 | var x = arguments.length <= 3 || arguments[3] === undefined ? 0 : arguments[3]; 471 | var y = arguments.length <= 4 || arguments[4] === undefined ? 0 : arguments[4]; 472 | 473 | //Create a Pixi Sprite object 474 | var message = new this.Text(content, { font: font, fill: fillStyle }); 475 | message.x = x; 476 | message.y = y; 477 | 478 | //Add a `_text` property with a getter/setter 479 | message._content = content; 480 | Object.defineProperty(message, "content", { 481 | get: function get() { 482 | return this._content; 483 | }, 484 | set: function set(value) { 485 | this._content = value; 486 | this.text = value; 487 | }, 488 | 489 | enumerable: true, configurable: true 490 | }); 491 | 492 | //Return the text object 493 | return message; 494 | } 495 | 496 | //The`bitmapText` method lets you create bitmap text 497 | 498 | }, { 499 | key: "bitmapText", 500 | value: function bitmapText() { 501 | var content = arguments.length <= 0 || arguments[0] === undefined ? "message" : arguments[0]; 502 | var font = arguments[1]; 503 | var align = arguments[2]; 504 | var tint = arguments[3]; 505 | var x = arguments.length <= 4 || arguments[4] === undefined ? 0 : arguments[4]; 506 | var y = arguments.length <= 5 || arguments[5] === undefined ? 0 : arguments[5]; 507 | 508 | //Create a Pixi Sprite object 509 | var message = new this.BitmapText(content, { font: font, align: align, tint: tint }); 510 | message.x = x; 511 | message.y = y; 512 | 513 | //Add a `_text` property with a getter/setter 514 | message._content = content; 515 | Object.defineProperty(message, "content", { 516 | get: function get() { 517 | return this._content; 518 | }, 519 | set: function set(value) { 520 | this._content = value; 521 | this.text = value; 522 | }, 523 | 524 | enumerable: true, configurable: true 525 | }); 526 | 527 | //Return the text object 528 | return message; 529 | } 530 | 531 | /* Shapes and lines */ 532 | 533 | //Rectangle 534 | 535 | }, { 536 | key: "rectangle", 537 | value: function rectangle() { 538 | var width = arguments.length <= 0 || arguments[0] === undefined ? 32 : arguments[0]; 539 | var height = arguments.length <= 1 || arguments[1] === undefined ? 32 : arguments[1]; 540 | var fillStyle = arguments.length <= 2 || arguments[2] === undefined ? 0xFF3300 : arguments[2]; 541 | var strokeStyle = arguments.length <= 3 || arguments[3] === undefined ? 0x0033CC : arguments[3]; 542 | var lineWidth = arguments.length <= 4 || arguments[4] === undefined ? 0 : arguments[4]; 543 | var x = arguments.length <= 5 || arguments[5] === undefined ? 0 : arguments[5]; 544 | var y = arguments.length <= 6 || arguments[6] === undefined ? 0 : arguments[6]; 545 | 546 | var o = new this.Graphics(); 547 | o._sprite = undefined; 548 | o._width = width; 549 | o._height = height; 550 | o._fillStyle = this.color(fillStyle); 551 | o._strokeStyle = this.color(strokeStyle); 552 | o._lineWidth = lineWidth; 553 | 554 | //Draw the rectangle 555 | var draw = function draw(width, height, fillStyle, strokeStyle, lineWidth) { 556 | o.clear(); 557 | o.beginFill(fillStyle); 558 | if (lineWidth > 0) { 559 | o.lineStyle(lineWidth, strokeStyle, 1); 560 | } 561 | o.drawRect(0, 0, width, height); 562 | o.endFill(); 563 | }; 564 | 565 | //Draw the line and capture the sprite that the `draw` function 566 | //returns 567 | draw(o._width, o._height, o._fillStyle, o._strokeStyle, o._lineWidth); 568 | 569 | //Generate a texture from the rectangle 570 | var texture = o.generateTexture(); 571 | 572 | //Use the texture to create a sprite 573 | var sprite = new this.Sprite(texture); 574 | 575 | //Position the sprite 576 | sprite.x = x; 577 | sprite.y = y; 578 | 579 | //Add getters and setters to the sprite 580 | var self = this; 581 | Object.defineProperties(sprite, { 582 | "fillStyle": { 583 | get: function get() { 584 | return o._fillStyle; 585 | }, 586 | set: function set(value) { 587 | o._fillStyle = self.color(value); 588 | 589 | //Draw the new rectangle 590 | draw(o._width, o._height, o._fillStyle, o._strokeStyle, o._lineWidth, o._x, o._y); 591 | 592 | //Generate a new texture and set it as the sprite's texture 593 | var texture = o.generateTexture(); 594 | o._sprite.texture = texture; 595 | }, 596 | 597 | enumerable: true, configurable: true 598 | }, 599 | "strokeStyle": { 600 | get: function get() { 601 | return o._strokeStyle; 602 | }, 603 | set: function set(value) { 604 | o._strokeStyle = self.color(value); 605 | 606 | //Draw the new rectangle 607 | draw(o._width, o._height, o._fillStyle, o._strokeStyle, o._lineWidth, o._x, o._y); 608 | 609 | //Generate a new texture and set it as the sprite's texture 610 | var texture = o.generateTexture(); 611 | o._sprite.texture = texture; 612 | }, 613 | 614 | enumerable: true, configurable: true 615 | }, 616 | "lineWidth": { 617 | get: function get() { 618 | return o._lineWidth; 619 | }, 620 | set: function set(value) { 621 | o._lineWidth = value; 622 | 623 | //Draw the new rectangle 624 | draw(o._width, o._height, o._fillStyle, o._strokeStyle, o._lineWidth, o._x, o._y); 625 | 626 | //Generate a new texture and set it as the sprite's texture 627 | var texture = o.generateTexture(); 628 | o._sprite.texture = texture; 629 | }, 630 | 631 | enumerable: true, configurable: true 632 | } 633 | }); 634 | 635 | //Get a local reference to the sprite so that we can 636 | //change the rectangle properties later using the getters/setters 637 | o._sprite = sprite; 638 | 639 | //Return the sprite 640 | return sprite; 641 | } 642 | 643 | //Circle 644 | 645 | }, { 646 | key: "circle", 647 | value: function circle() { 648 | var diameter = arguments.length <= 0 || arguments[0] === undefined ? 32 : arguments[0]; 649 | var fillStyle = arguments.length <= 1 || arguments[1] === undefined ? 0xFF3300 : arguments[1]; 650 | var strokeStyle = arguments.length <= 2 || arguments[2] === undefined ? 0x0033CC : arguments[2]; 651 | var lineWidth = arguments.length <= 3 || arguments[3] === undefined ? 0 : arguments[3]; 652 | var x = arguments.length <= 4 || arguments[4] === undefined ? 0 : arguments[4]; 653 | var y = arguments.length <= 5 || arguments[5] === undefined ? 0 : arguments[5]; 654 | 655 | var o = new this.Graphics(); 656 | o._diameter = diameter; 657 | o._fillStyle = this.color(fillStyle); 658 | o._strokeStyle = this.color(strokeStyle); 659 | o._lineWidth = lineWidth; 660 | 661 | //Draw the circle 662 | var draw = function draw(diameter, fillStyle, strokeStyle, lineWidth) { 663 | o.clear(); 664 | o.beginFill(fillStyle); 665 | if (lineWidth > 0) { 666 | o.lineStyle(lineWidth, strokeStyle, 1); 667 | } 668 | o.drawCircle(0, 0, diameter / 2); 669 | o.endFill(); 670 | }; 671 | 672 | //Draw the cirlce 673 | draw(o._diameter, o._fillStyle, o._strokeStyle, o._lineWidth); 674 | 675 | //Generate a texture from the rectangle 676 | var texture = o.generateTexture(); 677 | 678 | //Use the texture to create a sprite 679 | var sprite = new this.Sprite(texture); 680 | 681 | //Position the sprite 682 | sprite.x = x; 683 | sprite.y = y; 684 | 685 | //Add getters and setters to the sprite 686 | var self = this; 687 | Object.defineProperties(sprite, { 688 | "fillStyle": { 689 | get: function get() { 690 | return o._fillStyle; 691 | }, 692 | set: function set(value) { 693 | o._fillStyle = self.color(value); 694 | 695 | //Draw the cirlce 696 | draw(o._diameter, o._fillStyle, o._strokeStyle, o._lineWidth); 697 | 698 | //Generate a new texture and set it as the sprite's texture 699 | var texture = o.generateTexture(); 700 | o._sprite.texture = texture; 701 | }, 702 | 703 | enumerable: true, configurable: true 704 | }, 705 | "strokeStyle": { 706 | get: function get() { 707 | return o._strokeStyle; 708 | }, 709 | set: function set(value) { 710 | o._strokeStyle = self.color(value); 711 | 712 | //Draw the cirlce 713 | draw(o._diameter, o._fillStyle, o._strokeStyle, o._lineWidth); 714 | 715 | //Generate a new texture and set it as the sprite's texture 716 | var texture = o.generateTexture(); 717 | o._sprite.texture = texture; 718 | }, 719 | 720 | enumerable: true, configurable: true 721 | }, 722 | "diameter": { 723 | get: function get() { 724 | return o._diameter; 725 | }, 726 | set: function set(value) { 727 | o._lineWidth = 10; 728 | 729 | //Draw the cirlce 730 | draw(o._diameter, o._fillStyle, o._strokeStyle, o._lineWidth); 731 | 732 | //Generate a new texture and set it as the sprite's texture 733 | var texture = o.generateTexture(); 734 | o._sprite.texture = texture; 735 | }, 736 | 737 | enumerable: true, configurable: true 738 | }, 739 | "radius": { 740 | get: function get() { 741 | return o._diameter / 2; 742 | }, 743 | set: function set(value) { 744 | 745 | //Draw the cirlce 746 | draw(value * 2, o._fillStyle, o._strokeStyle, o._lineWidth); 747 | 748 | //Generate a new texture and set it as the sprite's texture 749 | var texture = o.generateTexture(); 750 | o._sprite.texture = texture; 751 | }, 752 | 753 | enumerable: true, configurable: true 754 | } 755 | }); 756 | //Get a local reference to the sprite so that we can 757 | //change the circle properties later using the getters/setters 758 | o._sprite = sprite; 759 | 760 | //Return the sprite 761 | return sprite; 762 | } 763 | 764 | //Line 765 | 766 | }, { 767 | key: "line", 768 | value: function line() { 769 | var strokeStyle = arguments.length <= 0 || arguments[0] === undefined ? 0x000000 : arguments[0]; 770 | var lineWidth = arguments.length <= 1 || arguments[1] === undefined ? 1 : arguments[1]; 771 | var ax = arguments.length <= 2 || arguments[2] === undefined ? 0 : arguments[2]; 772 | var ay = arguments.length <= 3 || arguments[3] === undefined ? 0 : arguments[3]; 773 | var bx = arguments.length <= 4 || arguments[4] === undefined ? 32 : arguments[4]; 774 | var by = arguments.length <= 5 || arguments[5] === undefined ? 32 : arguments[5]; 775 | 776 | //Create the line object 777 | var o = new this.Graphics(); 778 | 779 | //Private properties 780 | o._strokeStyle = this.color(strokeStyle); 781 | o._width = lineWidth; 782 | o._ax = ax; 783 | o._ay = ay; 784 | o._bx = bx; 785 | o._by = by; 786 | 787 | //A helper function that draws the line 788 | var draw = function draw(strokeStyle, lineWidth, ax, ay, bx, by) { 789 | o.clear(); 790 | o.lineStyle(lineWidth, strokeStyle, 1); 791 | o.moveTo(ax, ay); 792 | o.lineTo(bx, by); 793 | }; 794 | 795 | //Draw the line 796 | draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by); 797 | 798 | //Define getters and setters that redefine the line's start and 799 | //end points and re-draws it if they change 800 | var self = this; 801 | Object.defineProperties(o, { 802 | "ax": { 803 | get: function get() { 804 | return o._ax; 805 | }, 806 | set: function set(value) { 807 | o._ax = value; 808 | draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by); 809 | }, 810 | 811 | enumerable: true, configurable: true 812 | }, 813 | "ay": { 814 | get: function get() { 815 | return o._ay; 816 | }, 817 | set: function set(value) { 818 | o._ay = value; 819 | draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by); 820 | }, 821 | 822 | enumerable: true, configurable: true 823 | }, 824 | "bx": { 825 | get: function get() { 826 | return o._bx; 827 | }, 828 | set: function set(value) { 829 | o._bx = value; 830 | draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by); 831 | }, 832 | 833 | enumerable: true, configurable: true 834 | }, 835 | "by": { 836 | get: function get() { 837 | return o._by; 838 | }, 839 | set: function set(value) { 840 | o._by = value; 841 | draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by); 842 | }, 843 | 844 | enumerable: true, configurable: true 845 | }, 846 | "strokeStyle": { 847 | get: function get() { 848 | return o._strokeStyle; 849 | }, 850 | set: function set(value) { 851 | o._strokeStyle = self.color(value); 852 | 853 | //Draw the line 854 | draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by); 855 | }, 856 | 857 | enumerable: true, configurable: true 858 | }, 859 | "width": { 860 | get: function get() { 861 | return o._width; 862 | }, 863 | set: function set(value) { 864 | o._width = value; 865 | 866 | //Draw the line 867 | draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by); 868 | }, 869 | 870 | enumerable: true, configurable: true 871 | } 872 | }); 873 | 874 | //Return the line 875 | return o; 876 | } 877 | 878 | /* Compound sprites */ 879 | 880 | //Use `grid` to create a grid of sprites 881 | 882 | }, { 883 | key: "grid", 884 | value: function grid() { 885 | var columns = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0]; 886 | var rows = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1]; 887 | var cellWidth = arguments.length <= 2 || arguments[2] === undefined ? 32 : arguments[2]; 888 | var cellHeight = arguments.length <= 3 || arguments[3] === undefined ? 32 : arguments[3]; 889 | var centerCell = arguments.length <= 4 || arguments[4] === undefined ? false : arguments[4]; 890 | var xOffset = arguments.length <= 5 || arguments[5] === undefined ? 0 : arguments[5]; 891 | var yOffset = arguments.length <= 6 || arguments[6] === undefined ? 0 : arguments[6]; 892 | var makeSprite = arguments.length <= 7 || arguments[7] === undefined ? undefined : arguments[7]; 893 | var extra = arguments.length <= 8 || arguments[8] === undefined ? undefined : arguments[8]; 894 | 895 | //Create an empty group called `container`. This `container` 896 | //group is what the function returns back to the main program. 897 | //All the sprites in the grid cells will be added 898 | //as children to this container 899 | var container = new this.Container(); 900 | 901 | //The `create` method plots the grid 902 | 903 | var createGrid = function createGrid() { 904 | 905 | //Figure out the number of cells in the grid 906 | var length = columns * rows; 907 | 908 | //Create a sprite for each cell 909 | for (var i = 0; i < length; i++) { 910 | 911 | //Figure out the sprite's x/y placement in the grid 912 | var x = i % columns * cellWidth, 913 | y = Math.floor(i / columns) * cellHeight; 914 | 915 | //Use the `makeSprite` function supplied in the constructor 916 | //to make a sprite for the grid cell 917 | var sprite = makeSprite(); 918 | 919 | //Add the sprite to the `container` 920 | container.addChild(sprite); 921 | 922 | //Should the sprite be centered in the cell? 923 | 924 | //No, it shouldn't be centered 925 | if (!centerCell) { 926 | sprite.x = x + xOffset; 927 | sprite.y = y + yOffset; 928 | } 929 | 930 | //Yes, it should be centered 931 | else { 932 | sprite.x = x + cellWidth / 2 - sprite.width / 2 + xOffset; 933 | sprite.y = y + cellHeight / 2 - sprite.width / 2 + yOffset; 934 | } 935 | 936 | //Run any optional extra code. This calls the 937 | //`extra` function supplied by the constructor 938 | if (extra) extra(sprite); 939 | } 940 | }; 941 | 942 | //Run the `createGrid` method 943 | createGrid(); 944 | 945 | //Return the `container` group back to the main program 946 | return container; 947 | } 948 | 949 | //Use `shoot` to create bullet sprites 950 | 951 | }, { 952 | key: "shoot", 953 | value: function shoot(shooter, angle, x, y, container, bulletSpeed, bulletArray, bulletSprite) { 954 | 955 | //Make a new sprite using the user-supplied `bulletSprite` function 956 | var bullet = bulletSprite(); 957 | 958 | //Set the bullet's anchor point to its center 959 | bullet.anchor.set(0.5, 0.5); 960 | 961 | //Temporarily add the bullet to the shooter 962 | //so that we can position it relative to the 963 | //shooter's position 964 | shooter.addChild(bullet); 965 | bullet.x = x; 966 | bullet.y = y; 967 | 968 | //Find the bullet's global coordinates so that we can use 969 | //them to position the bullet on the new parent container 970 | var tempGx = bullet.getGlobalPosition().x, 971 | tempGy = bullet.getGlobalPosition().y; 972 | 973 | //Add the bullet to the new parent container using 974 | //the new global coordinates 975 | container.addChild(bullet); 976 | bullet.x = tempGx; 977 | bullet.y = tempGy; 978 | 979 | //Set the bullet's velocity 980 | bullet.vx = Math.cos(angle) * bulletSpeed; 981 | bullet.vy = Math.sin(angle) * bulletSpeed; 982 | 983 | //Push the bullet into the `bulletArray` 984 | bulletArray.push(bullet); 985 | } 986 | 987 | /* 988 | grid 989 | ---- 990 | Helps you to automatically create a grid of sprites. `grid` returns a 991 | `group` sprite object that contains a sprite for every cell in the 992 | grid. You can define the rows and columns in the grid, whether or 993 | not the sprites should be centered inside each cell, or what their offset from the 994 | top left corner of each cell should be. Supply a function that 995 | returns the sprite that you want to make for each cell. You can 996 | supply an optional final function that runs any extra code after 997 | each sprite has been created. Here's the format for creating a grid: 998 | gridGroup = grid( 999 | //Set the grid's properties 1000 | columns, rows, cellWidth, cellHeight, 1001 | areSpirtesCentered?, xOffset, yOffset, 1002 | //A function that returns a sprite 1003 | () => g.circle(16, "blue"), 1004 | //An optional final function that runs some extra code 1005 | () => console.log("extra!") 1006 | ); 1007 | */ 1008 | 1009 | }, { 1010 | key: "grid", 1011 | value: function grid() { 1012 | var columns = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0]; 1013 | var rows = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1]; 1014 | var cellWidth = arguments.length <= 2 || arguments[2] === undefined ? 32 : arguments[2]; 1015 | var cellHeight = arguments.length <= 3 || arguments[3] === undefined ? 32 : arguments[3]; 1016 | var centerCell = arguments.length <= 4 || arguments[4] === undefined ? false : arguments[4]; 1017 | var xOffset = arguments.length <= 5 || arguments[5] === undefined ? 0 : arguments[5]; 1018 | var yOffset = arguments.length <= 6 || arguments[6] === undefined ? 0 : arguments[6]; 1019 | var makeSprite = arguments.length <= 7 || arguments[7] === undefined ? undefined : arguments[7]; 1020 | var extra = arguments.length <= 8 || arguments[8] === undefined ? undefined : arguments[8]; 1021 | 1022 | //Create an empty group called `container`. This `container` 1023 | //group is what the function returns back to the main program. 1024 | //All the sprites in the grid cells will be added 1025 | //as children to this container 1026 | var container = this.group(); 1027 | 1028 | //The `create` method plots the grid 1029 | var createGrid = function createGrid() { 1030 | 1031 | //Figure out the number of cells in the grid 1032 | var length = columns * rows; 1033 | 1034 | //Create a sprite for each cell 1035 | for (var i = 0; i < length; i++) { 1036 | 1037 | //Figure out the sprite's x/y placement in the grid 1038 | var x = i % columns * cellWidth, 1039 | y = Math.floor(i / columns) * cellHeight; 1040 | 1041 | //Use the `makeSprite` function supplied in the constructor 1042 | //to make a sprite for the grid cell 1043 | var sprite = makeSprite(); 1044 | 1045 | //Add the sprite to the `container` 1046 | container.addChild(sprite); 1047 | 1048 | //Should the sprite be centered in the cell? 1049 | 1050 | //No, it shouldn't be centered 1051 | if (!centerCell) { 1052 | sprite.x = x + xOffset; 1053 | sprite.y = y + yOffset; 1054 | } 1055 | 1056 | //Yes, it should be centered 1057 | else { 1058 | sprite.x = x + cellWidth / 2 - sprite.halfWidth + xOffset; 1059 | sprite.y = y + cellHeight / 2 - sprite.halfHeight + yOffset; 1060 | } 1061 | 1062 | //Run any optional extra code. This calls the 1063 | //`extra` function supplied by the constructor 1064 | if (extra) extra(sprite); 1065 | } 1066 | }; 1067 | 1068 | //Run the `createGrid` method 1069 | createGrid(); 1070 | 1071 | //Return the `container` group back to the main program 1072 | return container; 1073 | } 1074 | 1075 | /* 1076 | shake 1077 | ----- 1078 | Used to create a shaking effect, like a screen shake 1079 | */ 1080 | 1081 | }, { 1082 | key: "shake", 1083 | value: function shake(sprite) { 1084 | var magnitude = arguments.length <= 1 || arguments[1] === undefined ? 16 : arguments[1]; 1085 | var angular = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; 1086 | 1087 | //Get a reference to this current object so that 1088 | //it's easy to maintain scope in the nested sub-functions 1089 | var self = this; 1090 | 1091 | //A counter to count the number of shakes 1092 | var counter = 1; 1093 | 1094 | //The total number of shakes (there will be 1 shake per frame) 1095 | var numberOfShakes = 10; 1096 | 1097 | //Capture the sprite's position and angle so you can 1098 | //restore them after the shaking has finished 1099 | var startX = sprite.x, 1100 | startY = sprite.y, 1101 | startAngle = sprite.rotation; 1102 | 1103 | //Divide the magnitude into 10 units so that you can 1104 | //reduce the amount of shake by 10 percent each frame 1105 | var magnitudeUnit = magnitude / numberOfShakes; 1106 | 1107 | //The `randomInt` helper function 1108 | var randomInt = function randomInt(min, max) { 1109 | return Math.floor(Math.random() * (max - min + 1)) + min; 1110 | }; 1111 | 1112 | //Add the sprite to the `shakingSprites` array if it 1113 | //isn't already there 1114 | if (self.shakingSprites.indexOf(sprite) === -1) { 1115 | 1116 | self.shakingSprites.push(sprite); 1117 | 1118 | //Add an `updateShake` method to the sprite. 1119 | //The `updateShake` method will be called each frame 1120 | //in the game loop. The shake effect type can be either 1121 | //up and down (x/y shaking) or angular (rotational shaking). 1122 | sprite.updateShake = function () { 1123 | if (angular) { 1124 | angularShake(); 1125 | } else { 1126 | upAndDownShake(); 1127 | } 1128 | }; 1129 | } 1130 | 1131 | //The `upAndDownShake` function 1132 | function upAndDownShake() { 1133 | 1134 | //Shake the sprite while the `counter` is less than 1135 | //the `numberOfShakes` 1136 | if (counter < numberOfShakes) { 1137 | 1138 | //Reset the sprite's position at the start of each shake 1139 | sprite.x = startX; 1140 | sprite.y = startY; 1141 | 1142 | //Reduce the magnitude 1143 | magnitude -= magnitudeUnit; 1144 | 1145 | //Randomly change the sprite's position 1146 | sprite.x += randomInt(-magnitude, magnitude); 1147 | sprite.y += randomInt(-magnitude, magnitude); 1148 | 1149 | //Add 1 to the counter 1150 | counter += 1; 1151 | } 1152 | 1153 | //When the shaking is finished, restore the sprite to its original 1154 | //position and remove it from the `shakingSprites` array 1155 | if (counter >= numberOfShakes) { 1156 | sprite.x = startX; 1157 | sprite.y = startY; 1158 | self.shakingSprites.splice(self.shakingSprites.indexOf(sprite), 1); 1159 | } 1160 | } 1161 | 1162 | //The `angularShake` function 1163 | //First set the initial tilt angle to the right (+1) 1164 | var tiltAngle = 1; 1165 | 1166 | function angularShake() { 1167 | if (counter < numberOfShakes) { 1168 | 1169 | //Reset the sprite's rotation 1170 | sprite.rotation = startAngle; 1171 | 1172 | //Reduce the magnitude 1173 | magnitude -= magnitudeUnit; 1174 | 1175 | //Rotate the sprite left or right, depending on the direction, 1176 | //by an amount in radians that matches the magnitude 1177 | sprite.rotation = magnitude * tiltAngle; 1178 | counter += 1; 1179 | 1180 | //Reverse the tilt angle so that the sprite is tilted 1181 | //in the opposite direction for the next shake 1182 | tiltAngle *= -1; 1183 | } 1184 | 1185 | //When the shaking is finished, reset the sprite's angle and 1186 | //remove it from the `shakingSprites` array 1187 | if (counter >= numberOfShakes) { 1188 | sprite.rotation = startAngle; 1189 | self.shakingSprites.splice(self.shakingSprites.indexOf(sprite), 1); 1190 | } 1191 | } 1192 | } 1193 | 1194 | /* 1195 | _getCenter 1196 | ---------- 1197 | A utility that finds the center point of the sprite. If it's anchor point is the 1198 | sprite's top left corner, then the center is calculated from that point. 1199 | If the anchor point has been shifted, then the anchor x/y point is used as the sprite's center 1200 | */ 1201 | 1202 | }, { 1203 | key: "_getCenter", 1204 | value: function _getCenter(o, dimension, axis) { 1205 | if (o.anchor !== undefined) { 1206 | if (o.anchor[axis] !== 0) { 1207 | return 0; 1208 | } else { 1209 | return dimension / 2; 1210 | } 1211 | } else { 1212 | return dimension; 1213 | } 1214 | } 1215 | 1216 | /* Groups */ 1217 | 1218 | //Group sprites into a container 1219 | 1220 | }, { 1221 | key: "group", 1222 | value: function group() { 1223 | var container = new this.Container(); 1224 | 1225 | for (var _len = arguments.length, sprites = Array(_len), _key = 0; _key < _len; _key++) { 1226 | sprites[_key] = arguments[_key]; 1227 | } 1228 | 1229 | sprites.forEach(function (sprite) { 1230 | container.addChild(sprite); 1231 | }); 1232 | return container; 1233 | } 1234 | 1235 | //Use the `batch` method to create a ParticleContainer 1236 | 1237 | }, { 1238 | key: "batch", 1239 | value: function batch() { 1240 | var size = arguments.length <= 0 || arguments[0] === undefined ? 15000 : arguments[0]; 1241 | var options = arguments.length <= 1 || arguments[1] === undefined ? { rotation: true, alpha: true, scale: true, uvs: true } : arguments[1]; 1242 | 1243 | var o = new this.ParticleContainer(size, options); 1244 | return o; 1245 | } 1246 | 1247 | //`remove` is a global convenience method that will 1248 | //remove any sprite, or an argument list of sprites, from its parent. 1249 | 1250 | }, { 1251 | key: "remove", 1252 | value: function remove() { 1253 | for (var _len2 = arguments.length, sprites = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { 1254 | sprites[_key2] = arguments[_key2]; 1255 | } 1256 | 1257 | //Remove sprites that's aren't in an array 1258 | if (!(sprites[0] instanceof Array)) { 1259 | if (sprites.length > 1) { 1260 | sprites.forEach(function (sprite) { 1261 | sprite.parent.removeChild(sprite); 1262 | }); 1263 | } else { 1264 | sprites[0].parent.removeChild(sprites[0]); 1265 | } 1266 | } 1267 | 1268 | //Remove sprites in an array of sprites 1269 | else { 1270 | var spritesArray = sprites[0]; 1271 | if (spritesArray.length > 0) { 1272 | for (var i = spritesArray.length - 1; i >= 0; i--) { 1273 | var sprite = spritesArray[i]; 1274 | sprite.parent.removeChild(sprite); 1275 | spritesArray.splice(spritesArray.indexOf(sprite), 1); 1276 | } 1277 | } 1278 | } 1279 | } 1280 | 1281 | /* Color conversion */ 1282 | //From: http://stackoverflow.com/questions/1573053/javascript-function-to-convert-color-names-to-hex-codes 1283 | //Utilities to convert HTML color string names to hexadecimal codes 1284 | 1285 | }, { 1286 | key: "colorToRGBA", 1287 | value: function colorToRGBA(color) { 1288 | // Returns the color as an array of [r, g, b, a] -- all range from 0 - 255 1289 | // color must be a valid canvas fillStyle. This will cover most anything 1290 | // you'd want to use. 1291 | // Examples: 1292 | // colorToRGBA('red') # [255, 0, 0, 255] 1293 | // colorToRGBA('#f00') # [255, 0, 0, 255] 1294 | var cvs, ctx; 1295 | cvs = document.createElement('canvas'); 1296 | cvs.height = 1; 1297 | cvs.width = 1; 1298 | ctx = cvs.getContext('2d'); 1299 | ctx.fillStyle = color; 1300 | ctx.fillRect(0, 0, 1, 1); 1301 | var data = ctx.getImageData(0, 0, 1, 1).data; 1302 | return data; 1303 | } 1304 | }, { 1305 | key: "byteToHex", 1306 | value: function byteToHex(num) { 1307 | // Turns a number (0-255) into a 2-character hex number (00-ff) 1308 | return ('0' + num.toString(16)).slice(-2); 1309 | } 1310 | }, { 1311 | key: "colorToHex", 1312 | value: function colorToHex(color) { 1313 | var _this2 = this; 1314 | 1315 | // Convert any CSS color to a hex representation 1316 | // Examples: 1317 | // colorToHex('red') # '#ff0000' 1318 | // colorToHex('rgb(255, 0, 0)') # '#ff0000' 1319 | var rgba, hex; 1320 | rgba = this.colorToRGBA(color); 1321 | hex = [0, 1, 2].map(function (idx) { 1322 | return _this2.byteToHex(rgba[idx]); 1323 | }).join(''); 1324 | return "0x" + hex; 1325 | } 1326 | 1327 | //A function to find out if the user entered a number (a hex color 1328 | //code) or a string (an HTML color string) 1329 | 1330 | }, { 1331 | key: "color", 1332 | value: function color(value) { 1333 | 1334 | //Check if it's a number 1335 | if (!isNaN(value)) { 1336 | 1337 | //Yes, it is a number, so just return it 1338 | return value; 1339 | } 1340 | 1341 | //No it's not a number, so it must be a string 1342 | else { 1343 | 1344 | return parseInt(this.colorToHex(value)); 1345 | /* 1346 | //Find out what kind of color string it is. 1347 | //Let's first grab the first character of the string 1348 | let firstCharacter = value.charAt(0); 1349 | //If the first character is a "#" or a number, then 1350 | //we know it must be a RGBA color 1351 | if (firstCharacter === "#") { 1352 | console.log("first character: " + value.charAt(0)) 1353 | } 1354 | */ 1355 | } 1356 | 1357 | /* 1358 | //Find out if the first character in the string is a number 1359 | if (!isNaN(parseInt(string.charAt(0)))) { 1360 | 1361 | //It's not, so convert it to a hex code 1362 | return colorToHex(string); 1363 | 1364 | //The use input a number, so it must be a hex code. Just return it 1365 | } else { 1366 | 1367 | return string; 1368 | } 1369 | 1370 | */ 1371 | } 1372 | }]); 1373 | 1374 | return SpriteUtilities; 1375 | })(); 1376 | //# sourceMappingURL=spriteUtilities.js.map -------------------------------------------------------------------------------- /bin/spriteUtilities.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/spriteUtilities.js"],"names":[],"mappings":"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","file":"spriteUtilities.js","sourcesContent":["class SpriteUtilities{\n constructor(renderingEngine = PIXI) {\n if (renderingEngine === undefined) throw new Error(\"Please supply a reference to PIXI in the SpriteUtilities constructor before using spriteUtilities.js\"); \n\n //Find out which rendering engine is being used (the default is Pixi)\n this.renderer = \"\";\n\n //If the `renderingEngine` is Pixi, set up Pixi object aliases\n if (renderingEngine.ParticleContainer && renderingEngine.Sprite) {\n this.renderer = \"pixi\";\n this.Container = renderingEngine.Container;\n this.ParticleContainer = renderingEngine.ParticleContainer;\n this.TextureCache = renderingEngine.utils.TextureCache;\n this.Texture = renderingEngine.Texture;\n this.Rectangle = renderingEngine.Rectangle;\n this.MovieClip = renderingEngine.extras.MovieClip;\n this.BitmapText = renderingEngine.extras.BitmapText;\n this.Sprite = renderingEngine.Sprite;\n this.TilingSprite = renderingEngine.extras.TilingSprite;\n this.Graphics = renderingEngine.Graphics;\n this.Text = renderingEngine.Text;\n \n //An array to store all the shaking sprites\n this.shakingSprites = [];\n }\n }\n\n update() {\n if (this.shakingSprites.length > 0) {\n for(let i = this.shakingSprites.length - 1; i >= 0; i--) {\n let shakingSprite = this.shakingSprites[i];\n if (shakingSprite.updateShake) shakingSprite.updateShake();\n } \n }\n }\n\n sprite(source, x = 0, y = 0, tiling = false, width, height) {\n\n let o, texture;\n\n //Create a sprite if the `source` is a string \n if (typeof source === \"string\") {\n\n //Access the texture in the cache if it's there\n if (this.TextureCache[source]) {\n texture = this.TextureCache[source];\n }\n\n //If it's not is the cache, load it from the source file\n else {\n texture = this.Texture.fromImage(source);\n }\n\n //If the texture was created, make the o\n if (texture) {\n\n //If `tiling` is `false`, make a regular `Sprite`\n if (!tiling) {\n o = new this.Sprite(texture);\n }\n\n //If `tiling` is `true` make a `TilingSprite`\n else {\n o = new this.TilingSprite(texture, width, height);\n }\n }\n //But if the source still can't be found, alert the user\n else {\n throw new Error(`${source} cannot be found`);\n }\n }\n\n //Create a o if the `source` is a texture\n else if (source instanceof this.Texture) {\n if (!tiling) {\n o = new this.Sprite(source);\n } else {\n o = new this.TilingSprite(source, width, height);\n }\n }\n\n //Create a `MovieClip` o if the `source` is an array\n else if (source instanceof Array) {\n\n //Is it an array of frame ids or textures?\n if (typeof source[0] === \"string\") {\n\n //They're strings, but are they pre-existing texture or\n //paths to image files?\n //Check to see if the first element matches a texture in the\n //cache\n if (this.TextureCache[source[0]]) {\n\n //It does, so it's an array of frame ids\n o = this.MovieClip.fromFrames(source);\n } else {\n\n //It's not already in the cache, so let's load it\n o = this.MovieClip.fromImages(source);\n }\n }\n\n //If the `source` isn't an array of strings, check whether\n //it's an array of textures\n else if (source[0] instanceof this.Texture) {\n\n //Yes, it's an array of textures. \n //Use them to make a MovieClip o \n o = new this.MovieClip(source);\n }\n }\n\n //If the sprite was successfully created, intialize it\n if (o) {\n\n //Position the sprite\n o.x = x;\n o.y = y;\n\n //Set optional width and height\n if (width) o.width = width;\n if (height) o.height = height;\n\n //If the sprite is a MovieClip, add a state player so that\n //it's easier to control\n if (o instanceof this.MovieClip) this.addStatePlayer(o);\n\n //Assign the sprite\n return o;\n }\n }\n\n addStatePlayer(sprite) {\n\n let frameCounter = 0,\n numberOfFrames = 0,\n startFrame = 0,\n endFrame = 0,\n timerInterval = undefined;\n\n //The `show` function (to display static states)\n function show(frameNumber) {\n\n //Reset any possible previous animations\n reset();\n\n //Find the new state on the sprite\n sprite.gotoAndStop(frameNumber);\n }\n\n //The `stop` function stops the animation at the current frame\n function stopAnimation() {\n reset();\n sprite.gotoAndStop(sprite.currentFrame);\n }\n\n //The `playSequence` function, to play a sequence of frames\n function playAnimation(sequenceArray) {\n\n //Reset any possible previous animations\n reset();\n\n //Figure out how many frames there are in the range\n if (!sequenceArray) {\n startFrame = 0;\n endFrame = sprite.totalFrames - 1;\n } else {\n startFrame = sequenceArray[0];\n endFrame = sequenceArray[1];\n }\n\n //Calculate the number of frames\n numberOfFrames = endFrame - startFrame;\n\n //Compensate for two edge cases:\n //1. If the `startFrame` happens to be `0`\n /*\n if (startFrame === 0) {\n numberOfFrames += 1;\n frameCounter += 1;\n }\n */\n\n //2. If only a two-frame sequence was provided\n /*\n if(numberOfFrames === 1) {\n numberOfFrames = 2;\n frameCounter += 1;\n } \n */\n\n //Calculate the frame rate. Set the default fps to 12\n if (!sprite.fps) sprite.fps = 12;\n let frameRate = 1000 / sprite.fps;\n\n //Set the sprite to the starting frame\n sprite.gotoAndStop(startFrame);\n\n //Set the `frameCounter` to the first frame \n frameCounter = 1;\n\n //If the state isn't already `playing`, start it\n if (!sprite.animating) {\n timerInterval = setInterval(advanceFrame.bind(this), frameRate);\n sprite.animating = true;\n }\n }\n\n //`advanceFrame` is called by `setInterval` to display the next frame \n //in the sequence based on the `frameRate`. When the frame sequence \n //reaches the end, it will either stop or loop\n function advanceFrame() {\n\n //Advance the frame if `frameCounter` is less than \n //the state's total frames\n if (frameCounter < numberOfFrames + 1) {\n\n //Advance the frame\n sprite.gotoAndStop(sprite.currentFrame + 1);\n\n //Update the frame counter\n frameCounter += 1;\n\n //If we've reached the last frame and `loop`\n //is `true`, then start from the first frame again\n } else {\n if (sprite.loop) {\n sprite.gotoAndStop(startFrame);\n frameCounter = 1;\n }\n }\n }\n\n function reset() {\n\n //Reset `sprite.playing` to `false`, set the `frameCounter` to 0, //and clear the `timerInterval`\n if (timerInterval !== undefined && sprite.animating === true) {\n sprite.animating = false;\n frameCounter = 0;\n startFrame = 0;\n endFrame = 0;\n numberOfFrames = 0;\n clearInterval(timerInterval);\n }\n }\n\n //Add the `show`, `play`, `stop`, and `playSequence` methods to the sprite\n sprite.show = show;\n sprite.stopAnimation = stopAnimation;\n sprite.playAnimation = playAnimation;\n }\n\n //`tilingSpirte` lets you quickly create Pixi tiling sprites\n tilingSprite(source, width, height, x, y) {\n if (width === undefined) {\n throw new Error(\"Please define a width as your second argument for the tiling sprite\");\n }\n if (height === undefined) {\n throw new Error(\"Please define a height as your third argument for the tiling sprite\");\n }\n let o = this.sprite(source, x, y, true, width, height);\n\n //Add `tileX`, `tileY`, `tileScaleX` and `tileScaleY` properties\n Object.defineProperties(o, {\n \"tileX\": {\n get() {\n return o.tilePosition.x;\n },\n set(value) {\n o.tilePosition.x = value;\n }, \n enumerable: true, configurable: true\n },\n \"tileY\": {\n get() {\n return o.tilePosition.y;\n },\n set(value) {\n o.tilePosition.y = value;\n }, \n enumerable: true, configurable: true\n },\n \"tileScaleX\": {\n get() {\n return o.tileScale.x;\n },\n set(value) {\n o.tileScale.x = value;\n }, \n enumerable: true, configurable: true\n },\n \"tileScaleY\": {\n get() {\n return o.tileScale.y;\n },\n set(value) {\n o.tileScale.y = value;\n }, \n enumerable: true, configurable: true\n },\n });\n \n return o\n }\n\n filmstrip(\n texture,\n frameWidth,\n frameHeight,\n spacing = 0\n ) {\n\n //An array to store the x/y positions of the frames\n let positions = [];\n\n //Find the width and height of the texture\n let textureWidth = this.TextureCache[texture].width,\n textureHeight = this.TextureCache[texture].height;\n\n //Find out how many columns and rows there are\n let columns = textureWidth / frameWidth,\n rows = textureHeight / frameHeight;\n\n //Find the total number of frames\n let numberOfFrames = columns * rows;\n\n for (let i = 0; i < numberOfFrames; i++) {\n\n //Find the correct row and column for each frame\n //and figure out its x and y position\n let x = (i % columns) * frameWidth,\n y = Math.floor(i / columns) * frameHeight;\n\n //Compensate for any optional spacing (padding) around the tiles if\n //there is any. This bit of code accumlates the spacing offsets from the \n //left side of the tileset and adds them to the current tile's position \n if (spacing > 0) {\n x += spacing + (spacing * i % columns);\n y += spacing + (spacing * Math.floor(i / columns));\n }\n\n //Add the x and y value of each frame to the `positions` array\n positions.push([x, y]);\n }\n\n //Return the frames\n return this.frames(texture, positions, frameWidth, frameHeight);\n }\n\n //Make a texture from a frame in another texture or image\n frame(source, x, y, width, height) {\n\n let texture, imageFrame;\n\n //If the source is a string, it's either a texture in the\n //cache or an image file\n if (typeof source === \"string\") {\n if (this.TextureCache[source]) {\n texture = new this.Texture(this.TextureCache[source]);\n }\n }\n\n //If the `source` is a texture, use it\n else if (source instanceof this.Texture) {\n texture = new this.Texture(source);\n }\n if (!texture) {\n throw new Error(`Please load the ${source} texture into the cache.`);\n } else {\n\n //Make a rectangle the size of the sub-image\n imageFrame = new this.Rectangle(x, y, width, height);\n texture.frame = imageFrame;\n return texture;\n }\n }\n\n //Make an array of textures from a 2D array of frame x and y coordinates in\n //texture\n frames(source, coordinates, frameWidth, frameHeight) {\n\n let baseTexture, textures;\n\n //If the source is a string, it's either a texture in the\n //cache or an image file\n if (typeof source === \"string\") {\n if (this.TextureCache[source]) {\n baseTexture = new this.Texture(this.TextureCache[source]);\n }\n }\n //If the `source` is a texture, use it\n else if (source instanceof this.Texture) {\n baseTexture = new this.Texture(source);\n }\n if (!baseTexture) {\n throw new Error(`Please load the ${source} texture into the cache.`);\n } else {\n let textures = coordinates.map((position) => {\n let x = position[0],\n y = position[1];\n let imageFrame = new this.Rectangle(x, y, frameWidth, frameHeight);\n let frameTexture = new this.Texture(baseTexture);\n frameTexture.frame = imageFrame;\n return frameTexture\n });\n return textures;\n }\n }\n\n frameSeries(startNumber = 0, endNumber = 1, baseName = \"\", extension = \"\") {\n\n //Create an array to store the frame names\n let frames = [];\n\n for (let i = startNumber; i < endNumber + 1; i++) {\n let frame = this.TextureCache[`${baseName + i + extension}`];\n frames.push(frame);\n }\n return frames;\n }\n\n /* Text creation */\n\n //The`text` method is a quick way to create a Pixi Text sprite\n text(content = \"message\", font = \"16px sans\", fillStyle = \"red\", x = 0, y = 0) {\n\n //Create a Pixi Sprite object\n let message = new this.Text(content, {font: font, fill: fillStyle});\n message.x = x;\n message.y = y;\n\n //Add a `_text` property with a getter/setter\n message._content = content;\n Object.defineProperty(message, \"content\", {\n get() {\n return this._content;\n },\n set(value) {\n this._content = value;\n this.text = value;\n },\n enumerable: true, configurable: true\n });\n\n //Return the text object\n return message;\n }\n\n //The`bitmapText` method lets you create bitmap text\n bitmapText(content = \"message\", font, align, tint, x = 0, y = 0) {\n\n //Create a Pixi Sprite object\n let message = new this.BitmapText(content, {font: font, align: align, tint: tint});\n message.x = x;\n message.y = y;\n\n //Add a `_text` property with a getter/setter\n message._content = content;\n Object.defineProperty(message, \"content\", {\n get() {\n return this._content;\n },\n set(value) {\n this._content = value;\n this.text = value;\n },\n enumerable: true, configurable: true\n });\n\n //Return the text object\n return message;\n }\n\n /* Shapes and lines */\n\n //Rectangle\n rectangle(\n width = 32, \n height = 32, \n fillStyle = 0xFF3300, \n strokeStyle = 0x0033CC, \n lineWidth = 0,\n x = 0, \n y = 0 \n ){\n\n let o = new this.Graphics();\n o._sprite = undefined;\n o._width = width;\n o._height = height;\n o._fillStyle = this.color(fillStyle);\n o._strokeStyle = this.color(strokeStyle);\n o._lineWidth = lineWidth;\n\n //Draw the rectangle\n let draw = (width, height, fillStyle, strokeStyle, lineWidth) => {\n o.clear();\n o.beginFill(fillStyle);\n if (lineWidth > 0) {\n o.lineStyle(lineWidth, strokeStyle, 1);\n }\n o.drawRect(0, 0, width, height);\n o.endFill();\n };\n\n //Draw the line and capture the sprite that the `draw` function\n //returns\n draw(o._width, o._height, o._fillStyle, o._strokeStyle, o._lineWidth);\n\n //Generate a texture from the rectangle\n let texture = o.generateTexture();\n\n //Use the texture to create a sprite\n let sprite = new this.Sprite(texture);\n\n //Position the sprite\n sprite.x = x;\n sprite.y = y;\n\n //Add getters and setters to the sprite\n let self = this;\n Object.defineProperties(sprite, {\n \"fillStyle\": {\n get() {\n return o._fillStyle;\n },\n set(value) {\n o._fillStyle = self.color(value);\n\n //Draw the new rectangle \n draw(o._width, o._height, o._fillStyle, o._strokeStyle, o._lineWidth, o._x, o._y);\n\n //Generate a new texture and set it as the sprite's texture\n let texture = o.generateTexture();\n o._sprite.texture = texture;\n }, \n enumerable: true, configurable: true\n },\n \"strokeStyle\": {\n get() {\n return o._strokeStyle;\n },\n set(value) {\n o._strokeStyle = self.color(value);\n\n //Draw the new rectangle \n draw(o._width, o._height, o._fillStyle, o._strokeStyle, o._lineWidth, o._x, o._y);\n\n //Generate a new texture and set it as the sprite's texture\n let texture = o.generateTexture();\n o._sprite.texture = texture;\n }, \n enumerable: true, configurable: true\n },\n \"lineWidth\": {\n get() {\n return o._lineWidth;\n },\n set(value) {\n o._lineWidth = value;\n\n //Draw the new rectangle \n draw(o._width, o._height, o._fillStyle, o._strokeStyle, o._lineWidth, o._x, o._y);\n\n //Generate a new texture and set it as the sprite's texture\n let texture = o.generateTexture();\n o._sprite.texture = texture;\n }, \n enumerable: true, configurable: true\n }\n });\n \n //Get a local reference to the sprite so that we can \n //change the rectangle properties later using the getters/setters\n o._sprite = sprite;\n\n //Return the sprite\n return sprite;\n }\n\n //Circle\n circle(\n diameter = 32, \n fillStyle = 0xFF3300, \n strokeStyle = 0x0033CC, \n lineWidth = 0,\n x = 0, \n y = 0 \n ){\n\n let o = new this.Graphics();\n o._diameter = diameter;\n o._fillStyle = this.color(fillStyle);\n o._strokeStyle = this.color(strokeStyle);\n o._lineWidth = lineWidth;\n\n //Draw the circle\n let draw = (diameter, fillStyle, strokeStyle, lineWidth) => {\n o.clear(); \n o.beginFill(fillStyle);\n if (lineWidth > 0) {\n o.lineStyle(lineWidth, strokeStyle, 1);\n }\n o.drawCircle(0, 0, diameter / 2);\n o.endFill();\n };\n\n //Draw the cirlce\n draw(o._diameter, o._fillStyle, o._strokeStyle, o._lineWidth);\n\n //Generate a texture from the rectangle\n let texture = o.generateTexture();\n\n //Use the texture to create a sprite\n let sprite = new this.Sprite(texture);\n\n //Position the sprite\n sprite.x = x;\n sprite.y = y;\n\n //Add getters and setters to the sprite\n let self = this;\n Object.defineProperties(sprite, {\n \"fillStyle\": {\n get() {\n return o._fillStyle;\n },\n set(value) {\n o._fillStyle = self.color(value);\n\n //Draw the cirlce\n draw(o._diameter, o._fillStyle, o._strokeStyle, o._lineWidth);\n\n //Generate a new texture and set it as the sprite's texture\n let texture = o.generateTexture();\n o._sprite.texture = texture;\n }, \n enumerable: true, configurable: true\n },\n \"strokeStyle\": {\n get() {\n return o._strokeStyle;\n },\n set(value) {\n o._strokeStyle = self.color(value);\n\n //Draw the cirlce\n draw(o._diameter, o._fillStyle, o._strokeStyle, o._lineWidth);\n\n //Generate a new texture and set it as the sprite's texture\n let texture = o.generateTexture();\n o._sprite.texture = texture;\n }, \n enumerable: true, configurable: true\n },\n \"diameter\": {\n get() {\n return o._diameter;\n },\n set(value) {\n o._lineWidth = 10;\n\n //Draw the cirlce\n draw(o._diameter, o._fillStyle, o._strokeStyle, o._lineWidth);\n\n //Generate a new texture and set it as the sprite's texture\n let texture = o.generateTexture();\n o._sprite.texture = texture;\n }, \n enumerable: true, configurable: true\n },\n \"radius\": {\n get() {\n return o._diameter / 2;\n },\n set(value) {\n\n //Draw the cirlce\n draw(value * 2, o._fillStyle, o._strokeStyle, o._lineWidth);\n\n //Generate a new texture and set it as the sprite's texture\n let texture = o.generateTexture();\n o._sprite.texture = texture;\n }, \n enumerable: true, configurable: true\n },\n });\n //Get a local reference to the sprite so that we can \n //change the circle properties later using the getters/setters\n o._sprite = sprite;\n\n //Return the sprite\n return sprite;\n }\n\n //Line\n line(\n strokeStyle = 0x000000, \n lineWidth = 1, \n ax = 0, \n ay = 0, \n bx = 32, \n by = 32\n ){\n\n //Create the line object\n let o = new this.Graphics();\n\n //Private properties\n o._strokeStyle = this.color(strokeStyle);\n o._width = lineWidth;\n o._ax = ax;\n o._ay = ay;\n o._bx = bx;\n o._by = by;\n\n //A helper function that draws the line\n let draw = (strokeStyle, lineWidth, ax, ay, bx, by) => {\n o.clear();\n o.lineStyle(lineWidth, strokeStyle, 1);\n o.moveTo(ax, ay);\n o.lineTo(bx, by);\n };\n \n //Draw the line\n draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by);\n\n //Define getters and setters that redefine the line's start and \n //end points and re-draws it if they change\n let self = this;\n Object.defineProperties(o, {\n \"ax\": {\n get() {\n return o._ax;\n },\n set(value) {\n o._ax = value;\n draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by);\n }, \n enumerable: true, configurable: true\n },\n \"ay\": {\n get() {\n return o._ay;\n },\n set(value) {\n o._ay = value;\n draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by);\n }, \n enumerable: true, configurable: true\n },\n \"bx\": {\n get() {\n return o._bx;\n },\n set(value) {\n o._bx = value;\n draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by);\n }, \n enumerable: true, configurable: true\n },\n \"by\": {\n get() {\n return o._by;\n },\n set(value) {\n o._by = value;\n draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by);\n }, \n enumerable: true, configurable: true\n },\n \"strokeStyle\": {\n get() {\n return o._strokeStyle;\n },\n set(value) {\n o._strokeStyle = self.color(value);\n\n //Draw the line\n draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by);\n }, \n enumerable: true, configurable: true\n },\n \"width\": {\n get() {\n return o._width;\n },\n set(value) {\n o._width = value;\n\n //Draw the line\n draw(o._strokeStyle, o._width, o._ax, o._ay, o._bx, o._by);\n }, \n enumerable: true, configurable: true\n }\n });\n\n //Return the line\n return o;\n }\n\n /* Compound sprites */\n\n //Use `grid` to create a grid of sprites\n grid(\n columns = 0, rows = 0, cellWidth = 32, cellHeight = 32,\n centerCell = false, xOffset = 0, yOffset = 0,\n makeSprite = undefined,\n extra = undefined\n ){\n\n //Create an empty group called `container`. This `container`\n //group is what the function returns back to the main program.\n //All the sprites in the grid cells will be added\n //as children to this container\n let container = new this.Container();\n\n //The `create` method plots the grid\n\n let createGrid = () => {\n\n //Figure out the number of cells in the grid\n let length = columns * rows;\n\n //Create a sprite for each cell\n for(let i = 0; i < length; i++) {\n\n //Figure out the sprite's x/y placement in the grid\n let x = (i % columns) * cellWidth,\n y = Math.floor(i / columns) * cellHeight;\n\n //Use the `makeSprite` function supplied in the constructor\n //to make a sprite for the grid cell\n let sprite = makeSprite();\n\n //Add the sprite to the `container`\n container.addChild(sprite);\n\n //Should the sprite be centered in the cell?\n\n //No, it shouldn't be centered\n if (!centerCell) {\n sprite.x = x + xOffset;\n sprite.y = y + yOffset;\n }\n\n //Yes, it should be centered\n else {\n sprite.x \n = x + (cellWidth / 2) \n - (sprite.width / 2) + xOffset;\n sprite.y \n = y + (cellHeight / 2) \n - (sprite.width / 2) + yOffset;\n }\n\n //Run any optional extra code. This calls the\n //`extra` function supplied by the constructor\n if (extra) extra(sprite);\n }\n };\n\n //Run the `createGrid` method\n createGrid();\n\n //Return the `container` group back to the main program\n return container;\n }\n\n //Use `shoot` to create bullet sprites \n shoot(\n shooter, angle, x, y, container, bulletSpeed, bulletArray, bulletSprite\n ) {\n\n //Make a new sprite using the user-supplied `bulletSprite` function\n let bullet = bulletSprite();\n\n //Set the bullet's anchor point to its center\n bullet.anchor.set(0.5, 0.5);\n\n //Temporarily add the bullet to the shooter\n //so that we can position it relative to the\n //shooter's position\n shooter.addChild(bullet);\n bullet.x = x;\n bullet.y = y;\n\n //Find the bullet's global coordinates so that we can use\n //them to position the bullet on the new parent container\n let tempGx = bullet.getGlobalPosition().x,\n tempGy = bullet.getGlobalPosition().y;\n\n //Add the bullet to the new parent container using\n //the new global coordinates\n container.addChild(bullet);\n bullet.x = tempGx;\n bullet.y = tempGy;\n\n //Set the bullet's velocity\n bullet.vx = Math.cos(angle) * bulletSpeed;\n bullet.vy = Math.sin(angle) * bulletSpeed;\n\n //Push the bullet into the `bulletArray`\n bulletArray.push(bullet);\n }\n\n /*\n grid\n ----\n\n Helps you to automatically create a grid of sprites. `grid` returns a\n `group` sprite object that contains a sprite for every cell in the\n grid. You can define the rows and columns in the grid, whether or\n not the sprites should be centered inside each cell, or what their offset from the\n top left corner of each cell should be. Supply a function that\n returns the sprite that you want to make for each cell. You can\n supply an optional final function that runs any extra code after\n each sprite has been created. Here's the format for creating a grid:\n\n gridGroup = grid(\n\n //Set the grid's properties\n columns, rows, cellWidth, cellHeight,\n areSpirtesCentered?, xOffset, yOffset,\n\n //A function that returns a sprite\n () => g.circle(16, \"blue\"),\n\n //An optional final function that runs some extra code\n () => console.log(\"extra!\")\n );\n */\n\n grid(\n columns = 0, rows = 0, cellWidth = 32, cellHeight = 32,\n centerCell = false, xOffset = 0, yOffset = 0,\n makeSprite = undefined,\n extra = undefined\n ){\n\n //Create an empty group called `container`. This `container`\n //group is what the function returns back to the main program.\n //All the sprites in the grid cells will be added\n //as children to this container\n let container = this.group();\n\n //The `create` method plots the grid\n let createGrid = () => {\n\n //Figure out the number of cells in the grid\n let length = columns * rows;\n\n //Create a sprite for each cell\n for(let i = 0; i < length; i++) {\n\n //Figure out the sprite's x/y placement in the grid\n let x = (i % columns) * cellWidth,\n y = Math.floor(i / columns) * cellHeight;\n\n //Use the `makeSprite` function supplied in the constructor\n //to make a sprite for the grid cell\n let sprite = makeSprite();\n\n //Add the sprite to the `container`\n container.addChild(sprite);\n\n //Should the sprite be centered in the cell?\n\n //No, it shouldn't be centered\n if (!centerCell) {\n sprite.x = x + xOffset;\n sprite.y = y + yOffset;\n }\n\n //Yes, it should be centered\n else {\n sprite.x \n = x + (cellWidth / 2) \n - sprite.halfWidth + xOffset;\n sprite.y \n = y + (cellHeight / 2) \n - sprite.halfHeight + yOffset;\n }\n\n //Run any optional extra code. This calls the\n //`extra` function supplied by the constructor\n if (extra) extra(sprite);\n }\n };\n\n //Run the `createGrid` method\n createGrid();\n\n //Return the `container` group back to the main program\n return container;\n }\n\n /*\n shake\n -----\n\n Used to create a shaking effect, like a screen shake\n */\n\n shake(sprite, magnitude = 16, angular = false) {\n\n //Get a reference to this current object so that\n //it's easy to maintain scope in the nested sub-functions\n let self = this;\n\n //A counter to count the number of shakes\n let counter = 1;\n\n //The total number of shakes (there will be 1 shake per frame)\n let numberOfShakes = 10;\n\n //Capture the sprite's position and angle so you can\n //restore them after the shaking has finished\n let startX = sprite.x,\n startY = sprite.y,\n startAngle = sprite.rotation;\n\n //Divide the magnitude into 10 units so that you can \n //reduce the amount of shake by 10 percent each frame\n let magnitudeUnit = magnitude / numberOfShakes;\n \n //The `randomInt` helper function\n let randomInt = (min, max) => {\n return Math.floor(Math.random() * (max - min + 1)) + min;\n };\n \n //Add the sprite to the `shakingSprites` array if it\n //isn't already there\n if(self.shakingSprites.indexOf(sprite) === -1) {\n\n self.shakingSprites.push(sprite);\n \n //Add an `updateShake` method to the sprite.\n //The `updateShake` method will be called each frame\n //in the game loop. The shake effect type can be either\n //up and down (x/y shaking) or angular (rotational shaking).\n sprite.updateShake = () => {\n if(angular) {\n angularShake();\n } else {\n upAndDownShake();\n }\n };\n }\n\n //The `upAndDownShake` function\n function upAndDownShake() {\n\n //Shake the sprite while the `counter` is less than \n //the `numberOfShakes`\n if (counter < numberOfShakes) {\n\n //Reset the sprite's position at the start of each shake\n sprite.x = startX;\n sprite.y = startY;\n\n //Reduce the magnitude\n magnitude -= magnitudeUnit;\n\n //Randomly change the sprite's position\n sprite.x += randomInt(-magnitude, magnitude);\n sprite.y += randomInt(-magnitude, magnitude);\n\n //Add 1 to the counter\n counter += 1;\n }\n\n //When the shaking is finished, restore the sprite to its original \n //position and remove it from the `shakingSprites` array\n if (counter >= numberOfShakes) {\n sprite.x = startX;\n sprite.y = startY;\n self.shakingSprites.splice(self.shakingSprites.indexOf(sprite), 1);\n }\n }\n \n //The `angularShake` function\n //First set the initial tilt angle to the right (+1) \n let tiltAngle = 1;\n\n function angularShake() {\n if (counter < numberOfShakes) {\n\n //Reset the sprite's rotation\n sprite.rotation = startAngle;\n\n //Reduce the magnitude\n magnitude -= magnitudeUnit;\n\n //Rotate the sprite left or right, depending on the direction,\n //by an amount in radians that matches the magnitude\n sprite.rotation = magnitude * tiltAngle;\n counter += 1;\n\n //Reverse the tilt angle so that the sprite is tilted\n //in the opposite direction for the next shake\n tiltAngle *= -1;\n }\n\n //When the shaking is finished, reset the sprite's angle and\n //remove it from the `shakingSprites` array\n if (counter >= numberOfShakes) {\n sprite.rotation = startAngle;\n self.shakingSprites.splice(self.shakingSprites.indexOf(sprite), 1);\n }\n }\n }\n\n /*\n _getCenter\n ----------\n\n A utility that finds the center point of the sprite. If it's anchor point is the\n sprite's top left corner, then the center is calculated from that point.\n If the anchor point has been shifted, then the anchor x/y point is used as the sprite's center\n */\n\n _getCenter(o, dimension, axis) {\n if (o.anchor !== undefined) {\n if (o.anchor[axis] !== 0) {\n return 0;\n } else {\n return dimension / 2;\n }\n } else {\n return dimension; \n }\n }\n \n\n\n /* Groups */\n\n //Group sprites into a container\n group(...sprites) {\n let container = new this.Container();\n sprites.forEach(sprite => {\n container.addChild(sprite);\n });\n return container;\n }\n\n //Use the `batch` method to create a ParticleContainer\n batch(size = 15000, options = {rotation: true, alpha: true, scale: true, uvs: true}) {\n let o = new this.ParticleContainer(size, options);\n return o;\n }\n\n //`remove` is a global convenience method that will\n //remove any sprite, or an argument list of sprites, from its parent.\n remove(...sprites) {\n\n //Remove sprites that's aren't in an array\n if (!(sprites[0] instanceof Array)) {\n if (sprites.length > 1) {\n sprites.forEach(sprite => {\n sprite.parent.removeChild(sprite);\n });\n } else {\n sprites[0].parent.removeChild(sprites[0]);\n }\n }\n\n //Remove sprites in an array of sprites\n else {\n let spritesArray = sprites[0];\n if (spritesArray.length > 0) {\n for (let i = spritesArray.length - 1; i >= 0; i--) {\n let sprite = spritesArray[i];\n sprite.parent.removeChild(sprite);\n spritesArray.splice(spritesArray.indexOf(sprite), 1);\n }\n }\n }\n }\n\n /* Color conversion */\n //From: http://stackoverflow.com/questions/1573053/javascript-function-to-convert-color-names-to-hex-codes\n //Utilities to convert HTML color string names to hexadecimal codes\n\n colorToRGBA(color) {\n // Returns the color as an array of [r, g, b, a] -- all range from 0 - 255\n // color must be a valid canvas fillStyle. This will cover most anything\n // you'd want to use.\n // Examples:\n // colorToRGBA('red') # [255, 0, 0, 255]\n // colorToRGBA('#f00') # [255, 0, 0, 255]\n var cvs, ctx;\n cvs = document.createElement('canvas');\n cvs.height = 1;\n cvs.width = 1;\n ctx = cvs.getContext('2d');\n ctx.fillStyle = color;\n ctx.fillRect(0, 0, 1, 1);\n let data = ctx.getImageData(0, 0, 1, 1).data; \n return data;\n }\n\n byteToHex(num) {\n // Turns a number (0-255) into a 2-character hex number (00-ff)\n return ('0'+num.toString(16)).slice(-2);\n }\n\n colorToHex(color) {\n // Convert any CSS color to a hex representation\n // Examples:\n // colorToHex('red') # '#ff0000'\n // colorToHex('rgb(255, 0, 0)') # '#ff0000'\n var rgba, hex;\n rgba = this.colorToRGBA(color);\n hex = [0,1,2].map(\n idx => this.byteToHex(rgba[idx])\n ).join('');\n return \"0x\" + hex;\n }\n\n //A function to find out if the user entered a number (a hex color\n //code) or a string (an HTML color string)\n color(value) {\n\n //Check if it's a number\n if(!isNaN(value)){\n\n //Yes, it is a number, so just return it\n return value;\n }\n\n //No it's not a number, so it must be a string \n else {\n\n return parseInt(this.colorToHex(value));\n /*\n\n //Find out what kind of color string it is.\n //Let's first grab the first character of the string\n let firstCharacter = value.charAt(0);\n\n //If the first character is a \"#\" or a number, then\n //we know it must be a RGBA color\n if (firstCharacter === \"#\") {\n console.log(\"first character: \" + value.charAt(0))\n }\n */\n }\n \n /*\n //Find out if the first character in the string is a number\n if (!isNaN(parseInt(string.charAt(0)))) {\n \n //It's not, so convert it to a hex code\n return colorToHex(string);\n \n //The use input a number, so it must be a hex code. Just return it\n } else {\n \n return string;\n }\n \n */\n\n }\n \n}\n\n\n\n"]} --------------------------------------------------------------------------------