Archived

This topic is now archived and is closed to further replies.

Please be aware that the content of this thread may be outdated and no longer applicable.

Ipsquiggle

Wots The Diff?? Prefabs, Components, Stategraphs, And Brains

Recommended Posts

Ipsquiggle    1,275

I understand that there might be some confusion about these four parts of an entity, so here's a brief description of when to use each one.

 

Prefabs and Components:

These two are the meat and potatoes of any entity in the world and where you'll be spending the most time. There is a simple way to think of the difference:

 

Prefabs are what a thing IS.

Components are what a thing DOES.

 

Lets look at the firepit as an example. What is it? A firepit! So it becomes a "firepit" prefab. What does it do? Well, it gets built, and it burns, and takes fuel, and can be looked at, and produces light... So we don't make a "firepit" component, instead we have "burnable" component, and "fueled" component, and so forth.

 

So components are where all the work happens, all the logic and data storage. A prefab is an instruction that says, "These are the components to use, and this is how you configure them.

 

"Most prefabs have a main function that basically does just that: attaches all the components and configures them, and for many prefabs this is the entire story, such as the "carrot" prefab.

 

The place it gets a little funny is that some components need to be told how to do something. For example, the combat component needs to know, "How do I choose a new target if I lose my target?". If we take a look at the "frog" prefab, we can see that it adds a combat component, and then calls inst.components.combat:SetRetargetFunction(3, retargetfn).

 

If we look, retargetfn is defined in the prefab, and it's an instruction for the combat component on how to pick a new target. So basically, it's still a configuration of the component. That's all prefabs really do!

 

Some prefab files define multiple prefabs. Usually these are things with similar components, but different configurations. "staff.lua" contains all the staves. Each one shares a common base, but then has different specialized configurations for each colour of gem.So when you are making your own mods, ask yourself, is this mod a new thing or a new something a thing can do. If you want to make a sword that shoots lightning, you've got a sword (prefab) which can shoot lighting (component). The cool thing about approaching the problem this way is that you can then put your lightning shooter component on a different weapon, or on a hat, or a spider queen, or....!

 

Stategraphs and Brains:

These two are a bit fancier and most of the prefabs in the game don't use them at all! This is because, as mentioned, components are really where the hard work is done. But it's worth knowing about them in case they suit your needs.

 

Stategraphs:

Stategraphs are mainly used for controlling animation and timing. We can look at "SGeyeplant", the stategraph for the Eye Plants that spawn with the Lure Plant, as an example. Its first state is "spawn" which gets played right when the Eye Plant is created. Notice that its "onenter" function plays some animations and plays a sound. It also listens for an "animover" event which gets triggered when the current animation finishes playing, which moves it to the "idle" state. All the idle state does is play the idle animation.Normally that kind of behaviour, "do this thing and when it's done do that thing" is kind of a drudgery to code up, but the stategraph makes it straightforward to handle. We just tell it to go to the "spawn" state and when it's done it will automatically end up idling.

 

Another interesting example is the "attack" state in SGtentacle. We wanted the tentacles to play sounds and deal damage during the "whipping" part of their animation, which occur at certain frames. So this state sets up a timeline which will do certain things at frames 2, 7, 15, etc. Again, we can set this all up in the stategraph, and then the combat component just says "go to your attack state!" and the timing gets taken care of over here.

 

Brains:

Brains are the least used of these four elements, because they are the most high-level and generally entities don't needs this level of complexity. Their purpose is for AI and decision-making so only the complicated creatures need this.I'm not going to describe them in detail here, but in a nutshell: Brains use a system called a "behaviour tree" to make decisions. Every now and then it goes to the first item in the tree and checks, "can I do this?" and if not, it goes to the next one, and then next one, and it's children, and their children, until it finds something it can do. Then it does that! So this means things higher in the list have more priority, which lets you do things like make pigs prefer fighting to eating, but to panic when they are on fire even if they were fighting.

 

Generally each behaviour in the tree boils down to sending instructions to components: components still end up doing all the real "work". So the ChaseAndAttack behaviour, for example, sends instructions to the Locomotor (for movement) and Combat (for attacking) components, and those components handle the details.So as mentioned, most mods will never need to touch brains or stategraphs. But in case you need to modify one, now hopefully you have an idea of what their purpose is.

 

All in all, I think understanding the prefab/component distinction is critical to getting the most out of Don't Starve modding. If any of this wasn't clear or if you have further questions, please ask below! :-)

Share this post


Link to post
Share on other sites
Luggs    667

Thanks, [MENTION=55]Ipsquiggle[/MENTION]. I'd never been too clear on the prefab component thing. Once I'm back at a computer with Don't Starve, I'll be putting this knowledge to use. And probably getting confused every few minutes.

Share this post


Link to post
Share on other sites
Heavenfall    183

It should be noted that [MENTION=55]Ipsquiggle[/MENTION] is describing the behaviour of different parts of the unmodded game. There are no limitations to what brains, stategraphs and components can do in a mod.

For example, I have a prefab that is produced from a crafting recipe. The prefab applies an effect to the player, then instantly disappears. It has no visuals or any such things, and thus it is not a prefab that "is" something.

Similarly, you can mix animations, component calls and stategraph calls freely. I personally found it unnecessarily complicated the way they built a ton of functions for their brains to call, instead of putting simple checks in the brain itself.

Share this post


Link to post
Share on other sites
simplex    2,597

It should be noted that [MENTION=55]Ipsquiggle[/MENTION] is describing the behaviour of different parts of the unmodded game. There are no limitations to what brains, stategraphs and components can do in a mod.

You certainly can bundle functionality together, as this is just a matter of code modularity and extensibility. A mod can choose not to follow it, gaining some simplicity at the cost of flexibility.

For example, I have a prefab that is produced from a crafting recipe. The prefab applies an effect to the player, then instantly disappears. It has no visuals or any such things, and thus it is not a prefab that "is" something.

It still is something, as opposed to a property or a set of states. It not being visible or whatnot doesn't make it less "thingy" than something more concrete (and the game does the same thing with "fx entities", for example). It's still a substantive, and not an adjective, verb or adverb. In your case, it is very verb-like, but still a substantive. It's a thing whose purpose is to do something.

Basically, the game adopts the decorator design pattern, where the decorated "class" is the prefab and the components are the decorations. And the prefab itself can be seen as a decoration of EntityScript at the object level.

Similarly, you can mix animations, component calls and stategraph calls freely. I personally found it unnecessarily complicated the way they built a ton of functions for their brains to call, instead of putting simple checks in the brain itself.

Again, modularity and extensibility. A brain shouldn't "know" about anything else other than itself and the fact that it's attached to an entity. I wouldn't say the game "overcomplicates" anything. Much to the contrary, the game is often overly simple by design, favoring rapid development. I'd much rather have a more complex game infrastructure allowing things like inheritability of components.

Share this post


Link to post
Share on other sites
Heavenfall    183

It is pointless to call a prefab that only exists until its main function has been executed a "prefab" with the above definition. Which is my point. Prefab, brains, stategraphs, components - they describe a data structure within the main game, but what data is actually there is not defined.

Share this post


Link to post
Share on other sites
Ipsquiggle    1,275

[MENTION=12700]Heavenfall[/MENTION] I agree that this is primarily a description of what's already there. In general I try to advise people according to what we've already done, as that will be sufficient for most people. For more experienced developers like yourself, you have the skill to move beyond these templates, and also have the knowledge that you can without me needing to spell it out. :) That said, certainly challenge my assumptions! Just because this is how we've done things internally doesn't mean it's best for you all moving forward!My main motivation for writing this post is that most people have never encountered a prefab/component method of building things before and it's not immediately intuitive. I want to help people understand this idea first, and if they then choose to ignore it that's okay by me! :DThe other question that I wanted to answer, which on rereading I didn't really answer (revisions coming, I guess) is: I've had a number of people ask me "where do I put the code?". In our own structures the answer is: in a component. Pretty much everything that is active at game time is in a component. Again, I don't think this is intuitive at first but I believe it's a very powerful model for adding functionality to a game rapidly and with minimal side effects.The brains and stategraphs are certainly farther afield than most of the other systems in the game. I think [MENTION=12700]Heavenfall[/MENTION] is quite right that I'm describing what's there: Our brains are what they are, and if you want to mod or extend them, hopefully this description will help you understand how they fit together (at least as a starting point).

Share this post


Link to post
Share on other sites
Heavenfall    183

And this is an example of a modified houndbrain I use in the summons mod (without the local auxiliary functions). If you compare it to the brains the devs have in their own files, you'll see I have put quite a few functions straight into the prioritynode instead of outsourcing it to other functions:

function HoundBrain:OnStart()        local root = PriorityNode(    {	IfNode(function() 			local target = self.inst.components.combat.target		    	if target and target:HasTag("bee") then				local beehome = target.components.homeseeker and target.components.homeseeker.home 				if beehome and beehome.prefab == "beebox" then					self.inst.components.combat:GiveUp()				end			end		end,		"OnFire", Panic(self.inst) ),			IfNode(function()		if self.inst.components.houndtypes.currenttype == "normal" then			return true 		end 		end, "isnormal",		DoAction(self.inst, EatGemAction, "eat food", true )),	-- set up hound to abandon us if it finds a fatherfigure	WhileNode(function() 		if (self.inst.components.follower.leader ~= GetPlayer()) then			return false		end		-- if we reach here or below it can be assumed that leader is currently player		if (self.inst.components.follower.leader ~= nil) then			local x,y,z = self.inst.Transform:GetWorldPosition()			local ents = TheSim:FindEntities(x,y,z, TUNING.SPIDERHAT_RANGE, {"realhound" })			if #ents == 0 then return false end			if (#ents > 0) then								--print ("Player-summoned Hound is abandoning player for better parent!")				local originalfather = self.inst.components.follower.leader				originalfather.components.leader:RemoveFollower(self.inst)				local newfather = ents[1]				newfather.components.leader:AddFollower(self.inst)				self.inst.components.follower.maxfollowtime = 99999999				self.inst.components.follower:AddLoyaltyTime(9999999)				self.inst:AddTag("hound")				self.inst:AddTag("monster")				if self.inst.components.followersitcommand then 					self.inst.components.followersitcommand.stay = false				end				if self.inst.components.unteleportable then					self.inst:RemoveComponent("unteleportable")				end			end		end		return true end, "OnFire", Panic(self.inst) ),		-- set up hound to return to us if it has no leader (keep in mind we have to repeat finding a hound first in case it loses its first father but a second hound father is viable)			WhileNode(function() 			-- fail node if it has father			if (self.inst.components.follower.leader ~= nil) then				return false			end			-- if we reach here or below it can be assumed that it has no father			-- remove its hound tag			self.inst:RemoveTag("hound")			self.inst:RemoveTag("monster")			-- search for hound fatherfigure			local x,y,z = self.inst.Transform:GetWorldPosition()			local ents = TheSim:FindEntities(x,y,z, TUNING.SPIDERHAT_RANGE, {"realhound"})			--if #ents == 0 then return false end			if (#ents > 0) then								--print ("Player-summoned Hound is choosing to reconnect with another hound rather than the player!")				local newfather = ents[1]				if not newfather.components.leader then	newfather:AddComponent("leader") end				newfather.components.leader:AddFollower(self.inst)				self.inst.components.follower.maxfollowtime = 99999999				self.inst.components.follower:AddLoyaltyTime(9999999)				self.inst:AddTag("hound")				self.inst:AddTag("hound")				if self.inst.components.followersitcommand then 					self.inst.components.followersitcommand.stay = false				end			else				local x,y,z = self.inst.Transform:GetWorldPosition()				local ents = TheSim:FindEntities(x,y,z, TUNING.SPIDERHAT_RANGE, {"player"})				if (#ents > 0) then					--print ("its hound father dead, reconnecting with player")					local newfather = ents[1]					newfather.components.leader:AddFollower(self.inst)					self.inst.components.follower.maxfollowtime = 99999999					self.inst.components.follower:AddLoyaltyTime(9999999)					-- if hound is trying to reconnect with player, it is likely that player is target. He will then attack player and be unattackable. Therefore trigger giveup if target					self.inst.components.combat:GiveUp()				end			end		return true end, "OnFire", Panic(self.inst) ),        WhileNode(function() return self.inst.components.health.takingfiredamage end, "OnFire", Panic(self.inst) ),	-- this may seem redundant but it sets the range it searches for targets to a lot less when having a leader        WhileNode(function() return GetLeader(self.inst) end, "HasLeader", 		WhileNode( function() return self.inst.components.combat.target == nil or not self.inst.components.combat:InCooldown() end, "AttackMomentarily",			ChaseAndAttack(self.inst, 10))),	WhileNode( function() return self.inst.components.combat.target and self.inst.components.combat:InCooldown() end, "Dodge",                    RunAway(self.inst, function() return self.inst.components.combat.target end, RUN_AWAY_DIST, STOP_RUN_AWAY_DIST) ),        -- no leader? larger hunt area	ChaseAndAttack(self.inst, 100),		-- set up hound to always get something to eat as soon as it is created	WhileNode(function() return self.inst.components.eater:TimeSinceLastEating() == nil  end, "Hasnteatenever",         	DoAction(self.inst, EatFoodAction, "eat food", true )),	-- set up hound to eat if it hasn't eaten in 480 seconds (one day)	WhileNode(function() return self.inst.components.eater:TimeSinceLastEating() ~= nil and self.inst.components.eater:TimeSinceLastEating() >= 480 end, "Hasnteateninaday",         	DoAction(self.inst, EatFoodAction, "eat food", true )),       	-- inst basically needs two states: one where it "follows" its leader (return to leader if it wanders too far away, otherwise return FALSE failing the node allowing it to Wander later on)	IfNode(function() 				if self.inst.components.follower.leader ~= nil and self.inst.components.followersitcommand and self.inst.components.followersitcommand:IsCurrentlyStaying() == false then			return true		elseif self.inst.components.follower.leader ~= nil and not self.inst.components.followersitcommand then			return true		end		end, "has leader",			Follow(self.inst, GetLeader, MIN_FOLLOW_LEADER, TARGET_FOLLOW_LEADER, MAX_FOLLOW_LEADER)),        -- and one where it is "gohome" to a position	IfNode(function() 				if self.inst.components.follower.leader ~= nil and self.inst.components.followersitcommand and self.inst.components.followersitcommand:IsCurrentlyStaying() == true then			return true		end		end, "has leader",			WhileNode(function() return ShouldGoHome(self.inst) end, "ShouldGoHome",            		DoAction(self.inst, GoHomeAction, "Go Home", true ))),         		Wander(self.inst, function() return GetWanderPoint(self.inst) end, 20),    }, .5)        self.bt = BT(self.inst, root)        self.inst:ListenForEvent("attacked", function(inst, data) self:OnAttacked(data.attacker) end)    self.inst:ListenForEvent("onattackother", function(inst, data) self:OnAttackOther(data.target) end)    end
Again, the point being it is possible to do this, if you want to.

Share this post


Link to post
Share on other sites
tehMugwump    428

Great post Ipsy. Very clear and well written. As you stated, the point of the post is to define what the game is at it's base. We can then take that and run with it (straight into walls, in my case :p)

Share this post


Link to post
Share on other sites
Blucher    11

These are very nice.  However, I imagine there are about (just hazarding a guess) about 20-25 THINGS people would like to do, from modifying how fast a tool is used up or tweaking a recipe to building a fully-fledged character or brand-new world actor with graphic assets and interactions.

 

Examples, I think, would be more useful to people than groundwork/definitions.

Share this post


Link to post
Share on other sites
RedRock911    36

Ok, so, lets say I have a character named  Dave

and I want danger dave to have a pet named Ed who I want to act like a container like chester. However Ed eats berries, and has a (behavior?) like a turkey (which I cant find any prefab file for) and honey from hives.

Which one is a behavior, a function  or a component, and would I need a brain for Ed or Dave? ( I'm new so I dont know what I'm doing, and Im pretty lost when it comes to adding behavior/component files)

Share this post


Link to post
Share on other sites
Aphid98    1

very handy for people wanting to mod but are not sure what the language pieces mean

the Morse code means 

 

EXORDIUM:

  1. the beginning or introductory part, especially of a discourse or treatise.

Share this post


Link to post
Share on other sites
Mobbstar    13,332

Actually, I used the internet  :friendly_wink:

 

Are you even aware of the sarcasm? I quoted the princess bride if you care: "Yes, you are very smart. Shut up."

 

Do not reply to old threads if you don't have anything essential to add to the original topic. In this case, your statement had literally nothing to do with the topic, and is also a fact that can be found on other threads in these forums. Anybody can google "morse code", you've done nothing of interest.

 

I kindly ask you to not reply here any further though. If you really need to reply, please do so using the Private Message system of this website. You know how to use the internet, so you shouldn't have trouble finding it.

Share this post


Link to post
Share on other sites