|
New tutorial Displaying 1-9 of 9 total.
1
Feyr
|
(sorry, I know there's already a ClassBuilder thread just a couple down, but I'm afraid that the people who actually need this tutorial wouldn't make it past the four screen long feature list that starts the thread off.)
I've neglected my coursework to bring all of you a decent tutorial for ClassBuilder. It takes you step by step through the process of building a simple game of Pong. I encourage anyone not familiar with the terms 'inheritance' and 'polymorphism' as they apply to programming to at least go read the first few sections, which explain just what ClassBuilder does (basically it brings object-oriented programming a lot closer to Verge) and why you might want to use it. My girlfriend is a nonprogrammer, doesn't even write VC, and she said she actually understood the first half. The second half is a bit more technical, since it involves stuff like collision detection, but I really only skim over the game physics and use them to present the main aspects of ClassBuilder. The tutorial ends with a list of suggestions for improving the game (which is rather rudimentary...it plays a single round of Pong, with sound effects and not much else), with tips on how to use ClassBuilder to implement each suggestion.
And those of you who are familiar with object oriented programming, please don't lynch me for the oversimplifications I've made when explaining inheritance and polymorphism. ;P
I'm reasonably sure that I've gotten any inconsistencies between the code in the finished product (which is available through the tutorial) and the code given in the tutorial itself, but I'll be going through it from scratch as soon as I get a chance and correcting any errors I find. If you're following along and find that the code doesn't work as advertised, let me know about it so I can fix it.
You can find the tutorial right here.
(edit)
Oh, keep in mind that this was all written over the course of two days. I wanted to do more with the game (add scoreboards, multiple rounds, improve the slightly flaky collision detection code, explain the Pong-related code better), but I just didn't have time before the weekend ended, so I gave priority to explaining the concepts underlying ClassBuilder.
Posted on 2004-10-25 07:08:27 (last edited on 2004-10-25 07:20:08)
|
Sungam
|
Okeh, got a few stupid questions for general ClassBuilder stuff.
1) Is it possible to make string fields (or hack it, somehow) in classes? I don't immediately see how, but then again, DMA logic eludes me at a certain point.
Say, for instance, I have a 'Character' class, for all my playable characters. I'd like to put the name of each character in there. How would you go about that? All the examples I could find that you made, you only ever used ints for fields.
2) Am I correct in assuming that you cannot access fields of classes in regular *.vc files, without creating Get/Set methods, or using dma.* functions? I'm assuming V3 will croak on the whole $variable_name$ thing.
Not a big deal either way, just for clarity. Might actually come in handy, forcing me to manage access to classes - something I always skipped over the easy way in 'real' OOP languages.
3) Say I want to create a field in a class, which references a linked list, which in turn references instances of another class...
As I understand it, the pointer to the class (what the constructor returns) is just that - a pointer. Not the actual class in memory. Same for linked lists (a pointer to the address of the first node).
If I'm correct so far, can I make an assumption as to how big that pointer will be? In other words, what sort of field (byte/word/quad) should I store a linked list pointer in, and how many bytes do I have to set aside for a pointer to a class instance?
I understand that I would have to take care of freeing the memory manually (I think).
If I'm completely wrong about all of this, do you have a suggestion for how to do the above?
4) Is it possible to make a field of a class an array?
Doesn't really matter if I can get linked lists working without too much hassle, but nice to know regardless, for keeping things simple.
I think that's it, for now. I'm sure I'll come up with more ;)
Thanks - for any advice, as well as an awesome utility.
Posted on 2004-10-25 20:13:49
|
zonker6666
|
1) Is it possible to make string fields (or hack it, somehow) in classes? I don't immediately see how, but then again, DMA logic eludes me at a certain point.
Say, for instance, I have a 'Character' class, for all my playable characters. I'd like to put the name of each character in there. How would you go about that? All the examples I could find that you made, you only ever used ints for fields.
Yes it is - Feyr released a string class along with the ClassBuilder. To use it just make a field that is a quad to hold the pointer to the string. Then use $myStringField$ = StringNew('Some string');
2) Am I correct in assuming that you cannot access fields of classes in regular *.vc files, without creating Get/Set methods, or using dma.* functions? I'm assuming V3 will croak on the whole $variable_name$ thing.
Not a big deal either way, just for clarity. Might actually come in handy, forcing me to manage access to classes - something I always skipped over the easy way in 'real' OOP languages.
You can access any variable from a .vc as you would normally. Meaning if you want to have the function SomeFunction() that belongs to an instance of class SomeClass modify the value of a global int OldGlobal thats in OldCode.vc you can just write it as OldGlobal=5; or OldGlobal=$this.SomeField$+1;
3) Say I want to create a field in a class, which references a linked list, which in turn references instances of another class...
As I understand it, the pointer to the class (what the constructor returns) is just that - a pointer. Not the actual class in memory. Same for linked lists (a pointer to the address of the first node).
If I'm correct so far, can I make an assumption as to how big that pointer will be? In other words, what sort of field (byte/word/quad) should I store a linked list pointer in, and how many bytes do I have to set aside for a pointer to a class instance?
I understand that I would have to take care of freeing the memory manually (I think).
If I'm completely wrong about all of this, do you have a suggestion for how to do the above?
Actually Classbuilder does that for you ! :) You no longer need to precalculate how many bytes your data structure will take up. just make a NewWhatever() <--- that constructor is what will allocated the needed # of bytes ... and the destructor will free it for you. No need to keep count ;)
4) Is it possible to make a field of a class an array?
Doesn't really matter if I can get linked lists working without too much hassle, but nice to know regardless, for keeping things simple.
That im not sure :)
Feyr?
--- also please correct me if i was wrong on any of those answers lol - i answered just to see how well i understood it.
Cheers,
Posted on 2004-10-25 20:25:50
|
mcgrue
|
Well, not v3-strings, but I remember when aen was on his v2 DMA bender he made DMA strings and had a go-between function to translate.
Posted on 2004-10-25 20:41:12
|
Sungam
|
Yes it is - Feyr released a string class along with the ClassBuilder. To use it just make a field that is a quad to hold the pointer to the string. Then use $myStringField$ = StringNew('Some string');
Excellent. Didn't see that. I'll play around with it in the morning :)
You can access any variable from a .vc as you would normally. Meaning if you want to have the function SomeFunction() that belongs to an instance of class SomeClass modify the value of a global int OldGlobal thats in OldCode.vc you can just write it as OldGlobal=5; or OldGlobal=$this.SomeField$+1;
I think you're thinking of the process opposite of what I am. Or I'm just thoroughly confused.
I'm aware that I can use regular VergeC variables (global ones, at least) in my classes, but can I use fields defined inside classes (ie. 'Field:SomeNumber:byte', resulting in $SomeClass.SomeNumber$) in regular VergeC?
I'm expecting that, since ClassBuilder.exe doesn't parse the *.vc file, the '$SomeClass.SomeNumber$' will be left intact (whereas, in a *.dma file, it would be converted to dma.byte[whatever]), and V3 won't know what to do with it.
Did I misunderstand you?
Actually Classbuilder does that for you ! :) You no longer need to precalculate how many bytes your data structure will take up. just make a NewWhatever() <--- that constructor is what will allocated the needed # of bytes ... and the destructor will free it for you. No need to keep count ;)
But don't I still need to define how big each of my fields are (ie. 'Field:MyList:quad'), or can I just omit the size at the end, and let the constructor figure it out ('Field:MyList')?
Thanks for the answers so far :)
Posted on 2004-10-25 20:47:03
|
zonker6666
|
ok i see the confusion here ...
$class.field$ is not vc and would generate an error - classbuilder will convert $class.field$ into a dma.byte[something+something] etc ...
To answer your question tho --- which at this point im understanding as can a class function modify a different classes field. The answer is i think so :) ---- just pass a pointer to that object to a class function and im 90% sure it would work.
yes youre right you do need to tell it what size it is :) but its still doing a lot of the work for you.
Posted on 2004-10-25 21:19:25
|
Feyr
|
'Field:SomeNumber:byte', resulting in $SomeClass.SomeNumber$) in regular VergeC?
I'm expecting that, since ClassBuilder.exe doesn't parse the *.vc file, the '$SomeClass.SomeNumber$' will be left intact (whereas, in a *.dma file, it would be converted to dma.byte[whatever]), and V3 won't know what to do with it.
That's right, anything in a *.vc file will have to use either an accessor method or something like:
dma.quad[myObject + CLASS_CObject_FIELD_SomeField] // OK
$myObject.SomeField$ // ERROR: V3 can't handle this, has to be run through ClassBuilder first.
The offsets for each field automatically have #defines generated in that format, and you can find them in the global_dma.vc file if you need them.
Personally, I would like to have -everything- go through ClassBuilder and only put the bare minimum in system.vc. (although writing accessors for your class is very easy, and good for you too ;)) Two problems with that are that all of the map VC would automatically be included in the system.vc, which would cause problems, and that each map would have to be a class of its own. The first one can be solved by adding another directive to indicate whether a VC file should be included in the global_dma.vc file or not. I don't really consider the second to be a problem, but it's something to think about.
With the newest version of ClassBuilder (which is what is distributed with the tutorial), you declare pointers as the class that they're pointing to. Using your example, you would write:
Field:MyList:CList
to indicate that MyList is a pointer to an object of type CList. You need to do this because otherwise ClassBuilder wouldn't be able to tell what fields MyList contains, and it would choke when you tried to access any of them.
The tutorial zip files don't include a string class, but the main distribution does. Just drop string.dma and asc.vc into your game directory and you can use the CString class like this:
Class:CPlayer
Field:Name:CString
Init:CPlayer CPlayerNew(string name)
$Name$ = CStringNew(name); // create a DMA string
CString title = CStringNew(' the Destroyer');
StringCat($Name$, title);
EndCode
EndClass
Then doing something like:
CPlayer this_player = CPlayerNew('Feyr');
would create a new character with the name 'Feyr the Destroyer'. You can check the docs for CString once you run ClassBuilder on string.dma to see what other string handling functions it defines.
ClassBuilder also defines each of the class names to be equivalent to 'int', so even in unprocessed VC it's legal to say stuff like 'CPlayer this_player = CPlayerNew();'
4) Is it possible to make a field of a class an array?
This is something that occurred to me halfway through writing the tutorial. Nope, sorry, no arrays. I may end up hacking them in later, but for the moment ClassBuilder is going to stay as it is...it's working well enough that it's usable for nontrivial programs, and I have other things to do. Anyone else who wants to modify it is welcome, though. ;) I've included the Perl source code (ClassBuilder.pl) along with every copy of the executable.
What you can do is make a field be a pointer to an array, and then write an accessor for it. Something like:
Class:BattleField
Field:PlayerArray:quad
Field:MonsterArray:quad
Init:BattleField BattleFieldNew(int numPlay, int numMon)
$PlayerArray$ = malloc(numPlay*4);
$MonsterArray$ = malloc(numPlay*4);
EndCode
Dest:void BattleFieldFree(BattleField this)
MemFree($PlayerArray$);
MemFree($MonsterArray$);
EndCode
Code:int GetElement(int array, int index)
return dma.quad[array + (index*4)];
EndCode
Code:void SetElement(int array, int index, int val)
dma.quad[array + (index*4)] = val;
EndCode
Code:void SetupMonsters(BattleField this)
Monster mon;
// set up a monster with some stats
mon = MonsterNew(100, 50);
SetElement($MonsterArray$, 0, mon);
mon = MonsterNew(150, 75);
SetElement($MonsterArray$, 1, mon);
mon = MonsterNew(200, 100);
SetElement($MonsterArray$, 2, mon);
EndCode
EndClass
Not as convenient as having builtin arrays, but it will work. You'll want more error checking than I have, though. ;P And to free any elements you allocated. (Actually, you'd probably want to write an array class.) My linked list code has a ForEach() function that I use to iterate through the list and free each pointer just before freeing the list (which also frees the nodes inside the list.)
I do have a linked list class that works well with ClassBuilder. It's in the Files section of this site, under V3 utilities, or something like that. It was written by hand, without the benefit of ClassBuilder, so you can't access any of its fields in the normal way, but there are sufficient accessors to make it useful. Just store the list pointer as a quad in one of your classes, and treat each node's data field as containing an unknown pointer. I used it to write the event scheduling classes that are also included in the latest distribution of ClassBuilder, and had no problems with it. *snicker* Of course, I am the one who wrote it, so that might not be saying much.
If you don't like my implementation of the linked list, you shouldn't have much more trouble writing your own using ClassBuilder than you would in, say, C++.
Posted on 2004-10-25 21:34:03 (last edited on 2004-10-25 21:51:26)
|
Feyr
|
To answer your question tho --- which at this point im understanding as can a class function modify a different classes field. The answer is i think so :) ---- just pass a pointer to that object to a class function and im 90% sure it would work.
Yes, classes can access the fields of other classes. The key point, though, is that you have to tell ClassBuilder -what kind- of class you're passing. Like this:
Class:DamageDealer
Code:void DoDamage(int player_object, int damage)
$player_object.HP$ -= damage;
// Classbuilder says: Huh? What class is
// player_object? Does it even have an 'HP' field?
// Time to give an error message so the programmer
// can fix this!
EndCode
EndClass
The above code is incorrect, because ClassBuilder needs to know both the class name and the field name to find out what offset the field is located at. The example below is correct:
Class:DamageDealer
Code:void DoDamage(CPlayer player_object, int damage)
$player_object.HP$ -= damage;
// Classbuilder says: Okay, I can see that
// player_object is of type CPlayer, and I know that
// the offset for the HP field in CPlayer is defined
// by CLASS_CPlayer_FIELD_HP.
EndCode
EndClass
If you absolutely can't get around passing normal ints (maybe you don't know what class you're passing until runtime, and you don't have a polymorphic function set up to deal with it) then you can filter the raw pointer through a class pointer, like this:
Class:DamageDealer
Code:void DoDamage(int player_object, int damage)
CPlayer player_ptr = player_object;
$player_ptr.HP$ -= damage;
EndCode
EndClass
Posted on 2004-10-25 22:00:08
|
Feyr
|
It looks like the latest version is stable enough to use, so I've gone ahead and put it in the Files section here. Grue, if you see this, could you remove the old version? The 'revision of file' feature isn't working, or I would've used it.
There is one minor change that I made, compared to the version that my tutorial uses (all of the zips on my site have been updated to include the latest version of the script, however, so now there's no difference.)
InheritCode no longer requires that all classes using the same virtual function name be in the same inheritance hierarchy. Here's an example of what this means:
Class:Test1
InheritCode:void Funky(int this, string test)
Log('Test1: Funky '+test);
EndCode
EndClass
Class:Test2
InheritCode:void Funky(int this, string test)
Log('Test2: Funkaaaay '+test);
EndCode
EndClass
Class:Test3
EndClass
Three classes, none of them related to each other in any way. The first two implement a function named 'Funky'. All InheritCode blocks require an argument named 'this', which should now be of type int. Here's how you'd use the classes:
Test1 t1 = Test1New();
Test2 t2 = Test2New();
Test3 t3 = Test3New();
// prints 'Test1: Funky chicken'
Funky(t1, 'chicken');
// prints 'Test2: Funkaaaay chicken'
Funky(t2, 'chicken');
// runtime error msg, T3 doesn't define Funky()
Funky(t3, 'chicken');
Technically you could get away with making the function signatures different for each inheritance hierarchy, but only -very slightly- different...in the example above, I could have said 'InheritCode:void Funky(Test1 this, string test1_string)' and 'InheritCode:void Funky(Test2 this, string test2_string)' and it should still work because Test1 and Test2 are actually aliases for int, and the argument names don't matter as long as the types and number of arguments are the same.
Posted on 2004-10-26 10:55:25 (last edited on 2004-10-26 11:00:09)
|
Displaying 1-9 of 9 total.
1
|
|