Sign in to follow this  
graveyard filla

level editor : give the mouse a state machine?

Recommended Posts

hi, im working on my level editor and have come up with a few problems. for example, there are different types of things you can add to the map. you can add a regular tile, or an NPC, or a link-to-another-map tile, etc. also, i want to allow the user the place free floating map objects on the map.i want it so they press a button, enter the data for the object, then a transparent version of the objects hovers over their mouse untill they click on the map, which then places the object where they click. the problem is, clickong on the map, any of these things could happen, so how do i judge what thing should happen when? IE, how do i know if the player wants to place a tile he has selected, or an NPC he has selected, or whatever? theres 2 ways ive thought about this. have a master class and have npc-map-object, regular-tile, link-tile, etc. all derive from it, then when i click, just do current_selected->on_click(). but, i was thinking this is kind of ugly... i could use a state machine! instead of using polymorphism and pointers, i could just use a state machine. so instead of current_mouse_selection->on_click(); it would be:
if(click)
{
switch(mouse_state)
{
    case PLACING_MAP_OBJECT:
        place_object();
}
so, what do you think? should i go with the state machine instead? it seems cleaner, i mean, isnt this sort of "Abuse" of inheritense? i try not to do that. thanks for any help!

Share this post


Link to post
Share on other sites
The path you do not like is obviously better.

If you code a FSM to perform mouse action then you'll have to modify your FSM each time you want to modify the behavior of particular mouse action. Adding an object will aslo require you modify the FSM.

About code readability, I find


void Window::onClick(POINT p)
{
if (getDoc()->getSelObj())
{
getDoc()->getSelObj()->onClick(getMouseObject(), p);
}
}


Far more readable than a switch witch will need that I scroll the code to find what function is called in a particular case.

This is not an abuse of inheritance at all. All the objects inhertits from a MouseActionable object [which is NOT the base object of your tile/npc/whatever class hierarchy], which seems perfeclty fine to me. Your mouse management code is simpler and do not depends on the objects you are handling, which seems perfecly fine to me again. You have less code to write to add a new MouseActionnable object, which seems perfectly fine to me too :)

Hope I'm clear, and HTH,

Share this post


Link to post
Share on other sites
Conceptually, the mouse does only have a few states: left button state, right button state, position etc. It does not make any real sense to give the mouse any more properties, since it's meant to only be a point-and-click instrument in the first place.

What happens when you click on something is the clicked thing's responsibility. The mouse should not have the smallest idea about what it clicked on in the first place, as this would in most cases break encapsulation and modularity.

On the other hand, you might be needing a brush object: in some cases, a click of the mouse on a certain object will have different effects depending on what was previously clicked. You can then create a brush object that is modified by mouse actions, and is used by the clicked object when it needs to perform an action.

For instance, a tilemap would be associated with a brush, and perform different actions upon a click whether the brush was a tile, or an eraser, or an object. After that, it's up to you to decide what the brush and clickable object know about each other, but remember that it will not break modularity or encapsulation if you allow a brush to know "more" about a clickable object's interface (for instance, you would be using tilemap brushes only tilemaps, so tilemap brushes would be allowed to know more about generic tilemap interfaces they can then use to modify the tilemap).

Share this post


Link to post
Share on other sites
Aye, a mouse has very little state, all you really need it to do is generate events (clicked, dragged, etc.). Then I use a collection of 'tools' which the user can chose betwee. The currently active tool then receives all the mouse events for it to act on them as it needs.

Thats handy 'cos then any internal state for each tool (like current object to place, configurations, image etc.) gets nicely wrapped up in each individual tool so it persists when the user changes between the different tools. Bung in a simple Tool interface and a tool selector to swich between them and you can easily add new tools without needing to change any of the framework code.

Share this post


Link to post
Share on other sites
Slightly dissenting... I actually use both. Like keyboard input can be both in 'text input' and 'keystroke trigger' modes, my mouse handler can be in 'selection' and 'click event' modes. The selection mode sends a stack of every object that is hit [rather than just the top object]. Then if the current state wants a tile, it can pick the top tile out of the list and ignore the rest. If it wants a npc, pick those from the list.

In practice, this tends to be fairly awkward, but it allows for a great amount of flexibility and the ability to select objects irregardless of the rendering context they're in [for example, selecting a target for a spell can now be done via the overland map, or from a sortable list, or from a description of a city's residents]

But then again, this is my first win32 game, and inheritance is one of my weak points.

Share this post


Link to post
Share on other sites
thanks everyone for your replies...

i guess i was thinking that the mouse will need some form of state machine at lease in this situation:

i want to allow the user to add a free floating map object to the map. by free floating object i mean something like a building, a street light, etc, only it doesnt have to be tiles, it can just be a whole image.

now to do this, there would be an "add free floating object" button. you would click this, and be prompted "what is the width /height, what is the path to the texture". after putting in this info, i want that object to be hovering over the mouse, transparently, and then when the user clicks the object is placed on the map where the user clicked.

but how could i do this? wouldnt i need to give my mouse states? something like NO_STATE and PLACING_MAP_OBJ. then my button / gui would get the w/h/texture, then somehow i would have to fill in the x/y of the map object when placed. or maybe i would fill in the x/y immediately, so the map object would be rendering itself at its own x/y, and the x/y would constantly be set to the mouse position untill the user pressed? hmmm, i guess i just have to think about the logic some more. thanks again

EDIT: by the way, im using OpenGL and SDL for everything. this means no nice and friendly GUI stuff. everything is kind of sloppily done with quads and unicode input. kind of nasty, but it gets the job done. im thinking my next game ill use a real API or C# or something instead.

[Edited by - graveyard filla on September 4, 2004 5:55:20 PM]

Share this post


Link to post
Share on other sites
Goto www.objectmentor.com and get the taskmaster article. You would have tasks associated with toolbar buttons(but don't have to be). Your task object would inherit from a generic task interface and when you click a toolbar button ie. selecting different task, you would switch current task for the selected task and all mouse messages would go to that new task object. As to the question about how to make sense of fsm, very simple. Write your task fsm on paper and then copy that straight into code. That's the beauty of fsm. You don't grok the code but the paper. You have to implement some sort of mouse handling logic because if you just brute force it then the logic becomes total mess and it will be harder to insert new task logic into existing mess. I use this taskmaster architecture in my editor and it works wonders. Heck, once I had tasks hooked to my right mouse popup menu button. I also have logic where I select task based on mouse clicks in the view rather than clicks on toolbar. Like when choosing between various xform modes of an object so you can then drag it with a mouse and change its shape rather than have separate xform buttons on the toolbar which is lame. One can also hookup 3D camera to its fsm and handle all camera movements that way. Doing it w/o fsm results in mess.

Share this post


Link to post
Share on other sites
I use that 'task' system as well. I have a base class IEditorMode, with 2 derived classes (so far, I want to keep as few as posible) a terrain editing mode, and a unit editing mode. There is a control bar (it is MFC) with a tab control in it, each tab has a corrosponding mode pointer. It passes off mouse messages to the mode specified by the selected tab.

Share this post


Link to post
Share on other sites
The "brush" or "tool" being talked about is kind of like a "mouse state". Except instead of being some enumeration representing a state, it's an object representing a State (or Strategy), which is a nice OO idea. The mouse itself has no concept of 'placing a picture', but the mouse's 'condition-of-placing-a-picture' certainly does.

See here and here.

Share this post


Link to post
Share on other sites
hmm, im a little confused, i checked out the links but im still confused.

anyway, so the general consesus is to use polymorphism instead of a state machine? for some reason, i think using a state machine is cleaner.. maybe im doing one of them / both wrong? im going to explain the problem again then the 2 options i think i have.

the problem:

in the map editor, i display the map on the screen. now there are different things i can add to the map. i can add an NPC, i can add a map-warp-tile, i can add a regular tile, etc.

so how do i decide, when the user clicks on the map, exactly what he wants to do? the way i have it now is i set up different keys... IE, while hovering the mouse over the map and pressing N, it places an NPC there, while hovering and pressing M, it places a map-warp-tile there, while hovering and right clicking the mouse, it places a regular tile.

i dont want to have to remember which keys to press, and neither does my artist. how can i have to so i can just click on an NPC brush, or map-warp brush, or regular tile brush, it selects the given brush, then when i click on the map (right click with the mouse), it places the currently selected brush.

there are 2 ways i can do this. either a state machine, or polymorphism. first ill show the poly way.

-make a Selectable_Brush base class.

-have Npc, Map-Warp, and Tile all derive from it. now i can just use a pointer to a Selectable_Brush to represent my currently selected brush.

so Selectable Brush is the base, and the other 3 derive from it. Then i give Selectable brush functions like Select() and Place(). then i just do something like if(click the tile map) selectable_brush = some_tile(). if (click the npc button) selectable_brush->Place()

the other choice is State machines. to be honest, i think that perhaps the inheritense way is more elegant, but using a state machine i think is more obviouse. this is my map editor which i dont work on frequently, so when i come back at the code in 2 months, i want to remember as much as possible how things work. im thinking with a state machine, it will be right in my face how everything works. basically there will be 3 things

- each frame, run through the current mouse state, and depending on what it is, draw a transparent version of that item over the mouse curer

-each frame, check if the user right clicks, if he does, run through a case statement checking the state, calling the appropriate function based on the state

-do the same as with poly. if (user clicks tilemap) current_state = PLACING_TILE. same for npc/map warps.


so, what do you think again? as of rigght now im about to implement the state machine, simply because its easier to do, and more obviouse. if this was my game, i would more then likely do the inheritense route because its more elegant, but since i dont work on the editor much, in a few months when i come back to the code, i want to remember what the hell i was doing. so, could i get your opnions again? =) thanks!

Share this post


Link to post
Share on other sites
FSMs come in handy when you have lots of states and events in a task object and you try to make sense out of it. Otherwise you might make state flags and when a mouse button is pressed you can check whether you have pushed some tool button down and recorded a state and see if you're in that state now so that you can place down a certain style brush. With simple thing like this you might not even need inheritance. Just some flags to check in mouse event handlers.

Share this post


Link to post
Share on other sites
You could also try a Chain Of Command pattern. That works well for widget hierarchies. What you do is say:

If ( mouse clicky ) {
GUI->MouseDown( x, y );
}

GUI::MouseDown() {
foreach toplevel child widget {
child->MouseDown(x,y);
}
}

Widget::MouseDown() {
if ( i am waiting for a mouse click for my own use ) {
// do stuph;
}

else {
foreach child widget {
child->MouseDown(x,y);
}
}
}


pardon my strange indentation style.

Share this post


Link to post
Share on other sites
Quote:
Original post by graveyard filla
now to do this, there would be an "add free floating object" button. you would click this, and be prompted "what is the width /height, what is the path to the texture". after putting in this info, i want that object to be hovering over the mouse, transparently, and then when the user clicks the object is placed on the map where the user clicked.

but how could i do this? wouldnt i need to give my mouse states? something like NO_STATE and PLACING_MAP_OBJ. then my button / gui would get the w/h/texture, then somehow i would have to fill in the x/y of the map object when placed. or maybe i would fill in the x/y immediately, so the map object would be rendering itself at its own x/y, and the x/y would constantly be set to the mouse position untill the user pressed? hmmm, i guess i just have to think about the logic some more. thanks again


Just an obvservation ... Conceptually, it's the editor that's placing objects down, not the mouse. Clicking the mouse should tell the editor to go into a placing_object state, whether that be directly or with an object tool, like OrangyTang was discussing.

Unless you plan to implenment the entire slew of Mechwarrior hotkeys with the mouse alone, mouse states might be to your disadvantage.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this