📜 ⬆️ ⬇️

Particles System in crowd modeling (2)

We continue the conversation from 04/07/2014 ( Particles System in crowd modeling ).

In this part I add:
  1. slow characters (these will be big shots)
  2. rounding in the way of slow arrows fast
  3. explosions (with body scattering)


A short remark about the style of writing code (for readers of the first part):


slow characters

We write the MainWaylines_2.setupEmitterForMonsterArrows () method. In fact, this is a copy-paste of the previous MainWaylines_1.setupEmitter () . I just deleted the old comments, and left them only where there are changes.
')
protected function setupEmitterForMonsterArrows():void { var emitter:Emitter2D = new Emitter2D(); //   -   1     emitter.counter = new Steady(1); var wayline:Wayline = _waylines[0]; emitter.addInitializer( new Position( new LineZone( new Point(wayline.x - wayline.radius*Math.cos(wayline.rotation), wayline.y - wayline.radius*Math.sin(wayline.rotation)), new Point(wayline.x + wayline.radius*Math.cos(wayline.rotation), wayline.y + wayline.radius*Math.sin(wayline.rotation)) ) ) ); // ,        //    emitter.addInitializer( new ImageClass( Arrow, [10] ) ); emitter.addAction( new DeathZone( new RectangleZone( -30, -30, stage.stageWidth+60, stage.stageHeight + 60 ), true ) ); emitter.addAction( new Move() ); emitter.addAction( new RotateToDirection() ); //      ,      , //        action // emitter.addAction( new MinimumDistance( 7, 600 ) ); //    emitter.addAction( new ActionResistance(.1)); emitter.addAction( new FollowWaylines(_waylines) ); var renderer:DisplayObjectRenderer = new DisplayObjectRenderer(); addChild( renderer ); renderer.addEmitter( emitter ); //   emitterWaylinesForMonsterArrows = emitter; emitterWaylinesForMonsterArrows.start(); } 


now expand and run MainWaylines_2.setup ()

 override protected function setup(e:Event=null):void { super.setup(); //        setupEmitterForMonsterArrows(); } 


we receive the picture similar to this. Large arrows merge with small ones - they exist in parallel



rounding in the way of slow arrows fast

in order for trivia to go around the big arrows, you need to give them a command. Add a line to MainWaylines_2.setup () , where Antigravities is another standard action from the library of the particle system (class library, right?).

 override protected function setup(e:Event=null):void { super.setup(); //        setupEmitterForMonsterArrows(); //   action   "  " //  (!)   ,      -       emitterWaylines.addAction( new Antigravities(emitterWaylinesForMonsterArrows, -400000) ); } 


and the result begins to look like this picture. Small shooters are already going around large ones, but a lot of them are accumulating behind. These traffic jams look ugly.



This is due to the following "conflict." Antigravities makes small arrows go around large ones. At the same time drives them forward. FollowWaylines - each arrow tends to a certain point on the perpendicular of the path, remember? Small arrows simply do not have time to go around the large one due to the fact that they are too quickly approaching the nodal points on the way. One of the solutions (and it seems to me the simplest) is to increase the length of the segments of the path (the distance between the nodes of the route).

rewrite MainWaylines_2.setupWaylines () for one line

 override protected function setupWaylines():void { _waylines = []; var w:Number = stage.stageWidth; var h:Number = stage.stageHeight; var points:Array = [new Point(-9,h*.4), new Point(w*.3,h*.4), new Point(w*.5,h*.1), new Point(w*.8,h*.1), new Point(w*.8,h*.9), new Point(w*.5, h*.9), new Point(w*.3, h*.8), new Point(-40, h*.8)]; var fitline:FitLine = new FitLine(points); var path:Path = new Path(fitline.fitPoints); /* *   .  40,  25 * *   ,    ,           * ,   ,         */ var step:Number = path.length / 25; var strength:Number = 100; for(var i:int=0; i<path.length; i+=step) { var segmentLength:int = 60;//*Math.random()+10; var pathpoint:PathPoint = path.getPathPoint(i); var wayline:Wayline = new Wayline(pathpoint.x, pathpoint.y, segmentLength, pathpoint.rotation-Math.PI/2, strength); _waylines.push(wayline); } } 


And yet, once the big arrows are significantly smaller than the small ones (60 times), they can be launched along a narrow channel (reduce the width of the Emitter for large arrows), and thus give small shooters the opportunity to bypass them more freely.

edit MainWaylines_2.setupEmitterForMonsterArrows () , reducing the LineZone emitter by 20 (10 pixels on each side)

 emitter.addInitializer( new Position( new LineZone( new Point(wayline.x - (wayline.radius-10)*Math.cos(wayline.rotation), wayline.y - (wayline.radius-10)*Math.sin(wayline.rotation)), new Point(wayline.x + (wayline.radius-10)*Math.cos(wayline.rotation), wayline.y + (wayline.radius-10)*Math.sin(wayline.rotation)) ) ) ); 


Now traffic jams behind large arrows became much less



explosions (with body scattering)


Create a new emitter - to animate the scattering of bodies

 protected function setupEmitterForExplosion():void { var emitter:Emitter2D = new Emitter2D(); //    -    emitter.addAction( new Move() ); //      ,       -     emitter.addAction( new SpeedLimit(40)); //      -  emitter.addAction( new Friction(40) ); //    -   (      ) emitter.addAction( new DeathZone( new RectangleZone( -30, -10, stage.stageWidth+40, stage.stageHeight + 20 ), true ) ); //   var renderer:DisplayObjectRenderer = new DisplayObjectRenderer(); addChild( renderer ); renderer.addEmitter( emitter ); //   emitterExplosion = emitter; emitterExplosion.start(); } 


Subscribe to MouseEvent.MOUSE_DOWN in MainWaylines_2.setup () - we will generate explosions for these events.

 stage.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown); 


why not immediately cause explosion (e); ? There you can add the animation of the explosion itself, after which you can generate the consequences

 private function handleMouseDown(e:MouseEvent):void { explosion(e); } 


Now the blast itself

 private function explosion(e:MouseEvent):void { if(emitterWaylines == null){ return; } if(emitterExplosion == null){ return; } //   var explRadius:int = 30; //      // (        ,   dot-     ) var particleOrigin:Particle2D; var particleClone:Particle2D; var particlePoint:Point = new Point(); //    ... var explPoint:Point = new Point(e.stageX, e.stageY); //     var particles:Array = emitterWaylines.particlesArray; var length:int = particles.length; //      for(var p:int=0; p<length; p++) { particleOrigin = particles[p]; particlePoint.x = particleOrigin.x; particlePoint.y = particleOrigin.y; // ,        if(Point.distance(explPoint, particlePoint) < explRadius) { /* *  ,    -         *       -    */ particleClone = particleOrigin.clone(emitterExplosion.particleFactory) as Particle2D; particleClone.angVelocity = -5 + Math.random() * 10; /* *    Arrow ( ) -    ActionScript  ,      * !      , *         emitterWaylines   * -       renderer.removeChild() * *    .        . *      ( , )   , *       ( -      ) */ particleClone.image = new Arrow(4, 0xff0000); //       emitterExplosion.addParticle(particleClone); //      particleOrigin.isDead = true; } } /* *  action    * *   , ,   -     , *    ,         (.     ). * *    -     :       * *  ,        ,    ... *   -     */ var explosion:Explosion = new Explosion(10000, explPoint.x, explPoint.y, 100); emitterExplosion.addAction(explosion); /* *          -        *       Emitter2D.update(.2) -      */ //         emitterExplosion.update(0.2); //  action Explosion -     emitterExplosion.removeAction(explosion); } 


We start. After a few clicks, we get the following picture - the red ones accumulate uncontrollably, and in fact they need to be returned back to the stream.



The essence of the necessary changes is simple - after a certain time has elapsed, it is necessary to “return” a particle to the previous flow.
1. First make changes to MainWaylines_2.setupEmitterForExplosion () :
 protected function setupEmitterForExplosion():void { var emitter:Emitter2D = new Emitter2D(); ... //  action  "" .   ,  . // .    ,       emitterExplosion.addAction( new Age() ); ... //   "   ",      ""  emitterExplosion.addEventListener(ParticleEvent.PARTICLE_DEAD, handleParticleDeadFromEmitterExplosion); } 


2. Now add changes to MainWaylines_2.explosion ()

 private function explosion(e:MouseEvent):void { ... //      for(var p:int=0; p<length; p++) { ... // ,        if(Point.distance(explPoint, particlePoint) < explRadius) { particleClone = particleOrigin.clone(emitterExplosion.particleFactory) as Particle2D; particleClone.angVelocity = -5 + Math.random() * 10; /* * action Age()   ,     *        ,  "" *           */ particleClone.lifetime = 3; particleClone.age = 0; ... } } ... } 


We start. We get.



Total:
  1. two types of units: small and large
  2. small units go around large
  3. explosions affect small units (let it be shrapnel, which does not act on tanks - large arrows)
  4. after the small ones recover from “cantusia”, they again return to the general stream


Obvious cons
  1. non-epic high speed shooter
  2. low FPS


If to solve the problem with claim 1. you can continue to play with the emitters settings (and my current method of using the particle system is not the most perfect), then what about item 2. ( FPS )? Is there potential for optimization? After all, it’s necessary to fasten the normal graphics, another bunch of game code ...

I think the potential for optimization is, and considerable
  1. The prohibition of collisions between small arrows, at current scales - in fact, a pure whim - you can increase the number of units by 2-5 times, and there will be nothing to see at all in the resulting porridge (and if the projection on the field is not top-down , as now, and isometric?). And there will be no “complete porridge” - after all, small arrows, do not forget, move along individually defined routes (each has its own position relative to the perpendicular to the tangent). Try to disable the action MinimumDistance , warning mutual collisions - you will not notice much of a difference (only when overtaking large ones). And the performance gain is significant (you can look at the action code and see, HOW MUCH there are calculations).
  2. Just turned off the "native" render - and FPS immediately grew to more than one and a half times (and if at Starling ).


Now about the complexity of the approach in general - working with the Particle System.
I hope it did not seem unnecessarily complicated - heaps of emitters, settings for them, transfer of particles between them ...
In fact, with the data-oriented approach, the entire logic of the behavior of hundreds of particles lies precisely in emitters. And now we have only three of them (of which emitters for small and large hands are twins altogether).
Emitters can also be represented as states ( State ) - following the route and being hit by a blast wave. A “transfer” of particles between emitters is nothing more than a transition between states.

The code is available on google code . Class MainWaylines_2

PS: In the next part I will add the death of the shooter (after all, the explosions are killing)
I will play with the settings of emitters - I want epic.

PPS: Question. I want to learn an easy way to create a sprite sheet of animated 3D characters. As I see it for myself:
  1. there is an animated character
  2. I want to set some parameters in a certain software product:
    • the size
    • camera angle
    • number of frames
  3. output - sprite sheet
Do not tell me which way to look? Maybe there is a DETAILED description of a similar WORKING technique?
Thank you in advance.

PPPS: added two lines to the code of the method MainWaylines_2.explosion () : zeroing the particle velocity vectors before the explosion - looks more natural

 protected function explosion(e:MouseEvent):void { ... particleClone.velX = 0; particleClone.velY = 0; ... } 

Source: https://habr.com/ru/post/218697/


All Articles