0
\$\begingroup\$

In other languages, such as Lisp, there is such a thing as "compound data", where several variables are grouped together.

For example, if I want to represent an orbital that rotate around the player without creating a new object, I may use something like this:

(define (Orbital rx ry colour)) 
;; creating a new data type called "Orbital", with 3 var.s

(define ORB1 (make-Orbital 30 -20 "red")) 
;; creating a particular orbital at (30, -20) with red as colour, 
;; stored under the name "ORB1"

Is there a similar mechanism in Game Maker (made in Delphi) that permits such grouping of information under a structure which itself can be considered a single item, and thus such things to be stored in lists?

\$\endgroup\$
  • \$\begingroup\$ Sounds like you're describing objects, as in object oriented programming. Is that right? It might help with searching for an answer to this. \$\endgroup\$ – MichaelHouse Feb 15 '17 at 23:26
  • \$\begingroup\$ @Byte56 Game maker is an oop, but I am exactly trying to avoid making "actual objects" and rather use compound variables which, for all intents and purposes, function almost exactly like an object except it takes up less space and performance \$\endgroup\$ – user289661 Feb 16 '17 at 0:08
  • \$\begingroup\$ I have an answer for this, but you won't like it very much. You basically have to write your own memory allocator and implement C-like strcuts. I will provide the interface in an answer right now. \$\endgroup\$ – The Great Duck Jun 8 '17 at 19:14
3
\$\begingroup\$

Because I don't have enough reputation to comment, I'm submitting an answer that further clarifies the previous answer regarding ds_map.

Here's an example of ds_map in action:

globalvar player;
player = ds_map_create();
ds_map_add( player, "acceleration", 1.0 );
ds_map_add( player, "maxspeed", 5.0 );
ds_map_add( player, "deceleration", 0.5 );
ds_map_add( player, "subBeatFirePoints", "0.5" );

This snippet is from an initialization script in a game I'm working on. It creates a global variable named player, which I reference in other scripts (e.g. in my player object's control script).

ds_map lets you work with key-value pairs. In this case, an example of a key-value pair in my player variable is { "acceleration", 1.0 }.

As you see above, ds_map_create() creates the DS map and the ds_map_add function adds the key-value pairs.

For your example, you'd probably do something like this:

globalvar orbital;
orbital = ds_map_create();
ds_map_add( orbital, "rx", 30 );
ds_map_add( orbital, "ry", -20 );
ds_map_add( orbital, "colour", "red" );
\$\endgroup\$
  • \$\begingroup\$ This answer fundamentally fails as DS structurs are flushed from memory upon loading a save using the default mechanism. However, this is a great answer and a great use of the built in structures. \$\endgroup\$ – The Great Duck Jun 8 '17 at 19:15
2
\$\begingroup\$

GMS doesn't implement compound data natively. By the way, you can use DS Maps as a handy alternative, in which you can store values and retrieve them by keys. It's an actual dictionary data structure, and you can even convert it in JSON format and vice versa. Hope that helps.

\$\endgroup\$
  • \$\begingroup\$ Fyi, I would add a disclaimer referring to the fact that DS's aren't preserved with the default save/load mechanism. \$\endgroup\$ – The Great Duck Jun 8 '17 at 20:20
2
\$\begingroup\$

If you read up on objects you'll see that references to them (aka instance id's) are shuffled every frame. So those won't cut it. Data structures are flushed from memory when you load a save file using the default mechanism. I only know of one other option:

C-style Dynamic Memory Allocation Within an Array

If you are not familiar with structs and whatnot, I would suggest reading up on the concepts. In general, they are little bitty tiny arrays, like your compound data types. In fact, they're probably synonymous, but since I haven't used that language I cannot say for sure. Here are the functions I used to implement malloc and free along with an initialization function used to set up and reset a local heap space. It is local to each game maker object. If you want a global version, just change the array variable to global or make an object with it in it. I personally prefer the local version.


mm_init

HEAD_NODE = 0;
/*no need to align 0*/
HEAP_SPACE[HEAD_NODE] = 0;
LINKED_LIST_LENGTH = 0;
TAIL_NODE = HEAD_NODE;
return 0;

mm_malloc

newsize = argument[0] + (argument[0] mod 2);
freeP = HEAD_NODE;

if (mm_getLength(freeP) - newsize > 0 && !mm_getFree(freeP))
{
    mm_setFree(freeP,true);
    mm_insert(freeP, newsize);
    return freeP + 1;
}
while (mm_getNext(freeP) < TAIL_NODE)
{
    freeP=mm_getNext(freeP);
    if (mm_getLength(freeP) - newsize > 0 && !mm_getFree(freeP))
    {
        mm_setFree(freeP,true);
        mm_insert(freeP, newsize);
        return freeP + 1;
    }
}

p = TAIL_NODE; /*add to the heap and return the starting position*/
//p = mem_sbrk(newsize + 8); /*add to the heap and return the starting position*/
if (p == -1)
{
    return NULL;
} else
{
    HEAP_SPACE[p] = (newsize) + (1 << 31);
    HEAP_SPACE[mm_getNext(p) - 1] = newsize;
    if (LINKED_LIST_LENGTH == 0)
    {
        HEAD_NODE = p;
    }
    TAIL_NODE = mm_getNext(p);
    LINKED_LIST_LENGTH += 1;
    return (p + 1);
}

mm_free

/* adjust so that ptr points
 * to the node for this block
 */
p = argument[0] - 1;
HEAP_SPACE[p] = mm_getLength(p);

/*did the pointer originate from the heap*/
if (p > HEAD_NODE && p < TAIL_NODE)
{
    if (!mm_getFree(mm_getPrevious(p)))
    {
        /* the previous node
         * can be coalesced
         */
        mm_removeNode(p);
        p = mm_getPrevious(p);
    }

    if (!mm_getFree(mm_getNext(p)) && mm_getNext(p) <= TAIL_NODE)
    {
        /* the next node
         * can be coalesced
         */
        if (mm_getNext(p) == TAIL_NODE)
        {
            TAIL_NODE = p;
        }
        mm_removeNode(mm_getNext(p));
    }
}

mm_getNext

return (argument[0] + mm_getLength(argument[0]) + 2);

mm_getPrevious

return (argument[0] - mm_getLength(argument[0]-1) - 2);

mm_getLength

/*zero out the leftmost bit, reserved for the free value*/
return HEAP_SPACE[argument[0]] & (~(1 << 31));

mm_getFree

return ((HEAP_SPACE[argument[0]] & (1 << 31)) > 0);

mm_setLength

HEAP_SPACE[argument[0]] = argument[1] + (mm_getFree(argument[0]) << 31);
HEAP_SPACE[mm_getNext(argument[0]) - 1] = argument[1];

mm_setFree

HEAP_SPACE[argument[0]] = mm_getLength(argument[0]) + (argument[1] << 31);

mm_removeNode

mm_setLength(mm_getPrevious(argument[0]),mm_getLength(mm_getPrevious(argument[0]))+mm_getLength(argument[0])+2);
LINKED_LIST_LENGTH -= 1;
/*length is in terms of integer words*/

mm_insert

/* the node has to fit to avoid distorting
 * the linked list's heap property
 */
if (argument[1] + 2 < mm_getLength(argument[0]))
{
    node = argument[0] + argument[1] + 2;
    /*ensure the free field is 0*/
    HEAP_SPACE[node] = 0;
    mm_setLength(node,mm_getLength(argument[0])-argument[1]-2);
    mm_setLength(argument[0],argument[1]);
} else
{
    /* all blocks and nodes are 2 byte aligned, so a difference of one
     * would only exist in the case of a system flaw
     */
    HEAP_SPACE[argument[0]] = mm_getLength(argument[0]) + (1 << 31);
}

Since I don't know exactly what kind of structs you want to make, I cannot help you by making them for you or showing an example. Lists and stuff are best done using linked lists constructed with node sub-classes. so I would suggest reading up on the stuff and making a node class. I have a few data structures I've written so if there is one in particular you need, I can help you by providing it if I have it. Either that or I can help you write your own.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.