WOWCube Docs logo
WOWCube Docs
Mission Control
Section Shortcuts
APIExamplesSourceWOWConnectChangelog
Filters
SDK and language defaults persist in cookies.
SDK version
Navigation Tree
Collapsed by default, focused on the active path.
Made byMcKay Seamons
GitHub
  1. Home
  2. Docs
  3. Examples
  4. Strings and Arrays
Mission NodeSDK 6.3PawnbasicsProject Included

Strings and Arrays

Arrays In the previous examples you have learned how to use varialbles for counting and measuring. You have been using one variable to count seconds, another...

Examples / SDK 6.3 / Pawn / basics

Arrays

In the previous examples you have learned how to use varialbles for counting and measuring. You have been using one variable to count seconds, another one for frames per second ratio and so on.

But what if you have got a problem that requires a lot of variables to solve?

What if you need tens of variables to hold some temporary values?

In that case, you should use arrays.

An array is a data structure consisting of a collection of element values, each identified by at least one array index. An array is stored such that the position of each element can be computed from its index

Arrays have many applications: they are used to implement mathematical vectors and matrices, as well as other kinds of rectangular tables.Applications use one-dimentional arrays for storing data records. Arrays are also used to implement other data structures, such as lists, queues and strings. Such data structures are used in game applications extensively. It is an essential part of nearly any game application.

When data objects are stored in an array, individual elements are selected by an index that is a non-negative integer. Indices are also called subscripts.

The first element of the array is indexed by subscript of 0. This leads to simpler implementation where the subscript refers to an offset from the starting position of an array, so the first element has an offset of zero.

Arrays can have multiple dimentions too. Thus, it is not uncommon to access an array using multiplt indices.

For example, a two-dimentional array A with three rows and four columns provides access to the element at the 2nd row and 4th column by the expression A[1][3].

Thus, two indices are used for a two-dimentional array, three for a three-dimentional array, and n for n-dimentional one.

Arrays in Pawn language

Pawn is a typeless language. All data elements are of type cell and a cell can hold an integral number.

The size of a cell is 32 bits.

Pawn language supports "array variables" than hold many cells/values. Both single-dimentional and multi-dimentional arrays are supported.

How to declare a new array variable?

The syntax name[constant] declares name to be an array of constant elements, where each element is a single cell, i.e 32 bit value. The name is a placeholder of an identifier name of your choosing, and constant is a positive non-zero value; constant may be absent too. If there is value between the backets, the number of elements is set equal to the number of initializers. We will get back to it later...

The array index range is zero-based which means that the first element is at name[0] and the last element is at name[constant-1].

The syntax name{constant} also declares name as an array of constant elements, but this time elements are bytes rather than cells.

Array variables can be initialized at their declaration. A values that gets passed to initialize array must be a constant.

Uninitialized arrays are getting automatically filled with zeros.

Let't take a look at some examples:

DeclarationMeaning
new a[] = [1,4,9,16,25]array a has 5 elements
new s1[20] = ['a','b']array s1 has 2 elements, other 18 are 0
new s2[] = ''Hello world...''array s2 is a string

The ellipsis operator (three dots or "...") continues the progression of the initialization constants for an array, based on the last two initialized elements. It initializes the array up to declared size.

Here's some examples:

DeclarationMeaning
new a[10] = [1,...]sets all ten elements to 1
new b[10] = [1,2...]b=1,2,3,4,5,6,7,9,10
new c[8] = [1,2,40,50,...]c=1,2,40,50,60,70,80,90
new d[10] = [10,9,...]c=10,9,8,7,6,5,4,3,2,1

Multi-dimentional arrays are arrays that contain references to the sub-arrays. That is, a two-dimentional array for example is an "array of single-dimentional arrays".

Currently, arrays with up to three dimensions are supported

Below are the few examples of declarations of two-dimentional arrays:

Declaration
new a[4][3]
new b[3][2] = [ [1,2], [3,4], [5,6] ]
new c[3][3] = [ [1], [2,...], [3,4,...] ]
new d[2]{10} = ["WOW","CUBE"]
new e[2][] = ["OK","CANCEL"]
new f[][] = ["OK","CANCEL"]

The last two examples with variables e and f show that the final dimention of an array may have an unspecified length, in which case the length of each sub-array is determined from the initializer of an element.

Every sub-array may have a different size too. In the example above, e[1][5] contains the letter L from the word CANCEL, but e[0][5] is invalid becasue the length of sub-array e[0] is only three cells (containing the letters O, K and a zero terminator)

The difference between the declarations of arrays e and f is that we let the application count the number of initializing elements for the major dimension.

Ok, but is it possible to get the length of an array after it has been initialized?

Yes, with sizeof operator.

The sizeof operator always returns the number of cells in the array. Look that the following code:

Strings and Arrays
PAWN
1new message{} = "HELP";
2LOG_i("Cells in array message: %d\n", sizeof message);
3
4new message2[] = "HELP";
5LOG_i("Cells in array message2: %d\n", sizeof message2);
6
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

The console output would be

Strings and Arrays
PAWN
1Cells in array message: 2
2Cells in array message2: 2
3
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

Why?

Let's see.. The HELP string contains 5 characters: four letters one byte each and the string terminator 0x00. Therefore, it does not fit into one cell of 4 bytes. Thus, 2 cells required!

And, as you can see, it doesn not matter how array is declared - the sizeof operator always returns the number of cells.

With multi-dimentional arrays, the sizeof operator can return the number of elements in each dimension. For the last (minor) dimension, the count will be in cells, but for the major dimension(s) the count is a size of a sub-array.

In the following code snippet, see that the syntax of sizeof matrix refers to the major dimension of the two-dimensional array and the syntax of sizeof matrix[] refers to the minor dimension of the array:

Strings and Arrays
PAWN
1new matrix[3][2] = {{1,2},{3,4},{5,6}};
2LOG_i("%d %d\n",sizeof matrix, sizeof matrix[]);
3
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

The values this snipped prints are

Strings and Arrays
PAWN
13 2
2
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

for the major and minor dimensions respectively.

WOWCube SDK supports the following array memory manipulation functions:

Function NameBrief Description
memcpy(dest[], const source[], index=0, numBytes, maxLength = sizeof dest))Copy bytes from one location to another

Strings

Unlike many other languages, Pawn language has no intrinsic string type. Character strings are stored in arrays, with the convention that the array element behind the last valid character is zero - 0x00, a string null-terminator.

This means that you can work with strings exactly the same way as with arrays !

String characters can be accessed by index, the sizeof operator will return string size in cells. Strings can also be used as sub-arrays in a multi-dimentional array.

But, there are some differences to arrays.

As it was mentioned above, the Pawn language does not have variable types. All variables are cells which are 32-bit wide. And a string is basically an array of cells that holds characters and that is terminated with zero.

However, in the character set supported by WOWCube SDK a character takes only a single byte and a cell is a four-byte entity, hence storing a single character per cell is then a 75% waste of memory.

And we know that memory is a prescious resource on WOWCube device, right?

For sake of compactness, Pawn language supports so-called packed strings, whrere each cell holds as many characters as it can fit. Typically, one cell contains four characters.

At the same time, Pawn language also supports unpacked strigns where each cell holds only a single character with the puropse of supporting Unicode and other wide-character sets.

Many programming langauges solve handling of ASCII character sets against Unicode with their typing system. A funciton whll then work either on one or on the other type of string, the the types can not be mixed. Pawn language, on the other hand, does not have types or a typing system, but it can check at runtime whethere a string is packed or unpacked.

WOWCube SDK supports the following string manipulation funcions:

Function NameBrief Description
bool:ispacked(const string[])Determine whether a string is packed or unpacked
strcat(dest[], const source[], maxLength= sizeof dest)Concatenate two strings
strcmp(const string1[], const string2[], bool:ignoreCase=false, length=cellmax)Compare two strings
strcopy(dest[], const source[], maxLength= sizeof dest)Create a copy of a string
bool:strdel(string[], start, end)Delete characters from string
bool:strequal(const string1[], const string1[], bool:ignoreCase=false, length=cellmax)Compare two strings
strfind(const string[], const subString[], bool:ignoreCase=false, index=0)Search for a sub-string in a string
strformat(dest[], size=sizeof dest, bool:pack=false, const format[])Convert values to text
bool:strins(string[], const subString[], index, maxLength=sizeof string)Insert a sub-string in a string
strlen(const string[])Return the length of a string in characters
strmid(dest[], const source[], start=0, end=cellmax, maxLength=sizeof dest)Extract a range of characters from a string
strpack(dest[], const source[], maxLength=sizeof dest)Create a "packed" copy of a string
strunpack(dest[], const source[], maxLength=sizeof dest)Create a "unpacked" copy of a string
strval(const string[], index=0)Convert from text (string) to numbers
valstr(dest[], value, bool:pack=false)Convert a number to text (string)

Several funcitons have a parameter that specifies the maximum number of cells that the destination buffer can hold. The purpose of this parameter is to avoid an accidental buffer overrun.

The example

Now when you've learned all that theory about arrays and strings, let's get to actual example.

In order to demonstrate the work with arrays and strings, we will use several arrays of different dimentions, initialize them with strings and then show these strings on the sides of WOWCube device with a bit of funky animations.

First, let's declare the arrays:

Strings and Arrays
PAWN
1new const arrWOWCUBE[7] = ['W','O','W','C','U','B','E']; //array of characters
2
3new const arrWOWCUBE2[3]{10} = ["WOWCUBE","IS","COOL"]; //array of strings
4
5new const arrTWISTY[]{} = ["T","W","I","S","T","Y"]; //array of strings too
6new Fixed:fParam[] = [0 ,20 ,10 ,-10,-20,-10]; //fixed point parameters used to calculate vertical offsets of each letter
7new prevPos[][2] = [0,0,0,0,0,0,0,0,0,0,0,0]; //array of previous letter positions
8
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

Pay attention!

Different ways to declare arrays in the code above are used purely for the demonstration purposes. We recommend to choose and use one particular style of string variable declarations in your game cubeapps.

So what exactly is declared here?

  • arrWOWCUBE declares an array of characters that form the word `WOWCUBE'
  • arrWOWCUBE2 declares an array of packed strings (or a two-dimensional arryay) that contains 3 strings up to 10 characters each
  • arrTWISTY also declares an array of packed strings, but this time dimensions are not specified explicitly
  • fParam declares an array of fixed point values
  • prefPos declares a two-dimentional array of cells that would be used later for storing some integer values

Second, we need to do some initialization. As always, variables are initialized in ON_Init() callback. We initialize two timers for animation effects and also arrayIndices structure which values are zeroed.

Then we declare a function that will help us to separate application logic that should be happening on timer tick.

Strings and Arrays
PAWN
1OnTimerTick(timerID)
2{
3 if(timerID==0)
4 {
5 //convert current letter to string
6 strformat(buffers.buff,sizeof(buffers.buff), true, "%c",arrWOWCUBE[arrayIndices.i1]);
7 //append it
8 strcat(buffers.buff2,buffers.buff);
9
10 arrayIndices.i1++;
11 if(arrayIndices.i1>7) {
12 arrayIndices.i1=0;
13 strdel(buffers.buff2,0,strlen(buffers.buff2));
14 }
15 return;
16 }
17
18 if(timerID==1)
19 {
20 arrayIndices.i2++;
21 if(arrayIndices.i2>3) arrayIndices.i2 = 0;
22
23 return;
24 }
25}
26
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

The function receives timerID identifier of a timer as an input parameter. We have two timers now, and we need to know which one produced a tick, right?

We have two timers that tick with different frequencies - one triggers every 100 milliseconds (10 times a second), another one produces a tick once a second.

So when the first timer ticks, OnTimerTick(0) is called. And when the second timer ticks, functions parameter changed to 1 and OnTimerTick(1) gets executed.

So let's see what happens when each timer ticks:

Timer 0

  • Take an element at arrayIndices.i1 index from arrWOWCUBE array and convert it into a string buffer buffers.buff
  • Take buffers.buff and append it to buffers.buff2
  • Increment arrayIndices.i1 by 1
  • If arrayIndices.i1 value gets greater than 7, reset it and delete all characters from buffers.buff2

Timer 1

  • Increment arrayIndices.i2 by 1
  • If arrayIndices.i2 gets greater than 3, reset it

This will result in constant change of array index parameters that we use for composing strings we render on screen.

Finally, we would want to render those strings (with a bit of animation, as promised).

Strings and Arrays
PAWN
1public ON_Render()
2{
3 //First screen
4 GFX_setRenderTarget(0);
5 GFX_clear(Colors.black);
6 GFX_drawText([ 120,120 ], TEXT_SIZE, 0, 0, TEXT_ALIGN_CENTER, Colors.teal, buffers.buff2);
7 GFX_render();
8
9 //Second screen
10 GFX_setRenderTarget(1);
11 GFX_clear(Colors.black);
12
13 if(arrayIndices.i2<3)
14 {
15 if(arrayIndices.i2>=0) GFX_drawText([ 120,120-50 ], TEXT_SIZE+2, 0, 0, TEXT_ALIGN_CENTER, Colors.blue1, arrWOWCUBE2[0]);
16 if(arrayIndices.i2>=1) GFX_drawText([ 120,120 ], TEXT_SIZE+2, 0, 0, TEXT_ALIGN_CENTER, Colors.blue2, arrWOWCUBE2[1]);
17 if(arrayIndices.i2>=2) GFX_drawText([ 120,120+50 ], TEXT_SIZE+2, 0, 0, TEXT_ALIGN_CENTER, Colors.blue3, arrWOWCUBE2[2]);
18 }
19 GFX_render();
20
21 //Third screen
22 GFX_setRenderTarget(2);
23 GFX_clear(Colors.black);
24
25 new x = 0;
26 new Fixed:offset
27
28 for(new i=0;i<sizeof(arrTWISTY);i++)
29 {
30 offset = FixedSin(fParam[i]);
31 fParam[i]+=0.015;
32
33 if(firstFrame==0)
34 {
35 firstFrame = 1;
36 }
37 else
38 {
39 if(firstFrame==1)
40 {
41 firstFrame = 2;
42 GFX_drawTextXY(10+x,DISPLAY_HEIGHT / 2+prevPos[i][0]+10, TEXT_SIZE, 0, 0, TEXT_ALIGN_LEFT_CORNER, Colors.pink2, arrTWISTY[i]);
43 }
44 else
45 {
46 GFX_drawTextXY(10+x,DISPLAY_HEIGHT / 2+prevPos[i][0]+20, TEXT_SIZE, 0, 0, TEXT_ALIGN_LEFT_CORNER, Colors.pink3, arrTWISTY[i]);
47 GFX_drawTextXY(10+x,DISPLAY_HEIGHT / 2+prevPos[i][0]+10, TEXT_SIZE, 0, 0, TEXT_ALIGN_LEFT_CORNER, Colors.pink2, arrTWISTY[i]);
48 }
49 }
50
51 prevPos[i][1] = prevPos[i][0];
52 prevPos[i][0] = fint(offset/fixed(4));
53
54 GFX_drawTextXY(10+x,DISPLAY_HEIGHT / 2+prevPos[i][0], TEXT_SIZE, 0, 0, TEXT_ALIGN_LEFT_CORNER, Colors.pink1, arrTWISTY[i]);
55 x+=40;
56 }
57 GFX_render();
58}
59
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

What we have here?

  • First screen displays the string buffer buffers.buff2 that is dynamically changed every 100 milliseconds on timer 0.
  • Second screen displays three words from arrWOWCUBE2 array selected by index that gets incremented on timer 1 each second.
  • Third screen... well, things are getting a little bit tricky here...

Third screen shows the word TWISTY built out of characters stored in arrTWISTY array.

If we would have wanted just to print that word on a straight line, we could just take an index of that array and print all characters one by one, right?

But that would be too easy and too boring. So instead, each letter in the word is animated, it sways up and down leaving a ghostly trace.

Behind every computer animation stands Math.

And our example is not an exclusion - in order to animate word letters we must apply some trigonometry. Cause if we want the letters to sway, we have to find the way to change their coordinates in an endless cyclical manner. And hopefully, make it look neat as well...

So, to generate per-letter vertical offsets, a Sine trigonometrical function is used:

Strings and Arrays
PAWN
1offset = FixedSin(fParam[i]);
2fParam[i]+=0.015;
3
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

WOWCube SDK does not support floating point values. The FixedSin function is used to do fast calculation of sine of an angle taking a fixed point value of an angle as an input parameter

fParam[i] contains a fixed point value of an angle that gets passed to the sine function on every tick and gets incremented by a small value. The offset parameter receives a value of a sine of an angle, then used for rendering letters at dynamic positions.

And finally, three copies of each letter are getting rendered for creation of a ghost trail effect.

Context Rail

Project files

main.pwn
project/src/main.pwn
wowcubeapp-build.json
project/wowcubeapp-build.json
Jump Grid

On This Page

ArraysArrays in Pawn languageStringsThe exampleTimer 0Timer 1
Context Rail

Related nodes

info.json
Examples / SDK 6.3 / Pawn / basics / Strings and Arrays
main.pwn
Examples / SDK 6.3 / Pawn / basics / Strings and Arrays / project / src
wowcubeapp-build.json
Examples / SDK 6.3 / Pawn / basics / Strings and Arrays / project
Getting Started
Examples / SDK 6.3 / Pawn / basics
Previous Node
wowcubeapp-build.json
Examples / SDK 6.3 / Pawn / basics / Working with Emulator / project
Next Node
info.json
Examples / SDK 6.3 / Pawn / basics / Strings and Arrays