Game programming without OOP

Started by
24 comments, last by apatriarca 14 years, 1 month ago
It's funny, but I've asked the same question, but from the other end. I've never written an OOP program (game or otherwise) in my life and I cannot imagine any reason why I should start now.

OOP and procedural programming is almost identical from a code perspective. When I write a program, I use structs instead of classes, and I separate the code into files based on what it does. For instance, vertex.h would include the vertex data structure, any global variables used by the vertex function and function prototypes. The vertex.cpp (".c" seems to no longer be considered a valid file type) file contains the actual code for manipulating vertices. With C++, typically this would be coded as a single header file, with the vertex class containing both the variables in the C data structure, along with the global variables and actual functions with code instead of prototypes. If there is a .cpp file, it would typically be empty or have empty functions.

Ultimately, the same thing is accomplished. There are pros and cons to each style of programming, but you can do exactly the same with each one. It all boils down to what you are comfortable with.

No, I am not a professional programmer. I'm just a hobbyist having fun...

Advertisement
Quote:Original post by maspeir
With C++, typically this would be coded as a single header file, with the vertex class containing both the variables in the C data structure, along with the global variables and actual functions with code instead of prototypes. If there is a .cpp file, it would typically be empty or have empty functions.


your lack of data hiding is concerning.
If your directly accessing struct members, or global variables, your codebase will be exceptionally brittle, this is not good. use accessors, even in structs. use data hiding, use abstract data types.

why? well, next time you want to swap out the array, for a linked list in one of your exposed struct's your going to have to do a hell of a lot of work to fix up everywhere your poking the internals. use accessors, hide your internals. itll make you far more productive.

also, dont put all your code in the header file, as this will be included in every file which includes the header, increasing your compilation time. and possibly exe size. use inline

and if you really are going to stick with C, for gods sake, use and understand the restrict keyword

as for C itself

- you end up using function pointers, or switch statements to do exactly what you get with type polymorphism anyway

so, your left with either a possible cache miss on a double indirection (vtable lookup) when using c++, or your left with either an instruction cache miss (function pointer) or instruction cache thrash (large switch statement)

either way, you get screwed, pick your poison :)

the ps3 isnt hostile to oop, it, along with modern architectures where memory latency is much greater than cpu cycle latency, are very sensitive to cache misses, thats all cache, including instruction cache. If you miss cache, your going to spend a lot of time waiting for your data to come back from main memory. but this isnt a reason to avoid c++, its a reason to be careful what you do, and when. If you expect to run a piece of code 100,000 times a frame, you want to avoid any sort of uncached memory lookup at all, including instruction cache.

if your doing it 3 times a frame, no one will care either way.

- you use abstract data types, and other data hiding mechanisms instead of using class based data hiding.

this is more of a style thing, its sometimes nice to be able to encapsulate data and functionality into a single object, sometimes its nice to have functionality separate from any type of data. both approaches are useful, and have their place within a game/engine/other.

when you need to do bulk processing, in a typical SoA layout, you want to have your processing in a flatter, ADT style.

if your doing things on individual objects, in a typical AoS layout, its far easier to use C++ and leverage the inherent polymorphism.

- people are scared of templates.

no really, they are. they are like all things, a tool, use them where they make sense, dont use them to fix every problem.
and in 13 years of professional life, ive never, ever, seen template metaprogramming in use outside of boost or stl.


basically, right now, leverage every language feature you can. you can always rip it out and replace it later if its slow. make it work first, then profile, then fix what's slow.

what's *actually* slow might surprise you.

and it most likely *wont* be vtable lookups, unless your doing something very wrong.

your never as good as they say you were, never as bad as they say you was.
Quote:Original post by Matt_D
Quote:Original post by maspeir
With C++, typically this would be coded as a single header file, with the vertex class containing both the variables in the C data structure, along with the global variables and actual functions with code instead of prototypes. If there is a .cpp file, it would typically be empty or have empty functions.


your lack of data hiding is concerning.
If your directly accessing struct members, or global variables, your codebase will be exceptionally brittle, this is not good. use accessors, even in structs. use data hiding, use abstract data types.

why? well, next time you want to swap out the array, for a linked list in one of your exposed struct's your going to have to do a hell of a lot of work to fix up everywhere your poking the internals. use accessors, hide your internals. itll make you far more productive.

also, dont put all your code in the header file, as this will be included in every file which includes the header, increasing your compilation time. and possibly exe size. use inline

and if you really are going to stick with C, for gods sake, use and understand the restrict keyword


Again, I've never written an OOP app in my life. I HAVE looked at plenty of OOP code (mostly C++) and I do understand it. What I wrote was based on what I have seen in some of the C++-based game engines I've looked at. I'm not saying it is good programming practice, but it seems to be common. I abhor code in header files!

No, I am not a professional programmer. I'm just a hobbyist having fun...

Antheus
Quote:
Overall, designing a non-OOP system today would be foolish, as would exposing global state (inhibits unit testing, runaway coupling). Polymorphism, inheritance and encapsulation are a good thing if applied at proper level. Central idea is to undertand what needs to be a self-contained, self-aware object, and what can be left as plain data and plain code.


It seems that you don't have much experience in non-OOP programming.
I am implementing my servers in Erlang which is a functional language.

I don't have any global state (the language doesn't even support global variables).

I don't do any unit testing yet, but when it comes to that I will probably use eunit, which comes bundled with Erlang.

Polymorphism is actually more easy to accomplish than in most OO languages (in my experiences). Here is a simplified code example how polymorphism can be achieved:
start() ->    State = #obj{module=my_first_module},    Pid = spawn(loop, State),    Pid.% Here we loop for ever, we listen to two types of messages, one that tells us% to tranform to another module and one that calls the function in our current% module.  loop(#obj{module=Module} = State) ->    receive        {transform, NewModule} ->            NewState = State#obj{module=NewModule},            loop(NewState);        {call, Fun, Args} ->            Module:Fun(Args),            loop(State)    end.


Then from some other part in my code I can do something like this:
Pid = start(),% This sends the message that tells the process to change module. All future% calls will now be made in my_second_module instead of my_first_module.Pid ! {transform, my_second_module} 


Ineritance, I do this my keeping a list of modules which I iterate through to find the function to be called, when found I cache the result. This is not as easy as OOP inheritance but still quite trival to implement.

Encapsulation of state is very common in Erlang. You build your programs by letting a large number of processes iterating over their own state, as the loop example above. Processes can not change each other states directly but rather send a messages to each other, asking for changes/updates.

Inheritance and polymorphism is not exclusive to OOP and OOP design is not always the best way to structure a system, beliving so is quite ignorant.

[Edited by - flodihn on March 14, 2010 6:29:30 AM]
Quote:Original post by flodihn
Antheus
Quote:
Overall, designing a non-OOP system today would be foolish, as would exposing global state (inhibits unit testing, runaway coupling). Polymorphism, inheritance and encapsulation are a good thing if applied at proper level. Central idea is to undertand what needs to be a self-contained, self-aware object, and what can be left as plain data and plain code.


Inheritance and polymorphism is not exclusive to OOP, and OOP design is not always the best way to structure a system, beliving so is quite ignorant.


I have to agree with flodihn. Most of the issues you raised were addressed with structured programming years before object-oriented programming became popular. Many other programming paradigms (especially functional) actually have much more powerful concepts of polymorphism. The main driver for object-oriented programming was code reuse (based on my understanding of interviews with Alan Kay).

That being said, sticking to the discipline of structured programming and object-oriented principles usually results in stable, maintainable software. Just keep in mind that other paradigms can get you there as well.
[size="1"]Try GardenMind by Inspirado Games !
All feedback welcome.
[s]
[/s]

[size="1"]Twitter: [twitter]Owen_Inspirado[/twitter]
Facebook: Owen Wiggins

[size="1"]Google+: Owen Wiggins

I am familiar with modern style C++, but I don't like it tbh especially for games.

I prefer "C with Classes" and don't overuse most of the higher concepts. One level of inheritance is what I try to stick to for example. Maybe more if it's a straight line - a better description would be only one level of inheritance per class declaration :)

I've found it gives me the best of both worlds without any of the drawbacks, but I guess mileage is going to vary between different programmers and even the same programmer on different types of project.

I go way back and have worked on a quite a few larger games using just functional C and even assembler. I can say that in all honesty when I first read up on C++, it felt like a breath of fresh air and things definitely got easier to design. We'd done OOP concepts in functional C, but with little to enforce it, bugs were rife. If nothing else, formal classes impose enough rules to stop you making silly mistakes and to make you think a bit before doing a big one! :)
------------------------------Great Little War Game
Quote:Original post by flodihn
Antheus
Quote:
Overall, designing a non-OOP system today would be foolish, as would exposing global state (inhibits unit testing, runaway coupling). Polymorphism, inheritance and encapsulation are a good thing if applied at proper level. Central idea is to undertand what needs to be a self-contained, self-aware object, and what can be left as plain data and plain code.


It seems that you don't have much experience in non-OOP programming.


Thank you for this useful insight.

It's always nice to hear opinions from a college student reporting on hobby project experience.

Quote:I am implementing my servers in Erlang which is a functional language.

I suggest you read the posts I've made above carefully again. The advice I give was the following:
- Seeking a silver bullet, or sticking to one single methodology is not worth it
- Higher level design is usually easiest to do with OOP
- Lower level design should be non-OOP

To not make any claims on my own, I will quote the following:
Quote:The sequential subset of Erlang is a functional language
Quote:For concurrency it follows the Actor model


What surprise. Higher level design is OOP (actor model is the canonical definition of OO), and the lower levels are non-OOP.

Quote:Inheritance and polymorphism is not exclusive to OOP and OOP design is not always the best way to structure a system, beliving so is quite ignorant.


Let's try to steer away from words like 'ignorant'. Dunning-Kruger and all that.
Quote:Original post by Antheus
What surprise. Higher level design is OOP (actor model is the canonical definition of OO), and the lower levels are non-OOP.


I know I'm risking sounding pedantic, but actor model and object-oriented programming mean different things and arose to solve different problems. Actor model is a way of dealing with concurrency, whereas object-oriented programming is primarily focused on imperative programming and dealing with code reuse.
[size="1"]Try GardenMind by Inspirado Games !
All feedback welcome.
[s]
[/s]

[size="1"]Twitter: [twitter]Owen_Inspirado[/twitter]
Facebook: Owen Wiggins

[size="1"]Google+: Owen Wiggins

Here is a link where some is using Clojure and Functional Programming concepts to create a simple RPG.

http://briancarper.net/blog/making-an-rpg-in-clojure-part-one-of-many

Michael
I've written some simple games in C and quite frankly I ended up emulating OOP to the point C++ would simply have been a much better choice. Well written C ends up being fairly verbose after all; saving typing via use of global variables is a bad idea. Thus you'll get structs and functions operating on structs that's not being very different than C++ with more verbose syntax.

If you're trying to model something that can be naturally thought of to consist of objects, OOP is a very good approach. (How surprising!)

Now, on the other hand I gained very little from trying to write a compiler in Java compared to C (if we exclude things such as memory management). That's because writing a compiler is mainly about the algorithms used, and much less about the data structures, that are often quite trivial. Shoehorning algorithms into functor objects can be done but it doesn't add much from a syntactic perspective. Operator overloading is nice for stuff like vectors, but not necessary.

Overall C has a reasonable syntax for imperative algorithm implementation. The ugly stuff is mainly related to pointer manipulation.

And to summarize after this derailment: For games I see not reason not to use OOP fgiven how easily games can be modeled by classes. I make reservations for functional programming however that I'm not skilled enough to comment on.

This topic is closed to new replies.

Advertisement