| Home Page | Recent Changes | Preferences

Dynamic Array

UT2003 dynamic arrays

Unreal Tournament 2003 has full native support for dynamic arrays. The full declaration and usage syntax is noted at Variable Syntax (Should I replicate that information here? – Up to you. :) )

Picking a (nearly) random commonly used UC file, the following examples are derived directly from the Console class. Comments are added in to try and clarify things

//example: Declares the dynamic array, type and name.
var array<string> BufferedConsoleCommands;

In the DelayedConsoleCommand function we find a good example of obtaining the number of elements in an existing array through the use of the length properthy and adding a new element.

function DelayedConsoleCommand(string command)
{
      //Adding a new element is easy, we simply increase the size and add a new value to it
    BufferedConsoleCommands.Length = BufferedConsoleCommands.Length+1; //adds another (empty) element to the array.
    BufferedConsoleCommands[BufferedConsoleCommands.Length-1] = Command; //assigns a value to the new element.

      //It should be noted that the Variable Syntax page mentions that simply assigning a value to a non-existent element
      //works just as well.
      //BufferedConsoleCommands[BufferedConsoleCommands.Length] = Command; //adds a new element and assigns a value.
      
}

In the Tick event of the class, we find an example of removing an element.

simulated event Tick( float Delta )
{

    while (BufferedConsoleCommands.Length>0)
    {
        ViewportOwner.Actor.ConsoleCommand(BufferedConsoleCommands[0]);

            //the following removes a specified element from the dynamic array.
            //the first number (0) defines the position within the array starting at element 0,
            //the second number (1) defines how many elements to remove from the array.
        BufferedConsoleCommands.Remove(0,1);
    }
}

I'm a strong VB user (GASP!! o_O ) so here is a short comparison for those VB nuts:

The UT2003 dynamic array is much closer to the Visual Basic idea of a Collection instead of the VB dynamic array with only very minor behavior differences. It appears that all of the standard VB Collection functions are represented here with the exception of the Add function. Instead of using an Add function, the UT2003 dynamic array is much closer to the VB Redim Preserve for VB arrays. Thinking of the UT2003 dynamic array as a collection will make it a little bit easier to digest for those who are familiar with the VB language.

To be honest though, I can't imagine a pure VB programmer actually wanting to cut their teeth on a C based OOP scripting language for a game. ;)

Wormbo: Uhm... I did. o_O Anyway, would it be better to move the VB stuff to UnrealScript for Visual Basic Programmers?

SavannahLion: Cool! So there is a strong VB programmer that hangs out here :p I suppose the little notation about VB should be moved, but not the whole dynamic array thing, that would be the last place anybody trying to look up dynamic arrays for UT2003 would look. I'll look in the morning (sometime later than noon) when I'm not so drained :( and I'll scratch all these comments away? Looks kind of messy...

Note: Remind me to build a nice class with a working example to encourage copy & pasting. Too late at night (12:23 AM) to toy with it. I shouldn't leave it for too long, don't want anybody beating me to the punch :p

Forgot my cookie....

Dynamic Arrays as Config Variables

Dynamic arrays can be initialized from .ini files by adding the config keyword to their declaration. Given the following class...

class DynamicArrayTest extends Object
  config;

var config array<string> MyDynamicArray;

...the corresponding .ini entries look as follows:

  [DynamicArrayTest.DynamicArrayTest]
  MyDynamicArray=first entry
  MyDynamicArray=second entry
  MyDynamicArray=third entry

Note the lack of array indices.

If you leave out the MyDynamicArray, like this:

  [DynamicArrayTest.DynamicArrayTest]

the result will be an empty MyDynamicArray, even if you have default values set in the class' defaultproperties block


UT custom dynamic arrays

This is an example of Scripting Operators.

Always wanted to use those elusive array<>'s, but found the GetPropertyText/SetPropertyText interface to be a problem? This is where operators come in. Here I provide a simple interface to a dynamic array of integers (although this is easily adaptable to support various types). You set elements of a dynamic array with 'Array << (Index:Data)' (totally arbitrary syntax I made up), and access an element with 'Array<Index>'.

class TestDynArray extends CommandLet;
// Our struct to store the index/data pairs created using the : operator
struct SetGroup
{
  var int Index;
  var int NData;
};

// The left side of the Array construct... the one that actually does something.
final operator(50) int < ( DynArray A, int B )
{
  return A.Get( B );
}

// The right side of the Array construct... does absolutely nothing, it's just there to make the syntax pretty (complete the brackets).
final postoperator int > ( int A )
{
  return A;
}

// Sets an element in a dynamic array, taking a index/data pair as the right side.
final operator(50) DynArray << ( out DynArray A, SetGroup B )
{
  A.Set( B.Index, B.NData );
  return A;
}

// Creates a index/data pair
final operator(23) SetGroup : ( int A, int B )
{
  local SetGroup C;
  C.Index = A;
  C.NData = B;
  return C;
}

// Just a test function to show that we can use all sorts of expressions within the index/data pairs
function int TestFunc()
{
  return 10;
}

function int main( string parm )
{
  local DynArray Arr;
  local int i;
  local setgroup g;

  // Instantiate a DynArray
  Arr = new class'DynArray';

  // Set some elements
  Arr << (5:78);
  Arr << (1:30);
  Arr << (TestFunc():69);

  // And log them
  Log( Arr<5> @ Arr<1> @ Arr<TestFunc()> );
  return 1;
}
// Interface to dynamic arrays. Evil.
class DynArray extends Object;

var array<int> Data;
var int CacheData[1024];
var int Num;
var bool bCached;

// Parse the elements out of a string like (2,3,5,2,4,6,2), and store them in our cache
function Recache()
{
  local int i;
  local string Nightmare;
  local int NextPos;
  local int z;
  Num = 0;
  Nightmare = GetPropertyText("Data");
  Nightmare = Right( NightMare, Len(NightMare)-1 );
  Nightmare = Left( NightMare, Len(NightMare)-1 );
  for(i = InStr( Nightmare, "," );i > 0;z++)
  {
    CacheData[Num++] = int( Left(Nightmare, i) );
    Nightmare = Mid( Nightmare, i + 1, Len( Nightmare ) );
    i = InStr( Nightmare, "," );
    if ( i == -1 && Len(NightMare) > 0 )
      CacheData[Num++] = int( Nightmare );
  }
  bCached = true;
}

// Set an element by building a string like (3,3,5,9), and recache.
function Set( int Index, int Data )
{
  local string Build;
  local int i;
  Recache();
  CacheData[Index] = Data;
  if ( Index > Num-1 )
    Num = Index+1;
  Build = Build $ "(";
  for(i=0;i<Num;i++)
  {
    Build = Build $ CacheData[i];
    if ( i != Num-1 )
      Build = Build $ ",";
  }
  Build = Build $ ")";
  SetPropertyText("Data", Build);
  bCached = true;
}

// Get a cached element
function int Get( int Index )
{
  if ( !bCached )
    Recache();

  return CacheData[Index];
}

Mychaeel: The new engine used for UT2003 comes with full dynamic array support, by the way.

Dma: Very ugly.... :-)

Mychaeel: Eh...? What? Engine-level support for dynamic arrays or the implementation above? I, personally, find the implementation above interesting at least from a theoretical point of view; whether it's really worthwhile to use the above implementation rather than a linked list in engine versions that don't have native support for dynamic arrays is a different matter though.

Mychaeel: What strikes me though is that using a static array as a cache (certainly a good idea from a performance point of view) somewhat defeats the purpose of dynamic arrays.

SavannahLion: Mychaeel (or anyone else who chooses to answer for that matter), is there an example of the dynamic array in the UT2003 engine?

El Muerte TDS: just check an avarage piece of UT2003 UScript for that, here's an [example], all the vars with array<string> are dynamic

SavannahLion: Thanks for being patient El. I also found it at Variable Syntax as well, third from the bottom of a search. Should've been an obvious place to be looking in, but I guess it didn't click in my mind. :rolleyes:

Tarquin: Now you've found it, Savannah, you might want to add a link to the top of this page. You could paste in an example of UT2003 code too, for the next person who comes looking. :) Of course, anyone could do this ...

Foxpaw: One of the code snippets mentioned assigning a value to the Length+1 element of a dynamic array to add a new element - this should actually read just Length. Length is the total number of elements, but the element numbering starts from 0, thus Length is the first unused element. I realized this when my dynamic arrays all became twice the size they should have been and accessed nones filled the log up to 99.7 MB. :P I've altered the snippet to show the correct method.

Dante: There's also a problem with dynamic arrays in netplay which I consider a engine bug. And NO I don't mean replication of them lol. I had an placeable actor in a map, thought not static. I used postNetReceive to change the skins clientside to some skins which the mapper could setup on the actor. While postNetReceive was called, the dynamic arrays skins, redSkins and blueSkins (my 2 new ones) were empty! So I thought making them bstatic would work, but then postNetReceive wasn't called anymore (which I consider correct engine behaviour). Now I come to the point why I consider it a bug, I set bNoDelete to true and it worked. Well to be honest, I think it's not really a bug but something they hacked in. That leads to 2 possible conclusions:

  • Dyn. arrays are a bit buggy
  • Because the engine might leak memory when the actor is destroyed due to some initialization code which sucks. Of course I might have added stuff to the arrays later which would be removed when deleting.

Daid303: Using SomeArray.Length ++; seems to crash the engine with a GPF.

Wormbo: The language reference explicitely tells you not to do that. The only operation you should be doing with the Length property is assignment, this does not include combined assignment operators like +=!

Daid303: Then, why does the compiler like it? :S

El Muerte TDS: because the syntax is correct, it's just that there is a side effect during the execution of it.

Daid303: Oh, and another thing I forgot, you can't create an element in a dynamic array of strucs, by assigning something to 1 of the struc elements. (bit hard to explain, I hope you guys get it :P)

struct SomeStruct
{
  var string Name;
  var int Value;
}

var array<SomeStruct> AnArray;

// Compiles ok, but gives an array out of bounds error in the logs, and doesn't work.
function Invalid()
{
  AnArray[AnArray.Length].Value = 5;  
}

// But this is okay.
function Valid()
{
  local SomeStruct NewStruct;

  NewStruct.Value = 5;
  AnArray[AnArray.Length] = NewStruct;  
}

//This also works.
function AlsoValid()
{
  AnArray.Length = AnArray.Length + 1; //DON'T USE ++
  AnArray[AnArray.Length-1].Value = 5;
}

Foxpaw: Can I ask how adding one to the length and then setting the Length-1 element is better than just using the Length element? I'm not sure why you changed that part of the example.

Daid303: I didn't say it was better, it's just another way how to do it ;) and I didn't change it, but added it. It's more memory friendly :P (not that that really matters in this case) but it could also be faster.

Foxpaw: How is that more memory friendly? Also, if anything, it would be slower, because you have to do an addition operation, only to do a subtraction operation right after it.

Daid303: well:

More memory frendly because it doesn't make an internal stuct (if your stuct is really large it could matter)

Well, I was thinking it would be faster because it only assings 1 part, not a whole stuct. But i'm not sure about that.

-But then again, I could be totaly wrong :)

Wormbo: If there's something in UnrealScript you don't have to think about, it's the execution speed and/or memory usage of relatively simple instructions. UnrealScript runs in a virtual machine, so the performance improvements you can achieve this way are almost non-existant. Optimizations only make sense for iterative and recursive code structures.

Foxpaw: Ahh, now I see where the memory savings comes in. Depending on the size of the struct you might even gain a speed increase like you said, but that depends on how smart the Unreal Virtual Machine is, or rather, how intelligent UCC is.

Daid303: Like I said, I could be totaly wrong :P but I did some assembly some time ago, for grafical calculators, and there is speed/size everything ;)

Foxpaw: It's really hard to say without seeing the source for the Unreal Virtual Machine. I suspect that creating a new element in the array likely allocates the space and initializes the entire struct, but it may not do the intiialization when you assign an element directly. So, depending on whether or not it does a redundant initialization there or not, would determine which method runs faster.


Category To Do

The Unreal Engine Documentation Site

Wiki Community

Topic Categories

Image Uploads

Random Page

Recent Changes

Offline Wiki

Unreal Engine

Console Commands

Terminology

Mapping Topics

Mapping Lessons

UnrealEd Interface

Questions&Answers

Scripting Topics

Scripting Lessons

Making Mods

Class Tree

Questions&Answers

Modeling Topics

Questions&Answers

Log In