Creating a Unit Entity

From C4 Engine Wiki

Jump to: navigation, search

Previous Section: Creating a Basic Navigation Camera

Creating a Unit Entity

It’s time to add an Entity to the game. For this tutorial, the unit Entity will be a tank. Generally, there are two parts to setting up an entity to be used in the game world: The world editor part and the code part. We’ll do the World Editor part first since it’s straightforward.

Download and unzip this COLLADA model file and place it in the <C4>/Import/RTS/ directory.

Now launch C4, create a new world (Ctrl + N). Click on World -> Import Scene… Navigate to the /RTS subfolder, and select the tank.dae file. Once you click on OK, the tank model should appear in the World Editor like this:


Image:rts_tank_import.jpg


The tank was created to be small, so that’s why it’s so small in the World Editor. Save the world as a model by clicking on World -> Save as Model… Navigate to the /RTS subfolder, and save the model as tank.mdl.

You can now open up this model in the Model Viewer by clicking on File -> Open Model… and selecting tank.mdl from the /RTS subfolder. The tank is untextured to keep it simple, and it’s fairly small, so you may have to move the camera a little lower to see it.


Image:rts_tank_model.jpg


Also, if you open up this model in the World Editor (By creating a new world and selecting World -> Insert Model… and selecting tank.mdl) you’ll notice a blue box contains the tank. Switching over to the Scene Graph View (right click on a viewport and select Scene Graph), you’ll see the following hierarchy:


Image:rts_tank_heirarchy.jpg


The Entity node represents a model, and the tank and turret nodes represent the geometries that we imported. The turret node was automatically made a subnode of the tank node because that is how it was parented in the 3D modeling program before it was exported to a COLLADA file. The tank node is made a subnode of the Entity node. The Infinite Zone node is NOT a part of the model, only the world, and it represents the root zone in the world.

Knowing the hierarchy of these nodes will later help in understanding the programming part.

Now let’s do the code part. The code will register the tank in a list of entities that can be placed in your world via the World Editor. (Where it may seem that we’re already done because we can import a model into the world, this method is only used for editing model files, and not for adding entities to the world).

Start off by creating 2 new files in your project for the tank-related code: Tank.h and Tank.cpp. This will contain the TankController along with a few constants that are used for entity and controller registration. Most of the functions are stubs that we’ll add to later, but this makes up the essential function overrides for creating a new Controller subclass. For the basics of creating a Controller, see the article Defining a Custom Controller.


<Tank.h>

#ifndef TANK_H
#define TANK_H

#include "C4Controller.h"
#include "C4World.h"

namespace C4
{
	enum
	{
		kControllerTank = 'tank'
	};

	enum
	{
		kEntityTank = 'tank'
	};

	class TankController : public Controller
	{
		private:
			TankController(const TankController& tankController);
			Controller *Replicate(void) const;
		public:
			TankController();
			~TankController();
			
			static bool ValidNode(const Node *node);
			void Pack(Packer& data, unsigned long packFlags) const;
			void Unpack(Unpacker& data, unsigned long unpackFlags);
			
			void Preprocess(void);
			
			void Move(void);
			void Travel(void);
	};
}

#endif


<Tank.cpp>

#include "Tank.h"
#include "RTS.h"

using namespace C4;

TankController::TankController() : 
	Controller(kControllerTank)
{
}

TankController::TankController(const TankController& tankController) : Controller(tankController)
{
	
}

Controller *TankController::Replicate(void) const
{
	return (new TankController(*this));
}

TankController::~TankController()
{
}

bool TankController::ValidNode(const Node *node)
{
	if(node && node->GetNodeType() == kNodeEntity)
	{
		return true;
	}
	return false;
}

void TankController::Pack(Packer& data, unsigned long packFlags) const
{
	Controller::Pack(data, packFlags);
}

void TankController::Unpack(Unpacker& data, unsigned long unpackFlags)
{
	Controller::Unpack(data, unpackFlags);
}
			
void TankController::Preprocess(void)
{
	Controller::Preprocess();
}

void TankController::Move(void)
{
}

void TankController::Travel(void)
{

}


NOTE: Notice that kEntityTank is added to the Tank header file. We don’t have to put it in Tank.h, but it will remind us that this entity type belongs to the TankController, especially since we’ve implemented the ValidNode function to attach to only Entity node types.

Okay, now for the actual registration. In the RTS class, we will register both the tank entity and the tank controller. (Don’t forget to add “#include “Tank.h” in RTS.h). Add the following private member variables:


<RTS.h>

private:
	EntityRegistration		tankEntityReg;
	ControllerReg<TankController>	tankControllerReg;

Construct the tank controller and the tank entity registration. In the initialization list of the constructor of the RTS class:

<RTS.cpp>

RTS::RTS(void) : Singleton<RTS>(TheGame),
	tankControllerReg(kControllerTank, "Tank"),	// <-- Add
	tankEntityReg(kEntityTank, "tank", kEntityPrecache, kControllerTank) // <-- Add


The constructor for tankControllerReg specifies the type to be kControllerTank and attaches the name “Tank” to it, which will appear in the list of controllers when the Node->Get Info dialog is shown.

The constructor for tankEntityReg specifies the type to be kEntityTank, the name “tank” is actually the path and resource name of the mdl file, which will appear in the Entities list in the WorldEditor. The entity is set to be precached, and kControllerTank is the default controller assigned to the entity when it’s placed in the World Editor.

At this point, the project should compile and run. Launch C4 and open up the rts world by typing “world rts/rts” in the console. If the Entities page is not showing in the page panel (you may have to scroll down to see if it’s there or not), click on Page -> Entities in the menu.

The Entities menu should now have “tank” listed:


Image:rts_entitieslist_tank.jpg


Click on “tank” and then click in any of the viewports to place a tank entity into the world. Then, click on Node -> Reset Transform to Identity in the main menu. This will snap the entity to the center of the playfield. Notice that the actual tank geometry does not appear in the world when we place the entity. Fear not! The tank will be initialized when the game starts, and the tank will appear. Try it! Click on World -> Save and Play World in the main menu and see for yourself.


Image:rts_tank_entity_in_game.jpg

Next Step

If all goes well, the new tank entity is in your world. We now have an entity that we can select, so the next step will be to handle the actual mouse selection in Mouse Input - Unit Selection