Rainy Day

In this tutorial we'll learn how to use particle effects and the T2DTriggerComponent to make a game in which rain will make plants grow.

1. Getting started

Start Visual Studio and create a new project using the Starter Game 2D template -- Call it RainyDayTutorial. Press the F5 button or select Build > Build Solution from the main menu. Next, start TXB, and select Open Project. Navigate to the projected directory that you created with Visual Studio, go to the Game subdirectory, and load the file StarterGame2D.txproj.

2. A little background information

Save the following image to your desktop or folder of choice, then drag it onto the builder to add it to your project. Alternatively, you can click the "Create New Material" button on the create pane to import the image as a new material.

background.png
background.png

From the Create pane in TXB, drag the backgroundMaterial you just added into the Scene View area, to create a static sprite that we can use as the background of our scene. Go to the Edit pane, and enter 0 for both Position X and Position Y -- this will center the sprite in the scene. For Width enter 100, and for Height enter 75. This will make the sprite exactly match the viewable area of the default camera, so the image will completely fill up the background of the scene.

For Layer, enter 31. Higher layers are further "back" in the scene, so we want our background to be very far back. Also, clear the Collision Enabled checkbox -- we don't want anything colliding with our giant background sprite.

When we dragged the image onto TXB that updated the project file to include the image. Since we updated the project file, we need to save it. From the menu, choose File -> Save Project. When we dragged the image into the scene and created the sprite, that updated the scene, so we need to save that, too. From the menu, choose File -> Save Scene As. Select "levelData.txscene" from the list and click the Save button (that's just the default scene that was created with the project, so don't worry about overwriting it).

Now go over to Visual Studio, and click the Reload button in the popup box -- this is just Visual Studio noticing that we just changed the project file. Let's see our background in action by choosing Debug -> Start Debugging from the menu! Okay, so it's a static background and as a result there's not a lot of action to see. Let's fix that.

3. Flower power

Save the following image to your desktop or folder of choice, then drag it onto the builder to add it to your project. Alternatively, you can click the "Create New Material" button on the create pane to import the image as a new material.

plant.png
plant.png

Material BuilderThis image is actually multiple frames of an animation. Let's tell TXB about how the image should be broken up into frames. Go to the Create pane, and the Materials rollout. Double-click the plantMaterial, to bring up the Material Builder window. Set the Image Mode to "Cell" Then in Cell Count X enter 6, and in Cell Count Y enter 3. Now you should see the image broken up into the individual frames. Click the Save button to save these changes.

Create a new AnimationNow that we have the image broken up into frames, let's string those frames together into an animation. Click the "Create a new Animation" button. Choose plantMaterial, and click the Select button. This brings up the Animation Builder window. Click the little green "Add all frames from the image" button -- this will add the frames in order to the animation sequence. The default speed is pretty fast for these particular frames, so for Frames Per Second enter 5. Also, this animation doesn't make sense when it cycles from the last frame to the first, so clear the checkmark out of the Cycle Animation checkbox. Click the Save button to save this animation. Now you have a new Animation in the Animations rollout.

Animation Builder

Let's start putting some flowers into the scene. We can use the Template system to make one flower and make multiple copies of it. First we need to create the template. From the Create pane, from the Animations rollout, drag the plantAnimation into the scene, but below the background image. Go to the Edit pane. In the Scene Object rollout, set the Width and Height 16, to shrink it down to a more reasonable size. In the Scripting rollout, set the Name to "plantTemplate". Click the Template checkbox to create a checkmark in it. Now that we have our flower template, we can create instances of it with Spawner object.

Create template

From the Create pane, from the Other rollout, drag a Spawner into the scene, to the lower left corner of the grassy area of the background. Go to the Edit pane, and in the Template Reference rollout, set Template Object to plant. Now we have an instance of our template! With the spawner still selected, use the menu to do Edit -> Copy. Now use the menu to do Edit -> Paste. This creates a copy of your spawner, although it pasted the copy right on top of the original. Drag the new copy away, to another position on the grassy hill. Paste in a bunch more copies, to cover the hill with flowers. Use the menu to do File -> Save Project and File -> Save Scene. Then go over to Visual Studio, reload the project, and use the menu to do Debug -> Start Debugging. Now we've got some action -- flower dying action! Kind of a downer, huh? Maybe we'll find a silver lining if we add a cloud.

Place a spawnerCopy and paste them all over

4. Into every life a little rain must fall

Add this image into TXB as a new Material.

cloud.png
cloud.png

Quick-Edit buttonsCreate a linkpointDrag the cloudMaterial from the Create pane into the middle of the scene. Click the "Edit this objects linkpoints" quick-edit button. Create a linkpoint in the middle of the main mass of the cloud, and hit the Esc key to exit out of linkpoint editing mode when you're done creating the linkpoint. Creating a linkpoint will let us mount things onto the cloud. What would we want to mount to the cloud? How about a particle effect that creates raindrops! Drag this image from your browser onto TXB:

raindrop.png
raindrop.png

Now, from the Create pane, from the Particle Effects rollout, drag a New Particle Effect into the scene. Go to the Edit pane. Expand out the "Emitter - Untitled Emitter" rollout. This will let us control aspects of what particles are emitted by the particle effect. Set Material to raindropMaterial so we can start emitting some raindrops. Set Type to LINEX so that the particles are created along a horizontal line instead of at a single point. Now lets start changing some parameters of the particles themselves. Click the Edit Emitter Graph button.

Edit the particle effect's emitter graph

Edit the Emission Arc BaseIn the select box, choose Emission Arc Base. This controls how the particles spread out. By default it's set to 360, so particles spread out in every direction. Drag the dot down to 0.000 0.000. This will cause the particles to be issued in only a single direction.

Now choose Emission Angle Base. Drag the dot to about 0.000 180.000, so the drops are created heading down instead of up.

Select Particle Life Base, and drag the dot to the top of the graph to set it to 0.000 10.000. This will keep the particles "alive" for 10 seconds from when they are created.

Select Size X Base. Drag the dot to about 0.000 11.013 -- this will make the drops nice and small.

Select Speed Base, and drag the dot to about 0.000 68.354. This will speed the drops up a bit.

Click the Close button.

Finish Editing Particle Effect Emitter Graph

Now that we have the particle effect creating drops, let's attach it to the cloud so the drops look like they're falling from the cloud. Click the "Mount this object to another object" quick-edit button, and then click on the yellow cross on the cloud that represents the linkpoint we created there earlier. Now the particle effect is connected to the cloud, but the drops are being created in front of it, which looks a little funky. Go to the Edit pane, and the Scene Object rollout, and set the Layer to 5. This will put the drops behind the cloud, so they'll look like they're falling out of the bottom.

Now let's let the player move the cloud, too. Select the cloud sprite, and go to the Edit pane. In the Components rollout, select MovementComponent in the select box, and click the green "+" button to add it. MovementComponent is a component that is part of the StarterGame template that we used to create the project. It moves whatever object it is attached to based on user input from the WASD or arrow keys, or the left analog stick of a gamepad.

Add a MovementComponent to the cloud

From the menu, use File -> Save Project and File -> Save Scene. Go over to Visual Studio, click the reload button, and use the menu to do Debug -> Start Debugging. Now you can move the cloud around, and watch the drops fall! But the rain isn't helping the flowers. That's because particle effects are just fancy graphical effects -- there's no game object interaction there. In order to make the rain have a game effect, we'll need to write some custom game code.

5. This plant is operating within acceptable parameters

In Visual Studio, use the menu to do Project -> Add New Item. Select the T2DComponent template, and enter the name PlantGrowthComponent.cs and click the Add button.

In the "Private, protected, internal fields" region, add this code to declare some variables:

        float _moisture;
float _maxMoisture;
float _minMoisture;
float _lowMoistureThreshold;
float _dryRate;
float _wateringRate;
bool _watering = false;
T2DAnimationData _growAnim;
T2DAnimationData _dieAnim;

And to enable us to set those variables in TXB, let's set up some public properties to access them. Go to the "Public properties, operators, constants, and enums" region, and add this code:

        public T2DAnimatedSprite AnimatedSprite
        {
            get { return Owner as T2DAnimatedSprite; }
        }

       
        [TorqueXmlSchemaType(DefaultValue="10")]
        public float Moisture
        {
            get { return _moisture; }
            set { _moisture = value; }
        }

        
        [TorqueXmlSchemaType(DefaultValue = "0")]
        public float MinMoisture
        {
            get { return _minMoisture; }
            set { _minMoisture = value; }
        }

        
[TorqueXmlSchemaType(DefaultValue = "10")] public float MaxMoisture { get { return _maxMoisture; } set { _maxMoisture = value; } }
[TorqueXmlSchemaType(DefaultValue = "5")] public float LowMoistureThreshold { get { return _lowMoistureThreshold; } set { _lowMoistureThreshold = value; } }
[TorqueXmlSchemaType(DefaultValue = "0.05")] public float DryRate { get { return _dryRate; } set { _dryRate = value; } }
[TorqueXmlSchemaType(DefaultValue = "0.05")] public float WateringRate { get { return _wateringRate; } set { _wateringRate = value; } } 

Whenever you add a property like that to a component, it's good practice to also update the CopyTo method to copy those properties if the object is ever cloned. Go to the "Public methods" region, and update the CopyTo method so it looks like this:

        public override void CopyTo(TorqueComponent obj)
{
base.CopyTo(obj);
PlantGrowthComponent obj2 = obj as PlantGrowthComponent;
obj2.Moisture = Moisture;
obj2.MinMoisture = MinMoisture;
obj2.MaxMoisture = MaxMoisture;
obj2.LowMoistureThreshold = LowMoistureThreshold;
obj2.DryRate = DryRate;
obj2.WateringRate = WateringRate;
}

Use the menu to select Build -> Build Solution. This causes Visual Studio to compile the C# code, and it also updates a schema file that tells TXB about the components that are available. Since we just created a new component, let's see what it looks like in TXB. Go to TXB, and tell the popop to reload the component definitions. Select your plant template (the one off the bottom of the viewable area), and go to the Edit pane. Go to the Components rollout, select PlantGrowthComponent, and click the green "+" button to add it.

Scroll down and you'll see a rollout for the component you created. It has editable fields for all of the public properties you set up, and the default values you set are in there. Exposing tweakable values on your components to TXB like this can be very useful in the game design process, since it lets you do it in TXB, where you do other design related tasks like placing objects. Let's get some practice tweaking values in TXB by setting WateringRate to 0.1. Afterwards, save your changes by using the menu to do File -> Save Scene.

Tweak the parameter

6. It's a matter of life and death

Well, we've got some variables and tunable parameters now, but we're not actually doing anything with them. Let's do something with them. Go back to Visual Studio, and go to the "Private, protected, internal methods" region. In the _OnRegister method, after the line that says "//todo: perform initialization for the component", add this code:

	Assert.Fatal(SceneObject is T2DAnimatedSprite, "PlantGrowthComponent only works with T2DAnimatedSprites!");
	_dieAnim = AnimatedSprite.AnimationData;
	_growAnim = AnimatedSprite.AnimationData.Clone() as T2DAnimationData;
	List<string> frames = new List<string>(_growAnim.AnimationFrames.Split(' '));
	frames.Reverse();
	_growAnim.AnimationFrames = string.Join(" ", frames.ToArray());

	ProcessList.Instance.AddTickCallback(Owner, this);

The first line, with the Assert, will signal an error if this component is ever attached to an object that isn't a T2DAnimatedSprite. Since the component is going to play animations it doesn't make sense to ever attach it to a different kind of object. Then we store the animation from the object as _dieAnim, the animation we'll play to make the plant die, since that's the animation we've got. We want to create another animation that makes the plant grow, basically just the same animation played in reverse. That's what the next few lines of code do -- they create a copy of the animation, and reverse its frames. Lastly, we register with the engine to call the ProcessTick method every tick of the engine (roughly 30 times per second).

Now that we have the two different animations, let's create a way to switch between them. In the "Public methods" region, create this method:

        public void RunAnimation(float direction)
{
if (direction < 0.0f)
{
if (AnimatedSprite.AnimationData != _growAnim)
{
int frame = AnimatedSprite.FinalFrame - AnimatedSprite.CurrentFrame;
AnimatedSprite.PlayAnimation(_growAnim);
AnimatedSprite.SetAnimationFrame((uint)frame);
}
}
else
{
if (AnimatedSprite.AnimationData != _dieAnim)
{
int frame = AnimatedSprite.FinalFrame - AnimatedSprite.CurrentFrame;
AnimatedSprite.PlayAnimation(_dieAnim);
AnimatedSprite.SetAnimationFrame((uint)frame);
}
}
}

This code decides which animation to run, and also accounts for the situation where the animation might be reversed while it's still running, and sets the current frame to be the corresponding frame in the opposite animation (e.g. one quarter dead is the same as three quarters alive), so there's a smooth transition between them.

Let's make use of that method. In the "Public methods" region, add this code to the ProcessTick method:

            if (_moisture < _lowMoistureThreshold)
            {
                // dying -- run animation forwards.
                RunAnimation(1.0f);
            }
            else
            {
                // growing -- run animation backwards.
                RunAnimation(-1.0f);
            }


// modify amount of moisture based on whether we're being watered or not. if (_watering) _moisture += _wateringRate; else _moisture -= _dryRate;
// clamp moisture amounts. if (_moisture < _minMoisture) _moisture = _minMoisture; if (_moisture > _maxMoisture) _moisture = _maxMoisture; 

 

This simply compares the current "moisture level" to the threshold, and makes the plant die if it doesn't have enough moisture, and grow if it does. It then uses the _watering variable to determine if we should increase the moisture, or dry out. Let's create some methods that will let us set that _watering variable. In the "Public methods" region, add these methods:

        public static void Grow(T2DSceneObject ourObject, T2DSceneObject theirObject)
        {
            PlantGrowthComponent plant = theirObject.Components.FindComponent<PlantGrowthComponent>();
            if (plant != null)
                plant._watering = true;
        }

        public static void Die(T2DSceneObject ourObject, T2DSceneObject theirObject)
        {
            PlantGrowthComponent plant = theirObject.Components.FindComponent<PlantGrowthComponent>();
            if (plant != null)
                plant._watering = false;
        } 

Why define these methods this way? Because defining them this way will let us use them with a T2DTriggerComponent. But before we can set that up, we need to expose these methods to TXB. Go to the "Public properties, operators, constants, and enums" region, and add these properties:

        public static T2DTriggerComponentOnEnterDelegate BeginWatering
        {
            get { return Grow; }
        }

       
        public static T2DTriggerComponentOnLeaveDelegate EndWatering
        {
            get { return Die; }
        }

       

Since these are read-only properties (i.e. they have a 'get', but no 'set'), there's no need to update CopyTo with them. Now use the menu to do Build -> Build Solution. As before, this updates the information that TXB uses. Go to TXB and accept the popup.

The particle effect was a graphical thing that had no effect on the game objects. Now let's do the opposite and create a game object that has an effect on other game objects, but isn't shown graphically. When we put the two together, we'll have a complete solution. From the Create pane, from the Other rollout, drag a new Blank Scene Object into the scene.

With the new scene object selection, click the "Mount this object to another object" quick-edit button. Click on the yellow cross in the middle of the cloud. Once the object is mounted, resize it by grabbing the blue dot on the bottom-middle of the bounding box, and stretch the object so the height expands quite a way outside the visible camera area.

Resize the blank scene object after it is mounted

Go to the Edit pane, and to the Components rollout. Select T2DTriggerComponent and click the green "+" button. The T2DTriggerComponent lets us define methods for what happens when objects enter or leave the region of the object they're attached to. Scroll down to the T2DTriggerComponent rollout, and for OnEnter select StarterGame.PlantGrowthComponent.BeginWatering. For onLeave, select StarterGame.PlantGrowthComponent.EndWatering.

Set the OnEnter and OnLeave properties

Now, when plants enter the region of this object (because the cloud moves it over top of them) they'll start growing, and when they leave the region (because the cloud moves it away) they'll being to die. Use the menu to do File -> Save Scene. Go to Visual Studio, and use the menu to do Debug -> Start Debugging. Now the plants respond to the rain!

We want your feedback! Please follow this link to take a short survey about this tutorial. Telling us what you think about our tutorials (good and bad) helps us make our tutorials better. We need to know what we're doing right and what we're doing wrong, and we can't do that without your help!