I am new to unity, and I tried to create a prefab for a tile in a game. So whenever a user clicks the tile it should change its sprite. The problem is that all the copies (instances) in my game are changing their sprite.
This is what I tried:
void Update()
{
if (Input.GetMouseButtonDown(0))
{
GameObject gameObject = this.gameObject;
SpriteRenderer renderer = gameObject.GetComponent<SpriteRenderer>();
Sprite sprite = Resources.Load<Sprite>("Sprites/red");
renderer.sprite = sprite;
}
}
What am I doing wrong? Thanks in advance
You are detecting if the mouse button is pressed, not if it's pressed over the given tile.
There are several ways to do it, but I would say the standard way is to:
Attach a Collider to the GameObject
Implement OnMouseDown
void OnMouseDown()
{
GameObject gameObject = this.gameObject;
SpriteRenderer renderer = gameObject.GetComponent<SpriteRenderer>();
Sprite sprite = Resources.Load<Sprite>("Sprites/red");
renderer.sprite = sprite;
}
Like akin said you are changing all sprites on a mouse click, you can raycast to your objects and check if they are hit then change it
Run this part on a script attached to your player or camera
void FixedUpdate()
{
RaycastHit hit;
if (Physics.Raycast(transform.position, -Vector3.up, out hit, 100.0f)) {
if (hit.transform.gameObject.GetComponent<yourscript>()) {
hit.transform.gameObject.GetComponent<yourscript>().ChangeSprite();
}
}
}
attach to tile game objects
public class yourscript : MonoBehaviour
{
public void ChangeSprite() {
SpriteRenderer renderer = gameObject.GetComponent<SpriteRenderer>();
Sprite sprite = Resources.Load<Sprite>("Sprites/red");
renderer.sprite = sprite;
}
}
Related
I have created a player prefab (called Tim in my project) and am trying to make all the references to gameObjects and transforms directly from the one of the players scripts which is actually attached to a gun object which is a child of the player prefab.
The issue is I cant manage to reference the camera in the script although I've looked and tried many different methods, none of them seemed to work. Unity prints this error in the console though : "NullReferenceException: Object reference not set to an instance of an object". And here is the script :
public class Gun_Control : MonoBehaviour
{
// References for GameObjects
[SerializeField] private Rigidbody2D rb;
private GameObject Player;
[SerializeField] private Transform PlayerTransform;
private GameObject Gun;
[SerializeField] private Transform GunTransform;
private Camera MainCamera;
private GameObject firePoint;
[SerializeField] private Transform firePointTransform;
[SerializeField] private GameObject bulletPrefab;
// Variables for Shooting
private Vector2 mousePos;
private float bulletForce = 20f;
// Start is called at the beginning
void Start()
{
Debug.Log("Starting");
Player = GameObject.FindWithTag("Player");
PlayerTransform = Player.transform;
Gun = GameObject.FindWithTag("PlayerGun");
GunTransform = Gun.transform;
MainCamera = GameObject.FindWithTag("Camera").GetComponent<Camera>();
firePoint = GameObject.FindWithTag("PlayerFirePoint");
firePointTransform = firePoint.transform;
}
// Update is called once per frame
void Update()
{
// Get mouse position
mousePos = MainCamera.ScreenToWorldPoint(Input.mousePosition);
// Run shoot function on left click
if(Input.GetButtonDown("Fire1"))
{
Shoot();
}
}
// Update is called on every physics frame
void FixedUpdate()
{
// Set gun position to player position
GunTransform.position = PlayerTransform.position;
// Set gun rotation to mouse position
Vector2 lookDir = mousePos - rb.position;
float angle = Mathf.Atan2(lookDir.y ,lookDir.x) * Mathf.Rad2Deg - 180f;
rb.rotation = angle;
}
void Shoot()
{
// Instantiate a bullet at the firepoint and give it force
GameObject bullet = Instantiate(bulletPrefab, firePointTransform.position, firePointTransform.rotation);
Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
rb.AddForce(firePointTransform.up * bulletForce, ForceMode2D.Impulse);
}
}
Right now I have a variable, MainCamera, and when the script starts I look for a camera which has "Camera" as its tag which is set correctly.
I can add on if anyone needs more details and thank you to everyone for taking the time to help.
Edit 1 :
I tried what thunderkill suggested but it doesnt seem to work.
Here is a picture of the new code.
And when I try to use Debug.Log(Camera.main); it prints null.
here is a good example to access your main camera :
Camera m_MainCamera;
void Start()
{
//This gets the Main Camera from the Scene
if(Camera.main != null){
m_MainCamera = Camera.main;
//This enables Main Camera
m_MainCamera.enabled = true;
}
}
Camera c = Camera.main;//get the camera Tagged "MainCamera"
So you have to Check if your Camera tag is "MainCamera".
or you try :
GameObject.FindObjectOfType<Camera>();
(I don't recommend the second solution)
So I ended up finding the answer.I just deleted the camera in my scene and created a new one and then ended up using : MainCamera = GameObject.FindWithTag("Camera").GetComponent<Camera>(); which worked this time. The issue could have also been caused by errors present before in my code.
I use Cinemachine camera collider for my project. This basically means that my entire game is inside a polygon collider.
With the following script my line renderer collides instantly with this camera collider:
[SerializeField] private float defDistanceRay = 10f;
public Transform laserFirePoint;
public LineRenderer m_linerenderer;
Transform m_transform;
private void Awake()
{
m_transform = GetComponent<Transform>();
}
private void Update()
{
ShootLaser();
}
void ShootLaser()
{
if(Physics2D.Raycast(m_transform.position, transform.right))
{
RaycastHit2D _hit = Physics2D.Raycast(m_transform.position, transform.right);
Draw2DRay(laserFirePoint.position, _hit.point);
Debug.Log(_hit.collider.name);
}
else
{
Draw2DRay(laserFirePoint.position, laserFirePoint.transform.right * defDistanceRay);
}
}
void Draw2DRay(Vector2 startPos, Vector2 endPos)
{
m_linerenderer.SetPosition(0, startPos);
m_linerenderer.SetPosition(1, endPos);
}
Can I somehow bypass this collider by setting a "tag" on it and in my code ignore colliders with that specific tag?
Anyone have a good work around for this issue?
EDIT: Forgot to mention Im looking at: https://docs.unity3d.com/ScriptReference/Physics2D.IgnoreCollision.html
But that takes in 2 colliders I just want to ignore 1.
EDIT2: I tried to set the Cinemachine camera polygon collider as a Layermask and ignore it with the following code:
public LayerMask layerMask;
void ShootLaser()
{
if (Physics2D.Raycast(m_transform.position, transform.right))
{
RaycastHit2D _hit = Physics2D.Raycast(m_transform.position, transform.right, layerMask);
Draw2DRay(laserFirePoint.position, _hit.point);
Debug.Log(_hit.collider.name);
}
else
{
Draw2DRay(laserFirePoint.position, laserFirePoint.transform.right * defDistanceRay);
}
}
EDIT 3:
RaycastHit2D _hit = Physics2D.Raycast(m_transform.position, transform.right, defDistanceRay, layerMask);
I added defDistanceRay into the Raycast and now it dosent seem to collide with the camera collider atleast! Super buggy but atleast i bypass the Cinemachine camera collider!
I'm making a top-down game where you drive a car and shoot targets at the same time. I have a script that makes a sprite of a crosshair follows the mouse cursor and I want to have it set up so that when the player presses the mouse button (the mouse button isn't in the code now) and the crosshair sprite is overlapping an enemy sprite, the enemy dies. I was following this documentation on Bounds.Intersects. Here's my code:
public class shootingScript : MonoBehaviour
{
public GameObject target, enemy;
CircleCollider2D targetCollider;
CapsuleCollider2D enemyCollider;
// Start is called before the first frame update
void Start()
{
//Check that the first GameObject exists in the Inspector and fetch the Collider
if (target != null)
{
print("targ not null");
targetCollider = target.GetComponent<CircleCollider2D>();
}
//Check that the second GameObject exists in the Inspector and fetch the Collider
if (enemy != null)
{
print("enemy not null");
enemyCollider = enemy.GetComponent<CapsuleCollider2D>();
}
}
// Update is called once per frame
void Update()
{
if (targetCollider.bounds.Intersects(enemyCollider.bounds))
{
print("hit");
Destroy(enemy);
}
}
}
In-game "targ not null" and "enemy not null" prints but when I move my cursor and the crosshair goes over the enemy "hit" is not printed and the enemy is not destroyed. I have a CircleCollider2D on the crosshair and a CapsuleCollider2D on the enemy. The script is on an empty game object. I also tried sprite.bounds but that resulted in the enemy getting killed as soon as I run the game.
EDIT:
Here's the code that keeps the crosshair sprite on the cursor. I copied it from somewhere. I set moveSpeed to 99999 since I want the crosshair sprite to be exactly where the mouse is.
public class mouseReticle : MonoBehaviour
{
private Vector3 mousePosition;
public float moveSpeed = 0.1f;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
mousePosition = Input.mousePosition;
mousePosition = Camera.main.ScreenToWorldPoint(mousePosition);
transform.position = Vector2.Lerp(transform.position, mousePosition, moveSpeed);
}
}
The target sprite and enemy sprite were at different depths (z).
I can't manage to instantiate a prefab at my mouse position.
I've tried to instantiate the prefab at the current mouse position, but on click, the block shows in the hierarchy and not the scene. It also creates 4-5 prefabs.
using UnityEngine;
public class Building : MonoBehaviour
{
public GameObject block;
void Update()
{
if (Input.GetMouseButton(0))
{
Instantiate(block, new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0f), Quaternion.identity);
}
}
}
I want to create 1 prefab of the block, and I want it to show up in the scene view.
Input.mousePosition is the coordinates of the mouse on the screen. Use Camera.ScreenToViewportPoint to get the world position.
The block will not show in the scene, because its position is likely something like (500, 300, 0), which is very far. Select the block in the Hierarchy and press "F" to see it.
Input.GetMouseButton() keeps firing as long as the mouse is held. Change this to Imput.GetMouseButtonDown()
using UnityEngine;
public class Building : MonoBehaviour {
public GameObject block;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector3 pos = Camera.main.ScreenToViewportPoint(Input.mousePosition);
Instantiate(block, pos, Quaternion.identity);
}
}
}
You need to convert from screen space to world space.
One way to do this is to use Camera.ScreenToWorldPoint:
private Camera mainCam;
void Start()
{
mainCam = Camera.main;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector3 blockPos = mainCam.ScreenToWorldPoint(Input.mousePosition);
Instantiate(block, blockPos, 0f), Quaternion.identity);
}
}
If you want to spawn further away from the camera, look into Camera.ScreenPointToRay.
I've got a draggable class that works great for dragging entire sprites and I'm trying to modify it to register dragging only within the defined 2D polygon collider component of the sprite object. I'm having a really hard time getting the drag to only register when I click within the boundaries of the polygon collider. Right now it's still dragging wherever I click-drag on the sprite including outside the boundaries of my polygon collider. I could really use some guidance on this. I've also attached an image to illustrate what I'm trying to achieve. Thanks!
PolygonCollider2D collider;
void Start ()
{
collider = transform.GetComponent<PolygonCollider2D>();
}
public void OnDrag (PointerEventData eventData)
{
Vector3 mousePosition = new Vector3(eventData.position.x, eventData.position.y, 0);
collider.transform.position = mousePosition;
if(transform.parent.gameObject == partsPanel)
transform.SetParent(dragLayer.transform);
if(transform.parent.gameObject == buildBoard)
transform.SetParent(dragLayer.transform);
}
AFAIK, The polygon collider we added to the sprite is editable. Goto inspector, collider component and there is a button which allow editing collider area. Adjust the points of this collider to cover just the desired area. This way only the desired area will receive touch events.
Ok, I know this is very old thread, but I had similar problem and I found a solution so I will post it here :)
Just create a class with this code and put it on your object
using UnityEngine;
[RequireComponent(typeof(RectTransform), typeof(Collider2D))]
public class Collider2DRaycastFilter : MonoBehaviour, ICanvasRaycastFilter
{
Collider2D myCollider;
RectTransform rectTransform;
void Awake()
{
myCollider = GetComponent<Collider2D>();
rectTransform = GetComponent<RectTransform>();
}
public bool IsRaycastLocationValid(Vector2 screenPos, Camera eventCamera)
{
var worldPoint = Vector3.zero;
var isInside = RectTransformUtility.ScreenPointToWorldPointInRectangle(
rectTransform,
screenPos,
eventCamera,
out worldPoint
);
if (isInside)
isInside = myCollider.OverlapPoint(worldPoint);
return isInside;
}
}