Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


Don't forget to read Tuesday's email newsletter for your chance to win a free copy of Construct 2!


Josh Petrie

Member Since 11 Jun 2003
Offline Last Active Private

#5001282 --- Finished Breakout ---

Posted by Josh Petrie on 15 November 2012 - 11:12 AM

There are two basic ideas that I'm trying to drive you towards: the idea that a given class or interface should serve a single function ("single responsibility principle") and, to that end, the idea that while object-oriented design begins with the conceptualization of types as real world analogues, it doesn't (and shouldn't end there) -- you can have classes that don't correspond to real-world objects.

Your sprite manager code has some game-specific constructs, such as a specific texture variable for the player and the ball. It also handles game-specific initialization and update logic, such as StartGame and the collision checks (even the fact that it handles collision at all is problematic).

At a high level, I would instead expect to see this:
  • A type called "SpriteManager" should only understand how to manage a list of (generic) sprite objects and send them to the graphics card at render time. While these sprites may include position, width and height as data, that data is not necessarily what is used to perform collision. The idea that collision geometry and render geometry are distinct is a common practice and becomes very important with 3D games or even in 2D games when you have complicated shapes.
  • You should have a distinct entity for representing collision geometry; perhaps a "CollisionShape" interface contains collision bounds information. These collision entities should be managed by a "CollisionWorld" or similar high-level interface that understands the relative positions of all collision entities and can raise events or invoke callbacks when any set of entities enters or leaves collision (or remains in collision across more than one frame, that can be useful as well).

Your top-level "game" class should be the one that contains all the game specific logic and types. The game class can maintain a set of logical game objects -- either using a single common class or, if their functionality differs wildly enough, many classes. Since we're already attempting to generalize quite a lot, I will leave further generalization of the game logic entities to you and, for the purposes of this example, say we have a Ball class and a Player class.

The game creates a ball instance, and asks the sprite manager to create a corresponding sprite. That sprite is stored in the ball object. Similarly, the game asks the collision world to construct a collision entity with the ball's dimensions and initial position in the world. A reference to that entity is also stored in the ball. Similarly for the player.

Now the game loop consists of, approximately: collecting player input, applying those inputs to the player object's collision entity (to move it), then asking the collision world to update. This will cause the collision world to fire any callbacks registered concerning inter-object collision -- the game would have installed a callback to handle ball-ball collision, ball-wall collision, and ball-player collision, possibly. In those callbacks, appropriate action can be taken by the game class -- perhaps to destroy one or both entities, or reflect the path of one to cause a "bounce" effect, et cetera.

Finally, the game looks at every game entity it knows about (balls, player, et cetera) and transfers the appropriate logical properties -- such as position, or damage state, or whatever -- to the object's renderable representation (the sprite in this case). Then your game asks the sprite manager to render all your frames.

With only this first level of generalization and refactoring (and there's much more you can do on top of this), you should have two systems -- the collision handling stuff and the sprite handling stuff -- that are entirely game-agnostic and can be reused and extended without having to couple them to the idea of a breakout (or any other) kind of game. It's only at the highest level of abstraction where your game-specific logic and means of tying these two systems together exist.


#4999706 The Singleton Pattern: To be or not to be [used]?

Posted by Josh Petrie on 10 November 2012 - 01:46 PM

You're right, people in the games industry can't design code, generally, but more importantly they don't. There is good reason for this. If you're writing software that gets men to the moon then:

- Get the best programmers available.
- Define a rock solid coding standard.
- Design every last operation detail of the code before writing a single line.
- Code review, code review, code review.

Unfortunately very often none of these rules apply in the games industry. The only time i've ever seen code designed to any kind of level in the industry was when a new boss of mine tried to enforce it but ended up having to drop it. There simply isn't time in the games industry because:

- You're usually working through shorter development iterations.
- Management and investors these days want to see progress on your project more frequently than they used to. (related to the above)
- Your title is out in a year and no time can be afford to be "wasted" on writing design documentation that, as soon as the game hits the shelf, is irrelevant.

I will say, however, that designing code and having good OOP has its merits. I'm not trying to say that noone in the world should be writing OOP, but in the games industry and to be honest, even more importantly for the hobbyist, don't waste all your time wondering how to add something to your perfectly architected system.

The more time you spend working out how the hell you're going to integrate physics into your engine the more time you're not spending integrating physics into your engine. You have to find a good balance between writing a codebase that will be productive to work on and also decently written and unfortunately for C++ and OOP allow you to code yourself into a corner too often.

I'm not disagreeing with you here, i understand the necessity of OOP in software engineering. I'm just not convinced the games industry is a suitable domain for it. I've been full circle with writing game engines in my personal time and my current one is my most productive. Gradually i'm migrating it over to plain C with a few C++ niceties like templates here and there.


While historically this is certainly true, I think our industry is maturing to the point where we can start to evolve out of the "cowboy coding" mentality that marked our formative years. Consider MMOs, for example -- you can't expect to build a successful MMO with poor engineering practices. You are building a service more than you are building a product you can ship and forget, a service you'll need to maintain and extend for a decade (or more, hopefully). Sure, MMOs as we know them today will eventually become passe, but some of their core elements, such as massively-connected (or "social," as the kids like to call it days) systems are probably going to embed themselves in the culture of our products for a long time. Even primarily single-player games are starting to integrate those kinds of features.


At ArenaNet, for example, we do a very good job of hitting three out of four of your bullet points. Our automated testing and test tools can be greatly improved (and we're working on it actively), but we take hiring extremely seriously, do up-front design of new systems and RFCs for changes to old ones, and have a very rigorous set of coding standards. We've developed these polices because we have to actively maintain two services -- Guild Wars 1 and now Guild Wars 2 -- for the foreseeable future and now, post-launch, we don't have the luxury of a five-year development cycle to get updates and patches out in to the live environment, so we've had to make sure these practices are followed reasonably during development so that they become habitual and we still employ them even when scrambling to patch an exploit or bug. If we don't, we're going to be building a house of cards for the next seven years -- the failure of which could basically cost the company its existence.

To your last point about writing more "C-like:" I don't think that's actually counter to the idea of writing code that espouses good (possibly OO) design principles. Such code tends to be simpler and consequently easier to read and maintain, and can still involve things like clear responsibility segregation, implementation hiding, et cetera. Much of our code at work is, for example, very C-like in its straightforwardness, and I do the same in many of my own hobby projects. Clever, complicated template metaprogramming hackery and overzealous design-pattern boilerplate implementations that adhere dogmatically to the Rules of Proper Design are, practically speaking, the domain of the academic, the "I wonder if I could..." masturbatory excursions that serve mostly to stretch the language and boost the ego of the author. Certainly, interesting innovations come from such endeavors, but increasingly I find must of them inappropriate for production code on any kind of scale.


#4999548 What's your take on protected variables?

Posted by Josh Petrie on 09 November 2012 - 11:09 PM


The thing about this is that for every 'Shape child' I have to include the width, height, x, y variables separately. Isn't one of the advance of class hierarchy not having to repeat oneself.

Regarding the bold bit -- no. You need to investigate the use of composition vs inheritance. Composition is a tool to allow for code re-use (not repeating yourself). Inheritance is a tool to allow for polymorphism. If you're looking for code re-use, you should default to composition, and only use inheritance where necessary.

Can every shape derivative be descrived with those 4 variables? Bregma hinted earlier that this probably isn't true -- e.g. polygons have a collection of vertices and edges, circles have a radius, squares have an edge-length... If you're making a polymorphic base class, you shouldn't include any variables that aren't going to be common to all derived classes.
If you do have variables that are common to all derived classes, you probably don't need to be using polymorphism either...


I would further stress that the "code reuse" advantage of inheritance is a plus, but should rarely be a major deciding factor in whether or not to employ inheritance. Just because two types might share a few member functions or member variables in their specific implementation doesn't mean one should immediately jump to the conclusion that you need a common base class to "save yourself the trouble of typing those members more than once." That's an abuse of the by-product of inheritance (which I have sadly seen in practice).


#4999350 The Singleton Pattern: To be or not to be [used]?

Posted by Josh Petrie on 09 November 2012 - 11:14 AM

In Game I might have a World class which also have Update() and Draw() methods. And in World there is a Player, but the Player class should be able to call a function in Graphics when a key is pressed. I can't do that in either Update() or Draw() since they only are passed one of Input and Graphics. In this case I find it really useful for one of them to global since I don't have to bother about this issue. I can see two options to solve this without the use of globals.

  • Change Game::Update() and World::Update() to take a Graphics* parameter.
  • Add a Graphics* mGraphics member variable to each class that needs it, and set it with SetGraphics(Graphics* pGraphics) on initialization (what if I forget to set it?)
Maybe there are more options, what do you recommend doing?


Ah, but like I noted before, I would argue here that the problem is with the design whereby "the Player class should be able to call a function in Graphics when a key is pressed." That's giving the player object responsibility over two very distinct systems (graphics and input), especially when the player appearance to be nothing more in this design than an entity within the logical world. You are now conflating, and thus coupling, your logical objects with your input and your rendering. With a bunch of singletons/globals this is less obvious, but when you remove them as you have suggested here, you discover those dependencies.

Ideally all three of those (logic, rendering, input processing) should be independent of each other. Communication between them should occur via the higher level container for each subsystem (in this case, the game itself, so perhaps in your "game" class).

The input system collects a bunch of keyboard events, or whatnot. Your game translates those (maybe via some kind of action mapper). Then it asks the world for the currently-controlled entity and uses the public interface of that entity to apply the actions. Once all that's done and the rest of the world goes through it's simulation step, the game collects all the visible/renderable entities and constructs (or updates) rendering descriptions for them, and feeds those rendering descriptions to the renderer object itself.

In this system:
  • The "Player" class makes the most sense as the action mapper; all it has are a pointer to the currently-controlled world entity, and a bunch of methods to translate unmapped key codes into actions based on the entity state (for example, making the "V" key either draw or stow the entity's weapons, depending on the entity's current "are weapons drawn" state). The responsibility of the player is to change entity state.
  • World logic, input logic and rendering are all independant, tied together at the topmost level by the game itself, which uses a Player object to translate between input and world systems.
  • Similarly, the topmost game object also feeds input to the graphics by iterating all the active and renderable game entities and creating render descriptions (sprites, for example) using that data. The game determines whether to use animation sequence 0 (weapons stowed) or 1 (weapons drawn) for an entity based on that entity's state, but the graphics system only knows about and only has to see a bunch of sprites.
You arrive at the quandary you posed about the player object by incorrectly taking the OO mentality too far -- OO design is about modelling objects in real-world terms, but only to extent. Your initial description of a Player does model a human player very literally -- it is the human player who, however indirectly, causes graphics to change when they press keys. But the in-code version of that object shouldn't be developed that literally, because it leads to exactly the kind of spaghetti of dependencies you encountered.


#4998925 The Singleton Pattern: To be or not to be [used]?

Posted by Josh Petrie on 08 November 2012 - 10:51 AM

I don't understand how you can completely avoid globals? I completely understand to avoid it as much as absolutely possible, but for instance a logger object. Should you really pass your logger instance to all objects in your game that might need logging? And also say for example you use a framework like GLFW for input, how would you pass on the callback without storing some info globally?

Remember that not everything has to be an object. Turning everything into an object can be very poor object-oriented design, in fact. Logging facilities are something that can be implemented perfectly well without resorting to some kind of "logger object" should you so choose.

But if you choose to build that API as an object or set of objects, then passing those objects to interfaces that require them is a perfectly valid solution and generally more desirable than making those objects global if for no other reason than it makes the dependency on the logging system explicit.

A typical argument for why singletons/globals are good or necessary is along the lines of your initial query: "without global access, I will need to pass this object to every function." The flaw in that argument is that it exists because of a deeper implementation issue: every function is coupled to that object.


#4998511 --- Finished Breakout ---

Posted by Josh Petrie on 07 November 2012 - 12:21 PM

You have one of the more unusual indentation styles I've ever seen (or was that the forum's useless "helpful" reformatting?). In any case, I will look over this when I get a chance and give you some specifics, but from a quick glance I can suggest two avenues you might want to look at:
  • Consider looking at a system by which you wouldn't need to hard-code collision detection mechanisms between "the ball" and "the wall" -- how could you generalize your collision representation and handling so you could add new things without modifying the ball code?
  • Your sprite manager is rather coupled to your game logic in a few key ways -- for example, "StartGame." How could you factor that dependency out so you can reuse this sprite management code in your next project?



#4998498 --- Finished Breakout ---

Posted by Josh Petrie on 07 November 2012 - 12:00 PM

I have no intention of playing it (and the OP did not specifically ask for that), I want to look at the code and critique that.

I'm also not in the habit of downloading and running random executable from forums anyhow, especially this one. It only takes one such instance for us to have a repeat of fongerchat.


#4998489 --- Finished Breakout ---

Posted by Josh Petrie on 07 November 2012 - 11:40 AM

Why don't you just paste the code here? It's far more likely that somebody will take a look at it.


#4991148 What to choose?

Posted by Josh Petrie on 17 October 2012 - 10:13 AM

And between SlimDX and normal DirectX with C# what would you recommend?


DirectX is a set of native COM-like libraries, but most of them are not well-behaved COM and so cannot be used via COM interop in C#. Direct3D in particular, which is what you're really asking about.

In other words, there is no "normal DirectX" for C#, you only have options that are wrappers (XNA is a very high level wrapper around D3D, as was Microsoft's MDX which is now deprecated and their CodePack which is now effectively abandoned). Your only sane choices are SlimDX or SharpDX, both of which will work fine.


#4990802 What makes Debug different from Release?

Posted by Josh Petrie on 16 October 2012 - 11:12 AM

The difference between Debug and Release is whatever you define it to be.

"Debug" and "Release" are idioms of Visual Studio (and many other IDEs) -- they are the names of default build configurations generated for a solution/project/whatever-your-IDE-calls-it. Generally, the "debug" configuration does no optimization and enables a bunch of extra sanity checking and safety mechanisms that are not present in the "release" configuration (which also enables optimizations). Almost all of the options in the configuration correspond directly to parameters that can be given to the compiler and linker and any other tools used to actually build your project.

Every major IDE that supports these kinds of configurations also allows you to modify them as you need to, and usually to define additional ones as well.


#4968530 Best way to follow up a tutorial?

Posted by Josh Petrie on 11 August 2012 - 04:40 PM

No. Read the tutorial and try to internalize the concepts it's conveying. Implement them yourself -- don't just copy the code, even if you think you "understand" it, that's a sure way to disaster. If you do understand the concepts and the code you'll have no trouble reimplementing the thing on your own.

Be aware that many tutorials are downright terrible and shouldn't be trusted on their own.


#4968426 To DirectInput or Not to DirectInput

Posted by Josh Petrie on 11 August 2012 - 09:37 AM

Through C++/CX for the .NET side of things. I'm entirely sure it's all possible yet, but I'm working on that side of it ATM and it seems to be going fairly smoothly... Is that not going to work?

No.

C++/CX is C++ with some extensions to support accessing the new Windows Runtime (WinRT); it is primarily syntactic sugar for COM support. C++/CX produces native code as WinRT is a native code framework. You cannot mix managed and native code in a C++/CX project, so you cannot even combine it with C++/CLI. Using C++/CX -- like using C++ -- gives you a 100% native binary.

C++/CLI looks the same as C++/CX because Microsoft basically lifted the syntax. C++/CLI would let you produce managed code, but it isn't C++ -- it's a managed language, so at that point you may as well just be using C#. There is no point to ever using C++/CLI unless you are writing an interop library to expose a native API to managed code or the other way around.

I might not be using the proper term here and may actually be referring to WInRT. But it all looks very similar to .NET to me... I wouldn't want to use a wrapper here either, I would want to interface directly with the library where at all possible for the clearest picture of what's going on.

Yeah, you're not. WinRT isn't .NET, it's not related to the CLR or managed code at all. Furthermore, you can't even use DirectInput for Metro apps in WinRT.

It sounds like all of your plans are based around an assumption of compatibility, when in fact the technologies you're all talking about are quite incompatible at varying levels. You should probably revisit your plan by determining what platform you want to target or what language you want to use and then doing some research into what is possible with that platform -- MSDN is an excellent resource, you should start there.


#4968220 Best language to make 2d games

Posted by Josh Petrie on 10 August 2012 - 04:22 PM

Well this got off-topic fast, didn't it?

I have stripped the topic of the irrelevant and off-course discussion concerning cx_freeze and Python, etc etc. Take it to some other forum, please.


#4967879 To DirectInput or Not to DirectInput

Posted by Josh Petrie on 09 August 2012 - 01:24 PM

So creating a game I hope to be able to compile and run as both Win32 and .Net 4.5,

To use Win32, you need C or C++ (or other native language). To use .NET 4.5 you need C#. How exactly do you expect to do this?

I'm considering using DirectInput to handle all user input as it'll basically be unchanged across both.

The code will have to change rather drastically in fact as the C or C++ interface to DirectInput (via COM) is superficially very different from the available managed wrappers that support DirectInput (SlimDX and SharpDX). The code won't directly compile at all.

I have a feeling you don't quite understand the distinction between Win32 and .NET. Win32 is a native C API, .NET is an implementation of the CLR. They are not equivalent technologies.

Could anyone elucidate why this might be the recommendation?

DirectInput is deprecated and just sits on top of Win32 messages or RawInput, depending. So it's generally better just to use Win32 messages or RawInput. The only thing you'd really need DirectInput for is joystick support.


#4967037 is C# an alternative to C++?

Posted by Josh Petrie on 07 August 2012 - 09:16 AM

This does not appear to be constructive at all.




PARTNERS