How can I open a canvas by clicking on an object in Unity - unity3d

As you can see in the first image, I have a store, which has many objects. My intention is to click on an object and a Canvas with the object's information is displayed, but I don't know how to do it.
I created a canvas example (as you can see in the second image) that I open by pressing the "space", but I want to open it by clicking on a vase (gameobject), any ideas?
Greetings.
Images:
Objects
Canvas piece information

Step 1:
Get a reference to your canvas
[SerializeField] private GameObject canvas;
Step 2:
Enable canvas onClick
void TaskOnClick() {
canvas.SetActive(true);
}
Make sure that the canvas is disabled by default.

Add IPointerClickHandler interface to your script and use the method OnPointerClick to call a method that turns on your canvas GameObject. You will need a reference for your canvas, you can set it in the inspector after adding defining it in the script as shown in the code below. (You need to attach the script to the item game object)
public class ItemInfo : MonoBehaviour, IPointerClickHandler
{
[SerializeField] private GameObject itemInfoCanvas;
public void ShowItemInfo()
{
itemInfoCanvas.SetActive(true);
}
public void OnPointerClick(PointerEventData eventData)
{
if (eventData.button == PointerEventData.InputButton.Right)
ShowItemInfo();
}
}
Another way to achieve it with the old input system:
public class ItemInfo : MonoBehaviour
{
[SerializeField] private GameObject itemInfoCanvas;
public void ShowItemInfo()
{
itemInfoCanvas.SetActive(true);
}
private void Update()
{
if (Input.GetMouseButtonUp(0)) //Right click, triggered after releasing.
ShowItemInfo();
}
}
Same way as above with the new input system:
private void Update()
{
if (!Mouse.current.leftButton.wasReleasedThisFrame)
{
ShowItemInfo();
}
Read more about the pointer click handlers here

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class CameraInteraction : MonoBehaviour
{
private new Transform camera;
private float rayDistance = 2;
public Canvas PieceInformationCanvas;
public Canvas MinimapCanvas;
public Text titleText, descriptionText;
private GameObject test;
void Start()
{
camera = transform.Find("Camera");
PieceInformationCanvas.enabled = false;
test = PieceInformationCanvas.transform.GetChild(2).gameObject;
test.SetActive(false);
}
void Update()
{
// Parametros:
// 1) Desde donde iniciara el rayo
// 2) Hacia donde se dirige el rayo (hacia adelante(eje x))
// 3) Color del rayo
Debug.DrawRay(camera.position, camera.forward * rayDistance, Color.red);
if (Input.GetButtonDown("Interactable"))
{
// RaycastHit hit contiene la informacion del objeto que estamos mirando
RaycastHit hit;
if (Physics.Raycast(camera.position, camera.forward, out hit, rayDistance, LayerMask.GetMask("Interactable")))
{
hit.transform.GetComponent<Interactable>().Interact();
ShowPieceCanvas();
}
}
}
public void ShowPieceCanvas()
{
string title = "Titulo Pieza Artesanal";
string description = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
titleText.text = title;
descriptionText.text = description;
test.SetActive(true);
Cursor.lockState = CursorLockMode.None;
PieceInformationCanvas.enabled = true;
MinimapCanvas.enabled = false;
}
public void HidePieceCanvas()
{
test.SetActive(false);
PieceInformationCanvas.enabled = false;
Cursor.lockState = CursorLockMode.Locked;
MinimapCanvas.enabled = true;
}
}

Related

How to Randomly disable box collider in unity

i have two simple 3d cube objects a,b. so my task is to randomly disable box collider one of the object every time game start.
my initial thought is randomly generate Boolean and if it is false then disable the box collider.
You can add you cube's to array and use Random.Range(0, yourArray.Length) to get index of the cube which should be deactivated. In your case it will be some overhead but this solution will be work for different cube`s count in the future.
In code it will looks like:
// You can serialize this array and add cubes from Inspector in Editor
// or add script with this code to the parent gameobject
// and use GetComponentsInChildren<BoxCollider>() to get all colliders
var cubes = new BoxCollider[2] {firstCube, secondCube};
var cubeToDeactivationIndex = Random.Range(0, cubes.Length);
cubes[cubeToDeactivationIndex].enabled = false;
About your second question. If I understand correctly, implementation will be next:
using System;
using UnityEngine;
using UnityEngine.Assertions;
using Random = UnityEngine.Random;
// Interface need to provide limited API
// without open references to gameobject and transform of the object
public interface ICollidable
{
event Action<ICollidable> OnCollided;
int HierarchyIndex { get; }
void DisableCollider();
}
// This component you should add to the object which will collide with player
public class CollidableObject : MonoBehaviour, ICollidable
{
[SerializeField]
private Collider _objectCollider;
public event Action<ICollidable> OnCollided;
public int HierarchyIndex => transform.GetSiblingIndex();
private void Start()
{
Assert.IsNotNull(_objectCollider);
}
//Here you can use your own logic how to detect collision
//I written it as example
private void OnCollisionEnter(Collision collision)
{
if (collision.transform.CompareTag("Player")) {
OnCollided?.Invoke(this);
}
}
public void DisableCollider()
{
_objectCollider.enabled = false;
}
}
// This component should be on the parent gameobject for your `
// collidable objects. As alternative you can serialize array
// and add all collidable objects from the Inspector but in that
// case signature of the array should be CollidableObject[]
// because Unity can`t serialize interfaces
public class RamdomizedActivitySwitcher : MonoBehaviour
{
private ICollidable[] _collidableObjects;
private void Awake()
{
_collidableObjects = GetComponentsInChildren<ICollidable>(true);
foreach (var collidable in _collidableObjects)
{
collidable.OnCollided += DisableObjectWhenMatch;
}
}
private void DisableObjectWhenMatch(ICollidable collidedObject)
{
var randomIndex = Random.Range(0, _collidableObjects.Length);
if (randomIndex == collidedObject.HierarchyIndex) {
collidedObject.DisableCollider();
}
}
}
}

Change Bullets as power

I am doing a 2d game and I want my player to be able to change bullets when he collides with an object(power) and destroy that object. I have a script and I was thinking that I need to implement 2 variables prefab ON/Off but now thinking much more I want to change with the help of a tag ( My player has in his script a public Rigidbody2D bullet) and this function
void Fire()
{
if (photonView.IsMine)
{
var firedBullet = Instantiate(bullet, barrel.position, barrel.rotation);
firedBullet.AddForce(barrel.up * bulletSpeed);
}
}
this is the script that I was working on for switching bullets but I think it will not work to change a bullet that I add in the inspector for the Character script , to disable from this script and add other bullet . How I can make it by tag?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WeaponSwitching : MonoBehaviour
{
[SerializeField] private GameObject pickupEffect;
public GameObject[] DisablePrefab;
public GameObject[] EnablePrefab;
public int selectBullet = 0;
// Start is called before the first frame update
// Update is called once per frame
public void Bullet(Character bullet)
{
var effect = Instantiate(pickupEffect, transform.position, transform.rotation);
foreach (GameObject disable in DisablePrefab)
{
disable.SetActive(false);
}
foreach (GameObject enable in EnablePrefab)
{
enable.SetActive(true);
}
Destroy(gameObject);
Destroy(effect, 3.0f);
}
}
and I try this think with a BulletSwitch script to call the function from Weapon Switching script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletSwitch : MonoBehaviour
{
[SerializeField] private Character bullet;
// Start is called before the first frame update
private void Awake()
{
if (!bullet) bullet = GetComponent<Character>();
}
private void OnTriggerEnter2D(Collider2D other)
{
// or whatever tag your powerups have
if (!other.CompareTag("Bullet"))
{
Debug.LogWarning($"Registered a collision but with wrong tag: {other.tag}", this);
return;
}
var Bullet = other.GetComponent<WeaponSwitching>();
if (!Bullet)
{
Debug.LogError($"Object {other.name} is tagged PowerUp but has no PowerUp component attached", this);
return;
}
Debug.Log("Found powerup, pick it up!", this);
Bullet.Bullet(bullet);
}
}
inspector character
after my player collides with an object the bullets disappear.
You could make an array of prefabs and change to the correct bullet type when you hit a powerup by setting a CurrentBulletType variable to one of the types from the array. So when you hit powerup X, change bullet type to one of the types from the array.
public GameObject currentPrefab;
public GameObject[] bulletPrefabs;
private void OnTriggerEnter2D(Collider2D other)
{
string tagString = other.tag;
bool foundObject = true;
switch (tagString)
{
case "standard bullet":
currentPrefab = bulletPrefabs[0];
break;
case "fancy bullet":
currentPrefab = bulletPrefabs[1];
break;
case "even fancier bullet":
currentPrefab = bulletPrefabs[2];
break;
default:
foundObject = false;
break;
}
if (foundObject) other.gameObject.SetActive(false);
}
You can instead use a dictionary to directly reference the correct prefab by string name (tag) to make it easy to add more bullet types.
//dictionairy to reference bullet types quickly
public Dictionary<string, BulletTypes> bulletLibrary = new Dictionary<string, BulletTypes>();
//set these in the inspector
public BulletTypes[] bulletTypes;
[System.Serializable]
public struct BulletTypes
{
public string bulletName;
public GameObject prefab;
//public int bulletPower; //more data if you wish
}
private void Start()
{
//fill the dictionary based on the filled bullet type array in inspector
for (int i = 0; i < bulletTypes.Length; i++)
{
if (bulletTypes[i].bulletName != "")
bulletLibrary.Add(bulletTypes[i].bulletName, bulletTypes[i]);
else
print("bullet added, but name empty");
}
}
private void OnTriggerEnter2D(Collider2D other)
{
string tagString = other.tag;
//if the collided tag is in the dictionary, we can reference the bullet type from that dictionairy
if (bulletLibrary.ContainsKey(tagString))
{
print("spawn: " + bulletLibrary[tagString].bulletName);
print("prefab: " + bulletLibrary[tagString].prefab);
//all the data you need is in: bulletLibrary[tagString];
//Bullet.Bullet(bulletLibrary[tagString]);
other.gameObject.SetActive(false);
}
}
Sidenote: If you spawn a lot of bullets, you can use pooling. SimplePool is nice and easy for this. Instead of calling: instantiate and destroy, you call: SimplePool.Spawn() and SimplePool.DeSpawn().
If you have any more questions on this feel free to ask ;)

Destroy GameObject when its clicked on

I'm making a building mechanic in my game and I want to be able to clear out certain objects around the map (trees, other decor) so I have room to build. I've tried using a ray cast to find what object is being clicked on and destroy it but that doesn't work.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectDestroy : MonoBehaviour {
// Start is called before the first frame update
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButtonDown (0)) {
Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
RaycastHit hit;
Debug.Log (Input.mousePosition);
if (Physics.Raycast (ray, out hit)) {
if (hit.collider.gameObject == gameObject) Destroy (gameObject);
}
}
}
}
Here is a little example script:
public class Destroyable : MonoBehaviour
{
private void OnMouseDown()
{
Destroy(gameObject);
}
}
You can attach this script to the GameObject you want to destroy and then during Play-Mode you can click on it to destroy it. It is modifiable if you just need it in your In-Game-Editor.
Note: You need an active Collider on the same Gameobject.
Edit:
The following script shows an example for changing the color of the object:
public class Destroyable : MonoBehaviour
{
public Color mouseHoverColor = Color.green;
private Color previousColor;
private MeshRenderer meshRenderer;
private void Start()
{
meshRenderer = GetComponent<MeshRenderer>();
previousColor = meshRenderer.material.color;
}
private void OnMouseDown()
{
Destroy(gameObject);
}
private void OnMouseOver()
{
meshRenderer.material.color = mouseHoverColor;
}
private void OnMouseExit()
{
meshRenderer.material.color = previousColor;
}
}
You don't need to add this script on every object, just add it to a manager and also I think you are missing Raycast parameters.
To see where you ray is going you can use Debug.Ray()
Also, I would prefer you use #MSauer way since is much cleaner for what you want, just be sure the object contains a collider, I think they can be a trigger and the click will still happen.

Unity Game Object Controlled by real light spot on the wall

We have got various controllers developed for moving gameobjects. I have used magnetometer/gyro sensor to move game object using MUVSlide through following code:
using UnityEngine;
using ForestIndieGames.Muvslide;
public class Connection : MonoBehaviour {
private static bool created = false;
private bool newInputAvailable;
private MuvslideConnection muvslideConn;
private void Awake()
{
if (!created)
{
DontDestroyOnLoad(this.gameObject);
created = true;
muvslideConn = new MuvslideConnection();
}
}
private void OnApplicationQuit()
{
if (muvslideConn != null)
muvslideConn.Close();
}
private void Update()
{
if (muvslideConn.GetInputManager().IsNewMotionAvailable())
newInputAvailable = true;
}
public bool IsNewInputAvailable()
{
bool result = newInputAvailable;
newInputAvailable = false;
return result;
}
public Vector3 GetAngles()
{
float[] angles = muvslideConn.GetInputManager().GetOrientationDegrees();
return new Vector3(angles[0], angles[1], angles[2]);
}
}
What I am trying to achieve is to move a gameobject by a real light spot on the wall. The spot is on the wall and fed through a camera. When the spot moves I want game object to follow exactly. The light spot can be a specific color or IR or UV etc.
Any leads please

Script code only seems to work on single instance of prefb

I am experiencing the strangest issue
I have a ray cast and when it touches a certain layer it calls my function which does a small animation.
The problem is, this only works on a single object, I have tried duplicating, copying the prefab, dragging prefab to the scene, it doesn't work.
Now I have this code below, and as you can see I have this line which allows me to access the script on public PlatformFall platfall; so I can call platfall.startFall();
Something I've noticed, If I drag a single item from the hierarchy to the public PlatFall in Inspector then that SINGLE object works as it should. ( in that it animates when startFall is called). HOWEVER, if I drag the prefab from my project to the inspector then they do not work. (Even if debug log shows that the method is called animation does not occur).
public class CharacterController2D : MonoBehaviour {
//JummpRay Cast
public PlatformFall platfall;
// LayerMask to determine what is considered ground for the player
public LayerMask whatIsGround;
public LayerMask WhatIsFallingPlatform;
// Transform just below feet for checking if player is grounded
public Transform groundCheck;
/*....
...*/
Update(){
// Ray Casting to Fallingplatform
isFallingPlatform = Physics2D.Linecast(_transform.position, groundCheck.position, WhatIsFallingPlatform);
if (isFallingPlatform)
{
Debug.Log("Here");
platfall.startFall();
}
Debug.Log(isFallingPlatform);
}
}
Platform Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlatformFall : MonoBehaviour
{
public float fallDelay = 0.5f;
Animator anim;
Rigidbody2D rb2d;
void Awake()
{
Debug.Log("Awake Called");
anim = GetComponent<Animator>();
rb2d = GetComponent<Rigidbody2D>();
}
private void Start()
{
Debug.Log("Start Called");
}
//void OnCollisionEnter2D(Collision2D other)
//{
// Debug.Log(other.gameObject.tag);
// GameObject childObject = other.collider.gameObject;
// Debug.Log(childObject);
// if (other.gameObject.CompareTag("Feet"))
// {
// anim.SetTrigger("PlatformShake");
// Invoke("Fall", fallDelay);
// destroy the Log
// DestroyObject(this.gameObject, 4);
// }
//}
public void startFall()
{
anim.SetTrigger("PlatformShake");
Invoke("Fall", fallDelay);
Debug.Log("Fall Invoked");
// destroy the Log
// DestroyObject(this.gameObject, 4);
}
void Fall()
{
rb2d.isKinematic = false;
rb2d.mass = 15;
}
}
I understood from your post that you are always calling PlatformFall instance assigned from inspector. I think this changes will solve your problem.
public class CharacterController2D : MonoBehaviour {
private PlatformFall platfall;
private RaycastHit2D isFallingPlatform;
void FixedUpdate(){
isFallingPlatform = Physics2D.Linecast(_transform.position, groundCheck.position, WhatIsFallingPlatform);
if (isFallingPlatform)
{
Debug.Log("Here");
platfall = isFallingPlatform.transform.GetComponent<PlatformFall>();
platfall.startFall();
}
}
}
By the way, i assume that you put prefab to proper position to cast. And one more thing, you should make physics operations ,which affect your rigidbody, in FixedUpdate.