Playmaker Forum

PlayMaker Help & Tips => PlayMaker Help => Topic started by: Snowbird on November 12, 2018, 11:11:37 PM

Title: How to have prefabs reference game objects without global variables?
Post by: Snowbird on November 12, 2018, 11:11:37 PM
I am just encountering numerous situations for entirely separate reasons where I'd like to have prefabs reference a game object (usually it's location, but sometimes other reasons as well).

The solutions I have worked with so far are:
1. Have the game object reference the prefab instead of vice versa, but this isn't always a practical solution, and will definitely not work in every situation as the main character would need countless FSM's.
2. Create a global variable and have things reference that. This seems like a terrible method so far as sometimes my global variables just randomly disappear. I would prefer to just stay away from this entirely and find other methods.
3. Find less-than-ideal workarounds (for example I can just have big collision boxes appear and run trigger checks with all sorts of different tag names in order to know the correct direction, or lots of things similar that I mentioned in point #1.)

So just as an example, what would be a way to get a prefab wolf to know the exact location of the player, at anytime, without using global variables? The wolf doesn't need to know this the second it is created, but needs to know this during combat. 2d btw.

Any help is much appreciated.
Title: Re: How to have prefabs reference game objects without global variables?
Post by: Athin on November 12, 2018, 11:42:15 PM
There are a few ways to do this.

#1 If you use a TAG for the player and then a Find Object, you can search for that tag. 

#2 If you only have one player the wolfs need to track, I'd use a global game object variable that is the player and use that to reference for the wolfs.  The wolfs can use a simple Get position action on that game object but this only works if you got one character to track.

#3 Have the player store its location every frame in an FSM.  Then the wolfs can use the Get FSM Vector2 action whenever they want to know where the player is.

Without knowing exactly how your FSM are working I can't really say the best solution but hopefully these ideas can help you out.
Title: Re: How to have prefabs reference game objects without global variables?
Post by: jeanfabre on November 13, 2018, 12:08:06 AM
Hi,

 you can also broadcast an event if you know all the interested fsm are live and running. Your player can send "HELLO IT IS ME" global event and you use the action Get Event Info to know the sender GameObject.

 Bye,

 Jean
Title: Re: How to have prefabs reference game objects without global variables?
Post by: Snowbird on November 13, 2018, 12:34:47 AM
Cool thanks for the ideas guys. I'll try them out tomorrow and get back if I have any questions still.
Title: Re: How to have prefabs reference game objects without global variables?
Post by: Thore on November 13, 2018, 05:06:20 PM
Another method is to use a kind of singleton approach: have one “Game Manager” game object with an FSM that can store variables. Now everything of import can find the manager and store itself (or some other variable) into the game manager. This hinges on the fact that you know, and take for granted, that this game manager definitely exists in the scene, under a fixed name or tag, and thus can always be found, even if everything else changes dynamically. Now the wolf can get the reference from the Game Manager.

However, it many situations its better to keep parts self sufficient, i.e. make the wolf find player by tag, by name etc. You can also use a game object under the player to store a tag when you need the player holder to use a different tag. Then you find that child object, and then its parent etc.
Title: Re: How to have prefabs reference game objects without global variables?
Post by: Snowbird on November 25, 2018, 03:51:28 AM
Another method is to use a kind of singleton approach: have one “Game Manager” game object with an FSM that can store variables. Now everything of import can find the manager and store itself (or some other variable) into the game manager. This hinges on the fact that you know, and take for granted, that this game manager definitely exists in the scene, under a fixed name or tag, and thus can always be found, even if everything else changes dynamically. Now the wolf can get the reference from the Game Manager.

Okay this is a good idea. However, what if I have a projectile prefab that collides with an enemy prefab and there needs to be a complicated damage formula that the game manager needs to do?

Both (the projectile and the enemy) are prefabs and both can't send events to the game manager when the collision occurs. So how can I get the game manager to know about the collision to do the damage calculation and subtract HP from the right enemy?

I might be able to do jeanfabre's suggestion but it doesn't sound like the kind of thing I want to be doing all the time, in some cases multiple times a second. Or am I wrong about that?

(bonus question: this isn't exactly related, but what happens if 2 enemies get hit at once? What problems are likely to occur?)
Title: Re: How to have prefabs reference game objects without global variables?
Post by: waveform on November 25, 2018, 07:41:05 AM
in my case, i dont use prefab at all, because prefab cant store gameobject reference, so i just simply create a gameobject in scene then give it name "Prefab", unactive it,now i just put all gameobject as child of this "Prefab" object and re use them as prefab,the only situation i use unity prefas is then it`s only a model
Title: Re: How to have prefabs reference game objects without global variables?
Post by: djaydino on November 25, 2018, 11:27:59 AM
Hi,
What i usually do is :
Have a gameobject variable on the prefab (called gamemanger for example)
Then when i create or spawn the prefab @ runtime, store the created/spawned object.
Then use a 'set fsm gameobject' and store the object that needs the reference.
Title: Re: How to have prefabs reference game objects without global variables?
Post by: Snowbird on November 25, 2018, 08:29:56 PM
Hi,
What i usually do is :
Have a gameobject variable on the prefab (called gamemanger for example)

Okay, just to make sure I understand. The prefab in question, in one of its FSM's, will have a gameobject variable called "gamemanager". (or another name I see fit)

Quote
Then when i create or spawn the prefab @ runtime, store the created/spawned object.

Okay, so lets say at runtime I spawn 5 arrows (projectile), deactivated, ready to be used when the character fires an arrow as part of my pooling system. However, this is where I am confused @ what you are saying.

I want to, when these arrows are created, "store the created/spawned object"? Like store it in the gameobject variable called "gamemanager" that I just created? How do I do that if the instance of the prefab doesn't exist yet? Also I am going to have 5 of them.


Quote
Then use a 'set fsm gameobject' and store the object that needs the reference.

This part I am having difficulty understanding as well. I will still have the same problem of trying to edit an FSM that the prefab can't reference, will I not?


Sorry I am having difficulty understanding this. I figured I was doing so well but am pretty confused again. Been trying at it for a long time in Unity and can't figure it out. I really appreciate the help.
Title: Re: How to have prefabs reference game objects without global variables?
Post by: Athin on November 25, 2018, 11:35:45 PM
So just as an example, what would be a way to get a prefab wolf to know the exact location of the player, at anytime, without using global variables? The wolf doesn't need to know this the second it is created, but needs to know this during combat. 2d btw.

Any help is much appreciated.

Going back to the start on this one.

All you simply need for this is have the player have a tag of "Player" or whatever you like to call it.  Then on the wolf prefab,  whenever it needs to find the player do the Find Game object action with the tag filter of "Player".  Then you will have the player as a Game object variable to then use the Get Position Action.

I think you might be over thinking the problem a bit and getting confused on the process but if the example above is what you need,  the answer above should work fine.

Let me know if you still need help though.
Title: Re: How to have prefabs reference game objects without global variables?
Post by: Snowbird on November 26, 2018, 01:31:52 AM


Going back to the start on this one.

All you simply need for this is have the player have a tag of "Player" or whatever you like to call it.  Then on the wolf prefab,  whenever it needs to find the player do the Find Game object action with the tag filter of "Player".  Then you will have the player as a Game object variable to then use the Get Position Action.

I think you might be over thinking the problem a bit and getting confused on the process but if the example above is what you need,  the answer above should work fine.

Let me know if you still need help though.

Yeah I got that one solved a while ago, and your solution was the one I ended up using. My problem now is I need instances of prefabs to communicate certain things to objects in the scene. (such as collision info, variables on the prefab instances, etc. I got 2 answers but I can't figure one of them out, and the other solution offered of just "not using prefabs" probably won't work for this project very well)
Title: Re: How to have prefabs reference game objects without global variables?
Post by: Athin on November 26, 2018, 03:31:09 AM
Ah sorry was at work and thought you had the same issue happening. 

So if I understand the problem correctly, you want to be able to detect if a projectile hits the target and when it does do "X"math depending on what got hit?

If thats the case you can try what I like to do in these cases. I assign an 'Armor ID' int value on anything that will be effected by the bullet (Think as 0=light armor, 1=heavy armor ect...).  Then I'd make a empty game object (We'll call it Battle Manager) that handles all the math when something gets hit. Once I do that I'd use a trigger event on a bullet with a tag filter to detect anything it hits.

When the bullet hits a target, I send the game object var it hit to the Battle Manager using the Set FSM Game Object action.  The Battle manager now knows who he needs to look at and will now use the Get FSM Int Action to find out his Armor ID.  A simple Int Switch from there will then determine the formula to use and once its done its calculations, it will then send the info back to the object that got it. 

That may be way more complex then what you actually need but I figured that you might be able to use some of that to help with your problem.  Hopefully I've helped out and not confused ;)   
Title: Re: How to have prefabs reference game objects without global variables?
Post by: Thore on November 26, 2018, 06:27:32 AM
For projectiles, use the pair of Set Event Data (https://hutonggames.fogbugz.com/default.asp?W531) on the sender and Get Event Info (https://hutonggames.fogbugz.com/default.asp?W530) on the receiver.

Brief: There is some condition (collision, trigger etc), and the sender knows about the receiver. Then, the sender sends an event over to the receiver, let’s say “HIT”. The receiver starts up, and with get event info reads a package of variables, like how much damage, from whom and so on.

Example. The weapon spawns (or switches on, or takes from pool) a damage dealer object, let’s call that projectile (it doesn’t have to fly around, can also be the sword blade, or invisible object that only does the mechanics, while it looks whatever you like).

This projectile has some mechanics on it to make it move, shoot, fall down, or may do nothing at all. In any case, make a standalone state not connected to any other state. Then add a global event to it, and from the system event list pick On Collision Enter or On Trigger Enter. Depending on what you use. Also beware, the if you specifically use 2D physics, you always need to pick the corresponding 2D actions. That means, when it hits, or something passes through it, it will then start the new unconnected state and go from there.

In this state, you place the Set Event Data, and a Send Event. The data contains the values you want to ship over, and the send event is to let the target know, so it picks up that data. Send Event uses a global event, let’s call this “HIT”

Now the target has a health FSM. Inside, it also has a state with a global event, that waits for “HIT”. Then, place Get Event Info, which makes it pick up the event data from the projectile.

Next, you want to process the data and run it through damage formula. The easiest is to substract from current health points. But you can add modifers before etcetera. After that you want to check if health is left and if not, do the death and despawn/respawn etc. If it was just a scratch, and everything is done, make sure to NULL out the variables that were used, i.e. set them to contain no relevant data to prevent that leftovers create bugs the next time.
Title: Re: How to have prefabs reference game objects without global variables?
Post by: Thore on November 26, 2018, 06:39:26 AM
Also, when you want to use armor types or resistances etc, you can store the type in a string in event data. Then on the receiver, before damaga calculation, do a string switch (https://hutonggames.fogbugz.com/default.asp?W455) that then leads it through the desired formula.

A more robust way is with Enums though, but that’s currently not directly possible with this approach. The target could however get an fsm enum from the projectile, if that action exists. Then you are sure to work with standardised categories.

Generally though: the projectile should not care about what the target does with the info. It should simply say: you got hit by me, I deal 23 damage of type fire.

The target then says, kthxbye, checks if “fire” means anything to it, and if it does, “haha, I am 50% resistant to fire!” take the damage and multiply it by 0.5. And if it takes twice fire damage, multipy it with 2 and so on.
Title: Re: How to have prefabs reference game objects without global variables?
Post by: Snowbird on November 26, 2018, 04:05:30 PM
When the bullet hits a target, I send the game object var it hit to the Battle Manager using the Set FSM Game Object action.

This is my entire question. I can't communicate to the battle manager because the projectile and the enemy are both prefabs.

Let me try and explain the situation in full, as you guys are trying to help me with stuff that I've already done and solved a while ago, and then brushing over the part that I don't understand.

We have 4 things:
Character - not prefab - has attributes like strength and agility that should affect arrow damage
Battle Manager - not prefab - takes in attributes from player, arrow projectile prefab, and wolf enemy prefab, does a calculation using the variables from all 3 of those, and then spits a number to the wolf enemy
Arrow Projectile - prefab - has a damage variable
Wolf Enemy - prefab - has a health variable and a physical resist variable


-Character creates an arrow.
-Arrow flies at enemy and collides with enemy
-Battle manager would now like to know of this collision, and grab information from the character, the arrow, and the wolf
-Battle manager does a calculation and sends that number to the wolf
-The wolf subtracts its health equal to the number sent to it by the battle manager

So as you see, I can do all this very easily enough. It is no problem for me. The problem occurs when I make the wolf and arrow into prefabs. Now I don't know how to communicate to the battle manager. Another thing to consider is there could be many arrow instances and many wolf instances all spawned from the same prefab, as well as tons of different types of attacks and tons of different types of enemies, making it nice to be able to use the same battle manager for everything.

(something I could do is store all this type of information in the scene, such as arrow damage, wolf armor, etc. but then I still need the battle manager to know of the collision that occurs and also how to send information to a specific instance of a prefab)
Title: Re: How to have prefabs reference game objects without global variables?
Post by: Thore on November 26, 2018, 06:16:22 PM
Your setup seems convoluted. The suggestion of using a scene based manager is good for some game types: if you cannot for some reason use names or tags, to have something akin to global variables or “variable containers” in one place, to keep track of something without every frame checks etc. However, as I wrote, it is better to handle each part itself with as minimal spaghetti (cross referencing etc) as possible.

In your case, you need to think more atomistic. Each wolf manages its own health. Each arrow delivers the damage. See the method above.

Player is tagged as player, and wolf is tagged enemy. Each arrow’s collision/trigger detection looks for something with the tag enemy, and then stores the object it hit (the wolf). Then the arrow “knows” the wolf that was hit. It also has the event data action, that contains the damage values, and sends an event to the wolf’s health FSM on the wolf, telling it, that it was hit. The wolf’s health FSM gets the info (see a post above of mine) and then substracts this value from its own health, then checks if the wolf is still alive etc.

If you really want to use a battle manager to do the calculation, which looks like spaghetti to me, you can find the Battle Manager by name, or tag. Then, it has two game object variables I assume, one for damage dealer and one for damage receiver.

When the collision or trigger hits, the arrow knows what it hit and also knows itself (at least with get owner, executed at the start). It can then Set FSM Game Object , twice, into the Battle Managr, once for itself as the dealer and the target it hit as the receiver. Then the battle manager knows them, too, and can do whatever calculation it needs to do, and use those same game object variables plus Send Events or Set FSM viables to report back the outcome.

However, unless you have a compelling reason for doing it like this, I recommend to do the more robust, simpler approach without the battle manager.
Title: Re: How to have prefabs reference game objects without global variables?
Post by: Snowbird on November 26, 2018, 06:51:46 PM
It also has the event data action, that contains the damage values, and sends an event to the wolf’s health FSM on the wolf, telling it, that it was hit.

This is the only thing I am asking. I don't understand how the arrow can communicate to the wolf. I don't know how to send an event from the instance of one prefab to the instance of another or to a scene object. That's what I am trying to figure out. I can tell the wolf that it has entered a collision with an arrow and therefore must subtract 10 health (not very useful). I can tell that arrow to disappear when it hits the enemy. What I have no idea about is how I can put a variable in the arrow that says it does 5 damage or 10 damage or whatever, and send that number to the instance of the wolf. I also don't know how to have that damage be adjusted based on a scene objects variables (characters attributes for example)

I appreciate the advice about cleaning it up and making it simpler, and will continue to work on making it better. But the thing that is confusing me is that. How can the instances of the prefabs send each other variables. You mention these actions like "send event", which work great for scene objects, but they don't work from prefabs to things outside of the prefabs.
Title: Re: How to have prefabs reference game objects without global variables?
Post by: Athin on November 26, 2018, 07:03:37 PM
You need to use the action Set FSM Int.  If you know the object you hit, you can then send the info to that object using that action. 

For example, the arrow hits the wolf.  The arrow knows itself and and the object it hit (Get owner for the arrow and Trigger event to find the hit object.).  Using that info you can then use the action Set FSM Int to give its damage to the wolf and an action "Send Event" to then have the wolf calculate the damage it took. 

I think its this Action "Set FSM Int" you are getting confused on as that is how you can throw values and variables to other FSM for them to use.  It can be used on prefabs or game scene objects alike as long as you know who to send it to which in this case we do.
Title: Re: How to have prefabs reference game objects without global variables?
Post by: Thore on November 26, 2018, 07:12:12 PM
Okay, I see now.

Read the post above about the Event Data & Event Info. In the arrow, place the Event Data, set a variable under e.g. float. Say, “damage”. Then find the FSMs variable pane, and select the damage float you created, and set the value there. Then, all arrows instantiated from this will use that setup and that value.

How to pass it on, I already explained above. Look in the ecosystem for an example scene on projectiles. Jean created it, and should be up there.
Title: Re: How to have prefabs reference game objects without global variables?
Post by: Snowbird on November 27, 2018, 03:48:16 AM
Okay thank you two for the help. I'm finally able to get some communication going between these things and I am trying to do less cross referencing as well (such as damage calculation being done purely on the arrow and resistances being factored in on the enemy right be we subtract health).

My problem was I didn't understand what storing the game object did upon collision or spawning of a prefab instance, and how I could use that stored variable to send events.  Maybe I just needed to sleep on it as sometimes the brain gets jumbled after working for a while.

This is how I see I should move forward—does this sound better?
-Character spawns an arrow and stores it, then sends it some attributes (such as strength and agility)
-Arrow is spawned and quickly calculates its total damage it should deal based off of its base damage and the attributes it was sent, and flies toward the enemy.
-Arrow collides with enemy and stores the enemy as a gameObject, and sends it the total damage number.
-Enemy receives the collision and the number sent, and reduces that number by its 'armor', then reduces it's HP by that amount.

I have this working fine now. This seems like a better way to do it?
(I'm already thinking of some slight improvements)

Bonus question: When the enemy dies, I want to send experience to the character. Is the best way to do this "find game object" with tag, seeing as it doesn't need to be quick or anything? Once again, thank you for the help and being patient.
Title: Re: How to have prefabs reference game objects without global variables?
Post by: Athin on November 27, 2018, 05:55:26 AM
It happens to all of us at times :)  I always find myself banging my head against the problem until one day it just hits me in the face with the answer.

What I'd do is have the enemy spawn in and find the player and store it.  Player shoots an arrow and the wolf detects that it got hit. When hit the wolf grabs the "Stats" needed from the player to do the calculations himself for the damage (Can also have the base arrow damage store in the player if needed).

That will be alot simpler to handle then player > arrow > enemy but also depends on how your game plays out too.
Title: Re: How to have prefabs reference game objects without global variables?
Post by: jeanfabre on December 05, 2018, 02:41:33 AM
Hi,

 ok, the solutions are multiple.

 - you tag your battle manager gameobject, and use FindGameObject ( using tag, not name) on all your prefab instances, then you can communicate with that directly, get values using GetFsmXXX, etc etc

- you send global events and you don't care about who is responsible for listenting to it ( the best option), fire "BATTLE MANAGER / DO SOMETHING" as a global event, now any fsm can catch that, your prefab instance is free from having to know who is the battle manager ( and maybe many fsm will be able to act upon that event.


- you reverse the second option and explain what's happening, your ennemy is doing something, don't talk to the battle manager, but actuall fire an event saying what your instance is doing, any one can act on this then, the player, the battel manager, the score manager anything. "ENNEMY / ON WILL START ATTACK", "ENNEMY / ON DAMAGE" etc etc. you can pass data to your event for additional data, etc etc.

 Bye,

 Jean