I'm very new to unity and I'm making a simple tag game with car like players. However, I'm having problems with my trigger collider. When I tag the other car it kind of flickers the is tagged true/false. Here is my code.
public Color TaggedColor;
public Color NoTaggedColor;
public bool Tagged;
// Start is called before the first frame update
void Start()
{
Tagged = true;
GetComponent<Image>().color = TaggedColor;
}
// Update is called once per frame
void Update()
{
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "P2")
{
if (Tagged == false)
{
Tagged = true;
GetComponent<Image>().color = TaggedColor;
}
else
{
if (Tagged == true)
{
Tagged = false;
GetComponent<Image>().color = NoTaggedColor;
}
}
Debug.Log("TAG!");
}
}
void GotTagged()
{
if (Tagged == false)
{
Tagged = true;
//GetComponent<Image>().color = TaggedColor;
}
}
void TaggedOtherPlayer()
{
if (Tagged == true)
{
Tagged = false;
GetComponent<Image>().color = NoTaggedColor;
}
}
I have a Second code for P2 that the only change is the start and the tag is P1. I'm not sure if this problem is something in my code or if it's the collider it's self. If anyone has any idea why this is happening I would love to hear it!
Update: I bevile the issue is coming from the Colliders because I have an empty as a child of both players that is the trigger collider. However when I tag one the other trigger goes off as well. Should I have only one trigger? And if so how would I go about doing it?
Thanks
once you tag a player SetActive(false) the empty triggers for about 1 second or so. If you had a button that whenever its pressed it changes s value from true to false the false to true if you press and hold that button it will "flicker" back and forth between true and false. Thats what's happening with your players the trigger is constantly being pulled while you're touching so make it inactive for long enough for you to not be touching anymore. I hope that makes sense.
Related
I'm making a Balloon Fight style game and I'm having trouble with object collision. Each character has two balloons on top of his head and each balloon has an on trigger Box Collider. I want to make it so only one balloon can be hit at a time so you can't destroy both balloons at the same time. In order to do this I added a boolean called isAttacking to prevent it from destroying more than one balloon at the same time.
Hello, I'm making a Balloon Fight style game and I'm having trouble with object collision. Each character has two balloons on top of his head and each balloon has an on trigger Box Collider. I want to make it so only one balloon can be hit at a time so you can't destroy both balloons at the same time. In order to do this I added a boolean called isAttacking to prevent it from destroying more than one balloon at the same time.
public bool isAttacking = false;
private void OnTriggerEnter(Collider collision)
{
if (collision.GetComponent<Collider>().gameObject.layer == 7 && collision.GetComponent<Collider>().gameObject.tag != this.gameObject.tag)
{
if (!isAttacking)
{
Destroy(collision.GetComponent<Collider>().transform.parent.gameObject);
transform.parent.gameObject.GetComponent<Jump>().jump = true;
isAttacking = true;
}
}
}
void LateUpdate()
{
if (isAttacking)
{
isAttacking = false;
}
}
While it does prevent two collisions from registering I still found this solution to be insufficient, since the balloon that is destroyed is not necessarily the one closest to the character destroying it. How could I improve the collision code in order for it to only register the collision happening closer to the character?
Within one frame afaik there is no reliable order of OnTriggerEnter calls (it is somewhat based on the instanceID of objects but that won't really help you).
What you could do instead would be comparing distances, somewhat like e.g.
private readonly HashSet<GameObject> hittingObjects = new();
[SerializeField] private Jump jump;
private void Awake()
{
if(!jump) jump = GetComponentInParent<Jump>(true);
}
private void OnTriggerEnter(Collider collision)
{
if (collision.layer == 7 && !collision.CompareTag(gameObject.tag))
{
hittingObjects.Add(collision.transform.parent.gameObject);
}
}
private void LateUpdate()
{
if (hittingObjects.Count > 0)
{
var closestHit = hittingObjects.OrderBy(hit => (transform.posiion - hit.transform.position).sqrMagnitude).First();
Destroy(closestHit);
jump.jump = true;
hittingObjects.Clear();
}
}
Note: This still doesn't prevent this object from colliding with the other balloon in the very next physics update. If you wanted to track this as well you could make it slightly more complex and only allow collisions if you are newly entering the trigger => You have to exit the object again before you can hit it again.
Somewhat like maybe
private readonly HashSet<GameObject> hastoExitFirstObjects = new();
private readonly HashSet<GameObject> newHittingObjects = new();
[SerializeField] private Jump jump;
private void Awake()
{
if(!jump) jump = GetComponentInParent<Jump>(true);
}
private void OnTriggerEnter(Collider collision)
{
if (collision.layer == 7 && !collision.CompareTag(gameObject.tag))
{
var hit = collision.transform.parent.gameObject;
if(!hastoExitFirstObjects.Contains(hit))
{
newHittingObjects.Add();
}
}
}
private void OnTriggerExit(Collider collision)
{
if (collision.layer == 7 && !collision.CompareTag(gameObject.tag))
{
var hit = collision.transform.parent.gameObject;
hastoExitFirstObjects.Remove(hit);
}
}
private void LateUpdate()
{
if (newHittingObjects.Count > 0)
{
var closestHit = newHittingObjects.OrderBy(hit => (transform.posiion - hit.transform.position).sqrMagnitude).First();
newHittingObjects.Remove(closestHit);
Destroy(closestHit);
jump.jump = true;
foreach(var hit in newHittingObjects)
{
hastoExitFirstObjects.Add(hit);
}
newHittingObjects.Clear();
}
}
First, check if your OnTriggerEnter is executing for both balloons. It probably is?
Then in the moment OnTriggerEnter executes, compare the position of collision with the position of your balloons and see which one is the closest, then destroy the closest balloon and maybe set a variable isInvulnerable as true, so that if it is true, no balloon can be destroyed inside OnTriggerEnter.
I'd just enable only one balloons box collider at once, and when it gets popped enable the other balloon after 1 second delay.
Anyone know how i can stop this from flicking basically I'm doing a raycast and if it hits the crafting table it it shows up a text saying press e to craft but it is flickering cause its in the void update function but if anyone knows a work around that would be nice thanks!
if(hit.collider.tag == "CraftingTable")
{
if(textIsOn)
{
noshowcraftingtext();
}
else
{
showcraftingtext();
}
}
void showcraftingtext()
{
textIsOn = true;
pressEToShowCraftingTableUI.SetActive(true);
}
void noshowcraftingtext()
{
textIsOn = false;
pressEToShowCraftingTableUI.SetActive(false);
}
The main problem of your code is that you are trying to change the state after state was changed.
That's why updated isn't the case in a big project, because all such things will lead to unpredictable or logically conflicted behavior, to avoid this you should use Data driven approach instead.
But to fix exactly your issue, you need to have a function, which isn't based on the state of the object, but returns what should happen right now.
There isn't much about what's going on inside your game, but I can guess that you are firing a raycast with mouse and then checking is it craftable or not.
public void Raycaster : MonoBehavior {
public void YourClickMethod()
{
if(hit.collider.tag == "CraftingTable")
{
hit.collider.GetComponent<UserInputReceiver>().SetClicked(this);
}
}
}
And on the object which is receiving your raycast you should add this:
public class UserInputReceiver : MonoBehavior {
private bool _isEnabled = false;
//set this inside inspector
public GameObject ObjectToEnable;
private Raycaster _currentSender = null
public void SetClicked(Raycaster sender){
_isEnabled = !_isEnabled;
_currentSender = sender;
}
public void Update(){
if(_currentSender != null && _isEnabled) {
_isEnabled = Vector3.Distance(transform.position, _currentSender.transform.position) < 1f; //set the threshold based on your unit system
}
//or another object, you can put it inside public field and enable him
//gameObject.SetActive(_isEnabled);
objectToEnable.SetActive(_isEnabled); //here you can pass a reference through inspector for your canvas
}
}
This is how you can outstand this issue with a short way.
This code will set the flag based on the received input and the distance. If you need to keep enabled text forever, than remove distance check than it will enable only with a second click, without distance dependency.
I need a way to check all scripts if boolean is true and then a way in this script to see in the if statement of the current light the character is standing next to has the boolean true to activate and only then should the Instantiate function be triggered.
private bool Once = true;
public Transform Spawnpoint1;
public Transform Spawnpoint2;
public GameObject Prefab1;
public GameObject Prefab2;
//Something like this, but I don't know where to go after that
GameObject[] Lights = GameObject.FindGameObjectsWithTag("lightSwitch");
//foreach(GameObject Light in Lights)
void OnTriggerEnter2D(Collider2D collision)
{
if (Once == true)
{
Debug.Log("It's true");
if (LightOnOff.isON == true) // It needs to check this constantly
{
Debug.Log(" It's Lit");
Instantiate(Prefab1, Spawnpoint1.position, Spawnpoint1.rotation);
Instantiate(Prefab2, Spawnpoint2.position, Spawnpoint2.rotation);
Once = false;
}
}
}
here is the Light script as well
public static bool isON;
public void lightOn()
{
this.GetComponent<Light>().enabled = true;
isON = true;
}
First of all you shouldn't use static
public static bool isON;
for an individual value! static makes the value a "non instanced" value, meaning it belongs to the entire class instead of instances => To say it in simple words this variable is shared between all your components (see this post for more information). Instead use
public bool isON;
Than access the insteance variable of the component like
Update: From the comments I now know that actually the components are not on the collider object but rather on a child of the object this script is attached to
void OnTriggerEnter2D(Collider2D collision)
{
// Update. TODO check if the correct Object collided
//if(!collision.gameObject == <your player>)
if (!Once) return;
// Update this now searches in its own children instead of children of collision
var lightOnOff = GetComponentInChildren<LightOnOff>(true);
if(!lightOnOff)
{
Debug.Log("No LightOnOff found on collision" + collision.name, this);
return;
}
Debug.Log("It's true");
if (!LightOnOff.isON) return;
Debug.Log(" It's Lit");
Instantiate(Prefab1, Spawnpoint1.position, Spawnpoint1.rotation);
Instantiate(Prefab2, Spawnpoint2.position, Spawnpoint2.rotation);
Once = false;
}
But instead of using your LightOnOff component you could also simply acces the Light component and do something like
Update: From the comments I now know that actually the components are not on the collider object but rather on a child of the object this script is attached to
void OnTriggerEnter2D(Collider2D collision)
{
if (!Once) return;
// directly access the Light
// Update this now searches in its own children instead of children of collision
var light = GetComponentInChildren<Light>(true);
if(!light)
{
Debug.Log("No Light found on collision" + collision.name, this);
return;
}
Debug.Log("It's true");
// Directly check if the Light component is enabled
if (!light.enabled) return;
Debug.Log(" It's Lit");
Instantiate(Prefab1, Spawnpoint1.position, Spawnpoint1.rotation);
Instantiate(Prefab2, Spawnpoint2.position, Spawnpoint2.rotation);
Once = false;
}
You don't need to keep track of all the lights just for that.
Something you do need to change is that you should make isON not static. This is because an actual light might be on or not, not that the concept of lights are on or not.
public bool isON;
Check the Collider2D for the associated object you're colliding with, and look for a light there. The following code assumes any light will be on the same GameObject as a Trigger or one of its children.
void OnCollisionEnter2D(Collider2D col) {
// only activate once
if (once) {
// Get light if exists
GameObject collidedObject = col.gameObject;
Light light = collidedObject.GetComponentInChildren<Light>();
if (light != null) {
// We have a light, check if it's on. We only care about the collided light
if (light.isON) {
Debug.Log("It's Lit fam");
Instantiate(Prefab1, Spawnpoint1.position, Spawnpoint1.rotation);
Instantiate(Prefab2, Spawnpoint2.position, Spawnpoint2.rotation);
// Note that if we run into another lit light nothing will happen,
// even if its the first time running into that particular light
Once = false;
}
}
}
}
Also, you can just use if(something) instead of if(something == true)
Currently I'm simply trying to change the sprites candle from unlit to lit when the player has 'picked up' both the candle and the matches and the candle will 'go out' after a certain amount of time. However, when the space bar is pressed the transition from unlit to lit isn't occurring, even though the debug log is returning true when it should. I'm posting here to get some guidance as I have spent most of the day looking online and literally have no idea how to proceed.
Basically the images I am trying to transition between are two different images which are in the sprites folder under assets.
This is what I've got so far.
//the two sprites transition
public Sprite unlitCandle;
public Sprite litCandle;
private SpriteRenderer spriteRenderer;
bool pickUpMatches = false;
bool pickUpCandle = false;
float timeRemaining =5;
bool candleLit = false;
// Use this for initialization
void Start () {
spriteRenderer = GetComponent<SpriteRenderer>();
if (spriteRenderer.sprite == null)
spriteRenderer.sprite = unlitCandle;
}
// Update is called once per frame
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.CompareTag("Matches"))
{
collision.gameObject.SetActive(false);
pickUpMatches = true;
}
if (collision.gameObject.CompareTag("UnlitCandle"))
{
collision.gameObject.SetActive(true);
pickUpCandle = true;
}
}
public void CandleTimer()
{
if (candleLit == true)
{
timeRemaining = 5;
timeRemaining -= Time.deltaTime;
if (timeRemaining <= 0)
{
candleLit = false;
spriteRenderer.sprite = unlitCandle;
}
}
}
public void ChangeSprite()
{
if (spriteRenderer.sprite == unlitCandle)
{
spriteRenderer.sprite = litCandle;
}
}
void Update () {
if (pickUpCandle == true && pickUpMatches == true)
{
//Debug.Log(candleLit);
if (Input.GetKey(KeyCode.Space) && !candleLit)
{
CandleTimer();
ChangeSprite();
Debug.Log(timeRemaining);
candleLit = true;
//Debug.Log(candleLit);
}
}
}
}
Try comparing with a method like equals() instead of == in
spriteRenderer.sprite == unlitCandle
Because right now you are just comparing references and not the objects.
At least I think thats the problem.
There are a few possible issues with your code. First, you are calling changeSprite at the top of Update, which means that it is unconditionally being called every frame. Therefore, after a single frame of your candle being unlit, it will immediately change its sprite to litCandle.
I assume that the reason you are calling changeSprite every frame is in order to process the timer if you have a lit candle already. Really, you should move the code to process the timer (your whole second if statement in changeSprite) to a separate function and name it something like processCandleTimer. Call that at the top of Update and save the changeSprite method to only be called on the keypress.
Lastly, the issue that I suspect is giving you the most trouble is that you aren't resetting your timer, timeRemaining. The first time you light the candle the timer will go down to 0 after the 5 seconds pass. Every time changeSprite is run after that, you will change the sprite to litCandle in the first if statement and then immediately change it back to unlitCandle because the timer is 0 in the second. To remedy this, you need to add a line like timeRemaining = 5.0f; when the key is hit.
I have animation Clip for swinging sword. At a specific frame, I added Event. I want when player swing sword, in that case only the enemy could die.
So I added the following OnTriggerEnter Code
void OnTriggerEnter(Collider col)
{
hit = true;
if (hit)
{
if (col.GetComponent<Collider>().tag == "enemy")
{
Destroy(col.gameObject);
}
}
}
When I try to add function OnTriggerEnter (in animation Clip) as animation Event, it is asking me to pass Collider parameter, Which i am not able to add.
Here is the screen shot of Add Event
Please help me , how can I add event with collider ( as parameter ) at a specific frame.. Thanks
Collision and Trigger events are called on their own on every frame.
Call another public method from animation event for your task and put necessary Boolean or Enum there to control collision, trigger after swinging the sword.
public void SwingingSword()
{
isSwingingSword = true; // make it false when not swinging the sword.
}
void OnTriggerEnter(Collider col)
{
hit = true; // not sure what it's job here
if (isSwingingSword && hit)
{
if (col.GetComponent<Collider>().tag == "enemy")
{
Destroy(col.gameObject);
}
}
}