Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: How to pass current instance of a window for input?  (Read 2886 times)

0 Members and 1 Guest are viewing this topic.

RedShift

  • Newbie
  • *
  • Posts: 6
    • View Profile
How to pass current instance of a window for input?
« on: January 12, 2012, 06:35:06 am »
Hey all,
I'm trying to construct an input class so that my game can be cleanly separated from SFML in-case the framework changes or I change frameworks.

However, i'm having a problem figuring out how to pass the current instance of a window to another class.

I have a main game class that contains all the usual init, game loop, update, draw and cleanup functions. This class also initializes the current window.

I wrote a function that returns the current window in hopes I could use it to write my input class (since sf::input is partnered with a window instance):

Code: [Select]

sf::Window & getWindow()
 {return window;}


However, when I go to my input class I still need to write this:

Code: [Select]

Game game
sf::Input input = game.getWindow().GetInput();


Which is really awkward and actually doesn't work. I think I'm complicating this matter too much.

How can I pass an instance of a window to an input class?

I could write a function like:

Code: [Select]

bool leftKeyDown;

bool LeftKeyDown(sf::Input & input)
{
return (leftkeyDown = input.IsKeyDown(sf::Key::Left);)
}


Would this be more correct?

Serapth

  • Full Member
  • ***
  • Posts: 105
    • View Profile
How to pass current instance of a window for input?
« Reply #1 on: January 12, 2012, 02:41:47 pm »
If your end goal is to be able to potentially switch libraries in the future, you don't want to be passing Window objects anywhere.  Basically, you don't want to pass any SFML objects between classes if you can help it, those are exactly the couplings you need to break.

This is the point where you might want to start looking in to design patterns.  The mediator design pattern is all about having different objects able to interact, while knowing nothing about each other.  The observer pattern is all about having objects automatically updated if another object's state changes ( ie, an input object has new input, or a keyboards key state changed ), while in my own tutorial I demonstrate the service locator, in which I illustrate how you could swap out SFML's sound subsystem for FMOD, without changing a line of code.






EDIT:


To put into more simple terms to directly answer your question, you are going to have to access the window object at some point, as simply put, that is the interface SFML provided.  Your goal is to abstract away that dependency.


So for example, instead of creating a SFMLInput class you create a generic Input class ( or interface ) and a SFMLInput class and your code interacts with the Input class, not the implementation specific class, so making changes in the future is trivial.  In this scenario, the SFMLInput class is the only one that needs to know about the SFML specifics.


EDIT2:

Decoupling the dependency on a library like SFML ( which handles so much of your overall game ) is a right pain in the ass.  It's good form and over all a pretty good idea, especially if you plan to port to portables or possibly other platforms that SFML doesn't support, just be sure you know what you are getting into.  If you are working on your first or second game, I don't really think I would be worried too much about this.

This is where using a really light weight interface can be a good comprimise.  So then what you do is have a (global) class like Game with methods like Game::GetInput() or Game::GetWindow() etc.  You are creating a coupling every single time you call that GetWhatever() method, but you are creating a very easy to find coupling.  Mean that if you want to changes things out in the future, its mostly a matter of find and replace.  A choir to be sure, but a hell of a lot easier than have to scour your code for all IO calls, etc.

RedShift

  • Newbie
  • *
  • Posts: 6
    • View Profile
How to pass current instance of a window for input?
« Reply #2 on: January 12, 2012, 08:29:11 pm »
Ok, I appreciate your reply. You made several good points. I will have to keep this in mind. I'll have to review your tutorial as it seems it goes over exactly what i'm trying to accomplish.

If I understand you right this is how I could create a buffer between my code and SFML:

/////Only reason I define functions in the same file as the header is to save space////
Code: [Select]

class SFMLInput
{
private:

public:
bool IsKeyDown(sf::Input & Input, sf::Key::Code KeyCode)
{return (input.IsKeyDown(Keycode));}
//...
};

class GenInput
{
private:
//...
public:
bool IsKeyDown(Keycode keycode)
{return (SFMLInput.IsKeyDown(keycode));}
}


Is this somewhat what you're talking about? With this example it seems I would run into the problem of separating Keycodes from SFML's keycodes.  The first solution I see is to create my own enum of keycodes, but that is kind of redundant and doesn't reuse code, so probably a bad idea.

So unless I can use a reference or pointer to an enum (i've never really tried), i'm going to have to think this through further. I could use a get function to return the value of the key, so:

Code: [Select]

class SFMLInput
{
public:
//Get functions for all keys I would use
int GetKeyW() {return sf::Key::W;}
//...
};

class GenInput
{
bool IsKeyDown(int KeyCode)
{return SFMLInput.IsKeyDown(KeyCode);}

//And Could Be Used As Such///

class SomethingToDoWithSpriteMovement
{
private:
vector2 position;
GenInput genInput;
SFMLInput sfmlInput;
public:
void move() {
if (genInput.IsKeyDown(sfmlInput.GetKeyW()))
    position.x += speed;

};





If I don't get this implemented 100% I won't mind but it is good practice to try anyway.

I am more concerned with my game's architecture and class structure.

[EDIT]

I just realized I messed this up because I was not passing sf::Input to SFMLInput.IskeyDown.

So this doesn't work.  :(

Serapth

  • Full Member
  • ***
  • Posts: 105
    • View Profile
How to pass current instance of a window for input?
« Reply #3 on: January 12, 2012, 11:09:14 pm »
That system is close, yes, and illustrates perfectly what I mean about it being a pain in the ass to abstract away a library like SFML.


You are completely right, you would end up having to map SFML keycodes to a non SFML value that you could expose to the rest of your code.  The question really comes down to "is it worth it?".  Keep in mind, there is no issue with using SFML objects directly, you aren't breaking some kind of design rule by doing so, just know that porting your code to another library might be a pain in the ass later.

If you do in fact want to proceed with this, read the part I wrote about ServiceLocator, as that would most likely be exactly what I would tell you to do.  The approach you just suggested would work too with a bit of adaption, but you end up turning GenInput into a Proxy of sorts.  If you are going to go ahead with your approach, change it so you can pass SFMLInput into GenInput, either as a constructor or using some init() function.  Then you only have to change code at one location, when you initialize your input system in the first place.

RedShift

  • Newbie
  • *
  • Posts: 6
    • View Profile
How to pass current instance of a window for input?
« Reply #4 on: January 12, 2012, 11:53:49 pm »
Ok, I just looked over your tut. on the service provider. I am going to implement that idea into my program and let you know how it goes. I do not have my tile engine up and running yet, but if all complies fine then it's a good start.