Adventure Game Studio

AGS Support => Advanced Technical Forum => Topic started by: Phemar on Sun 01/07/2018 12:43:21

Title: Is it possible to encapsulate this?
Post by: Phemar on Sun 01/07/2018 12:43:21
I have a couple of structs that relate to the same object, I was wondering if there was a way to encapsulate them into a single object that I can create instances of.

My project is a simple 3D renderer in AGS Script (just for fun) and I'd really like to be able to create several instances of one object.

Right now my code is hard coded to accept only one mesh. I have it set up like this at the moment:

struct Vertex
{
  float world[3];
  float camera[3];
  float screen[3];
  int raster[2];
};

struct Triangle
{
  int vertex[3];
};


The arrays correspond to x, y and z coordinates.

I'd like to have it all encapsulated into one object, something like this:

struct Box
{
  Vertex vertex[8];
  Triangle triangle[12];
};


It would be even better if I could have a Vec3 struct, so I can access the box like

Box * box1;
box1.vertex[4].world.x = 1.4;
box1.triangle[0].vertex[1] = 3;


Is this possible? Sorry if this seems silly, I'm not really a programmer, just a hobbyist.
Title: Re: Is it possible to encapsulate this?
Post by: dayowlron on Sun 01/07/2018 13:24:14
I believe it should be possible, however I do see one problem. you reference ....world.x but world is an array so it would be world[0] unless you declare world to be another type other than float world[3]
you could create another struct called coordinate so you can reference it with .x, .y, and .z, but then you can't reference it with indexes.

struct Coordinate
{
    float x;
    float y;
    float z;
}
struct Vertex
{
  Coordinate world[3];
  Coordinate camera[3];
  Coordinate screen[3];
  int raster[2];
};
Title: Re: Is it possible to encapsulate this?
Post by: Phemar on Sun 01/07/2018 13:39:43
Dayowlron, I know, that's why I said it would be nice to have another Vec3 struct, and include that in the other attributes.

Struct Vec3 {
float x; y; z;
};


Currently I'm using an array with an enum (world[eX], world[eY] etc. instead of world.x, world.y), because AGS won't let me include structs within structs.
Title: Re: Is it possible to encapsulate this?
Post by: Crimson Wizard on Sun 01/07/2018 13:51:36
The limitations of AGS Script currently are that you cannot include structs into structs, but you may include pointers to non-managed structs, e.g.:
Code (ags) Select

managed struct Vec3
{
   float x, y, z;
}

struct Triangle
{
    Vec3 *vertex[3];
}


Other method, people seem to use, is to have large arrays for storing objects, and connect one object with other using array indexes.
EDIT: sorry, scrap this, I did not realize at first that you use that now.

Anyway, there seem to be no other way. You could mix these two methods to some degree.
Title: Re: Is it possible to encapsulate this?
Post by: Snarky on Sun 01/07/2018 14:18:38
If the intermediate classes are just for convenience, you can also do something like this:

Code (ags) Select
// Header

managed struct Point3D
{
  import static Point3D* Create(float x, float y, float z);

  float x;
  float y;
  float z;
};

managed struct Triangle3D
{
  protected float points[9];

  import attribute Point3D* Point1;
  import attribute Point3D* Point2;
  import attribute Point3D* Point3;
};

// Script

static Point3D* Point3D::Create(float x, float y, float z)
{
  Point3D* p = new Point3D;
  p.x = x;
  p.y = y;
  p.z = z;
  return p;
}

Point3D* get_Point1(this Triangle3D*)
{
  return Point3D.Create(this.points[0], this.points[1], this.points[2]);
}

void set_Point1(this Triangle3D*, Point3D* p)
{
  // TODO: Deal with null
  this.points[0] = p.x;
  this.points[1] = p.y;
  this.points[2] = p.z;
}

// TODO: Same for Point2 = points[3-5], and Point3 = points[6-8]


You use attributes to make it seem like you have a struct as a property, but internally you just store the raw data and encode/decode it on set/get.

It does mean you break the reference link (if you set a triangle to contain a certain point, and then you update the point, the triangle is not going to update), so it's not suitable for every situation.

Edit: Code fix
Title: Re: Is it possible to encapsulate this?
Post by: Phemar on Sun 01/07/2018 15:59:49
Thanks for the replies guys. Appreciate the long post, Snarky, however I'm not entirely sure what your code does or how it works. I tried to compile it and got an error at Point3D* Point3D::Create(float x, float y, float z)

"TESTING_HELL.asc(25): Error (line 25): Attributes of identifier do not match prototype"

Anyway, I tried your method Crimson, however I'm getting a "Null Pointer Error" whenever I try and set values. Am I doing something wrong?

//Header

managed struct Vec3f
{
  float x;
  float y;
  float z;
};

struct Tester
{
  Vec3f * vertex[3];
};



Tester aTest;
Tester anotherTest;

void TestingShit ()
{
  aTest.vertex[0].x = 4.0;
  anotherTest.vertex[0].x = 5.0;
}

function on_key_press (int keycode)
{
  if (keycode == eKeyM)
  {
    TestingShit ();
    Display ("aTest %f[anotherTest %f", aTest.vertex[0].x, anotherTest.vertex[0].x);
  }
}


Edit: Also, Crimson, what do you mean by this: "EDIT: sorry, scrap this, I did not realize at first that you use that now." Realize that I use what now?
Title: Re: Is it possible to encapsulate this?
Post by: Snarky on Sun 01/07/2018 16:07:08
Yeah, I forgot to put the "static" keyword in the function definition.

I think what CW realized you were doing is store array indexes in the struct, and then look up the other struct in a global array using the index. Which is the workaround you have to do when you can't have pointers in the struct (the index is essentially a kind of pseudo-pointer).

Phemar, check out the threads that discuss the new "managed struct" datatype. The key thing is that when you create one, you have to put "new" whatever it is. The Vec3f array in Tester is an array of pointers: they haven't actually been created until you set them with "new" somewhere. (It's like you store an array index, but the array doesn't actually exist or have anything in it.)

Title: Re: Is it possible to encapsulate this?
Post by: Phemar on Sun 01/07/2018 16:34:52
Awesome, I'm beginning to understand. But if I create a pointer within the struct (like I'm doing at the moment), where do I initialize the managed struct (Vec3f)? I can't do it in the header (which makes sense), but I can't think of where else to put it.

I tried this:
struct Tester
{
  import attribute Vec3f * vertex1;
};


However that just crashes the game upon launch. Compiles fine though. (P.S. What does 'attribute' do? Can't find it in the manual).

Again, thanks for the help. And sorry if I sound absolutely noobish.

Edit: Ignore this, I figured it out! Adding:

void Initialize ()
{
  aTest.vertex[0] = new Vec3f;
  anotherTest.vertex[0] = new Vec3f;
}


fixes it all.

Thanks again!
Title: Re: Is it possible to encapsulate this?
Post by: Crimson Wizard on Sun 01/07/2018 17:49:03
Quote from: Phemar on Sun 01/07/2018 16:34:52
Awesome, I'm beginning to understand. But if I create a pointer within the struct (like I'm doing at the moment), where do I initialize the managed struct (Vec3f)?

You initialize it where you need, for example when the parent object is initialized.
Code (ags) Select

Tester testers[100];

void CreateTester(int index, float x, float y, float z)
{
   Vec3 *v3 = new Vec3;
   v3.x = x;
   v3.y = y;
   v3.z = z;
   testers[index].vertex1 = v3;
   // other variables
}


You may move the Vec3 initialization into separate constructor-like function, like in the example I gave in the other topic:
http://www.adventuregamestudio.co.uk/forums/index.php?topic=50178.msg636588783#msg636588783


Quote from: Phemar on Sun 01/07/2018 16:34:52However that just crashes the game upon launch. Compiles fine though. (P.S. What does 'attribute' do? Can't find it in the manual).

Attribute is not a real variable (data field) but a convenience syntax for two functions that set or get certain value. If you declare attribute you also must implement functions called get_AttributeName and set_AttributeName.
http://www.adventuregamestudio.co.uk/wiki/Keyword:_attribute
Title: Re: Is it possible to encapsulate this?
Post by: jhonberg on Sat 04/08/2018 11:02:52
Is there any way this could work for Strings ??  in my case i'm getting this error

Failed to save room room1.crm; details below
room1.asc(4): Error (line 4): Member variable of managed struct cannot be pointer
Code (ags) Select

managed struct ScreenText{
String text;
FontType fontType;
};

ScreenText* createScreenText(String text_,FontType fontType_ ){
 
  ScreenText* screenText= new ScreenText();
  screenText.text=text_;
  screenText.fontType=fontType_;
   
  }


Seems that this only works for primitives?   :confused:
Title: Re: Is it possible to encapsulate this?
Post by: Snarky on Sat 04/08/2018 11:58:43
The problem is that you can't store pointers in a managed struct (for reasons that have to do with AGS not having a garbage collector), and String is a pointer type.

A workaround (as already discussed above in the thread) would be to store the String in an array, and store the array index in the ScreeText (you planning on fixing that typo?). You could then use an attribute to look up the String in the array and make it seem like it's part of the struct.
Title: Re: Is it possible to encapsulate this?
Post by: Crimson Wizard on Sat 04/08/2018 12:55:18
Quote from: Snarky on Sat 04/08/2018 11:58:43
The problem is that you can't store pointers in a managed struct (for reasons that have to do with AGS not having a garbage collector)

AGS has garbage collector, otherwise managed structs would not work at all. What it does not have is reflection and/or runtime type information. In simplier words it cannot distinct the types of variables inside the struct. With regular structs there is still information telling whether the variable is managed or not which is saved directly as a part of script commands, but with managed structs it does not know even that. Hence, when it deletes the managed struct from memory it won't be able to detect pointers there and would not decrease their use counters, which would lead to them staying in memory forever (memory leak).

Potential solutions do exist, we were discussing them in the past, but no one have tried to actually implement them.
Title: Re: Is it possible to encapsulate this?
Post by: Snarky on Sat 04/08/2018 13:01:19
Interesting, thanks for the clarification!
Title: Re: Is it possible to encapsulate this?
Post by: jhonberg on Sat 04/08/2018 14:14:35
Quote from: Snarky on Sat 04/08/2018 11:58:43
The problem is that you can't store pointers in a managed struct (for reasons that have to do with AGS not having a garbage collector), and String is a pointer type.

A workaround (as already discussed above in the thread) would be to store the String in an array, and store the array index in the ScreeText (you planning on fixing that typo?). You could then use an attribute to look up the String in the array and make it seem like it's part of the struct.

Fixed the typo :D
Title: Re: Is it possible to encapsulate this?
Post by: jhonberg on Sat 04/08/2018 20:22:40
Since i can't pass the struct as parameter i just decomposed the object primitives, i don't feel confortable with this workaround but it serves my initial purpose, which was organize the data and make it easy to manipulate.

 

Code (ags) Select

//header
struct ScreenText{
String text;
FontType fontType;
import function init(String text_,FontType fontType_);
};

//script
Overlay* roomText[];

function displayAnimatedText(String fullText,FontType fontType,int row, int maxRows){
   String message="";
   for(int i=0;i<fullText.Length;i++){
     message=message.AppendChar(fullText.Chars[i]);
     roomText[row]=ShowTextinRowLeft(message,53665, row+1, maxRows,fontType);
     Wait(2);
   }
   Wait(80);
}

function room_AfterFadeIn()
{
int maxRows=10;
ScreenText textRows[10];
roomText= new Overlay[maxRows];
textRows[0].init("Earth year 1947",eFontNormal);
textRows[1].init("70.000 years after the last colony left",eFontSpeech);
textRows[2].init("A strong high energy pulse was picked up by a consulate",eFontSpeech);
textRows[3].init("cruiser 2 years earlier, origing is planet Earth.",eFontSpeech);
textRows[4].init("A scout mission is sent to investigate it's origing,",eFontSpeech);
textRows[5].init("no intelligent life is expected to be found.",eFontSpeech);
textRows[6].init("   ",eFontNormal);
textRows[7].init("Mission:",eFontNormal);
textRows[8].init("Identify energy source and evalute  explore the abbandoned ",eFontSpeech);
textRows[9].init("settlement for lost artifacts left behind on the exodus.",eFontSpeech);

 
   for(int i=0;i<maxRows;i++){
      displayAnimatedText(textRows[i].text,textRows[i].fontType,i, maxRows);
   }

}
Title: Re: Is it possible to encapsulate this?
Post by: Phemar on Sun 05/08/2018 20:24:28
textRows[0].init("Earth year: 1947",eFontNormal);
textRows[1].init("70 000 years after the last colony left,",eFontSpeech); //you can use a space or a comma here, but I wouldn't recommend using a period. It's a stylistic choice at the end of the day though
textRows[2].init("a strong energy pulse was picked up by a consulate",eFontSpeech);
textRows[3].init("cruiser 2 years earlier. Origin is planet Earth.",eFontSpeech); //changed origing to origin
textRows[4].init("A scout mission has been sent to investigate,",eFontSpeech);
textRows[5].init("no intelligent life is expected to be found.",eFontSpeech);
textRows[6].init("   ",eFontNormal);
textRows[7].init("Mission:",eFontNormal);
textRows[8].init("Identify the energy source and explore the abandoned ",eFontSpeech); //removed 'evaluate' and changed 'abbandoned' to 'abandoned'
textRows[9].init("settlement for lost artifacts left behind on the Exodus.",eFontSpeech); //capitalized 'Exodus' (proper noun)


Did some quick proof reading for you. Hope you don't mind :D