Rune is a third person action, combat, and adventure games with strong themes from Norse mythology. You play a young Viking trying to uncover the mysteries around recent attacks on your village. The game is played from third person perspective to make the melee combat with a huge variety of weapons more fun, as well as giving you a chance to fully appreciate and explore the complex world.

Post tutorial Report RSS Coding Tutorial - Part 2

Coding Tutorial - Part 2 Author: Charles "MrBlonde" Palmer Last Updated: April 26, 2001 at 02:46:03 PM

Posted by on - Intermediate Mapping/Technical

Originally posted here: Coding Tutorial part 2

Mirrored for archival purposes


Before you begin this tutorial I recommend that you read and get comfortable with the ideas at Unreal.epicgames.com. Also checkout the first tutorial.

NOTE: Be sure to download the test map.

When we get down to it...

Before we can really start on any of the game logic its always best to get a rough idea of what we want to achieve down on paper, so we can refer to it whilst coding (its very easy to sidetrack). For this mod we are going to need:

* Someway of telling whether someone if in the Hill Zone. - Not to hard the Unreal engine supports 'Zones' so we should be able to extend this to our needs.

* Ability to keep track of the number of seconds in the zone. - Again not hard as the Unreal engine has excellent multiplayer support infact a whole class, PlayerReplicationInfo is dedicated to storing information we need to send out. So we can also extend that.

* A visual indication of the above. - We can use the HUD and Scoreboard.

Let's Setup Our Variables:
Its always sensible once you have a map on paper or in your mind of what you want to do, to get some main variable down. Things we need to track for each Player usually and the best place to do this is in the PlayerReplicationInfo. So create a new file in your /cotm/classes/ directory called COTMPlayerReplicationInfo.uc into which we are going to place:

//==========================================================
// COTMPlayerReplicationInfo.
//==========================================================
class COTMPlayerReplicationInfo expands PlayerReplicationInfo;

var float TotalTime; //Total Time for each Player in the Zone
var float ZoneEnterTime; //Time the Player last entered the Zone
var float ZoneExitTime; //Time the Player last left the Zone
var bool bIsInZone; //Is the Player currently in the Zone?

replication
{
reliable if ( Role == ROLE_Authority )
TotalTime, ZoneEnterTime, ZoneExitTime, bIsInZone;
}

defaultproperties
{
}

Breaking it down, we firstly define the new classes and extend it from PlayerReplicationInfo which stores lots of information about each player. ie. name, score

//==========================================================
// COTMPlayerReplicationInfo.
//==========================================================
class COTMPlayerReplicationInfo expands PlayerReplicationInfo;


We then start to define variable's, these are values we can set later or allow people designing the levels to set (although there are none of the later here). Each variable is set in a specific way, firstly 'var' tells the compiler that we are declaring a variable. The next word is the variable type, here we have a float which is a number with decimals eg. 13.102 and bool which is short for Boolean which can only have values True or False. I've placed comments next to them so that when I come back to them as well as the descriptive variable name I know what they do. Comments of this type are a valuable tool when your programming, especially if a lot of people may be working with the code.

var float TotalTime; //Total Time for each Player in the Zone
var float ZoneEnterTime; //Time the Player last entered the Zone
var float ZoneExitTime; //Time the Player last left the Zone
var bool bIsInZone; //Is the Player currently in the Zone?


Replication, now heres a thorny issue, Replication basically says what gets sent from the server to the clients and visa versa. In this case we are using ROLE_Authority which means all the variables are sent across to the client from the server, we then list the variables. Explaining the whole of Replication and what to use when warrants a tutorial in itself, if you want to learn more then I suggest you take a look around a few of the Unreal coding sites, most of them provide a take on Replication.

replication
{
reliable if ( Role == ROLE_Authority )
TotalTime, ZoneEnterTime, ZoneExitTime, bIsInZone;
}

Finally we have no default properties to set, so leave it empty.

defaultproperties
{
}

Z is for Zone

First off lets take a look at the code for the Hill Zone. Create a new file in your /cotm/classes/ directory called HillZone.uc and in it place:

// ========================================================
// HillZone
//=========================================================
class HillZone expands ZoneInfo;

event ActorEntered( actor Other )
{
local COTMPlayerReplicationInfo PRI;

if(Other.IsA('PlayerPawn'))
{
if(PlayerPawn(Other).PlayerReplicationInfo.IsA('COTMPlayerReplicationInfo'))
{

PRI = COTMPlayerReplicationInfo(PlayerPawn(Other).PlayerReplicationInfo);
PRI.Z
PRI.bIsInZ
BroadcastMessage(PRI.PlayerName$" has entered.");
}
}

Super.ActorEntered(Other);
}

event ActorLeaving( actor Other )
{
local COTMPlayerReplicationInfo PRI;
local int InZoneTime; //for a better looking message

if(Other.IsA('PlayerPawn'))
{
if(PlayerPawn(Other).PlayerReplicationInfo.IsA('COTMPlayerReplicationInfo'))
{

PRI = COTMPlayerReplicationInfo(PlayerPawn(Other).PlayerReplicationInfo);
PRI.Z
PRI.TotalTime += (KOTH.ZoneExitTime - KOTH.ZoneEnterTime);
InZ - KOTH.ZoneEnterTime);
PRI.Z
PRI.Z
PRI.bIsInZ
BroadcastMessage(PRI.PlayerName$" stayed in the zone for "$InZoneTime$" seconds");

}
}
Super.ActorLeaving(Other);
}

defaultproperties
{
}


Now this is where things start to get interesting, after we have declared the classes name and also which class it inherits from notice we have an 'event'. Now in its simplest form the event ActorEntered would look like this:

event ActorEntered( actor Other )
{

}

An event is just a type of function albeit one that is run when something happens. For more on functions checkout the Unreal Engine Tech Pages.

Taking a closer look at the complete ActorEntered event.

event ActorEntered( actor Other )
{
local COTMPlayerReplicationInfo PRI;

if(Other.IsA('PlayerPawn'))
{
if(PlayerPawn(Other).PlayerReplicationInfo.IsA('COTMPlayerReplicationInfo'))
{

PRI = COTMPlayerReplicationInfo(PlayerPawn(Other).PlayerReplicationInfo);
PRI.Z
PRI.bIsInZ
BroadcastMessage(PRI.PlayerName$" has entered.");
}
}
Super.ActorEntered(Other);
}

It accepts one variable an actor called Other which is the object that entered the zone, now this could be anything any item that travels around the level will fire off this event when it enters the zone. So we need to filter the actors so we only do what we want with them. Luckily you can check the class of an actor entering a zone with the IsA function. This returns true if the class of the actor entering inherits or is the class we specify. In this instace we check that the actor entering is a Player, then we make sure its a Player using our newly made COTMPlayerReplicationInfo. Once we have determined this to be true the following code is then run.

PRI = COTMPlayerReplicationInfo(PlayerPawn(Other).PlayerReplicationInfo);
PRI.Z
PRI.bIsInZ
BroadcastMessage(PRI.PlayerName$" has entered.");

Firstly we grab the COTMPlayerReplicationInfo and place it in a local variable (one thats only set for the event) PRI this saves us typing large amounts again and again to access that information. We then set the time the Player entered the zone in his Replication Info, we also set the variable that determines whether he is in the zone to true and finally broadcast a message to all the players so they know which player just entered the zone. The $ sign is called a concation operator and is used to join variables together, so in the call to the BroadcastMessage function we join the Players name to the string " has entered.".

After this has all been done we make sure we call the previous version of the event from the previous class. This insures that we don't break anything the game wanted to do before we started changing the function.

Super.ActorEntered(Other);

Now lets take a look at the ActorLeaving event, we start off by declaring two local variables one to hold the Player's Replcation Info and the other to hold an integer (whole number) value of the time the Player was in the zone so our broadcast message will look nicer.

event ActorLeaving( actor Other )
{
local COTMPlayerReplicationInfo PRI;
local int InZoneTime; //for a better looking message

if(Other.IsA('PlayerPawn'))
{
if(PlayerPawn(Other).PlayerReplicationInfo.IsA('COTMPlayerReplicationInfo'))
{

PRI = COTMPlayerReplicationInfo(PlayerPawn(Other).PlayerReplicationInfo);
PRI.Z
PRI.TotalTime += (KOTH.ZoneExitTime - KOTH.ZoneEnterTime);
InZ - KOTH.ZoneEnterTime);
PRI.Z
PRI.Z PRI bIsInZ
BroadcastMessage(PRI.PlayerName$" stayed in the zone for "$InZoneTime$" seconds");

}
}
Super.ActorLeaving(Other);
}

Again we check to make sure we do the right actions to the right actor leaving the zone. Once this has been established we shorten the call to the Player's Replication Info and get the time that the Player left the zone (since the start of the level) we then calculate the length of time the Player has been in the zone and add it to his total time spent in the zone. Then all the COTMPlayerReplication variables are reset ready for the next time the Player enters the zone and finally a message is broadcast to all players to show how long the Player stayed in the zone.

A couple of adjustments then time to Compile!

Open up the COTM.uc file and after the class is declared and before the defaultproperties add the following to ensure that when the game is run the Player starts with our new COTMPlayerReplicationInfo.

event playerpawn Login
(
string Portal,
string Options,
out string Error,
class SpawnClass
)
{
SpawnClass.Default.PlayerReplicati
return Super.Login(Portal, Options, Error, SpawnClass);
}

Also in the defaultproperties remove the following lines (just for the moment).

ScoreBoardType=Class'COTM.COTMRuneScoreBoard'
HUDType=Class'COTM.COTMRuneHUD'

Save everything and lets compile what we've got!

But first, head over to your /system/ directory (from the Rune root) and edit the Rune.ini file. Head down to the [Editor.EditorEngine] section and underneath "EditPackages=RuneServerAdmin" add.

EditPackages=COTM

This means that the compiler will now compile our code. You may now want to open a MSDOS prompt and change to your Rune system directory and type "ucc.exe make" or create a shortcut to do the same thing. The compiler should now run and hopefully bring up no errors (it didn't for me anyway) and a new file called COTM.u should appear in the system directory. Thats it you've now compiled, if an error is generated it will give the line number and a general description of the error, most likely you missed a closing bracket or a semi-colon.

Let's Run This Puppy!

Well its very basic at the moment and we can't see any of the player's scores but what the heck we may as well see if our zone works. z0gre kindly made a test map for me which you can download here which includes a pre-made zone. Download it and place it in you /maps/ directory from the Rune root.

Now we have all we need to get running, except one thing we need to make sure the new Game Type will show up in the game menus. Create a new file in the system directory called COTM.int and place the following in it.

[Public]
Object=(Name=COTM.COTM,Class=Class,MetaClass=Engine.GameInfo)

Preferences=(Caption="Chieftan of the Mound",Parent="Game Types",COTM.COTM,Immediate=True)

[COTM]
GameName=Chieftan of the Mound

Now we are ready to startup Rune and begin a new multiplayer game with our GameType enabled and run in and out of the zone and you should see the messages. But wait! There are a few bugs, firstly if you die in the zone your time will still increase (although entering and exiting will fix it) and also there is no way (currently) to see your score. How will we fix this? Tune in next week to find out.

Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: