Fork me on GitHub
Not signed in (Sign In)

Welcome, Guest

Want to take part in these discussions? Sign in if you have an account, or apply for one below

    • CommentAuthorDraco18s
    • CommentTimeMay 24th 2008
     
    This is more of a collection of things I'm trying to do.

    I started with the Flocking.as script and started adding things and removing things to try and get a somewhat liquid-like behavior out of the particles. For the most part I've succeeded, however, now I want to add more complex behavior.

    What I'm trying to accomplish is use the system to try and avoid writing my own particle system to appoximate flowing lava while the user commands a water cannon (it's a game). I need to be able to effect the particles (make them stop moving and change color to black, and at some point they should be able to revert) as well as get the rest of the particles to flow around the "frozen" ones.
    • CommentAuthorRichard
    • CommentTimeMay 24th 2008
     
    Hi

    My immediate thoughts are...

    You can stop a particle by changing its velX and velY properties to zero. You can change its color to black by setting its color property to zero (or 0x000000 which is the same thing). You could also store the previous values in the particle's dictionary for retrieval later. Where you place this code depends on what causes the particle to stop. To integrate it with the rest of the system you probably want to create a custom action. This might look something like this...

    package
    {
    import org.flintparticles.actions.Action;
    import org.flintparticles.emitters.Emitter;
    import org.flintparticles.particles.Particle;

    public class CustomStopStart extends Action
    {
    private function shouldStop( particle:Particle ):Boolean
    {
    // do calculations and return a boolean to indicate if the particle should be stopped or moving
    }

    override public function update( emitter:Emitter, particle:Particle, time:Number ):void
    {
    if ( shouldStop( particle ) )
    {
    if( !particle.dictionary[this] )
    {
    var oldValues:Object = { velX:particle.velX, velY:particle.velY, color:particle.color };
    particle.dictionary[this] = oldValues;
    particle.velX = 0;
    particle.velY = 0;
    particle.color = 0;
    }
    }
    else
    {
    if( particle.dictionary[this] )
    {
    var oldValues:Object = particle.dictionary[this];
    particle.velX = oldValues.velX;
    particle.velY = oldValues.velY;
    particle.color = oldValues.color;
    particle.dictionary[this] = null;
    }
    }
    }
    }
    }


    Use the action object itself as the key in the dictionary so you don't clash with any other actions that use the dictionary.

    The shouldStop method does whatever it needs to do to determine if the particle should be stationary or not.

    What remains (other than deciding what goes in the shouldStop method) is to make the moving particles flow around the stationary ones. You may be able to do this with the minimumDistance action, although I suspect you might get a logjam of particles stopping in front of the stationary particles. Adding a TargetVelocity may fix this - it will cause the particles to try not to stop, but you need to try it to find the consequences of this mix, and it may mess with your other actions. If it doesn't work, you need an avoidance action, which requires particles to steer around other particles. That's not so easy but their are some resources on steering behaviour online - your favourite search engine is a good place to start.

    Good luck and let us know how you get on.
    • CommentAuthorDraco18s
    • CommentTimeMay 24th 2008
     
    Thanks. I'll try this.
    I'm new to AS3 (had to grab Flash CS3 just to use the great system you guys built) and was more or less at a loss on how to make it do New Stuff.
    • CommentAuthorDraco18s
    • CommentTimeMay 25th 2008
     
    Alright, I copied the script and made it an .as file and started thinking about how I'd define a way to freeze the particle. Obviously I don't want them all to stop at once, so I need something that I can trigger on a particle by particle basis and will persist for a time. I was thinking a mouse event, but as a bitmap (which is what it looks like the paricles are) they don't have that.

    Looks like Age might be able to do the duration, but I need to be able to check for the mouse event.

    Thanks again.
    • CommentAuthorDraco18s
    • CommentTimeMay 26th 2008
     
    Slight problem: compiler doesn't like the duplicate definition of var oldValues:Object.
    However, if I make a constructor function and create the variable there, the compiler complains about undefined properties.
    • CommentAuthorRichard
    • CommentTimeMay 26th 2008 edited
     
    Whether the particles are displayed using a bitmap or a displayObject depends on the renderer you use. To get displayObjects use the DisplayObjectRenderer (if you've used the code from the flocking example then you are already using this). To catch the mouse events, you need to set the renderers mouseEnabled and mouseChildren properties to true so that the renderer and the particles will respond to mouse events.

    To avoid the duplicate definition of oldValues, try this.

    package
    {
    import org.flintparticles.actions.Action;
    import org.flintparticles.emitters.Emitter;
    import org.flintparticles.particles.Particle;

    public class CustomStopStart extends Action
    {
    private function shouldStop( particle:Particle ):Boolean
    {
    // do calculations and return a boolean to indicate if the particle should be stopped or moving
    }

    override public function update( emitter:Emitter, particle:Particle, time:Number ):void
    {
    var oldValues:Object;
    if ( shouldStop( particle ) )
    {
    if( !particle.dictionary[this] )
    {
    oldValues = { velX:particle.velX, velY:particle.velY, color:particle.color };
    particle.dictionary[this] = oldValues;
    particle.velX = 0;
    particle.velY = 0;
    particle.color = 0;
    }
    }
    else
    {
    if( particle.dictionary[this] )
    {
    oldValues = particle.dictionary[this];
    particle.velX = oldValues.velX;
    particle.velY = oldValues.velY;
    particle.color = oldValues.color;
    particle.dictionary[this] = null;
    }
    }
    }
    }
    }
    • CommentAuthorDraco18s
    • CommentTimeMay 27th 2008 edited
     
    Ok no errors now, but when shouldStop returns true the paricle disapears. I'm poking around with the update function, which I don't understand. It doesn't look like it does what would make sense for it to do.

    By "disapears" I mean that if I do anything that could result in shouldStop() in returning true (such as an if(Math.random() < 0.01) { return true; } else {return false;} ) then no particles are rendered, at all, ever.

    slowing the movie down to 1 FPS I did see them flash briefly at 0,0.
    • CommentAuthorDraco18s
    • CommentTimeMay 27th 2008
     
    Update:

    I got them to not all disapear at once. However, when the shouldStop() does return true, the particle simply pops (flashes briefly at 0,0 and then is gone). Had to declare oldValues as a class variable, not a function variable. Thought something was wrong.

    But still, what causes _x and _y (as per movieClip) to become undefined?
    • CommentAuthorDraco18s
    • CommentTimeMay 27th 2008 edited
     
    Appears that setting velX and velY to 0 the particle pops. I can set one to 0 and leave the other at a very very tiny number, but it still vibrates visibly (0.001 and 0.00000001 seem to vibrate the same amount). Setting color to 0 also makes them pop. Needs to be particle.color = 0xFF000000.

    On another note, I was looking at the possibility of adding and removing actions from the list for these stopped particles, but that doesn't look feasible (things go nuts), not that the removeAction function seems to work. I can't find any examples of how to call it, and the only way I've gotten it to compile it hasn't worked anyway. The idea was to remove the minimumDistance that the moving ones have (a pretty soft one, distance 30, acceleration 30) and make it a bit firmer, to prevent the moving particles from passing throught the stopped ones (distance 30, acceleration 100).

    Anyway, here's what I've done, if it makes more sense that way, assuming I'm using the right tags. ^^;


    package org.flintparticles.actions
    {
    import org.flintparticles.actions.Action;
    import org.flintparticles.emitters.Emitter;
    import org.flintparticles.particles.Particle;

    public class CustomStopStart extends Action
    {
    //I also added these to the particle.as script, having them
    //here didn't seem to let things work right
    //it would use the same variable for ALL the particles instead
    //of on a one-by-one basis
    public var Stopped:Boolean;
    public var oldValues:Object;
    public var oldVelX:Number = 0;
    public var oldVelY:Number = 0;
    //end particle.as mod

    private function shouldStop( particle:Particle ):Number
    {
    //at the moment, a RNG determines state--was the first
    //thing I got to make all the particles different
    if(particle.Stopped) {
    if(Math.random() < 0.001) {
    //trace("stopping");
    return 1;
    }
    //trace("still stopped");
    return -2;
    }
    else{
    if(Math.random()< 0.001) {
    //trace("starting");
    return -1;
    }
    //trace("still moving");
    return 2;
    }
    return 0;
    }

    override public function update( emitter:Emitter, particle:Particle, time:Number ):void
    {
    var s = shouldStop( particle );
    if (s < 0 )
    {
    if( s == -1)
    {
    particle.oldValues = particle.dictionary[this];
    particle.oldVelX = particle.velX;
    particle.oldVelY = particle.velY;
    particle.scale = 0.5;
    particle.velX = .0000000001;
    particle.velY = .0000000001;
    particle.color = 0xFF000000;
    particle.dictionary[this] = null;
    particle.Stopped = true;
    }
    else {
    particle.velX = -particle.velX;
    particle.velY = -particle.velY;
    }
    }
    else
    {
    if( s == 1 )
    {
    particle.dictionary[this] = oldValues;
    particle.scale = 1;
    particle.color = 0xFFFFFFFF;
    particle.Stopped = false;
    }
    }
    }
    }
    }
    • CommentAuthorRichard
    • CommentTimeMay 27th 2008 edited
     
    This works perfectly for me -

    package org.flintparticles.actions
    {
    import org.flintparticles.actions.Action;
    import org.flintparticles.emitters.Emitter;
    import org.flintparticles.particles.Particle;

    public class CustomStopStart extends Action
    {
    private function shouldStop( particle : Particle ) : Boolean
    {
    if( particle.dictionary[this] ) // particle is stopped
    {
    return Math.random() < 0.95;
    }
    else // particle is not stopped
    {
    return Math.random() < 0.05;
    }
    }

    override public function update( emitter : Emitter, particle : Particle, time : Number ) : void
    {
    var oldValues : Object;
    if ( shouldStop( particle ) )
    {
    if( !particle.dictionary[this] )
    {
    oldValues = { velX:particle.velX, velY:particle.velY, color:particle.color };
    particle.dictionary[this] = oldValues;
    particle.velX = 0;
    particle.velY = 0;
    particle.color = 0;
    }
    }
    else
    {
    if( particle.dictionary[this] )
    {
    oldValues = particle.dictionary[this];
    particle.velX = oldValues.velX;
    particle.velY = oldValues.velY;
    particle.color = oldValues.color;
    particle.dictionary[this] = null;
    }
    }
    }
    }
    }


    Things to be aware of -

    • If you set the velocity to zero (particle.velX = 0, particle.velY = 0) in this action, other actions may change it again or may react to it. So if you are getting strange behaviours look for other actions that are dependent on the properties you're setting here. For example, if you have a minimumDistance action on the particles, this will cause the particles to accelerate away from other particles, which means particles will start moving again after you set the velocity to zero.

    • The particle class is not dynamic. Trying to add random properties to it (e.g. particle.stopped) is likely to cause errors.

    • But the particle has a dictionary that you can add properties to (hence reading and writing to particle.dictionary[this]).

    • By setting the dictionary entry for this class to null when the particle is not stopped, we can tell if the particle is stopped just by testing whether this property is set [if( particle.dictionary[this] )]

    • Actions are not set on particles, they are set on the emitter and apply to all particles created by that emitter. So removeAction is a method of the emitter, not the particle.

    • One instance of the action class is used by all particles so any state held in this instance is shared by all the particles. Particle specific state must be stored in the particle dictionary.

    • CommentAuthorDraco18s
    • CommentTimeMay 30th 2008
     
    Thanks for that. I found out what it was: emitter.addAction( new SpeedLimit( 20, true ) );
    I'm not sure *why* it pops the particle when certain attributes are set to 0 (didn't really look into it), but that's what was doing it.

    Anyway, new question:

    Is it possible to use a constant stream emission of particles, yet have a cap on the number of existant particles? emitter.pause() pauses the whole thing, what I need is just a pause on the creation of new particles so I don't ever have more than about 3 or 4 hundred on the screen at once.

    Thanks.
    • CommentAuthorDraco18s
    • CommentTimeJun 1st 2008 edited
     
    Removed because I solved the problem through a discussion on the Adobe forums. I hate AS 3 now. Why can't it make sense? _root *should always be available!*
    • CommentAuthorericr
    • CommentTimeJun 2nd 2008 edited
     
    Why can't it make sense?
    Give it time - it will. An absolutely indispensable resource for AS3 programming is Collin Moock's "Essential ActionScript 3.0": a fantastic resource for even the most seasoned programmer.

    _root *should always be available!*
    _root is a deprecated AS2 convention. In AS3, _root is known as the stage and you always have access to it. All display objects that are in the Display List (including your "Main" class which has to extend Sprite or MovieClip) have a reference to the Stage.

    Let's say you're in your Main class or equivalent (or the Action panel) and you need to access the Stage. It's as simple as this:
    trace(this.stage.stageWidth);
    That will trace the width of your Stage. It's pretty darn simple and convenient once you get the hang of things!
    • CommentAuthorDraco18s
    • CommentTimeJun 2nd 2008
     
    It's just exceedingly annoying to have to relearn things.

    BTW, my question about stopping particle emission is still relevant. Still curious on how to do that. Almost had what I wanted at one point by flipping the emitter.counter variable, though I had a limit to how well the comparison opperator worked (had to cast it as a string, rather than being able to directly compare one object to another, so I couldn't change it from one kind of steady to another; had to flop between a blast (zero emission) and a steady (normal emission)). I was wondering if there was a better way. The...performance adjusted counter didn't quite do what I wanted. It seemed to slow down and then no speed back up again, as if it found an emission rate that it would be able to keep up forever regardless of what I did to prevent or encourage particle death.
    • CommentAuthorRichard
    • CommentTimeJun 5th 2008
     
    You can turn off the steady counter by calling the stop() method, and turn it on again using the resume() method. You can also adjust the rate once it's going by setting the rate property.

    So, for example...

    emitter.counter = new Steady( 100 );

    // stop the counter

    emitter.counter.stop();

    // restart the counter

    emitter.counter.resume();

    // adjust the rate of the counter

    emitter.counter.rate = 50;
    • CommentAuthorDraco18s
    • CommentTimeJun 5th 2008 edited
     
    Ah ha! Great! Thanks! I hadn't thought to check the counter for a stop function, despite how obvious that is of a place to put it. Oi.

    Anyway, all that's left is to figure out why the mouse event listener I set up this afternoon* isn't working (despite being coded exactly how the documentation coded it, sole exception being that the stage was passed to the package instead of being called using 'stage'). That one I'll hit the Adobe forums for.

    You guys have been great. :)

    *I have a "water cannon" at the bottom of the screen shooting blue particles (Sparkler example modified) towards the mouse (not the best solution to use TurnTowardsMouse, but the easiest) that upon 'contact' with the magma, hardens it (script as above, only instead of distance to the mouse, distace to each particle in the water spray--probably not the fastest meathod, but an easy solution).
    • CommentAuthorRichard
    • CommentTimeJun 5th 2008
     
    Would love to see the finished result when you're done.
    • CommentAuthorDraco18s
    • CommentTimeJun 5th 2008
     
    Went to use the emitter.counter.stop() and .resume() and Flash yells at me about undefined functions. :-\
    And I did check that they're in the Steady.as file, this is weird.

    Meh, I'll go back to the setting it to Blast and then back to Steady, it works as long as I make sure it's only being called once when it needs to change.
    • CommentAuthorRichard
    • CommentTimeJun 6th 2008 edited
     
    Ahh. Sorry about that. That will be because the methods are in Steady but not in Counter. Either cast the counter to Steady...

    Steady( emitter.counter ).stop();

    or store the counter in a variable of type Steady...

    var steadyCounter:Steady = new Steady( 100 );
    emitter.counter = steadyCounter;
    steadyCounter.stop();
    • CommentAuthorDraco18s
    • CommentTimeJun 7th 2008
     
    Ah. Well, I finished up last night and spent an hour and a half making buttons (three to be precise) and decided that AS3's way of doing buttons is not better than AS2's, because of the variables needed by the button (the particle emitters can't be started until the Play button is clicked, which means the code needs to be inside one of the particle package scripts that has access to the stage, which means that the Play button MUST be somewhere on the screen on the first frame of the movie...meaning that my group member's nice pre-game animation has to play behind the buttons.

    Anyway, I've tossed up the swf here: http://www.pages.drexel.edu/~mmj29/temp/Magma.swf

    I think there are about 4 custom .as files extending action in that. Oh, and emitter.dispose() doesn't actually clean up the particles because the ParticleFactory code contains three empty functions. Didn't find a quick work-around. Also never figured out how to use the particle dictionary, mostly because what I had worked and I was running out of time (there's still some temporary graphics because a group member didn't give me any actual graphics).
    • CommentAuthorRichard
    • CommentTimeJun 9th 2008
     
    emitter.dispose() doesn't actually clean up the particles because the ParticleFactory code contains three empty functions

    ParticleFactory is an interface - interfaces contain method signatures without implementations. The implementation being used is in ParticleCreator. There is a different reason for dispose not working (it wasn't telling the renderer to remove the particles) - I've checked a fix into SVN.
    • CommentAuthorericr
    • CommentTimeJun 9th 2008
     
    Well, I finished up last night and spent an hour and a half making buttons (three to be precise) and decided that AS3's way of doing buttons is not better than AS2's, because of the variables needed by the button (the particle emitters can't be started until the Play button is clicked, which means the code needs to be inside one of the particle package scripts that has access to the stage, which means that the Play button MUST be somewhere on the screen on the first frame of the movie...meaning that my group member's nice pre-game animation has to play behind the buttons.

    I'm not 100% sure what your build process and development setup is but you should be importing the Flint packages you need into your project (whether that's in the Actions panel in CS3 or your Main class in Flex/Flash Develop/Other). Either way you do it you can hide the buttons (even if this means making them transparent) if you feel they need to be on the Stage from the get-go...

    If you're serious about Flash development then I'd seriously take a look at Essential Actionscript 3.0. That book is the first place I go whenever any AS3 confusions arises and it's saved my butt time and again. :)