proton_entity
Differences
This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
| proton_entity [2010/10/25 04:22] – created seth | proton_entity [2012/02/07 09:38] (current) – aki | ||
|---|---|---|---|
| Line 9: | Line 9: | ||
| I've made a lot of engines and a lot of game frameworks and for some reason I'm constantly trying new methods, mostly because it keeps things fun and interesting. | I've made a lot of engines and a lot of game frameworks and for some reason I'm constantly trying new methods, mostly because it keeps things fun and interesting. | ||
| - | Here past methods I've tried when creating games: | + | Here are some past methods I've tried when creating games: |
| - | * Not knowing what I'm doing and hacking everything (see, Dink source) **VERDICT: | + | * Not knowing what I'm doing and hacking everything (see, Dink source) |
| - | * Create a giant entity class hierarchy where each kind of thing gets its own class. | + | * **VERDICT: |
| - | * Create one SUPER HUGE Entity class that does everything, possibly having it initialize sub objects. It ends up with 200 functions and bloated beyond belief. (See Novashell' | + | * Create a giant entity class hierarchy where each kind of thing gets its own class. |
| + | * **VERDICT: | ||
| + | * Create one SUPER HUGE Entity class that does everything, possibly having it initialize sub objects. It ends up with 200 functions and bloated beyond belief. (See Novashell' | ||
| + | * **VERDICT: | ||
| Using all of these, this is how it went: | Using all of these, this is how it went: | ||
| - | (Ladies, look at your game development cycle) | + | (Ladies, look at your game programming |
| {{: | {{: | ||
| Line 29: | Line 32: | ||
| ==== The Proton Entity System ==== | ==== The Proton Entity System ==== | ||
| - | Aka, my version of "my interpretation of how Unity internals sort of work after I used it for a few hours" | + | Aka, "my interpretation of how Unity internals sort of work after I used it for a few hours" |
| If you take a look at Entity.h you'll see some functions but nothing about thinking/ | If you take a look at Entity.h you'll see some functions but nothing about thinking/ | ||
| - | Well, not much, alone. | + | Well, not much, alone. |
| - | ** Instantiate an entity ** | + | The power comes from Components. |
| + | |||
| + | They way they accomplish this is by connecting a component function (say, Render) to a function object called " | ||
| + | |||
| + | There is no looping through the entities to do things, Entities don't even need to be in the same hierarchy or in a list to work. But it's a good idea as the hierarchy makes rendering/ | ||
| + | |||
| + | ====Test drive===== | ||
| + | |||
| + | Ok, time to just cannon-ball into the pool and show you some stuff. | ||
| + | |||
| + | If you see a weird function, like, BobEntity(), | ||
| + | |||
| + | There are a TON of tiny helper functions in **EntityUtils.cpp**, check the h sometime. | ||
| + | |||
| + | If you'd like to work along with me, open the RTSimpleApp project now! | ||
| - | Let's do some real code to give you an idea of how to use these things, feel free to type along. (you could write it in App:: | ||
| <code cpp> | <code cpp> | ||
| - | Entity ent; //instantiate | + | //Open App.cpp. |
| - | //Ok, now we've got one. | + | //MainMenuCreate(pGUIEnt); |
| - | ent.SetName("DeLorean"); | + | AddFocusIfNeeded(pGUIEnt); |
| + | Entity *pEnt = CreateOverlayEntity(pGUIEnt, | ||
| - | LogMsg(" | + | </ |
| - | //Shortcut: we could' | + | Instead of the menu as before, you should |
| - | //Yes, I like printf formatting, so sue me. I also like std::strings. | + | {{: |
| + | |||
| + | Well, we see the logo there. | ||
| + | |||
| + | Add a few more lines of code: | ||
| + | <code cpp> | ||
| + | // | ||
| + | AddFocusIfNeeded(pGUIEnt); | ||
| + | Entity *pEnt = CreateOverlayEntity(pGUIEnt, | ||
| + | // | ||
| + | pEnt-> | ||
| + | |||
| + | // | ||
| + | GetMessageManager()-> | ||
| + | //and, move back to 0,0 a second after that. | ||
| + | GetMessageManager()-> | ||
| </ | </ | ||
| - | ** Is it an entity or a database?! ** | + | |
| - | This "Delorean" | + | So now the ball sort of jerks around the screen. |
| + | |||
| + | Instead of jerkily moving, we can use an InterpolateComponent to smoothly change one variant value to another. | ||
| + | |||
| + | Instead of adding InterpolateComponent directly, we'll use some helper functions that apply it to pos2d, but just keep in mind we can do this for any variable. | ||
| + | |||
| + | Replace with this code: | ||
| <code cpp> | <code cpp> | ||
| - | ent.GetVar(" | + | // |
| - | ent.GetVar("trivia")->Set("Doc pronounced giggawatt wrong in the movie"); | + | AddFocusIfNeeded(pGUIEnt); //so it will render, update, and draw. Really it just adds a few components. |
| + | Entity *pEnt = CreateOverlayEntity(pGUIEnt, | ||
| - | //retrieve the data like this: | ||
| - | LogMsg(" | + | CL_Vec2f imageSize = pEnt->GetVar(" |
| + | |||
| + | FadeInEntity(pEnt, true, 1000); | ||
| + | |||
| + | //zoom to bottom right, take 1000 ms to get there, initiate it in 1000 ms. | ||
| + | ZoomToPositionEntityMulti(pEnt, | ||
| + | //zoom back to start | ||
| + | ZoomToPositionEntityMulti(pEnt, CL_Vec2f(0, | ||
| + | //zoom to bottom right again | ||
| + | ZoomToPositionEntityMulti(pEnt, | ||
| + | FadeOutAndKillEntity(pEnt, | ||
| </ | </ | ||
| - | Great. Notice that to retrieve | + | The logo now fades in, smoothly zooms around the screen a bit, then fades out. (and kills the entity completely) |
| + | |||
| + | Notice that most things work by time in milliseconds. | ||
| + | |||
| + | Let's add a graphic trail effect to our moving logo. | ||
| + | |||
| + | <code cpp> | ||
| + | // | ||
| + | AddFocusIfNeeded(pGUIEnt); | ||
| + | Entity *pEnt = CreateOverlayEntity(pGUIEnt, | ||
| + | |||
| + | //only this part is new, we're adding a component here | ||
| + | EntityComponent *pComp = pEnt-> | ||
| + | |||
| + | CL_Vec2f imageSize = pEnt-> | ||
| + | FadeInEntity(pEnt, | ||
| + | |||
| + | //zoom to bottom right, take 1000 ms to get there, initiate it in 1000 ms. | ||
| + | ZoomToPositionEntityMulti(pEnt, | ||
| + | //zoom back to start | ||
| + | ZoomToPositionEntityMulti(pEnt, | ||
| + | //zoom to bottom right again | ||
| + | ZoomToPositionEntityMulti(pEnt, | ||
| + | FadeOutAndKillEntity(pEnt, | ||
| + | </ | ||
| + | |||
| + | {{: | ||
| + | |||
| + | You see the same thing as before but with trail. | ||
| + | |||
| + | By simply adding the TrailRenderComponent we can add a " | ||
| + | |||
| + | In fact, this will even work with visuals not invented yet because the method TrailRenderComponent is using is just recording specific variables such as " | ||
| + | |||
| + | We accepted TrailRender' | ||
| + | |||
| + | First, take a look at TrailRenderComponent:: | ||
| + | |||
| + | We see: | ||
| + | <code cpp> | ||
| + | //shared with the rest of the entity | ||
| + | m_pPos2d = & | ||
| + | m_pSize2d = & | ||
| + | m_pScale2d = & | ||
| + | m_pColor = & | ||
| + | m_pAlignment = & | ||
| + | m_pColorMod = & | ||
| + | m_pAlpha = & | ||
| + | m_pRotation = & | ||
| + | m_pTrailAlpha = & | ||
| + | //register ourselves to render if the parent does | ||
| + | GetParent()-> | ||
| + | |||
| + | //our own variables/ | ||
| + | m_pFrames = & | ||
| + | m_pTimeBetweenFramesMS = & | ||
| + | </ | ||
| + | |||
| + | The first big chunk setting up pointers to variables that the parent has (or doesn' | ||
| + | |||
| + | Look near the bottom, it's not using the GetParent() prefix, it's using variables from its own VariantDB. | ||
| + | |||
| + | (It means every 50 ms it takes a " | ||
| + | |||
| + | So those are the two properties we can change! | ||
| + | |||
| + | Add this code somewhere after the TrailRenderComponent | ||
| + | |||
| + | <code cpp> | ||
| + | pComp-> | ||
| + | pComp-> | ||
| + | </ | ||
| + | |||
| + | Then compile and you'll get this: | ||
| + | |||
| + | {{: | ||
| + | |||
| + | Whee! | ||
| + | |||
| + | Let's say you want the image to be clickable, and when clicked, it changes to a random tint color. | ||
| + | |||
| + | Here is the new code: | ||
| + | <code cpp> | ||
| + | //first add this anywhere above the App:: | ||
| + | |||
| + | void OnEntityTouched(VariantList *pVList) | ||
| + | { | ||
| + | CL_Vec2f touchPt = pVList-> | ||
| + | Entity *pEntTouched = pVList-> | ||
| + | |||
| + | LogMsg(" | ||
| + | |||
| + | // | ||
| + | pEntTouched-> | ||
| + | } | ||
| + | |||
| + | //Next, add this to your existing init code from the last section | ||
| + | |||
| + | //add the change color when touch stuff | ||
| + | pComp = pEnt-> | ||
| + | pEnt-> | ||
| + | </ | ||
| + | |||
| + | {{: | ||
| + | |||
| + | Clicking (tapping once on a touch device) it will now change the color randomly and show something like " | ||
| + | |||
| + | Well, we came this far, let's make a few changes so ten of these things spawn and randomly move around forever. | ||
| + | |||
| + | First we'll need to add another separate function to wire to, in addition to the one we did. Here they both are: | ||
| + | |||
| + | <code cpp> | ||
| + | |||
| + | void OnEntityTouched(VariantList *pVList) | ||
| + | { | ||
| + | CL_Vec2f touchPt = pVList-> | ||
| + | Entity *pEntTouched = pVList-> | ||
| + | |||
| + | LogMsg(" | ||
| + | |||
| + | //change to a new random tint | ||
| + | pEntTouched-> | ||
| + | } | ||
| + | |||
| + | void MoveEntityToRandomPlace(VariantList *pVList) | ||
| + | { | ||
| + | Entity *pEnt = pVList-> | ||
| + | |||
| + | CL_Vec2f vPosToMoveTo = CL_Vec2f(Random(GetScreenSizeX()), | ||
| + | |||
| + | int timeToTakeForMoveMS = RandomRange(100, | ||
| + | ZoomToPositionEntityMulti(pEnt, | ||
| + | |||
| + | // | ||
| + | //message manager | ||
| + | GetMessageManager()-> | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | And here is the replacement code for the init: | ||
| + | |||
| + | <code cpp> | ||
| + | // | ||
| + | AddFocusIfNeeded(pGUIEnt); | ||
| + | |||
| + | int logosToCreate = 10; | ||
| + | |||
| + | while (logosToCreate--) | ||
| + | { | ||
| + | |||
| + | //start in a random place | ||
| + | Entity *pEnt = CreateOverlayEntity(pGUIEnt, | ||
| + | EntityComponent *pComp = pEnt-> | ||
| + | pComp-> | ||
| + | pComp-> | ||
| + | float scale = RandomRangeFloat(0.4f, | ||
| + | pEnt-> | ||
| + | FadeInEntity(pEnt, | ||
| + | |||
| + | //add the change color when touch stuff | ||
| + | pComp = pEnt-> | ||
| + | pEnt-> | ||
| + | |||
| + | // | ||
| + | //then use the MessageManager to trigger it when we want | ||
| + | |||
| + | pEnt-> | ||
| + | |||
| + | //Call it now to get things started. | ||
| + | pEnt-> | ||
| + | } | ||
| + | </ | ||
| - | Types the database understands are float, string, uint32, CL_Vector2, CL_Vector3, CL_Rect2f, *Entity, and *EntityComponent. | + | {{:entity_5.png?|}} |
| - | VariantDB' | + | And now you get a bundle of crazy logos bouncing around. |
| - | ** | + | So now you probably can sort of see the concept of " |
| + | Well written components respond to property changes at any time - for instance, if you were to change the " | ||
proton_entity.1287980572.txt.gz · Last modified: by seth
