Code How To's

From TACWiki
Revision as of 13:45, 11 October 2007 by Zinsser (Talk | contribs) (Use a Particle Effect)

Jump to: navigation, search

This page provides tutorials for some of the most common actions a programmer may wish to implement.

Coding Conventions

  • Place the name of the file, authors, and copyright info at the top of each file
    • Any person who makes significant changes to a file should add their name to the list of authors in a file
      • This helps anyone reading the code because they know who to ask if they have a question about something
  • All variables are lowerCamelCase
  • All functions and properties (sometimes called "smart fields") are UpperCamelCase
  • Member variables are preceded with "m"
  • This code is intended to be viewed by many people who do not have strong programming backgrounds, therefore documentation is very important
    • Document the purpose of each class's member variables
    • Create a summary documentation block for every function
      • This can be done by placing the cursor above a function definition and typing "///"
      • This block shows up in Intellisense when writing that function in code
    • Add a summary to any properties that are possibly confusing or not straightforward
    • Document the purpose of each block of code unless the block is absolutely self-explanatory
    • See MeteorMadness.cs for an example of the appropriate level of documentation

Tweak Game Parameters

It is often helpful to adjust variables during runtime. This can be accomplished by adding them to the game's cheat menu, which can be activated in game by pressing ctrl+alt+c.

  • Add the variable to the Game Manager
    • Declare a new member variable in ColonyGameManager next to the other tweakable variables
    • Create a getter and setter for that variable
  • Add the variable to the Control Panel
    • Open ColonyControlPanel.cs[Design]
    • If the minigame using this variable does not yet have a group, do the following
      • Create a new radio button in the upper left corner in the same manner as the other games
      • Create a new group anywhere in the right area (only the left most 250 px or so are visible in the game)
      • Add a new if block to butUpdate_Click(object sender, EventArgs e)
        • Double click on the form to bring up the code
        • If this creates a new empty function, just delete the new function
      • Move the group out of view in hidePanels()
      • Double-click the new radio button to create the radNewminingame_CheckedChanged(...) function
        • Fill this function with code similar to radColony_CheckedChanged(...)
      • Add a new if block in UpdateMode(...)
    • Add a label and text box to the minigame's group in the same manner as the existing games
    • Add the line ColonyBaseApplication.GameManager.NewVariable = ...; to butUpdate_Click(...)
    • Add the line txtNewVariable.Text = "" + ColonyBaseApplication.GameManager.NewVaraible; to ControlPanel_Load(...)
  • Use the variable in the minigame
    • The value can be accessed with ColonyBaseApplication.GameManager.NewVariable
  • Set the default value for the variable in the config file
    • The config file is not yet implemented, so for now just the default value in ColonyGameManager

Create a New Minigame

  • Declare the class
    • Create a new folder under ColonySimulator
    • Create a new file in that folder named NewMinigame.cs where NewGame is the name of the new minigame
    • Place all new minigame-specific classes in that folder
    • Have the class derive from MiniGame
      • class NewGame : MiniGame {...}
  • Implement the Constructor
    • Initialize game variables
    • Declare a ScoreCard (see Scoring tutorial)
    • Log the GameBegin code (see the Logging tutorial)
  • Implement Update()
    • public override void Update(ContentManager content){...}
    • Do not assume a constant call rate
      • This function will almost always be called 60 times per second, but that rate may vary depending on CPU load
      • At the beginning of each cycle, get the time since the last cycle like this:
        • float dt = ColonyBaseApplication.GameManager.ElapsedSeconds;
        • DO NOT use XNA.Framework.GameTime.ElapsedGameTime as tAC_Engine implements update timing differently than a standard XNA application!
      • Build the time into all movement and timers, for example:
        • myEntity.LifeSpan -= dt;
        • myEntity.Position += myEntity.Velocity * dt;
  • Implement Draw()
    • public override void Draw(SpriteBatch spriteBatch){...}
    • Loop through all the game's entities and call their Draw() functions
    • Consider using a generalized render-queue such as GenericWorldSpace (not-yet-implemented as of Oct 2007)
  • Create a new game mode
    • Add a new entry to ColonyBaseApplication.Modes = { ModeSelector, ColonySim, ...}
    • Add the appropriate case to ColonyBaseApplication.SwitchMode(...)
    • Add the appropriate case to ColonyBaseApplication.Update(...)
    • Add the appropriate case to ColonyBaseApplication.Draw(...)
    • As of Oct 2007, it is necessary to add a new case to ModeSelector.Update and .Draw, but that will eventually not be necessary
  • End the game
    • Flush the particle system (see the particles tutorial) if any emitters were created
    • Log the GameEndSuccess/GameEndFailure code
    • Flush the log (forces it to write any cached codes to disk)
    • Show the score
    • Switch the mode to ColonySim
      • ColonyBaseApplication.SwitchMode(ColonyBaseApplication.Modes.ColonySim);

Display a Static Image

Before manually drawing something to screen consider using some type of game object, be it an Entity, HUD element, or even a Ticker (see the tutorials below). Game objects all know how to draw themselves, so it is unlikely that a programmer will need to draw something directly to the screen. Should the need arise, however, here's how it's done:

  • Import the content into the project
    • Browse to the appropriate folder in Visual Studio's Solution Explorer
      • For example, Content\MiniGames\MyImage.png
    • Right click on that folder and select Add->Existing Item
    • Select the image to import
  • Load it in code:
GraphicsDeviceManager gdm = ...;    // This is declared in GenericGameManager.cs
ConentManager cm = ...;             // Usually a parameter, as in Update(ContentManager content)
SpriteBatch sb = new SpriteBatch(gdm.GraphicsDevice);                     // Basically a wrapper for a DirectX render target
Texture2D texture = cm.Load<Texture2D>(@"Content\MiniGames\MyImage");     // Source image with no file extension
Rectangle destination = ...;        // Display destination
Color tint = Color.White;           // Tint the image with this color
float rotation = 0f;                // Rotation (in radians)
Vector2 center = new Vector2(texture.Width / 2f, texture.Height / 2f);         // Origin of rotation
float zDepth = 1f;                  // Order to draw sprites within this spritebatch

sb.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.BackToFront, SaveStateMode.SaveState);

sb.Draw(
  texture,
  destination ,
  null,               // What part of the source image to sample from--null tells it to use the whole image
  tint ,
  rotation,
  center,
  SpriteEffects.None, // How to mirror the image
  zDepth 
);

sb.End();

Display an Animation

Animations are built into Entities. There are no plans to support animation in any other context. Here's an example:

Animation anim = new Animation(new Vector2(100, 100));  // Create an animation that is 100x100 pixels when displayed
anim.AddClip("Idle", content.Load<Texture2D>(@"Content/IdleSpriteSheet"), 5, 1f/60f);       // a 500x100 spritesheet
anim.AddClip("Walk", content.Load<Texture2D>(@"Content/WalkSpriteSheet"), 12, 1f / 60f);    // a 400x300 spritesheet
Entity ent = new Entity(anim, new Vector3(10f, 50f, 0f), new Vector3(100f, 100f, 0f));      // a 100x100 2D entity located at (10,50)

ent.Play("Idle");       // Start the idle animation
// ...
ent.Play("Walk");       // Switch to the walking animation
// ...
ent.Draw(sb);           // Draw to the SpriteBatch sb

Create a New Type of Game Object

All specific game objects (players, enemies, collectibles, projectiles, etc.) should derive from Entity. This gives them easy ways to draw themselves, move, collide with other entities, and be passed to many tAC_Engine functions.

Get User Input

DO NOT use the standard XNA keyboard accessors (Xna.Framework.Input.KeyboardState or Xna.Framework.Input.Keyboard) because tAC_Engine does a lot caching and logging behind the scenes. Instead get keyboard input like this:

if (InputState.IsKeyDown(Keys.Left))
{
  // The left key was pressed
}

If an exception is thrown stating "Unregistered Key Comparison" then add that key to the list in the InputState constructor.

Manage a Heads Up Display

Not yet implemented.

Display Text or Icons with the Ticker

string text = "w00t.";                      // Text to display (can be "")
Texture2D image = null;                     // Can also be set to an image
Vector2 imageSize = new Vector2(25f, 25f);  // Size of the (optional) icon
Vector2 offset = new Vector2(700f, 50f);    // This will display the ticker 700 pixels to the right of the screen's 
                                            //   center and 50 pixels below the screen's center
Vector2 velocity = new Vector2 (-10f, 0f);  // Move left 10 pixels per second
Ticker.Font font = Ticker.Font.Standard;    // Regular text
Color color = Color.DeepPink;               // The manliest of colors
float opacity = .5f;                        // 50% transparent
float seconds = 5f;                         // Disappear after 5 seconds
bool fade = true;                           // Fade in and fade out

Ticker.Display(text, image, imageSize, offset, velocity, font, color, opacity, seconds, fade);

Display Text Manually

Most text displayed should be done using the Ticker, but if you really want to display text manually use the Nuclex font library. First you must import the desired font:

  • Create an xml file for the new font called FontName.tffimport that looks like this:
<?xml version="1.0"?>
<TrueTypeImport>
  <Path>Content\Fonts\MercuryII.ttf</Path>
  <Size>32</Size>
</TrueTypeImport>
  • Put the font (FontName.ttf) and FontName.tffimport in the Content\Fonts directory
  • Set the following properties for FontName.ttf
    • Build Action: None
    • Copy to Output Directory: Copy if newer
    • File Name: MercuryII.ttf
  • Set the following properties for FontName.tffimport
    • Build Action: Content
    • Content Importer: TrueTypeFont - XNA Framework
    • Content Processor: Bitmap Font - XNA Framework
    • Copy to Output Directory: Do not copy
    • XNA Framework Content: True

Now for the code:

ContentManager cm = ...;
SpriteBatch sb = ...;
BitmapFont bmf = cm.Load<BitmapFont>(@"Content\Fonts\Arial");
sb.Begin(SpriteBlendMode.AlphaBlend);
bmf.DrawString(new Vector2(10.0F, 10.0F), "Manual Text!", Color.Azure);
sb.End();

Import and Play a Sound

  • Run Autism Collaborative\Utilities\XACT\Xact.exe
    • This is the XACT tool from Microsoft DirectX SDK August 2006
    • If--after following the steps in this tutorial--playing a file in code throws an exception about using the wrong version of XACT, try using Xact.exe from a different SDK build
  • Open Autism Collaborative\Game\Game\Content\Sounds\AudioProject.xap
  • Expand Wave Banks and double click Wave Bank in the panel on the left
  • Expand Sound Banks and double click Sound Bank in the panel on the left
  • Go to Wave Banks->Insert Wave File(s)...
  • Select the file to import (MySound.wav) and click OK
  • Drag the newly-added file to the Cue Name section in the Sound Bank:Sound Bank window
  • Go to File->Build
  • Save and close XACT
  • Open game.sln with Visual Studio
  • Browse to the Content\Sounds folder in the Solution Explorer
  • Right click the Sounds folder and select Add->Existing Item...
  • Select MySound.wav and click OK
  • To play this new sound, use the following code:
Sound.Play("MySound"); // No file extension

Use a Particle Effect

Particles are handled by Sparx, a particle engine being developed alongside the Autism Game. Features are added to it on an as-needed basis so if a new feature is needed contact August or email support.

Particle Effects are imported via the content pipeline, so they can be called in a similar manner:

Emitter mRootEmitter;
mRootEmitter = ParticleEngine.LoadParticleEffect(@"Content\Particle Effects\MyParticleEffect.spx");
ParticleEngine.AddEmitter(mRootEmitter);

Don't forget to kill emitters when they are no longer needed.

mRootEmitter.Alive = false;  // Kills the Emitter
ParticleEngine.Flush();      // Kills all Emitters

Emitters can be moved around:

mRootEmitter.Position2D = new Vector2(100f, 100f);  // Emitter will move, but existing Particles will not be affected

They can also move and keep their Particles in the same relative location.

mRootEmitter.Transform = Matrix.CreateTranslation(100f, 100f, 0); // Particles will move with the Emitter

Any aspect of a Particle Effect can be changed at runtime. For example, to make an effect green add a Modifier:

mRootEmitter.RegularEmissionType.Modifiers.Add(new Modifier("Colorizer", 0f, 0f, Color.Green, Color.Green, 1f, 1f, 1f, 1f, 0f, 0f));

Or apply forces at runtime:

mRootEmitter.SubscribeEmissionsToForce(new Current("theWind", 0f, 0f, new Vector2(10f, 0f), 1f));

Create a New Particle Effect

Keep Score and Give Resources to the Simulator Mode

Write to the Experimental Log

Output Data Through the Parallel Port

Use the Game Script