I am trying to make fire evacuation simulation by using Unity. I need to count the number of agent passes through the exit door during the evacuation. Is there any way to do it ?
You could set up a simple trigger collider system.
First, you would place a box collider at the exit door, and set it to trigger so it isn’t a solid object (objects can path through it, rather than walk into it). Now, add a Rigidbody to this box collider, and set first drop-down menu that says ‘Dynamic’ to ‘Kinematic’. Now, a way to count them. We will ad the following script to the box collider object:
using UnityEngine;
public class ExitDoor : MonoBehavour
{
void OnTriggerEnter(Collider obj)
{
if (obj.gameObject.tag == “agent”)
{
}
}
}
This doesn’t work yet, because we don’t have anything other than an OnTriggerEnter statement. OnTriggerEnter is called every time either a game object passes through a trigger collider or this game object passes through a trigger collider. We set up an if statement to detect if the game object that passed through it has a certain tag. We are searching for a tag called “agent”. Set each agent’s tag to “agent”. Now we should start a counting system.
using UnityEngine;
public class ExitDoor : MonoBehavour
{
public int agents;
void OnTriggerEnter(Collider obj)
{
if (obj.gameObject.tag == “agent”)
{
agents += 1;
}
}
}
Now, we add 1 to a variable each time an agent enters the collider. The only problem with this is that if the agent goes through the collider twice, it will count it twice.
This system is done, but now you might want to access this from different scripts. We will use GetComponent<>() to access the script. Add this to your game management script, or whatever you want to be accessing this:
using UnityEngine;
public class GameManagement : MonoBehavour
{
public GameObject ExitDoor;
public int agents;
void Update()
{
agents = ExitDoor.GetComponent<ExitDoor>().agents;
if (agents == 10)
{
Debug.Log(“10 agents have exited.”);
}
}
}
Now, we access the script ExitDoor, and get agents from it. Make sure you set the ExitDoor variable from the game management script to the box collider game object used for counting.
This was untested and if you get any bugs or this wasn’t what you wanted, comment on this post to ask me.
Related
I am working on this 3d game were a player has to press E to interact with objects.
the player has a collider that when touching a type of trigger that has this type of code, makes an object pop up showing that the player's is "selecting" something.
when the player is in the trigger I made it were when they press E, the objects animation plays. When adding the objecting selecting thing it made it now that I cant make the animation play when pressing E
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Fixedpress : MonoBehaviour
{
public Animator Tributton;
public GameObject GM;
private Coroutine routine;
private void Start()
{
GM.SetActive(false);
}
private void OnTriggerStay(Collider other)
{
// in general rather use CompareTag instead of ==
// it is slightly faster and also shows an error if the tag doesn't exist instead of failing silent
if (!other.CompareTag("LookTrig")) {
GM.SetActive(true);
return;
}
// just in case to prevent concurrent routines
if (routine != null) StopCoroutine(routine);
// start a new Coroutine
routine = StartCoroutine(WaitForKeyPress());
}
private IEnumerator WaitForKeyPress()
{
// check each FRAME if the key goes down
// This is way more reliable as OnTriggerStay which is called
// in the physics loop and might skip some frames
// This also prevents from holding E while entering the trigger, it needs to go newly down
yield return new WaitUntil(() => Input.GetKeyDown(KeyCode.E));
// set the trigger once and finish the routine
// There is no way to trigger twice except exit the trigger and enter again now
Tributton.SetTrigger("Fiveyon");
Debug.Log("Fiveyon!");
// If you even want to prevent this from getting triggered ever again simply add
enabled = false;
// Now this can only be triggered ONCE for the entire lifecycle of this component
// (except you enable it from the outside again of course)
}
void OnTriggerExit(Collider other)
{
if (!other.CompareTag("LookTrig")) {
GM.SetActive(false);
return;
}
// when exiting the trigger stop the routine so later button press is not handled
if (routine != null)
{
StopCoroutine(routine);
GM.SetActive(false);
}
}
}
OnTrigger methods are called when another object with a Collider enters in your object Collider.
First, make sure everything was well set up well, and maybe you want to use OnCollision instead of OnTrigger methods, if you want to detect a collision whether than a "penetration" if i may say
I have a button that is supposed to switch beetween light and dark mode in my game by running the method "ToggleTheme" inside the ObjectTheme script, which all the objects that I want to be affected by light/dark mode have. ToggleTheme just changes the boolean "DarkMode", since all the objects' transitions use this DarkMode boolean. It all works fine if I just assign the objects and select ObjectTheme.ToggleTheme, but if I assign the objects' prefabs and select ObjectTheme.ToggleTheme I get the warning "Animation is not playing an AnimatorController" on button press. Is there any way around this, because assigning every object in every scene would just be to impractical and one of the objects has up to 30 copies in every level of the game?
P.S. I know It probably would have been easier if I just used a toggle instead of a button, but I'm new to Unity and I just couldn't get the toggle to work how I wanted it, so I'm using a button instead.
Here is the ObjectTheme script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectTheme : MonoBehaviour
{
public Animator animator;
// Start is called before the first frame update
void Start()
{
animator = GetComponent<Animator>();
}
// Update is called once per frame
public void ToggleTheme()
{
if(animator.GetBool("DarkMode") == true)
{
animator.SetBool("DarkMode", false);
}
else
{
animator.SetBool("DarkMode", true);
}
}
}
**Edited after Jonatan's comments below.
I understand the desire to just assign the prefab as the button's event target. But the prefab itself is in some sense also just an instance that is just not living in the scene. While in edit mode, all changes in the prefab itself will reflect in the scene instances. But when you are in play mode (runtime) the prefab instances in the scene will no longer automatically update themselves with changes in the prefab file.
In this case, we are trying to set a bool value on an Animator component, but the Animator on the prefab is not really playing - only the Animators on the scene instances are playing. That is why you get the 'not playing' warning.
One option to solve the issue could be something like the following.
First add a script to the button that has a function that can be hooked up with your button's OnClick() UnityEvent. The script will look for instances of another script, which is present on all the objects that should react to dark mode state. This other script could be your ObjectTheme script but here I call it DarkModeReceiver. When the button triggers the function, the script will simply call a function on all the script instances stored in its array.
//Put this script on the Button,
//and hook up the Button's OnClick event with the OnButtonClicked() function
using UnityEngine;
public class DarkModeHandler : MonoBehaviour
{
static bool isDarkMode;
public static bool IsDarkMode => isDarkmode;//Public get property
//Make your Button call this function in its OnClick() event
public void OnButtonClicked()
{
isDarkMode = !isDarkMode;//Toggle bool
SendIsDarkMode();
}
//Alternatively, if you choose to use a Toggle instead
//you could hook this function up with the Toggle's OnValueChanged(Boolean) event
//with the dynamic bool of that event.
public void OnToggleValueChanged(bool isToggledOn)
{
isDarkMode = isToggledOn;
SendIsDarkMode();
}
void SendIsDarkMode()
{
var darkModeReceivers = FindObjectsOfType<DarkModeReceiver>(true);
foreach (var receiver in darkModeReceivers)
{
receiver.SetIsDarkMode(isDarkMode);
}
}
}
And then the receiving script (attached on all the game objects / prefabs that should react to dark mode state) could be something like this (or a modified version of your ObjectTheme script).
using UnityEngine;
public class DarkModeReceiver : MonoBehaviour
{
Animator myAnimator;
void Awake()
{
myAnimator = GetComponent<Animator>();
}
void Start()
{
//Ensure that our state is in sync with the DarkModeHandler
SetIsDarkMode(DarkModeHandler.IsDarkMode);
}
public void SetIsDarkMode(bool isDarkMode)
{
myAnimator.SetBool("DarkMode", isDarkMode);
}
}
Alternatively, you could do something where the DarkModeReceivers/ObjectThemes register themselves on the DarkModeHandler on their Start() and unregister themselves again on their OnDestroy() - for example by subscribing to an event. Then the DarkModeHandler wouldn't have to look for receivers every time the button is clicked.
I am in the middle of creating a simple 3d platformer in unity and i'm trying to make it so if you are in a certain radius and you hit the "f" key the enemy will disappear. the way i'm checking to make sure the enemy is close enough to be attacked is to have trigger sphere and when its triggered and you press "f" the enemy will be removed. i'm currently trying to see what collided with the trigger but i cant figure it out. here is my current code
using UnityEngine;
public class PlayerCombat : MonoBehaviour
{
public GameObject Enemy1;
public GameObject Enemy2;
public GameObject Enemy3;
public GameObject Enemy4;
// Update is called once per frame
void OnTriggerEnter (Trigger triggerInfo)
{
if (triggerInfo.collider.tag == "Enemy" & Input.GetKey("f"))
{
if (triggerInfo.collider.name == Enemy)
{
Enemy1.SetActive(false);
}
}
}
}
The signature is and has always been OnTriggerEnter(Collider) otherwise that message method will not be recognized by Unity and not get called at all!
And then you already have the according Collider ... what else do you need?
public class PlayerCombat : MonoBehaviour
{
// There is no need to know the enemy references beforehand at all
// you will get all required references from the Collider parameter of OnTriggerEnter itself
// I personally would however expose these two settings to the Inspector to be more flexible
// this way you can adjust these two settings within Unity without having to touch your code
public string listenToTag = "Enemy";
public KeyCode listenToKey = KeyCode.F;
// The signature has to be this otherwise the message method is never invoked at all
private void OnTriggerEnter (Collider other)
{
// Rather use "CompareTag" instead of `==` since the latter will silently fail
// for typos and nonexistent tags while "CompareTag" shows an error which is good for your debugging
// And in general you want to use the logical "&&" instead of the bitwise operator "&" for bools
if (other.CompareTag(listenToTag) && Input.GetKey(listenToKey))
{
// simply set the object you collide with inactive
other.gameObject.SetActive(false);
}
}
}
Finally just make sure that all your enemy instances actually have the tag Enemy
First, I'm pretty sure OnTriggerEnter takes a Collider as its parameter, no? https://docs.unity3d.com/ScriptReference/Collider.OnTriggerEnter.html
You can get the name of the collided object like so
//Upon collision with another GameObject,
private void OnTriggerEnter(Collider other)
{
Debug.Log($"collided with {other.gameObject.name}");
//you can check for specific components too
MyComponent myComponent = other.GetComponent<MyComponent>();
if (myComponent != null) {
// do something if it has that component on it!
}
}
My player object has 4 children objects, called Pawn 1 through 4. When I click on one of them, it becomes selected. When a Pawn is selected, it should glow. Now, the trouble is that in order for glowing to happen properly, each Pawn has to know if it is selected at the moment, or not. I did that part by attaching a
public class PlayerController : MonoBehaviour {
public GameObject selectedObject;
}
to the Player object, and a script to each Pawn object that, among other things, does this
void Update()
{
if (transform.parent.gameObject.GetComponent<PlayerController>().selectedObject ==
gameObject)
{
Glow();
}
}
I can't help but to think that there has to be a better way to do this, as performing a GetComponent on every Update, on every Pawn, for every player seems incredibly wasteful.
Is there a way to get a reference to the selectedObject in Start(), so it keeps getting updated without manually getting it the whole time?
Is there a way to get a reference to the selectedObject in Start(), so
it keeps getting updated without manually getting it the whole time?
Cache PlayerController in the Start function.
private PlayerController playerController;
void Start()
{
playerController = transform.parent.gameObject.GetComponent<PlayerController>();
}
void Update()
{
if (playerController.selectedObject ==
gameObject)
{
Glow();
}
}
Why not have the Pawn handle the click interaction and store whether or not it is selected? Then you'd have something like:
if(IsSelected)
Glow();
This is semi complicated of a question but I'll do my best to explain it:
I am making this mobile game in which you have to shoot four cubes. I'm trying to make it so when the cubes are shot by a bullet, they're destroyed and a UI text says 1/4, to 4/4 whenever a cube is shot. But it's being really weird and only counts to 1/4 even when all four cubes are shot and destroyed. I put these two scripts on the bullets (I made two separate scripts to see if that would do anything, it didn't)
And to give a better idea of what I'm talking about, here's a screenshot of the game itself.
I've been using Unity for about 6 days, so I apologize for anything I say that's noob-ish.
EDIT
So I combined the two scripts onto an empty gameobject and here's the new script:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class GameManagerScript : MonoBehaviour {
public GameObject cubes;
public Text countText;
public int cubeCount;
public Transform target;
// Use this for initialization
void Start () {
}
void OnTriggerEnter(Collider other)
{
cubes = other.gameObject;
}
// Update is called once per frame
void Update () {
cubes.transform.position = Vector3.MoveTowards(transform.position, target.position, 1f * Time.deltaTime);
if (cubes.gameObject.tag == "BULLET")
{
cubeCount = cubeCount + 1;
countText.text = cubeCount + "/4";
cubes.SetActive(false);
}
}
}
ANOTHER EDIT
I tried everything, so is there a way to detect when all the children in a parent on the Hierarchy are destroyed? Instead of counting up? This can give a better idea:
So I want to be able to detect when Cube, Cube1, Cube2, and Cube3 have all been destroyed.
The answer is pretty simple: Since every individual bullet has that script, each bullet has its own score.
For something like a score you want a single spot to store it, e.g. a script on an empty gameobject that serves as game controller. Just access that in the collision and increase the score (maybe have a look on singletons here).
You can combine those two scripts and actually it might be better to not have this on the bullet, but on the target because there are probably less of them which will save you some performance. (And it does more sense from a logical point of view.)
Edit:
I assume you create the bullets using Instantiate with a prefab. A prefab (= blueprint) is not actually in the game (only objects that are in the scene/hierarchy are in the game). Every use of Instantiate will create a new instance of that prefab with it's own version of components. A singleton is a thing that can only exist once, but also and that is why I mention it here, you can access it without something like Find. It is some sort of static. And an empty gameobject is just an object without visuals. You can easily create one in unity (rightclick > create empty). They are typically used as container and scriptholders.
Edit:
What you want is:
An empty gameobject with a script which holds the score.
A script that detects the collision using OnTriggerEnter and this script will either be on the bullets or on the targets.
Now, this is just a very quick example and can be optimized, but I hope this will give you an idea.
The script for the score, to be placed on an empty gameobject:
public class ScoreManager : MonoBehaviour
{
public Text scoreText; // the text object that displays the score, populate e.g. via inspector
private int score;
public void IncrementScore()
{
score++;
scoreText.text = score.ToString();
}
}
The collision script as bullet version:
public class Bullet : MonoBehaviour
{
private ScoreManager scoreManager;
private void Start()
{
scoreManager = GameObject.FindWithTag("GameManager").GetComponent<ScoreManager>(); // give the score manager empty gameobject that tag
}
private void OnTriggerEnter(Collider other)
{
if(other.CompareTag("Target") == true)
{
// update score
scoreManager.IncrementScore();
// handle target, in this example it's just destroyed
Destroy(other.gameObject);
}
}
}