OK, I think I’m beginning to get it. It’s true that in class inheritance systems I’ve written, I do find myself writing little notes in the comments, “class X overrides function A, but classes Y and Z inherit it”, etc. I guess inheritance allows you to be more lazy, which can come back to haunt you.
I found these two articles quite useful for comparing component systems to inheritance. Although their examples are in C++ and C# respectively (the second one describes Unity’s component system), their discussion of the principles was very helpful for me in understanding the issues raised in this thread.
Quick review of Game Programming Patterns by Robert Nystrom
One more reading link. This one is excellent. It’s an entire book about software architecture (it’s called game programming patterns, but everything here is applicable to other coding projects). It’s free to read online, but I think I’m going to purchase it because it’s very good. This link is to the chapter on components, but the entire section on decoupling is relevant to this discussion, as are many of the other chapters (in fact, the entire book could be said to be about various strategies for decoupling, one way or another, ie the early chapters on the observer pattern and the command pattern). The examples are in C++ (I think?) which I don’t read, but the discussion still makes sense for me. It’s very engagingly written, and as a plus, the pages themselves are beautifully laid out (I really like the way footnotes are to the side of the main text body).
@yojimbo2000 wow small world, I already have that book! I agree, it’s a great book, but make sure you know at least a little C++ or C before going into it so you are more likely to be able to use the examples in your own projects.
@TheSolderKing@Jmv38 One of the things I find valuable about the book is that he makes an argument that coders should tightening up our terminology slightly, so that we can describe our coding patterns to others more accurately without causing confusion. For instance, if you compare the chapter on Events to the one on Observers, he argues that “Event system”, “event manager” etc should be reserved for systems that cause an action to be held in a queue and triggered at a later point in time (ie decoupling over time). So what I’ve been calling my “event manager” so far, is not, according to Nystrom, an event manager. Because it triggers actions instantly, it’s actually an Observer pattern (if we follow the nomenclature that Nystrom argues for).
Is there a way to wrap every function we write in our classes with that thing in LUA that let’s you pause execution of functions? I know you can’t do multi threading in codea, but this would be a means to say “execute 200 lines, then run whatever is in the event queue”. Does that make sense? There’s gotta be a way for codea to read a project tab, if you can save Project info to tab. Maybe write something in the first tab which should contain Main () and setup () and have it read every other tab and wrap each function in the other tabs with that thing that can pause instruction execution.
That would be possible. But it may clutter the memory is you have too many threads.
You would have to add in every function a call to a function that yields if the condition object.n>200 is true. Better to use os.clock>object.t0+0.001 or so.
About reading the Game Patterns book:
I have a problem with the state pattern: making a new class for each state of an object seems so… heavy? can anyone explain me what i am missing here? http://gameprogrammingpatterns.com/state.html
One of the things I like about the book is that he isn’t particularly evangelical; he presents a range of different methods and implementations and is quite upfront about the shortcomings of different scenarios. So a full class-based FSM or hierarchical or stack based machine might be overkill for certain objects in the game (I’ve used this for the state of the entire game, though, game, game_over, splash_screen etc).
I suppose I would rather have lots of smaller classes than a few giant ones. For one project I’m working on a component system (based on his chapter on components) where each component in the game object is a class. To balance things out though in the complexity stakes, all objects in the game belong to a single class. So you have very simple, almost empty objects. I guess its method-oriented rather than object-oriented programming (I’ll post it at some point).
For AI behaviour, which I guess I’ve implemented as a kind of FSM, I use closures. Lighter and simpler than a full blown class, but with many of the benefits (remembering their internal variables, modularity etc). He also suggests, if you don’t need to remember variables, using virtual methods. This is something I haven’t really explored that much yet, but I guess you would implement it something like this: (EDIT added a second method and a state pointer to switch between 2 methods upon touch)
--# Main
-- Game object finite state manager using virtual methods
--touch screen to switch states
function setup()
anInstance = MyClass{pos=vec2(WIDTH,HEIGHT)*0.5} --i didn't know til recently you could do this. () not needed if argument is just a table or a string.
end
function draw()
background(0)
anInstance:update()
end
function touched(touch)
if touch.state == BEGAN then anInstance:setMoveState() end
end
virtual = {} --just a table, doesn't need to be a class
function virtual:move(t) --use the colon : syntax when defining method, so it can access self
self.pos = self.pos + t.vel
end
function virtual:wave(t)
local y = math.sin(self.pos.x/20)
local x = -t.vel.x
self.pos = self.pos + vec2(x,y)
end
--# MyClass
MyClass = class()
local states = {virtual.move, virtual.wave}
function MyClass:init(t)
self.pos = t.pos
self.state = 0
self:setMoveState()
end
function MyClass:update()
-- virtual.move(self, {vel = vec2(1,0)}) --nb have to use dot method with self as first argument when calling method
-- self.move(self, {vel = vec2(1,0)}) --this works too
self:move{vel = vec2(1,0)} --this is easiest
ellipse(self.pos.x,self.pos.y, 30)
end
function MyClass:setMoveState()
self.state = self.state + 1
if self.state > #states then self.state = 1 end
self.move = states[self.state] --is this an fsm pointer? a shallow copy? a mix-in?
end