Home  |  FAQ  |  About  |  Contact  |  View Source   
 
SEARCH:
 
BROWSE:
    My Hood
Edit My Info
View Events
Read Tutorials
Training Modules
View Presentations
Download Tools
Scan News
Get Jobs
Message Forums
School Forums
Member Directory
   
CONTRIBUTE:
    Sign me up!
Post an Event
Submit Tutorials
Upload Tools
Link News
Post Jobs
   
   
Home >  Tutorials >  C# >  Terrarium .NET: Introduction to Creature Development
Add to MyHood
   Terrarium .NET: Introduction to Creature Development   [ printer friendly ]
Stats
  Rating: 3.88 out of 5 by 8 users
  Submitted: 03/07/02
Max G ()

 

If you think this belongs in code samples rather than tutorials, I disagree. The large code sample below is simply to get you started. There are step by step directions, explanations, and comments along the way, making it a tutorial. This is also my first tutorial, please be merciful when rating it. Thanks!

Abstract

This tutorial summarizes so you don't have to read through many pages before you can start coding. It will go through all the steps required to run Terrarium on your computer and the first steps of Terrarium creature development using C#.

Introduction: What is Terrarium?

Terrarium is a multiplayer game developed by Microsoft using the .NET Framework. It is a simulation of an ecosystem, run concurrently on multiple computers (clients). The three basic components of the ecosystem open to the imagination of software developers are plants, herbivores, and carnivores. The goal of the game is to create a component that would outlive others and have the largest population.

The game was developed to demonstrate the capabilities of the .NET Framework and test it at the same time. Since the code you develop runs on many client machines, there is a potential for malicious actions. The security built into the Terrarium engine and the software license agreement prevent users from these actions.

Installation and Setup

In order to run Terrarium on your computer, follow these steps:

  1. Install either the or the
  2. Download and install the
  3. The installation would place a shortcut to .Net Terrarium in the Start Menu->Programs. Click on the shortcut to start the program.
  4. Familiarize yourself with the interface. Read the for more information.

When Terrarium starts up, you will be connected to the www.terrariumgame.net server by default. The server keeps statistics for all the introduced plants and animals. You can also run your own Terrarium server, which requires SQL 2000 server (or newer) besides the .NET Framework.

The first thing you will notice is the blue ball roaming about on your screen. This is the teleporter. It picks up whatever creature is in its way and teleports it to a random computer connected to the same Terrarium server. All the computers connected to the server are known as peers, and their number can be seen on the top toolbar of the program.

Your screen will begin populating with various plants and animals. Those plants and animals were developed by other programmers, introduced to the Ecosystem, and sent to your computer by their teleporters.

Prototyping

Although you are not required to have Visual Studio .NET to program the creatures, it would make development much easier. This tutorial assumes most of the readers got their hands on a copy of Visual Studio.

Your assignment is to appropriately distribute essential attribute values and to program the event handlers. There are 7 attributes and 10 event handlers, all of which are shown in the code below. More information (not necessary, but just in case): ,

Let us make a simple carnivore. Please do the following:

  1. Open Visual Studio .NET
  2. Click File -> New -> Project
  3. Click on Visual C# Projects in the Project Types pane
  4. Select Class Library in the Templates pane
  5. Type the desired name for your carnivore in the Name field and specify its location.

Visual Studio will display the Class1.cs file. Delete everything in the file, and replace it with the following code (comments should be pretty helpful if you've never created a creature before):

//This sample was based on the template and sample codes 
// provided at http://www.gotdotnet.com/terrarium/ 
// I used them to learn all the possibilities and tried to combine the 
// most helpful features and algorithms to create this simple example. 

using System; 
using System.Drawing; 
using System.Collections; 
using System.IO; 
// Every organism assembly must have all the attributes below defined on it [assembly: OrganismClass("MyCreature")] 
// The class that derives from Animal [assembly: AuthorInformation("Your Name Here!!!", "Your EMAIL here")] 
// Your Email Alias [CarnivoreAttribute(true)] 
// true for carnivores, false for herbivores [MatureSize(25)] 
// smaller -> reproduce quicker between 25 and 45 
// fill in the question marks below with corresponding attributes 
// [AnimalSkin(AnimalSkinFamilyEnum.?)] 
// [MarkingColor(KnownColor.?)] 
// Point Based Attributes: You get 100 points to distribute among these 
// attributes to define what your organism can do. Choose based on strategy. [MaximumEnergyPoints(5)] 
// how much energy the creature can store [EatingSpeedPoints(5)] 
// how fast the creature can eat [AttackDamagePoints(16)] 
// how well the creature attacks in increments of 4 [DefendDamagePoints(12)] 
// how well the creature defends in increments of 4 [MaximumSpeedPoints(12)] 
// how fast the creature can move [CamouflagePoints(0)] 
// how well the creature can hide [EyesightPoints(50)] 
// how far the creature can see in increments of 10 
// Organism Classes must be serializable 
public class MyCreature : Animal { 
    Point parentLocation = new Point(-1,-1); 
    // hello, mother AnimalState targetAnimal = null; 
    // current target (hunting for, eating, attacking) int huntingSpeed; int cruisingSpeed; 
    // Set up event handlers when first initialized 
    protected override void Initialize() { 
        huntingSpeed = Species.MaximumSpeed; 
        cruisingSpeed = (int) (Species.MaximumSpeed * .4); 
        Born += new BornEventHandler(BornEvent); 
        Load += new LoadEventHandler(LoadEvent); 
        Idle += new IdleEventHandler(IdleEvent); 
        Teleported += new TeleportedEventHandler(TeleportedEvent); 
        // the commented event handlers below are empty 
        // they are here to show you what is available 
        // ReproduceCompleted += new ReproduceCompletedEventHandler(ReproduceCompletedEvent); 
        // EatCompleted += new EatCompletedEventHandler(EatCompletedEvent); 
        DefendCompleted += new DefendCompletedEventHandler(DefendCompletedEvent); 
        Attacked += new AttackedEventHandler(AttackedEvent); 
        // AttackCompleted += new AttackCompletedEventHandler(AttackCompletedEvent); 
        MoveCompleted += new MoveCompletedEventHandler(MoveCompletedEvent); } 

    // *** User defined helper functions ********************************************** 
    // Tells you whether you have enough energy to move towards a given location at the given speed 
    // speed can be between 0 and Species.MaximumSpeed 
    bool CanMove(OrganismState target, int speed) { 
        return (State.EnergyRequiredToMove(DistanceTo(target), speed) < State.StoredEnergy); 
    } 
    // Starts moving towards the location at the given speed 
    void MoveToTarget(Point location, int speed) { 
        BeginMoving(new MovementVector(location, speed)); 
    } 
    // Finds creatures in the visible area and starts moving towards the first animal it finds 
    // A good strategy here would be to use the DistanceTo() function to find the closest creature 
    void ScanForTargetAnimal() { 
        ArrayList foundAnimals = Scan(); 
        if (foundAnimals.Count > 0) 
            foreach (OrganismState organismState in foundAnimals) 
                if (organismState is AnimalState) 
                    if (!IsMySpecies(organismState)) { 
                        targetAnimal = (AnimalState) organismState; 
                        MoveToTarget(targetAnimal.Position, huntingSpeed); 
                        return; 
                    } 
    }
    // *** Predefined event handlers ************************************************** 
    // Fired when we're first born 
    void BornEvent(object sender, BornEventArgs e) { 
        byte[] dna = e.Dna; 
        if (dna != null) { 
            // retrieve DNA info from the parent 
            MemoryStream m = new MemoryStream(dna); 
            BinaryReader b = new BinaryReader(m); 
            parentLocation = new Point(b.ReadInt32(), b.ReadInt32()); 
            b.Close(); 
            MoveToTarget(parentLocation, cruisingSpeed); 
            // start moving towards the parent 
        } 
    } 
    // First event fired on an organism each turn 
    void LoadEvent(object sender, LoadEventArgs e) { 
        // refresh and check targetAnimal just in case 
        if(targetAnimal != null) 
            targetAnimal = (AnimalState) LookFor(targetAnimal); 
        // targetAnimal could have disappeared due to death, teleportation, or error 
        if(targetAnimal == null) { 
            this.WriteTrace("Target animal disappeared."); 
            ScanForTargetAnimal(); 
        } 
    } 
    // Fired after all other events are fired during a turn 
    void IdleEvent(object sender, IdleEventArgs e) { 
        if(CanReproduce) { 
            // Reproduce as often as possible 
            // send "DNA" info to the offspring 
            MemoryStream m = new MemoryStream(); 
            BinaryWriter b = new BinaryWriter(m); 
            b.Write(State.Position.X); 
            b.Write(State.Position.Y); 
            BeginReproduction(m.ToArray()); 
            b.Close(); 
        } 
        if(IsAttacking || IsDefending || IsEating) 
            // busy 
            return; 
        if(IsMoving && targetAnimal != null) 
            // moving towards the target 
            return; 
        if (CanEat) { 
            this.WriteTrace("Hungry."); 
            if (targetAnimal != null) { 
                if (targetAnimal.IsAlive) { 
                    if (WithinAttackingRange(targetAnimal)) { 
                        this.WriteTrace("Live target within attack range. Attacking."); 
                        BeginAttacking(targetAnimal); 
                    } 
                    else { 
                        this.WriteTrace("Live target out of attack range. Moving closer."); 
                        MoveToTarget(targetAnimal.Position, huntingSpeed); 
                    } 
                } 
                else { 
                    if(WithinEatingRange(targetAnimal)) { 
                        this.WriteTrace("Dead target within eating range. Eating."); 
                        if(IsMoving) 
                            StopMoving(); 
                        BeginEating(targetAnimal); 
                    } 
                    else { 
                        this.WriteTrace("Dead target out of eathing range. Moving closer."); 
                        MoveToTarget(targetAnimal.Position, huntingSpeed); 
                    } 
                } 
            } 
            else { 
                if(!IsMoving) { 
                    this.WriteTrace("No target. Start Searching."); 
                    // move to a random location in search of food 
                    BeginMoving(new MovementVector(new Point(OrganismRandom.Next(0, WorldWidth - 1), OrganismRandom.Next(0, WorldHeight - 1)), cruisingSpeed)); 
                } 
                else { 
                    this.WriteTrace("No target. Already Searching."); 
                } 
            } 
        }
        else {
            this.WriteTrace("Full. Lazy."); 
            if(IsMoving) 
                StopMoving(); 
        } 
    } 
    // Fired if we got teleported 
    void TeleportedEvent(object sender, TeleportedEventArgs e) { 
        targetAnimal = null; 
    } 
    // Fired after we've given birth 
    void ReproduceCompletedEvent(object sender, ReproduceCompletedEventArgs e) { 
    } 
    // Fired after an eat action is completed 
    void EatCompletedEvent(object sender, EatCompletedEventArgs e) { 
    } 
    // Fired after a defend action is completed 
    void DefendCompletedEvent(object sender, DefendCompletedEventArgs e) { 
    } 
    // Fired if we're being attacked 
    void AttackedEvent(object sender, AttackedEventArgs e) { 
        AnimalState theAttacker = e.Attacker; 
        try { 
            if (theAttacker.IsAlive) { 
                // sanity check 
                this.WriteTrace("We're under attack!"); 
                if (!IsDefending) 
                    BeginDefending(theAttacker); 
                if (!IsAttacking) 
                    BeginAttacking(theAttacker); 
            } 
        } 
        catch (Exception ex) { 
            this.WriteTrace("AttackedEvent Exception: " + ex.ToString()); 
        } 
    } 
    // Fired if attack completed 
    void AttackCompletedEvent(object sender, AttackCompletedEventArgs e) { 
    } 
    // Fired if we arrived at our destination or got blocked 
    void MoveCompletedEvent(object sender, MoveCompletedEventArgs e) { 
        if (e.Reason == ReasonForStop.Blocked) { 
            if (e.BlockingOrganism is AnimalState) { 
                // this needs to be here because UpdateRadar may not see the blocker 
                // if the blocker is invisible we can assume he is harmless 
                AnimalState blockingAnimal = (AnimalState) e.BlockingOrganism; 
                if (blockingAnimal.IsAlive) { 
                    if (!IsMySpecies(blockingAnimal)) { 
                        if (WithinAttackingRange(blockingAnimal)) { 
                            this.WriteTrace("Blocking animal within attacking range. Attacking."); 
                            targetAnimal = blockingAnimal; 
                            BeginDefending(targetAnimal); 
                            BeginAttacking(targetAnimal); 
                        } 
                        else { 
                            this.WriteTrace("Blocking animal not in attacking range."); 
                            // insert code to pick an alternate route 
                        } 
                    } 
                    else { 
                        this.WriteTrace("MySpecies in the way."); 
                        // insert code to pick an alternate route 
                    } 
                } 
                else { 
                    this.WriteTrace("Blocking animal is dead. Free food!"); 
                    if (WithinEatingRange(blockingAnimal)) { 
                        if ((State.EnergyState < EnergyState.Full)) { 
                            // CanEat ? 
                            if (IsMoving) StopMoving(); targetAnimal = blockingAnimal; BeginEating(targetAnimal); 
                        } 
                        else { 
                            // I'm full. And lazy. 
                            if (IsMoving) StopMoving(); 
                        } 
                    } 
                    else { 
                        // move closer to the dead animal and eat it 
                        MoveToTarget(targetAnimal.Position, cruisingSpeed); 
                    } 
                } 
            } 
            else { 
                this.WriteTrace("Plant in the way."); 
                // insert code to pick an alternate route 
            } 
        } 
    } 
    // This gets called whenever your animal is being saved -- either the game is closing 
    // or you are being teleported. Store anything you want to remember when you are instantiated 
    // again in the stream. 
    public override void SerializeAnimal(MemoryStream m) { BinaryWriter b = new BinaryWriter(m); 
        if (targetAnimal != null) { 
            b.Write(true); b.Write(targetAnimal.ID); 
        } 
        else 
            b.Write(false); 
        if (parentLocation.X > -1) { 
            b.Write(true); 
            b.Write(parentLocation.X); 
            b.Write(parentLocation.Y); 
        } 
        else 
            b.Write(false); 
    } 
    // This gets called when you are instantiated again after being saved. You get a chance 
    // to pull out any information that you put into the stream when you were saved 
    public override void DeserializeAnimal(MemoryStream m) {
        BinaryReader b = new BinaryReader(m); 
        if (((bool) b.ReadBoolean())) 
            targetAnimal = (AnimalState) RefreshState(b.ReadString()); 
        if (((bool) b.ReadBoolean())) parentLocation = new Point(b.ReadInt32(), b.ReadInt32()); 
    }
}

Now you have to actually tell the compiler what classes you just overloaded:

  1. Click on Project -> Add Reference...
  2. Select System.Drawing.dll in the .NET tab and click the "Select" button
  3. Click the "Browse..." button, find your Terrarium installation folder (C:\Program Files\Terrarium by default), browse to the Bin directory and double-click on organismbase.dll
  4. OK all the dialogs, click Build -> Build Solution

Hopefully these instructions are clear and you get no compilation errors. Read the next section to learn how to test your carnivore.

First Steps

Now that you have compiled a dll, you can test your animal. To do so, start the Terrarium client, click on the small aquarium icon in the bottom left corner of the window, and click on Create New Terrarium button. Call it whatever you want. You now have your own playground.

You can add creatures to your Terrarium by clicking the Introduce Animal... button. This will display the database of all animals stored on the server you are connected to. Start by adding a few plants (food for herbivores) and a few herbivores (food for your carnivore). Then introduce your creature by clicking the Browse... button on the Reintroduce Species screen, and find the dll you just compiled (bin/Debug in your project directory).

To see the trace messages printed using this.WriteTrace() function above, select an instance (or multiple instances) of your carnivore, and click the Show Trace Window button

The Real Deal

Once you feel confident about your bug, you can introduce it to the Ecosystem:

  1. Click on the Globe icon, then click Join Ecosystem. This will connect you to the server specified in your preferences. In case you don't know how to change those preferences, right click anywhere on the screen and click Game Settings... menu item, then Server tab
  2. Click on Introduce Animal..., Browse..., and select your creature's dll

Remember that once you introduce a bug into the Ecosystem, you cannot reintroduce it until all the instances of your animal die off. If you want to "upgrade" it and add it to the Ecosystem, you have to modify the name of your creature like so:

[cs] [assembly: OrganismClass("MyCreature01")] ... public class MyCreature01 : Animal { ... } [/cs]

The matching keywords above must always be identical.

Conclusion

You should now be on your way to programming the best AI on Terrarium. The sample code above will not produce a smart carnivore, since advanced features like group behavior or even walking around obstacles are not provided. I will leave that to your imagination.

References and Additional Resources

You can find more code samples and full documentation on the Terrarium homepage at . Please feel free to email me if you have questions or suggestions.

There are also active threads on the forums:
Messages > C# > Terrarium
Messages > General Technical > .NET Terrarium Project

Thanks to Joseph for his input and suggestions on writing the tutorial.


Return to Browsing Tutorials

Email this Tutorial to a Friend

Rate this Content:  
low quality  1 2 3 4 5  high quality

Reader's Comments Post a Comment
 
Wow, that is some scary code to look at. I don't doubt that it will work or anything but that is just ugly. This seems suspiciously like the tutorial they provide on gotdotnet.com.
-- Adam Patridge, March 11, 2002
 
After reading it, I basically felt it was an expanded version of different parts of gotdotnet.com's tutorials. Though you did add in some of your own comments into the code to help explain, it was very bulky and it would probably have been better if you had broken it down into sections to really "introduce" creature development. I haven't tried to build the code yet either, but in the early part of it I noticed is that you failed to add in the animal's attributes, while still leaving in the comments for them, or rather upon closer inspection, adding them into the comments themselves (for future reference, that tends not to be a good technique ;) ). That could lead to some head-aches for people who haven't created a creature before and without refering to gotdotnet.com's tutorials first. I gave it 2 stars **.
-- Robin Schenck, March 12, 2002
 
I don't know how that happened, but the comments with the attributes were not supposed to appear that way. Apologies.

Like the abstract says, this is for people who want to create something without reading through pages and pages of gotdotnet documentation.
-- Max G, March 12, 2002
 
Interesting tutorial.
-- Brian Simoneau, March 12, 2002
 
very interesting tutorial, but as someone put it earlier...very ugly looking. i agree this does look a lot like the gotdotnet tutorials, but it looks like it is all slammed together. it might have been better if you would have broken the code up as robin stated, and talked about each part in a little more detail. then it may have been better. overall not bad, but it is just a little overwhelming.
-- J J, March 12, 2002
 
Great work!
-- Kuniaki Tran, March 12, 2002
 
I couldn't get the code provided to compile. Maybe I just made a newbie error. But, nonetheless, I found that frustrating for this newbie and ended up just going to the terrarium web page and following their intro.
-- Andrew Petro, March 13, 2002
 
Has any1 figured out the tricks of Terrarium longetivity ??
-- Nimit Sawhney, March 15, 2002
 
I just want to win the xbox from the terrarium contest.
-- Duk Lee, March 17, 2002
 
It does look a lot like the Carnivore on gotdotnet in the Terratium Animal Farm, however there are a few difference.... and this did teach me how to know if a blocking animal is of my own species (I couldn't figure out the syntax of how to figure it out... now I know that I was making it a LOT harder than it really is..... there is a method to tell me.... nice). So I think that it is informative in some respects. Definately check out the terrarium animal farm for more code examples tho.
-- Ian Mercer, March 19, 2002
 
great info just long, very long
-- Robert Hill, March 20, 2002
 
An event driven AI routine has severe limitations
-- Michael Tung, March 28, 2002
 
nice tutorial
-- Kyoungjin Park, December 06, 2002
 
Copyright © 2001 DevHood® All Rights Reserved