Unreal Developer Network Content Creation
Welcome to the Unreal Developer Network The Engine Unreal Powered Content Creation Technical Playstation2 Xbox Gamecube Licensee Log In

Content Creation

Content Creation home

Documents listed below, but not hyperlinked, are restricted to engine licensees only.

As certain texts become relevant, due to released games using that technology, new documents will be made available. Check back often!
 
Getting Started
   - WhatToReadFirst
   Support
   - UnEdit (mailing list)
   - UnEditTraffic (summaries)
   - UnDevIRC (chat)
   - UnDevIRCTraffic (summaries)
   - UnrealEdSux0rs (bug list)
   Engine Prerequisites
   - BasicConcepts?
   - TextureSpecifications
   - TextureComparison
   - Etc.

General Editor
   Basics
   - IntroToUnrealEd
   - UnrealEdInterface
   - RotationGizmo
   - UnrealEdKeys
   - BrushClipping
   - VertexEditing
   - BoxSelection
   - ShapeEditor
   - ExampleMaps
   - TriggersTutorial
   - WorkFlow
   Primitives
   - BspBrush
   - MirrorsAndWarpZones
   - HardwareBrush
   - MoversTutorial
   - UKXPackagesTutorial
   - TerrainTutorial
   - VolumesTutorial
   - LightingTutorial
   - ProjectiveTutorial
   - MaterialTutorial
   - CollisionTutorial
   - FluidSurfaceTutorial
   Animation
   - AnimNotifies
   Particles
   - EmittersTutorial
   - EmittersExamples
   New Particle Editor
   - ParticleSystems
   - ExampleParticleSystems
   Matinee
   - MatineeTutorial
   - MatineeExample
   - MatineeDemoOpening
   - MatineeDemoDropship
   - MatineeDemoSoldiers?
   - SampleMatineeTips
   Scripted Sequences
   - ScriptedSequenceTutorial
   - ScriptedSequenceActions
   - AIControllers
   Techniques
   - LevelOptimization
   - GroupsBrowser
   - NavigationAI
   - VertexBlendingTutorial
   - ConvertingContent739To829

Tools
   - ActorX
   - UmodWizard
   - ModelingTableOfContents
   - CADtoUnreal

mathengine.gif
Karma Physics
   - Karmareference
   - KarmaExample1
   - KarmaExample2
   - ImportingPhysicsAssets
   - RagdollsInUT2003
   - KarmaExampleUT2003
   - ExampleMapsKarmaColosseum

secretlevel.gif
PlayStation2 and GameCube
   - ConsoleDevelopment

Contribute!
You can create a new page, and then you can edit this list to add it to the categories, as well as edit the Content Creation homepage to tell everyone about it!

Make Requests!
You can also stop by the UdnStaff page to see what we're working on, and edit it to add your own document requests.


Please take note! For mod developers working with Unreal Tournament 2003, this documentation is meant to be a starting point for your own explorations into UT2003, not a definitive guide. There will be differences between the documentation here and the product in your hands, and you may have to figure out quite a bit for yourself. Check out the Unreal Tournament 2003 page in the Unreal Powered area for links to community sites if you're having problems. UDN is a licensee support site, and cannot provide technical support or game-specific assistance to end users.

EmittersTutorial

Licensees can log in.

Interested in the Unreal engine? Check out the licensing page.

Questions about UDN itself? Contact the UDN Staff.

Emitters

  • updated by Bob Berry to add link to crash bugfix thread
  • updated under engine build 777 by Lode Vandevenne.
  • Original Author: Lode Vandevenne

Introduction

The Emitters are particle spawners: they can emit particles, and using a huge amount of properties, you can tell the emitter what particles to make, how many, what the particles will do and more. Almost anything you can think of is possible, and you can create very cool effects with it. You can make fire, rain, snow, waterfalls, smoke, fireworks, lightnings and much more.

demo1.jpg demo2.gif demo3.jpg

Don't exaggerate the number of particles or Emitters, they can kill the performance easily. You can make most of the cool effects with only a few particles.

Like the Fire Textures, also the Emitters need a certain warmup time. This means when you open the map and look at the Emitter for the first time, there aren't spawned any particles yet, and it can take some time before the effect has reached the maximum amount of particles. The length on it depends on the properties you set and the effect you want to create. Most of the times, the warmup is finished when the maximum number of particles is reached, so the first particle dies and in most cases respawns. It's possible to make the engine to precalculate warmuptime so the effect of it isn't visible.

For the Properties, a certain order is used in this tutorial so properties are explained after other properties they depend on, but sometimes it's impossible to do this so there will be a lot of references from one section to another.

This document covers the theory about Emitters: what they are, how their properties work, how to add them in your map, etc... Another document covers the practical side of emitters, how you can make the cool effects with it, with example maps included. To fully understand that document, you better read this one first. Sorry, but it's a long one...

There are some animated gifs here and there, so please don't press the "Stop" button of your browser or they'll stop animating.

The final section, Shapes, is there to show how you can make the particles to form certain shapes together if you use certain combinations of properties.

Setting it up

To add a working Emitter in your map, first you have to select the Emitter in the Actor Class Browser, it's directly in the Actors list.

actorclasses.jpg

Add the Emitter in your map at the place where you want the effect. In the editor, the Emitter looks like 5 colored dots. (in 777, it might be invisible; use the Search For Actors button and search for Emitter0, then open Actor Properties in the view menu to open the properties of the invisible emitter)

5dots.jpg

This Emitter will not do a lot on it's own, it's just a placeholder for one or more sub- emitters, called ParticleEmitters. Open the properties of the Emitter and expand Emitter. Click on Emitters (you can't expand this yet, because it's empty now) and press the Add button that appears. Now Emitter[0] gets created in the list.

add1.jpg add2.jpg

But it's not done yet! Expand Emitters and expand [0]. There you have to choose between SpriteEmitter, SparkEmitter, MeshEmitter or BeamEmitter. When you made your choice, press the New button and there will be the new ParticleEmitter with all it's properties.

add3.jpg add4.jpg

ParticleEmitters

From now on, I'll call the actor you added in the map that looks like 5 colored dots a "Emitter", and the things you can create in the properties of it a "ParticleEmitter", because these are also called like this in the Actor Class Browser. So there are 4 kinds of ParticleEmitters: SpriteEmitter, SparkEmitter, MeshEmitter and BeamEmitter. Before you actually see something happening, you have to apply some other properties, and this is different for every kind of ParticleEmitter. An explanation for each of them is later in this tutorial.

As you've seen, you have to add a ParticleEmitter inside the properties of the Emitter. You can also add two or more ParticleEmitters in one Emitter. Each of these ParticleEmitters works completely independent, but they are inside the same actor. The ParticleEmitters are automatically stored in the MyLevel package, with names like "BeamEmitter0" or "MeshEmitter8". In the previous section of this tutorial, you had to do 2 things to add a new ParticleEmitter: first you had to make a new place for the ParticleEmitter by pressing the Add button, and then you had to fill that new place with an actual ParticleEmitter. To fill that space, you pressed the New button, so a new ParticleEmitter was created in the MyLevel package. You can also copy the name of an already existing ParticleEmitter in there.

For example, this is an Emitter with 6 places in it. 2 of the places are filled with a ParticleEmitter, and the other 4 are still empty.

particleemitters.jpg

To add a new ParticleEmitter, for example a SpriteEmitter in place [2], press the button next to the [2], there choose SpriteEmitter and press the New button. Now the name SpriteEmitter'MyLevel.MyLevel.SpriteEmitter0' should appear in the textfield, assuming this is the first SpriteEmitter in your map.

new.jpg

To remove this SpriteEmitter again, press the Clear button next to the [2]. Now place [2] will be empty again. Then it'll be the same again as it was before you added the SpriteEmitter.

clear.jpg

To delete a placeholder, for example place[5], press the Delete button next to the [5]. Now the Emitter will have 5 placeholders left.

delete.jpg

To add place[5] again, press the Insert button at any of the other placeholders, or press the Add button next to Emitters. If you use the Insert button, the new place will be inserted before the one with the Insert button. This means if you pressed the Insert button of place [4], the new place will become place [4] and the one where you pressed the button becomes place [5].

insert.jpg

If you want to give place [2] exactly the same SparkEmitter as the one in place [0], first select the name of the SparkEmitter in [0] and copy it (press CTRL + c), then click on the word "None" in place [2] and paste the name of the SparkEmitter there (Press CTRL + v). Now the ParticleEmitter of place [0] and place [2] share the same data, so if you change a property (for example the Velocity) in place [0], this property is also automatically changed in place [2]. This is not useful for 2 places that are in the same Emitter: two identical ParticleEmitters that emit their particles at the same Location, look like one ParticleEmitter. But you can also copy the name in one Emitter, and paste it again in another Emitter somewhere else in your map. This way, you can make one ParticleEmitter that is used in more than one Emitter. For example, you can make 4 fires in a room, that use the same ParticleEmitter, so when you change the properties of one of the fires, the others change as well. This way you don't have to change the properties of the 4 fires separately.

copy.jpg paste.jpg

If you want to remove all the places from an Emitter, just press the Empty button next to Emitters, and it's all gone.

empty.jpg empty2.jpg

Every place that is filled with a ParticleEmitter, gets all the properties of this ParticleEmitter. To view the properties, you have to press one or more buttons. If you open the properties of an Emitter, you'll have to press 4 different buttons to get into the ParticleEmitter properties.

SpriteEmitter

The SpriteEmitter emits sprites. This are 2-dimensional textures, so for the computer they're much easier to calculate than a mesh. In old games, for example Wolfenstein3D or Doom, sprites were used for all actors, decorations, pickups, etc... because it was impossible for the computers of that time to draw good looking 3D models in real time. For most emitter effects, SpriteEmitters will do perfectly: because there will be a lot of sprites, it will look as if it were 3D.

If you follow the steps of the section Setting it up and at the end choose for the SpriteEmitter, you have one. You can preview the ParticleEmitters in the editor. If you activate the Realtime Preview button realtimepreview.jpg on top of the 3D view and move the camera a little bit in it, you'll already see a sprite particle appear. It's a large, translucent version of the 5 colored dots texture. Actually, it's not one particle, but a lot of particles painted on exactly the same Location. Later, when you use the Velocity or Acceleration properties they will start moving. These particles now have the same texture as the Emitter actor itself, but you'll be able to change it later.

spriteemitter.jpg

Now, all the properties of the SpriteEmitter get explained. The SparkEmitter, MeshEmitter and BeamEmitter have most of these properties in common, but because they have their own characteristics and unique properties, they are explained later. First expand the Properties of the SpriteEmitter: you have to press 4 buttons in the Emitter properties:

properties.jpg

Remark: to select the Emitter actor, you can also click on one of the particles instead of having to click that small 5 colored dot texture.

Acceleration

To make the particles moving, you can give them Velocity and/or Acceleration. The Acceleration makes them move faster and faster, and is very easy to set up: open the properties of the Emitter and in there open the properties of the SpriteEmitter. In there, expand Acceleration.

acceleration2.jpg

There you can enter the amount of Acceleration you want along the X, Y and Z axis. You can enter negative values too. If you assume that the upper part of the Top View is the North, here's what the settings do:

base.jpg

If X > 0, the particle will go to the East If X < 0, it'll go to the West If Y > 0, it'll go to the South If Y < 0, it'll go to the North If Z > 0, it'll go to the ceiling or sky If Z < 0, it'll go to the ground The total acceleration is the sum of these 3 components, so if X = 425, Y = -950 and Z = -950, the particles will go to the North-East and down, and will go to the North with more acceleration than to the East. However, if you rotated the Emitter actor with the Rotation tool, this won't be correct anymore, the CoordinateSystem will be rotated then. The X-axis will then be the direction of the arrow if you set bDirectional to True in advanced. Once you gave it some acceleration, you'll see the particles move.

acceleration.gif

Velocity

If you expand all the Velocity settings, it looks like this:

velocity.jpg

Use StartVelocityRange to give the particles a constant speed. The X, Y and Z values use the same directions as the X, Y and Z of the Acceleration (for example if X < 0, it'll go to the West). There are a Min and a Max value for every axis. If Min and Max are the same, you get what you would expect: the particle moves with the speed you entered to the direction you used. If Min < Max, the particle will move with a random, constant speed that is something between the Min and Max value. For example, if you give X(Min), X(Max), Y(Min) and Y(Max) of StartVelocityRange all the same value +500 (and make sure there is no acceleration), all the particles will move towards the same direction:

velocity1.jpg

But if you set X(Min) to -500 and X(Max) to +500 (and keep the Y values), the particles will move with a random direction:

velocity2.jpg

If you'd have made X(Min) = -150 and X(Max) = +150, there'd be less difference in the directions:

velocity3.jpg

While Acceleration makes the particle move faster every second, the StartVelocityRange is a constant speed. If there is no Acceleration used, but there is a Velocity, the particle will move with the same speed as long as it lives. If you use Velocity AND Acceleration, the total speed of the particle will be the sum of the constant Velocity and the variable speed caused by the Acceleration. This sum is called the AbsVelocity. If there is a Velocity and a Acceleration along the same axis, but they have opposite signs, the particle will first move in one direction, but at a certain point the absolute value of the speed caused but the Acceleration will be larger than the absolute value of the Velocity, so the particles will move back to the direction they came from. If you use a Velocity and an Acceleration, both along a different axis, the particles will make a parabola. A parabola is the realistic trajectory for example a bullet or an object you throw follow. For example if Acceleration has Z = -950 (close to the gravity on Earth), and StartVelocityRange has X(Min) = X(Max) = + 500, it'll look like this:

parabola1.jpg

If you now set also the X of Acceleration to -1000, it'll do the following:

parabola2.jpg

The sum of the velocity caused by the Acceleration and the Velocity is the AbsVelocity, and you can set a MaxAbsVelocity value for every axis. If you use this, the Acceleration will keep speeding up the particle until the MaxAbsVelocity speed is reached. Then the particle will have a constant speed, equal to the AbsVelocity. If you set in the first parabola example, the Z value of MaxAbsVelocity to 600, the function will look like a parabola in the beginning, but once the absolute value of the speed along the Z axis is 600, the function will become a constant function (a line). Always give MaxAbsVelocity a positive value, even if the Acceleration is negative. If you give it a negative value, the particles might do weird, for example trying to be at two places at the same time.

parabola3.jpg

The VelocityLossRange setting hinders the particle from moving along the axis you chose. If it's larger than 100, the particles suddenly get shot away at very high speed. Again, you can use a Min and Max value if you want the VelocityLossRange to be something random between the Min and Max value.

AddVelocityFromOtherEmitter gives the particles the same Velocity as a random particle coming from another ParticleEmitter inside the same Emitter actor. In 777 however, there's a small bug with big consequences that makes the editor crash if you use this setting, so you can't use it. This setting works very similar to AddLocationFromOtherEmitter in Location.

With GetVelocityDirectionFrom, you can choose in what direction the Velocity is used. PTVD_None is the default and works as explained above. With PTDV_StartPositionAndOwner, the particles will go in the direction determinated by the start position of the particle, and the Emitter Actor, and it moves away from the Emitter. PTDV_OwnerAndStartPosition does the same, but now the particle will move from it's startposition towards the Emitter Actor and when it reached that keep going further. This only works if it's startposition is not the Emitter itself, more about this is in Location.

Texture

texture2.jpg

The first thing you probably want to change now, is the texture of the sprite. First open the Texture Browser, and select the texture you like. For a fire effect you'll need a yellow/red texture, for rain you'll need a drip-like texture, etc. Once you selected the texture, select Texture inside the Texture properties of the SpriteEmitter and press the Use button, so the name of the selected texture appears there.

texture1.jpg

Now the sprite will look like the selected texture. Using the DrawStyle property you can change the way the texture is drawn in the map. The DrawStyle for Particles is somewhat the same as the DrawStyle you find under Display of normal actors, but has some added options, and some other removed.

drawstyle.jpg

For the example screenshots, the blue texture is one without alphachannel, and the second one is a texture with different colors and an alphachannel that has 16 dark dots, like this:

alphatexture1.jpg

PTDS_Regular makes the texture opaque, so it's a non-transparent square.

regular2.jpg regular.jpg regular3.jpg

PTDS_AlphaBlend makes the darker parts of the A-channel of the RGBA texture more transparent than the bright parts. Black becomes 100% transparent (invisible) and white becomes 100% opaque. Grey becomes semi-transparent. This effect is not good visible on very bright textures.

alphablend2.jpg alphablend.jpg

PTDS_Modulated makes the texture some sort of inverse translucent: it makes the brightest colors of the texture more transparent than the darkest colors, so black becomes opaque, white becomes 100% transparent (invisible). Grey, red, blue, etc... become semi-transparent.

modulated2.jpg modulated.jpg

PTDS_Translucent makes the darkest colors of the texture more transparent than the brightest colors, so white become opaque, while black become 100% transparent (invisible). Grey, red, blue, etc... become semi-transparent.

translucent2.jpg translucent.jpg

PTDS_AlphaModulate makes the actual RGB texture brighter on places where the alphachannel is darker. It looks somewhat the opposite of AlphaBlend. This effect isn't good to see on very bright textures.

alphamodulate2.jpg alphamodulate.jpg

PTDS_Darken makes the colors of the texture negative and translucent.

darken2.jpg darken.jpg

PTDS_Brighten makes the texture to brighten the background, so the texture becomes translucent and brighter than it's original version.

brighten2.jpg brighten.jpg

As you can see, the alphachannel of an RGBA8 texture is only used for the DrawStyles AlphaBlend and AlphaModulate.

Remark: in 777, this might not work, the particles might look like PTDS_Translucent no matter what you set.

Subdivisions

You can divide one texture into different Subdivisions. You can make the particles randomly pick one of the textures, or make one particle change from one Subdivision to another during it's LifeTime. You have to make a texture that can be used for this yourself: divide into a * b equal rectangles, and give every rectangle it's own picture. For example, this is a texture that can be divided into 3 * 3 = 9 Subdivisions:

shapes.jpg

To set the Subdivisions, use TextureUSubdivisions and TextureVSubdivisions for the horizontal and vertical Subdivisions.

subdivisions.jpg

Setting these settings to 0 or 1 does exactly the same: it divides the texture into 1 Subdivision, meaning there are no Subdivisions at all (the Subdivision is the texture itself), so the particles will look like this:

subdiv1.jpg

If you set both TextureUSubdivisions and TextureVSubdivisions to 3, it looks like this:

subdiv2.jpg

If you set them to 2 or 4, some pictures of this texture will be cut into half, because it has 3 * 3 Subdivisions and isn't made for other values.

subdiv3.jpg subdiv4.jpg

If you set respectively TextureUSubdivisions=1 and TextureVSubdivisions=3 or TextureUSubdivisions=3 and TextureVSubdivisions=1, it'll look like this:

subdiv13.jpg subdiv31.jpg

If you set UseRandomSubdivision to True, the ParticleEmitter will give each particle a random Subdivision at the beginning, and the particle keeps this Subdivision as long as it lives.

subdiv5.jpg

If you set UseRandomSubdivision to False, the particles will change during their LifeTime. For example if you'd have 2 * 2 Subdivisions, then first they'll have the upper left Subdivision, then the bottom left, then the upper right and finally the bottom right. The time it takes to change from one Subdivision to another, depends on their LifeTime, more about this further in the section Time. On the screenshot, there are 9 Subdivisions.

subdiv6.jpg

If you set BlendBetweenSubdivisions to True (and UseRandomSubdivision is False), the particles will Fade from one Subdivision to another:

subdivblend.jpg

Normally, the particles go through the different Subdivisions this way, that they will have all the Subdivision one for the same amount of time. The time is divided in equal parts for each Subdivision. If you don't want this, you can use the SubDivisionScale feature. First select SubDivisionScale in the properties and press the add button that appears 3 times, so there are 3 SubDivisionScales (then 4 of the 9 Subdivisions will be used). If you want the 9 Subdivisions to be used, you have to make 8 SubDivisionScales. If you make less than 8 SubDivisionScales, the last Subdivisions will never appear. There's also nothing wrong with making too many SubDivisionScales, then the last ones will just be ignored. One warning though: the editor crashes quite a lot while making SubDivisionScales, so save your map and all changed packages before you do this (or have a programmer apply this fix to your build). Also, set UseSubDivisionScale to True, otherwise the engine will not use the SubDivisionScales.

subdivscaleadd.jpg subdivscale3.jpg

The SubDivisionScale is relative to the LifeTime of the particles (see the section Time for this), so a SubDivisionScale of 1.000000 takes as long as the LifeTime of the particles. Each SubDivisionScale represents a certain point in the LifeTime of the particle. For example if the LifeTimeRange is 4 seconds, a SubDivisionScale of 0.25 is the end of the first second of the LifeTime. A successive SubDivisionScale should always be larger than the previous one, otherwise you're going back in time and it will be ignored. For example if you make Subdivision [0] = 0.1, [1] = 0.3 and [2] = 0.6, and LifeTimeRange is 4 seconds, the particle will have Subdivision 1 for 0.4 seconds, then Subdivision 2 for 0.8 seconds, Subdivision 3 for 1.2 seconds and Subdivision 4 for the remaining 1.6 seconds. On the screenshot, the particles move from left to right.

subdivscale1.jpg

If you make SubDivisionScale [0] = 0.4 and [1] = 1, only two of the Subdivisions will be used. Then it doesn't matter anymore what value you entered in [2].

subdivscale2.jpg

To get this effect at it's best, you have to use the correct LifeTimeRange and also make sure the MaxParticles value is high enough. Read sections General and Time for this.

Use the SubdivisionEnd and SubdivisionStart values if you want only a few Subdivisions of the texture to be used. Subdivision 0 is the Subdivision in the upper left corner of the texture, Subdivision 1 the one below that, and the last one is in the lower right corner of the texture. This picture shows the numbering:

subdiv.jpg

General

Open the General Properties of the SpriteEmitter.

general.jpg

The CoordinateSystem determinates what the X, Y and Z values for the Location of the particles mean. If you set the CoordinateSystem to PTCS_Relative, the (X,Y,Z) = (0,0,0) position for the particles is the position of the Emitter actor in the editor, so the particles will get spawned there:

coorrelative.jpg

But if you set the CoordinateSystem to PTCS_Absolute, the CoordinateSystem of the world in the editor is used, and there the (0,0,0) Location is the exact center of the world (there the lines of the grid are painted a little darker than the others in the 2D views). So the particles will start in the center of the map (where the thicker gridlines in the 2D view cut each other), no matter where the Emitter actor is: note the emitter actor on the right of the screenshot:

coorabsolute.jpg

The default CoordinateSystem PTCS_Independent does the same as PTCS_Relative, but after they are spawned the particles become independent and use the absolute coordinatesystem. The difference between PTCS_Relative and PTCS_Independent becomes important when you use Moving Emitters.

All the three CoordinateSystems will rotate if you rotate the Emitter actor with the Rotation Tool. If you set in the properties of the Emitter actor in Advanced bDirectional to True, an arrow will be displayed in the editor to help you to rotate it better.

In the section Location is explained how you can make the particles spawn at another Location, again relative or absolute to the Emitter actor depending on the CoordinateSystem you use.

The MaxParticles setting sets the maximum number of particles that may be on screen for this ParticleEmitter. If the maximum number is reached, the oldest particle gets killed, so a new one can get spawned. If you set it to 0, the editor crashes. For example, if you set this number to 4, there will be 4 particles on screen once the warmuptime is over. On the screenshot, the particles are moving to the left, and the most left one gets destroyed while a new one appears on the right:

maxparticles.jpg

This setting is quite important, especially if you want to reduce the amount of particles on screen to get more performance. Another way to reduce the number of particles is by lowering their LifeTime.

If ResetAfterChange is True, the ParticleEmitter will restart from zero again after every time you changed a property, but they do that already if this property is False too, so actually this property does almost nothing.

With EffectAxis, you can rotate the CoordinateSystem that determinates the spawnlocation of the particles 90 degrees, at least if you base the Rotation on PTRS_Normal. More about this later.

Time

time.jpg

The LifeTimeRange is the number of seconds the particle will live. After this time, it gets destroyed and might respawn. If you find out that they live much shorter than you would expect, then probably the MaxParticles is set too low. Set MaxParticles high enough so there can be enough of them.

The InitialTimeRange lets the particles be several seconds old already when they get spawned. So if LifeTimeRange is 4 and InitialTimeRange is 3, the particle will already be 3 seconds old when spawned and live for only one second. This setting can be interesting when you use scales: the SubDivisionScale, ColorScale and SizeScale will also be calculated as if they're 3 seconds old already. (read more about these in the sections Subdivisions, Color and Size)

The InitialDelayRange is the time it takes before the emitter starts his job. For example, if you enter a InitialDelayRange of 5, when you start the map or the emitter gets reset, it'll do nothing for 5 seconds before it starts emitting.

For all the settings you can use a Min and Max value. If you make them the same, that exact value will be used. If you make Min smaller than Max, every particle will get it's own random LifeTimeRange or InitialTimeRange when it gets spawned, and this random value is something between Min and Max. If you use a random LifeTime, the particles will look like the screenshot at constant speed: there appear holes because some of the particles die already while others are still continuing their way.

randomlifetime.jpg

Spawning

spawning.jpg

If AutomaticInitialSpawning is True, and ParticlesPerSecond = 0, there will be spawned a certain number of particles per second, this way that there are exactly the amount of particles you entered in MaxParticles, AND they can live exactly as long as you entered in LifeTimeRange. So when LifeTimeRange is larger, there will be spawned less particles per second. When MaxParticles is larger, there will be spawned more particles per second. In many cases, this can be very handy.

If you set AutomaticInitialSpawning to False, you can enter the number of ParticlesPerSecond manually. InitialParticlesPerSecond is the number of particles per second that gets spawned during the warmup time. The warmup time ends when the MaxParticles is reached. Once this is the case, the ParticlesPerSecond value gets used. For example, if MaxParticles = 20, InitialParticlesPerSecond = 100 and ParticlesPerSecond = 5, there will first be spawned 20 particles very quickly: in 1/5th of a second (because you had set it to 100 particles per second, and only 20 have to be spawned). Then, once the 20 particles are there, only 5 particles get spawned every second, but also the dead particles get respawned, unless you set RespawnDeadParticles to False in Local. In that case, at the beginning, it'll look like the first screenshot, but once the warmup is gone, it looks like the second screen. (the particles are going from left to right).

initial1.jpg initial2.jpg

If ParticlesPerSecond = 0, all the InitialParticles are respawned again after they died if the setting RespawnDeadParticles in Local is True. If it's False, there will be no more particles spawned after all the InitialParticles are spawned (this is, until MaxParticles is reached), so the emitter won't do anything anymore.

Tick

tick.jpg

MinSquaredVelocity: the minimum velocity a particle may have before it gets inactive. This is important if you use [[#CollisioN][Collision]: the particles will then get less velocity every time they bounce on the wall.

SecondsBeforeInactive: when this is 0.000000, the movements of the particles are always calculated by the computer, even if they're out of view. If you enter a value in here, the computer will stop calculating the movements of the particles after the Emitter actor is out of view for this many seconds. This way you gain performance. So when you look away from the Emitter, the particles will freeze, and only come back to life when you look at them again. So when SecondsBeforeInactive = 0.010000 and you see this:

initial1.jpg

and then rotate the camera 180 degree, wait an hour and then look again at the Emitter, you'll still see exactly the same image (where the warmup is not finished yet). When you can't see the Emitter actor, but you should be able to see some particles, you'll not see these particles! Only when you can see the Emitter itself, you'll be able to see his particles. If you don't want this, set SecondsBeforeInactive to 0.000000.

Warmup

With these settings, you can get rid of the warmup time!

warmup.jpg

You can make the engine to precalculate the Emitter already, so when you start the map it's as if the ParticleEmitter has been there already for a few seconds and you'll not see the warmup effect of it. The warmup is the time where not yet all the particles are spawned. With the setting RelativeWarmupTime you can set how many seconds have to be precalculated, relative to the LifeTime of the particles. This means if you set it to 1, there are exactly as much precalculated seconds as the first particles will live. But before this works you also have to set the WarmupTicksPerSecond: this is how many ticks each second of the RelativeWarmupTime has. The higher the WarmupTicksPerSecond, the more there will be precalculated.

So basicly, the longer the warmuptime of a certain ParticleEmitter takes, the higher you have to make the WarmupTicksPerSecond setting (and set RelativeWarmupTime to 1) if you want there to be no visible warmup.

Object

object.jpg

This are the same Object settings as any normal actor has (the Emitter itself has it too), but this time it's for the ParticleEmitter. The Group settings are missing here, and you can't change any of the values, you can just check the Class and Name of the ParticleEmitter here.

Local

local.jpg

AutoDestroy and AutoReset should work here, but they work much better in Global instead.

Disabled: if this is True, the ParticleEmitter will not work. You can for example use it if you want to remember the settings of a ParticleEmitter, but you currently don't want it to work in the map.

DisableFogging: if True, the fogging on the particles will be disabled, so you can see them through the DistanceFog.

Name: you can enter a name here if you want to remember what ParticleEmitter this was again

RespawnDeadParticles: If RespawnDeadParticles is True, the InitialParticles (read Spawning for more details) get respawned over and over again, so there will be spawned more particles than you filled in ParticlesPerSecond. But if it is False, the InitialParticles will get spawned only once, and after that the ParticlesPerSecond is used. If ParticlesPerSecond is 0 then, there will be no more particles spawned after all the InitialParticles are spawned (this is, until MaxParticles is reached), so the emitter won't do anything anymore. It then can stay the way it is, or get Reset or Destroyed automatically if these settings are enabled in Global.

Global

This setting is NOT a setting for the ParticleEmitter, but for the Emitter actor itself and thus all the ParticleEmitters that are in it. This setting is directly in the Emitter Properties window.

global.jpg

AutoDestroy: if this is True, the Emitter actor will be destroyed when all particles are dead. (this can happen if ParticlesPerSecond = 0 and RespawnDeadParticles is False). If you try this in the editor, the Emitter will really be destroyed so you LOSE it! In other words, don't test it in the editor.

AutoReset: if this is True the Emitter actor will be reset when all particles are dead, so it restarts from the beginning (the InitialParticles are spawned again).

DisableFogging: if True, the fogging on the particles will be disabled, so you can see them through the DistanceFog.

GlobalOffsetRange: this does the same as the OffsetRange in the Location settings, but the GlobalOffsetRange does this for all the ParticleEmitters inside the emitter actor. This way you can get a random StartLocation that is the same for all the ParticlesEmitters?. Note that this GlobalOffsetRange is determinated only when you start the map, or when the emitter is reset. So RespawnDeadParticles in Local must be False for all ParticleEmitters, and AutoReset in Global has to be True to make this work at best.

TimeTillResetRange: this is the number of seconds the emitter waits before it AutoResets when all the particles are dead.

Sprite

This is the only set of properties that is unique for the SpriteEmitter.

sprite.jpg

Here you can set how the 2D sprite image will be displayed: with UseDirectionAs you can get the sprite to use an axis for displaying it.

PTDU_None: with this, the sprite will always look the same, no matter from what direction you look at it. It always faces you. It's like the particles can rotate to any direction, and always rotate towards you with one side. The following 3 screenshots show how it looks in different situations.

none1.jpg none2.jpg none3.jpg

PTDU_Up: with this, the sprites also rotate towards you, but they can use only one axis (with PTDU_None, they can rotate along all axes). The direction of this axis is determinated by the velocity the particle has at the moment you look at it. So in a curved Beam, the sprites will look like as if they rotate.

up1.jpg up2.jpg up3.jpg

PTDU_Right: this does the same as PTDU_Up, but the texture is rotated 90 degrees.

right1.jpg right2.jpg right3.jpg

PTDU_Forward: now the sprites will always face towards the direction they move to.

forward1.jpg forward2.jpg forward3.jpg

PTDU_Normal makes the sprites to be flat planes, they don't rotate.

normal1.jpg normal2.jpg normal3.jpg

You can set the direction of the planes yourself with ProjectionNormal. With X, Y and Z you can set the direction of the normal of the plane. So if you enter X = 0, Y = 0 and Z = 1, the normal is the Z-axis (the axis vertical on the floor), and the plane will be horizontal, like the floor or the ceiling.

plane.jpg

If you enter for example X = 2, Y = 1, Z = 0, the normal will be the yellow line (horizontal with the floor), and the planes will be like the black line. The second picture shows how the planes have that direction indeed, if you have your camera oriented the same way as the top view.

plane2.jpg plane3.jpg

If you enter (1,2,0) or you enter (2,4,0) or (5,10,0), it all does the same, because the ratio of the numbers is the same. Never enter (0,0,0), because then the normal and thus the plane can't be defined.

PTDU_UpAndNormal does the same as PTDU_Normal, BUT the planes can be rotated only around the axis determinated by the AbsVelocity of the particle. This means one or two of the X, Y and Z settings is not used for the Rotation, but instead to stretch the sprite. What axis this is, depends on the current direction of the Velocity of the particle. On the screens, X = 0, Y = 0 and Z = 1.

uan1.jpg uan2.jpg uan3.jpg

PTDU_RightAndNormal does the same as PTDU_UpandNormal, but the texture is rotated 90 degrees.

ran1.jpg ran2.jpg ran3.jpg

If you find only PTDU_None works and all the other settings make the particles invisible, check in Size --> StartSizeRange if the X and Y values both arel larger than 0.

Location

location.jpg

With these settings you can make the particles to start somewhere else than at the Emitter itself. The easiest setting is StartLocationOffset: here you can set exactly where in the coordinatesystem the particles have to start. If you leave it at X=Y=Z=0, the particles will always start in the origin of the coordinatesystem, for PTCS_Independent or PTCS_Relative this is the Emitter itself, and with PTCS_Absolute this is the (0,0,0) position in the grid from the editor. For example if you set X=100, Y=100 and Z=100, the particles will start 100 units to the east, 100 units to the south and 100 units above the Emitter actor

location1.jpg

To give the particles a random StartLocation, you can use StartLocationRange or SphereRadiusRange. First you have to choose if you want this random StartLocation to be inside a sphere or a box: set StartLocationShape to PTLS_Sphere for a sphere, or PTLS_Box for a box.

If you use PTLS_Sphere, you have to set a minimum and maximum radius in SphereRadiusRange. The particles will then start somewhere between these 2 radii.

sphere.jpg

If you use PTLS_Box, you can set 6 values to determinate the 6 sides of the box: X(Min), X(Max), Y(Min), Y(Max), Z(Min) and Z(Max). For example if you set X, Y and Z(Min) to -150 and X, Y and Z(Max) to +150, all the particles will start inside a 300*300*300 box.

box.jpg

Note that PTLS_Box and PTLS_Sphere work in combination with LocationOffset, so the final StartLocation is the sum of those. More about the shapes and effects you can tweak with these settings is in the section Shapes.

In AddLocationFromOtherEmitter, you can enter the number of another ParticleEmitter that is inside the same Emitter actor. If you leave it at -1, nothing will happen, but if you set it for example to 1, the particles will not be spawned where they normally should spawn, but at the Location of one of the particles of ParticleEmitter[1]. They will not keep following that particle, they only get spawned there and get independent after that. You can for example make a trace of particles behind another moving particle. This is done in the DEMO-Particles example map:

locationfromotheremitter.jpg

The blue sprites never move, but when they die they respawn at one of the bouncing balls, so together all the blue sprites form the shape of the traces the balls follow.

Collision

collision.jpg

The Collision can make the particles realistically bounce on walls, floors or ceilings. It looks for example like the animated gif:

collision.gif

If you have no Collision, the particles will just disappear in the wall:

collision0.jpg

But if you set UseCollision to True, the particles will bounce on the wall. If they hit the wall perpendicularly, they will bounce back to from where they came as on the first screenshot, in any other case they'll bounce like on the second screenshot. Note that collision doens't work if the coordinatesystem is PTCS_Relative.

collision1.jpg collision2.jpg

The DampingFactor changes the velocity of the Particle every time it bounces on the wall. The Velocity gets multiplied by this factor. For example in the real world if you let a tennis-ball fall on the ground, it bounces less high every time it hits the ground again. On the following screenshots, there is an acceleration Z = -950, and a velocity of X = 500. The Particles are falling towards the ground, and then bounce. On every screenshot, the Z value of the DampingFactor is modified to show the difference. It's the Z value that's used, because in this case the particles are bouncing on the floor. If the DampingFactor is 1 they can keep and keep bouncing as long as they live, as shown on the first screenshot. If you make it lower than 1, for example 0.9, they'll bounce less high every time they hit the ground (this is most realistic).

collision3.jpg collision4.jpg

If you make the DampingFactor higher than 1, for example 1.5, the particles will bounce higher and higher (1st screenshot). If you give a different Min and Max value, every particle will bounce randomly something between the Min and Max value (2nd screenshot).

collision5.jpg collision6.jpg

The ExtendMultiplier is multiplied by the Size of the particle, and this is used for the collision detection. This multiplied Size determinates where the colliding sides of the particles are, and as soon as that (invisible) side reached the surface, the particle will bounce.

If you set UseMaxCollisions to True, the particle will die when it bounced the (random) number of times you enter in MaxCollosions, no matter what LifeTime it has.

If you set UseCollisionPlanes to True, you can make invisible planes, where the particles will bounce on. The planed don't collide anything else, only the particles of that particular emitter. These planes are 1-sided, the particle can go through it at one side but will bounce against it if it goes the other direction. You have to make the planes in the CollisionPlanes setting. For each plane, you can enter W, X, Y and Z. W is the distance from the plane to the center of the editor grid, and X, Y and Z determinate the direction of the normal of that plane. You can also enter negative values, for example to get on the other side of the grid or to turn around the 1-sided direction. On the screenshots, the red dot represents the center of the editor grid, the grid itself is 256 units, and the red line shows the (invisible) plane. The small red triangles point into the direction the particles can go through the plane. On the first screenshot, W=256, X=0, Y=1 and Z=0. The particles can go to the south until they bounce against the wall, and when going north again, they'll bounce against the plane. On the second screenshot, W is changed to -256 and Y to -1, so the direction turns around, and the particles will bounce against the other side of the plane instead.

plane256_0_1_0.jpg plane256_0_-1_0.jpg

With SpawnFromOtherEmitter, you can make something to get spawned at the Location where the particle bounces, for example for some smoke in the sand, or rippels in shallow water. For this to work, you have to make a second emitter inside of the same emitter actor, and in SpawnFromOtherEmitter enter the number of that new emitter, for example 1 if it's called SpriteEmitter [1]. The particles of this new emitter are the ones that will be spawned when the bouncing particle bounces. For the new emitter, set RespawnDeadParticles in Local and AutomaticInitialSpawning in Spawning to False. The reason for this is, otherwise these particles may also get spawned at the wrong moment and the wrong place.

Then, in SpawnAmountRange, you have to enter the number of particles you want to get spawned when the bouncing particle hits the surface.

If you set UseSpawnedVelocityScale to True, you can also give the particles that get spawned when it bounces a velocity. You can set this Velocity in the SpawnedVelocityScaleRange settings: you can give Minimum and Maximup X, Y and Z settings. If the particle bounces, the spawned particles only get the velocity in the normal direction on the surface.

Color

color.jpg

This is another scale that can be applied to the particles, it works somewhat the same way as the SubDivisionScale in Texture. A scale changes something from the particle during it's LifeTime, in this case the color. To use a ColorScale, first set UseColorScale to True, and then add a new ColorScale: click on ColorScale, press the Add button and a new Color and RelativeTime will appear. To choose a color, use the "..." button to get the Windows Color Picker, use the "Pick" button to get a dropper tool, or enter the RGB value manually.

color2.jpg

color3.jpg

First choose a color, for example red. Then you need to enter a RelativeTime. This time depends on the LifeTime of the particles (you can set this in Time), and on the ColorScaleRepetitions value. The RelativeTime does NOT depend on the MaxParticles, so if you have too few MaxParticles, it might be the LifeTime is too long. Then you'll have to make more MaxParticles, or reduce the LifeTime. If ColorScaleRepeats is at it's default value of 0, and RelativeTime = 1, the particles will be their own texture color (in this case blue) at the start of their Life, and will have finished fading to red at the end of their life.

color4.jpg

If RelativeTime = 0.5, they will have Faded to red in the half of their LifeTime, and then get their original color back (first screen). If RelativeTime = 1.5, the particles will never get read (they die already at "1"), but you will see the fading process already (second screen)

color5.jpg color7.jpg

With ColorScaleRepeats, the whole fading process will be repeated the number of times you enter, so if you enter 1 there will be 2 ColorScales. Then, the RelativeTime is also reduced so if RelativeTime is 1 and ColorScaleRepeats is 1, the particles will have become red in the half of their LifeTime AND at the end of their LifeTime, as shown on the screenshot.

color6.jpg

You can also set more than one Color by adding more ColorScales. Then you can set as many Colors and RelativeTimes as there are ColorScales.

color8.jpg

color9.jpg

If you want the particle to be red at the beginning of it's life already, set the RelativeTime of the red color to 0.

color10.jpg

Remark: the color the particle will get depends on the Color you entered in ColorScale, but also on the color of the texture from the particle itself. On a black particle, the ColorScale will not be visible at all, on a white texture it works at it's best. In the examples above, the texture was blue.

Fading

fading.jpg

With this, you can make the particles Fade in or Fade out, for example when they Fade in they are invisible at the beginning and become more and more visible. You can also make only a few of the colorchannels to Fade, so for example the red and green parts of the texture will be invisible while the blue parts are always visible.

To enable the FadeIn, set FadeIn to True, and enter a FadeInEndTime. This time is NOT relative, but the actual number of seconds it takes for the particle to Fade in, starting from the moment it was spawned. On the screenshot, the particles have a LifeTime of 5 and a FadeInEndTime of 5, so they are 100% visible at the moment they die:

fadein.jpg

FadeOut works exactly the same, only now you have to enable FadeOut and to enter the FadeOutStartTime, the time they start fading out. If this is 0 they will already start fading when they're born. They're completely Faded out at the moment they die. On the screenshot, FadeOutStartTime is 1.

fadeout.jpg

If you use a FadeIn AND a FadeOut, the effect will look like the first screen if FadeInEndTime is smaller than FadeOutStartTime (here 1 and 4), and it'll look like the second screen if FadeInEndTime is larger than FadeOutStartTime (here 4 and 1)

fadeinout1.jpg fadeinout2.jpg

You can use FadeInFactor and FadeOutFactor to Fade individual color channels. X = Red, Y = Green and Z = Blue. The W represents the alphachannel, if any. If the values are 1, they Fade normally. If you make for example X = 0, the red channel will get changed 0%, so while the other colors are invisible (this is at the start of a FadeIn, or the end of a FadeOut), the red will still be visible (left part of the first screen). Or when Y = 0, the green will still be visible (right part of the first screen). If X and Y are 0, both will be visible, it looks somewhat yellowish here (left part of the second screen). If X, Y and Z are 5, the whole texture will get super-invisible so it Fades out much faster (right part of the second screen). If X, Y and Z would all be 0, the texture wouldn't get invisible at all.

fade3.jpg fade4.jpg

Rotation

rotation.jpg

With Rotation, you can make the particles Spin, and you can also rotate the coordinatesystem.

To make the particles Spin while they move, set SpinParticles to True and enter an X(Min) and X(Max) value at SpinsPerSecondRange. For Sprites, only the X value is useful, the Y and Z are for MeshEmitters. The value you enter is the number of times per second the texture of the particle will Spin. If X(Min) and X(Max) are different, each particle will get a random value between Min and Max. You can also set if the particles have to Spin CounterClockWise or ClockWise in SpinCCWorCW. Again you can do this for X, Y and Z but again only the X is useful now. If you set the X of SpinCCWorCW to 0, all the particles will rotate CounterClockWise. If you set it to 1, all the particles will rotate ClockWise. If you set it to something between 0 and 1, for example 0.7, there's 70% chance a particle will rotate ClockWise and 30% chance it'll rotate CounterClockWise. If SpinsPerSecondRange is 0.5 and SpinCCWorCW is 1, it'll look somewhat like the first animated gif. If you set SpinCCWorCW to 0.5, it'll do for example like on the second animated gif (on the second screenshot, some particles may suddenly switch fro CCW to CW, but that's because otherwise the gif would require TOO much frames. This never happens in the editor.)

spin1.gif spin2.gif

With RotationDampingFactor, you can set the damping factor for the Spinning particles, this works the same way as the DampingFactor of Collision, only it makes the particles Spin slower or faster every time they bounce. For example if you make the Z(Min) and Z(Max) values of the RotationDampingFactor 0, the Spin will stop after the first bounce on the floor already (because the SpinsPerSecondRange gets multiplied by 0). Note on the screenshot all the textures to have the same Rotation after the bounce:

spin3.jpg

With StartSpinRange, you can set the Rotation the particle has when it's spawned. For SpriteEmitters, only X is important. A value of 1 rotates the texture 360 degrees (so 1 and 0 do the same), and 0.5 means 180 degrees. You can also make the particle to have a random StartSpinRange, if you enter a different X(Min) and X(Max) value.

You can also rotate the coordinatesystem for the particles. This affects the Location and Velocity, but not the Acceleration. With UseRotationFrom, you can set how to rotate it. If it's PTRS_None, the coordinatesystem is not rotated, for example the X-Axis has the same direction as the main X-Axis in the editor.

If it's PTRS_Actor, the Rotation of the Emitter Actor is used. If you set in Advanced bDirectional=True, the Emitter gets an arrow. The direction of that arrow is the X-Axis that now will be used for Velocity and Location.

If it's PTRS_Offset, the settings in RotationOffset are used. There you can set how much the coordinatesystem has to Pitch, Yaw and Roll. A value of 16384 means 90 degree.

If it's PTRS_Normal, the settings in RotationNormal are used for the Rotation. Now, you can enter the direction of a plane, and the X-Axis will be perpendicular on that plane. If you use PTRS_Normal, you can also make the coordinatesystem for the StartLocation rotate 90� along the Pitch Rotation. The Velocity will remain the same, only the StartLocation you set in Location will be rotated, and you can only see this if the StartLocation is not the Emitter itself. To do this, set in General the EffectAxis to PTEA_PositiveZ.

For example on the first screenshot there is no RotationOffset used, and the particles are moving with a Velocity of X = 500. On the second screenshot, they still have the same Velocity, but UseRotationFrom is set to PTRS_Offset and the Pitch of RotationOffset is set to 16384.

rotation1.jpg rotation2.jpg

Size

size.jpg

Here you can determinate the Size of the particles, and also set a SizeScale that works the same way as the ColorScale. The StartSizeRange determinates the Size the particles will get when spawned. For Sprites, only the X values are useful if in Sprite UseDirectionAs = PTDU_None, and for the other UseDirectionAs settings, the X and Y can be used to change the width and height of the texture separately. The default value is 100, the screenshots show X(Min) = X(Max) = 50, and X(Min) = X(Max) = 150.

size1.jpg size2.jpg

Again, if you enter a different X(Min) and X(Max) value, the particles will get a random Size.

size3.jpg

The SizeScale works exactly the same as the ColorScale, only now you're working with a Size instead of a color. If you set UseSizeScale to True AND UseRegularSizeScale to True, the Particles will always shrink, and you don't have to add any SizeScales.

size4.jpg

If you want to use the SizeScales, you have to set UseRegularSizeScale to False, and add new SizeScales the same way you did with the ColorScale. As for the color, you can use a RelativeTime and a SizeScaleRepeats value. The RelativeSize comes in place of the RGB settings for the color, and it multiplies the StartScaleRange? values. The screenshot shows an example.

size5.jpg

If UseDirectionAs is for example PTDU_Normal, you need X and Y to scale the U and V direction of the texture. If you set UniformSize to True however, only the X is used for both U and V. You may need this if you use a random Size and you ant the proportions to be remained.

Force

force.jpg

With this, it's possible to make the particles get affected by other objects that are flying around. For example to shoot a projectile through smoke and have the smoke smoothly and realistically dissipate. To make this work, you have to set UseActorForces to True, and the projectile or whatever other object going through it must have Forces enabled. I tried this with a Flappy (an animal) running through some particles. So in the properties of the Flappy, the projectile or whatever else go to Force, and set ForceType to FT_DragAlong. Also set a ForceScale (how big the Force is) and a ForceRadius (the radius of the Force).

flappyforce.jpg

If now the Flappy runs through the particles (because it fears my grenade launcher), some of them will fly away. Of course you can make this effect looking much better with nice smoke and projectiles.

flappy1.jpg flappy2.jpg

Mass

This setting does nothing at the moment.

MeshEmitter

Until now, the tutorial was about the SpriteEmitter, the ParticleEmitter that spawns 2-dimensional things. The MeshEmitter spawns 3-dimensional things, Static Meshes even. To make a MeshEmitter do the same as for the SpriteEmitter, but instead choose MeshEmitter. When you open it's properties, there's one new property: Mesh. This one comes instead of the property Sprite for the SpriteEmitter. Almost all the other properties (for example the Acceleration, Velocity, Time, Collision, etc...) work the same as for the SpriteEmitter. Only the ones that are different or new are explained for the MeshEmitter here.

meshemitterprops.jpg

Mesh

mesh.jpg

Here you can choose the StaticMesh that gets used for the particles, as long as you leave this empty the particles are invisible. To choose a StaticMesh first select it in the Static Mesh Browser, then click on StaticMesh in the Mesh properties and press the Use button.

staticmesh1.jpg

Now you won't see anything yet, not even if you give some Velocity, because the particles are displayed WAY too big: the StartSizeRange settings in Size are 100 by default, and for the MeshEmitter this means the StaticMesh is 100 times as big as it should be. Set all the X(Min), X(Max), Y(Min), Y(Max), Z(Min) and Z(Max) settings to something very low, for example 0.5, or for the huge Static Mesh that's used here: 0.03. More about the Size for MeshEmitters is in the section Size][. If you now give also some velocity you might see something like the screenshot.

meshparticles.gif

UseMeshBlendMode: if this is True, the Meshes will be rendered the way they are: if they have translucent textures, they're translucent, if they have opaque texture, they're opaque, etc.... If it's False, the DrawStyle in the Texture setting is used. More about this setting and the DrawStyles for Static Meshes is in the section Texture][.

RenderTwoSided: whether the textures of the StaticMesh should be rendered TwoSided or not. The result of this is only visible if UseMeshBlendMode is False: the Static Mesh will look brighter if it's True.

Size][

size.jpg

The Size settings for the MeshEmitter are exactly the same as the Size setting of the ParticleEmitter (explained in the section Size), only now you can change the X, Y and Z Size of the StaticMesh particles independently. X and Y resize the width and breath of the particles, Z resizes the height of it. Again, you can set a different Min and Max value for a random Size. On the first screenshot, the X Size is larger than Y and Z. On the second screen, the Y Size is larger than X and Z.

size21.jpg size22.jpg

You can give the particles a random X, Y and Z Size. On the first screenshot, X and Y are the same, but Z(Min) = 0.03 and Z(Max) = 0.3 so the height is random. Where the resize happens (what part gets stretched) depends on the StaticMesh, here the bottom of the StaticMeshes is stretched but the top keeps the same height. On the second screenshot, all the X, Y and Z values have a Min of 0.03 and a Max of 0.2 so they're all random. If UniformSize = False, the proportions of the particles will not be kept, but if you set UniformSize to True, the proportions are kept and only the total Size is random. Only the X values are used then, Y and Z are ignored.

size23.jpg size24.jpg

The SizeScale works exactly the same as the one for the SpriteEmitter and is explained in the section Size. This time, X, Y and Z are locked to each other: the RelativeSize value resizes them all at the same time.

sizescale2.jpg

Texture][

texture2.jpg

If in Mesh you set UseMeshBlendMode to False, the DrawStyle in Texture will be used. This does the same on the mesh as it does on sprites, for more go to the section Texture. If in Mesh RenderTwoSided is True as well, the effect will look brighter. The first screenshot shows the DrawStyle PTDS_Brighten, the second screen shows the same DrawStyle but this time with RenderTwoSided to True.

brighten21.jpg brighten22.jpg

If the StaticMesh is opaque and UseMeshBlendMode is True, the StaticMeshes will of course look opaque (first screenshot). If you now set UseMeshBlendMode to False, and set DrawStyle to PTDS_Regular, they will be opaque as well, but something weird will be going on: you can see the other particles through the opaque walls of the particles (second screenshot).

seethrough1.jpg seethrough2.jpg

The Subdivision settings are useless on MeshParticles?, because they use a StaticMesh and not a texture.

Rotation][

rotation.jpg

The Rotation for MeshEmitters works the same as for SpriteEmitters (explained in the section Rotation), only you can now set 3 independent Spins in X, Y and Z direction: each have their own SpinsPerSecondRange and SpinCCWorCW.

These screenshots show respectively Rotation in X, Y and Z direction. (the last two are animated)

rotationX.jpg rotationY.gif rotationZ.gif

Other Properties

All the other properties work exactly the same as for the SpriteEmitter, for example on the first screenshot there is used a ColorScale, Acceleration, Collision, and a random StartLocation. On the second screenshot is used a FadeIn, some Acceleration and also another StaticMesh than on all the previous screenshots.

meshparticle1.jpg meshparticle2.jpg

BeamEmitter

This Emitter is very useful to create lightnings and such. It stretches the chosen Texture into a Beam with HighFrequencyPoints and LowFrequencyPoints?. There must be at least two of each to make the lightning work. Each particle will be such a Beam, and one Beam never moves. The way to get the lightning moving is to create a new Beam all the time while the old one gets destroyed, or to make the lightning flash with a ColorScale. Normally, the Beam is only one angular line, but you can also give it branches.

First of all: if you leave all the settings at default, and add some Velocity to the BeamEmitter, you'll already see something. The length of it depends on the Velocity you gave it.

beam1.jpg

You may recognize the 5 colored dot texture in it. If you want, you can change it in something better in Texture --> Texture. More about textures for lightnings is in the section Texture]|[. In the further examples here is used a square, white texture.

Beam

In the properties of the BeamEmitter, go to Beam.

beam.jpg

The most important setting here is DeterminateEndPointBy:

determinateendpoint.jpg

This property sets by what the EndPoint of the Beam will be determinated. The EndPoint of the Beam is where it ends, and it's StartPoint will be the Location of the Emitter actor, or, if you modified the StartLocationRange in Location, that will be the StartPoint. There's also more about StartPoints for lightnings in the section Location]|[.

With the default setting PTEP_Velocity, the EndPoint is determinated by the Velocity of the particle (that is a combination of StartVelocityRange in Velocity, and Acceleration). The length of the Beam is then determinated by the StartVelocityRange values and the LifeTime of the particles. The direction of it is determinated by the X, Y and Z values of the StartVelocityRange. You can again use random values, so for example if X(Max) = Y(Max) = Z(Max) = +1000 and X(Min) = Y(Min) = Z(Min) = -1000, each Beam will be anything random between this. It'll do almost exactly the same as on this animated gif below. You'll be able to see more than one Beam now, because MaxParticles in General is set to 10 by default. Also, lightnings normally only go straight towards the ground, so only using negative Z values for StartVelocityRange is useful.

lightning1.gif

PTEP_Distance does the same as PTEP_Velocity, but now only the direction of the Beams is determinated by the StartVelocityRange. You can set the length with BeamDistance, in editor units.

PTEP_Offset allows you to determinate the EndPoint in relative coordinates. You have to enter these coordinates in Offset, inside BeamEndPoints[0]. To get this, first click on BeamEndPoints and press the Add button. Then there should appear a [0]. If you expand it, you can see the properties inside [0].

beam3.jpg

beam4.jpg

In there, in Offset, enter the X, Y and Z coordinates for the EndPoint of the Beam. The coordinates are relative to the StartPoint (most of the times this is the Emitter actor), so for example X = 0, Y = 0, Z = -1000 means the EndPoint will be 1000 units below the StartPoint. Before it works, you also have to set Weight to something larger than 0. You can again use different Min and Max values if you want a random Location for the EndPoint. You can add more BeamEndPoints, for example [1] and [2]. Then the Beam will use one of these BeamEndPoints by random. With Weight, you can set the importance of each BeamEndPoint, this is the probability that it'll be used. If you give all the BeamEndPoints a Weight of 1, the chances will be equally divided. If for the Offset X = Y = Z = 0, the EndPoint and the StartPoint the same, so there won't be a Beam. You can use this if you want the lightning to strike only a few times and to be invisible for the rest of the time. Then you can give the BeamEndPoint with this 0-Offset a high Weight, so the lightning is most of the times invisible.

PTEP_Actor: With this, you can make the lightning go to a certain actor. This also uses BeamEndPoints[0], but this time, you have to fill in the ActorTag? property (and not the Offset property). The lightning will go towards the actor you gave that Tag (in the Event properties of that actor). If you want the lightning to go randomly towards different actors, you have to add more BeamEndPoints, for example [1], [2], etc..., and in each BeamEndPoint enter a different ActorTag?. Of course also give the actors these Tags. If you'd give three trees the SAME tag and use only BeamEndPoints[0], it won't work, the lightning will go to only one of the trees then. Again you have to give each of the BeamEndPoints a Weight higher than 0, that is the chance this BeamEndPoint will be used.

For example if there are three trees, with Tags FullTree, FullTree2 and FullTree3, and you want the lightning to strike each of the trees randomly, but to strike FullTree3 twice as much as the other trees, give the following properties to the BeamEmitter:

beam2.jpg lightning2.gif

If the actor is moving, the Beam will follow it:

lightning4.jpg

PTEP_TraceOffset does the same as PTEP_Offset, but if there is a solid surface in the way the lightning will hit against it, instead of going to the actual EndPoint. Oh, and PTEP_TraceOffset doesn't work good in combination with the coordinatesystem PTCS_Relative.

RotatingSheets: this option determinates how many sheets the Beam has. 0 and 1 mean the same: only one sheet. The screenshots show 0, 3 and 10.

sheet0.jpg sheet3.jpg sheet10.jpg

BeamTextureUScale and BeamTextureVScale: these are covered in the section Texture]|[

BeamNoise

beamnoise.jpg

Here you can give the Beam Noise. You can give HighFrequencyPoints and LowFrequencyPoints. Both do exactly the same, but you can use them independent for example to give the lightning a lot of small bends inside a few large bends. There must be at least 2 HighFrequencyPoints (and 2 LowFrequencyPoints), these represent the StartPoint and EndPoint. If you set it to 0, the editor crashes. If you set it to 1, there will be no Beam. If it's 2, the Beam will always be straight. If HighFrequencyPoints is 3 (and LowFrequencyPoints is 2), there will be 1 bend in the half of the Beam. The higher the value, the more bends. You can determinate the Size of the bend with HighFrequencyNoiseRange, and you can enter Min and Max values for X, Y and Z there. This way you can set the direction of the bend, the Size of it, and you can make it to be random. Actually, you have to make it random, otherwise it'll not work the way you want: the Min value must be smaller than the Max value, otherwise there'll always be only one bend. For example if HighFrequencyNoiseRange has Y(Max) = 1000 and Y(Min) = -1000 and there are 4 HighFrequencyPoints, it'll look like the screenshot. 1 particle will not change during it's LifeTime, so to get the lightning moving make sure the LifeTime is short enough or new particles get spawned fast enough.

lightning5.gif

You can also make a HFScale, this is similar to the ColorScale, SizeScale, etc..., only the HFScale determinates the HighFrequencyNoiseRange on the different places of the Beam. You can see the effect of the HFScale best on Beams with a lot of HighFrequencyPoints, so set it to for example 100. Without the HFScale, the whole Beam has the same NoiseRange? everywhere:

noise.jpg

To enable the HFScale, first set HighFrequencyScale to True, and then add HFScales in HFScaleFactors, the same way as you added ColorScales in the section Color: click on HFScales and press the Add button until you have enough HFScaleFactors. In there, FrequencyScale multiplies the HighFrequencyNoiseRange (X, Y and Z separately), and RelativeLength determinates the Location of the Beam where you want this multiplier to happen. 1 represents the length of the whole Beam. If you set for HFScaleFactor[0] the RelativeLength to 0.7, the first 70% of the Beam will have the FrequencyScale of that HFScaleFactor, and if you then set the RelativeLentgh of HFScaleFactor[1] to 1, the remaining 30% will get multiplied by the FrequencyScale of HFScaleFactor[1]. On the screenshot the FrequencyScale of [0] is 1 for X, Y and Z, and the FrequencyScale of [1] is 10 for X, Y and Z.

freqscale.jpg

You can use HFScaleRepeats to repeat the HFScale 1 on more times, just the same way as for the ColorScale and the other scales, for example it looks like this if HFScaleRepeats = 1.

freqscale2.jpg

The LowFrequencyPoints and all other LF settings do exactly the same as the HighFrequencyPoints. LF and HF work independently from each other, and can be used "through" each other. For example, you can use a lot of small HighFrequencyPoints, and a few, very large, LowFrequencyPoints. Then the Beam will have a few large bends, and a lot of small bends at the same time, as shown on the screenshot:

lightning6.jpg

Location]|[

location7.jpg

For lightnings, it's very interesting if you can make it start at random Locations, because lightnings never start at the same Location in real life. With StartLocationRange in the Location settings (more about this in the section Location), you can make random StartPoints for the lightning by entering a different value in Min and Max for the X and Y (the Z is not interesting for lightnings, because most of the times lightnings start at the same height).

lightning7.gif

If you want to add a lightingeffect to the lightning, you can better use the GlobalOffsetRange in Global instead, then the emitter that will create the lighting effect and the lightnings can be spawned at the same Location easily.

%IMAGE{src="http://udn.epicgames.com/pub/Content/EmittersTutorial/GlobalOffsetRange.jpg"}%

Texture]|[

To make the lightning look much better, use a better texture for it. The texture for the lightning should be rotated 90�, like this:

lightning8.jpg

and then it'll be displayed the correct way on the Beam. Set the texture in the Texture properties, the same way you do it for a SpriteEmitter.

lightning9.jpg

With the X values of the StartSizeRange in Size, you can make the lightning more thin or more fat. This doesn't change the length of the lightning, only the width of it's texture.

By default, the texture is repeated only once for the Beam. In Beam, with BeamTextureUScale you can set how many times to repeat the texture in the length of the Beam, and with BeamTextureVScale how many times to repeat it in the width: it's repeated 4 times on the screenshots.

lightning12.jpg lightning13.jpg

Color]|[

The best way to make the lightning to flash realistically is to use a color scale. This ColorScale will let the whole Beam change it's color during it's LifeTime. How to make the ColorScale is explained in the section Color. On the animated screenshot, there are 2 ColorScales: orange and yellow and the LifeTime of the particles is 0.6 seconds.

lightning14.gif

You can also use FadeIn and FadeOut to make a flashing effect.

BeamBranching

beambranching.jpg

To make the good looking lightnings, you can give them branches. If this is used correctly, you can get a very cool and realistic lightning. It works like this: you have to create more than one BeamEmitter (inside the same Emitter). Then you can say the first BeamEmitter to use the second BeamEmitter for the branches. Then again, you can tell the second BeamEmitter to use a third one for sub-branches, and so on. The MaxParticles setting is also the maximum number of branches.

First add multiple BeamEmitters:

branchemitters.jpg

Give each Emitter the properties you want. Make the first one (number [0]) big, and set it's MaxParticles to 1. Make the second and third one smaller, they're for the branches. Give them much more MaxParticles, for example 10 or 25. Also, in Local, set RespawnDeadParticles to False for all branches, and set AutomaticInitialSpawning in Spawning to False, otherwise the branches might get spawned at the StartPoint of the emitter itself instead of the new StartPoint they get when they become branch. Also, make sure the BeamEmitters that will get branches, have a lot of BeamNoise. The branches will appear in the bends only, so if there aren't enough HF and LF points, there isn't place enough for all the branches! You can of course make the noise very subtle, so you won't notice it, only the number of HF points is important, the more the better.

Then go to the BeamBranching properties of the first BeamEmitter. Set UseBranching to True, and set BranchEmitter to 1. Now, BeamEmitter number [1] will be used for the branches.

With BranchProbability, you can set the chance of branches to appear. If you set both Min and Max to 1, all the branches will be there (this is, the number of branches that you set in MaxParticles). If you enter a very high number, the branches may appear only at the top of the main Beam. If Min=0 and Max=1, the branches will be divided over the whole Beam. You have to experiment a little with this setting to find the best result.

In BranchSpawnAmountRange, both Min and Max have to be larger than 1, as soon as you make one of them smaller than 1, there won't appear any branches at all.

If LinkupLifetime is False, the branches may stay behind while the large Beam is somewhere else already. This happens when the branch has a bigger LifeTime. If you set LinkupLifetime to True, this problem is gone because the branches will live as long as the main Beam, no matter what LifeTime they have. Only make sure RespawnDeadParticles is False for the branches then, otherwise they get spawned at the wrong place when LinkupLifetime is True.

For example, here the main Beam is red, it has 12 LowFrequencyPoints, and 60 very small HighFrequencyPoints that can be used as spawnpoints for the 25 green branches.

branchemitter1.jpg

You can now do the same in the BeamBranching properties of the second BeamEmitter, the one that was used as branch. So there also set UseBranching=True, and set BranchEmitter to 2. This way, the branches get sub-branches themself. Note that not all the branches will get sub-branches, especially the ones closest to the top will get the most. Also, if MaxParticles is 10 for the sub-branches, there will be 10 sub-branches total, and not 10 for every branch. For example, this emitter has 10 sub-branches (pink).

branchemitter2.jpg

Lighteffect

For a lighteffect the lightning can cast on the clouds, you can use an extra SpriteEmitter with a lighteffect sprite. You can't use real lights for this. This sprite then has to appear at the same moment and Location where the lightning is.

Make the SpriteEmitter inside the same Emitter actor where the Beams are, and give it a a lighteffect-on-clouds texture:

lighteffect.jpg

In Sprite set DirectionAs to PTDU_Normal and set Z=1 to make the sprite horizontal. Make it's LifeTime somewhat the same as the LifeTime for the lightning, and give it a similar ColorScale.

To make sure the Location of the Beams and this sprite, set all the Location settings for both sprite and Beams to 0, and use the GlobalOffsetRange in the Global settings of the Emitter actor instead. Set for both the Beam and sprite in Local RespawnDeadParticles to False, and in Global set AutoReset=True. Now, once all the Beams and the sprite disappeared, they will all reappear at the same time again. This is the best way to get perfect timing.

lightning.jpg

SparkEmitter

This ParticleEmitter generates sparks, for example the kind of sparks you see when someone's welding. What this emitter does is turning it's Texture into one thin line with the colors of the texture. Settings such as Acceleration and Velocity still work on it, but Collision, Spin, SizeScale, etc... don't work on it because the particles are limited to be lines. To get a working SparkEmitter, add one the same way you'd add a Sprite- or MeshEmitter, and first give the SparkEmitter Velocity or Acceleration and then go to it's Spark properties.

Spark

spark.jpg

In there, if you set the Min and Max value of TimeBetweenSegmentsRange to 1, you'll see the SparkEmitter working already.

spark1.jpg

LineSegmentsRange: this determinates how long the sections of the lines of the sparks will be, and also how visible they are. You can also make the length to be random if you enter a different Min and Max value. If you make this setting too high or too low (depending on other settings), the SparkEmitter will not work. The first screenshot has Min = Max = 5, the second screen has Min = Max = 3. A setting of 5 most of the times works the best.

spark1.jpg spark2.jpg

TimeBeforeVisibleRange: this doesn't seem to do too much.

TimeBetweenSegmentsRange: the effect of this setting is best visible if you let the line to be curved, when you use some acceleration. The TimeBetweenSegmentsRange determinates in how many segments the line should be divided. If TimeBetweenSegmentsRange is low, for example 0.1, the line will be divided into a lot of segments so the curve looks smooth (first screenshot). If it's high (for example 0.5), you'll see the line broken into segments. (second screenshot). A higher TimeBetweenSegmentsRange means less particles and thus better performance, but it doesn't look as good.

spark3.jpg spark4.jpg

For low TimeBetweenSegmentsRange settings, there will have to be created more particles (every segment of the line is a particle), so you might have to set MaxParticles and InitialParticlesPerSecond to something higher before it looks good. A very high MaxParticles and InitialParticlesPerSecond might create something like this (again with low and high TimeBetweenSegmentsRange)

spark5.jpg spark6.jpg

More Properties

To make the SparkEmitter useful, here are some properties that you can set:

If you use only Velocity, the particles will be one straight line. If you also use some acceleration, the sparks will be curved, and if you use a negative Z value for the acceleration it'll be like gravity. If you want to get a full circle of sparks, set in Velocity: X(Max) = +1000, X(Min) = -1000, Y(Max) = +1000 and Y(Min) = -1000, and also add gravity. You can of course also use other numbers than 1000 if you want the circle to have a smaller or larger radius. You'll need a lot of particles for this, so set MaxParticles and InitialParticlesPerSecond high enough, or make TimeBetweenSegmentsRange higher for more performance.

spark7.jpg

Changing the Texture in Texture can also do a lot. The texture that was used up to now, was the default 5 colored dots texture (that has invisible parts), and the sparks can look a lot better with other textures, for example with a fiery texture you get real firesparks.

spark8.jpg spark9.jpg

Things such as the ColorScale or Fading work, but these work per segment so you might not get the look you expected. Properties such as the StartLocationRange, Warmup, etc. work the same way as for the SpriteEmitter or MeshEmitter. On the screenshot: a random StartLocationRange and a subtle red ColorScale on a white texture.

spark10.jpg

Moving Emitters

Moving Emitters work! For example if you attach an Emitter to a mover, the Emitter and all it's particles will really move together with the mover. This is where the CoordinateSystem in General comes in again: if CoordinateSystem is PTCS_Independent, the particles become independent once they're spawned, and don't move together with the mover. Only the place where particles get spawned moves. The screenshots show what happens then before the mover moves, while the mover moves and after he moved.

move1.jpg move2.jpg move3.jpg

If CoordinateSystem is PTCS_Relative, the particles are always relative to the position of the Emitter actor, and when this one moves the particles will move as well, so it'll look like the following 3 screenshots. The main difference is visible on the second one.

move4.jpg move5.jpg move6.jpg

Shapes

This section shows how to make certain shapes by using certain combinations of the properties of a ParticleEmitter. It's about the shapes the particles will form together. You can of course also get shapes by using one big MeshEmitter. Most of the times, a SpriteEmitter is used here, but there's also something for BeamEmitters specifically. These examples show only one way to get a certain shape, but most of the times there are more ways to do it. You can get certain shapes with the startposition, or by making the particles move with a certain shape.

If you use, in Location, the setting StartLocationShape to PTLS_Box, you can make a line, a rectangle or a box. You get a line when you use only one axis in StartLocationRange, for example the X axis, and leave the settings for the other axes 0. To get the line, you need to enter a different X(Min) and X(Max) value, so all the particles will get spawned between these two values. For example on the screenshot it's going from -100 to 200.

shape1.jpg

If you use two axes, you'll get a rectangle. For example, on the screenshot X and Z are used, and the rectangle ranges from X(Min) = -100 to X(Max) = 200 and from Z(Min) = 50 to Z(Max) = 200.

shape2.jpg

If you use X, Y and Z, you get a box. This one has all the Min values -100 and the Max values +100.

shape4.jpg

To rotate your line/rectangle/cube, you have to use the Rotation settings in Rotation.

shape3.jpg

With PTLS_Sphere, they'll form a sphere. You can set the radius with SphereRadiusRange. If Min=0 and Max=1000, all the particles will start inside the sphere with a radius of 1000 units. However if you set Min=1000 and Max=1000, the particles will start only at the shell of this sphere.

shape5.jpg

With the combination of Velocity and Acceleration, you can get parabolic shapes, for example to use for a fountain or waterfall. For the waterfall, you also need to give a random StartLocation, to give the waterfall a width.

fountain1.jpg waterfall.jpg

To get a fountain that goes to all directions, set in Velocity --> StartVelocityRange for example X(Min) to -150, X(Max) to +150, Y(Min) to -150 and Y(Max) to +150.

fountain2.jpg

You can also get certain shapes by shrinking or growing the praticles while they move, for example to make a flame. To make a very basic flame, set in Velocity --> StartVelocityRange Z(Min) and Z(Max) to 300 (or another value if you want the flame to be smaller or larger), then in Size set UseSizeScale to True and UseRegularSizeScale to False. Make a new SizeScale and in there, set RelativeSize to 0 and RelativeTime to 1. For the best effect, also make a FadeOut in Fade, with FadeOutStartTime 0. Set LifeTime to 1, and MaxParticles to 50, because you need a lot of them to make the flame look good. It's best to use a circle texture with soft edges for this.

flame1.jpg

This flame is pretty boring and doesn't look very natural: it's too smooth. For a much better effect, give it a subtle random StartLocation: in Location --> StartLocationRange set X(Min) = -20, X(Max) = 20, Y(Min) = -20 and Y(Max) = 20, and it'll look like the first screenshot. With more progressive random StartLocations, for example with the value 100 instead of 20, you'll get a much wider flame. This is shown on the second screenshot.

flame2.jpg flame3.jpg

Use X or Y acceleration for a wind effect. The color effect is done with the settings in Fade --> FadeOutFactor.

flame4.jpg

Smoke: for smoke, do exactly the same as for fire, only this time make the SizeScale to grow the particles. On the Screenshot, the Relative of the SizeScale is 10, the Z of Velocity is 100, the LifeTime of the particles is 10 and MaxParticles is 100.

smoke.jpg

Final Notes

  • SpriteEmitters, BeamEmitters and SparkEmitters also work with animated or procedural textures
  • If you place the Emitter Actor inside a solid brush, the emitter might not work. If this happens, but you want the StartLocation to be inside the brush, you can use the GlobalOffsetRange.
  • The 4 ParticleEmitter classes are in the Actor Class Browser, in Object --> ParticleEmitter (after you turned off "Actor classes only")
  • If you type "stat particle" in the console, you see the particle stats. pclrender and pcltick have to do with the rendering time for the particles, pcls is the total number of particles in the map and emitters is the number of emitters in the map.
  • Thanks to Daniel Vogel (and any other people who worked on it) for making such a great particle system :)
  • There's another Emitter tutorial with example maps and a description on how they were made.


EmittersTutorial - r1.11 - 30 May 2003 - 12:13 GMT - Copyright © 2001-2003 Epic Games
Unreal Developer Network Content by those crazy Perilith guysSite design and art by 2 design