boyserk84 / zombie-real-time-strategy-game

Automatically exported from code.google.com/p/zombie-real-time-strategy-game
0 stars 0 forks source link

Do not know what game events are necessary. Need to drive these requirements. #21

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
Hey guys.

Here's an interesting article:

http://stackoverflow.com/questions/550785/c-events-or-an-observer-interface-pros
-cons

It discusses how in C# you can use Delegates and Events to implement a freer 
form of the observer pattern.  This is PERFECT for what we are doing.  When a 
model component changes it fires off events.  Example, it gets attacked, so it 
fires off a "UnitHPChanged" event.  Let's say its HP dropped below 0, so it 
instead fires a "UnitDied" event.  Things can register methods via method 
pointer (Delegates) to these events, and can perform whatever logic we need.  
No observer interface necessary (I can hear the groans of the view team ahahaha)

Examples:

Move from the bottom of the model up.  So a Player's resources:  You'd be 
interested in a "PlayerResourcesChanged" event.  The arguments sent (see 
EventArgs) would be maybe oldValue, newValue, resourceType.  What's interested 
in listening to this?  Say we have a UI Component at the top of the screen that 
displays your gold.  It registers for this event, and this event only.  Say you 
have a BuildingUI component that shows you all the possible units you can 
build.  It grays out those you cannot afford.  It would register for this 
event, because if you ended up getting enough gold, it would have to change the 
view to now display the unit as not grayed out.  In our old observer pattern, 
this view would refresh any time the model changed in general, and would have 
to perform a bunch of logic to determine if the resources changed.

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.  We know that we need to display an attack animation if UnitAttacked was 
passed.  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.  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", "NotifyOfHPChanged" 
"NotifyOfDeath"..blah blah blah.  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).

So this is a big question for the views and game designers (Dans).  What do you 
consider a "Game Event"?

If you were a UI piece, or a trigger, what would YOU be interested in listening 
for?

Leave comments here.  Try to do it for the following classes:

Building
BuildingList (List of all buildings owned by a player)
Unit
UnitList (List of all units owned by a player)
PlayerResources
Player
PlayerList
Cell (Probably the only one here would be ResourcesChanged)
Map
Gameworld
Scenario

Original issue reported on code.google.com by marc.cel...@gmail.com on 8 Mar 2011 at 11:00

GoogleCodeExporter commented 9 years ago

Original comment by marc.cel...@gmail.com on 8 Mar 2011 at 11:00

GoogleCodeExporter commented 9 years ago
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

GoogleCodeExporter commented 9 years ago
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

GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
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

GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
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

GoogleCodeExporter commented 9 years ago
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