Open GoogleCodeExporter opened 9 years ago
Original comment by marc.cel...@gmail.com
on 8 Mar 2011 at 11:00
Events I think would be good.
Building
----------------------
HPChanged
BeganProducingUnit
FinishedProducingUnit
Unit
----------------------
HPChanged
WasAttacked
Attacked
Moved
BuiltBuilding
Harvested
BuildingList
----------------
AddedBuilding
RemovedBuilding
RanOutOfBuildings
UnitList
----------------
AddedUnit
RemovedUnit
RanOutOfUnits
PlayerResources
----------------
AmountChanged
Player
----------------
ResourcesChanged
UnitListChanged
BuildingListChanged
PlayerList
----------------
PlayerAdded
PlayerRemoved
Cell
----------------
(N/A)
Map
----------------
ResourcesChanged (Would include cell in arguements)
Gameworld
----------------
(N/A)
Scenario
----------------
(N/A)
Not sure on all of these but its a start. Probably a lot more I'm missing.
Original comment by marc.cel...@gmail.com
on 8 Mar 2011 at 11:08
I agree that the freer form of the observer pattern looks PERFECT as you've
suggested,
but in reality that is NOT how the (main) game application usually works.
Game development is quite different from developing traditional software.
Most conforming standard/pattern may not fit well with the game. It needs to be
readjusted. Even MVC pattern for the game is different from traditional MVC
pattern.
Prof. Johnson can confirm on this one as well (it's in his slides as well).
MVC should suffice the (main) game logic that we are aiming for.
The example that you gave about getting attacked and fires off a UnitHPChanged
are all usually handled by the controller.
To recap how this normally works, the controller once updates its logic, it
will query a view to change or view already knows this (by reference objects).
Basically, controller will know things about models/logic in the game.
There are parts that I agree and disagree with.
Now I'm going to point out why we may need and don't need freer form of the
observer pattern:
>>For a Unit, you'd be interested in "UnitMoved", "UnitAttacked",
"UnitWasAttacked", "UnitDied", "UnitHPChanged"
>>(may be because attacked, healed, or regened), UnitBuffsChanged,
UnitStatsChanged...
>>When it comes time to display, we know we need to refresh the main map view
if a unit on screen moved.
Not exactly,
In general, View doesn't care about game logic at all. What it does care is the
information inside the models.
View just aggregates data and determines what it should be displayed. View does
not even update the game logic.
All game logic and data are handled by the controller and View already has
reference to the data. Therefore, View will just read data and update the
display. That's it.
The controller will always be inside the update function and the update
function (in XNA) will always run indefinitely. It is a nature of game app
mechanic/structure.
Therefore, there is no need to care when the unit will be killed, attacked,
dead and etc.
Even if you develop a game in flash/Actionscript3, the developing pattern in
flash/actionscript is very simliar to the one you've mentioned,
the update function will still need to run indefinitely.
>>We know that we need to display an attack animation if UnitAttacked was
passed.
This already handles by View. Yes, agreed we need to display the attack
animation.
>>we know we need to change the selected unit on the bottom of the screen if it
died.
>>And now, we can listen in for specific events and encapsulate the handling
logic within a single method.
To display if the selected unit is dead is not really the issue, the real issue
is that if you have menu associated with the selected unit once it has been
killed, how are we going to notify view to change the menu display. That's when
we need the pattern you've mentioned. I agree that we may need a listener for
specific events if we ever need to change the menu associated with the selected
unit.
>>Specific views can register for whatever events they want without having to
worry about a ton of interfaces
>>to implement. Compared to the observer pattern, we don't have to be exposing
a ton of interfaces like
>>"Register" "Unregister",
Specific view, yes such as ViewSelect (our new implementation for handling
selected units by the user). Not the actual gameView.
Btw, the view team already incorporated observer-pattern for this. It is not
perfect. It is a modified version of the observer-pattern and works well for
now.
>>"NotifyOfHPChanged" "NotifyOfDeath"..blah blah blah.
No, we don't actually need "notifyofHPChange" or "NotifyOfdeath" and blah blah
because the controller or game logic has checks for these things.
Anything that deals with game logic or actual game view should not be confused
with the user's non-game-world UI.
>>We dynamically add event handlers however we please, as long as their
parameter list
>>is (Object sender, EventArgs args).
>> In the old way, our map view would refresh any time the model changed at all,
>>when it may not need to (game is idol).
Like I mentioned above, game by its nature needs constant refreshing and
updating (i.e. animation, AI logic, timer, and etc).
Yes, View will be updated. That's how the game app will always work.
I partially agree with you, Marc. But I don't think we need to drastically
adopt the pattern you suggested for all components.
We should choose which part of the game is appropriate for which pattern.
I agree (aye aye) for:
- Anything that deals with User interaction's UI (like click button, menu
appeared based on a different type of units selected) should be implemented
with something simliar to event-based handling.
- GameEvent and Gametrigger components look like good candidates.
Regards,
Original comment by nkem...@gmail.com
on 9 Mar 2011 at 4:07
[deleted comment]
You bring up some good points regarding the main game view. It is likely that
on every tick, the view will be invalidated in some fashion and will have to
repaint. If the view is going to always refresh, it has no reason to observe.
(The purpose of the observer pattern is to reduce the number of "useless"
queries into the model - the model hasn't changed, so there was no reason to
query. However, since the query that the game view is making will probably
always give different results (not useless), then it doesn't even have to
observe in the first place!)
But you also brought up, correctly, that some other views will only need to be
refreshed on occassion - such as ViewSelect.
Let's talk more about view select. We can choose three different ways of
having view select refresh.
A. Needs to be told to be refreshed by a controller
B. Needs to be told to be refreshed by a piece of model.
C. Refreshes every tick.
There is no other way that it will know to refresh! I tend to say that C is a
bad implementation choice, because its going to result in a lot of Gets from
the model, and oftentime does not have to do this. It will also repaint when
it doesn't have to.
Controller is fine, but now we are coupling our controller to our views, losing
reusability of our controller. In general, controllers should be able to
service all sorts of views, and not be tied down to them. That is why it is
common practice for the view to observe the model that it is representing with
UI. A view merely has to tell the controller and game logic of certain
mouse/keyboard events, have the controller handle them appropriately, and be
done. The controller merely has to accept these events (from ANY view) and
know what to do with them.
If, for example, we rewrite or remove our ViewSelect, and we tied the
controller to this ViewSelect, we have to rewrite our controller (which
contains all of our game logic).
If we add a new view, we need to rewrite the controller to call refreshes on
this new view every time a piece of model is changed, rather than having the
view register as an observer for game events in the beginning and just listen.
I think we can do better.
What does ViewSelect do? It displays units that are selected. You can't say
that the view doesn't have knowledge of the model - it MUST in order to display
them! It is true that it should have no knowledge of game logic, but as you
will see, it will not.
ViewSelect needs to observe the units that it is selecting. It probably needs
to display a picture of that unit, a bar representing its HP, and it needs to
remove it when it dies.
Well, we can have it observe all the units that are selected. Then, when a
unit changes, we can update the view so that it reflects changes in HP, or
removes it if it dies.
But I think we can do better than that!
Under this observation, the view would refresh any time a selected unit moves
or attacks or harvests...or changes in ANY way. Considering they are selected,
it is very likely that we will have them move or attack.
Really, the view isnt interested in all that. All it wants to know is when one
of the selected units dies, or changes HP.
And hence, Events. Rather than the UI calling "Notify" on the view, it fires
off an event. The UI handles the event. You can even split up the handling
further down! Say you have a UI class that represents the bar, a UI class that
represents an entire single unit, and a UI class that represents the entire
Selection View. Selection View has a layout for displaying the UnitUIs, and
adds the UnitUIs to this layout. UnitUIs listen for the UnitDies event for the
unit they are representing. If the unit dies, the UI removes itself from the
layout in the SelectView. Modular. The HPBarUI is really only interested in
when the unit's HP changes. UnitHPChanged event fires from the unit whenever
that HP changes. Bar receives it, and refreshes.
And there you have it.
Views and models are almost ALWAYS composites in MVC (don't see why it should
be different for games). The purpose is so that you can encapsulate WITHIN A
VIEW the behavior for when it updates, and have it update when the piece of
model it is bound to changes.
The events can also be listened to by Triggers. Triggers are simply pieces of
game logic. But do we really want to always have to evaluate triggers? Are we
going to have a list of triggers in the controller and loop through them and
evaluate them all every single tick? Instead lets say we have a trigger that
reads "If the player runs out of units, he loses" Well, we shouldn't have to
evaluate that trigger every tick - only when a unit dies. That evaluation
happens at the UnitList level - when a unit dies, it checks "Has this list just
emptied?" and if so, it fires off a RanOutOfUnits event. The trigger just has
to listen for this event, and when it gets fired, it knows that its condition
has been fully met and it can execute its TriggerAction code.
So as you see, these hooks are not meant just for views - they can be used for
game logic as well, and helping us keep from evaluating every piece of game
logic on every single tick. This is a mechanism for organizing when to
evaluate or execute some piece of code. It will GREATLY simplify our
controller, and make the logic and views more modular.
Original comment by marc.cel...@gmail.com
on 9 Mar 2011 at 6:12
[deleted comment]
[deleted comment]
Here is my response and clarification of the prior posts:
>>Let's talk more about view select. We can choose three different ways of
having view
>>select refresh.
>>A. Needs to be told to be refreshed by a controller
>>B. Needs to be told to be refreshed by a piece of model.
>>C. Refreshes every tick.
>>There is no other way that it will know to refresh! I tend to say that C is
a bad
>>implementation choice, because its going to result in a lot of Gets from the
model, and
>>often time does not have to do this. It will also repaint when it doesn't
have to.
C is not exactly how ViewSelect operates. It was told by a controller with a
piece of model as a medium. It is “View” that needs to do C.
I understand your concern about things will get repainted although there is
nothing to update.
I do not think keep drawing all elements on the screen is the real issue here
as we were not living in the 1984 eras where computational power was very less.
I don’t think we need to be frugal about it, especially in XNA. However, I
agree with you. That’s a good point.
I’ll take a look at this issue and come up with the solution.
There is a major difference between game and the rest of the software app. Most
traditional software programs respond to user input and do nothing without it.
For example, a word processor formats words and text as a user types. If the
user doesn't type anything, the word processor does nothing. Some functions may
take a long time to complete, but all are initiated by a user telling the
program to do something.
Games, on the other hand, MUST continue to operate regardless of a user's
input. The game loop allows this. A highly simplified game loop, in pseudocode,
might look something like this: (citation : wikipedia)
while( user doesn't exit )
check for user input (equivalent to View’s converting method)
run AI
move enemies
resolve collisions (game Controller/Game logic)
draw graphics (View and other types of View)
play sounds
end while
In the XNA framework, things are more organized. So drawing graphics has their
own dedicated method called Draw() and the rest of the game loop is in update()
method.
I think the definition of “controller” is fuzzy here. When I say
“controller” I meant “game loop” (aka main program) and “game
controller” is the controller for the actual game logic. I think from now on,
I will use “game loop” instead of controller.
>> Controller is fine, but now we are coupling our controller to our views,
losing
>> reusability of our controller.
>> In general, controllers should be able to service all sorts of views, and
not be tied
>> down to them.
I don’t see how exactly the “ViewSelect” is currently tied to the (game)
controller as you have described.
Yes it is tied to the “main program” or “game loop” at the moment in
order to fetch data (models) from the game controller whenever there are
changes.
>> A view merely has to tell the controller and game logic of certain
mouse/keyboard
>> events, have the controller handle them appropriately, and be done. The
controller
>> merely has to accept these events (from ANY view) and know what to do with
them.
Isn’t it what we’re doing here inside the “gameloop”? Whenever there is
input from the mouse or IO, View tell the game controller to do the game logic
stuff.
ZRTSGame (XNA)
Public void update(GameTime) (a.k.a “gameloop”)
{
1) Listen for I/O input
2) View converts that I/O input (screen location) to gameLoc and then pass it
to the game controller. (This part is one the reasons why View in MVC game is
different from traditional MVC. I think it is equivalent of telling game
controller that here is an (translated) input, go handle it. )
3) The “game controller” processes it.
4) If there’s something going on with the data, “game controller”
notifies (a.k.a. notify()) the ViewSelect via observer pattern or “game
controller” passes the (updated) data back to the ViewSelect.
}
>> If, for example, we rewrite or remove our ViewSelect, and we tied the
controller to
>>this ViewSelect, we have to rewrite our controller (which contains all of our
game
>> logic).If we add a new view, we need to rewrite the controller to call
refreshes on
>> this new view every time a piece of model is changed, rather than having the
view
>> register as an observer for game events in the beginning and just listen.
Game controller has no direct relation to the ViewSelect at all.
ViewSelect will need to be notified of changes the “game controller” has
made inside the “gameloop” in some ways.
>> I think we can do better.
>> What does ViewSelect do? It displays units that are selected. You can't
say that the
>> view doesn't have knowledge of the model - it MUST in order to display them!
It is
>> true that it should have no knowledge of game logic, but as you will see, it
will not.
I did not say that View doesn’t have knowledge of the model. It DOES in order
to display them!
>> ViewSelect needs to observe the units that it is selecting. It probably
needs to
>> display a picture of that unit, a bar representing its HP, and it needs to
remove it
>> when it dies.
The way ViewSelect works is that the ViewSelect will draw only a cosmetic part
of the unit, which means ViewSelect will draw that selected black box (that you
saw) on the top of the real units. In fact, only “View” actually draws the
real units. It does not matter if we ever need to remove a bar representing HP
for each unit or not because ViewSelect will only draw those bars corresponding
to the units being passed to it. If no unit is selected, no bar will be drawn
on the screen. No object needs to be allocated/deallocated.
>> Well, we can have it observe all the units that are selected. Then, when a
unit
>> changes, we can update the view so that it reflects changes in HP, or
removes it if it
>> dies.
>> But I think we can do better than that!
>> Under this observation, the view would refresh any time a selected unit
moves or
>> attacks or harvests...or changes in ANY way. Considering they are selected,
it is
>> very likely that we will have them move or attack.
>> Really, the view isnt interested in all that. All it wants to know is when
one of the
>> selected units dies, or changes HP.
To be clear, “View” will only be interested in information it can read from
the models. If models happen to be changed, sure, View will draw according to
whatever it needs to be drawn corresponding to that change. (i.e. unit is now
in the harvesting state, the View draw animation of that unit harvesting
something.)
>> And hence, Events. Rather than the UI calling "Notify" on the view, it
fires off an
>> event. The UI handles the event. You can even split up the handling
further down!
>> Say you have a UI class that represents the bar, a UI class that represents
an entire
>> single unit, and a UI class that represents the entire Selection View.
Selection View
>> has a layout for displaying the UnitUIs, and adds the UnitUIs to this
layout. UnitUIs
>> listen for the UnitDies event for the unit they are representing. If the
unit dies,
>> the UI removes itself from the layout in the SelectView. Modular. The
HPBarUI is
>> really only interested in when the unit's HP changes. UnitHPChanged event
fires from
>> the unit whenever that HP changes. Bar receives it, and refreshes.
>> And there you have it.
“ViewSelect” has a layout for displaying the cosmetic UI (for now that
selected box around each unit, in the future, health bar and etc.) of the unit.
>> Views and models are almost ALWAYS composites in MVC (don't see why it
should be
>> different for games). The purpose is so that you can encapsulate WITHIN A
VIEW the
>> behavior for when it updates, and have it update when the piece of model it
is bound
>> to changes.
The main purpose of the MVC in the game is to separate “drawing logic” and
“game logic” so that you don’t have to care about “drawing” when you
do “game logic”. For example, you can have a chess game with an isometric
view and top down view while the game logic remains the same. View and Model
are closely related. “View” needs to know “Model” in order to draw and
respond to the screen correctly. “(Game) Controller” and “Model” work
closely to deliver the result of the game logic. I don’t if that answers your
question or not. My suggestion is try to actually make a small game with MVC
and see how this would be different from the traditional MVC. The main concept
of the MVC is relatively the same, but details and relationship between parts
will be different.
>> The events can also be listened to by Triggers. Triggers are simply pieces
of game
>> logic. But do we really want to always have to evaluate triggers? Are we
going to
>> have a list of triggers in the controller and loop through them and evaluate
them all
>> every single tick? Instead lets say we have a trigger that reads "If the
player runs
>> out of units, he loses" Well, we shouldn't have to evaluate that trigger
every tick –
>> only when a unit dies. That evaluation happens at the UnitList level - when
a unit
>> dies, it checks "Has this list just emptied?" and if so, it fires off a
RanOutOfUnits
>> event. The trigger just has to listen for this event, and when it gets
fired, it
>> knows that its condition has been fully met and it can execute its
TriggerAction code.
This part, please ask Dan G. He’s the architect of the game models/design.
I’m just doing things to integrate and create a representation for his parts.
>> So as you see, these hooks are not meant just for views - they can be used
for game
>> logic as well, and helping us keep from evaluating every piece of game logic
on every
>> single tick. This is a mechanism for organizing when to evaluate or execute
some
>> piece of code. It will GREATLY simplify our controller, and make the logic
and views
>> more modular.
>> PS, that is not to say that you couldnt use this pattern with the main game
view.
>> I feel like the trap you are running into is thinking of the main game view
as a
>> single object, which it hopefully is not. The main game view should be a
composite.
>> It should have a background TileGridUI and have children that are overlaid
ResourcesUI
>> pieces and UnitUIs for the units that are displayed, and BuildingUIs for
each
>> building, etc. The TileUI, UnitUI and ResourceUI pieces should all carry
them a
>> location offset, a bitmap that they will display (or some rendering data if
we were
>> doing 3D), and a size.
At the moment, View is only a single object. Why? Because it was written before
the observer pattern was introduced last week. So it has remained that way just
for the sake of validating the game logic and for the sake of simplicity so
that the group that deals with game logic can treat this as a black box without
having their models and stuff being converted to/derived from observable.
>> When you are rendering the main view (composite), you are
>> really rendering all of these individual pieces of UI.
>> When a unit comes into view, you can merely create a new piece of UI and add
it as a
>> child to your composite. By making this view a composite, what you are able
to do is
>> not have to AGGREGATE (it even SOUNDS expensive) data for the entire view on
every
>> tick. Instead, you merely have to update the individual ui pieces that have
changed –
>> those that have moved change their offset. If you scroll, you change
everyones
>> offset. If a resource has been used up, you delete that UI object. Make
sense?
There are 2 issues here. I personally tend not to mix them up. View for the
actual game-based objects and View for the user interaction (non-game-based
objects). The reason that “View” has to aggregate data is that game-based
objects will keep changing along with dynamic animation, AI and etc. At the
moment, it aggregates all data because all data fits within the screen. If the
map size is larger than the screen size, a certain part of the map should only
be rendered (only aggregate through the certain data).
Non-game-based objects’ UI should be based on event-trigger as you’ve
suggested (i.e. menu for building an object, button to command unit, etc).
Agree!
I think the real problem that I see is that you and I have a totally different
perspective about XNA and game programming. My suggestion is that perhaps if
you have times, try to create a simple and small game on your own and see how
it’s different from other type of software program structure. What you have
suggested may suit well for every component for other types of software app,
but for game I don’t think it will suit every component. That’s why I
suggest that some components may be suitable for what you’ve suggested.
Is that why you feel that event-based pattern should strongly be implemented
for every component for the game because it works well for MapEditor?
I feel a bit like this is a religious war. I designed and implemented
‘View’ to suit and cover the scope in the iterative manner. There are
always tradeoffs. Nothing is perfect. I try to make a room (to some certain
extends) for everything in order to improve.
There are some good points that you’ve made and I agree.
Regards,
Original comment by nkem...@gmail.com
on 10 Mar 2011 at 12:54
Let's set up a meeting for this offline. Our posts are way too long for it to
be good in this medium. How does Monday's meeting sound?
Original comment by marc.cel...@gmail.com
on 10 Mar 2011 at 3:33
Original issue reported on code.google.com by
marc.cel...@gmail.com
on 8 Mar 2011 at 11:00