September 7, 2017 at 7:41 am #10297
I recently started to build my own Component system “on top” of Mojo3D, so that I could have objects with components, a design that I really like, where each component can have events like “OnUpdate”, “OnDraw( canvas )”, but also more gameplay specific things like “OnCollision” or even “OnPause” and “OnInteract”, etc.
Since I didn’t want to modify Mojo3D, I wrapped its Entity class in my own class, GameObj, that contained the components , and had “OnStuff” events of its own as well. So far so good. I could create my own objects, add any kind of Mojo3D entities (Model, Sprite, Camera, etc.) to each one, add components that did stuff, and life was good for a while.
Then as I moved forward, I noticed I was having to wrap a lot of things, like having my own scenes on top of Mojo3D scenes, my own parent/child hierarchy that in turn managed Mojo3D’s, etc. Things started to get messy and annoying to deal with.
Not being a real programmer, I wanted to ask the real ones their thoughts on this problem. Should I:
- Stop whining, deal with it and wrap everything like a champ!
- Fork Monkey2 and create a new branch just to modify Mojo3D (adding things like Component Stacks, some Virtual Methods, etc)? Seems like this would work fine, until I want to share this with other people, then I’d have to ask them to install this modified version of Mojo3D, which sounds messy to me. I like having as little dependencies as possible. Or…
- Beg Mark please please pretty please to implement some sort of basic, but extendable, component system in Mojo3D’s entities? Seems like he’s got a lot to do on his hands already.
Cheers!September 7, 2017 at 8:28 am #10303
I also like component-based system and even started to do similar ‘framework’ for 2d, almost unity3d-clone by concept / structure / naming.
But I left / freeze this project.
The most difficult thing for me was coordinate processing – each GameObject can have it’s own scale and rotation and children hierarchy. And we should to calculate local and global values, matrix matrix matrix. 🙂
I can help to concrete problems. Looks like if you complete your wrapper – you can easily add new features. And you can start with minimal functional and extend it as needed.September 7, 2017 at 8:33 am #10304
I have no desire to turn mojo3d into a component system, it’s very lightweight and I like it like that.
I’m not even 100% what you’re try to achieve here anyway. Do you want to make Entity final and implement cameras, lights, models, etc via components instead of subclassing? The way Unity does it, even entity transform is not part of entity – you want to do this too?September 7, 2017 at 8:58 am #10305
it’s very lightweight and I like it like that
Me too! I love how fast it is, which is why I didn’t want to modify anything, and want to add my gameplay stuff “on top”.
My primary focus is mostly on adding gameplay via components, so the unity “everything is a component” is not necessary (although I have to admit I like it a lot). Then you can basically create game logic by loading .json files that specify new entities, then their components (i.e. Inventory, Stats, etc.), then just the component values that differ from the default.
I also like making the components work via “OnStuff” events, it’s really easy to wrap your head around code written like that for me.
Currently I have a GameObj class, and since it can contain any Mojo3D entity, I can turn a GameObj into anything. But I have to have components for all that, like a “LoadModel” or “LoadSprite” component that only kicks in at the game start, loads the Mojo3D entity into the “_entity” field, then does nothing else, while the gameplay components generally work on every frame. It works, but I was wondering if I’m going down a road full of unnecessary pain, that’s all.
Prior to Mojo3D I actually had a pretty neat, functioning 2D entity/component system that I started way back with Monkey-X, but it was taking me a long time dealing with all the transformations (with hierarchies), collisions, any math stuff, etc. So I’d rather not deal with that!September 7, 2017 at 9:05 am #10306
Currently I have a GameObj class, and since it can contain any Mojo3D entity, I can turn a GameObj into anything. But I have to have components for all that, like a “LoadModel” or “LoadSprite” component that only kicks in at the game start.
I have no idea what you’re talking about here sorry! What exactly would you like done to mojo3d?
I’m certainly into exploring ideas here, and I think there may be some overlap with several problems I’m yet to solve, but I just want to make sure we’re on the same page. From the above, it sounds more to me like you’re talking about a ‘scripting’ system what with all the loading going on!
Perhaps post some hypothetical sample code, ‘making up’ any methods you’d like to see added?September 7, 2017 at 9:14 am #10307
Also, I have to ask: would it be a good idea for Class Extensions to allow adding fields and virtual methods? Because that would essentially solve my problem… I’d be able to extend “Entity” by adding a Component Stack, some “OnStuff” virtual methods, and not that much else, and all classes that inherit Entity would inherit those changes! The rest is done by the SceneView class, which knows how to handle use extra fields and methods (by calling “OnUpdate” every frame, for instance).
But I’m pretty sure there’s some Computer Science reason why it shouldn’t be done like that, isn’t there?September 7, 2017 at 9:28 am #10308
would it be a good idea for Class Extensions to allow adding fields and virtual methods?
As for me – GameObject is a core object which don’t extends any base class. It contains components and allow us some manipulating by setting layer and enabled properties, as well as providing OnStuff callbacks. In my framework I add OnStuff callbacks to MonkeyBehaviour class to left gameObject as simple as possible.
Then you create any components like1Class Camara Extends Component
Mesh, Sprite, etc (not LoadMesh, LoadSprite)
And then add it into game object. To simplify process you can write helper functions like123456Function CreateCamera:GameObject( name:String )Local go:=New GameObject( name )go.AddComponent<Camera>()Return goEnd
In my framework I extends Behaviour class for scriptable, that contains ‘enabled’ property.
(need to add repository to rely on code)September 7, 2017 at 9:33 am #10310
Inside of your components you can store ‘raw’ monkey2 entities / parts. And manage them under the hood. 🙂September 7, 2017 at 9:47 am #10312
Here’s my work in progress… like I said, this is working, but it’s getting messy. I wanted tips on how to better design something like this: https://github.com/DoctorWhoof/game3d
This is the base GameObj class that holds the components and the Mojo3D entity. You can see a bunch of virtual methods near the bottom. Most of those don’t do anything yet, but they worked well in my old Monkey-X 2D entity system, and I’m still porting it to Monkey2 / 3D.
You can look at this example for a very simple test use (no gameplay yet, just some basic components):
The “Sprite Component” is not a good example of a component, as it simply creates a Mojo3D Sprite and associates it with the GameObj that can actually contain the components. However, GameObjects that don’t render to the 3D scene don’t have Mojo3D entities, and can simply use components that draw directly to the canvas, for example.September 7, 2017 at 10:45 am #10313
1234567891011121314151617181920212223242526272829303132333435363738Method AddComponent<T>:T() Where T Extends ComponentLocal comp:=ComponentBridge_Go.NewComponent<T>()Local name:=Typeof( comp ).NameLocal items:=_components[name]Local unique:=comp.isUniqueIf uniqueAssert( items=Null Or items.Length=1,"Allowed only one instance of '"+name+"' component to be attached!" )EndifIf items=Nullitems=New Stack<Component>_components.Add( name,items )Endifitems.Add( comp )ComponentBridge_Go.SetGameObject( comp,Self )ScriptExecutorBridge_Go.GrabComponent( comp )Return compEndMethod GetComponent<T>:T() Where T Extends ComponentLocal c:T=Null ' hack to get type of TLocal name:=Typeof( c ).NameLocal items:=_components[name]If items=Null Or items.Empty Return NullReturn Cast<T>( items )EndPrivateField _components:=New StringMap<Stack<Component>>
- GameObj name sounds not good for me, GameObject is better
- GameObj have a few public global stringmaps – not good, user can clear them in any program place
- For adding and getting components you are using Name property, I do like that in monkey-x, but mx2 allow us to do it more clean way – using reflection:
XXX_Bridge_YYY classes is a little magic to get acces to protected field, it allow me set protected fields and provide read-only public properties to end user (I’ll write a post about that).
My rule is – hide under the hood as more logic as possible, stay public for really needed things.
Also in my framework I deny directly creation of components – constructor is protected. Because I need to set it protected field _gameObject in creation time. In your code user can set null to someComponent.gameObj field and get memory access violation.September 7, 2017 at 7:55 pm #10321
After thinking about this a little longer, I think I found a pretty good solution!
- Created an Entity extension that adds methods like AddComponent, GetComponent, etc.
- Created a “helper” class, ComponentBox, that holds the components and any useful extra functionality I want Entities to hold, like having an internal time offset.
- ComponentBox keeps track of which box is associated with which entity, and makes sure everything runs according to plan, i.e. If a component calls Entity.Move, internally it’s actually calling _box._entity.Move, so it can act as if it belongs to the Entity.
Here’s the (still work in progress) modified version, with examples:
And here’s a sample code from the repo:123456789101112131415161718192021222324252627282930313233343536373839404142434445464748Class Game3dView Extends SceneViewField wasdControls :BoolMethod New( width:Int, height:Int, enable3D:Bool )Super.New( width, height, enable3D )EndMethod OnStart() OverrideScene.ClearColor = New Color( 0.1, 0.1, 0.1 )'traditional mojo3d model creationLocal test1 := Model.CreateTorus( 2, .5, 48, 24, New PbrMaterial( Color.Red, 0.1, 0.5 ) )test1.AddComponent( New Spin(0,1,0) )'component based model creation - the component only "runs" at the startLocal test2 := New Modeltest2.Parent = test1test2.Position = New Vec3f( 4, 0, 0 )test2.AddComponent( New Spin(3,0,0) )test2.AddComponent( New DonutRenderer( 1, 0.25 ) )'I may move a lot of the AnimSprite functionality into the SpriteRenderer component, and use regular Sprites insteadLocal test3 := New AnimSprite( "asset::blob.png", 16, 16, 0, 0, Null )test3.LoadAnimations( "asset::blob.json" )test3.Animation = "WalkRight"test3.Position = New Vec3f( -4, 0, 0 )test3.Scale = New Vec3f( 2, 2, 2 )test3.Parent = test1test3.AddComponent( New SpriteRenderer ) 'currently does nothing but calling "Update(time)" on AnimSprite on every frame'Another model creation componentLocal test4 := New Modeltest4.Name = "CatCard"test4.Position = New Vec3f( 4, 0, 0 )test4.Parent = test1test4.AddComponent( New Card( "asset::cats.png", 12, 2, 2, 16, 16, TextureFlags.None ) )WasdInit( Self )Local pivot := New EntityCamera.Parent = pivotEndMethod OnUpdate() OverrideIf wasdControls Then WasdCameraControl( Camera, Self, Clock.Delta() )EndEnd
but mx2 allow us to do it more clean way – using reflection
That’s cool, thanks! I always wondered if I could do GetComponent<Class> with reflection, just like in Unity. Would it add overhead compared with getting it from a String Map? It can run on every frame on several hundreds of objects, so I think it’s important to pick the fastest way.September 8, 2017 at 1:49 am #10328
Would it add overhead compared with getting it from a String Map?
AFAIK name of type is a minimal reflection part that directly return precompiled string.
What is inside of Typeof I don’t know.
It can run on every frame on several hundreds of objects, so I think it’s important to pick the fastest way.
Using GetComponent () inside of loops is a bad idea. You can store (cache) needed components in a private filds of objects which should use them.
I dislike idea with external box container. 🙂September 8, 2017 at 4:26 am #10331
I dislike idea with external box container.
I totally get what you’re saying, but it solved the problem of creating duplicate structures just to hold the GameObjects, since they were the “core” objects, instead of the Mojo3D entities. I had to create their own Scene class, their own Parenting system, etc. and keep those and Mojo3D’s systems in sync. Kind of a pain.
I think the new system works really well for the end user, since they never have to even know about the EntityBox class (I renamed it from ComponentBox, made more sense to me). This is what I was looking for, keeping Mojo3D simple but with the added functionality of components.
Cheers!September 8, 2017 at 4:44 am #10332
Ok, try it in real project to know is it good enough.
I think the new system works really well for the end user, since they never have to even know about the EntityBox class
This time it’s FALSE:
- you add public method GetComponentBox:EntityBox() to Entity
- all your virtual OnStuff methods are inside of EntityBox – maybe you didn’t done with that part yet, I’m interested in how you will override them?
You must be logged in to reply to this topic.