proton_entity
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| proton_entity [2010/10/25 05:40] – 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. |
| - | 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::Update() from the RTSimpleApp example) | + | They way they accomplish this is by connecting a component function (say, Render) |
| - | <code cpp> | + | |
| - | Entity ent; // | + | |
| - | //Ok, now we've got one. | + | 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. |
| - | ent.SetName(" | + | |
| - | LogMsg(" | + | ====Test drive===== |
| - | //Shortcut: we could' | + | Ok, time to just cannon-ball into the pool and show you some stuff. The important thing to realize is we're just using entities and components to do everything. |
| - | //Yes, I like printf formatting, so sue me. | + | If you see a weird function, like, BobEntity(), |
| - | </ | + | |
| - | ** Is it an entity or a database? | + | There are a TON of tiny helper functions in **EntityUtils.cpp**, check the h sometime. |
| - | This " | + | |
| + | If you'd like to work along with me, open the RTSimpleApp project now! | ||
| <code cpp> | <code cpp> | ||
| - | ent.GetVar(" | + | //Open App.cpp. Find MainMenuCreate(pGUIEnt) in App::Update(), comment it out and add two lines so you have this: |
| - | ent.GetVar(" | + | |
| - | //retrieve the data like this: | + | //MainMenuCreate(pGUIEnt); |
| + | AddFocusIfNeeded(pGUIEnt); | ||
| + | Entity *pEnt = CreateOverlayEntity(pGUIEnt, | ||
| - | LogMsg(" | ||
| </ | </ | ||
| - | Great. Notice that to retrieve | + | Instead of the menu as before, you should see the proton logo rendered. (I've set my screen output |
| - | Types the database understands are float, string, uint32, CL_Vector2, CL_Vector3, CL_Rect2f, *Entity, and *EntityComponent. | + | {{:entity_1.png?|}} |
| - | VariantDB' | + | Well, we see the logo there. |
| - | ** Adding | + | Add a few more lines of code: |
| + | <code cpp> | ||
| + | // | ||
| + | AddFocusIfNeeded(pGUIEnt); | ||
| + | Entity | ||
| + | //Instead of drawing at 0,0, let's set the pos2d variable to 100,100 so it draws there instead. | ||
| + | pEnt-> | ||
| + | |||
| + | // | ||
| + | GetMessageManager()-> | ||
| + | //and, move back to 0,0 a second after that. | ||
| + | GetMessageManager()-> | ||
| + | </ | ||
| + | |||
| + | So now the ball sort of jerks around the screen. | ||
| - | Well, we'd better get a visual on the screen before you fall asleep. | + | Instead of jerkily moving, we can use an InterpolateComponent to smoothly change one variant value to another. |
| - | We're going to use an " | + | 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. |
| - | Each entity can hold an unlimited number of components, or child entities. | + | Replace with this code: |
| <code cpp> | <code cpp> | ||
| - | Entity | + | // |
| - | EntityComponent *pComp | + | AddFocusIfNeeded(pGUIEnt); |
| - | ent.AddComponent(pComp); | + | Entity |
| + | |||
| + | |||
| + | CL_Vec2f imageSize | ||
| + | |||
| + | 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, true, 1000, 4000); | ||
| </ | </ | ||
| - | Wait, we forgot | + | 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> | <code cpp> | ||
| - | pComp->GetShared("fileName" | + | // |
| + | AddFocusIfNeeded(pGUIEnt); | ||
| + | Entity *pEnt = CreateOverlayEntity(pGUIEnt, | ||
| + | |||
| + | //only this part is new, we're adding a component here | ||
| + | EntityComponent *pComp = pEnt->AddComponent(new TrailRenderComponent); | ||
| + | |||
| + | 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, | ||
| </ | </ | ||
| - | Great, so we're ready to render. | + | {{: |
| + | |||
| + | You see the same thing as before but with trail. | ||
| + | |||
| + | By simply adding the TrailRenderComponent | ||
| + | |||
| + | 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> | <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 | //register ourselves to render if the parent does | ||
| - | GetParent()-> | + | GetParent()-> |
| + | |||
| + | //our own variables/ | ||
| + | m_pFrames = & | ||
| + | m_pTimeBetweenFramesMS = & | ||
| </ | </ | ||
| - | Ok, don't panic. | + | The first big chunk setting up pointers to variables |
| - | So, to get this component to run its "Render" (you'd have to do this per frame) you could do: | + | 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 is added: | ||
| <code cpp> | <code cpp> | ||
| - | ent.GetFunction("OnRender" | + | pComp-> |
| + | pComp-> | ||
| </ | </ | ||
| - | That would do it. Or, you could call it on the component itself: | ||
| + | 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> | <code cpp> | ||
| - | ent->GetComponentByName("OverlayRender" | + | //first add this anywhere above the App:: |
| + | |||
| + | void OnEntityTouched(VariantList *pVList) | ||
| + | { | ||
| + | CL_Vec2f touchPt = pVList->Get(0).GetVector2(); | ||
| + | Entity *pEntTouched = pVList-> | ||
| + | |||
| + | LogMsg("Touched %s at %s!", pEntTouched-> | ||
| + | |||
| + | //change to a new random tint | ||
| + | pEntTouched->GetVar("color" | ||
| + | } | ||
| + | |||
| + | //Next, add this to your existing init code from the last section | ||
| + | |||
| + | //add the change color when touch stuff | ||
| + | pComp = pEnt-> | ||
| + | pEnt-> | ||
| </ | </ | ||
| - | This would render the image from a reference point of 0,0. | ||
| - | Now, let's say you had an entity that had 50 child entities, some of which had additional child entities. Wow, that's a lot of OnRender' | + | {{:entity_4.png?|}} |
| - | No worries, you can recursively call the function | + | Clicking (tapping once on a touch device) it will now change |
| + | |||
| + | Well, we came this far, let's make a few changes | ||
| + | |||
| + | 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> | <code cpp> | ||
| - | ent. CallFunctionRecursively(" | ||
| - | //Now, the above would work, but a better way would be to pass on each entities frame of reference, so the positions are relative to their parents xy position. | + | void OnEntityTouched(VariantList *pVList) |
| + | { | ||
| + | CL_Vec2f touchPt = pVList-> | ||
| + | Entity *pEntTouched = pVList-> | ||
| + | |||
| + | LogMsg(" | ||
| + | |||
| + | // | ||
| + | pEntTouched-> | ||
| + | } | ||
| + | |||
| + | void MoveEntityToRandomPlace(VariantList *pVList) | ||
| + | { | ||
| + | Entity *pEnt = pVList-> | ||
| + | |||
| + | CL_Vec2f vPosToMoveTo = CL_Vec2f(Random(GetScreenSizeX()), | ||
| - | ent.CallFunctionRecursivelyWithUpdatedVar(" | + | int timeToTakeForMoveMS = RandomRange(100, 1000); |
| + | ZoomToPositionEntityMulti(pEnt,vPosToMoveTo, timeToTakeForMoveMS, | ||
| - | //The above says " | + | //let's schedule this to run again at the exact point they reach their final destination, |
| + | //message manager | ||
| + | GetMessageManager()-> | ||
| + | } | ||
| </ | </ | ||
| - | Now, what if instead of calling it, we build a component that called | + | And here is the replacement code for the init: |
| - | (FocusRenderComponent attaches its OnRender to GetBaseApp()-> | + | <code cpp> |
| + | // | ||
| + | AddFocusIfNeeded(pGUIEnt); //so it will render, | ||
| + | |||
| + | int logosToCreate = 10; | ||
| - | So, to sum up, this means you can store entities anywhere, you aren't limited to a single hierarchy. | + | while (logosToCreate--) |
| + | { | ||
| - | Similarly, FocusInputComponent is used to catch touches and keystrokes | + | // |
| + | Entity *pEnt = CreateOverlayEntity(pGUIEnt, " | ||
| + | EntityComponent *pComp = pEnt-> | ||
| + | pComp-> | ||
| + | pComp-> | ||
| + | float scale = RandomRangeFloat(0.4f,1.0f); | ||
| + | pEnt-> | ||
| + | FadeInEntity(pEnt, | ||
| - | That said, there is a handy place for you to store your entities, GetBaseApp()->GetEntityRoot(). (It's an entity you can add to, think of an empty entity as functioning like a folder if you add more entities as children to it) | + | //add the change color when touch stuff |
| + | pComp = pEnt-> | ||
| + | pEnt->GetFunction(" | ||
| - | ** The MessageManager | + | // |
| + | //then use the MessageManager | ||
| - | Ok, take everything you've learned and understand that you can "schedule" | + | pEnt-> |
| - | Here is an example, and now we're going to use helper functions. | + | //Call it now to get things started. |
| + | pEnt-> | ||
| + | } | ||
| + | </ | ||
| - | //one time init | + | {{:entity_5.png?|}} |
| - | Entity *pEnt = CreateOverlayEntity(GetBaseApp()-> | + | |
| - | AddFocussIfNeeded(pEnt); | + | |
| - | GetMessageManager()-> | + | |
| + | 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.1287985223.txt.gz · Last modified: by seth
