BoxCollider2D IsTrigger through the Script

I want to change BoxCollider2D attached to the Bricks from isTrigger= false to isTrigger=true during the run time
so i tried this

brick.GetComponent<BoxCollider2D> ().isTrigger = true;

there is many bricks in the scene but i noticed during the execution of the code that its only set isTrigger to true on one brick only not all the bricks

take into your considerations that all the bricks got the same components and the same brick script


edit
is that something related to using FindObjectOfType
brick=GameObject.FindObjectOfType<BrickPc>();
and it should be
brick=GameObject.FindObjectsOfType<BrickPc>();
to get ll the bricks objects so when changing the behavior of the box collider it change it in all the bricks

1 Like

Hi @Kamal_Ezz-eldin,

From which script are you calling your code from, I’m assuming not the Brick itself but instead another script responsible for managing your game state?

GameObject.FindObjectOfType<>() returns only the first, active, object of the specified type.

GameObject.FindObjectsOfType<>() returns all of the active objects of the specified type.

In both instances, you have to check through every game object in order to find the one(s) you are interested in. You may consider a design change here to afford some performance improvements for your game also, but to help with the initial problem at hand…

Once you have your list of objects that met the specified type you will need to iterate through them, you could achieve that like so;

GameObject[] bricks;    // declare an array called "bricks" of type GameObject

bricks = (GameObject)GameObject.FindObjectsOfType<Brick>();    // cast the return array as type GameObject

foreach(GameObject brick in bricks)    // iterate
{
    brick.GetComponent<BoxCollider2D>().isTrigger = true;    // set isTrigger
}

I don’t think you need to do any further casting of types in the above, but this is off the top of my head, so I’ve not had a chance to test before posting.


See also;

i followed this code but in the unity it shows an error in this line i quoted above it says

Cannot convert type BrickPc[]' toUnityEngine.GameObject’
is that because an error in this line itself or other code has an effect and causing this !

It throws the error because your array type is of BrickPC, not GameObject.

Can you post your code?

OK here is the Brick script i did some tidy up hopefully you dont’t get confused

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;

public class BrickPc : MonoBehaviour {

	public static float BreakableCounter = 0;
	private int hits  ;
	private PcLevelManager pcLevelManager;
	public Sprite[] HitSprites;
	private bool BrickDestroyed;
	bool IsItBreakableTag ;
	public AudioClip brickCrack;
	public GameObject Smoke;
	public GameObject[] bricks;

	// Use this for initialization

	void Start () {

		pcLevelManager=GameObject.FindObjectOfType<PcLevelManager> ();
		bricks =(GameObject) GameObject.FindObjectsOfType<BrickPc> ();

		//let the counter detect the bricks with different tags
		IsItBreakableTag = (this.tag == "Breakable");
		if (IsItBreakableTag) {
			BreakableCounter++;
			//print ("Breakable bricks = "+BreakableCounter);
		}
		hits = 0;
	}

	// Update is called once per frame
	void Update () {
	}

	//to Destroy the brick if its Box collider trigger is checked
	void OnTriggerEnter2D(Collider2D x){
		if (x.gameObject.tag == "Ball") {
			HandleBricks ();
		}
	}
	void OnCollisionEnter2D(Collision2D z)
	{

		if ( z.gameObject.tag=="Ball") {
			AudioSource sfx = GetComponent<AudioSource> ();
			sfx.Play ();
			if (IsItBreakableTag) {
				HandleBricks ();

			}
		}
	}

	void HandleBricks(){
		hits++;
		int maxHits = HitSprites.Length + 1;	
		if (hits >= maxHits) {
			BreakableCounter--;
			//print (BreakableCounter);
			GameObject SmokePuff = Instantiate (Smoke, transform.position, Quaternion.identity) as GameObject;
			//TODO the following line of code is obselte need some fix
			SmokePuff.GetComponent<ParticleSystem> ().startColor = gameObject.GetComponent<SpriteRenderer> ().color;
			Destroy (gameObject);
			AudioSource.PlayClipAtPoint (brickCrack, transform.position, 0.8f);
			IsItBreakableTag = (this.tag == "Breakable" );
			if (BreakableCounter <= 0) {
				Win ();
			}

		} else {
			LoadSprites ();
		}
	}
		
	void LoadSprites()
	{ 
		int SpriteIndex = hits - 1;
		if (HitSprites [SpriteIndex]) {
			this.GetComponent<SpriteRenderer> ().sprite = HitSprites [SpriteIndex];
		}
	}

	//TODO detect win and go to next level
	void Win()
	{ 
		pcLevelManager.LoadNextScene ();

	}

}

and here is the Paddle Script which i use to call the Bricks BoxColliders2D component and turning the trigger ON
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PaddlePc : MonoBehaviour {

	private BrickPc brick;
	private BallPc ballPc;
	public bool autoPlay = false; 
	Vector3 PaddlePos;


	void Start() {
		ballPc=GameObject.FindObjectOfType<BallPc>();
		brick=GameObject.FindObjectOfType<BrickPc>();

	}
	void Update(){

		if (autoPlay==false) {
			moveWithMouse ();
		}
		else{
			AutoPlay ();
		}
	}
	void moveWithMouse()
	{
		PaddlePos = new Vector3 (0f, this.transform.position.y, 0f);	
		float mousePosInBlocks = Input.mousePosition.x / Screen.width * 8;
		//	print (mousePosInBlocks);
		PaddlePos.x = Mathf.Clamp (mousePosInBlocks,0.65f, 7.65f);
		this.transform.position = PaddlePos;
	}
	void AutoPlay ()
	{
		PaddlePos = new Vector3 (0f, this.transform.position.y, 0f);
		Vector3 ballPos =ballPc.transform.position;
		PaddlePos.x=Mathf.Clamp (ballPos.x, 0.65f, 7.65f);
		this.transform.position = PaddlePos;
	}
	void OnCollisionEnter2D(Collision2D col ) {
		if (tag == "NoSfx") {

		} else {
			if(ballPc.started==true){
				AudioSource sfx = GetComponent<AudioSource> ();
				sfx.Play ();}
		}

	}
	void OnTriggerEnter2D(Collider2D x) 
	{
		
		if (x.gameObject.tag == "Fire Ball") {
			StartCoroutine (FireBall ());
			Destroy (x.gameObject);
		}
	}

	IEnumerator FireBall(){
	
		LoadSpriteFire ();

		foreach(GameObject brick in brick.bricks)
		{
			brick.GetComponent<BoxCollider2D>().isTrigger=true;
		}

		yield return new WaitForSeconds (30);

		LoadSpriteBLue();	

		foreach(GameObject brick in brick.bricks)
		{
			brick.GetComponent<BoxCollider2D>().isTrigger=false;
		}

		// (Not Working) brick.GetComponent<BoxCollider2D> ().isTrigger =false;


	}
	void LoadSpriteFire()
	{ 
		int SpriteIndex = 0;

		if (ballPc.ballSprite [0]) {
			if (ballPc != null) {
				ballPc.GetComponent<SpriteRenderer> ().sprite = ballPc.ballSprite [SpriteIndex];
			}
		}

	}
	void LoadSpriteBLue()
	{ 
		int SpriteIndex = 1;

		if (ballPc.ballSprite [SpriteIndex]) {
			if (ballPc != null) {
				ballPc.GetComponent<SpriteRenderer> ().sprite = ballPc.ballSprite [SpriteIndex];
			}


		}
	}
}

Iam sorry if my scripts looks messy i very beginner

Hiya,

I am sorry if my scripts looks messy I very beginner

Don’t apologise, no need - just keep moving forwards and enjoy what you are doing :slight_smile:


Some observations…

  • BrickPc - it has an array of GameObjects called bricks

  • bricks is initialised in the Start() method by finding all of the BrickPc game objects

  • The bricks array is only in use within the Fireball() method within PaddlePc class


Consider this…

If you have 200 bricks in your scene, each brick instance will hold and array of 200 bricks…

…we want to be changing that :slight_smile:


I personally wouldn’t choose to keep the array maintaining the collection of bricks actually in a class called brick, or in this case, BrickPc, instead I’d probably place it in some form of level manager / game controller class - but, let’s not worry about that just yet, we can refine again and again etc.

What we do need to worry about though is your 200 bricks storing arrays holding 200 bricks!

You may remember that when you added the BreakableCounter variable you defined it as static. The reason for this was because by doing so it would be a member of the class rather than the instance of the class, it, therefore, exists only once.


Let’s do the same with the bricks array in the BrickPc class, we will also change the type to BrickPc from GameObject (that was my mistake, sorry), change this line;

public GameObject[] bricks;

…to this;

public static BrickPc[] bricks;

We also need to change the Start() method just slightly, as otherwise, whilst we only have one instance of the array, we will continue to try to initialise it 200 times…

In the Start() method within the BrickPc class change this line;

bricks = (GameObject)GameObject.FindObjectsOfType<BrickPc>();

…to this;

// check whether we have already initialised our bricks array
if(bricks == null || bricks.Count <=0)
{
    bricks = (BrickPc[])GameObject.FindObjectsOfType<BrickPc>();
}

Let’s now turn our attention to PaddlePc. The FireBall() method iterates through the array of bricks using this line;

foreach(GameObject brick in brick.bricks)

…because we have made the bricks array static, we need to refer to the class not the instance of the class for our iteration. We also need to change the type back from GameObject to BrickPc. Change the above line to this;

foreach(BrickPc brick in BrickPc.bricks)

Note, you need to do this twice as you have the same line again after the yield.


We are getting close now I believe, this is obviously all untested from my end, but there is one more thing I can think of that may cause you a problem.

With the above changes, you get the array of bricks once and maintain it in the class via the static array. When the player plays the game, some of the individual bricks are going to, presumably, get destroyed. When your player uses the FireBall mechanic, we will iterate through the array collection and try to set the isTrigger property for each of the game object’s BoxCollider2D components.

The problem is, some of those game objects won’t exist anymore.

You could potentially test this before making further changes, but what I would expect is for your to get some form of NullReferenceException error when there is an attempt to change the value for isTrigger on the destroyed brick.

Arrays are going to be a bit problematic as there isn’t a nice way to remove items from them, what we can use instead are generic Lists. A further problem is going to be how we actually identify which game object we need to remove from the list.

I have an idea…


Firstly, let’s change the brick array to a list in the BrickPc class.

Change the following line;

public static BrickPc[] bricks;

…to;

public static List<BrickPc> bricks;    // generic list of type BrickPc

We also need to update the Start() method again where you retrieve the array of BrickPc game objects, so change;

// check whether we have already initialised our bricks array
if(bricks == null || bricks.Count <=0)
{
    bricks = (BrickPc[])GameObject.FindObjectsOfType<BrickPc>();
}

…to;

// check whether we have already initialised our bricks list
if(bricks == null || bricks.Count <= 0)
{
	// written long-hand to make it easier to read
	BrickPc[] tempBricks = (BrickPc[])GameObject.FindObjectsOfType<BrickPc>();    // gets the BrickPc game objects as an array
	bricks = new List<BrickPc>(tempBricks);    // initialise our list from an array
}

Now we need to consider what happens when a brick is destroyed, for that, let’s look at the HandleHits() method in the BrickPc class.

Before we destroy the game object, we want to remove it from our list, add the following line;

// remove current instance of the BrickPc from the list
bricks.Remove(this);

…before the Destroy(gameObject) statement.


A couple of final thoughts…

In the same way that the original game reset the breakableCount variable before loading the next level, it would be worth clearing the bricks list before loading the next level. Because bricks is static it will retain it’s value throughout it’s lifetime (until you exit the game).

You can clear the list using the following;

bricks.Clear();

If you have any other features which create bricks, or destroy bricks, you will need to factor this in also, the list needs to reflect, accurately, the bricks that are active in the scene.

Lastly, the above is untested, I think it should be ok, but obviously I don’t have the full project to give it a test.

I hope this helps. :slight_smile:

1 Like

@Rob Yes It fixed the problem
As you expected

So i changed the brick array to List as you mentioned
and it worked fine
just there was a small syntax error in the array solution which is

there is no count with array , so i used bricks.length
and of course that was before get rid of the array and replace it with the list
bricks.count worked fine with the list

Thank you , you are so helpful :bouquet:

1 Like

Hi @Kamal_Ezz-eldin,

Ah, sorry for that typo regarding .Count, it was some what of a late night putting all of that together for you and I was trying to write some of it outside of the forum and then pasting bits in… hoping that once it was altogether it would be helpful and not too overwhelming. Good spot and well done on fixing my mistake! :slight_smile:

Really pleased this has helped and of course you are more than welcome :slight_smile:

1 Like

Hello, I am doing the same thing as this poster, and am using an overall level manager. Everything seems well until the part where you say Remove(this) before block destruction, as there isn’t a connection between the block destroyed and the level manager. I’ve tried to experience but haven’t figured it out yet.

Hi @Amber_Rose,

The last answer in this thread was posted 3 years ago, so it is relatively unlikely that you will get an answer by the people who participated in the discussion. I don’t know why the thread has not been closed yet but I’ve set a timer now. The best would be to create your own thread and to share as much relevant information on your project as possible.

This topic was automatically closed after 2 days. New replies are no longer allowed.

Privacy & Terms