Random array element gets value changed with variable
Hi all,
I'm making a roguelike RPG in VS 2008, XNA Studio 3.0.
In the function that follows, I take a random element from an array (the master list of items, iM) and put it into a variable. The array is of type Item, an inherited class I created for all the different kinds of items. Each Item initially has a startingPos of (0,0), and they're supposed to be given a random startingPos before they're added to the mapItems.
Code:
public void CreateItems(ref List<Item> iM)
{
Random r = new Random();
for (int it = 0; it < maxMapItems; it++)
{
int tmpR = r.Next(0, iM.Count);
Item tmpItem = iM[tmpR];
//set starting position for the item on the current map
tmpItem.setStartingP( new Vector2((float)r.Next(4, maxTilesH - 4), (float)r.Next(4, maxTilesV - 4)));
if (tmpItem.getStartingP() == player.startingPos || tileArray[(int)tmpItem.getStartingP().X, (int)tmpItem.getStartingP().Y].isSolid)
{
it--;
continue;
}
mapItems.Add(tmpItem);
//if the map has a shop, populate it with items
}
if (curMap.isShopMap)
{
Item tmpShopItem = new Item();
foreach (Tile t in tileArray)
if (t.name == "floor")
{
tmpShopItem = iM[r.Next(0, iM.Count)];
tmpShopItem.setStartingP(t.absPos);
//new Potion(potionTexture, potionNames[r.Next(0,
//potionNames.Count())] + " potion", t.absPos.X, t.absPos.Y);
mapItems.Add(tmpShopItem);
}
}
return;
}
The problem is hard to explain. Essentially, after the line
Code:
Item tmpItem = iM[tmpR];
, the array element somehow has its value changed, with the startingPos becoming tmpItem's startingPos. It's as if the new Item is just pointing to the same object. So when the map has a shop, the items just pile on top of each other, because they have the same startingPos.
I noticed that when I created objects of a subclass of Item (in the above code, Potions) and filled the shop with them, they were in the right place. So it's not the logic, but something in the memory screwing things up.
Any ideas? Thanks so much to anyone who can help me.
Katie
Re: Random array element gets value changed with variable
Quote:
Originally Posted by
katnip84
The problem is hard to explain. Essentially, after the line
Code:
Item tmpItem = iM[tmpR];
, the array element somehow has its value changed, with the startingPos becoming tmpItem's startingPos.
It's as if the new Item is just pointing to the same object. So when the map has a shop, the items just pile on top of each other, because they have the same startingPos.
I'm pretty sure that's exactly what's happening. C# memory handling is similar to that of Java. When you say Item newItem = something, you're not creating a new object. You're creating a pointer to the 'something' object. If you want to create truly new object, you'll have to call Item newItem = new Item() and then initialize the values to the ones you want. (Probably have a copy constructor.)
I didn't read through your code all the way, but I noticed that and thought I would mention it. Also, you should really come up with a better variable naming scheme. It's easy to understand for you, but impossible to understand for us.
Re: Random array element gets value changed with variable
Hi A_D,
The whole thing was resolved by writing a copy constructor. Thank you!~! I had tried just having the new Item() before, but that had never worked.
Sorry about the variable names. My function now takes List<Item> iMasterList, which I think is a bit clearer. Following a style was never one of my strongest suits... but I do make a lot of comments (which you may see one day if I need help again) :P
Re: Random array element gets value changed with variable
No problem. I'm not a huge fan of automatic memory management, so I tend to run into these issues a lot myself. I don't even really mind the automatic memory management, but they really need to explicitly differentiate between pointers and objects.
Also, I was never a stickler for style either until I was given an orphaned project with a shitty design and no documentation to work on. Suddenly, it was like my eyes were opened to the importance of clean code and documentation. I'm sure the same will happen to you one day. :p
Re: Random array element gets value changed with variable
Quote:
Originally Posted by
Access_Denied
No problem. I'm not a huge fan of automatic memory management, so I tend to run into these issues a lot myself. I don't even really mind the automatic memory management, but they really need to explicitly differentiate between pointers and objects.
Ummm... they do. It's pretty clear and well documented. Here is one resource of many which explain how things work. It's not terribly complicated.
http://www.codeproject.com/Articles/...lue-Types-in-C
Re: Random array element gets value changed with variable
But you can use the same syntax for both. For example:
Code:
Item foo = new Item();
Item foo2 = foo;
foo2.method();
In this case, you've changed the original foo item, not the copy you thought you changed. But in C++, it would look like this:
Code:
Item * foo = new Item();
Item foo2 = *foo;
foo2.method();
Now you've actually copied data and changed the copied data, which is fairly easy to see because of the pointer syntax. I'm not saying that it's not just as good or can't perform the same functions, I'm just saying that it can get confusing. Especially for people who come from a C/C++ background where you can't just change between pointers to objects and objects.
Re: Random array element gets value changed with variable
You could say that about anything really. C# is not and is not intended to be C++. You'll probably have a hard time going from C++ to Lisp too, and perhaps a dynamic language like Ruby. They are different, it doesn't help to think of one in terms of another.
Re: Random array element gets value changed with variable
But when you're moving from one language to another, that's all you can do. When I moved to functional programming, the only thing I could do was compare it to procedural programming. Sure, now I know that you shouldn't compare the two, as they have different goals, but when first learning a new type of programming, it's all you can do. And that's exactly what people are going to do when MS finally convinces them to switch to C#: compare it to C++.
EDIT: And this is all just a matter of opinion anyway. I personally think C# would be better if there was an explicit pointer type. But that's just my opinion. Not a fact.
Re: Random array element gets value changed with variable
Quote:
Originally Posted by
Access_Denied
EDIT: And this is all just a matter of opinion anyway. I personally think C# would be better if there was an explicit pointer type. But that's just my opinion. Not a fact.
If you have pointers then you can't make the guarantees about data integrity and memory management that you can otherwise. It would go against much of the original design of C#. Again, if you need/want pointers, use a different language....
Re: Random array element gets value changed with variable
You guys keep forgetting that C# has pointers and that it uses the exact same C++ syntax for them.
Reference types are alike to pointers in some respect, but they are not pointers.
Besides, it's relatively easy to distinguish them from value types, with most IDEs it only takes hovering the mouse over a variable.
Besides from the "built-in" value types, most of the types in C# will be reference types anyway.
Re: Random array element gets value changed with variable
Well yes, but there are strict rules as to when and how they may be used, so while the syntax is the same as C++, the semantics are not, and their use case is very narrow and rare in C#.
Re: Random array element gets value changed with variable
I would say that it is rather the other way around: the reference types are semantically different from pointers (for example, no support for pointer argumetics, garbage collection), and thus, C# type system is conceptually different, which in turn imposes restrictions on pointers. But, I agree with you - it's a different language, and it should be treated as such.
Re: Random array element gets value changed with variable
Re: Random array element gets value changed with variable
Quote:
Originally Posted by
TheGreatCthulhu
(for example, no support for pointer argumetics [...])
*arithmetics
D4mn T9 dictionary.
Missed the "h".
Re: Random array element gets value changed with variable
Quote:
I'm not saying that it's not just as good or can't perform the same functions, I'm just saying that it can get confusing. Especially for people who come from a C/C++ background where you can't just change between pointers to objects and objects.
That's definitely me :D
I'm really thankful for the response so far, so I'm hoping someone among you might help me with a new problem.
As it is, I create a new Item, copy the Item attributes in the constructor, and plunk it into List<Item> mapItems.
But Item is essentially just supposed to be an abstract class (I think that's the correct term?), as all the items are derived types (Potions, Scrolls, Gold, Rings, etc). So In the process of creating a new Item, I lose all the methods and fields of the derived types. That's a really big problem...
I've been scouring the help and looking at predicates, but I don't want to convert a base type object to a derived one... I just want to keep what's already there. Is there anything I can do?
Sorry if this question is already answered elsewhere. I'd really appreciate any resources you could point me to :)
Re: Random array element gets value changed with variable
1) About the syntax.
I understand that it can be a bit confusing for someone with previous experience in C++, but the confusion has more to do with not fully understanding the type system of C#. Once you do, the syntax stops being a problem. In fact, I personally appreciate the consistency of using the same "." operator for both value and reference types. Note that it is called "member access operator", and the name describes precisely what it does in both cases. Furthermore, C++ is actually not so different. IM(H)O, the C++ "->" operator has less to do with making pointer variables distinct from other types of variables, and more to do with providing syntactic sugar for this:
(*pVar).member // equivalent to pVar->member
As you can see, that makes it obvious that it's a pointer, but it's a pain to type in, and it's not particularly visually appealing.
Now, with C# references, you can't directly assign a memory address, or use pointer arithmetics, or explicitly delete memory. In other words, you never directly work with their pointer-like aspects, but with the pointed-to object instead, so there's no need to explicitly dereference references - it's done automatically.
In C#, your responsibility is to know to what of the two major type-kinds you're working with, and it's not hard to find out (reflection enables IDEs to provide whatever info is relevant, and there's documentation available in addition).
2) About your inheritance question.
This is a good question, and it's language independent (as long as we talk about OO languages). This has to do with OO application design. First, the term "abstract class" denotes a class designed to be a base class to other types, while not providing a way for the client code to instantiate it. In other words, it's an abstract type, and variables of that type can only reference objects which are instances of classes derived from it.
If your class has those properties, than it's the correct term. Otherwise, you would just call it a base class.
Now, how do you get your objects to exhibit derived, specialized behaviors, via a base-class reference?
As you noticed, without type-casting, you can't just call derived-class specific methods.
But, type-casting only works if you know what the actual type of the object is, and often enough, you don't.
So, how to solve this?
I assume you experimented with Windows Forms? Let's examine the design briefly. The IDE creates a class to represent your main form, and usually names it Form1. Form1 is derived from Form (provided by the .NET library), from which it inherits some methods/properties and implementation. You then add your own, application-specific methods, run the code, and it all works. And yet, the framework that runs your windows application has absolutely no idea of your Form1 class, or your application-specific methods.
How and why, then, it works at all?
Because of how the base class, Form, is designed. The purpose of the base class, with respect to the calling code, is to provide a well defined interface (public methods/properties) for the calling code to rely on.
So, the framework only sees this abstract interface, and calls its members, which in turn, end up, one way or the other, calling your application-specific methods.
For this to work, a bit of planing is required, and experience helps a lot.
Essentially, the idea is to override inherited abstract or virtual methods in a derived class, so es to make them do something else, or something in addition, or call methods on other objects, etc. Alternatively, the base class can provide ways to "configure" it with other objects.
For example, even if you don't override any methods in Form1, and only use event-handlers, when you assign an event handler, you're actually configuring the base class, Form, with a method to call when the framework raises the given event.
Another way is to pass an object to a method, which would then assign it to some internal variable some other method later uses. Setting a property essentially has the same effect.
In your case, you have to think about what all of the types are required to do, and what the calling code is required to do, and how to abstract it all away in a unified interface.
For example, you wouldn't create a bunch of different methods like Potions.Drink(), Scrolls.Use(), Gold.Spend(), Rings.Wear(), etc., but would find a name that describes all of those on a higher level, and put it into the Item class.
A good candidate is, for example, Item.Use().
Then, the calling code would just call the Use() method, and the actual derived class would implement it in a way that is suitable for the kind of item it represents.
So, you must make sure that your abstract interface is general and expressive enough - and to do that, you must carefully study what the requirements are, what the calling code is supposed to do, what your game should be able to do, etc. Don't hesitate to take pen & paper - they can be great tools.
If you provide more detail, we can provide a more focused guidance.
In the meantime, you might want to check out Design Patterns, as these are well-established, time tested solutions to common design problems.
Also, this is my post from another thread that among other things, has some (I think) illustrative examples that show inheritance in action (VS solutions attached), so if you take some time to examine it, it can get you started. The first example is rather simple, to illustrate the concept, and the other is an implementation of the Command design pattern.
Re: Random array element gets value changed with variable
Wow, that's exhaustive :) I'm familiar with overriding methods and how Windows Forms work, especially in VB.Net (thank you, two years of college). It couldn't hurt to know more about design patterns, but I think my question still remains one of C# language specifics.
Here is an example that succinctly illustrates the issue.
Code:
Item tmpGoldSm = new Gold(Content.Load<Texture2D>("gold small"), r.Next(2, 50));
This line comes from my Load method. It creates a Gold object with the base class Item. The random number is the amount , a property that no other item really need have.
Code:
iMasterList = new List<Item>() { tmpFood, tmpFood, tmpGoldSm, tmpGoldSm, tmpPotion1, tmpPotion2, tmpScroll, tmpScroll2, tmpRing, tmpRing2 };
When this line runs, the temporary gold variable goes into the master list of items. At this point, it still has its derived class info.
Later, in having to do this:
Code:
for (int it = 0; it < maxMapItems; it++)
{
int tmpR = r.Next(0, iMasterList.Count);
//put randomly-selected item into this map's items, and give it a random starting
position on the map
Item tmpItem=
new Item(iMasterList[tmpR],
new Vector2((float)r.Next(4, maxTilesH - 4),
(float)r.Next(4, maxTilesV - 4)));
mapItems.Add(tmpItem);
... I lose the Gold object information that was contained in iMasterList[tmpR]. So later, I can't verify that the object is gold to add the amount to the player's gold. If I could just copy the object in its entirety to the new list, I think this could be avoided, but currently that doesn't seem possible, because
Code:
Item tmpItem = new Item();
tmpItem = iMasterList[tmpR];
just made tmpItem a pointer.
Alternatively, I've thought of giving Item its own amount and only overriding it in the Gold class--is that the right idea from a design perspective? Checking for such properties in every object of a derived type would add a lot of needless arithmetic operations, though (i.e., player.Gold + 0 for every item that doesn't have an amount).
Re: Random array element gets value changed with variable
This is unnecessary:
Code:
Item tmpItem = new Item();
tmpItem = iMasterList[tmpR];
You create a new object and immediately throw it away. Simply use
Code:
Item tmpItem = iMasterList[tmpR];
Re: Random array element gets value changed with variable
Hi Ed,
In my original post, you can see that that particular assignment was causing the first problem. I'm afraid all that does is make tmpItem a reference to the same object as iMasterList[tmpR], which in turn, causes changes to tmpItem.startingPos to affect the original item.
Re: Random array element gets value changed with variable
I was thinking about this a lot last night, and I realize now what I should do (I think).
I'm going to implement new, intermediary sub-classes in Item. A class like Usables : Item would comprise potions, scrolls, and anything else with a Use method. I already have an Equipment subclass for all the various equippables. The Gold class will be on the same level as these, and used just for gold, since its only unique method will be Pay.
Making Lists of each of these types will let me aviod copying my gold, potions, etc. to a generic Item object, and losing the derived class info. I'll just have to add a few more loops to the CreateItems() method.
Thanks for making me think of design, Cthulu :)