Game Development Community

TGB Isometric Add-On Pack - Part 2 - Cont.

by Neo Binedell · 06/14/2006 (4:09 pm) · 16 comments

www.neoji.co.za/neo/gg/logo50x50bc.gif

- TGB Isometric Add-On Pack -


Continued from my previous plan:


Added map element Aspects and Orientations.

An aspect is somewhat like a state for a map element (e.g. a building could have a e.g. a default, damaged and destroyed aspect and each aspect would have say two orientations (facing SE and
facing SW).

So you'd define datablocks for each aspect orientation visual, and add them to an aspect
datablock that in turn is added to the map element datablock.
(This is all done in the editor for you, but you could add them manually ;p).

This simplifies managing related animations and world info for elements and removes the need to
handle all this in script.

Another example, say you have an character that has two aspects: stand and walk, and
animations for each aspect in 8 directions. If you wanted to change your player state to e.g
walking in a certain direction, you'd just do:

// setup an entity
%this.dude = %this.map.createEntity( "DudeElementDatablock" ); // pass an element db name

...

// later in code when we want the dude to walk
%this.dude.setAspect( "dudeWalk" );

...

// and if we want him to face a direction
%this.dude.setDirection( 45 ); // 0 -> north in iso space -> top/right diagonal in screen space
...

The engine will find the closest orientation on the current aspect to the direction passed in
and play that animation (or static image) automatically. So above it would pick the orientation closest
to 45 degrees ( NNE on iso map X/Y axis) and play that.

Of course if you set a velocity vector for the element it will update the orientation automatically ;p

I think this removes a lot of boring movement scripting like doing case statements then setting animations directly, etc, as well as encapsulating everything an element can do visually in one place. It also allows
setting world size and bounds for every aspect so that you don't get weird errors when the bounds dont
match the animation.

Quote:

As a side note I noticed that SimObject::addField() does not work on dynamic arrays so adding
more than one visual to an aspect datablock would require having a maximum amount of visuals
allowed and then need to keep an array of that size around even if you only used one visual.
That adds up to a lot of wasted space (apart from restricting number of visuals) once you have a ton of
map element datablocks.

I got around this by noting that in SimObject::setDataField() it checks whether a setter method is
defined and if that method returns false does not call Con::setData() on the field address.

This allows me to add the field using addProtectedField() and passing in a dummy var
for the offset parameter, and a setter method that can add the value to a dynamic Vector
when the field is set. Voila, no waste and an elegant solution.



Neo Binedell

#1
06/14/2006 (4:53 pm)
The more you post about this project the more excited I get!

Do you have any news on the networking aspect of this? I'm really, really hoping you get to implement full networking support... (because I want to develop multiplayer games mostly)
#2
06/14/2006 (5:09 pm)
Oh yea. Makes me want to break out the lanolin hand cream!

I so can't wait for this to be finished.
#3
06/14/2006 (5:11 pm)
Well like I said before the networking is a very low priority until I have the core pack
features all in. I am however building everything so that networking can be added
without breaking anything. I will be testing to see how far I can push the commandToServer/Client
stuff and if thats not enough then I will have to implement realtime networking myself as I
don't think GG has any plans updating TGB to use it in the near future.

I will probably release the core pack when its done, and then work on the networking
and release that as an add-on pack or whatever as it will prob require a ton of merges.

But don't worry networking is important to me as any RPG/RTS basically require it and
it would be a large hole in the engine if it did not have it.

~neo
#4
06/14/2006 (7:30 pm)
Heck, even if you made a stand-alone real-time networking add-on for TGB I'd BEG you to take my money from me for it. I'm just not capable of adding this functionality to TGB... (eg. synchronizing player/projectile movement in a 2D [TGB] game)

Anyways, good luck with the isometric pack!
#5
06/15/2006 (9:01 am)
G'day Neo,

Keep it coming mate, this is awsome!

What colour background do the sprites, units, buildings and objects etc need to be rendered on for inclusion onto the map and what colour do the shadows need to be?

I am modelling up a stack of vertical type objects such as sandbags, poles, fences etc and will send some to you to see if they can be added as a test if thats ok with you ;)

I will render them as .png.

Already started saving for this hehe. Cant wait to see what the maximum map size and unit number will be.

Cheers
Paul.
#6
06/15/2006 (9:19 am)
Hiya Paul,

No background color, use transparent (alpha=0).

I don't know Rhino 3D but I'm sure there's an option to do it.
I 3DS Max you just select RGBA as output channels.

Send em on! Thanks ;p

Map size can be as big as memory allows, it only renders what is in view, the CPU eater
is how many elements need to be updated per tick and how complicated their thinking code is.
At the moment only objects that need updates get added to the update list and I plan on refining
that so that objects are updated in batches over several ticks, ie. some items might only need to be
updated once a second and others every tick.

All controllers states can be shared and only states are updated, then applied to all objects that
are in step. So if you had say 1000 animated tiles but they all were synced then you simply update
1 state per tick and apply it to all of them, which is really fast.

I'm adding a lot of options to the element datablocks themselves to allow the user to specify flags
that help the engine optimize stuff. E.g. a tile can be set as covering an entire sector and never needing
updates and blocks all. Anything that tries to move there just checks the sector and if its blocked, discards it. Or it the sector can be set as empty (only rendering terrain tiles and overlays), etc.

Collision/map validity is hierarchical for early outs. If something moves it checks the sector(s) its moving to,
if it needs more then it checks cells and if more then each cell and finally each object's world bounding box/sphere/etc.


~neo
#7
06/16/2006 (4:59 am)
hell yeah man.. =)
#8
07/07/2006 (6:04 pm)
Where's the pre-order button?
;p
#9
07/16/2006 (4:00 am)
Neo,
Glad to hear you found the protected fields, they're great, no? Can't wait to see this in action! :)

Cheers,

-Justin
#10
07/16/2006 (8:57 am)
@Justin:

Well turns out they're just short of great ;p

When writing out an object it does not call the accessors but rather Con::getData() asks the assigned
type to read data directly from the address, and BOOM!

As I didn't want to make any changes to the underlying code I created a derived class
that overrides writeFields and which is basically a copy of the code except that it calls
any protected field accessors if they exist instead.

I toyed with just creating a custom StringVectorType but the implementation of the value read
from the type does not pass in indices so one could only get the value of the full list as a string
and not each individual string.


~neo
#11
07/16/2006 (11:14 am)
In addition, this really is a controlled hack as the protected fields are fundamentally flawed.
They lose much or their power by always calling Con::getData() which operates on memory
offsets from the base memory location.

Even when calling the accessors it calls Con::getData() to get the value to pass the accessors!
( See getDataField() for an example ).

If i wanted to make this bullet proof and consistent I'd have to override most of the access methods
and I would be better off making the modifications in SimObject, etc.

For example instead of using the field element count I created a virtual method getFieldElementCount
which defaults to an inline which just returns the passed fields elemenCount value but which derived
classes can override to return e.g. a vectors size, etc.

Oh well, perhaps I'll just stick to access methods on the object but I do like being able to say:

new nxIsoElementDatablock(SomeElement)
{
    aspects[ 0 ] = "someAspect";
    aspects[ 1 ] = "someOtherAspect";
};


~neo
#12
07/20/2006 (7:55 pm)
Neo - I don't even have any words...looks killer. Any new updates, can't wait to get my hands on it. I think its going to save me a year of dev time and let me really focus on content.
#13
08/09/2006 (3:03 pm)
This looks amazing! So any updates or extra details for us? *winks*
#14
08/09/2006 (3:36 pm)
Hi all, I've been terribly busy on contract work and have not had a chance to post about the progress
of the Iso Pack, but it is coming along very nicely and I will have a new plan with updates out some time this week or the next.

I've rewritten the editor stuff a few times (and will probably again) to try and make it as easy and
efficient to work with as possible, etc...

More when I post the plan...
#15
08/10/2006 (3:47 pm)
sounds good Neo thanks for the update.
#16
10/03/2006 (4:51 pm)
Neo,
I'm intrigued with your dynamic array trick using addProtectedField() since I've been using a slightly different trick using a TypeValidator to do pretty much the same thing. I've been looking closely at your solution to see if it might offer improvements to what I'm doing. So, I have a question... is your solution simply ignoring the array indices when it gets a new entry? The setter method doesn't seem to have access to the specified array index.

// Do these two datablocks produce the same result?

new nxIsoElementDatablock(SomeElement)
{
    aspects[ 0 ] = "someAspect";
    aspects[ 1 ] = "someOtherAspect";
};

new nxIsoElementDatablock(SomeElement)
{
    aspects[ 3 ] = "someAspect";
    aspects[ 8 ] = "someOtherAspect";
};

Also, does the dynamic array get copied properly using the datablock initialize from copy syntax?

new nxIsoElementDatablock(SomeElement)
{
    aspects[ 0 ] = "someAspect";
    aspects[ 1 ] = "someOtherAspect";
};

new nxIsoElementDatablock(SomeElement2 : SomeElement)
{
};

Thanks,
- Faust