How to detect Collision on specific Object? - c#

Let's suppose we have two cylinders in the scenes with red and blue materials on them. Also, we have two UI images with red and blue background. Now, what should I do to make the red image only draggable onto red cylinder and the blue image only draggable onto blue cylinder.
If I drag the red image onto the blue cylinder, then an error message should appear:
same for dragging blue image on red cylinder or vice versa.
See the picture below
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class wrench : MonoBehaviour {
public bool Dragging = false;
public bool collision = false;
Vector3 position;
public List<GameObject> UIimages;
public void BeginDrag() {
position = gameObject.transform.position;
Dragging = true;
foreach (GameObject obj in UIimages) {
obj.GetComponent<BoxCollider2D> ().enabled = false;
}
}
public void Drag() {
transform.position = Input.mousePosition;
}
public void Drop() {
if (!collision) {
gameObject.transform.position = position;
}
Dragging = false;
}
}

Assuming your dropps are already detected as you say and the question is more about how to differ between the objects I would use a simple enum e.g.
public enum WhichObject
{
red,
blue
}
the advantage is that later you can very easily add more options without having to deal with layers, tags etc.
On the GameObject you drag around add a component e.g.
public class ObjectInformation
{
public WhichObject IsObject;
}
and simply select in the Inspector (or if you spawn them via script) for each of your dragged objects which value the WhichObject variable shall have.
Than on your target objects where you detect the collision also add a WhichObject variable but this time configure the value you expect to be dropped here
public WhichObject ExpectedObject;
again set it via the inspector or script.
Than simply add a check
var info = droppedObject.GetComponent<ObjectInformation>();
// Something else dropped?
if(!info) return;
// Now check if the value matches
// the one this object expects
if(info.IsObject == ExpectedObject)
{
// Correct object
Debug.Log("Yeah!");
}
else
{
// Wrong object
Debug.LogFormat("Dropped object {0} does not match the expected object {1}!", info.IsObject, ExpectedObject);
}
Later you can also simply extend it using multiple enums like e.g. colors, forms etc.
And maybe you could as well make the check the other way round and check on the dragged object, if the values match and if the don't match not even allow to be dropped on that target object (this depends of course on your implementation).

Related

Changing Tag of Collided Object Through Collision

Either the script is outdated, or it's not what I need, but I cannot find an answer to this.
To start off, I'm making a pinball styled game, whenever the ball hits a piece, it changes color, but I have multiple colored balls, and I want to lock the color in place as to not have the other balls change them (to make the game a little bit easier). I've provided a script, which may be a little too complex for a simple solution. The problem area is at the bottom with void FixedUpdate.
(I just wanna change a tag ): )
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ColorBlue : MonoBehaviour
{
public Material mat;
public string ballTag;
public bool reset = false;
public bool found = false;
public void OnCollisionEnter (Collision collisionInfo)
{
if (collisionInfo.collider.tag == "Ball" )
{
gameObject.GetComponent<MeshRenderer>().material.color = Color.blue;
reset = true;
}
}
public void FixedUpdate ()
{
if(reset)
{
GameObject.FindWithTag("Ball");
} found = true;
if(found)
{
GameObject.FindWithTag("Ball").tag = "Untagged";
}
}
}
Instead of searching for the GameObject with the "Ball" Tag which you probably have multiple off in your scene. You can rather just change the tag directly when the collision happens.
Because in the OnCollisionEnter Function you have a reference to the gameObject already, you can just use that to change the tag with collisionInfo.gameObject.tag = "Untagged".
public void OnCollisionEnter (Collision collisionInfo) {
if (collisionInfo.gameObject.CompareTag("Ball") && gameObject.CompareTag("Ball")) {
// Get Mesh Renderer of the Colided Ball Component.
var meshRenderer = collsionInfo.gameObject
.GetComponent<MeshRenderer>();
// Change Color of collided ball to be the same as the ball it collided with.
meshRenderer.material.color = gameObject
.GetComponent<MeshRenderer>().material.color;
// Set Tag to "Untagged" to make sure ball won't change color anymore
colliderInfo.gameObject.tag = "Untagged";
}
}
You could also add additional Code to change the color depending on the current Color of the gameObject. Additionaly I would advise you to use CompareTag(), which checks if the tag even exists in your scene.
If you want to get the collided gameObject you can do that with collisionInfo.gameObject.tag

GameObject is finded as active when it's not active in the hierarchy

So I build a custom class named Obstacles
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Obstacle{
public GameObject gameObj;
public Color color;
public GameObject[] particleEffects;
public static Vector3 SpawnLocation()
{
int positionQuadran = Random.Range(1, 3);
switch (positionQuadran)
{
//spawn above the player
case 1:
return new Vector3(Random.Range(1.5f, -1.5f),
Random.Range(4f - SpawnStars.closerToPlayer, 4.5f),
Random.Range(1, -3.2f));
//spawn benith the player
case 2:
return new Vector3(Random.Range(1.5f, -1.5f),
Random.Range(-0.5f, SpawnStars.closerToPlayer),
Random.Range(1f, -3.2f));
}
return Vector3.zero;
}
}
Next I created a pool with 6 objects, every two objects with the same color, so there are 3 distinct colors in the obstaclePool
The problem is that I am trying to find the inactive Obstacle, it works for the first Objects that have a color, but when I ended all the colors and iterate again through the loop it says that the Obstacle.gameObj is active in the hierarchy, I don't understand, every Obstacle.gameObj is inactive (cuz I set them to be inactive in the start() so every Obstacle should have an inactive Obstacle.gameObj BUT after I activate the first Obstacle.gameObj the next ones seem to not exist, the loop "finds" only the gameObj that are active and assumes that EVERY Object in the list is now active, when actually only the first 3 are active, the remaining 3 are NOT active...And I don't understand why...Why doesn't the code find the remaining gameObj as inactive?
foreach (Obstacle star in obstaclePool)
{
if (!star.gameObj.activeInHierarchy)
{
Color color = star.color;
color.a = 0.5f;
panel.GetComponent<Renderer>().material.color = color;
return star;
}
}
Soo I eventually figure it out:
So if I create a costum class like Obstacle I NEED TO MAKE SHORE that if the class has a variable like a GameObject or something that needs to be instantiated or assigned I will do that
This is how I a eventually solved the problem:
Obstacle go = new Obstacle
{
gameObj = Instantiate(prefab.gameObj)
};
poolList.Add(go);
So we literally create a new Object Obstacle and after that we make it's gameObj variable equal with Instantiate(prefab.gameObj), in this way we create every part of the Obstacle.

Change the UI image using script in unity c#

I want to change the UI image in random order. I have a gameobject in UI(canvas) containing Image component and it has null image initially. I have a script attached to it(gameobject) to change the image on run time.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class changeImg : MonoBehaviour {
public Sprite sprite1;
public Sprite sprite2;
public Sprite sprite3;
void Start()
{
ChangeImg();
}
void ChangeImg()
{
int rand=Random.Range(0,3);
if(rand==0)
{
gameObject.GetComponent<Image> ().sprite = sprite1;
//gameObject.GetComponent<UnityEngine.UI.Image> ().sprite = sprite1;
}
else if(rand==1)
{
gameObject.GetComponent<Image> ().sprite = sprite2;
// gameObject.GetComponent<UnityEngine.UI.Image> ().sprite = sprite2;
}
else if(rand==2)
{
gameObject.GetComponent<Image> ().sprite = sprite3;
//gameObject.GetComponent<UnityEngine.UI.Image> ().sprite = sprite3;
}
}
}
I have assigned the public field (sprite1,sprite2,sprite3) in inspector. And I tried the both option as I had commented in code. I did not get an error but also the image did not get change as I want. During runtime of a program, GameObject(to which the script is attached) has null image source as it was initially.
Use overrideSprite field instead of sprite - documentation
Unfortunately, unity ui is full of such pitfalls and it's api is totally counter-intuitive, so you have to be careful and check the docs regularly
You can also just use Image if you are in Unity 2017.3 (not sure if this works for older versions). For example:
using UnityEngine.UI;
-----
public Image ObjectwithImage;
public Sprite spriteToChangeItTo;
void Start () {
ObjectwithImage.sprite = spriteToChangeItTo;
}
Works great for me.
Have you checked the position of the Gameobject? Also the color of the image?
To change the Image from a Button, don't use GetComponent<Image> () as you can potentially get another Image component that does not belong to the button. It can also return null if the object is disabled.
Use the Button.image.sprite or Button.image.overrideSprite variable instead.
public Button pb;
public Sprite newSprite;
void Start()
{
pb.image.sprite = newSprite;
}
Or
pb.image.overrideSprite = newSprite;
It really doesn't matter which one is used. Any of these two should work.

Why won't my sprite change during runtime?

On my scene view in unity, I have three gameobjects set up, each with a respective sprite renderer and sprites. Two of the three are smaller in dimensions than the third one.
The images displayed by both smaller sprites are different. What I'd like to do is to be able to change the sprite of the third one by clicking on the other two. Clicking on one of the smaller sprites will make the third larger sprite match the small sprite that was previously clicked.
Here are the relevant scripts assigned to the two smaller sprites.
1st one that handles which sprite is displayed on the small sprite:
public class CardModel : MonoBehaviour
{
SpriteRenderer spriteRenderer; // renders sprite
public int cardIndex; // indicates what the card is
public bool show; // shows or hides card
public Sprite[] cardFronts; // collection of cards' front sides
public Sprite cardBack; // backside of all cards
// if card is turned up, show front of card, show back otherwise
public void DisplaySprite()
{
if (show)
spriteRenderer.sprite = cardFronts[cardIndex];
else
spriteRenderer.sprite = cardBack;
}
void Awake()
{
spriteRenderer = GetComponent<SpriteRenderer>(); // retrieve our sprite renderer
DisplaySprite(); // show card's sprite
}
}
And 2nd one that makes the smaller sprites clickable:
public class ClickCard : MonoBehaviour
{
CardModel model; // used to retrieve card index and bool to assign to larger sprite
public GameObject largeSprite; // third large sprite
void OnMouseDown()
{
CardModel cModel = GetComponent<CardModel>(); // retrieve card model from smaller sprite
LargeCardModel largeCardModel = largeSprite.GetComponent<LargeCardModel>(); // retrieve card model for larger sprite
largeCardModel.matchSprite(cModel.show, cModel.cardIndex); // match large sprite with small sprite clicked
}
}
And finally, the script attached to the third large sprite:
public class LargeCardModel : MonoBehaviour
{
public int largeIndex; // determines which sprite to show
public Sprite[] largeCardFaces; // collection of large sprites
public Sprite largeCardBack; // large card back
SpriteRenderer largeRenderer; // renders the image upon the large sprite
void Awake()
{
largeRenderer = GetComponent<SpriteRenderer>(); // retrieve renderer
}
// method to allow sprite to be changed
public void matchSprite(bool showFace, int cIndex)
{
if (largeRenderer == null)
largeRenderer = GetComponent<SpriteRenderer>(); // for some reason, I need to include this line or else I get an exception
if (showFace)
largeRenderer.sprite = largeCardFaces[cIndex];
else
largeRenderer.sprite = largeCardBack;
}
}
For whatever reason, I can't get the image to the larger sprite to change during runtime however when I stop running the program, the large sprite changes to that of the last smaller sprite I clicked on.
Okay, bare with as I have not tested this in Unity yet.
I think you're over complicating what you are trying to do. What you want is basically when sprite 1 or 2 is clicked, sprite 3 becomes what you have just clicked.
Here's an idea to try out:
Small Card GameObject:
public int cardIndex; // indicates what the card is
public bool show; // shows or hides card
public Sprite[] cardFronts; // collection of cards' front sides
public Sprite cardBack; // backside of all cards
GameObject largeSprite; //the large sprite that we will be changing!
void Start()
{
largeSprite = GameObject.Find("Name of the large sprite"); //find our large sprite!
}
// if card is turned up, show front of card, show back otherwise
public void DisplaySprite()
{
if (show)
gameObject.sprite = cardFronts[cardIndex]; //or it could be gameObject.renderer.sprite, I havent tested!
else
gameObject.sprite = cardBack;
}
void Awake()
{
DisplaySprite(); // show card's sprite
}
void OnMouseDown()
{
largeSprite.sprite = gameObject.sprite; //make the large one have the same as the small one!
}
Explanation:
Assuming this works, I've completely removed the script from the large GameObject and merged the 2 scripts together in the small one.
In Start() we find the large card gameObject using GameObject.Find() that way Unity will find your object with the name you've supplied and will go from there.
In OnMouseDown() we basically say:
if you clicked on me
make the large card the same as me! I havent tested it so that line might be a bit off, but the idea is there.
All in all, this is now one script that is attached to your small card object. Like I said I havent tested it, but I think the idea is there.
Hope this helps! :)

Changing an objects material based on what is looked at in Unity

I'm trying to change my objects material colour at run time based on if a ray I'm casting hits a certain object. When I quit my program I'm then wanting it to change back to its original material colour.
My menu with the material I want to switch between has a highlight function which lets the user know the material they are about to select. During the last few days I've been trying to get this to work I've had the following issues:
an instance of my desired material colour is attached to the gameobject but doesn't change the rendered material
the highlight material colour is placed on my game object as opposed to the actual material I want
I've ran out of ideas on how to fix this and I've gotten tunnel vision on this where I need a fresh pair of eyes to look over what I have so far.
Has anyone ever done this before? Could you please tell me what it is I'm doing wrong?
This is the current state of the code I have been working with:
My class storing the original material so that it can switch back to it once the program ends.
public GameObject targetMaterial;
public Color orignalMaterial;
//store GO original colour
void Awake()
{
orignalMaterial = targetMaterial.renderer.material.color;
}
//highlight code
public void ChangeObjectMaterialColour(Color materialColour)
{
targetMaterial.renderer.material.color = materialColour;
}
//
void OnApplicationQuit()
{
targetMaterial.renderer.material.color = orignalMaterial;
}
How I'm trying to change the material colours, I have three different options I want to switch between:
ChangeObjectColour new_colour1;
ChangeObjectColour new_colour2;
ChangeObjectColour new_colour3;
void Start ()
{
new_colour1 = GameObject.Find("Colour 1").GetComponent<ChangeObjectColour>();
new_colour2 = GameObject.Find("Colour 2").GetComponent<ChangeObjectColour>();
new_colour3 = GameObject.Find("Colour 3").GetComponent<ChangeObjectColour>();
}
void CastRay()
{
if (Physics.Raycast(transform.position, fwd, out hit))
{
foreach(string t in _tags)
{
if(hit.collider.gameObject.tag == t)
{
HighLight(hit.collider.gameObject);
hitTaggedObject = true;
}
}
if(hit.collider.gameObject.tag == "Colour1")
{ new_colour1.ChangeObjectMaterialColour(hit.collider.gameObject.renderer.material.color);
}
if(hit.collider.gameObject.tag == "Colour2")
{ new_colour2.ChangeObjectMaterialColour(hit.collider.gameObject.renderer.material.color);
}
if(hit.collider.gameObject.tag == "Colour3")
{
new_colour3.ChangeObjectMaterialColour(hit.collider.gameObject.renderer.material.color);
}
}
Can anyone see anything I'm doing wrong?
I think part of the problem is the way that your accessing the material properties. Here are two extension methods that change the color and material "Not permanently" Also you dont have to change the color back when the applications quits. It will revert back to its original state automatically on exit.
/// <summary>
/// Changes the material attached to the gameObject
/// </summary>
public static void ChangeMaterial(this GameObject go, Material mat)
{
go.renderer.material = mat;
}
/// <summary>
/// Changes the color of the material
/// </summary>
public static void ChangeColor(this Material mat, Color color)
{
mat.SetColor("_Color", color);
}
Ive used these methods hundreds of times in a single runtime session.
Put them in a static class and call it like its a member of its extended class.
For example:
gameObject.renderer.material.ChangeColor(Color.red);
gameObject.ChangeMaterial( /* Your Material */ );
The "_Color" is the name of the color component defined inside of the shader. All of unitys built in shaders have this property. But if you have a custom shader this might not be the case and it could be called something else. Unfortuantly there is no way to programmatically know this, you just have to keep track.

Categories

Resources