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;
}
}
Related
I am currently working on a game in Unity and I have a huge problem. I want to draw a level with a tilemap and then move the player on this tilemap. But I want to add an edge collider with the script at the start of the level, because I want to include a Level-Creator in the game. So, I cannot create every collider manual.
Here is an example picture of a "level":
I want to have an edge collider to prevent moving outside the tilemap, but I do not know how to implement that in code. I either cannot find anything helpful on web.
I would be glad about any ideas!
If you get the Collider component of your tilemap, you are able to access the "OverlapPoint" method. With this method you're able to make a basic edge detection system for your custom tile map.
https://docs.unity3d.com/ScriptReference/Collider2D.OverlapPoint.html
What I would do is:
Save Your player's current position in a "lastPosition" variable, check for every update if the corners are inside the collider with the "OverlapPoint" method. If not: do not save the current position but set the current position of the player to the lastPosition variable.
public Vector3 playerSize;
public Collider2d collider;
Vector2 lastPosition;
void Start()
{
lastPosition = transform.position;
}
void Update()
{
if(!collider.OverlapPoint(transform.position) ||
!collider.OverlapPoint(transform.position + playerSize) ||
!collider.OverlapPoint(transform.position + new Vector2(playerSize.x, 0)) ||
!collider.OverlapPoint(transform.position + new Vector2(0, playerSize.y)))
{
transform.position = lastPosition;
}
lastPosition = transform.position;
}
As the title says, I have a scene with three objects, a cube, a sphere, and a cylinder-like you can see in the image below.
What I'm trying to achieve is that when I press the "Rotate" button, the three objects rotate in an anti-clockwise direction, so the cylinder goes where the cube was, the cube goes where the sphere was, and the sphere goes where the cylinder was. If I click the button again, they rotate once again and so on. So far, I managed to make them rotate around an empty object at the center of the "triangle" they form with their initial position.
This is what happens when I first click the "Rotate" button:
As you can see, the object rotates, but they don't keep the same coordinate of the object that was previously in that position, so I was thinking of having them exchange coordinates. How can I achieve that or making them rotate how I want to?
Here is the code I wrote so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RotateObject : MonoBehaviour {
//rotating objects
[SerializeField]
private GameObject cube;
[SerializeField]
private GameObject sphere;
[SerializeField]
private GameObject cylinder;
private Renderer cubeRenderer;
private Renderer sphereRenderer;
private Renderer cylinderRenderer;
//rotation target
public GameObject target;
private void Start() {
cubeRenderer = cube.GetComponent<Renderer>();
sphereRenderer = sphere.GetComponent<Renderer>();
cylinderRenderer = cylinder.GetComponent<Renderer>();
gameObject.GetComponent<Button>().onClick.AddListener(Rotate);
}
void Rotate() {
//rotate objects by 120 degrees
cube.transform.RotateAround(target.transform.position, Vector3.up, -120);
sphere.transform.RotateAround(target.transform.position, Vector3.up, -120);
cylinder.transform.RotateAround(target.transform.position, Vector3.up, -120);
}
}
Thanks in advance for helping me!
The easiest way to make an object(s) rotate around a point is to have it/them as a child of the point then rotate the point itself.
Object Hierarchy
Rotator Script to be put on the parent object:
public class RotateObjects : MonoBehaviour
{
[SerializeField] Button rotateButton;
// Start is called before the first frame update
void Start()
{
rotateButton.onClick.RemoveAllListeners();
rotateButton.onClick.AddListener(RotateClockwise);
}
void RotateClockwise()
{
float newRotation = (360 / transform.childCount);
transform.Rotate(new Vector3(0, newRotation, 0));
}
}
You are rotating around an object. Like the earth rotates around the sun; that means it will change its position. Use
transform.rotation = new Quaternion(rotx, roty, rotz, rotw);
or
transform.Rotate(rotx, roty, rotz);
Instead of rotating about a point rotate the shape, it's self.
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;
}
}
im trying to make a mouse trail. So when you hold the mouse button down, it starts the trail and you can move around and then when you release, it dissapers. Im using the trail renderer for this.
Im tryna to replica the blade thats seen in stuff like fruit ninja. So i have an empty game object called blade with a kinematic rigidbody 2d and my blade script.
I then have a trail which is a prefab that i drag into the blade script. Here is the blade script:
bool isCutting = false;
Rigidbody2D rb;
Camera cam;
public GameObject trail;
GameObject currentTrail;
private void Start()
{
rb = GetComponent<Rigidbody2D>();
cam = Camera.main;
}
void Update ()
{
if (Input.GetMouseButtonDown(0))
{
isCutting = true;
currentTrail = Instantiate(trail, transform);
} else if (Input.GetMouseButtonUp(0))
{
isCutting = false;
currentTrail.transform.SetParent(null);
}
if (isCutting)
{
rb.position = cam.ScreenToWorldPoint(Input.mousePosition);
Destroy(currentTrail, 2f);
}
}
The only problem is, when i hold my mouse down, the blade insanely teleports to my mouse position with the trail renderer.
So i think the default blade position is in the middle of the camera, if i drag and hold at the top, u can see the beginning of a blade is a straight line trail from the middle to the top and i want the trail to start where i click.
I hope that makes sense. Thanks
Try changing the last section of the Update() method to:
if (isCutting)
{
currentTrail.transform.position = rb.position = cam.ScreenToWorldPoint(Input.mousePosition);
Destroy(currentTrail, 2f);
}
I'm trying new things and I'm stuck to find a way to drag and drop objects. My player is a square and he have a hand attached. Here is an example:
That red thing in the arm is the "hand", when I press shift it turns green. I made the detection like a ground check. Here is the code:
void Update () {
touch = Physics2D.OverlapCircle(touchDetect.position, 0.01f, objectLayer);
mao1.SetBool("Ligado", ligado);
if (Input.GetKey(KeyCode.LeftShift)) {
ligado = true;
} else {
ligado = false;
}
}
The touchDetect is working fine, because he turns to "true" when touches the box:
My question is: I don't know how to put in the script that I want him to grab the object and drop when I want to. If I need to use Raycast, how would the code looks like?
In order to "grab" an object in unity, simply set the transform.parent of the gameobject you wish to grab, to the transform of the gameobject you wish to grab it with.
For example, in your code you are using Physics2D.OverlapCircle which returns a Collider2D object. You can use that collider to grab on to your gameobject.
Collider2D touch = Physics2D.OverlapCircle(touchDetect.position, 0.01f, objectLayer);
if (Input.GetKey(KeyCode.LeftShift) && touch != null)
{
//grab on to the object
touch.gameObject.transform.parent = this.transform;
//if your box has a rigidbody on it,and you want to take direct control of it
//you will want to set the rigidbody iskinematic to true.
GetComponent<Rigidbody2D>().isKinematic = true;
}
else if( touch != null)
{
//let the object go
touch.gameObject.transform.parent = null;
//if your object has a rigidbody be sure to turn kinematic back to false
GetComponent<Rigidbody2D>().isKinematic = false;
}
Just place a GameObject with SprieRender component attach it to hand tip.
Put this script to that qube and follow what I commented
using UnityEngine;
using System.Collections;
public class collid : MonoBehaviour {
// Use this for initialization
public Sprite mySprite; // Put Here that Qube Sprite
public GameObject Cursorimg; // Put the gameobject here from hierarchy
void Start () {
mySprite = GetComponent<SpriteRenderer>().sprite;
}
// Update is called once per frame
void Update () {
if(Collide conditions) {
Debug.Log(" Collide ");
Cursorimg.GetComponent<SpriteRenderer>().sprite = mySprite;
Cursorimg.GetComponent<SpriteRenderer>().sortingOrder = 3;// U change based on your option
}
if(DropCondition)
{
Debug.Log("Drop");
Cursorimg.GetComponent<SpriteRenderer>().sprite =null;
}
}
If this not works Here is a script that gameobject with Sprite will follow mouse position,
Vector3 pos = Input.mousePosition;
pos.z = Cursorimg.transform.position.z - Camera.main.transform.position.z;
Cursorimg.transform.position = Camera.main.ScreenToWorldPoint(pos);
this code follows in update and Cursorimg is Gameobject
I think This would help you, Let me know further