Unexceptional.Net: Dynamic Content Design

Description:

The dynamicContent.cs script file holds all the neccessary functions for handing the dynamic placement and generation of the world. It has the functions that are called to fill blocks of the world randomly as well as to draw the roads and add neccessary triggers.

The trigger.cs script has the trigger code that is used to toggle the player relocation (at the edges of the screen) and generation of the world as the player moves through it.

How it Works:

The user is able to move in an "infinite" space through the use of a pacman like system. That is, the user is actually restricted to a small square area that they can move through - when the user reaches an edge of this square, he is teleported to the opposite side (from top to bottom, left to right, etc.). As this is occuring, the square area is dynamically changing such that the user is not continuously seeing the same area. This gives the illusion of an infinte space. All of this is accomplished using a psudo-random algorithm. Using this grants us the ability to ensure that the world is consistent - that is, when the user retraces his steps, the world he already walked through will be the same.

Techinical Specifics:

In order to achieve the pacman like behavior, triggers were placed at the edges of the square area that the user can move through. When the user enters one of these triggers, he is instantly teleported to the opposite edge of the screen. The user's movement continues exactly as it was before entering the trigger.

In order to maintain the illusion of the infinite space and hide the pacman like behavior, one needs to ensure that when the user reaches an edge (but before he enteres the trigger) he can see exactly what he will see once teleported to the opposite edge (the world should not suddenly change before the user). To achieve this, a mirroring system is used where, essentially, the navigable square is copied onto the edges of it such that when the user is standing at the top of the navigable square looking upward, he will be staring at a copy of the bottom of the square. Then, when the user moves upward into the trigger and is teleported to the bottom of the square, he will be looking at exactly the same thing. Optimizations were put in place such that the entire square is not copied to every edge, but instead, only the appropriate parts (the portions of the square that would be within the users view).

Finally, in order to have a dynamically changing world, triggers were placed along the roads. As the user enters one of these triggers, the blocks in the world that are just beyond the users view are redrawn. A block's content is determined by the psudo-random algorithm that is seeded with the block coordinate of the particular block that is being redrawn. A block's coordinate is calculated based on the users position in the world.

Script Specification

dynamicContent.cs:

Member Variables:

Name: $numGridsPerBlock
Description: Number of grid spots in a given block (Each block is 3x2 = 6 total grid spots)

Name: $numBlocksPerRow
Description: Number of blocks per row in the world space

Name: $numLogicalBlocksPerRow
Description: Number of blocks per row in the navigable world space

Name: $numBlocks
Description: Number of total blocks in the world

Name: $blockWidth
Description: Width of a block in the Torque coordinates

Name: $blockHeight
Description: Height of a block in the Torque coordinates

Name: $roadWidth
Description: Width of a road in the Torque coordinates

Name: $gridWidth
Description: Width of a grid spot in the Torque coordinates

Name: $gridHeight
Description: Height of a grid spot in the Torque coordinates

Name: $numGridsPerBlockRow
Description: Number of grid spots per row in a block

Name: $playerBlock
Description: Array used to keep track of which relative block the user is in

Functions:

Name: createPlacedInterior(%index, %name, %x, %y, %z)
Description: Draws / Replaces an object at the given coorditaes and stores it in an array at the given index.
Parameters:
%index: Index in array where the object should be stored
%name: Filename of the object to be placed.
%x: x-coordinate of the position
%y: y-coordinate of the position
%z: z-coordinate of the position
Returns: Nothing

Name: deleteInterior(%index)
Description: Deletes the drawn object at the given index position
Parameters:
%index: Index in array of the object to be deleted
Returns: Nothing

Name: fillBlock(%index)
Description: Randomly fills the given block based on the users position
Parameters:
%index: Block number to be filled
Returns: Nothing

Name: getInteriorArrayIndex(%blockNumber, %position)
Description: Gets the array index of a particular grid spot in a particular block
Parameters:
%blockNumber: Number of the block
%position: Number of the grid spot
Returns: Array index

Name: getRealFromLogicalBlock(%index)
Description: Converts from a logical block index (the navigable blocks) to a real block index (all drawn blocks in the world - including mirrored blocks)
Parameters:
%index: Logical block number
Returns: Array index

Name: fillWorld()
Description: Intilizes all variables and builds the initial world
Parameters: None
Returns: Nothing

Name: drawRoads()
Description: Draws the roads of the world
Parameters: None
Returns: Nothing

Name: drawInteriorInstance(%fileName, %x, %y, %z, %rotation)
Description: Draws an object at the given coordinates with the given rotation
Parameters:
%fileName: Filename of object to be drawn
%x: x-coordinate of the position
%y: y-coordinate of the position
%z: z-coordinate of the position
%rotation: Rotation of the object
Returns: Reference to the drawn object

Name: getX(%index)
Description: Calculates the Torque X coordinate of a grid spot corresponding to an array location
Parameters:
%index: Index of the grid spot in the array
Returns: x-coordinate

Name: getY(%index)
Description: Calculates the Torque Y coordinate of a grid spot corresponding to an array location
Parameters:
%index: Index of the grid spot in the array
Returns: y-coordinate

Name: getBlockX(%playerX, %blockIndex)
Description: Calculates the relative x coordinate of a particular block in the navigable space based on the players location
Parameters:
%playerX: Player's x coordinate
%blockIndex: Block number (in the navigable space)
Returns: x-coordinate

Name: getBlockY(%playerY, %blockIndex)
Description: Calculates the relative x coordinate of a particular block in the navigable space based on the players location
Parameters:
%playerY: Player's y coordinate
%blockIndex: Block number (in the navigable space)
Returns: y-coordinate

Name: mod(%value, %divisor)
Description: Calculates the modulus of a given value with a given divsor
Parameters:
%value: Number to be modded
%divisor: Number to mod by
Returns: %value mod %divisor

Name: integerDivision(%value, %divisor)
Description: Performs interger division (disregarding the remainder)
Parameters:
%value: Number to be divded
%divisor: Number to divide by
Returns: %value / %divisor without the remainder

Name: addTriggers()
Description: Adds all the needed triggers to world
Parameters: None
Returns: Nothing

Name: buildCopyLookUpTable()
Description: Constructs an array which maps a navigable block to all the blocks in needs to be mirrored to
Parameters: None
Returns: Nothing

Name: fillBuildingArray()
Description: Fills an array with all the possible buildings that can be placed (Buildings have multiple entries depending on their desired frequency of appearance)
Parameters: None
Returns: Nothing

Name: randomSeed(%x, %y)
Description: Seeds the random number generator with the given values
Parameters:
%x: x-value for seeding
%y: y-value for seeding
Returns: Nothing

Name: randomGenerate(%limit)
Description: Generates a random integer between 0 and %limit
Parameters:
%limit: Maximum value of the random number
Returns: Random integer

Name: absoluteValue(%value)
Description: Calculates the absolute value of a given number
Parameters:
%value: Number to take the absolue value of
Returns: absolute value of %value

ENGINE CHANGES:

Only 1 change needed to handle null characters by unexceptional.ner custom server. Change made within "tcpObjet.cc".
(This file will need to be changed again if a new build that handles lighting better is used.)

File located in: SDK\engine\game\net\

ConsoleMethod( TCPObject, sendWithNull, void, 3, 0, "(...)"
              "Parameters are transmitted as strings, one at a time.")
{
   for(S32 i = 2; i < argc; i++)
      object->send((const U8 *) argv[i], dStrlen(argv[i]) + 1);
}

COMMON PATHS/FILES:

example\fps\client\client.cs
example\fps\client\scripts\client.cs
example\fps\client\scripts\dynamicContent.cs
example\fps\client\scripts\getFile.cs
example\fps\client\scripts\getNewFiles.cs
example\fps\data\interiors\maps (downloaded structures)

DEV TIPS:

To load scripts in the cmd shell:

'~' to bring up cmd shell
exec("fps/client/scripts/getNewFiles.cs");
ANY function call now available to execute (ex: 'GetNewFiles();')