1 /**************************************************************************** 2 Copyright (c) 2010-2012 cocos2d-x.org 3 Copyright (c) 2008-2010 Ricardo Quesada 4 Copyright (c) 2011 Zynga Inc. 5 6 http://www.cocos2d-x.org 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy 9 of this software and associated documentation files (the "Software"), to deal 10 in the Software without restriction, including without limitation the rights 11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 copies of the Software, and to permit persons to whom the Software is 13 furnished to do so, subject to the following conditions: 14 15 The above copyright notice and this permission notice shall be included in 16 all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 THE SOFTWARE. 25 ****************************************************************************/ 26 27 // ideas taken from: 28 // . The ocean spray in your face [Jeff Lander] 29 // http://www.double.co.nz/dust/col0798.pdf 30 // . Building an Advanced Particle System [John van der Burg] 31 // http://www.gamasutra.com/features/20000623/vanderburg_01.htm 32 // . LOVE game engine 33 // http://love2d.org/ 34 // 35 // 36 // Radius mode support, from 71 squared 37 // http://particledesigner.71squared.com/ 38 // 39 // IMPORTANT: Particle Designer is supported by cocos2d, but 40 // 'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d, 41 // cocos2d uses a another approach, but the results are almost identical. 42 // 43 44 /** 45 * Shape Mode of Particle Draw 46 * @constant 47 * @type Number 48 */ 49 cc.PARTICLE_SHAPE_MODE = 0; 50 /** 51 * Texture Mode of Particle Draw 52 * @constant 53 * @type Number 54 */ 55 cc.PARTICLE_TEXTURE_MODE = 1; 56 57 /** 58 * Star Shape for ShapeMode of Particle 59 * @constant 60 * @type Number 61 */ 62 cc.PARTICLE_STAR_SHAPE = 0; 63 /** 64 * Ball Shape for ShapeMode of Particle 65 * @constant 66 * @type Number 67 */ 68 cc.PARTICLE_BALL_SHAPE = 1; 69 70 /** 71 * The Particle emitter lives forever 72 * @constant 73 * @type Number 74 */ 75 cc.PARTICLE_DURATION_INFINITY = -1; 76 77 /** 78 * The starting size of the particle is equal to the ending size 79 * @constant 80 * @type Number 81 */ 82 cc.PARTICLE_START_SIZE_EQUAL_TO_END_SIZE = -1; 83 84 /** 85 * The starting radius of the particle is equal to the ending radius 86 * @constant 87 * @type Number 88 */ 89 cc.PARTICLE_START_RADIUS_EQUAL_TO_END_RADIUS = -1; 90 91 /** 92 * Gravity mode (A mode) 93 * @constant 94 * @type Number 95 */ 96 cc.PARTICLE_MODE_GRAVITY = 0; 97 98 /** 99 * Radius mode (B mode) 100 * @constant 101 * @type Number 102 */ 103 cc.PARTICLE_MODE_RADIUS = 1; 104 105 // tCCPositionType 106 // possible types of particle positions 107 108 /** 109 * Living particles are attached to the world and are unaffected by emitter repositioning. 110 * @constant 111 * @type Number 112 */ 113 cc.PARTICLE_TYPE_FREE = 0; 114 115 /** 116 * Living particles are attached to the world but will follow the emitter repositioning.<br/> 117 * Use case: Attach an emitter to an sprite, and you want that the emitter follows the sprite. 118 * @constant 119 * @type Number 120 */ 121 cc.PARTICLE_TYPE_RELATIVE = 1; 122 123 /** 124 * Living particles are attached to the emitter and are translated along with it. 125 * @constant 126 * @type Number 127 */ 128 cc.PARTICLE_TYPE_GROUPED = 2; 129 130 // backward compatible 131 cc.PARTICLE_TYPE_FREE = cc.PARTICLE_TYPE_FREE; 132 cc.PARTICLE_TYPE_GROUPED = cc.PARTICLE_TYPE_GROUPED; 133 134 135 /** 136 * Structure that contains the values of each particle 137 * @Class 138 * @Construct 139 * @param {cc.Point} pos Position of particle 140 * @param {cc.Point} startPos 141 * @param {cc.Color4F} color 142 * @param {cc.Color4F} deltaColor 143 * @param {cc.Size} size 144 * @param {cc.Size} deltaSize 145 * @param {Number} rotation 146 * @param {Number} deltaRotation 147 * @param {Number} timeToLive 148 * @param {cc.Particle.ModeA} modeA 149 * @param {cc.Particle.ModeA} modeB 150 */ 151 cc.Particle = function (pos, startPos, color, deltaColor, size, deltaSize, rotation, deltaRotation, timeToLive, atlasIndex, modeA, modeB) { 152 this.pos = pos ? pos : cc.PointZero(); 153 this.startPos = startPos ? startPos : cc.PointZero(); 154 this.color = color ? color : new cc.Color4F(0, 0, 0, 1); 155 this.deltaColor = deltaColor ? deltaColor : new cc.Color4F(0, 0, 0, 1); 156 this.size = size || 0; 157 this.deltaSize = deltaSize || 0; 158 this.rotation = rotation || 0; 159 this.deltaRotation = deltaRotation || 0; 160 this.timeToLive = timeToLive || 0; 161 this.atlasIndex = atlasIndex || 0; 162 this.modeA = modeA ? modeA : new cc.Particle.ModeA(); 163 this.modeB = modeB ? modeB : new cc.Particle.ModeB(); 164 this.isChangeColor = false; 165 this.drawPos = cc.p(0, 0); 166 }; 167 168 /** 169 * Mode A: gravity, direction, radial accel, tangential accel 170 * @Class 171 * @Construct 172 * @param {cc.Point} dir direction of particle 173 * @param {Number} radialAccel 174 * @param {Number} tangentialAccel 175 */ 176 cc.Particle.ModeA = function (dir, radialAccel, tangentialAccel) { 177 this.dir = dir ? dir : cc.PointZero(); 178 this.radialAccel = radialAccel || 0; 179 this.tangentialAccel = tangentialAccel || 0; 180 }; 181 182 /** 183 * Mode B: radius mode 184 * @Class 185 * @Construct 186 * @param {Number} angle 187 * @param {Number} degreesPerSecond 188 * @param {Number} radius 189 * @param {Number} deltaRadius 190 */ 191 cc.Particle.ModeB = function (angle, degreesPerSecond, radius, deltaRadius) { 192 this.angle = angle || 0; 193 this.degreesPerSecond = degreesPerSecond || 0; 194 this.radius = radius || 0; 195 this.deltaRadius = deltaRadius || 0; 196 }; 197 198 199 /** 200 * <p> 201 * Particle System base class. <br/> 202 * Attributes of a Particle System:<br/> 203 * - emmision rate of the particles<br/> 204 * - Gravity Mode (Mode A): <br/> 205 * - gravity <br/> 206 * - direction <br/> 207 * - speed +- variance <br/> 208 * - tangential acceleration +- variance<br/> 209 * - radial acceleration +- variance<br/> 210 * - Radius Mode (Mode B): <br/> 211 * - startRadius +- variance <br/> 212 * - endRadius +- variance <br/> 213 * - rotate +- variance <br/> 214 * - Properties common to all modes: <br/> 215 * - life +- life variance <br/> 216 * - start spin +- variance <br/> 217 * - end spin +- variance <br/> 218 * - start size +- variance <br/> 219 * - end size +- variance <br/> 220 * - start color +- variance <br/> 221 * - end color +- variance <br/> 222 * - life +- variance <br/> 223 * - blending function <br/> 224 * - texture <br/> 225 * <br/> 226 * cocos2d also supports particles generated by Particle Designer (http://particledesigner.71squared.com/).<br/> 227 * 'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d, <br/> 228 * cocos2d uses a another approach, but the results are almost identical.<br/> 229 * cocos2d supports all the variables used by Particle Designer plus a bit more: <br/> 230 * - spinning particles (supported when using CCParticleSystemQuad) <br/> 231 * - tangential acceleration (Gravity mode) <br/> 232 * - radial acceleration (Gravity mode) <br/> 233 * - radius direction (Radius mode) (Particle Designer supports outwards to inwards direction only) <br/> 234 * It is possible to customize any of the above mentioned properties in runtime. Example: <br/> 235 * </p> 236 * @class 237 * @extends cc.Node 238 * 239 * @example 240 * emitter.radialAccel = 15; 241 * emitter.startSpin = 0; 242 */ 243 cc.ParticleSystem = cc.Node.extend(/** @lends cc.ParticleSystem# */{ 244 //***********variables************* 245 _plistFile:"", 246 //! time elapsed since the start of the system (in seconds) 247 _elapsed:0, 248 249 _dontTint:false, 250 251 // Different modes 252 //! Mode A:Gravity + Tangential Accel + Radial Accel 253 modeA:null, 254 //! Mode B: circular movement (gravity, radial accel and tangential accel don't are not used in this mode) 255 modeB:null, 256 257 //private POINTZERO for ParticleSystem 258 _pointZeroForParticle:cc.p(0,0), 259 260 //! Array of particles 261 _particles:null, 262 263 //particle pool 264 _particlePool:null, 265 266 // color modulate 267 // BOOL colorModulate; 268 269 //! How many particles can be emitted per second 270 _emitCounter:0, 271 //! particle idx 272 _particleIdx:0, 273 274 _batchNode:null, 275 /** 276 * return weak reference to the cc.SpriteBatchNode that renders the cc.Sprite 277 * @return {cc.ParticleBatchNode} 278 */ 279 getBatchNode:function () { 280 return this._batchNode; 281 }, 282 283 /** 284 * set weak reference to the cc.SpriteBatchNode that renders the cc.Sprite 285 * @param {cc.ParticleBatchNode} batchNode 286 */ 287 setBatchNode:function (batchNode) { 288 if (this._batchNode != batchNode) { 289 this._batchNode = batchNode; //weak reference 290 291 if (batchNode) { 292 for (var i = 0; i < this._totalParticles; i++) { 293 this._particles[i].atlasIndex = i; 294 } 295 } 296 } 297 }, 298 299 _atlasIndex:0, 300 /** 301 * return index of system in batch node array 302 * @return {Number} 303 */ 304 getAtlasIndex:function () { 305 return this._atlasIndex; 306 }, 307 308 /** 309 * set index of system in batch node array 310 * @param {Number} atlasIndex 311 */ 312 setAtlasIndex:function (atlasIndex) { 313 this._atlasIndex = atlasIndex; 314 }, 315 316 //true if scaled or rotated 317 _transformSystemDirty:false, 318 319 _allocatedParticles:0, 320 321 //drawMode 322 _drawMode:cc.PARTICLE_SHAPE_MODE, 323 324 /** 325 * Return DrawMode of ParticleSystem 326 * @return {Number} 327 */ 328 getDrawMode:function () { 329 return this._drawMode; 330 }, 331 332 /** 333 * DrawMode of ParticleSystem setter 334 * @param {Number} drawMode 335 */ 336 setDrawMode:function (drawMode) { 337 this._drawMode = drawMode; 338 }, 339 340 //shape type 341 _shapeType:cc.PARTICLE_BALL_SHAPE, 342 343 /** 344 * Return ShapeType of ParticleSystem 345 * @return {Number} 346 */ 347 getShapeType:function () { 348 return this._shapeType; 349 }, 350 351 /** 352 * ShapeType of ParticleSystem setter 353 * @param {Number} shapeType 354 */ 355 setShapeType:function (shapeType) { 356 this._shapeType = shapeType; 357 }, 358 359 _isActive:false, 360 /** 361 * Return ParticleSystem is active 362 * @return {Boolean} 363 */ 364 isActive:function () { 365 return this._isActive; 366 }, 367 368 _particleCount:0, 369 370 /** 371 * Quantity of particles that are being simulated at the moment 372 * @return {Number} 373 */ 374 getParticleCount:function () { 375 return this._particleCount; 376 }, 377 378 /** 379 * Quantity of particles setter 380 * @param {Number} particleCount 381 */ 382 setParticleCount:function (particleCount) { 383 this._particleCount = particleCount; 384 }, 385 386 _duration:0, 387 /** 388 * How many seconds the emitter wil run. -1 means 'forever' 389 * @return {Number} 390 */ 391 getDuration:function () { 392 return this._duration; 393 }, 394 395 /** 396 * set run seconds of the emitter 397 * @param {Number} duration 398 */ 399 setDuration:function (duration) { 400 this._duration = duration; 401 }, 402 403 _sourcePosition:cc.PointZero(), 404 /** 405 * Return sourcePosition of the emitter 406 * @return {cc.Point} 407 */ 408 getSourcePosition:function () { 409 return this._sourcePosition; 410 }, 411 412 /** 413 * sourcePosition of the emitter setter 414 * @param sourcePosition 415 */ 416 setSourcePosition:function (sourcePosition) { 417 this._sourcePosition = sourcePosition; 418 }, 419 420 _posVar:cc.PointZero(), 421 /** 422 * Return Position variance of the emitter 423 * @return {cc.Point} 424 */ 425 getPosVar:function () { 426 return this._posVar; 427 }, 428 429 /** 430 * Position variance of the emitter setter 431 * @param {cc.Point} posVar 432 */ 433 setPosVar:function (posVar) { 434 this._posVar = posVar; 435 }, 436 437 _life:0, 438 /** 439 * Return life of each particle 440 * @return {Number} 441 */ 442 getLife:function () { 443 return this._life; 444 }, 445 446 /** 447 * life of each particle setter 448 * @param {Number} life 449 */ 450 setLife:function (life) { 451 this._life = life; 452 }, 453 454 _lifeVar:0, 455 /** 456 * Return life variance of each particle 457 * @return {Number} 458 */ 459 getLifeVar:function () { 460 return this._lifeVar; 461 }, 462 463 /** 464 * life variance of each particle setter 465 * @param {Number} lifeVar 466 */ 467 setLifeVar:function (lifeVar) { 468 this._lifeVar = lifeVar; 469 }, 470 471 _angle:0, 472 /** 473 * Return angle of each particle 474 * @return {Number} 475 */ 476 getAngle:function () { 477 return this._angle; 478 }, 479 480 /** 481 * angle of each particle setter 482 * @param {Number} angle 483 */ 484 setAngle:function (angle) { 485 this._angle = angle; 486 }, 487 488 _angleVar:0, 489 /** 490 * Return angle variance of each particle 491 * @return {Number} 492 */ 493 getAngleVar:function () { 494 return this._angleVar; 495 }, 496 497 /** 498 * angle variance of each particle setter 499 * @param angleVar 500 */ 501 setAngleVar:function (angleVar) { 502 this._angleVar = angleVar; 503 }, 504 505 // mode A 506 /** 507 * Return Gravity of emitter 508 * @return {cc.Point} 509 */ 510 getGravity:function () { 511 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity"); 512 return this.modeA.gravity; 513 }, 514 515 /** 516 * Gravity of emitter setter 517 * @param {cc.Point} gravity 518 */ 519 setGravity:function (gravity) { 520 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity"); 521 this.modeA.gravity = gravity; 522 }, 523 524 /** 525 * Return Speed of each particle 526 * @return {Number} 527 */ 528 getSpeed:function () { 529 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity"); 530 return this.modeA.speed; 531 }, 532 533 /** 534 * Speed of each particle setter 535 * @param {Number} speed 536 */ 537 setSpeed:function (speed) { 538 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity"); 539 this.modeA.speed = speed; 540 }, 541 542 /** 543 * return speed variance of each particle. Only available in 'Gravity' mode. 544 * @return {Number} 545 */ 546 getSpeedVar:function () { 547 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity"); 548 return this.modeA.speedVar; 549 }, 550 551 /** 552 * speed variance of each particle setter. Only available in 'Gravity' mode. 553 * @param {Number} speedVar 554 */ 555 setSpeedVar:function (speedVar) { 556 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity"); 557 this.modeA.speedVar = speedVar; 558 }, 559 560 /** 561 * Return tangential acceleration of each particle. Only available in 'Gravity' mode. 562 * @return {Number} 563 */ 564 getTangentialAccel:function () { 565 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity"); 566 return this.modeA.tangentialAccel; 567 }, 568 569 /** 570 * Tangential acceleration of each particle setter. Only available in 'Gravity' mode. 571 * @param {Number} tangentialAccel 572 */ 573 setTangentialAccel:function (tangentialAccel) { 574 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity"); 575 this.modeA.tangentialAccel = tangentialAccel; 576 }, 577 578 /** 579 * Return tangential acceleration variance of each particle. Only available in 'Gravity' mode. 580 * @return {Number} 581 */ 582 getTangentialAccelVar:function () { 583 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity"); 584 return this.modeA.tangentialAccelVar; 585 }, 586 587 /** 588 * tangential acceleration variance of each particle setter. Only available in 'Gravity' mode. 589 * @param {Number} tangentialAccelVar 590 */ 591 setTangentialAccelVar:function (tangentialAccelVar) { 592 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity"); 593 this.modeA.tangentialAccelVar = tangentialAccelVar; 594 }, 595 596 /** 597 * Return radial acceleration of each particle. Only available in 'Gravity' mode. 598 * @return {Number} 599 */ 600 getRadialAccel:function () { 601 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity"); 602 return this.modeA.radialAccel; 603 }, 604 605 /** 606 * radial acceleration of each particle setter. Only available in 'Gravity' mode. 607 * @param {Number} radialAccel 608 */ 609 setRadialAccel:function (radialAccel) { 610 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity"); 611 this.modeA.radialAccel = radialAccel; 612 }, 613 614 /** 615 * Return radial acceleration variance of each particle. Only available in 'Gravity' mode. 616 * @return {Number} 617 */ 618 getRadialAccelVar:function () { 619 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity"); 620 return this.modeA.radialAccelVar; 621 }, 622 623 /** 624 * radial acceleration variance of each particle setter. Only available in 'Gravity' mode. 625 * @param radialAccelVar 626 */ 627 setRadialAccelVar:function (radialAccelVar) { 628 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_GRAVITY, "Particle Mode should be Gravity"); 629 this.modeA.radialAccelVar = radialAccelVar; 630 }, 631 632 // mode B 633 /** 634 * Return starting radius of the particles. Only available in 'Radius' mode. 635 * @return {Number} 636 */ 637 getStartRadius:function () { 638 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius"); 639 return this.modeB.startRadius; 640 }, 641 642 /** 643 * starting radius of the particles setter. Only available in 'Radius' mode. 644 * @param {Number} startRadius 645 */ 646 setStartRadius:function (startRadius) { 647 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius"); 648 this.modeB.startRadius = startRadius; 649 }, 650 651 /** 652 * Return starting radius variance of the particles. Only available in 'Radius' mode. 653 * @return {Number} 654 */ 655 getStartRadiusVar:function () { 656 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius"); 657 return this.modeB.startRadiusVar; 658 }, 659 660 /** 661 * starting radius variance of the particles setter. Only available in 'Radius' mode. 662 * @param {Number} startRadiusVar 663 */ 664 setStartRadiusVar:function (startRadiusVar) { 665 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius"); 666 this.modeB.startRadiusVar = startRadiusVar; 667 }, 668 669 /** 670 * Return ending radius of the particles. Only available in 'Radius' mode. 671 * @return {Number} 672 */ 673 getEndRadius:function () { 674 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius"); 675 return this.modeB.endRadius; 676 }, 677 678 /** 679 * ending radius of the particles setter. Only available in 'Radius' mode. 680 * @param {Number} endRadius 681 */ 682 setEndRadius:function (endRadius) { 683 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius"); 684 this.modeB.endRadius = endRadius; 685 }, 686 687 /** 688 * Return ending radius variance of the particles. Only available in 'Radius' mode. 689 * @return {Number} 690 */ 691 getEndRadiusVar:function () { 692 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius"); 693 return this.modeB.endRadiusVar; 694 }, 695 696 /** 697 * ending radius variance of the particles setter. Only available in 'Radius' mode. 698 * @param endRadiusVar 699 */ 700 setEndRadiusVar:function (endRadiusVar) { 701 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius"); 702 this.modeB.endRadiusVar = endRadiusVar; 703 }, 704 705 /** 706 * get Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode. 707 * @return {Number} 708 */ 709 getRotatePerSecond:function () { 710 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius"); 711 return this.modeB.rotatePerSecond; 712 }, 713 714 /** 715 * set Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode. 716 * @param {Number} degrees 717 */ 718 setRotatePerSecond:function (degrees) { 719 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius"); 720 this.modeB.rotatePerSecond = degrees; 721 }, 722 723 /** 724 * Return Variance in degrees for rotatePerSecond. Only available in 'Radius' mode. 725 * @return {Number} 726 */ 727 getRotatePerSecondVar:function () { 728 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius"); 729 return this.modeB.rotatePerSecondVar; 730 }, 731 732 /** 733 * Variance in degrees for rotatePerSecond setter. Only available in 'Radius' mode. 734 * @param degrees 735 */ 736 setRotatePerSecondVar:function (degrees) { 737 cc.Assert(this._emitterMode == cc.PARTICLE_MODE_RADIUS, "Particle Mode should be Radius"); 738 this.modeB.rotatePerSecondVar = degrees; 739 }, 740 ////////////////////////////////////////////////////////////////////////// 741 742 //don't use a transform matrix, this is faster 743 setScale:function (scale, scaleY) { 744 this._transformSystemDirty = true; 745 this._super(scale, scaleY); 746 }, 747 748 setRotation:function (newRotation) { 749 this._transformSystemDirty = true; 750 this._super(newRotation); 751 }, 752 753 setScaleX:function (newScaleX) { 754 this._transformSystemDirty = true; 755 this._super(newScaleX); 756 }, 757 758 setScaleY:function (newScaleY) { 759 this._transformSystemDirty = true; 760 this._super(newScaleY); 761 }, 762 763 764 _startSize:0, 765 /** 766 * get start size in pixels of each particle 767 * @return {Number} 768 */ 769 getStartSize:function () { 770 return this._startSize; 771 }, 772 773 /** 774 * set start size in pixels of each particle 775 * @param {Number} startSize 776 */ 777 setStartSize:function (startSize) { 778 this._startSize = startSize; 779 }, 780 781 782 _startSizeVar:0, 783 /** 784 * get size variance in pixels of each particle 785 * @return {Number} 786 */ 787 getStartSizeVar:function () { 788 return this._startSizeVar; 789 }, 790 791 /** 792 * set size variance in pixels of each particle 793 * @param {Number} startSizeVar 794 */ 795 setStartSizeVar:function (startSizeVar) { 796 this._startSizeVar = startSizeVar; 797 }, 798 799 800 _endSize:0, 801 /** 802 * get end size in pixels of each particle 803 * @return {Number} 804 */ 805 getEndSize:function () { 806 return this._endSize; 807 }, 808 809 /** 810 * set end size in pixels of each particle 811 * @param endSize 812 */ 813 setEndSize:function (endSize) { 814 this._endSize = endSize; 815 }, 816 817 _endSizeVar:0, 818 /** 819 * get end size variance in pixels of each particle 820 * @return {Number} 821 */ 822 getEndSizeVar:function () { 823 return this._endSizeVar; 824 }, 825 826 /** 827 * set end size variance in pixels of each particle 828 * @param {Number} endSizeVar 829 */ 830 setEndSizeVar:function (endSizeVar) { 831 this._endSizeVar = endSizeVar; 832 }, 833 834 835 _startColor:new cc.Color4F(0, 0, 0, 1), 836 /** 837 * set start color of each particle 838 * @return {cc.Color4F} 839 */ 840 getStartColor:function () { 841 return this._startColor; 842 }, 843 844 /** 845 * get start color of each particle 846 * @param {cc.Color4F} startColor 847 */ 848 setStartColor:function (startColor) { 849 this._startColor = startColor; 850 }, 851 852 _startColorVar:new cc.Color4F(0, 0, 0, 1), 853 /** 854 * get start color variance of each particle 855 * @return {cc.Color4F} 856 */ 857 getStartColorVar:function () { 858 return this._startColorVar; 859 }, 860 861 /** 862 * set start color variance of each particle 863 * @param {cc.Color4F} startColorVar 864 */ 865 setStartColorVar:function (startColorVar) { 866 this._startColorVar = startColorVar; 867 }, 868 869 870 _endColor:new cc.Color4F(0, 0, 0, 1), 871 /** 872 * get end color and end color variation of each particle 873 * @return {cc.Color4F} 874 */ 875 getEndColor:function () { 876 return this._endColor; 877 }, 878 879 /** 880 * set end color and end color variation of each particle 881 * @param {cc.Color4F} endColor 882 */ 883 setEndColor:function (endColor) { 884 this._endColor = endColor; 885 }, 886 887 _endColorVar:new cc.Color4F(0, 0, 0, 1), 888 /** 889 * get end color variance of each particle 890 * @return {cc.Color4F} 891 */ 892 getEndColorVar:function () { 893 return this._endColorVar; 894 }, 895 896 /** 897 * set end color variance of each particle 898 * @param {cc.Color4F} endColorVar 899 */ 900 setEndColorVar:function (endColorVar) { 901 this._endColorVar = endColorVar; 902 }, 903 904 _startSpin:0, 905 /** 906 * get initial angle of each particle 907 * @return {Number} 908 */ 909 getStartSpin:function () { 910 return this._startSpin; 911 }, 912 913 /** 914 * set initial angle of each particle 915 * @param {Number} startSpin 916 */ 917 setStartSpin:function (startSpin) { 918 this._startSpin = startSpin; 919 }, 920 921 _startSpinVar:0, 922 /** 923 * get initial angle variance of each particle 924 * @return {Number} 925 */ 926 getStartSpinVar:function () { 927 return this._startSpinVar; 928 }, 929 930 /** 931 * set initial angle variance of each particle 932 * @param {Number} startSpinVar 933 */ 934 setStartSpinVar:function (startSpinVar) { 935 this._startSpinVar = startSpinVar; 936 }, 937 938 _endSpin:0, 939 /** 940 * get end angle of each particle 941 * @return {Number} 942 */ 943 getEndSpin:function () { 944 return this._endSpin; 945 }, 946 947 /** 948 * set end angle of each particle 949 * @param {Number} endSpin 950 */ 951 setEndSpin:function (endSpin) { 952 this._endSpin = endSpin; 953 }, 954 955 _endSpinVar:0, 956 /** 957 * get end angle variance of each particle 958 * @return {Number} 959 */ 960 getEndSpinVar:function () { 961 return this._endSpinVar; 962 }, 963 964 /** 965 * set end angle variance of each particle 966 * @param {Number} endSpinVar 967 */ 968 setEndSpinVar:function (endSpinVar) { 969 this._endSpinVar = endSpinVar; 970 }, 971 972 _emissionRate:0, 973 /** 974 * get emission rate of the particles 975 * @return {Number} 976 */ 977 getEmissionRate:function () { 978 return this._emissionRate; 979 }, 980 981 /** 982 * set emission rate of the particles 983 * @param {Number} emissionRate 984 */ 985 setEmissionRate:function (emissionRate) { 986 this._emissionRate = emissionRate; 987 }, 988 989 _totalParticles:0, 990 /** 991 * get maximum particles of the system 992 * @return {Number} 993 */ 994 getTotalParticles:function () { 995 return this._totalParticles; 996 }, 997 998 /** 999 * set maximum particles of the system 1000 * @param {Number} totalParticles 1001 */ 1002 setTotalParticles:function (totalParticles) { 1003 cc.Assert(totalParticles <= this._allocatedParticles, "Particle: resizing particle array only supported for quads"); 1004 this._totalParticles = totalParticles; 1005 }, 1006 1007 _texture:null, 1008 /** 1009 * get Texture of Particle System 1010 * @return {cc.Texture2D} 1011 */ 1012 getTexture:function () { 1013 return this._texture; 1014 }, 1015 1016 /** 1017 * set Texture of Particle System 1018 * @param {cc.Texture2D | HTMLImageElement | HTMLCanvasElement} texture 1019 */ 1020 setTexture:function (texture) { 1021 //TODO 1022 if (this._texture != texture) { 1023 this._texture = texture; 1024 this._updateBlendFunc(); 1025 } 1026 }, 1027 1028 /** conforms to CocosNodeTexture protocol */ 1029 _blendFunc: {src:gl.ONE, dst:gl.ONE}, 1030 /** 1031 * get BlendFunc of Particle System 1032 * @return {cc.BlendFunc} 1033 */ 1034 getBlendFunc:function () { 1035 return this._blendFunc; 1036 }, 1037 1038 /** 1039 * set BlendFunc of Particle System 1040 * @param {Number} src 1041 * @param {Number} dst 1042 */ 1043 setBlendFunc:function (src, dst) { 1044 if(arguments.length == 1){ 1045 if (this._blendFunc != src ) { 1046 this._blendFunc = src; 1047 this._updateBlendFunc(); 1048 } 1049 }else{ 1050 if (this._blendFunc.src != src || this._blendFunc.dst != dst) { 1051 this._blendFunc = {src:src, dst:dst}; 1052 this._updateBlendFunc(); 1053 } 1054 } 1055 1056 }, 1057 1058 _opacityModifyRGB:false, 1059 /** 1060 * does the alpha value modify color getter 1061 * @return {Boolean} 1062 */ 1063 getOpacityModifyRGB:function () { 1064 return this._opacityModifyRGB; 1065 }, 1066 1067 /** 1068 * does the alpha value modify color setter 1069 * @param newValue 1070 */ 1071 setOpacityModifyRGB:function (newValue) { 1072 this._opacityModifyRGB = newValue; 1073 }, 1074 1075 _isBlendAdditive:false, 1076 /** 1077 * <p>whether or not the particles are using blend additive.<br/> 1078 * If enabled, the following blending function will be used.<br/> 1079 * </p> 1080 * @return {Boolean} 1081 * @example 1082 * source blend function = GL_SRC_ALPHA; 1083 * dest blend function = GL_ONE; 1084 */ 1085 isBlendAdditive:function () { 1086 //return this._isBlendAdditive; 1087 return (( this._blendFunc.src == gl.SRC_ALPHA && this._blendFunc.dst == gl.ONE) || (this._blendFunc.src == gl.ONE && this._blendFunc.dst == gl.ONE)); 1088 }, 1089 1090 /** 1091 * <p>whether or not the particles are using blend additive.<br/> 1092 * If enabled, the following blending function will be used.<br/> 1093 * </p> 1094 * @param {Boolean} isBlendAdditive 1095 */ 1096 setBlendAdditive:function (isBlendAdditive) { 1097 //TODO 1098 this._isBlendAdditive = isBlendAdditive; 1099 if (isBlendAdditive) { 1100 this._blendFunc.src = gl.SRC_ALPHA; 1101 this._blendFunc.dst = gl.ONE; 1102 } else { 1103 this._blendFunc.src = cc.BLEND_SRC; 1104 this._blendFunc.dst = cc.BLEND_DST; 1105 /*if (this._texture && !this._texture.hasPremultipliedAlpha()) { 1106 this._blendFunc.src = gl.SRC_ALPHA; 1107 this._blendFunc.dst = gl.ONE_MINUS_SRC_ALPHA; 1108 } else { 1109 this._blendFunc.src = cc.BLEND_SRC; 1110 this._blendFunc.dst = cc.BLEND_DST; 1111 }*/ 1112 } 1113 }, 1114 1115 _positionType:cc.PARTICLE_TYPE_FREE, 1116 /** 1117 * get particles movement type: Free or Grouped 1118 * @return {Number} 1119 */ 1120 getPositionType:function () { 1121 return this._positionType; 1122 }, 1123 1124 /** 1125 * set particles movement type: Free or Grouped 1126 * @param {Number} positionType 1127 */ 1128 setPositionType:function (positionType) { 1129 this._positionType = positionType; 1130 }, 1131 1132 _isAutoRemoveOnFinish:false, 1133 /** 1134 * <p> return whether or not the node will be auto-removed when it has no particles left.<br/> 1135 * By default it is false.<br/> 1136 * </p> 1137 * @return {Boolean} 1138 */ 1139 isAutoRemoveOnFinish:function () { 1140 return this._isAutoRemoveOnFinish; 1141 }, 1142 1143 /** 1144 * <p> set whether or not the node will be auto-removed when it has no particles left.<br/> 1145 * By default it is false.<br/> 1146 * </p> 1147 * @param {Boolean} isAutoRemoveOnFinish 1148 */ 1149 setAutoRemoveOnFinish:function (isAutoRemoveOnFinish) { 1150 this._isAutoRemoveOnFinish = isAutoRemoveOnFinish; 1151 }, 1152 1153 _emitterMode:0, 1154 /** 1155 * return kind of emitter modes 1156 * @return {Number} 1157 */ 1158 getEmitterMode:function () { 1159 return this._emitterMode; 1160 }, 1161 1162 /** 1163 * <p>Switch between different kind of emitter modes:<br/> 1164 * - CCPARTICLE_MODE_GRAVITY: uses gravity, speed, radial and tangential acceleration<br/> 1165 * - CCPARTICLE_MODE_RADIUS: uses radius movement + rotation <br/> 1166 * </p> 1167 * @param {Number} emitterMode 1168 */ 1169 setEmitterMode:function (emitterMode) { 1170 this._emitterMode = emitterMode; 1171 }, 1172 1173 /** 1174 * Constructor 1175 * @override 1176 */ 1177 ctor:function () { 1178 this._super(); 1179 this._emitterMode = cc.PARTICLE_MODE_GRAVITY; 1180 this.modeA = new cc.ParticleSystem.ModeA(); 1181 this.modeB = new cc.ParticleSystem.ModeB(); 1182 this._blendFunc = {src:cc.BLEND_SRC, dst:cc.BLEND_DST}; 1183 1184 this._particles = []; 1185 this._sourcePosition = new cc.Point(0,0); 1186 this._posVar = new cc.Point(0,0); 1187 1188 this._startColor = new cc.Color4F(1,1,1,1); 1189 this._startColorVar = new cc.Color4F(1,1,1,1); 1190 this._endColor = new cc.Color4F(1,1,1,1); 1191 this._endColorVar = new cc.Color4F(1,1,1,1); 1192 1193 this._particlePool = []; 1194 }, 1195 1196 /** 1197 * initializes a cc.ParticleSystem 1198 */ 1199 init:function () { 1200 return this.initWithTotalParticles(150); 1201 }, 1202 1203 /** 1204 * <p> initializes a CCParticleSystem from a plist file. <br/> 1205 * This plist files can be creted manually or with Particle Designer:<br/> 1206 * http://particledesigner.71squared.com/<br/></p> 1207 * @param {String} plistFile 1208 * @return {cc.ParticleSystem} 1209 */ 1210 initWithFile:function (plistFile) { 1211 var ret = false; 1212 //TODO 1213 this._plistFile = plistFile; 1214 var dict = cc.FileUtils.getInstance().dictionaryWithContentsOfFileThreadSafe(this._plistFile); 1215 1216 cc.Assert(dict != null, "Particles: file not found"); 1217 return this.initWithDictionary(dict); 1218 }, 1219 1220 /** 1221 * return bounding box of particle system in world space 1222 * @return {cc.Rect} 1223 */ 1224 getBoundingBoxToWorld:function () { 1225 return cc.rect(0, 0, cc.canvas.width, cc.canvas.height); 1226 }, 1227 1228 /** 1229 * initializes a CCQuadParticleSystem from a CCDictionary. 1230 * @param {object} dictionary 1231 * @return {Boolean} 1232 */ 1233 initWithDictionary:function (dictionary) { 1234 var ret = false; 1235 var buffer = null; 1236 var deflated = null; 1237 var image = null; 1238 1239 var maxParticles = parseInt(this._valueForKey("maxParticles", dictionary)); 1240 // self, not super 1241 if (this.initWithTotalParticles(maxParticles)) { 1242 // angle 1243 this._angle = parseFloat(this._valueForKey("angle", dictionary)); 1244 this._angleVar = parseFloat(this._valueForKey("angleVariance", dictionary)); 1245 1246 // duration 1247 this._duration = parseFloat(this._valueForKey("duration", dictionary)); 1248 1249 // blend function 1250 this._blendFunc.src = parseInt(this._valueForKey("blendFuncSource", dictionary)); 1251 this._blendFunc.dst = parseInt(this._valueForKey("blendFuncDestination", dictionary)); 1252 1253 // color 1254 this._startColor.r = parseFloat(this._valueForKey("startColorRed", dictionary)); 1255 this._startColor.g = parseFloat(this._valueForKey("startColorGreen", dictionary)); 1256 this._startColor.b = parseFloat(this._valueForKey("startColorBlue", dictionary)); 1257 this._startColor.a = parseFloat(this._valueForKey("startColorAlpha", dictionary)); 1258 1259 this._startColorVar.r = parseFloat(this._valueForKey("startColorVarianceRed", dictionary)); 1260 this._startColorVar.g = parseFloat(this._valueForKey("startColorVarianceGreen", dictionary)); 1261 this._startColorVar.b = parseFloat(this._valueForKey("startColorVarianceBlue", dictionary)); 1262 this._startColorVar.a = parseFloat(this._valueForKey("startColorVarianceAlpha", dictionary)); 1263 1264 this._endColor.r = parseFloat(this._valueForKey("finishColorRed", dictionary)); 1265 this._endColor.g = parseFloat(this._valueForKey("finishColorGreen", dictionary)); 1266 this._endColor.b = parseFloat(this._valueForKey("finishColorBlue", dictionary)); 1267 this._endColor.a = parseFloat(this._valueForKey("finishColorAlpha", dictionary)); 1268 1269 this._endColorVar.r = parseFloat(this._valueForKey("finishColorVarianceRed", dictionary)); 1270 this._endColorVar.g = parseFloat(this._valueForKey("finishColorVarianceGreen", dictionary)); 1271 this._endColorVar.b = parseFloat(this._valueForKey("finishColorVarianceBlue", dictionary)); 1272 this._endColorVar.a = parseFloat(this._valueForKey("finishColorVarianceAlpha", dictionary)); 1273 1274 // particle size 1275 this._startSize = parseFloat(this._valueForKey("startParticleSize", dictionary)); 1276 this._startSizeVar = parseFloat(this._valueForKey("startParticleSizeVariance", dictionary)); 1277 this._endSize = parseFloat(this._valueForKey("finishParticleSize", dictionary)); 1278 this._endSizeVar = parseFloat(this._valueForKey("finishParticleSizeVariance", dictionary)); 1279 1280 // position 1281 var x = parseFloat(this._valueForKey("sourcePositionx", dictionary)); 1282 var y = parseFloat(this._valueForKey("sourcePositiony", dictionary)); 1283 this.setPosition(cc.p(x, y)); 1284 this._posVar.x = parseFloat(this._valueForKey("sourcePositionVariancex", dictionary)); 1285 this._posVar.y = parseFloat(this._valueForKey("sourcePositionVariancey", dictionary)); 1286 1287 // Spinning 1288 this._startSpin = parseFloat(this._valueForKey("rotationStart", dictionary)); 1289 this._startSpinVar = parseFloat(this._valueForKey("rotationStartVariance", dictionary)); 1290 this._endSpin = parseFloat(this._valueForKey("rotationEnd", dictionary)); 1291 this._endSpinVar = parseFloat(this._valueForKey("rotationEndVariance", dictionary)); 1292 1293 this._emitterMode = parseInt(this._valueForKey("emitterType", dictionary)); 1294 1295 // Mode A: Gravity + tangential accel + radial accel 1296 if (this._emitterMode == cc.PARTICLE_MODE_GRAVITY) { 1297 // gravity 1298 this.modeA.gravity.x = parseFloat(this._valueForKey("gravityx", dictionary)); 1299 this.modeA.gravity.y = parseFloat(this._valueForKey("gravityy", dictionary)); 1300 1301 // speed 1302 this.modeA.speed = parseFloat(this._valueForKey("speed", dictionary)); 1303 this.modeA.speedVar = parseFloat(this._valueForKey("speedVariance", dictionary)); 1304 1305 // radial acceleration 1306 var pszTmp = this._valueForKey("radialAcceleration", dictionary); 1307 this.modeA.radialAccel = (pszTmp) ? parseFloat(pszTmp) : 0; 1308 1309 pszTmp = this._valueForKey("radialAccelVariance", dictionary); 1310 this.modeA.radialAccelVar = (pszTmp) ? parseFloat(pszTmp) : 0; 1311 1312 // tangential acceleration 1313 pszTmp = this._valueForKey("tangentialAcceleration", dictionary); 1314 this.modeA.tangentialAccel = (pszTmp) ? parseFloat(pszTmp) : 0; 1315 1316 pszTmp = this._valueForKey("tangentialAccelVariance", dictionary); 1317 this.modeA.tangentialAccelVar = (pszTmp) ? parseFloat(pszTmp) : 0; 1318 1319 } else if (this._emitterMode == cc.PARTICLE_MODE_RADIUS) { 1320 // or Mode B: radius movement 1321 this.modeB.startRadius = parseFloat(this._valueForKey("maxRadius", dictionary)); 1322 this.modeB.startRadiusVar = parseFloat(this._valueForKey("maxRadiusVariance", dictionary)); 1323 this.modeB.endRadius = parseFloat(this._valueForKey("minRadius", dictionary)); 1324 this.modeB.endRadiusVar = 0; 1325 this.modeB.rotatePerSecond = parseFloat(this._valueForKey("rotatePerSecond", dictionary)); 1326 this.modeB.rotatePerSecondVar = parseFloat(this._valueForKey("rotatePerSecondVariance", dictionary)); 1327 } else { 1328 cc.Assert(false, "Invalid emitterType in config file"); 1329 return false; 1330 } 1331 1332 // life span 1333 this._life = parseFloat(this._valueForKey("particleLifespan", dictionary)); 1334 this._lifeVar = parseFloat(this._valueForKey("particleLifespanVariance", dictionary)); 1335 1336 // emission Rate 1337 this._emissionRate = this._totalParticles / this._life; 1338 1339 //don't get the internal texture if a batchNode is used 1340 if (!this._batchNode) { 1341 // Set a compatible default for the alpha transfer 1342 this._opacityModifyRGB = false; 1343 1344 // texture 1345 // Try to get the texture from the cache 1346 var textureName = this._valueForKey("textureFileName", dictionary); 1347 var fullpath = cc.FileUtils.getInstance().fullPathFromRelativeFile(textureName, this._plistFile); 1348 1349 var tex = cc.TextureCache.getInstance().textureForKey(fullpath); 1350 1351 if (tex) { 1352 this._texture = tex; 1353 } else { 1354 var textureData = this._valueForKey("textureImageData", dictionary); 1355 1356 if (textureData && textureData.length == 0) { 1357 cc.Assert(textureData, "cc.ParticleSystem.initWithDictory:textureImageData is null"); 1358 tex = cc.TextureCache.getInstance().addImage(fullpath); 1359 if (!tex) 1360 return false; 1361 this._texture = tex; 1362 } else { 1363 buffer = cc.unzipBase64AsArray(textureData, 1); 1364 if (!buffer) 1365 return false; 1366 var newImageData = cc.encodeToBase64(buffer); 1367 if (!newImageData) 1368 return false; 1369 1370 var img = new Image(); 1371 img.src = "data:image/png;base64," + newImageData; 1372 this._texture = img; 1373 1374 //save image to TextureCache 1375 cc.TextureCache.getInstance().cacheImage(fullpath, img); 1376 } 1377 } 1378 cc.Assert(this._texture != null, "cc.ParticleSystem: error loading the texture"); 1379 } 1380 ret = true; 1381 } 1382 return ret; 1383 }, 1384 1385 /** 1386 * Initializes a system with a fixed number of particles 1387 * @param {Number} numberOfParticles 1388 * @return {Boolean} 1389 */ 1390 initWithTotalParticles:function (numberOfParticles) { 1391 this._totalParticles = numberOfParticles; 1392 1393 this._particles = []; 1394 this._particlePool = []; 1395 1396 if (!this._particles) { 1397 cc.log("Particle system: not enough memory"); 1398 return false; 1399 } 1400 this._allocatedParticles = numberOfParticles; 1401 1402 if (this._batchNode) 1403 for (var i = 0; i < this._totalParticles; i++) 1404 this._particles[i].atlasIndex = i; 1405 1406 // default, active 1407 this._isActive = true; 1408 1409 // default blend function 1410 this._blendFunc.src = cc.BLEND_SRC; 1411 this._blendFunc.dst = cc.BLEND_DST; 1412 1413 // default movement type; 1414 this._positionType = cc.PARTICLE_TYPE_FREE; 1415 1416 // by default be in mode A: 1417 this._emitterMode = cc.PARTICLE_MODE_GRAVITY; 1418 1419 // default: modulate 1420 // XXX: not used 1421 // colorModulate = YES; 1422 this._isAutoRemoveOnFinish = false; 1423 1424 // Optimization: compile udpateParticle method 1425 //updateParticleSel = @selector(updateQuadWithParticle:newPosition:); 1426 //updateParticleImp = (CC_UPDATE_PARTICLE_IMP) [self methodForSelector:updateParticleSel]; 1427 1428 //for batchNode 1429 this._transformSystemDirty = false; 1430 1431 // udpate after action in run! 1432 this.scheduleUpdateWithPriority(1); 1433 1434 return true; 1435 }, 1436 1437 destroyParticleSystem:function () { 1438 this._particlePool = null; 1439 this.unscheduleUpdate(); 1440 }, 1441 1442 _getParticleObject:function(){ 1443 if(this._particlePool.length > 0) 1444 return this._particlePool.pop(); 1445 return new cc.Particle(); 1446 }, 1447 1448 /** 1449 * Add a particle to the emitter 1450 * @return {Boolean} 1451 */ 1452 addParticle:function () { 1453 if (this.isFull()) 1454 return false; 1455 1456 var particle = this._getParticleObject(); 1457 this.initParticle(particle); 1458 this._particles.push(particle); 1459 ++this._particleCount; 1460 1461 return true; 1462 }, 1463 1464 /** 1465 * Initializes a particle 1466 * @param {cc.Particle} particle 1467 */ 1468 initParticle:function (particle) { 1469 // timeToLive 1470 // no negative life. prevent division by 0 1471 particle.timeToLive = this._life + this._lifeVar * cc.RANDOM_MINUS1_1(); 1472 particle.timeToLive = Math.max(0, particle.timeToLive); 1473 1474 // position 1475 particle.pos.x = this._sourcePosition.x + this._posVar.x * cc.RANDOM_MINUS1_1(); 1476 particle.pos.y = this._sourcePosition.y + this._posVar.y * cc.RANDOM_MINUS1_1(); 1477 1478 // Color 1479 var start = new cc.Color4F( 1480 cc.clampf(this._startColor.r + this._startColorVar.r * cc.RANDOM_MINUS1_1(), 0, 1), 1481 cc.clampf(this._startColor.g + this._startColorVar.g * cc.RANDOM_MINUS1_1(), 0, 1), 1482 cc.clampf(this._startColor.b + this._startColorVar.b * cc.RANDOM_MINUS1_1(), 0, 1), 1483 cc.clampf(this._startColor.a + this._startColorVar.a * cc.RANDOM_MINUS1_1(), 0, 1) 1484 ); 1485 1486 var end = new cc.Color4F( 1487 cc.clampf(this._endColor.r + this._endColorVar.r * cc.RANDOM_MINUS1_1(), 0, 1), 1488 cc.clampf(this._endColor.g + this._endColorVar.g * cc.RANDOM_MINUS1_1(), 0, 1), 1489 cc.clampf(this._endColor.b + this._endColorVar.b * cc.RANDOM_MINUS1_1(), 0, 1), 1490 cc.clampf(this._endColor.a + this._endColorVar.a * cc.RANDOM_MINUS1_1(), 0, 1) 1491 ); 1492 1493 particle.color = start; 1494 particle.deltaColor.r = (end.r - start.r) / particle.timeToLive; 1495 particle.deltaColor.g = (end.g - start.g) / particle.timeToLive; 1496 particle.deltaColor.b = (end.b - start.b) / particle.timeToLive; 1497 particle.deltaColor.a = (end.a - start.a) / particle.timeToLive; 1498 1499 // size 1500 var startS = this._startSize + this._startSizeVar * cc.RANDOM_MINUS1_1(); 1501 startS = Math.max(0, startS); // No negative value 1502 1503 particle.size = startS; 1504 1505 if (this._endSize == cc.PARTICLE_START_SIZE_EQUAL_TO_END_SIZE) { 1506 particle.deltaSize = 0; 1507 } else { 1508 var endS = this._endSize + this._endSizeVar * cc.RANDOM_MINUS1_1(); 1509 endS = Math.max(0, endS); // No negative values 1510 particle.deltaSize = (endS - startS) / particle.timeToLive; 1511 } 1512 1513 // rotation 1514 var startA = this._startSpin + this._startSpinVar * cc.RANDOM_MINUS1_1(); 1515 var endA = this._endSpin + this._endSpinVar * cc.RANDOM_MINUS1_1(); 1516 particle.rotation = startA; 1517 particle.deltaRotation = (endA - startA) / particle.timeToLive; 1518 1519 // position 1520 if (this._positionType == cc.PARTICLE_TYPE_FREE) 1521 particle.startPos = this.convertToWorldSpace(this._pointZeroForParticle); 1522 else if (this._positionType == cc.PARTICLE_TYPE_RELATIVE) 1523 particle.startPos = this._position; 1524 1525 // direction 1526 var a = cc.DEGREES_TO_RADIANS(this._angle + this._angleVar * cc.RANDOM_MINUS1_1()); 1527 1528 // Mode Gravity: A 1529 if (this._emitterMode == cc.PARTICLE_MODE_GRAVITY) { 1530 var v = cc.p(Math.cos(a), Math.sin(a)); 1531 var s = this.modeA.speed + this.modeA.speedVar * cc.RANDOM_MINUS1_1(); 1532 1533 // direction 1534 particle.modeA.dir = cc.pMult(v, s); 1535 1536 // radial accel 1537 particle.modeA.radialAccel = this.modeA.radialAccel + this.modeA.radialAccelVar * cc.RANDOM_MINUS1_1(); 1538 1539 // tangential accel 1540 particle.modeA.tangentialAccel = this.modeA.tangentialAccel + this.modeA.tangentialAccelVar * cc.RANDOM_MINUS1_1(); 1541 } else { 1542 // Mode Radius: B 1543 1544 // Set the default diameter of the particle from the source position 1545 var startRadius = this.modeB.startRadius + this.modeB.startRadiusVar * cc.RANDOM_MINUS1_1(); 1546 var endRadius = this.modeB.endRadius + this.modeB.endRadiusVar * cc.RANDOM_MINUS1_1(); 1547 1548 particle.modeB.radius = startRadius; 1549 1550 if (this.modeB.endRadius == cc.PARTICLE_START_RADIUS_EQUAL_TO_END_RADIUS) { 1551 particle.modeB.deltaRadius = 0; 1552 } else { 1553 particle.modeB.deltaRadius = (endRadius - startRadius) / particle.timeToLive; 1554 } 1555 1556 particle.modeB.angle = a; 1557 particle.modeB.degreesPerSecond = cc.DEGREES_TO_RADIANS(this.modeB.rotatePerSecond + this.modeB.rotatePerSecondVar * cc.RANDOM_MINUS1_1()); 1558 } 1559 }, 1560 1561 /** 1562 * stop emitting particles. Running particles will continue to run until they die 1563 */ 1564 stopSystem:function () { 1565 this._isActive = false; 1566 this._elapsed = this._duration; 1567 this._emitCounter = 0; 1568 1569 this._particlePool = []; 1570 }, 1571 1572 /** 1573 * Kill all living particles. 1574 */ 1575 resetSystem:function () { 1576 this._isActive = true; 1577 this._elapsed = 0; 1578 for (this._particleIdx = 0; this._particleIdx < this._particleCount; ++this._particleIdx) { 1579 var p = this._particles[this._particleIdx]; 1580 p.timeToLive = 0; 1581 } 1582 }, 1583 1584 /** 1585 * whether or not the system is full 1586 * @return {Boolean} 1587 */ 1588 isFull:function () { 1589 return (this._particleCount >= this._totalParticles); 1590 }, 1591 1592 /** 1593 * should be overriden by subclasses 1594 * @param {cc.Particle} particle 1595 * @param {cc.Point} newPosition 1596 */ 1597 updateQuadWithParticle:function (particle, newPosition) { 1598 // should be overriden 1599 }, 1600 1601 /** 1602 * should be overriden by subclasses 1603 */ 1604 postStep:function () { 1605 // should be overriden 1606 }, 1607 1608 /** 1609 * update emitter's status 1610 * @override 1611 * @param {Number} dt delta time 1612 */ 1613 update:function (dt) { 1614 if (this._isActive && this._emissionRate) { 1615 var rate = 1.0 / this._emissionRate; 1616 //issue #1201, prevent bursts of particles, due to too high emitCounter 1617 if (this._particleCount < this._totalParticles) 1618 this._emitCounter += dt; 1619 1620 while ((this._particleCount < this._totalParticles) && (this._emitCounter > rate)) { 1621 this.addParticle(); 1622 this._emitCounter -= rate; 1623 } 1624 1625 this._elapsed += dt; 1626 if (this._duration != -1 && this._duration < this._elapsed) 1627 this.stopSystem(); 1628 } 1629 this._particleIdx = 0; 1630 1631 var currentPosition; // = cc.PointZero(); 1632 if (this._positionType == cc.PARTICLE_TYPE_FREE) { 1633 currentPosition = this.convertToWorldSpace(this._pointZeroForParticle); 1634 } else if (this._positionType == cc.PARTICLE_TYPE_RELATIVE) { 1635 currentPosition = cc.p(this._position.x, this._position.y); 1636 } 1637 1638 if (this._visible) { 1639 while (this._particleIdx < this._particleCount) { 1640 var selParticle = this._particles[this._particleIdx]; 1641 1642 // life 1643 selParticle.timeToLive -= dt; 1644 1645 if (selParticle.timeToLive > 0) { 1646 // Mode A: gravity, direction, tangential accel & radial accel 1647 if (this._emitterMode == cc.PARTICLE_MODE_GRAVITY) { 1648 var tmp, radial, tangential; 1649 1650 // radial acceleration 1651 if (selParticle.pos.x || selParticle.pos.y) 1652 radial = cc.pNormalize(selParticle.pos); 1653 else 1654 radial = cc.PointZero(); 1655 1656 tangential = radial; 1657 radial = cc.pMult(radial, selParticle.modeA.radialAccel); 1658 1659 // tangential acceleration 1660 var newy = tangential.x; 1661 tangential.x = -tangential.y; 1662 tangential.y = newy; 1663 tangential = cc.pMult(tangential, selParticle.modeA.tangentialAccel); 1664 1665 // (gravity + radial + tangential) * dt 1666 tmp = cc.pAdd(cc.pAdd(radial, tangential), this.modeA.gravity); 1667 tmp = cc.pMult(tmp, dt); 1668 selParticle.modeA.dir = cc.pAdd(selParticle.modeA.dir, tmp); 1669 tmp = cc.pMult(selParticle.modeA.dir, dt); 1670 selParticle.pos = cc.pAdd(selParticle.pos, tmp); 1671 } else { 1672 // Mode B: radius movement 1673 1674 // Update the angle and radius of the particle. 1675 selParticle.modeB.angle += selParticle.modeB.degreesPerSecond * dt; 1676 selParticle.modeB.radius += selParticle.modeB.deltaRadius * dt; 1677 1678 selParticle.pos.x = -Math.cos(selParticle.modeB.angle) * selParticle.modeB.radius; 1679 selParticle.pos.y = -Math.sin(selParticle.modeB.angle) * selParticle.modeB.radius; 1680 } 1681 1682 // color 1683 if (!this._dontTint) { 1684 selParticle.color.r += (selParticle.deltaColor.r * dt); 1685 selParticle.color.g += (selParticle.deltaColor.g * dt); 1686 selParticle.color.b += (selParticle.deltaColor.b * dt); 1687 selParticle.color.a += (selParticle.deltaColor.a * dt); 1688 selParticle.isChangeColor = true; 1689 } 1690 1691 // size 1692 selParticle.size += (selParticle.deltaSize * dt); 1693 selParticle.size = Math.max(0, selParticle.size); 1694 1695 // angle 1696 selParticle.rotation += (selParticle.deltaRotation * dt); 1697 1698 // 1699 // update values in quad 1700 // 1701 var newPos; 1702 if (this._positionType == cc.PARTICLE_TYPE_FREE || this._positionType == cc.PARTICLE_TYPE_RELATIVE) { 1703 var diff = cc.pSub(currentPosition, selParticle.startPos); 1704 newPos = cc.pSub(selParticle.pos, diff); 1705 } else { 1706 newPos = selParticle.pos; 1707 } 1708 1709 // translate newPos to correct position, since matrix transform isn't performed in batchnode 1710 // don't update the particle with the new position information, it will interfere with the radius and tangential calculations 1711 if (this._batchNode) { 1712 newPos.x += this._position.x; 1713 newPos.y += this._position.y; 1714 } 1715 1716 if (cc.renderContextType == cc.WEBGL) { 1717 this.updateQuadWithParticle(selParticle, newPos); 1718 } else { 1719 selParticle.drawPos = newPos; 1720 } 1721 //updateParticleImp(self, updateParticleSel, p, newPos); 1722 1723 // update particle counter 1724 ++this._particleIdx; 1725 } else { 1726 // life < 0 1727 var currentIndex = selParticle.atlasIndex; 1728 cc.ArrayRemoveObject(this._particles, selParticle); 1729 1730 //cache particle to pool 1731 this._particlePool.push(selParticle); 1732 1733 if (this._batchNode) { 1734 //disable the switched particle 1735 this._batchNode.disableParticle(this._atlasIndex + currentIndex); 1736 1737 //switch indexes 1738 this._particles[this._particleCount - 1].atlasIndex = currentIndex; 1739 } 1740 1741 --this._particleCount; 1742 1743 if (this._particleCount == 0 && this._isAutoRemoveOnFinish) { 1744 this.unscheduleUpdate(); 1745 this._parent.removeChild(this, true); 1746 return; 1747 } 1748 } 1749 } 1750 this._transformSystemDirty = false; 1751 } 1752 1753 if (!this._batchNode) 1754 this.postStep(); 1755 1756 //cc.PROFILER_STOP_CATEGORY(kCCProfilerCategoryParticles , "cc.ParticleSystem - update"); 1757 }, 1758 1759 updateWithNoTime:function () { 1760 this.update(0); 1761 }, 1762 1763 /** 1764 * return the string found by key in dict. 1765 * @param {string} key 1766 * @param {object} dict 1767 * @return {String} "" if not found; return the string if found. 1768 * @private 1769 */ 1770 _valueForKey:function (key, dict) { 1771 if (dict) { 1772 var pString = dict[key]; 1773 return pString != null ? pString : ""; 1774 } 1775 return ""; 1776 }, 1777 1778 _updateBlendFunc:function () { 1779 cc.Assert(!this._batchNode, "Can't change blending functions when the particle is being batched"); 1780 1781 if (this._texture) { 1782 if ((this._texture instanceof HTMLImageElement) || (this._texture instanceof HTMLCanvasElement)) { 1783 1784 } else { 1785 var premultiplied = this._texture.hasPremultipliedAlpha(); 1786 this._opacityModifyRGB = false; 1787 1788 if (this._texture && ( this._blendFunc.src == cc.BLEND_SRC && this._blendFunc.dst == cc.BLEND_DST )) { 1789 if (premultiplied) { 1790 this._opacityModifyRGB = true; 1791 } else { 1792 this._blendFunc.src = gl.SRC_ALPHA; 1793 this._blendFunc.dst = gl.ONE_MINUS_SRC_ALPHA; 1794 } 1795 } 1796 } 1797 } 1798 } 1799 }); 1800 1801 //Compatibility with IE9 1802 (function () { 1803 var 1804 object = typeof window != 'undefined' ? window : exports, 1805 chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', 1806 INVALID_CHARACTER_ERR = (function () { 1807 try { document.createElement('$'); } 1808 catch (error) { return error; }}()); 1809 1810 object.btoa || ( 1811 object.btoa = function (input) { 1812 for ( 1813 // initialize result and counter 1814 var block, charCode, idx = 0, map = chars, output = ''; 1815 // if the next input index does not exist: 1816 // change the mapping table to "=" 1817 // check if d has no fractional digits 1818 input.charAt(idx | 0) || (map = '=', idx % 1); 1819 // "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8 1820 output += map.charAt(63 & block >> 8 - idx % 1 * 8) 1821 ) { 1822 charCode = input.charCodeAt(idx += 3/4); 1823 if (charCode > 0xFF) throw INVALID_CHARACTER_ERR; 1824 block = block << 8 | charCode; 1825 } 1826 return output; 1827 }); 1828 1829 object.atob || ( 1830 object.atob = function (input) { 1831 input = input.replace(/=+$/, '') 1832 if (input.length % 4 == 1) throw INVALID_CHARACTER_ERR; 1833 for ( 1834 // initialize result and counters 1835 var bc = 0, bs, buffer, idx = 0, output = ''; 1836 // get next character 1837 buffer = input.charAt(idx++); 1838 // character found in table? initialize bit storage and add its ascii value; 1839 ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, 1840 // and if not first of each 4 characters, 1841 // convert the first 8 bits to one ascii character 1842 bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0 1843 ) { 1844 // try to find character in table (0-63, not found => -1) 1845 buffer = chars.indexOf(buffer); 1846 } 1847 return output; 1848 }); 1849 }()); 1850 1851 cc.encodeToBase64 = function (data) { 1852 return btoa(String.fromCharCode.apply(data, data)).replace(/.{76}(?=.)/g, '$&\n'); 1853 }; 1854 1855 /** 1856 * <p> return the string found by key in dict. <br/> 1857 * This plist files can be creted manually or with Particle Designer:<br/> 1858 * http://particledesigner.71squared.com/<br/> 1859 * </p> 1860 * @param {String} plistFile 1861 * @return {cc.ParticleSystem} 1862 */ 1863 cc.ParticleSystem.create = function (plistFile) { 1864 return cc.ParticleSystemQuad.create(plistFile); 1865 }; 1866 1867 cc.ParticleSystem.createWithTotalParticles = function (number_of_particles) { 1868 var emitter = cc.ParticleSystemQuad.create(number_of_particles); 1869 //emitter.initWithTotalParticles(number_of_particles); 1870 return emitter; 1871 }; 1872 1873 // Different modes 1874 /** 1875 * Mode A:Gravity + Tangential Accel + Radial Accel 1876 * @Class 1877 * @Construct 1878 * @param {cc.Point} gravity Gravity value. 1879 * @param {Number} speed speed of each particle. 1880 * @param {Number} speedVar speed variance of each particle. 1881 * @param {Number} tangentialAccel tangential acceleration of each particle. 1882 * @param {Number} tangentialAccelVar tangential acceleration variance of each particle. 1883 * @param {Number} radialAccel radial acceleration of each particle. 1884 * @param {Number} radialAccelVar radial acceleration variance of each particle. 1885 */ 1886 cc.ParticleSystem.ModeA = function (gravity, speed, speedVar, tangentialAccel, tangentialAccelVar, radialAccel, radialAccelVar) { 1887 /** Gravity value. Only available in 'Gravity' mode. */ 1888 this.gravity = gravity ? gravity : cc.PointZero(); 1889 /** speed of each particle. Only available in 'Gravity' mode. */ 1890 this.speed = speed || 0; 1891 /** speed variance of each particle. Only available in 'Gravity' mode. */ 1892 this.speedVar = speedVar || 0; 1893 /** tangential acceleration of each particle. Only available in 'Gravity' mode. */ 1894 this.tangentialAccel = tangentialAccel || 0; 1895 /** tangential acceleration variance of each particle. Only available in 'Gravity' mode. */ 1896 this.tangentialAccelVar = tangentialAccelVar || 0; 1897 /** radial acceleration of each particle. Only available in 'Gravity' mode. */ 1898 this.radialAccel = radialAccel || 0; 1899 /** radial acceleration variance of each particle. Only available in 'Gravity' mode. */ 1900 this.radialAccelVar = radialAccelVar || 0; 1901 }; 1902 1903 /** 1904 * Mode B: circular movement (gravity, radial accel and tangential accel don't are not used in this mode) 1905 * @Class 1906 * @Construct 1907 * @param {Number} startRadius The starting radius of the particles. 1908 * @param {Number} startRadiusVar The starting radius variance of the particles. 1909 * @param {Number} endRadius The ending radius of the particles. 1910 * @param {Number} endRadiusVar The ending radius variance of the particles. 1911 * @param {Number} rotatePerSecond Number of degress to rotate a particle around the source pos per second. 1912 * @param {Number} rotatePerSecondVar Variance in degrees for rotatePerSecond. 1913 */ 1914 cc.ParticleSystem.ModeB = function (startRadius, startRadiusVar, endRadius, endRadiusVar, rotatePerSecond, rotatePerSecondVar) { 1915 /** The starting radius of the particles. Only available in 'Radius' mode. */ 1916 this.startRadius = startRadius || 0; 1917 /** The starting radius variance of the particles. Only available in 'Radius' mode. */ 1918 this.startRadiusVar = startRadiusVar || 0; 1919 /** The ending radius of the particles. Only available in 'Radius' mode. */ 1920 this.endRadius = endRadius || 0; 1921 /** The ending radius variance of the particles. Only available in 'Radius' mode. */ 1922 this.endRadiusVar = endRadiusVar || 0; 1923 /** Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode. */ 1924 this.rotatePerSecond = rotatePerSecond || 0; 1925 /** Variance in degrees for rotatePerSecond. Only available in 'Radius' mode. */ 1926 this.rotatePerSecondVar = rotatePerSecondVar || 0; 1927 }; 1928