melonjs / melonJS

a fresh, modern & lightweight HTML5 game engine
https://melonjs.org
MIT License
5.85k stars 643 forks source link

Add a Entity of Display Entity's Container #59

Closed cubemoon closed 11 years ago

cubemoon commented 12 years ago

Similar to the "DisplayObjectContainer" class in ActionScript3.

melonjs commented 12 years ago

Hi,

Actually we have something that could look similar in melonJS, as all objects are actually currently regrouped in TMXOBjectGroup (that’s actually the view you have on Tiled). For now they just contains the Tiled object definition (and not object instance), but with some enhancement and a few more api, it could be turned into something more usable.

What would be the usage on your side for it ?

cubemoon commented 12 years ago

my game for a one-time moving all entity in the same manner. I think if provides a container to do it. will be very easy and simple.

melonjs commented 11 years ago

@parasyte first thank you very much for merging back the main branch into the ticket #15 branch ! i definitely need to spend some time playing with git...

then, and before going further, I was thinking that what I did for ticket #15 could be extended to cover this one, and eventually the entity composition you did in neverwell moor,

it's just a raw idea in my mind, but after all, sprite object could be seen a child of an entity container too, assuming that sprite object would also implement the interface (update/draw ?) required by child object.

what do you think ?

parasyte commented 11 years ago

@obiot I'm thinking about this one like a generic class that resembles ObjectEntity and includes the same API as the game manager; add(), remove(), sort(), removeAll(), countObjects(), update(), and draw() (I think that's everything). Call the class GroupObject.

All the game manager stuff in 'me.game' gets replaced with stub functions that call the main GroupObjects API. Other GroupObjects can be added to it, etc. update() and draw() functions iterate all child objects, calling the requested function.

GroupObject also acts like ObjectEntity/SpriteObject because it supports scaling (draw() scales the context before iterating child objects) and child pos vectors are relative to it (draw() translates the context before iterating child objects).

Another thing it needs is a method that returns an array of rectangles for all children in the object tree. This is for the dirtyRects feature.

So it's kind of like the sprite composition in Neverwell Moor, but with a more familiar API and more powerful features. :)

A GroupObject tree might look like this:

+ main (GroupObject)
|
+--- bg (TMXLayer)
|
+--- player (GroupObject)
|    |
|    +--- body (ObjectEntity)
|    |    |
|    |    +--- body sprite (SpriteObject)
|    |
|    +--- clothes (SpriteObject)
|    |
|    +--- hair (SpriteObject)
|    |
|    +--- weapon (SpriteObject)
|
+--- coin (ObjectEntity)
|    |
|    +--- coin sprite (SpriteObject)
|
+--- power up (ObjectEntity)
|    |
|    +--- power up sprite (SpriteObject)
|
+--- HUD (GroupObject)
     |
     +--- score (GUIObject)
     |
     +--- health (GUIObject)
     |
     +--- pause button (GUIObject)

Interested to hear what you have in mind for SpriteObject. It sounds like it really could become an array of SpriteObjects! Is your vision similar?

parasyte commented 11 years ago

Oops! Mobile failed me (no backtick character on the virtual keyboard) I've updated my comment above with proper formatting!

melonjs commented 11 years ago

I exactly had the same idea with the sprite. the point is that when adding a child object to the entity, it could either be another entity, or a sprite, or anything else.

However we could also establish that a base container can only contains entities, and that for each entity can support one "renderable" (I call it "renderable" here as it could be either a SpriteObject or something else). the advantage here is that we have one API/Interface to manage (object entity) rather than two (Object Entity and Sprite Object), and then sprite object can evolve freely (as far as it support a update/draw function) and other format be added.

one thing we need as well is to change the constructor parameters for SpriteObject, AnimationObject and ObjectEntity. Today they receive two parameters (x,y) for their intitial position, and it would be better to change this to a vector2d object. the advantage being that in that case a base container can pass a reference to it's own initial position (it simplify the way to manage child position when drawing them).

melonjs commented 11 years ago

@parasyte

additionally I would like to hide (private) the current property holding the sprite or animation object. I was then thinking about function name like the following one setRenderable(Object); getRenderable()

any better idea about the name ? setDisplayObject ?

melonjs commented 11 years ago

@parasyte

ok more thoughts about this during the night and after reading some article on the internet :

I would propose creating a renderable base object, from which (SpriteObject) for example would derivate :

me.Renderable = me.Rect.extend({

    isRenderable : true,

    /**
     * update function
     * @return true if the object has been updated
     **/
    update : function() {
        return false;
    },

    /**
     * object draw
     * @param {Context2d} context 2d Context on which draw our object
     **/
    draw : function(context) {
        // do something
    }

});

ObjectEntity would obviously not extend this class, but could eventually contains a child implementing this class, that could then be added using a addChild type function, like in the above proposiion.

In the update() function, an Object Entity would then call the update function of each of ALL his child components/entities

In the draw() function, an Object Entity would then call the draw function of each of ALL his child implementing the Renderable class (i added a isRenderable boolean here, but typeof could be used too)

parasyte commented 11 years ago

In your last statement, I guess you mean instanceof() ? I don't know if that works with the inheritance pattern, or how it performs compared to checking a Boolean. :)

I like the idea though; it provides separation between objects that just want to be updated and objects that want to be updated and draw. I would be happy with any API that allows creating object hierarchies. Because it would enable us to create modal stacks and similar concepts.

It sounds like your proposal turns the ObjectEntity class into the container. That's cool, too. It already implements scaling and alpha, etc. so it's a good place to start. Also I can't think of any limitations that it might impose on object hierarchies; and ObjectEntity can contain an ObjectEntity child, so it just works. I like it!

melonjs commented 11 years ago

@parasyte
yep sorry, I meant instanceof() :)

About turning ObjectEntity into a container, I don't know to be honest, but we could also have a speciifc "renderable" property that would allow to link a single sprite object or whatever to an ObjectEntity, and then have a specific container class as you were proposing before.

I'm not sure I really see the big advantage of one or the orher so far...

melonjs commented 11 years ago

@parasyte So what do we decide we want to do? :)

I have my today afternoon free on my side, but i'm not really sure which approach is the best :)

melonjs commented 11 years ago

@parasyte

So yesterday I finally worked on turning the entity object into some simple containers, but I finally toss it out this morning, as for simple stuff (basic entity with 1 basic sprite) it requires a lots of complexity (1 loop in the entity update function, and 1 more in the draw function where this time we add a test on renderable object) which won't help with perfomances.

Furthermore an object container would be more inline with the TMXObjectGroup "philosophy" and could be reused as you were explaining in the actual main loop (as after all it loop throu a first level of object)

I kept however the renderable base class idea, as I think it could be used some place else in e engine when refactoring other objects.

I also kind of solved the child sprite object position, by assuming that its position is relative to the object ancestor (which means pos 0,0 by default for the child object), so I juste translated the display to the ancestor position, draw the child, and translate back to previous point. Same can be used for the container.

parasyte commented 11 years ago

I expect its just a matter of consolidating the features we expect and go with whatever interface makes most sense for implementing those features. Here are the "must have" features for this ticket, IMHO:

Hmmm I think that's it? Given these requirements, I thought it would be best to move the game manager logic to this new "branching" class (aka "container" or "group") and replace the original game manager with an instance of a single default/main container. Then the code become reusable, and some interesting things could be done like modal dialogs; a dialog and its child objects live inside a container somewhere in the tree, and the game manager just switches to the context of that object, pausing every object outside of the modal dialog's branch. This can be used to implement multiple levels of modality. A "modal stack" IOW.

I don't know if ObjectEntity is the best object to turn into a container like this? But for sure ObjectEntity needs to contain multiple child sprite objects. Can it be extended and generalized to handle other tasks, and is this a good idea?

melonjs commented 11 years ago

Fully in line with the above requirements.

about the last question, my concern is mostly about the code overhead in the ObjectEntity, and the performance impact for an ObjectEntity containing only one sprite, that's mostly the reason why I reverted my changes to what I submitted yesterday,

About object compisition you implemented in neverwell moor, how do you see it translated in melonJS :as an entity container, or as an object entity containing several sprite objects ?

parasyte commented 11 years ago

My composition object resembles ObjectEntity containing multiple SpriteObjects. I like the flexibility that can be afforded by a generic container. The problem is I don't know how many people would make use of it. Still surprised to see how many use cases come up on the forum that would benefit from it, though. I don't see it adding any performance overhead.

Also doing things like the Quintus Breakouts block group; they scale in together when a stage begins, yet they have individual collision detection and such things.

Speaking of performance, an ObjectEntity with only one sprite can use a different function for update() and draw() select an appropriate private function when adding and removing child SpriteObjects. :)

cubemoon commented 11 years ago

composition object is very necessary.I think ObjectEntity is best used to expand the object,

obiot commented 11 years ago

@parasyte 0.9.8 as well ? i won't myself do anything on this one on 0.9.7 (at least I think)

obiot commented 11 years ago

@parasyte hi, for the 0.9.9 I wanted to look at this one, with the idea to actually replace the current gameObject under me.game by an actual DisplayEntityContainer Object (which then contains Entities or other DisplayEntityContainer).

the point being that in the process I'm maybe planning to actually remove the dirtyRect stuff from melonJS. I'm not sure that this is really helpuf today, as only working with fixed level anyway (which is 1% of the existing games).

what do you think ?

obiot commented 11 years ago

@parasyte

Hi, that would be great if you could have a look at what I did, as I think it's pretty cool and give something much more clean in terms of code and much more evolutive for later. As I was writing it previously I also completely removed the dirtyRegion code, as anyway it was not working properly (and only useful with fixed level, without performance benefit for scrolling level), this can be however added later anyway, in a more simple way (it was anyway better to restart from scratch on this one).

so what i did :

and I think that's it for now, still some stuff that need to be cleaned up under me.game, but for now all examples are running just fine, and i'm not seeing any major regressions :)

aaschmitz commented 11 years ago

Ticket #211 redesign object entity and provide a more lightweight default one

parasyte commented 11 years ago

@obiot I commented the heck out of the patch! I haven't yet tried the code, so keep that in mind.

Glad to see the dirty region stuff removed. It was very hard to maintain. See #101! That ticket can completely rewrite the "intelligent" rectangle merging code now, starting from a clean slate. Still very tricky, but much better, IMHO.

I agree, the recursive update + draw looks good! It's exactly what I always wanted. :smile:

The auto-sorting is a good idea in the general case, but could make efficient particle emitters difficult to create. One option just off the top of my head is that things like particle emitters (dynamically adding and removing many object all the time) could be added/removed on its own EntityContainer which has auto-sorting disabled. Then the add just becomes this.children.append(obj); and the remove is this.children.splice(this.children.indexOf(obj), 1); That requires a new boolean property on EntityContainer to enable auto-sorting, and would also be good to include the old sort() method for "manual" sorting:

sort : function (sortFunc) {
    if (this.autoSort !== true) {
        // ...
    }
    else {
        throw "melonJS : Do not call sort() with autoSort enabled";
    }
}

Also there are some TODOs left, but I assume you're aware of those! :wink:

parasyte commented 11 years ago

I just noticed the EntityContainer.[pos|width|height] properties are initialized but not used for anything. This would be the perfect opportunity to use these in EntityContainer.draw() to translate to pos.x and pos.y, and update the rect passed to child.draw()!

The reasons for this are all listed above in earlier posts; first described by @cubemoon. Imagine a game of Space Invaders, where rows and columns of multiple entities all move in unison. I would implement such a game by placing all of the Invaders into an EntityContainer, and move them around by adjusting the position vector on the EntityContainer. That also lends itself to scaling and rotating the entire group, too (also mentioned before, citing the Quintus Breakouts port).

That makes the child draw positions relative to their parent container, by default. I like it!! :D

obiot commented 11 years ago

@parasyte yep, this is what i had in mind as well, like we did for the renderable property for entities.

thank you very much for all the comments and feedback, i started this thing on Friday, believing it would take me a few hours, but at the end it took me two days to reach this first result (but yeah not full time of course) ! As in the process i asked myself a tons of questions, by realising more of the thing that could be done with it, or by redoing the same thing several times.

very good progress here, i'm very happy !

obiot commented 11 years ago

@parasyte I was looking at the possibility to position object respecting to their parent (ancestor) container, and had a couple of question :

in order to follow something more or less standard, i wanted to name it after the corresponding CSS property (position) with the following possible values :

however in case of relative positioning, but also scaling or rotating, what points shall we use ? top/left coordinates, or based on both items anchor points, or different following the usage (pos, scale, etc..) ?

(that makes me think that we could have used the same naming "convention" instead of the floating property, I see that CSS also defined "static" or "fixed" that probably also correspond to the same behavior).

obiot commented 11 years ago

@parasyte

one more question : how do you think we should manage collision with these entity container objects ?

parasyte commented 11 years ago

@obiot I would be concerned that multiple levels of EntityContainers with alternating "relative"/"absolute" positioning would be very difficult to support. We should say instead that a child's position is always relative to its container. Much simpler for us to build, and simpler for game developers to use.

As for scaling and rotation, me.EntityContainer already extends me.Renderable, which handles the scaling and rotation using a configurable anchor point. I don't see any more additional work needed here. ;)

Collisions should definitely be managed with the collision layering and masking as specified in #103.

obiot commented 11 years ago

@parasyte

Among other things, Sort functions done !

Basically : • I removed all the childIndex stuff as I realized it was not really useful • me.game now also defines a global propertyToSortOn (“x”, “z”, “y”) • Upon EntityContainer creation, the local propertyToSortOn is set to the me.game current one (but can be then overwrite) • The engine now auto-sort objects, unless the container autoSort property is set to false ( I don’t however throw an error if the sort function is called with autoSort on, It just do nothing) • The engine now implement all 3 possible sorting algorithm, so no need to have that sort_func function for sorting on something else than z.

TODO :

As always your wise code review is always appreciated ! :P

that's all for today !

obiot commented 11 years ago

Errrr... came across this article tonight : http://gamealchemist.wordpress.com/2013/05/01/lets-get-those-javascript-arrays-to-work-fast/

Most interesting point is the "Do not loop backward within an array." that apparently creates some cache miss... Will play a bit with jsperf tomorrow :)

obiot commented 11 years ago

@parasyte

I'm starting to feel quite happy about this one, and based on my testing results, it's getting close to ~0 regressions :)

Couple of questions though, as i'm having mix feelings on the following ones :

parasyte commented 11 years ago

@obiot wow! a lot more commits for me to go through. :smile:

I don't have any opinion on grouping TMX layers or objects instantiated from TMX. Although I could absolutely see the organization that it provides as a useful tool.

For the off-screen canvas, that has always been a thorn when working with the HUD object. Possibly because it doesn't have the right API to allow proper updates. I think maybe some benchmark is in order before spending time working on it. If it's a performance win, then yes! If no performance gain, then also keep in mind it will take up valuable texture space on mobile ...

obiot commented 11 years ago

Don't worry about all the commit, you'll give it a real life test during your next 1-game-a-month game :):):)

Else for the offscree canvas you are probably right, and one of the major complain on the HUD was the lack of scaling/rotation feature and so ovet, so that might not be a good idea to add this back here !

obiot commented 11 years ago

@parasyte with this last commit, the engine will (or not) create EntityContainer object correspondingly to the group objects created in Tiled.

this is configurable through the me.game.mergeGroup setting :

for the moment I set the option to true, as this is what is matching the current (or previous I shall say) behaviour, but i'm unsure about my decision, do you have recommendations ?

when false, it adds more complexity (as you have to go through the child objects,i.e. when checking for collision), and objects are bound to their container in terms of z order.

when true, we are loosing all the benefits of the entityContainer, but might fit most of the needs ?

so, small dilemma :)

obiot commented 11 years ago

this might be my last commit on this ticket as well, i think it's done now :)

parasyte commented 11 years ago

@obiot Probably good to leave at true for now. We can remove this limitation when #228 and #103 land. :smile:

obiot commented 11 years ago

probably a good idea :)

obiot commented 11 years ago

@parasyte

I’m scratching my head on something here : what do we do with the floating property when defined on an entity container and/or on the child objects :

https://github.com/melonjs/melonJS/blob/master/src/entity/entitycontainer.js#L512

For entityContainer I believe it’s kind of obvious, when floating is true we translate the container to match with screen coordinates, child object are then draw using the default (screen) position of the container.

If floating is not defined for the container, but defined for a child I guess we ignore the initial container position and then translate the child object coordinates to the screen coordinates ?

If floating is defined for both, what do we do ? do we ignore the child floating property?

obiot commented 11 years ago

my suggestion would be to do something like :

if ( (this.floating== true) || (obj.floating==true)) {
    context.save();
    // translate back object
    context.translate(me.game.viewport.screenX -this.pos.x, me.game.viewport.screenY -this.pos.y);
}
parasyte commented 11 years ago

Well ... every entity with floating is just going to switch to screen coordinates. Every child should be positioned relative to its parent EntityContainer by default. Don't force child entities to be relative to the screen just because its parent EntityContainer is.

Imagine a HUD object implemented as a floating EntityContainer, and I place it at the bottom of the screen. Any child objects I add to this HUD will by default be positioned relative to it, so e.g. they will always be drawn within its frame. However, if I set floating = true on any of those children objects, their position switches to being relative to the viewport instead. And that will change where it gets drawn, for sure!

DonaldBaird commented 11 years ago

Hi, so I've been playing around with the entity container object to be able to have an entity that is capable of drawing multiple renderables as segments to make up a player object. Basically legs, torso, and a head. I was told that the entitycontainer object could be used for player movement and collision, but a lot of the physics functionality of ObjectEntity aren't implimented. Such as setVelocity, setFriction, etc. Are these planned to be implemented into the entitycontainer?

obiot commented 11 years ago

If you want do use this to play with sprite composition, you should rather use it as a renderable components for your entity, and not as an EntityObject. me.ObjectContainer base class is actually me.Renderable, and this object is purely meant to be a container and won’t manage advanced physics.

However it can be included in collision check (though I did not try it yet, will also trigger the onCollision function) and also define the collidable property in order to disable collision per group.

Furthermore (also untested) it should be working in a scenario where the objectEntity detect collision using its own rectangle, and then call it’s renderable (the object container here) collide function, and see which part of the body has been touched.

obiot commented 11 years ago

@parasyte i just realized that the name i choose (me.EntityContainer) for this is not really appropriate, since this container primary contains renderable object and then possibly entities,

so shall we rename this to me.ObjectContainer or set it back based on the original class it's based on me.DisplayObjectContainer ?

obiot commented 11 years ago

I'm closing this one now, as I really don't see anything else to be done for now !:)