Skip to main content
gamedev.net
🔒 Locked

[.net] Struct and Class

Started by p997 8 replies 1.4k views
Original Post
p997
p997
Hi, I have a question about an Array defined under a class. Im using C# to code a 2D game, and I use a 2 dimension Array to store tiles of the map. For example: public Class Tiles { Tiles [,] map; public void InitilizeMap() { map = new Tiles [5,5]{{w,w,w,w,w}, {w,s,s,s,w}, {w,s,s,s,w}, {w,s,s,s,w}, {w,w,w,w,w}, } } Inside the array, each w represents the wall (bool w.blocked = true) and s represents the empty space (bool s.blocked = false)- meaning the player can move freely. However, whenever i change any single s to s.blocked = true then all the s in the array become s.block = true. Well, I finally solved the problem by changing the Tiles clas to public Struct Tiles, and everything worked. But I just dont understand why is that? Could someone explain? Thank you in advance.
Rob Loach
Rob Loach
That's a very good question, actually. Nice work on trying things out and learning on your own. Most people wouldn't try that kind of thing out and just bring the issue straight to the forums...

The difference between a class and a struct is that classes are held by reference, and structs are held by value. This means that structs are dealt almost the same way as an integer, float, etc. If Tile is a class, when you say:
Tile theTile = new Tile("grass"); // or something
A new tile is created and theTile "points" to the reference of it. If Tile is a struct:
Tile theTile = new Tile("grass");
... There ends up being two Tile variables. One where you said new Tile("grass") and another where you said Tile theTile =. This is the same way primitive data types are passed, by value.

Because primitive data types, and structs, are passed by value, this means that when you passed your w tile to the Tiles array, it made a copy of w when it was passed. When Tiles was set as a class, when you passed w, the reference was passed as opposed to the value-type (not a copy, just a reference).
Rob Loach [Website] [Projects] [
p997
p997
Thanks for your explanation.
I understand that a struct is a value type and a class is a reference type.
However, in my little program when i assign a particular item inside the array to another object then only that specific item is affected.
For example:
originaly, map[2,3]= s
when i do: map[2,3] = w;
then only this item is affected, while all other s inside the array remain unchanged (still =s).
But whenever i do: map[2,3].blocked = true;
all the s inside the array became .blocked = true.
This puzzles me.
The Frugal Gourmet
The Frugal Gourmet
Quote:
Original post by p997
Thanks for your explanation.
I understand that a struct is a value type and a class is a reference type.
However, in my little program when i assign a particular item inside the array to another object then only that specific item is affected.
For example:
originaly, map[2,3]= s
when i do: map[2,3] = w;
then only this item is affected, while all other s inside the array remain unchanged (still =s).
But whenever i do: map[2,3].blocked = true;
all the s inside the array became .blocked = true.
This puzzles me.


You are essentially passing the same reference to every item in the array. So, anytime you change that item's properties that change is reflected everyhwere. Why? Because a reference to the exact same item is stored in every spot in the array. Basically: it's the same item in every position. If you're using a reference type, you need to pass a new *copy* of that item -- not the item itself.

Co-creator of Star Bandits -- a graphical Science Fiction multiplayer online game, in the style of "Trade Wars'.
TMP72
TMP72
I think this is what your doing. If you declare the following:

Tiles w = new Tiles();
Tiles s = new Tiles();

Here you created 2 objects of type Tiles. The first Object is w and the second is s.

Now you created a reference to an array of Tiles called map

Tiles [,] map;

Next you fill the array with the with references to the 2 objects you created by calling this method:

public void InitilizeMap()
{

map = new Tiles [5,5]{{w,w,w,w,w},
{w,s,s,s,w},
{w,s,s,s,w},
{w,s,s,s,w},
{w,w,w,w,w}};
}

Each w and each s does not represent a new instance of that object just references to the same two objects created. Therefore when you call the blocked property on an s reference it effects all the references to the same object.
p997
p997
Quote:
Original post by TMP72
I think this is what your doing. If you declare the following:

Tiles w = new Tiles();
Tiles s = new Tiles();

Here you created 2 objects of type Tiles. The first Object is w and the second is s.

Now you created a reference to an array of Tiles called map

Tiles [,] map;

Next you fill the array with the with references to the 2 objects you created by calling this method:

public void InitilizeMap()
{

map = new Tiles [5,5]{{w,w,w,w,w},
{w,s,s,s,w},
{w,s,s,s,w},
{w,s,s,s,w},
{w,w,w,w,w}};
}

Each w and each s does not represent a new instance of that object just references to the same two objects created. Therefore when you call the blocked property on an s reference it effects all the references to the same object.



Indeed, that exactly what i did. so for argument sake, if i still want to define Tiles as a class instead of a struct, what do i do to change the property of 1 s without affecting the rest of them. How do i pass a "new" copy of the s item?


RipTorn
RipTorn
it would need to be something like this:



map = new Tiles [5,5]{{new Tiles(...),new Tiles(...),new Tiles(...),new Tiles(...),

....

}};

Ideally, you would do this in some automated way, using (for example) for loops, reading from a config file, etc. Having each item defined in code is quite ugly (as things get bigger).

By doing it this way, you allocate memory and create a new 'Tiles' object for every single entry in the array. So each reference in the array points to a unique location in memory, which is a unique Tiles object.
When tiles is a structure, it means instead of the array being an array of references (ie, an object that stores where something else is...) you get an array of actual memory that stores the actual objects. Hard one to explain, it has it's advantages and disadvantages. For example, it's often very useful to have many things 'know' about the same object, in this case a structure wouldn't work as they would all store a duplicate, and changing one would leave the others in the original state. A reference lets you store the one copy of the object, and lets heaps of other things reference it. structures also eat up more memory (as they are duplicates) and have some other, more complex limits.

An example, the string "grass" is actually a referenced string. 'grass' is a hard coded string, and occurs often, however it will only actually be stored in memory once. There may, however, be many,many references to that string (which is good, otherwise the contents would be duplicated in memory).
However, smaller types like integers or floats are stored as structures, because the extra overhead of referencing is not desirable, plus things could get really, really confusing if they were (think about the case arrow.position = player.position when you fire an arrow, then arrow.position += 5 when the arrow is in flight... If integers were reference types, you player would then follow the arrow)

hope that helps a bit more. I'm kinda lightheaded at the moment so it may not be all that well explained.
p997
p997
very well put, thanks.
TMP72
TMP72
Hey P997,

Here is a code sample showing you one way to use a tile class for you game. Just a demo, may not be the best game code.

using System;namespace Test{    class Program    {        static void Main(string[] args)        {            char[,] mapBase = new char[5, 5] { { 'w', 'w', 'w', 'w', 'w' }, {'w','s','s','s','w'},            {'w','s','s','s','w'},{'w','s','s','s','w'},{ 'w', 'w', 'w', 'w', 'w' } };                       Tiles[,] levelOne = new Tiles[5, 5];                        levelOne = Map.FillMap(mapBase);// Call Static FillMap Method in Map                        /* From this point on levelOne contains an array Of individual Tiles objects.             /  The renderer can now transverse all the tiles and draw the apropriate image based            /  the TileStyle property in the Tiles Objects*/                       Map.Render(levelOne); // Call Static Render Method in Map        }       }    class Tiles    {        private string tileStyle = "";        private bool blocked = false;        public Tiles(char tileCode)        {            switch (tileCode)            {                case 'w':                    TileStyle = "w"; //wall - always blocked                     Blocked = true;                    break;                case 's':                    TileStyle = "s"; //wall - not blocked (secret)                    break;                case 'e':                    TileStyle = "e"; //empty space - not blocked                     break;                case 'b':                    TileStyle = "b"; //empty space - blocked                    Blocked = true;                    break;}        }        public string TileStyle        {            get            {                return tileStyle;            }            set            {                tileStyle = value;            }        }        public bool Blocked        {            get            {                return blocked;            }            set            {                blocked = value;            }        }    }    class Map    {        public static Tiles[,] FillMap(char[,] tileLayout)        {            Tiles[,] tileMap = new Tiles[tileLayout.GetLength(0), tileLayout.GetLength(1)];            for (int i = 0; i < tileMap.GetLength(0); i++)            {                for (int j = 0; j < tileMap.GetLength(1); j++)                {                    tileMap[i, j] = new Tiles(tileLayout[i,j]);                }            }            return tileMap;        }        public static void Render(Tiles[,] level)        {            for (int i = 0; i < level.GetLength(0); i++)            {                for (int j = 0, c = 0; j < level.GetLength(1); j++, c++)                {                    Console.Write(level[i, j].TileStyle);                    if (c == 4)                    {                        Console.WriteLine();                        c = 0;                    }                }            }        }    }}


I hope this helps you some more.
T!
p997
p997
Thanks for the code sample.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.