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: 2010/10/25 05:40 by seth