Adventure Game Studio

AGS Development => Engine Development => Topic started by: Billbis on Wed 26/06/2013 09:57:09

Title: Accessing AGS objects by array index / Hardcoding editor values in code
Post by: Billbis on Wed 26/06/2013 09:57:09
I have no idea of what will be the best solution, but I just wanted to enphasize that we should keep a way to quickely iterate things across all (existing) regions, like what we can do now :
Code (AGS) Select
int i =1;
while (i < 50) { //current hostpot limit
    //do stuff on hotspot[i]
    i++;
}

But maybe there is no need to remind this to you, gods of code. ;)

(Split from here (http://www.adventuregamestudio.co.uk/forums/index.php?topic=48513.0). - moderator)
(sorry about derailing the topic on the first reply.  :( - Billbis)
Title: Re: Re: Ideas for UI in the context of unlimited regions
Post by: Crimson Wizard on Wed 26/06/2013 10:04:36
Quote from: Billbis on Wed 26/06/2013 09:57:09
I have no idea of what will be the best solution, but I just wanted to enphasize that we should keep a way to quickely iterate things across all (existing) regions, like what we can do now
There's no reason why this may become unusable...
What changes is actual number of hotspots in room, i.e. you will need to write:
Code (ags) Select

while (i < Room.HotspotCount)
Title: Re: Re: Ideas for UI in the context of unlimited regions
Post by: Billbis on Wed 26/06/2013 10:13:36
Quote from: Crimson WizardThere's no reason why this may become unusable...
What changes is actual number of hotspots in room, i.e. you will need to write:
Yes, but that will somehow require to rearrange hostopt.ID when user is deleting one hotspot, wich can be ... disturbing.
But anyway, as long as I can iterate through all hostpots, I will be happy.  ;-D
Title: Re: Re: Ideas for UI in the context of unlimited regions
Post by: Crimson Wizard on Wed 26/06/2013 10:31:56
Quote from: Billbis on Wed 26/06/2013 10:13:36
Yes, but that will somehow require to rearrange hostopt.ID when user is deleting one hotspot, wich can be ... disturbing.
As well as deleting a Character, or a GUI. That's being a problem for a long time.
But using explicit ID may be replaced with script name, which is far more safe.
Range operations and calculating relative id will still work so far as you can address array of hotspots from script.
Title: Re: Re: Ideas for UI in the context of unlimited regions
Post by: Ryan Timothy B on Wed 26/06/2013 14:50:22
Quote from: Crimson Wizard on Wed 26/06/2013 10:31:56
But using explicit ID may be replaced with script name, which is far more safe.
I'm really not sure why being able to access the "Object" ID was ever implemented in AGS. Likely due to the limitations of AGS script and then trying to stick with the whole backwards compatibility - which in my opinion is only hindering all radical improvements of the editor.
Title: Re: Re: Ideas for UI in the context of unlimited regions
Post by: MiteWiseacreLives! on Wed 26/06/2013 15:29:05
Quote from: Ryan Timothy on Wed 26/06/2013 14:50:22
I'm really not sure why being able to access the "Object" ID was ever implemented in AGS. Likely due to the limitations of AGS script and then trying to stick with the whole backwards compatibility - which in my opinion is only hindering all radical improvements of the editor.
I've used the object[ID] to apply settings to a large number of objects etc during runtime, kind of like Billbis is describing. Not so handy with script names, unless I am overlooking a method.
Title: Re: Re: Ideas for UI in the context of unlimited regions
Post by: Crimson Wizard on Wed 26/06/2013 15:45:12
Quote from: MiteWiseacreLives! on Wed 26/06/2013 15:29:05
I've used the object[ID] to apply settings to a large number of objects etc during runtime, kind of like Billbis is describing. Not so handy with script names, unless I am overlooking a method.
Indeed, working with ranges of elements is important possibility, and its not going anywhere.
Although I am not sure if Ryan meant accessing elements of array; I thought he means accessing Object.ID property.

Anyway, I started this topic for completely different reasons, and I think it is better to continue the discussion on IDs in separate thread.

I really need to figure out how to update user interface. This is pretty irritating, 'cause the code for unlimited regions is practically ready.
So far I can only imagine adding +/- buttons on the topmost panel of Room Editor, as a temporary solution for Alpha version.
Title: Re: Re: Ideas for UI in the context of unlimited regions
Post by: Ryan Timothy B on Wed 26/06/2013 22:56:56
Quote from: MiteWiseacreLives! on Wed 26/06/2013 15:29:05
I've used the object[ID] to apply settings to a large number of objects etc during runtime, kind of like Billbis is describing. Not so handy with script names, unless I am overlooking a method.
Quote from: Crimson Wizard on Wed 26/06/2013 15:45:12
Indeed, working with ranges of elements is important possibility, and its not going anywhere.
Although I am not sure if Ryan meant accessing elements of array; I thought he means accessing Object.ID property.
Nope. I meant exactly what I said. Accessing "Objects" by a strict ID is a terribly, terribly poor coding practice ("Objects" being GUIs, Characters, Objects, Hotspots, Regions, WalkableAreas, etc). They should be accessible via an ArrayList (which of course is the same thing, but you shouldn't have Character.ID to find what index it is in the ArrayList).

Code (ags) Select

  // Character variables created in the accessible "Game" script
  Character cHenry = new Character("Henry", 100, 150, 5);  // Name: Henry, X: 100, Y: 150, Room: 5
  Character cSally = new Character("Sally", 110, 160, 6);  // Name: Sally, X: 110, Y: 160, Room: 6
  Character cFrank = new Character("Frank", 120, 170, 7);  // Name: Henry, X: 120, Y: 170, Room: 7
 
  // Characters being added to the AGS character ArrayList
  Game.Characters.Add(cHenry);
  Game.Characters.Add(cSally);
  Game.Characters.Add(cFrank);

  // Getting the index of cSally - if you ever needed it, which you shouldn't in most cases
  int sallyIndex = Game.Characters.IndexOf(cSally);

  // iterating through the Characters ArrayList
  foreach (Character c in Game.Characters) {
    c.Visible = false;
  }

  // iterating through the Character ArrayList as it is now
  int i;
  while (i < Game.Characters.Length) {
    Game.Characters[i].Visible = false;
    i++;
  }


Etc etc. You understand how ArrayLists work. This is how AGS should've been designed, without the strict IDs. And yes, I think it's very relevant to the discussion of adding unlimited regions, as even regions should be Dynamic. If you're going to do it, may as well do it right.
Title: Re: Re: Ideas for UI in the context of unlimited regions
Post by: Crimson Wizard on Wed 26/06/2013 23:07:04
Quote from: Ryan Timothy on Wed 26/06/2013 22:56:56And yes, I think it's very relevant to the discussion of adding unlimited regions, as even regions should be Dynamic. If you're going to do it, may as well do it right.
Dynamically created entities were in plans, but I wasn't intending to do this right away. I can't tell for sure how much more time this will take, and I have no idea if I will find time (and enough devotion) to work on this.
Meanwhile few people already asked me personally for a version with increased limits of objects. So here it goes.
And while what you say may be related to unlimited regions, this topic is not about unlimited regions. It is about Editor UI. (Not any more, it isn't. - moderator)


EDIT: removed overly emotional line.
Title: Re: Re: Ideas for UI in the context of unlimited regions
Post by: Calin Leafshade on Wed 26/06/2013 23:21:03
Quote from: Ryan Timothy on Wed 26/06/2013 22:56:56
Nope. I meant exactly what I said. Accessing "Objects" by a strict ID is a terribly, terribly poor coding practice ("Objects" being GUIs, Characters, Objects, Hotspots, Regions, WalkableAreas, etc). They should be accessible via an ArrayList (which of course is the same thing, but you shouldn't have Character.ID to find what index it is in the ArrayList).

Says who?

There's nothing intrinsically wrong with using integer IDs as part of an array. Providing the characters are also available as global, named symbols it doesn't matter.

Also, beware foreach loops. They have sneaky caveats, especially in game programming.

Title: Re: Re: Ideas for UI in the context of unlimited regions
Post by: Ryan Timothy B on Wed 26/06/2013 23:45:22
Says me obviously.  :P  If you ever introduced a delete method to delete an "Object" it would reek havoc on any code you have. Whereas the currently deleted character index would be swapped with the last index (or having the whole array sort itself to remove the deleted index).

The only reason I believe it's even useful to have "Object".ID at this very moment is because we don't have support to extend to the current AGS structs yet.
Title: Re: Re: Ideas for UI in the context of unlimited regions
Post by: Crimson Wizard on Thu 27/06/2013 00:03:27
I had an argue about IDs with Iceboty 7000 about a year ago, and funny thing is, back then I was on Ryan's place.
(I may try to find the thread later)

However I quickly learnt there are cases when you must have a way to iterate through part of array, or take out an item by its relative position.
Of course there are ways to do this without numeric ids, but they may be too difficult for usual user of AGS. Any way, this is absolutely not a simple matter.


E: Ah, here you go:
http://www.adventuregamestudio.co.uk/forums/index.php?topic=36835.msg627199#msg627199
Title: Re: Re: Ideas for UI in the context of unlimited regions
Post by: tzachs on Thu 27/06/2013 08:50:56
Quote from: Crimson Wizard on Wed 26/06/2013 22:32:12
tzachs, if I understand you right, what you say sounds like a simplified version of my old suggestion
Yes, I guess you can say that..

Quote from: Calin Leafshade on Wed 26/06/2013 23:21:03
Also, beware foreach loops. They have sneaky caveats, especially in game programming.
What? Care to explain that?
The opposite is true, as far as I know, at least in c#. By using foreach you allow the compiler to make optimizations so it'll be more efficient.
The only scenarios which you'd prefer to use 'for' is if:
1. You have an interest in the index as well as the object you're iterating on.
2. You want to remove items from the collection while iterating on it, then you'll do an opposite for (from end to start).

Title: Re: Re: Ideas for UI in the context of unlimited regions
Post by: Calin Leafshade on Thu 27/06/2013 13:45:34
Quote from: tzachs on Thu 27/06/2013 08:50:56
What? Care to explain that?

foreach loops create extra work for the garbage collector because they create extra references. This can be especially problematic in game programming because it can potentially create several thousand new references every second (a particle system perhaps). Also the .NET GC is non-deterministic so you can never be sure exactly when the GC will do its work which can create random usage spikes which can severely fuck with your frame rate, especially in embedded systems like the Xbox360 or a phone.
Title: Re: Re: Ideas for UI in the context of unlimited regions
Post by: Ryan Timothy B on Thu 27/06/2013 14:28:12
Oh right. I remember reading about that on the LibGDX forum.
Title: Re: Re: Ideas for UI in the context of unlimited regions
Post by: Snarky on Thu 27/06/2013 14:58:46
I agree with Calin in that there's nothing inherently wrong with using an integer index to access data objects, and that it's important to have a way to loop through multiple objects. Using an iterator in a foreach loop may be a bit more elegant, but it doesn't really improve the program logic in any way, and as pointed out is has implementation drawbacks.

In any case, AGS script is pretty far from supporting something like that. We don't have ArrayLists, or any good way of implementing them. Hell, we don't even have for loops! Maybe deal with that first, before jumping ahead to foreach? In the mean time, the built-in arrays offer a simple and perfectly workable way to iterate through the different data objects. It's not "sticking to backwards compatibility" to preserve a useful feature when a possibly superior alternative remains out of reach for the time being.
Title: Re: Re: Ideas for UI in the context of unlimited regions
Post by: Ryan Timothy B on Thu 27/06/2013 15:59:00
I believe me adding the foreach in that code snippet has confused you folks (I'm not actually sure why I even included that into my example in the first place). I never meant this conversation to argue that accessing "Objects" by array was bad coding practice - who the hell would want to abolish an array via index? I was stating that getting the index the "Object" (also via "Object".ID) shouldn't be hardcoded AT ALL.

For instance: when you add a character in the tree, it shouldn't be
0: cGeorge
1: cSally
2: cFrank

Because when you are scripting, you can go crazy typing character[1] all over the place and know it'll always be cSally. But if AGS were to one day improve itself, and one were to ever delete cSally, or insert a new character before cSally, then goodbye to having character[1] point to cSally.

And I apologize for this to have derailed your thread, Crimson. I agree to the topic split.
Title: Re: Accessing AGS objects by array index
Post by: Snarky on Fri 28/06/2013 12:12:58
I don't really see your point, Ryan. As long as the array index is the best way to iterate through multiple AGS objects, (or to do various forms of "pointer arithmetic"), we need a way to tell which index corresponds to which object. If you write some code that affects some subset of characters, for example, you probably want to check that the range is the right one. If you don't have it in the editor and not as a field, how are we supposed to use it at all? Iterate through the array until we find a match?

Sure, you shouldn't go around referring to a known character by its index (since, as you say, the link between index and object is less immediate and less stable), but that's a matter for good coding practice, not an argument for removing the engine or editor feature.
Title: Re: Accessing AGS objects by array index
Post by: Ryan Timothy B on Fri 28/06/2013 17:08:49
Quote from: Snarky on Fri 28/06/2013 12:12:58
I don't really see your point, Ryan.
I know you aren't. I don't know why you don't see the point, which is why I'll have to explain it in full depths. (laugh)

Let me walk you through this so you understand in clear logic. In AGS create 2 GUIs, 2 buttons, 2 dialogs, 2 characters, 2 objects, etc. Then access these elements via script referring to them by the very specific ID AGS allows you to believe they are. For example: your first character is Character1, second is Character2. The Character ID's are as follows:

Character1 = character[0]
Character2 = character[1]

Or as I said above:
Quote0: cGeorge
1: cSally
2: cFrank

You know that when you type the following it will always point to the Character1, because AGS tells you it will.
Code (ags) Select
character[0].Visible = false

Now after you've added all that code pointing to character[0] or any of those multiple "Objects", delete it from the editor.

My argument is that it is using hardcoded numbers in the EDITOR, making you feel safe that that's the ID number to use, when in fact it ISN'T. Deleting that numbered "Object" only reorganizes all other objects, therefor making the constant ID mute and a TERRIBLE design for the editor. If I still don't have a bandwagon of followers, I'll have to post a double face palm picture or something of the sort.  (laugh)

Now in a future version of AGS where everything is Dynamic, the "Object".ID can still actually stay, just have the Get function access the ListArray like in the code example I posted above. Having it find the first and only matching object in the array, telling you the index. Or you can go the arduous route of an integer variable within every object, but that requires a change every time other objects get a new order - which is a much heavier issue to deal with and more prone to errors on the editor/engine scripting side of things.

Just so we're clear, I'll say it one last time to be sure. AGS shouldn't tell you what ID the "Object" is in the tree. If ANYTHING, it should be assigned during compile time. Thank-you for your time.

Also that's why I posted this in the Editor thread, but that's fine.. It can always get moved back. But now that I've reread my posts, I suppose my arguments have been half and half on both matters.

Edit: I'm just really terrible at clearly pointing out and describing what I thought was a very known caveat about the editor.
Title: Re: Accessing AGS objects by array index
Post by: Snarky on Fri 28/06/2013 17:36:26
Quote from: Ryan Timothy on Fri 28/06/2013 17:08:49
You know that when you type the following it will always point to the Character1, because AGS tells you it will.
Code (ags) Select
character[0].Visible = false

Now after you've added all that code pointing to character[0] or any of those multiple "Objects", delete it from the editor.

My argument is that it is using hardcoded numbers in the EDITOR, making you feel safe that that's the ID number to use, when in fact it ISN'T. Deleting that numbered "Object" only reorganizes all other objects, therefor making the constant ID mute and a TERRIBLE design for the editor. If I still don't have a bandwagon of followers, I'll have to post a double face palm picture or something of the sort.  (laugh)

Well, I get what you're saying now, but you're making an IMO unreasonable leap from "AGS displays this value and I can use it in my script" to "this value will never change, no matter what changes I make to my project."

Of course, in your code example there's no reason not to refer to the character explicitly by scripting name, but imagine that you wanted a feature in your game to put hats on any character. One way you might implement this would be to make each hat a character and have them follow the "real" characters around. If we pretend FollowCharacter() doesn't exist, you might code it something like this (simplified example):

Code (AGS) Select

#define HATTABLE_START 15
#define HATTABLE_COUNT 8
#define HAT_START      328

function repeatedly_execute()
{
   int i=0;
   while(i<HATTABLE_COUNT)
   {
      if(character[HAT_START + i].Room != character[HATTABLE_START + i).Room)
         character[HAT_START + i].ChangeRoom(character[HATTABLE_START + i).Room);
      character[HAT_START + i].x = character[HATTABLE_START + i).x;
      character[HAT_START + i].y = character[HATTABLE_START + i).y;
      i++;
   }

   // ...
}


Where the defined values are the character indexes used as slots in the editor. OK, if I delete a character I might have to change these values (one reason I "never" use magic numbers in the body of my code; I always try to define them with meaningful names at the top of the script), but hey, that's development for you!

If you took away this feature, how would you deal with situations like this?
Title: Re: Accessing AGS objects by array index
Post by: Monsieur OUXX on Fri 28/06/2013 18:16:09
integer IDs have a reason to exist only if they are unique (that is: even if you delete an object in the array, and create a new one somewhere else, then the new one cannot receive an ID that was given before).

And if they're used that way, they would only exist as "convenient" way for the end-scripter to compare two objects. For example, in a loop. By convenient, I mean "for someone who isn't comfortable with comparing references".

But, as it has been said before, if kept, the IDs couldn't be used any more as a way to iterate on arrays and stuff. They would become totally independent from indices.

==============

Ryan, I understand your point, but you're saying nothing new : using "character[1]" instead of "cSally" is bad coding practice. The scripter should use indices ( [1] ) only if he needs some abstract arithmetics on objects : iteration, addition, shifting, whatnot. And if he wants to do that, we assume he's scripted long enough to understand what he's doing. (e.g. "not deleting an object in an array while iterating on the array, using the last index as an exit condition" -- classic mistake with beginners, in any language)

Title: Re: Accessing AGS objects by array index
Post by: Ryan Timothy B on Fri 28/06/2013 18:21:55
That right there is an example of borderline magic numbers - which lead to error prone code.

There are numerous ways to deal with something like that, one being this:

Code (ags) Select

#define CHARACTER_COUNT 44
struct CharacterHat {
  Character* Hat;
};
CharacterHat characterHat[CHARACTER_COUNT];

void AssignHat(this Character*, Character* hatCharacter) {
  characterHat[hatCharacter.ID].Hat = hatCharacter;
}

function game_start()
{
  if (CHARACTER_COUNT < Game.CharacterCount) AbortGame("Due to AGS limitations: CHARACTER_COUNT is less than Game.CharacterCount");
 
  cGeorge.AssignHat(cGeorgeHat);
}

function repeatedly_execute()
{
  int i;
  while (i < Game.CharacterCount) {
    if (characterHat[i].Hat != null) {
      if (character[i].Room != characterHat[i].Hat.Room) {
        characterHat[i].Hat.ChangeRoom(character[i].Room);
      }
      characterHat[i].Hat.x = character[i].x;
      characterHat[i].Hat.y = character[i].y;
    }
    i++;
  }
}


And yes, before you mention it, I did use character.ID. Only reason is that I can't extend the Character struct (which to me is the main reason why anyone should ever need the ID). Edit: I've deleted the struct extend example because I'm quite sure I screwed it up.

Edit: With your example, if you suddenly realized you forget to add a character to the tree and its hat character, good luck getting his hat to match without deleting all characters to make the IDs match again.
Title: Re: Accessing AGS objects by array index
Post by: Ryan Timothy B on Fri 28/06/2013 18:41:46
Quote from: Monsieur OUXX on Fri 28/06/2013 18:16:09
Ryan, I understand your point, but you're saying nothing new : using "character[1]" instead of "cSally" is bad coding practice.
No, quite frankly you don't see my point. You should NEVER have a reason to use character[1] instead of cSally. NEVER! Not even if you're a level 100 master programmer.  :-D

When you add a button in Visual Studio, does it tell you that the ID is 0. No. Why? Because why would you reference the button via button[0] when you have a completely error prone method via bButton1.

Quit trying to argue that it makes sense to use character[1] to point to cSally. It doesn't and never will. No. (face palm coming very soon)

Edit: Actually the funniest thing you've said is this:
QuoteAnd if he wants to do that, we assume he's scripted long enough to understand what he's doing.
This is AGS. Nearly every programmer making AGS games are completely new to the experience. If ANYONE, advanced programmer or complete noob use the array pointer instead of the syntax name (whatever the correct terminology is for that), they obviously don't know what they're doing. Unless you access it via array looping statement, but that's not what we're arguing here.
Title: Re: Accessing AGS objects by array index
Post by: monkey0506 on Fri 28/06/2013 19:32:45
Quote from: Ryan Timothy on Fri 28/06/2013 18:21:55
Code (ags) Select
struct CharacterHat extends Character { ... };

// ...

cGeorge.Hat = cGeorgeHat;

Firstly, I wanted to say that the way you've defined it here isn't accepted in any real programming language. "cGeorge" is an instance of the base class Character, and will never be an instance of CharacterHat. Now, it would be feasible to allow extender attributes (in addition to extender methods), which would ultimately amount to the same thing. But your definition breaks good coding convention.

The following uses extender methods. If extender attributes were implemented, they would be syntactic sugar for the following.

Code (ags) Select
Character *characterHats[];
import Character* GetHat(this Character*);
import void SetHat(this Character*, Character *hat);

function game_start()
{
  characterHats = new Character[Game.CharacterCount];
  cGeorge.SetHat(cGeorgeHat);
}

function repeatedly_execute()
{
  int i = 0;
  while (i < Game.CharacterCount)
  {
    Character *c = character[i];
    Character *hat = c.GetHat();
    if (hat != null)
    {
      if (c.Room != hat.Room) hat.ChangeRoom(c.Room);
      hat.x = c.x;
      hat.y = c.y;
    }
    i++;
  }
}


This example avoids the problem of linking the hat by ID, but iterating the characters and their hats (in the absence of a foreach loop (for wouldn't be sufficient, obviously)) still requires using the ID. For simplicity and readability sake, I've stored references in the pointers.

(Edit: I've also gone back and realized what you meant by "Due to AGS limitations" in your code snippet that did have an extender method. You were wrongly and falsely indicating that AGS provides no means of creating an array based on the size of Game.CharacterCount. Granted, your example is a primitive one, and in the real world the structure would likely be much more complex, and it is true that AGScript won't allow dynamic arrays of custom struct types (due to the no pointer issue). However, any keen module developer wouldn't stop writing just because they had to avoid using structures in favor of several file-scope arrays.)

I agree that in the majority of code, referencing something by it's ID should be avoided. Again though, without a foreach loop there's no way of iterating certain things without it.

Oh, and as for the "Game.Characters.IndexOf(Character*)" method:

Code (ags) Select
int IndexOfCharacter(Character *c)
{
  if (c == null) return -1;
  int i = 0;
  while (i < Game.CharacterCount)
  {
    if (c == character[i]) return i;
    i++;
  }
  return -1;
}


Of course, this is exactly equivalent to just accessing Character.ID.
Title: Re: Accessing AGS objects by array index
Post by: Ryan Timothy B on Fri 28/06/2013 19:56:17
Lol about my extending example. I actually edited my post to remove it before I saw yours. I was sitting here working on my 3D modeling when it suddenly hit me how wrong that code was. I've only extended a Class a few times and that was over a year ago.  (laugh)

As for my example using a struct, I only did that for organization. Since you'd likely have X and Y offsets and such. I kept it simple as an example with only the character pointer, but you're right, my example should've been a dynamic array (or a bunch of dynamic arrays - if you were to add more elements). There's nothing wrong with characterHats_Hat[] versus characterHats[].Hat. I just have slight OCD tendancy when it comes to keeping things grouped together. lol

The main question is, do you agree about AGS not showing the developer what the index of the "Object" is in the tree?
Title: Re: Accessing AGS objects by array index
Post by: Snarky on Fri 28/06/2013 19:58:21
Ryan, in your code you're almost literally recreating the wheel by creating an unnecessary duplicate of an already-existing data structure. Yes, it gives you some increased flexibility, but it has definite drawbacks as well (for example, the way you've implemented it you have to iterate through every character each cycle, even though you already know that half of them are hat-dummies). I really don't see it as any better than my example.

Quote from: Ryan Timothy on Fri 28/06/2013 18:21:55
That right there is an example of borderline magic numbers - which lead to error prone code.

Direct access to object array indices is a convenience/optimization that is useful for games programming. Like, say, pointer arithmetic (which is effectively what it is), it breaks out of the abstractions of the Object-Oriented layer, which means you should take care when using it. But as long as you're disciplined and know what you're doing, it works and makes for elegant code.

Quote from: Ryan Timothy on Fri 28/06/2013 18:21:55
Edit: With your example, if you suddenly realized you forget to add a character to the tree and its hat character, good luck getting his hat to match without deleting all characters to make the IDs match again.

That's a good argument for giving users a way to reorder sprites/characters/etc., or for inserting a new one in the middle of the sequence.

Quote from: Ryan Timothy on Fri 28/06/2013 18:41:46
When you add a button in Visual Studio, does it tell you that the ID is 0. No. Why? Because why would you reference the button via button[0] when you have a completely error prone method via bButton1.

UI programming isn't like games programming. But for analogy: once you add a button to a Window in e.g. C#, you can access it by indexing into that Window's Controls[]. You could abuse that by hardcoding references to certain indexes (which would break if you changed the Window controls), but that's no reason not to offer the functionality. (The main difference is that the index is assigned dynamically at runtime, rather than in the editor - although usually you can easily calculate what it's going to be ahead of time - but that just makes using the index more risky in C# than in AGS.)

Quote from: Ryan Timothy on Fri 28/06/2013 18:41:46Quit trying to argue that it makes sense to use character[1] to point to cSally. It doesn't and never will. No. (face palm coming very soon)

Mr. Ouxx explicitly said it doesn't make sense, that you should only use it for abstract arithmetics on characters (or other game objects).

Quote from: Ryan Timothy on Fri 28/06/2013 18:41:46This is AGS. Nearly every programmer making AGS games are completely new to the experience. If ANYONE, advanced programmer or complete noob use the array pointer instead of the syntax name (whatever the correct terminology is for that), they obviously don't know what they're doing. Unless you access it via array looping statement, but that's not what we're arguing here.

I think that's exactly what we're discussing here. Looping as well as pointer arithmetic-style calculations.
Title: Re: Accessing AGS objects by array index
Post by: Ryan Timothy B on Fri 28/06/2013 20:28:13
Quote from: Snarky on Fri 28/06/2013 19:58:21
I really don't see it as any better than my example.
I do. Flexibility and yours is chalked full of potential issues.

If you were creating a module, would you have:
  hatModule_SetStartCharacterIndex(50);
  hatModule_SetHatStartCharacterIndex(60);
  hatModule_SetHatCharacterAmount(10);

Nope. Or at least I would hope not.

If AGS had ArrayLists, I wouldn't need to iterate through every character, I could simply just iterate through the ArrayList which contains all the Characters with Hats.

QuoteDirect access to object array indices is a convenience/optimization that is useful for games programming.
Direct access yes, but not having AGS tell you beforehand so you can code everything with the indices - aka magic numbers.
Title: Re: Accessing AGS objects by array index
Post by: Snarky on Fri 28/06/2013 20:51:38
Quote from: Ryan Timothy on Fri 28/06/2013 20:28:13
If you were creating a module, would you have:
  hatModule_SetStartCharacterIndex(50);
  hatModule_SetHatStartCharacterIndex(60);
  hatModule_SetHatCharacterAmount(10);

Nope. Or at least I would hope not.

No, I would put in the readme file to edit the #define statements at the top of the module to match whatever had been set up in the editor.
Title: Re: Accessing AGS objects by array index
Post by: Crimson Wizard on Fri 28/06/2013 21:22:38
I don't want to blame anyone, but frankly I don't like how this discussions goes: a bit of mess IMHO.
I think you are starting to mix numbers being Keys and numbers being Order, which is different things.
Like in above example:

hatModule_SetStartCharacterIndex(50);

What is 50? Is it an order of item in internal array, or a key? If it is a key, it is as valid as a string, or a pointer. The only serious difference is that remembering the string or object name may be easier for us, people (at least for normal ones ;)).
E: err, I should have said - "holds more obvious meaning", of course.
Title: Re: Accessing AGS objects by array index
Post by: Ryan Timothy B on Fri 28/06/2013 21:34:50
It was just a code example based off the code Snarky posted about how he finds the index being shown to you editor side as useful.

Quote from: Snarky on Fri 28/06/2013 17:36:26
Code (AGS) Select

#define HATTABLE_START 15
#define HATTABLE_COUNT 8
#define HAT_START      328

I didn't want to scroll up so I just tossed in arbitrary numbers as he had.

Edit: I was just proving my point at how unorganized it was.

Edit again: Snarky, how does someone take a hat off? ;)  (I meant with the module of yours - you don't have to post code, I was only being an arse)
Title: Re: Accessing AGS objects by array index
Post by: Snarky on Fri 28/06/2013 22:05:45
Quote from: Ryan Timothy on Fri 28/06/2013 21:34:50
Edit: I was just proving my point at how unorganized it was.

Configuring key values at the top of the script is disorganized now?

Quote from: Ryan Timothy on Fri 28/06/2013 21:34:50
Edit again: Snarky, how does someone take a hat off? ;)  (I meant with the module of yours - you don't have to post code, I was only being an arse)

Obviously neither your version nor mine is a complete solution, and the example is a bit contrived anyway because as I said up top, you could simply use FollowCharacter(). My point was to demonstrate using the array indices to iterate over a subset of characters and do logic based on the index. Which I maintain are useful coding techniques (not just for characters, but for views, sprites, hotspots, room objects, music tracks, etc.). If the only argument against it is "if you delete a character, you'll have to change the numbers you use for the indices," I don't find that a major problem, since with good coding practice it's a simple change in a single place in the script.

To talk about how to improve object referencing in general, I say get rid of the ridiculous hardcoded behavior for Hungarian notation, where you use different versions of the name in the AGS script and the dialog script IF the name is prefixed with certain magic characters. (The whole thing is confusing enough that I'm actually not 100% sure exactly how it works.)
Title: Re: Accessing AGS objects by array index
Post by: Monsieur OUXX on Fri 28/06/2013 22:24:30
Quote from: Ryan Timothy on Fri 28/06/2013 18:41:46
Quit trying to argue that it makes sense to use character[ 1 ] to point to cSally. It doesn't and never will. No. (face palm coming very soon)

As I wrote: using that to access only cSally is bad practice. But otherwise, I believe what you wrote is not correct (and also very condescending). How do I iterate on a collection of objects if I have neither "objects[ i ]" nor an iterator (foreach or something else) ?

I understand how you misunderstood what I wrote.
But Crimson wizard is right : in this discussion, there are people talking about the Keys, and some talking about the Orders. That was the point of my very first post. Nobody can agree on anything until everybody has decided what they want to talk about.
Title: Re: Accessing AGS objects by array index
Post by: tzachs on Fri 28/06/2013 22:59:31
Quote from: Calin Leafshade on Thu 27/06/2013 13:45:34
foreach loops create extra work for the garbage collector because they create extra references. This can be especially problematic in game programming because it can potentially create several thousand new references every second (a particle system perhaps). Also the .NET GC is non-deterministic so you can never be sure exactly when the GC will do its work which can create random usage spikes which can severely fuck with your frame rate, especially in embedded systems like the Xbox360 or a phone.
Ok, so I did a search to try and find the source. I found this: http://blogs.msdn.com/b/etayrien/archive/2007/03/17/foreach-garbage-and-the-clr-profiler.aspx

He didn't explain the weirdness of what he experienced, here's what's going on behind the scenes: the compiler, when facing a foreach wants to do the best it can. So when it sees a foreach on a list, array or pretty much stuff with indexers, it will translate it to a standard for..
In the examples in the link it didn't do it for Collection and IEnumerable because they don't have an indexer, so you couldn't write a standard for loop for those if you wanted to.
In the second example the author 'tricked' the compiler by passing a list to a method accepting IEnumerable, so the compiler couldn't make that optimization, and used the enumerator which is the extra reference..
Granted, that is something we should be aware of, but it's not such a common scenario that deserves the 'beware foreach' comment..

On the other hand, foreach gives us benefits, not counting the better readability.
First, it saves from a common sceanrio where the developer accesses the indexed object more than once inside the loop.
Second, since c# is a managed language, it is also safe, so you wouldn't accidentally access somebody else's memory. So when you access an index, it actually adds boundary checks, which hurts performance. When doing foreach it knows it can skip the boundary checks. In truth, it also make this optimization in the standard for loop, but you can easily trick it, for example in another common scenario where you take the length to a local member outside the for declaration. In c++ it's good for performance since it avoids extra access to the length property, but in c# it's the other way around, and by using foreach you won't be able to make that mistake..
Title: Re: Accessing AGS objects by array index
Post by: Ryan Timothy B on Sat 29/06/2013 06:32:46
Quote from: Monsieur OUXX on Fri 28/06/2013 22:24:30
How do I iterate on a collection of objects if I have neither "objects[ i ]" nor an iterator (foreach or something else) ?
I know you've crossed this text out, but it leaves me wondering if you or anyone actually believe I want to abolish accessing AGS "Object" members via array using indices. I realize after I had read them again that my posts above have come across this way, but this is not the case at all (I think it was this post (http://www.adventuregamestudio.co.uk/forums/index.php?topic=48535.msg636459513#msg636459513). I had quickly quoted Crimson and Mite, wrote some code examples and then forgot to break up the quotes later to further explain that I hadn't been talking about the "Object" array - oh well).

I suppose whoever moved this thread over and named the thread "Accessing AGS objects by array index" didn't help matters.

Also I'm sorry if I've come across as condescending. We're like a group of friends gathering together on the net. I get heated up in a debate that, to me, is completely logical to rid AGS of the editor-side hardcoded indices. Having them assigned at compile time, where you wouldn't know what they are before compile which can only lead to fallible code.

Now with me having discussed ridding the Object.ID was mostly if AGS were to advance in the scripting department. So that was me talking about a semi-future AGS, not this one. Overall there isn't a huge issue having access to it in this manner, but I personally can't see you needing it. If you did though, you would have access to the index with the Object's ArrayList. On an unrelated personal opinion, I am slightly bothered by how it's called ID instead of Index - perhaps it's a personal vendetta. :-D

Quote from: Snarky on Fri 28/06/2013 22:05:45
Configuring key values at the top of the script is disorganized now?
In my opinion, from that example, yes. it's not exactly a coding practice I would teach anyone (on a side-note: there's actually a fog module that Dualnames mostly wrote off some code I supplied him. I did that very same thing with index start and amount. It was only as a quick example to show how the fog should be designed - graphically, not scripting. I never meant for it to turn into a module or ever be used in someone's game. I feel guilty about that module even existing. Ha).
Title: Re: Accessing AGS objects by array index / Hardcoding editor values in code
Post by: Snarky on Sat 29/06/2013 09:58:53
So if I understand you correctly this time around, Ryan, you are objecting to the idea that the object indexes (of characters, but the same system is used for views, sprites, fonts, rooms, GUIs, etc.) are shown in the editor and are known before compile time, so that people can use those values in their code as "magic numbers" and exploit the index-ordering in their game logic?

First, I think without this, the IDs are nearly useless, because unless you are simply looping through every character, you're going to want to restrict it to some index range, and that relies on knowing something about how they are ordered.

Second, not all the AGS object types have scripting names (sprites, views and speech clips being the most obvious), and it's not obvious that forcing a user to name every sprite (for example) is a good idea. I don't see us eliminating the notion of indices entirely from AGS, so there's a lot to be said for consistency in how they're presented and used.

Third, there's really no engine reason not to assign character IDs before compile-time. Even if we remove the character limit and make the number of characters dynamic (ArrayList instead of Array), they're still defined in the editor, no? Only if you actually wanted to generate new character dynamically at run-time would it be impossible to know the IDs ahead of time.

Fourth, I don't really see what your problem is with the existing feature. Some sort of visceral revulsion? That it "feels" too low-level and not properly object-oriented? Not compelling arguments to me.

Anyway, since different participants seem to have had different understandings of what we were discussing, I've renamed the thread again.
Title: Re: Accessing AGS objects by array index / Hardcoding editor values in code
Post by: Ryan Timothy B on Sat 29/06/2013 11:24:14
Quote from: Snarky on Sat 29/06/2013 09:58:53First, I think without this, the IDs are nearly useless, because unless you are simply looping through every character
Yep. The only reason they should have any use would be because we currently still require the ID for making matching arrays. Like the hats example (preferably the one Monkey posted). Unless in the future there was a way to add your own properties to any Object. Or a ListArray of any AGS variable type - including structs.

Quoteyou're going to want to restrict it to some index range, and that relies on knowing something about how they are ordered.
Which, as I've said, is error-prone and a very bad coding practice. ;)

QuoteNot all the AGS object types have scripting names (sprites, views and speech clips being the most obvious)
Views have their own variable name. Albeit the View is actually just an integer variable, and I'm pretty sure very few people actually use it.

My main argument was about Characters, Objects, GUIs and the Controls. Everything else can slowly follow along.

QuoteIt's not obvious that forcing a user to name every sprite (for example) is a good idea.
But the sprite is already named before you import the file. ;)   [/joke]
I haven't had a full desire of sprites dropping the ID number. But I certainly wouldn't argue as it's much nicer reading code that has readable sprite name like this   Sprites.George.Stand.Left001   vs    5.  What sprite is 5? I don't remember, let me keep browsing through all my sprite folders until I find it. (laugh)

QuoteOnly if you actually wanted to generate new character dynamically at run-time would it be impossible to know the IDs ahead of time.
Or as I've discussed about deleting a character via the editor before compiling. Calling it by the Index instead of the variable name is almost as bad as randomly doing  character[10]  and hoping it points to the right one. Without extensive searching through code to find the problem, no one would be able to work on your project, and you couldn't walk away from it for a year and come back. Once you delete that one character everything goes to hell.

QuoteFourth, I don't really see what your problem is with the existing feature. Some sort of visceral revulsion? That it "feels" too low-level and not properly object-oriented? Not compelling arguments to me.
To be honest, yes. Except for the visceral revulsion part, because that would be better described as your feelings. All my points why we shouldn't know the indices before compile are logical and would help make everyone a better coder. You want the exact opposite.

As for the low-level and not properly OO, that's the main reason. I haven't come across a single UI compiler that assigns the Index for you to see before compile; for the very reason that it allows the  possibility for poor coding.

QuoteAnyway, since different participants seem to have had different understandings of what we were discussing, I've renamed the thread again.
I still don't agree with the name because I never tried to change how we access objects by index. Which is why I suggested you to name it: 
AGS Indexing Objects  [shouldn't be assigned before compile]
That's all I wanted this thread to be talking about and I don't want others coming along thinking someone wants to eliminate accessing objects by array index.

As for why I ever mentioned removing the property Object.ID and replacing it for the Game.Characters.IndexOf(Character*) was for the very same reason. Having the ID as a property to the Object will give any inexperienced coder a sense of security that the index integer they receive will always point to their Character. But if it's something they can only get via the Character array, it may help them realize that it's not the Character "ID", it's actually the index of the Character in the array. Right now there's no issue having Object.ID as we don't have the ability for dynamic objects yet.

So could you please, just rename it to what I suggested and allow this thread be a tiny little bit less confusing.
Title: Re: Accessing AGS objects by array index / Hardcoding editor values in code
Post by: MiteWiseacreLives! on Wed 03/07/2013 17:17:14
I don't know if this topic is dead, but I just got the chance to read it. I just finished(well sorta finished) a game where I had to several times iterate through characters and objects, Crankosaur, to sometimes apply stuff that was not in the editor plane or just redundant and time consuming for 25 characters.
@Ryan, I know you don't want to abolish the ID #'s (character[ID] etc ), but not having them visible in the editor would be a nightmare! For example if you use folders, like I did, and go back up into the middle of the tree and add 13 characters to a folder these get assigned a sequential ID. Now how do I figure out if its Character[17] to Character[29] I want to suddenly move baselines on or Character[7] to Character[19], as it would seem if expand all the trees and start counting, without the ID's being visible. And as a newb protection, does not AGS adjust the visible numbering of all the Characters when you delete Character[2] or whatever, you could discover how you broke your game pretty quickly IMO by looking.
Just my thoughts.
Title: Re: Accessing AGS objects by array index / Hardcoding editor values in code
Post by: Ryan Timothy B on Thu 04/07/2013 00:07:09
The reason anyone would argue that it's useful to see the Index before compile is, simply and only, that they aren't an adept programmer; which, granted, is the majority of AGS users. Or they're being lazy. Since scripters have visible access to this index before compile, it allows them to possibly make faulty code (unless they were amazing and never deleted a single Object - which is likely super rare; or were clever enough to backtrack to all their code pointing to that specific index and renumber them).

There are numerous ways to store special Objects instead of relying on the visible Index - without having the Index A to B scripting method. With the index being visible to all, it only teaches people to rely on it and therefor become be a problematic scripter.

Properties is one method (although currently it's still read-only, but Crimson is doing a fantastic job at tweaking the engine). With that you could assign a boolean to every Object that is given this special treatment. But of course with this method you need to iterate through each and every character to find the ones with that property. Snarky's comment on that matter was quite moot as a deterrent, as iterating through 30-40 characters (likely to be the max in the majority of games) wouldn't show you any signs of slowdown.

Other methods being a dynamic array which holds each special Object. One basic solution being this (warning: typed in browser):
Code (ags) Select
Character* specialCharacter[];
int specialCharacterCount;

void SpecialCharacter_Add(Character* specialCharacter) {
  z_specialCharacter[];
  if (specialCharacterCount > 0) z_specialCharacter = new Character[specialCharacterCount];
  specialCharacter = new Character[specialCharacterCount + 1];
  if (specialCharacterCount > 0) {
    int i;
    while (i < specialCharacterCount) {
      specialCharacter[i] = z_specialCharacter[i];
      i++;
    }
  }
  specialCharacter[specialCharacterCount] = specialCharacter;
  specialCharacterCount++;
}

void SpecialCharacters_ChangeRoom(int room) {
  int i;
  while (i < specialCharacterCount) {
    specialCharacter[i].ChangeRoom(room);
    i++;
  }
}

void SpecialCharacter_Remove(Character* removeCharacter) {
  int i;
  while (i < specialCharacterCount) {
    if (specialCharacter[i] == removeCharacter) {
      specialCharacterCount--;
      specialCharacter[i] = specialCharacter[specialCharacterCount];
      return;
    }
    i++;
  }
}

// called when the game starts, before the first room is loaded
function game_start()
{
  SpecialCharacter_Add(player);    // Add a character to the special character custom List
  SpecialCharacters_ChangeRoom(player.Room);    // Change the room of ALL the special characters
  SpecialCharacter_Remove(player);    // Remove character from the special character List
}


Of course with the ability to have custom struct pointers and ArrayLists, or other fun stuff, something like this would be less than a dozen lines of code.
Title: Re: Accessing AGS objects by array index / Hardcoding editor values in code
Post by: Crimson Wizard on Thu 04/07/2013 00:34:43
Quote from: Ryan Timothy on Thu 04/07/2013 00:07:09
Properties is one method (although currently it's still read-only, but Crimson is doing a fantastic job at tweaking the engine).
Yeah... (https://github.com/adventuregamestudio/ags/commit/2427d410a4713843a0c66b867889fe9927a3e475)(heh) to the point when it stops working. (wtf)

Anyway, the thing is that it might be much faster both for user and for program to iterate between two indexes, rather then fill the custom array every time.
And what if the range is varied?
With indexes you can do
Code (ags) Select

function DoActionWithObjects(int first, int last)
{
}

While without them you'll have to manually fill the custom array, checking some property maybe, and don't forget to change that property if you need to save the object from being included to that list.
Code (ags) Select

         ---(pseudo-code)---
int obj;
while (obj < Room.ObjectCount)
{
   if (object[obj].GetProperty("blah") == 1)
      AddObjectToArray(obj);
   obj++;
}

DoActionWithObjectArray();


Yes, that's safer, and more formal, but that would be real pain to program to suit your particular needs. Besides it is slower than it could be. The latter may or may not be an issue, though I think I am getting paranoid over execution speed lately (seeing as a piece of code may slow down game execution by several percents if moved to separate function).


The real solution here, IMHO, would require to change the scripting paradigm. Scripter would need to seek the root of why he needs a range of indices in this particular case, and radically change the implementation. If the range of objects is constant, the special list could be precreated beforehand at the game start, or even at design time by creating a "filter" folder and filling it with visual "references". If the list is changing over time, it could be implemented as a linked list for faster insertion/removal.
Title: Re: Accessing AGS objects by array index / Hardcoding editor values in code
Post by: Snarky on Thu 04/07/2013 07:11:15
I left the topic alone because I felt it was going around in circles, but since it's come back to life...

Quote from: Ryan Timothy on Thu 04/07/2013 00:07:09
The reason anyone would argue that it's useful to see the Index before compile is, simply and only, that they aren't an adept programmer; which, granted, is the majority of AGS users. Or they're being lazy. Since scripters have visible access to this index before compile, it allows them to possibly make faulty code (unless they were amazing and never deleted a single Object - which is likely super rare; or were clever enough to backtrack to all their code pointing to that specific index and renumber them).

Being lazy is a positive trait! It's a matter of pragmatism: preferring a simple, straightforward solution that works to an overly complicated/slow workaround, even if it breaks some abstract notion of what "good code" looks like. (Similarly, the dialog system uses GOTO, even though it's often "considered harmful," because that's a simple and straightforward way to implement dialog trees.)

"Allows them to possibly make faulty code"? Any programming construct has the possibility of errors! Yes, if someone writes code that references specific items in the object arrays, they have to watch out for those references changing. Is that really so hard? I think with a few simple programming practices (e.g. defining index references at the top of the script, perhaps making references relative to the index of the bottom item), errors are not difficult to avoid or fix.

QuoteOf course with the ability to have custom struct pointers and ArrayLists, or other fun stuff, something like this would be less than a dozen lines of code.

If we could extend the built-in objects (properly, not the monkey-hack way), if we could have pointers to custom structs, and if we had ArrayLists, then many of the cases where array referencing is useful today could be solved equally well/easily with making your own object collections. Maybe then the argument would have some merit.

Quote from: Ryan Timothy on Sat 29/06/2013 11:24:14
Views have their own variable name. Albeit the View is actually just an integer variable, and I'm pretty sure very few people actually use it.

As I recall, you have to use the View index any time you want to assign an animation to an object, for example. OK, so Views aren't fully-fledged AGS data objects, but the logic is the same.

Quote from: Ryan Timothy on Sat 29/06/2013 11:24:14
My main argument was about Characters, Objects, GUIs and the Controls. Everything else can slowly follow along.

I haven't had a full desire of sprites dropping the ID number. But I certainly wouldn't argue as it's much nicer reading code that has readable sprite name like this   Sprites.George.Stand.Left001   vs    5.  What sprite is 5? I don't remember, let me keep browsing through all my sprite folders until I find it. (laugh)

Sure, but having the index is also very useful.

Quote from: Ryan Timothy on Sat 29/06/2013 11:24:14
QuoteAnyway, since different participants seem to have had different understandings of what we were discussing, I've renamed the thread again.
I still don't agree with the name because I never tried to change how we access objects by index. Which is why I suggested you to name it: 
AGS Indexing Objects  [shouldn't be assigned before compile]
That's all I wanted this thread to be talking about and I don't want others coming along thinking someone wants to eliminate accessing objects by array index.

The title of the thread shouldn't represent just your understanding of what it was about, when all (apparently) of the other participants understood it differently.