Direct3D Tutorial 8 / has VS is

The Partridge Family were neither partridges nor a family. Discuss.
Firepath
Posts: 77
Joined: March 10th, 2018, 11:53 pm

Direct3D Tutorial 8 / has VS is

Post by Firepath » February 28th, 2019, 7:26 am

I watched tutorial 8 (adding keyboard input) a couple of weeks ago and have now gotten around to following the tutes through and am back to that one. I had this thought, and haven't fleshed it out or thought really hard about it, but it seems like it might be OK depending on how you see things. It might be short-sighted or it might be genius.

So initially Chili is designing a client / server and says he doesn't want a monolithic class to take care of all the window, keyboard, mouse, etc. I agree, and this is probably where this idea falls down the most.

Later on Window is made a friend of Keyboard so it can access private methods without those methods being exposed publicly, to those who would use the public methods it has.

This all hinges on the Window HAVING a Keyboard and HAVING a Mouse, etc.

My thought was to have a kind of monolithic class where Window and Keyboard and Mouse, etc. are all PARTS of it. Something like a human-machine interface class (or player-game interface (PGI), whatever you want to call it).

(This idea hinges on multiple inheritance working as I ASSUME it should. I haven't checked that out so yeah...)

This PGI would consist of all the ways the player interacts with the game. It would BE the Window AND the Keyboard AND the Mouse, AND Speakers, etc. It would inherit from all.

This way it could access protected methods of Keyboard, etc. without gaining access to the private methods and member variables, and still have separate public methods for those than need them, as it currently does.

All the Keyboard functionality would still be in Keyboard class, Mouse functionality in Mouse, so there isn't REALLY one big monolithic behemoth that does everything.

Now it would really only have to be the Window due to the message pump currently. Maybe later it would need to include it for the graphics (and sound maybe) output.

I thought further and though you could set the custom user data to the PGI (instead of the Window) and / or pass the message handler function from the PGI to the Window to be set up as the main WndProc from the setup WndProc. This way the Window is basically irrelevant (I think?) as the PGI would process the messages and handle them to the Keyboard, Mouse, etc. functionality where needed.

You do end up with a behemoth of sorts, but one that has functionality separated into classes that it inherits from. You end up with being able to access methods that only the PGI needs while not being able to access the private functionality and data that underlies it, and still provide the same public methods to those that will use it.

The other way is still kind of the same in terms on being monolithic. Instead of going wnd.kbd.KeyIsPressed(...) you would be going pgi.KeyIsPressed(...). The functionality and variables are still separate, they just appear less so, which could be a little more convenient. In one way you're basically asking the Window (wnd.kbd.KeyIsPressed(...)) to tell you if the key is pressed. In the other you're asking the PGI. The fact that the first way you're actually asking the Window's KEYBOARD, is almost just semantics.


I dunno, just spitballing / brainstorming. I may be way off base. I know my own grasp on design isn't great, and I also have trouble seeing how things will be beneficial or detrimental in the future (I guess this is just not understanding the design implications well).


Would love to see some discussion on this.


Also good job on getting the forum moved Chili, I didn't notice a thing, wouldn't have known until I visited had I not watched your vid on it. Even then I would have assumed there was just some style sheet update or something that ruined the look and feel lol.

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Direct3D Tutorial 8 / has VS is

Post by albinopapa » February 28th, 2019, 11:21 am

In the old tutorials, chili actually did split up the keyboard and mouse classes into server and client interfaces. The server portion was windows facing, while the client side was application facing. When the Game object was initialized, you'd pass the keyboard server as a way of initializing the keyboard client that was stored in Game. It didn't have a Window class, so all Win32 initialization was just in the WinMain function. He has definitely modernized the framework in that respect.

Something that I have done several times in the past was to create a System class that was responsible for storing and initializing a Window as well as all the subsystems like Graphics, Mouse and Keyboard and the Game objects. In my setup, the Window class strictly handled Win32 initialization and passed the message pump responsibilities to the System class. The Graphics class did the same as chili's framework for the most part and the input classes ( keyboard and mouse ) were basically just copy and paste of his old framework's code. The reason I did this was because if the message came in that resized a window, I'd need some way of telling the Graphics class about the change as well. Keeping the Graphics and Window classes a part of the System class made this simpler.

As far as the wnd.kbd.KeyIsPressed() issue, I would say most of your logic really shouldn't be in the Game object ( my own personal opinion of course ) so passing the Mouse and Keyboard objects by reference to functions or by pointer if you wanted to store in a class would make this much simpler and cleaner. This way you only type: kbd.KeyIsPressed(...) or mouse.LeftIsPressed() for references and kbd->KeyIsPressed(...) or mouse->LeftIsPressed() for pointers. Since the screen width and height is in the Graphics class ( though I feel it should logically be a part of the Window class ) there isn't much need for any other class to access the Window object anywhere else in your application. Even if you need to end the program for some reason, throw an exception and the program will end. Ok, well, there might be better options like sending a message to the Game object and let it call wnd.Kill() ( can't remember if that is still a part of the framework or not in the Hardware 3D setup. )

There are countless ways to handle the issue, for instance, another approach I have tried was to make an abstract Input class, this approach was pointless as the only reason I did this was for code reuse.
Another was to have the Input class have functionality for mouse and keyboard input.KeyIsPressed(...) and input.LeftIsPressed() ( I guess similar to your PGI approach ) and while this is convenient, there isn't the separation of concerns that is considered "best practice". However, I did like the fact that I only had to pass the Input class instead of writing: player.Update( _mouse, _keyboard ); I could write: player.Update( input );

I suppose it just depends on preference. I like having the mouse and keyboard as part of the Window class only for the simple fact that it is where the message pump is handled so it makes it easier to dispatch to the objects when they are owned by the class. Though I suppose, my System class to me, makes a lot more sense. Some might even think having a messaging system makes more sense and they might be correct. The reason for it would be looser coupling between classes. The ALL the classes involved would get access to the Event/Message class, so for instance, the Window class would post messages or events. Those messages would be forwarded to their respective classes with the data they need ( key strokes, mouse button presses, etc... ) all the processing would be done by the classes themselves. Each of those input classes after processing the data would then need to inform the other classes they have been updated with a new state. So if ESC is pressed and it is suppose to trigger the menu to appear, you'd send a message to the ScreenStateManager ( or whatever ) to switch from GamePlayScreen to GameMenuScreen or if the left mouse button is pressed and you need to fire a weapon, you could emit a message to the Player object, that would create a projectile. This is mostly referred to as the Observer pattern. You have subjects that will inform the observers that something has changed their state, and the observers do something with that information ( like switch screen states or create a projectile ).

Another way to deal with the setup would be the MVC pattern. The MVC pattern stands for Model, View and Controller. I haven't found a definitive definition of the pattern, but the one I like the most is one chili has pointed out in one of my earlier posts.

The model is where all the game logic goes like physics calculations and position/velocity updates, it "models" the world you are playing.
The View is where all your graphics code goes, which in 2D might just be the Graphics object and calls to gfx.DrawSprite or gfx.DrawRect.
The controller would be stuff like input or other things that might need to control either the view or the model. It's like a bridge between the two. You might use the Controller object to control screen state changes by updating or changing the View object to a different view object for the new screen state.

I guess normally, the MVC model is mostly used in windowed programs ( ie Win32 applications or even Linux applications ) that don't rely on 60+ fps, so some explanations say the View is also responsible for user input since there is a link between what people see on screen and where they click. The view would need to update the Model of any changes. The model doesn't really need to know about the view, as it should depend on any particular view or no view at all, it only needs to tell the view that it's state has changed so the view can update whats being presented. To prevent circular dependencies, people have used a messaging system as the bridge.

Other implementations of MVC has the model being nothing more than structs of data that represent the object, the controller contains the program logic and the view is the same as before and communicates the same as before with a messaging system.

One of the things I liked about the MVC pattern is how clean and simple the interfaces are. I know the draw code and associated data objects that go with it ( color, sprites, etc.. ) don't amount to much, but it did seem to keep me focused on things a little easier. I knew that the only code in my model object was available to me, and only the code in my view objects was used for drawing.

TL;DR: There are many ways to approach initializing and interfacing with different components, and it's all a matter of preference. I prefer a top level class handling the initialization of all components and handling the Win32 message pump in this top level class, that way it is easier to communicate with all components when a specific message comes through. I prefer the separation of concerns as opposed to one monolithic class. Using a messaging system might work, this allows decoupling of classes. Two design patterns that use a messaging system are the Observer patter and the MVC pattern.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Firepath
Posts: 77
Joined: March 10th, 2018, 11:53 pm

Re: Direct3D Tutorial 8 / has VS is

Post by Firepath » February 28th, 2019, 9:46 pm

I think initially (like two weeks ago when I first saw the tutorial and the idea was running through my mind) my main question was "Is this idea bad or good or neither but just how I see it / want to design it / prefer?" And that is what you commented on and showed your own idea.

I get a bit hung up on wanting the best design. It really kills my enthusiasm to go with a design idea then down the track, after building on it, find some massive flaw / limitation in it that another design would not have.

For work I did 80 hours (of my own time, mainly during my holidays) of MVC using "Pro ASP.NET MVC 5" by Adam Freeman. Very good book on the subject and I liked it too. Clean, simple interfaces, and a lot of other more modern things than I'd been using. At university and shortly after I never got the hang on model-view as it felt there was something missing and that the model was doing the controlling as well as being the data & logic model. I never started doing anything with it though...

I really like the idea of HAVING a Keyboard versus having a blob player-game interface class that IS the Keyboard and Mouse, etc. The idea I had in my first post was only trying to "fix" the (small) "problem" of making Window a friend. Even in C# I haven't thought before of a way to have a class that one object accesses one way and another accesses it another way. I (now) assume that you would make it implement two interfaces and one would have one interface and another would have the other. From what I've read this is done in C++ by inheriting from multiple base classes.

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Direct3D Tutorial 8 / has VS is

Post by albinopapa » March 1st, 2019, 7:29 am

"fix" the (small) "problem" of making Window a friend
Why would you consider it a problem to have the Mouse and Keyboard classes friends of Window?
Even in C# I haven't thought before of a way to have a class that one object accesses one way and another accesses it another way. I (now) assume that you would make it implement two interfaces and one would have one interface and another would have the other. From what I've read this is done in C++ by inheriting from multiple base classes.
Well, in C++ I suppose you could have say a PGI class inherit from Mouse and Keyboard, then in function parameters, you'd have a Mouse& _mouse or Keyboard& _keyboard, then when you pass the PGI object to one of these functions, it would treat the object as a base Mouse or Keyboard reference instead of just a PGI reference. So I suppose that is one way of doing it, though I don't think that is good design ( that may just be me though ).

So, here's some shell code that kind of illustrates how the old framework use to work, without Mouse being friends with Window.

The Mouse.h file

Code: Select all

class MouseServer;

class Mouse
{
public:
	struct Event
	{
		enum class Type
		{
			Invalid, LPress, RPress, LRelease, RRelease
		};

		
		Type m_type = Type::Invalid;
		int x = -1, y = -1;
	};
public:
	Mouse( MouseServer& _mServ );
	bool LeftIsPressed()const noexcept;
	auto Read()noexcept->Event;

private:
	MouseServer& m_mServ;
};

class MouseServer
{
public:
	void OnLeftPressed( int x, int y );
	void OnLeftReleased( int x, int y );
	void OnRightPressed( int x, int y );
	void OnRightReleased( int x, int y )noexcept;
	void OnMouseMove( int x, int y )noexcept;
	void OnMouseEnter()noexcept;
	void OnMouseLeave()noexcept;

	bool IsInWindow()const noexcept;
private:
	friend class Mouse;
	std::queue<Mouse::Event> m_events;
	int x = -1, y = -1;
	bool m_leftPressed = false, m_rightPressed = false;
};

The MainWindow.h file

Code: Select all

class MainWindow
{
public:
	LRESULT MsgHandler( HWND, INT _msg, WPARAM _wparam, LPARAM _lparam );
	Mouse CreateMouse()noexcept
	{
		return Mouse( mouse );
	}
private:
	MouseServer mouse;
};
MainWindow.cpp

Code: Select all

LRESULT MainWindow::MsgHandler( HWND, INT _msg, WPARAM _wparam, LPARAM _lparam )
{
	switch(_msg)
	{
		case WM_MOUSEMOVE:
		{
			int x = LOWORD( lParam );
			int y = HIWORD( lParam );
			if( x > 0 && x < Graphics::ScreenWidth && y > 0 && y < Graphics::ScreenHeight )
			{
				mouse.OnMouseMove( x, y );
				if( !mouse.IsInWindow() )
				{
					SetCapture( hWnd );
					mouse.OnMouseEnter();
				}
			}
			else
			{
				if( wParam & ( MK_LBUTTON | MK_RBUTTON ) )
				{
					x = std::max( 0, x );
					x = std::min( int( Graphics::ScreenWidth ) - 1, x );
					y = std::max( 0, y );
					y = std::min( int( Graphics::ScreenHeight ) - 1, y );
					mouse.OnMouseMove( x, y );
				}
				else
				{
					ReleaseCapture();
					mouse.OnMouseLeave();
					mouse.OnLeftReleased( x, y );
					mouse.OnRightReleased( x, y );
				}
			}
			break;
		}
	}
}
The Game.h file

Code: Select all

class Game
{
public:
	Game( MainWindow& _window );
	void Go();
private:
	MainWindow& m_window;
	Mouse m_mouse;
};
Game.cpp file

Code: Select all

Game::Game( MainWindow& _window )
	:
	m_window( _window ),
	m_mouse( _window.CreateMouse() )
{}

void Game::Go()
{
	if( m_mouse.LeftIsPressed() )
	{
		// Handle left click
	}
}
Here there is the separation of concerns, MainWindow gets it's interface to the mouse in the form of server, and Game gets its interface to the mouse as a client.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Firepath
Posts: 77
Joined: March 10th, 2018, 11:53 pm

Re: Direct3D Tutorial 8 / has VS is

Post by Firepath » March 1st, 2019, 10:54 pm

albinopapa wrote:
March 1st, 2019, 7:29 am
"fix" the (small) "problem" of making Window a friend
Why would you consider it a problem to have the Mouse and Keyboard classes friends of Window?
I just feel friends is kinda "dirty". I look at it as a way to break rules of privacy for convenience without just making things public, which would be worse. It is only used (in this context) to be able to access some private functions but also exposes all the private functions and member variables. It would be nice to be able to set custom visibility levels so certain things can use certain functions. Or maybe just another level of friendship like "acquaintance" that would allow protected methods to be seen but not private stuff. I'd actually like this idea most now that I've thought of it.
albinopapa wrote:
March 1st, 2019, 7:29 am
Well, in C++ I suppose you could have say a PGI class inherit from Mouse and Keyboard, then in function parameters, you'd have a Mouse& _mouse or Keyboard& _keyboard, then when you pass the PGI object to one of these functions, it would treat the object as a base Mouse or Keyboard reference instead of just a PGI reference. So I suppose that is one way of doing it, though I don't think that is good design ( that may just be me though ).
Exactly my thoughts. I feel it is bad design, blobbing it all together, but it also seems like it might be a good idea if you keep the functionality in the classes inherited from (Keyboard and Mouse, etc.). It would work well and not require friending. You could get a Mouse& or Keyboard& using getters that just return PGI, so like you mentioned they could be treated like just a Mouse or Keyboard. I mean I guess it is one poor idea (massive, multiple-base-class class) to overcome a problem VS another not-ideal solution.

After getting through #13 now though, there are some extra changes (like adding App and moving the WndProc) that may make this idea even less cool though. And the COM stuff, and D3D stuff shuts me up. Chili sure knows what he is doing, so that's a pretty good indicator that how he did this stuff is a better way.

Firepath
Posts: 77
Joined: March 10th, 2018, 11:53 pm

Re: Direct3D Tutorial 8 / has VS is

Post by Firepath » March 1st, 2019, 11:22 pm

I like the MouseServer idea. I was thinking (for about a minute or so) yesterday about making a wrapper (or two) for each class. Very similar to this but using my idea of inheritance and protected methods. I think I would bring the protected OnMouse... methods through into the inheriting MouseServer and expose them publicly there, and have a MouseClient to expose the client methods (well the only real purpose would be to hide the client methods from the MouseServer as they would otherwise need to be public). But after seeing the example you posted, this idea seems needlessly complicated.

THAT use of friend I can get behind, where it is really an extension of the same class so seeing its private bits is the same as if it were all in the same class.

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Direct3D Tutorial 8 / has VS is

Post by albinopapa » March 2nd, 2019, 3:48 am

Well, to me it wouldn't make much sense to have the accessor functions in the input server classes (MouseServer, KeyboardServer ) when the only class that needs that information is the MouseClient, and to be fair, they are a single unit with different facing interfaces, one for updating it's state and one for querying it's state.

I mean I suppose you could have a struct that is a shared data object between the client and server interfaces using the getters for the client side and setters for the server side. This would avoid having to have any friends, and still have the separations of concerns, but you would still need to figure out ( design-wise ) how the data object is to be constructed and how to pass the instantiated object to the two interface classes.

Now, here's my question, is this really about the Mouse/Keyboard being friends with Window or just a general example?

I probably should have started with the friends topic first. I for the first couple years of learning C++, I wanted to work in complete encapsulation and things are difficult and you end up making a lot of public functions that don't involve other classes except those that NEED to change the state of the object. So you end up creating getters and setters for almost all of the members of the class. This still allows for encapsulation because a setter could do more than JUST set the value of something like testing if the new value is within limits, but let's say you have a state machine with each state represented as a separate state.

class Object;
class IdleState;
class WalkState;
class RunState;
class IdleToJumpState;
class WalkToJumpState;
class RunToJumpState;

Now, as each state might require different sprites ( let's stay 2D for this ) would it make sense to have a public SetSprite( std::unique_ptr<Sprite> _sprite ) when no other classes besides these states need to have access to this functionality? Can't really use inheritance in this case, as each new class inheriting from Object for instance would be a new instance with new data, so you'd have to copy over the old data to the new object for every state change. So in this instance, I think it would make sense to make each state friends with the Object class and just pass a reference to the object upon construction to be stored in the state.

You could even do nested classes, where the states are inside of the Object class and not have to declare each state a friend, because they automatically become friends. This is probably considered better as not even the Game class or Graphics class need to even know about the different states of this Object.

Code: Select all

class Object
{
   class IdleState;
   class WalkState;
   class RunState;
   class IdleToJumpState;
   class WalkToJumpState;
   class RunToJumpState;
};
So I guess basically what I'm getting at is the stigma of using the friend keyword isn't just about "being lazy". It should make sense to use it when one interface extends another, but isn't similar enough to use inheritance. I mean, if you take it too far and just want to be lazy, why not just use structs where everything is public and you use the data directly instead of through accessor function? I see enforcing encapsulation through accessors, but not at the expense of design complexity and "because that's they way it's done".

Irrelevant rant
Another thing I never understood is requiring child classes to access the parent class through accessors as well, which is considered "best practice" as well, but I don't see the point. A child will inherit it's parent's public and protected members and functions and usually a child is extending the behavior of the parent through a common interface. So why does it make sense to treat the child class as more of a wrapper for the parent than an extension of the parent?

I whole heartedly support the use of friends when it makes sense and yes, others in the greater C++ community would like access level friendships to be a thing, that would be cool. Hell, I though just putting the friend statement under protected and above private would allow the friend to read only the public and protected stuff only, but sadly as you know this is not the case.

I'm sure it wouldn't be that hard to do: protected friend class IdleState; and disallow any Object's private data form being changed by it's IdleState. This might raise a design question of what to put now in the protected section and what would be in the private section, making class design a bit more complex than it already can be.

In one project I worked on a few years ago, early on in my C++ journey I made wanted to make a state machine. I used a state machine class just to manage the state of a single object's states. So for each object that had multiple states I had to make a separate state machine class, each of the multiple states and the objects with states. I found this to be very cumbersome and wished I had found a better way. Now that I have dropped my defenses on the friend keyword, I think it would go a lot smoother if I were to redo it today.

I understand how you feel about design, this is one of my biggest hangups about coding in general. I have given up on projects because I didn't like the designs I had come up with and had already rewritten the project once or twice to try different strategies. This seriously wears one out and requires a significant about of time to recharge, or at least for me it does.

Hope this helps some.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

User avatar
chili
Site Admin
Posts: 3948
Joined: December 31st, 2011, 4:53 pm
Location: Japan
Contact:

Re: Direct3D Tutorial 8 / has VS is

Post by chili » March 3rd, 2019, 4:24 am

Everybody wants to learn about APIs, or networking, or ASM, but the real meat of software engineering is architectural stuff. For me it really comes together once you understand the underlying principles (stuff like SOLID and GRASP).

BTW, for the state machine, I actually used private friend classes in TWIN to implement the behavior states. It makes sense too, since behaviors are really particular to the particular entity and they are an integral part of it, not a separate independent entity. I make all behaviors private inner friend classes and it works fine. You'll also see another interesting pattern I developed later on in HW3D where the base class has a friend relationship but the children do not.
Last edited by albinopapa on March 3rd, 2019, 9:46 am, edited 1 time in total.
Reason: Add links to wiki articles for SOLID and GRASP.
Chili

User avatar
cyboryxmen
Posts: 190
Joined: November 14th, 2014, 2:03 am

Re: Direct3D Tutorial 8 / has VS is

Post by cyboryxmen » March 3rd, 2019, 8:48 am

Just use Functional programming and avoid all of this bullshit Image
Zekilk

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Direct3D Tutorial 8 / has VS is

Post by albinopapa » March 3rd, 2019, 9:44 am

Sorry, I prefer

Code: Select all

if( mouse.LeftIsPressed() ) 
over

Code: Select all

if( LeftIsPressed( mouse ) )
or worse, because you don't want to pollute the global namespace

Code: Select all

if( Mouse::LeftIsPressed( mouse ) )
or the C-style

Code: Select all

if( mouse_LeftIsPressed( mouse ) ) 
I know there are benefits to using functional programming, like it being faster and/or more secure, but I have noticed a significant gain in readability in using OOP when chaining functions vs nesting functions.

More readable to me:
object.Fn( obj2 ).FnFromReturnObj( someString ).DoSomething();

Less readable to me:
object = DoSomething( FnFromReturnObj( Fn( object, obj2 ) ) );
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Post Reply