Game Development Community

ZaBingo! Torque X Game Development Diary 2

by John Kanalakis · 06/02/2009 (4:07 pm) · 8 comments

Focus on Core Game Play

By now the foundation for the game is in place: I have a paper sketch of the game, I’ve identified the game objects, components, and services, I have my art assets gathered or created, and I have the game scaffolding created and running. Now my focus is on getting the core game play working. It doesn’t need to look great, but it does need to model the final game play. For this game, that means I need to get the following working:

1. Creating new game cards
2. Picking and displaying, and dropping new tiles
3. Adding Player Input
4. Determining who claims the tile
5. Dynamically creating cards
6. Moving Tiles over the Tilemap

Creating new game cards

After creating several (not all) tiles, I added them to the game project as materials. I drag them into the scene using Torque X Builder 2D and have them sized to fit within a cell of the player’s tilemap card. I put all of the tiles outside of the viewable scene area, give them a name using a naming convention (tileN), disable collisions, and then mark them as templates. Also go ahead and add the empty MoveToComponent to each tile’s component collection.

Next, above the game scene, I add two spawners. For now, I’m just pointing them to the first two tile templates, so I can see where the spawners are. Below the two spawners are two blank scene objects, named target0 and target1. These are simply markers that will tell the dropping sprites where to go within the scene, using the MoveToComponent.

I also add the two player tilemaps for the cards and temporarily set them with placeholder cards. Now, this is starting to look like the game, sans-GUI.

www.envygames.com/share/zabingo_layout.jpg

C# 3.0 Tip: Auto-Properties

In case you weren’t aware, the language spec for C# 3.0 introduces auto-implemented properties. So, instead of defining the full body of a property accessor, you can use a shorter form that does not require declaring a private variable. So… instead of this:
int _myValue;
public MyValue
{
   get { return _myValue; }
   set { _myValue = value; }
}
You can just define everything like this:
public MyValue
{
   get;
   set;
}
Since you don’t declare a private field, your code is forced to always interact with the public property. This forced abstraction is great since you can later change the behavior of the get/set accessors without breaking any code.

Creating the MoveToComponent

This component simply moves its owning game object from its starting position to a designated target at a specified speed. I’ll use this component to have new tiles slide into the scene and then slide into their place over the tilemap (rather than just instantly warp them into position). The guts of the component are in the ProcessTick() method. This is what will move the game object from its current position to a target Vector2 position.
public virtual void ProcessTick(Move move, float dt)
{
   //if enabled, then travel from the start position to the end position at the specified speed
   if (Enabled)
   {
      if ((int)Vector2.Distance(SceneObject.Position, TargetPosition) > 5) 
      {
         //determine the direction to move towards
         Vector2 direction = Vector2.Subtract(TargetPosition, SceneObject.Position);

         //turn this into a unit vector
         direction = Vector2.Normalize(direction);

         //convert the direction into a movement speed 
         Vector2 movement = Vector2.Multiply( direction, new Vector2(Speed));
                    
         //add the movement to the current position
         SceneObject.Position = Vector2.Add(SceneObject.Position, movement);
      }
      else
      {
         Enabled = false;
      }
   }
}

You can download the source from here: www.envygames.com/share/MoveToComponent.cs.txt remove the .txt extention after download

Creating the PlayerInputComponent

Next, I create a new new player component and then attach it to each of the two newly added blank scene objectsat the top of the scene. These components will setup input maps that capture and process player input. Essentially, the _OnRegister() method calls a new method, named _SetupInputMap().
private void _SetupInputMap(TorqueObject player, int playerIndex, String gamePad, String keyboard)
{
    // Set player as the controllable object
    PlayerManager.Instance.GetPlayer(playerIndex).ControlObject = player;

    // Get input map for this player and configure it
    InputMap inputMap = PlayerManager.Instance.GetPlayer(playerIndex).InputMap;

    int gamepadId = InputManager.Instance.FindDevice(gamePad);

    if (gamepadId >= 0)
    {
        inputMap.BindCommand(gamepadId, (int)XGamePadDevice.GamePadObjects.Up, BuzzIn, null);
        inputMap.BindCommand(gamepadId, (int)XGamePadDevice.GamePadObjects.Down, BuzzIn, null);
        inputMap.BindCommand(gamepadId, (int)XGamePadDevice.GamePadObjects.Left, BuzzIn, null);
        inputMap.BindCommand(gamepadId, (int)XGamePadDevice.GamePadObjects.Right, BuzzIn, null);

    }


    int keyboardId = InputManager.Instance.FindDevice(keyboard);
    if (keyboardId >= 0)
    {
        if( PlayerNumber == 0 )
            inputMap.BindCommand(keyboardId, (int)Keys.LeftControl, BuzzIn, null);
        
        if (PlayerNumber == 1)
            inputMap.BindCommand(keyboardId, (int)Keys.RightControl, BuzzIn, null);
    }

}

You can download the source from here: www.envygames.com/share/PlayerInputComponent.cs.txt remove the .txt extention after download

The BuzzIn(), called by this input map, method is defined in the my Game class, so the event handler within this component looks like this:
void BuzzIn()
        {
            Game.Instance.Referee.BuzzIn(PlayerNumber);
        }

Determining who claims the tile

Since two players can buzz-in simultaneously, a new small class is added to serve as the arbiter. The Buzzer class declares a delegate, which is essentially a C# equivalent to a function pointer. A public delegate variable of type OnBuzzDelegate is created so that any other object within the game can “listen” for the Buzz-in event.

public delegate void OnBuzzDelegate(int Winner);

public OnBuzzDelegate OnBuzz;

        public void BuzzIn(int PlayerNumber)
        {
            if (WinningBuzzer == -1)
            {
                WinningBuzzer = PlayerNumber;

                if (OnBuzz != null)
                    OnBuzz.Invoke(WinningBuzzer);
            }

            WinningBuzzer = -1;
        }

Dynamically creating cards

Since each game should have a unique bingo card, I create them randomly at the start of the game. This method takes a reference to a tilemap that will represent the player’s bingo card. The ref parameter attributes indicates that this method is receiving a reference (not a copy) of the original tilemap, so any changes to this tilemap within this method is having a real-time effect.

The method iterates through each cell, column-by-column, row-by-row. I pick a random number (that is seeded by the current time) and using this number, I build the name of the card material. When I created each of my card cell materials, I used the naming convention: “card_” number “Material”. Then, I create a new T2DTileType object and set its material name, then attach it to a new T2DTileObject and use the SetTileByGridCoords() method to set this new tile into the tile layer.
public void CreateCard(ref T2DTileLayer PlayerTilemap, int Columns, int Rows)
{
    Random randomizer = new Random(DateTime.Now.Millisecond);
    int result = 0;

    for (int row = 0; row < Rows; row++)
    {
        for (int column = 0; column < Columns; column++)
        {
            //randomly choose a tile number
            result = randomizer.Next(_maximumNumberOfTiles);

            //add to the list of known tiles used
            _listUsedTiles.Add(result);

            T2DtileType _tileType = new T2DtileType();
            _tileType.Material = TorqueObjectDatabase.Instance.FindObject<GarageGames.Torque.Materials.RenderMaterial>("card_" + result + "Material");

            //create a tile object and attach it to the tilemap
            T2DtileObject tile = new T2DtileObject();
            tile.TileType = _tileType;

            //set the new tile into the tilemap
            PlayerTilemap.SetTileByGridCoords(row, column, tile);
        }
    }
}

Moving Tiles over the Tilemap

As the player correctly picks a new tile that enters the scene, I use the MoveToComponent to slide it into place over the correct cell within the tile layer for a smooth effect. All I need is a Vector2 that identifies the position where the object should slide to . The easiest way to do this is to get a reference to the T2DTileObject using the cell position within the tile layer:

T2DTileObject _tileObject = PlayerTileMap.GetTileByGridCoords( row, column);

Next, you can get the world position vector from the tile object using the GetWorldPosition() method and passing in a reference to the tile layer that contains the tile.

Vector2 result = _tileObject.GetWorldPosition(PlayerTileMap);

Now, with the position in hand, you can use the MoveToComponent to move the scene object into position over the tilemap.

Conclusion

Okay, that’s enough of the code over-dose. In the next post, we’ll add the user intface, add some sound, and improve the graphics to shape up the game. Then, in the last post, we’ll prepare the project for the Xbox 360 and get this game into public play testing.

Diary History

#2 Focus on Core Game Play
#1 Designing and Preparing the Game

John Kanalakis
www.envygames.com

About the author

John Kanalakis is the owner of EnvyGames, an independent game development studio in Silicon Valley that produces games and tools for Xbox 360, Windows, and the Web.


#1
06/02/2009 (5:07 pm)
//Man I love TXNA wish it had the real Torque3D tech. Any hope?

Public String MyThoughts { get; set; }
...
MyThoughts = "Great Blog!";
#2
06/02/2009 (6:11 pm)
LOL! Thanks, OmegaDog. I agree, I'd also like to see some new features added into the stock engine. I think we have a pretty good base to build upon.

The next post will be on GUI and Audio for the game, and then we're off to Xbox 360 deployment and play testing on Xbox Live Communnity Games. Is there something specific you would like to see in either of those posts?

John K.
www.envygames.com
#3
06/02/2009 (6:39 pm)
Input Input, going to have to do all this next year I am going to publish PAINFRAME in T3D and TX3D so it can get Xbox 360 play. I very interested in this Diary and your next Diary on 3D Game.

1? - Are you going to register and/or trademark or copyright your game before releasing.
#4
06/03/2009 (9:35 am)
PAINFRAME is looking great, by the way! I like the video progress with T3D. I do want to see the same level of features added to Torque X as soon as possible, so I'm sure they will make it in eventually. My only concern is that T3D is so different from Torque X (different language and class library), that porting your game between them is going to be way too much work. I definitely suggest that you choose just one toolset and focus 100% to make your game awesome with that toolset only.

What I really like about auto-properties is that I can be lazy again. So many times I want to quickly expose a class variable and want to say WTF, just make it public, instead of going through the effort of adding the proper get/set wrappers. Now, I can quickly turn a private field into a public property by just adding the get;set; text.

John K.
www.envygames.com
#5
06/03/2009 (12:14 pm)
Thanks, and thanks for the insight. Will definitly complete T3D totally before port and maybe by then GG may have TX3D upto par with T3D.

#6
06/04/2009 (6:49 am)
Ah more great work John.
I love the stuff you produce, I keep starting and stopping with developing for TX2d as I have trouble with getting a working framework that does all the bits it needs to, checking controllers, memory cards, signin/singout etc.

Reading up on what your doing is certainly inspiring.
#7
06/04/2009 (9:43 am)
Thanks Gavin, that really is a pain, I agree. I have some ideas for making that easier. I'll try to cover as much as possible in the post about Xbox deployment.

John K.
www.envygames.com
#8
06/04/2009 (10:37 am)
Sounds good to me. I will certainly keep a watch on your work. In the meantime i guess i will carry on trying to build a functional framework for xbox games :-)