How can I detect transparent areas in a sprite in Unity - c#

I have a 1024x1024 pixel sprite with some transparent areas in it. I am rendering it on a game scene using Sprite Renderer. Is there any way check whether the pixel at mouse position is transparent or not when the mouse is hovered over it.

We could cast ray and get the world position of our hit point, Here I am assuming your SpriteRenderer has a collider.
private RaycastHit CastRay()
{
RaycastHit hit;
Ray ray = _camera.ScreenPointToRay(Input.mousePosition);
Physics.Raycast(ray, out hit);
return hit;
}
Then we need a method to convert that world space into texture coordinates
public Vector2 TextureSpaceCoord(Vector3 worldPos) {
float ppu = _sprite.pixelsPerUnit;
Vector2 localPos = transform.InverseTransformPoint(worldPos) * ppu;
var texSpacePivot = new Vector2(_sprite.rect.x, _sprite.rect.y) + _sprite.pivot;
Vector2 texSpaceCoord = texSpacePivot + localPos;
return texSpaceCoord;
}
Once we get the texture coordinates we could just use GetPixel() of Texture2D to get the color
private void PickColor()
{
RaycastHit hit = CastRay();
if (hit.collider != null)
{
Vector2 coord = TextureSpaceCoord(hit.point);
Color selectedColor = _sprite.texture.GetPixel((int) coord.x, (int) coord.y);
// Here you can check if color is transparent
if(selectedColor == Color.clear){
// do stuff
}
}
}
You could call the PickColor() in Update(), _camera would be Camera.main and _sprite would be the Sprite of your SpriteRenderer

Related

Unity get collision coordinate with hidden part of a plane

i have a sphere on a plane that stick to my mouse. i want to place that sphere so the position of the pointer, where the pointer would hit the plane. But the plane is hidden by the ball. It works, but the movement of the ball is noisy.
i would like to ignore everything except the plane for the collision.
can anyone help ?
This is what i do actually:
if (Ball!= null) {
RaycastHit raycastHit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out raycastHit, 100f))
{
if (raycastHit.transform != null)
{
//Our custom method.
var x = raycastHit.point.x;
var z = raycastHit.point.z;
Ball.pos().get_x().update_value(x);
Ball.pos().get_z().update_value(z);
}
}
}
You can do this by using the Layers & Layer Masks. Create a new layer and name it MyHiddenLayer and assign your object to it. Then do:
// Define output
RaycastHit raycastHit;
// Define Ray
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
// Define Layer
int layerMask = 1 << LayerMask.NameToLayer("MyHiddenLayer");
if (Physics.Raycast(ray, out raycastHit, 100f, layerMask))
{
...
}

Drawing a sprite on Raycast collision point

I am trying to emit a raycast from the player object and project a crosshair texture onto the world position the crosshair is aimed at. The crosshair should not overlap with the player and it should also only be emited in front of the Player gameObject.
I have tried this so far:
private float range = 100f;
public Texture crosshair;
private Rect crosshairPos;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Shoot();
}
Ray ray = new Ray(transform.position, transform.forward);
crosshairPos.x = ray.GetPoint(100f).x;
crosshairPos.y = ray.GetPoint(100f).y;
Graphics.DrawTexture(crosshairPos, crosshair);
Edit: After some testing, I am currently on the following snippet of code:
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Shoot();
}
Ray ray = new Ray(transform.position, transform.forward);
crosshairPos.x = ray.GetPoint(5f).x;
crosshairPos.y = ray.GetPoint(5f).y;
crosshairPos = Camera.main.WorldToScreenPoint(crosshairPos);
Vector2 crosshairPosSize = new Vector2(crosshair.width, crosshair.height);
Graphics.DrawTexture(new Rect((Vector2)crosshairPos, crosshairPosSize), crosshair);
}
I am however still unable to see a projected crosshair.
When you use a Ray from a GameObject you get a Vector in the world coordinate system. (The game world where your player is).
Graphics.DrawTexture() uses screen coordinates to draw the texture on screen.
Consider using Camera.Main.WorldToScreenPoint to change the world points that you get from Ray into points you can display on screen.
Here's an example of that
Vectors crosshairPos = new Vector3();
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Shoot();
}
Ray ray = new Ray(transform.position, transform.forward);
crosshairPos.x = ray.GetPoint(100f).x;
crosshairPos.y = ray.GetPoint(100f).y;
crosshairPos = Camera.Main.WorldToScreenPoint(crosshairPos);
Graphics.DrawTexture((Vector2)crosshairPos, crosshair);
}

Raycasting from the camera center doesn't work

I'm using the following code to raycast from the center of the camera.
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(Camera))]
public class CameraPointer : MonoBehaviour {
private GameObject hitObject = null;
private Vector3 reticlePosition = Vector3.zero;
public Camera mcamera;
public float Distance = 10f;
void Update () {
reticlePosition = mcamera.transform.position;
Ray ray = mcamera.ScreenPointToRay(reticlePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, Distance)) {
if (hitObject != hit.transform.gameObject) {
if (hitObject != null) {
hitObject.SendMessage("OnReticleExit", SendMessageOptions.DontRequireReceiver);
}
hitObject = hit.transform.gameObject;
hitObject.SendMessage("OnReticleEnter", SendMessageOptions.DontRequireReceiver);
} else {
hitObject.SendMessage("OnReticleHover", SendMessageOptions.DontRequireReceiver);
}
} else {
if (hitObject != null) {
hitObject.SendMessage("OnReticleExit", SendMessageOptions.DontRequireReceiver);
}
hitObject = null;
}
}
}
Unfortunately, it doesn't work at all. It is not hitting any object at all. How can I sort this out?
Ray ray = mcamera.ScreenPointToRay(reticlePosition);
this function is the wrong approach.
vector3 parameter in screenpoint should be the GameScreen Position(ex:coordinate at screen 1920x1080)
So, you have to use function mcamera.ViewportPointToRay(new Vector3(0.5f,0.5f));
or you can use
if (Physics.Raycast(reticlePosition, transform.forward,out hit Distance))...
The reason why this may note be working as you intended is that you're projecting a ray from screen space to world space when you call:
Ray ray = mcamera.ScreenPointToRay(reticlePosition);
See the docs here
So if mcamera is located at the origin, you're Raycasting from the bottom left corner of the screen.
If you want to just cast a ray from the center of the camera's viewport, then something like this would work:
Ray ray = new Ray(mcamera.transform.position, mcamera.transform.forward);
This basically casts a ray along the forward vector of the camera starting at the camera's current position.

How to make the tank look to the side of the mouse and shoot where the player clicks?

I have a tank in a 3rd person 3D game so my camera is looking at the tank from above and a bit from the side. I need the turret of my tank to rotate on the y-axis so that it always looks at the mouse and shoot where the player clicks. The problem is that my game is 3D and all I get is that it actually looks at the mouse (like upwards to the sky).
This is the code I have now:
private Vector3 target;
private Camera cam;
private void Start()
{
cam = Camera.main;
}
void Update()
{
Vector2 mousePos = new Vector2();
mousePos.x = Input.mousePosition.x;
mousePos.y = Input.mousePosition.y;
target = cam.WorldToScreenPoint(new Vector3(mousePos.x, mousePos.y, cam.nearClipPlane));
transform.LookAt(target);
}
Your problem lies here: target = cam.WorldToScreenPoint(new Vector3(mousePos.x, mousePos.y, cam.nearClipPlane));
What you actually need to do is generate a ray that comes out of the camera with ScreenPointToRay(), then do a Physics.Raycast() with that ray and get the RaycastHit from it. Then set the target equal to the RaycastHit.
For example:
Ray screenRay;
RaycastHit screenRayHit;
float maxRaycastDistance;
screenRay = cam.ScreenPointToRay(mousePos.x, mousePos.y);
if(Physics.Raycast(screenRay, out screenRayHit, maxRaycastDistance))
{
target = screenRayHit;
}
else
{
//just sets the turret to aim at the endpoint of the ray if you aim at nothing
target = screenRay.GetPoint(maxRaycastDistance);
}

How to limit/Clamp RotateAround for camera movement in Unity?

I have a scene with a globe, where the camera rotates around the globe. I need to clamp the position and rotation of this movement in certain instances of the game. I have tried several methods but none have worked. My code is below:
private void LateUpdate()
{
if (Input.GetMouseButtonDown(1))
mouseStartPosition = GetMouseHit();
if (mouseStartPosition != null)
DragPlanet();
if (Input.GetMouseButtonUp(1))
StaticPlanet();
}
public void RotateCamera(Vector3 dragStartPosition, Vector3 dragEndPosition)
{
//normalised for odd edges
dragEndPosition = dragEndPosition.normalized *planetRadius;
dragStartPosition = dragStartPosition.normalized * planetRadius;
// Cross Product
Vector3 cross = Vector3.Cross(dragEndPosition, dragStartPosition);
// Angle for rotation
float angle = Vector3.SignedAngle(dragEndPosition, dragStartPosition, cross);
//Causes Rotation of angle around the vector from Cross product
holderTransform.RotateAround(planet.transform.position, cross, angle);
}
private static Vector3? GetMouseHit()
{
RaycastHit hit;
int layer_mask = LayerMask.GetMask("Planet"); //raycasting on the planet
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, Mathf.Infinity, layer_mask))
{
return hit.point;

Categories