Re: new here, my lang project...
From: cr88192 (cr88192_at_NOSPAM.hotmail.com)
Date: 01/24/05
- Next message: Roger L. Cauvin: "Re: Singletons"
- Previous message: Roger L. Cauvin: "Re: Singletons"
- In reply to: H. S. Lahman: "Re: new here, my lang project..."
- Next in thread: H. S. Lahman: "Re: new here, my lang project..."
- Reply: H. S. Lahman: "Re: new here, my lang project..."
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Mon, 24 Jan 2005 15:47:25 GMT
"H. S. Lahman" <h.lahman@verizon.net> wrote in message
news:HUSId.5225$cx2.4061@trndny03...
> Responding to Cr88192...
>
>>>>everything is wrapped up in the general package known as an entity,
>>>>which is shared between many subsystems (and takes the form of a bag of
>>>>slots).
>>>
>>>Ah, yes. The old one-size-fits-all trick. I now see where your
>>>predilection to pointer indirection came from. In a word: Yech!
>>>
>>>This approach leads to monolithic god objects that lurk in the center of
>>>the application with tendrils touching every activity. Their behaviors
>>>tend to have lots of IF decisions about context like the your example
>>>above. Usually any change in peripheral objects also needs a change in
>>>this critter. Better yet, a change to it to accommodate one subject
>>>matter will often break interactions in another subject matter that seems
>>>completely unrelated.
>>>
>>
>> yes, however, at least the scope is limited "a little".
>
> Maybe for very small values of 'little'. B-) The problem is the entity
> itself has application-wide scope.
>
not necissarily, things like memory management, io, the typesystem, and even
the scripting language (all coded by me in my projects) fall outside the
domain of where "entities" are relevant.
now, if one looks into "lbxgl" (the lib where I put all my 3d, graphical,
physics, sound, ... stuff), then, yes, entities are monolithic objects used
all over the place.
>> ammusing is that the nature of these objects feels pulls even from other
>> engines, eg, to match their specific conventions.
>> what breaks there have been thus far (eg: calibrating space in meters vs.
>> carpentry or standard inches) have resulted in endless annoying troubles.
>
> The old mix & match trick. One can do the same thing with software
> construction paradigms with similar results -- which launched this thread
> as I recall. B-)
>
yes, about.
albeit, mix and match is more of a direct hassle when dealing with code, as
it is not allways clear where and when to convert, and this has led to some
annoying kludging.
yes, the float/double issue is also a hassle, but at least it is clear where
I have to convert, so it is less so.
for some things at least I had considered a convention of going to "SI
numbers", or essentially '<number><scale><unit>', eg: I could write '3mm'
and have it understood as "3 millimeters" and have it autoconverted.
however, this does not fix the problem in general (units are not
known/retained in many situations).
another issue would be textual ambiguity, eg, is '3m' 3 meters, or 3
milli-somethings. as far as SI goes afaik, it should be 3 meters, but there
may be many cases where users may ommit the unit, which could cause problems
('3k' is more obvious, at least 'K' is upper case).
I could go against si, and declare that if the unit is present, the scale is
required. I could lift 'z' for this purpose, so '3m' implicitly means
3-milli, and 3 meters is 3zm. it sucks and is potentially error prone, oh
well.
conversely, the unit could be required for the scale, '3m' is thus 3 meters,
but 3k is invalid and would need to be written 3km. this agrees with si, but
is imo a hassle...
still, it could fix some problems.
>>>When one encapsulates subject matters one can implement their view of the
>>>world independently from other subject matters. That simplifies the
>>>subject matter implementation, allowing one to focus only on the
>>>requirements for the subject matter. It also allows one to recognize
>>>invariants unique to the subject matter much more easily. It will also
>>>tend to isolate changes to particular subsystems when requirements
>>>change. All those things combine to make the application more reliable
>>>and maintainable.
>>>
>>
>> yes.
>>
>>
>>>The main reason is that each subsystem has its own functionality that is
>>>unique to the subject matter. If that functionality is properly isolated
>>>and decoupled from other functionalities via a pure data transfer
>>>interface, then one has a much more robust design than passing around
>>>monolithic objects that any context can trash.
>>>
>>
>> however, the monolithic objects aproach is sort of forced by the
>> predecessors I have inherited some formats, tools, ... from.
>> in some cases, I can do little really wrt fundamental issues.
>
> Right. That's the classic problem of legacy code. It is generally
> regarded as high risk to introduce OO development into a shop with a lot
> of legacy code. That's because the legacy code was not constructed with
> OO principles so there is no convenient way to partition
> responsibilities -- the legacy code has already usurped some of the
> responsibilities of the new objects one would like to have. So you either
> do a lot of refactoring of the legacy code or you do a kludge.
>
well, more like legacy data and tools in some ways...
I don't control and I can't change the tools, I am stuck with what they
generate and understand, and doing something different would be too much
work.
the unit pain is just one, it could have been worse. however, a fix to the
unit pain might also allow unit-abstraction, eg:
the map can be in whatever unit, and the app silently converts. this would
consist of generally replacing some of the hard coded scales (eg: a lot of
scaling and dividing by 30 or 36...).
> In this context I would argue it is not just the OO paradigm. Your legacy
> code was apparently not properly structured to begin with using /any/
> paradigm. Trying to fix one part tends to bleed shotgun changes into
> other parts. So the choice is rewriting or kludging.
>
> However, there is still a way to minimize the risk when providing large
> scale features. One can provide new subsystems or replace old ones while
> minimizing the aggravation of dealing with the legacy code into which they
> are inserted. I cover that in yet another topic, Legacy Replacement, in
> my blog. But it may be mostly of academic interest in your situation
> since you aren't planning on a commercial product with problems like
> time-to-market deadlines. That is, the sorts of risks, like scheduling,
> that it ameliorates are probably not heavyweight concerns for you.
>
yes.
>>>Note that you don't need to be doing OO to design this way (though
>>>abstraction makes it easier). In C one keeps local structs in each
>>>subsystem that are organized explicitly to that subsystem's needs. They
>>>will have only the necessary subset of all the data associated with the
>>>entity that they need to resolve the subject matter's responsibilities.
>>>Some of that data will appear in structs in other subsystems, so when it
>>>is changed, those subsystems are informed via their subsystem API. The
>>>API function understands which structs within the subsystem the data goes
>>>into so they are updated. The API also knows if that change should
>>>trigger any behaviors within the subsystem and, if so, dispatches to
>>>them.
>>>
>>
>> yes.
>>
>> typically, yes, the subsystems are independent and have specialized data.
>>
>> however, in my code in many cases the data is not known or managed by the
>> subsystems, but is instead used by others, and is either destroyed
>> (freeing up memory sooner) or forgotten (freeing up memory later).
>
> Right. That is directly due to not having each subsystem responsible for
> its own view of the data. When one does that, the synchronization issues
> for shared data are usually pretty obvious and are often trivial to
> implement. [For example, in an OO application one will have a setter that
> is used to modify the data. If one knows that some other subsystem cares
> about that data, one can have the setter itself generate the
> synchronization message to the other subsystem. That way you get
> synchronization in one place without worrying about who changes the data
> or why the data is being changed.]
>
ok.
>>>[Aside. If one uses events one can put the event queue in each subsystem
>>>in its own thread. Then one gets concurrent processing for the various
>>>subsystems in a quite robust manner almost "for free" --
>>>provided the subsystems' functionality's are self-contained and cohesive.
>>>(There are some situations where synchronization is more complicated, but
>>>they will usually become apparent when one designs the subsystem APIs.)]
>>>
>>
>> yes.
>>
>> I had thought about it recently, and eventually figured how one could
>> pass data between threads w/o locking.
>
> Actually, you can use state machines to provide a poor man's threading
> within a subsystem. Just provide an event queue manager for each group of
> objects (or functions) one would like to have in a single concurrent
> thread. Then employ semaphores in the event queues to let them process in
> a sequential manner (i.e., only one event queue is "on" and pops an event
> at a time). In effect one has time slicing at the event scope level. If
> you don't have hard real time constraints, managing the semaphores will
> usually be much more efficient than using the OS threading infrastructure.
> That's because the OS threading needs to do time slicing at the
> instruction level, which creates some interesting context switching
> issues.
>
> I point this out because once one gets the state machines to interact
> properly without concurrency (i.e., a single event queue for all events),
> that <inherently asynchronous> model will Just Work when one introduces
> multiple state machines and semaphores.
>
ok.
>>
>> oh well, it has been quite a while since I have used (system level)
>> threads. on current systems in many cases threads don't have a strong
>> justification for the extra work needed to maintain them (eg: avoiding
>> race conditions, securing subsystems via locks or such, ...).
>
> I'm kind of surprised a game like quake didn't have threading. Smooth
> graphics usually requires higher priority and I would expect the AI to be
> doing something useful while waiting for the next player keystroke.
>
nope, quake was single-threaded all the way.
the main power is a big main loop, that cycles through and calls all the
related subsystems to cause them to do whatever, and the loop repeats.
the main power of things is keeping the loop cycling fast (>30Hz is good,
>50 is better, ...). 10 or 20Hz, however, is not so good, the game lags and
things start getting jittery.
I can't answer for newer games, but presumably they haven't changed much wrt
this.
yeah, just multithreading is an extra hassle, so I personally don't really
do it. it will be more compelling once I have to worry about multi-processor
machines or such.
>>>>I had before considered that on a general scale, doing something like
>>>>class-based inheritence on the directory level (eg: defining directories
>>>>as kinds of classes that extend and override different directories) and
>>>>could construct general item or monster prototypes, could be quite
>>>>useful.
>>>
>>>You usually want to avoid subclassing when doing parametric polymorphism.
>>>The reason is that if the tree changes, that is a static relationship so
>>>one has no choice but to modify the code. Whenever possible one should
>>>provide the parametric changes via external configuration data so that
>>>one doesn't have to touch the code. [It can also reduce code size a lot.
>>>There have been examples where the combination of encoding invariants and
>>>parametric polymorphism have resulted in a 90% reduction in executable
>>>statements when an application was rewritten.]
>>>
>>
>> dunno, the class model seems more straightforward though, and is an
>> improvement over "typical" practices in many cases.
>> actually, similar could be said for an anim sequencer, so the scripts no
>> longer have to worry about or manage the actual animations...
>
> It may seem more intuitive to do something like:
>
> 1 *
> [Depreciation] ----------------- [Asset]
> + computePeriod (t) + type
> A + baseValue
> |
> +----------+-------------+
> | |
> [Linear] [Double Declining]
>
> where each subclass implements its own formula for computePeriod. But it
> will be a lot more robust to
>
> [Spec]
> + maxYears
> + fractionArray[]
> | 1
> |
> |
> | * * 1
> [Asset] --------- [Depreciation]
> + type + computePeriod (t)
> + baseValue
>
> Now the formula for computePeriod is simply
>
> if (t <= Spec.maxYears)
> return asset.baseValue * spec.gractionArrat[t]
> else
> // signal software error.
>
>
> One uses the formulas to compute the fractions of the asset's base value
> that are contained in [Spec]. That can be done once outside of the
> application and stored in a configuration file. Then all one has to do
> when creating an [Asset] is to instantiate a relationship with the right
> [Spec] for the asset's type.
>
> You only need one instance of [Depreciation] for the generic formula and
> the [Spec] instances are created at startup for the available formulas and
> number of periods from the configuration file. The [Spec] instance is
> selected when an Asset is instantiated based upon its asset type.
>
> The reason this is better is because one can add, remove, or modify
> depreciation specifications without touching the application code. It can
> all be done in the configuration files. [In situations where the
> configuration files are complicated one can develop an small editing tool
> to handle all the formatting issues and even compute the formulas. Then if
> one needs a new formula, only that tool needs to be touched.]
>
I don't really get it, however:
my inheritence model is also outside the app code.
it is basically just files and directories in the resources directory which
define "classes", which serve to specify the search path for resources,
specify scoping for the scripts, ...
all this is dynamic, eg, no compiled code involved, and even fairly minor
impact on the scripting. "classes as an abstraction for data" really. the
masses of game resources are becomming large and difficult to manage or
navigate, possibly eventually serving as an impediment to modders or such...
>>>Unfortunately this is getting into rendering issues that are well beyond
>>>my ken. I haven't paid any attention to graphics techniques for a
>>>decade, which makes me pretty obsolete in that venue.
>>>
>>
>> yes.
>>
>> sprites have gone away. statically deformed meshes have risen and gone
>> away. static animations are fading, and may be replaced eventually. ...
>>
>> always new systems with new data, and old tools that can no longer
>> generate new data or have arbitrary limitations. it is sort of like a
>> treadmill on this front.
>> there also seems to be an ever-increasing amount of ties between the
>> subsystems, eg, physics, scripts, and rendering appear to be getting
>> integrated at ever finer levels in newer games, ...
>
> That can be good or bad, depending on the context. It is bad if the ties
> represent bleeding cohesion and dependencies between the various subject
> matters. OTOH, I think I mentioned that good decoupling interfaces
> usually have more, finer-grained message traffic.
>
> IOW, the quality is more about why the messages are sent and how they are
> interpreted than it is about the volume of traffic.
>
dunno. I am not really that sure how to split them up, even with lots of
messages.
looser parts are easier, just probably need registration-based interfaces or
message passing or such.
the main problem is a sort of combinatorial problem, namely, it is unclear
how any of the stuff can be done in isolation or reasonably broken up into
message traffic.
I guess ways are found by people to manage this and maintain abstraction,
just like physics can be largely simplified to discreet
timeslices/integration, the use of a set of handlers for every combination
of 2 of each type, and a number of passes for each kind of process:
setup (preparing the entity for movement);
prediction;
detection/compensation (possible multiple passes);
verification/adjustment (if a movement can't be worked out by here it is
just stopped);
finalization (finishing the move and stabilizing the entity state).
detection is a biggie here. one has to deal with the various pairings of
model/solid types.
compensation is a little easier, since one can take shortcuts.
this is a hassle not that easily explained, and it requires multiple
attempts to get a really usable physics engine.
now, if one looks at it enough, it might also become apparent why things
like ik/"ragdoll" are scary-looking. they have both combinatorial and
integration related problems. not only that, but the renderer gets involved,
the current state of a model/entity (positions, bounds, ...) are needed both
by physics and rendering, and don't go well with the typical abstractions
(eg: frames, state variables, ...). actually, it could probably be said
physics is doing the heavy lifting and rendering is just along for the ride.
this is part of why I am leaving them along for the time being.
someone else would probably just be like "you suck, ik is easy" or "you are
just imagining issues". dunno, but I am presently doubting it.
I have to wasit until I am ready to challenge the problem, even if it is
months later (as it was with skeletal animation in the first place...).
tools and other software is another problem, I don't make the tools and I
can't control how they do things or what they understand. one has to
compromise a lot on this front.
- Next message: Roger L. Cauvin: "Re: Singletons"
- Previous message: Roger L. Cauvin: "Re: Singletons"
- In reply to: H. S. Lahman: "Re: new here, my lang project..."
- Next in thread: H. S. Lahman: "Re: new here, my lang project..."
- Reply: H. S. Lahman: "Re: new here, my lang project..."
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|