Create scipt to allow dragging and snapping - c#

I have a script that lays out prefabs, on a surface, by creating an array of vectors. Each array contain 14 vectors, which produce evenly spaced locations, on a plane.
public List<Vector3> handEastVectorPoints = new List<Vector3>();
public List<Vector3> handSouthVectorPoints = new List<Vector3>();
public List<Vector3> handNorthVectorPoints = new List<Vector3>();
public List<Vector3> handWestVectorPoints = new List<Vector3>();
Then when a prefab is instantiated, my code grabs the correct vector, from the correct array, and add the prefab, like this:
public IEnumerator addTileInHand(GameObject tile, Vector3 v, float time, Vector3 rotationVector, int pos)
{
Quaternion rotation = Quaternion.Euler(rotationVector);
GameObject newTile = Instantiate(tile, v, rotation);
tile.GetComponent<TileController>().canDraw = true;
tile.GetComponent<TileController>().gameStatus = getCurrentGameState();
tile.GetComponent<TileController>().currentTurn = sfs.MySelf.Id;
PlayersHand[pos] = newTile;
yield return new WaitForSeconds(time);
}
When this function is called, its passed a vector, which comes from the above array. The prefab is created, and added to an array of gameObjects.
What I now want to do is allow the player to drag and drop the to various "hot spots", as defined in the first set of arrays (again, their are 14 vectors) and drop them. If there is already a prefab in that position, I would want it to snap to the new position that just opened up. If the tile is dropped anywhere else, it should snap back to its original location. I also just want the object to be dragged either left or right, no need to up and down or up through the Y axis.
Can anyone point me to similar script that might help or provide assistance?
thanks
Edit: Draggable Script Got this working. You can only drag on the X axis, but for my use case, this works.
public class DraggableObject : MonoBehaviour {
private bool dragging = false;
private Vector3 screenPoint;
private Vector3 offset;
private float originalY;
void Start()
{
originalY = this.gameObject.transform.position.y;
}
void OnMouseDown()
{
screenPoint = Camera.main.WorldToScreenPoint(gameObject.transform.position);
offset = gameObject.transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, originalY, screenPoint.z));
}
void OnMouseDrag()
{
Vector3 cursorPoint = new Vector3(Input.mousePosition.x, originalY, screenPoint.z);
Vector3 cursorPosition = Camera.main.ScreenToWorldPoint(cursorPoint) + offset;
transform.position = cursorPosition;
dragging = true;
}
void OnMouseUp()
{
dragging = false;
}
}

I have done a similar thing for my game. This is quite easy. Follow the following steps and let me know if you have any further queries:
Write a script (let's say HolderScript ) and add a boolean occupied to it. Add an OnTriggerEnter (A Monobehaviour function) to the script and update the boolean accordingly:
Case 1 - Holder is not occupied - Make boolean to true and disable Colliders if the prefab is dropped there, else do nothing.
Case 2 - Holder is occupied - if occupied prefab is removed then in OnTriggerExit function make boolean to false and enable colliders.
Write a Script (let's say DraggableObject) and set the drag and isDropped boolean accordingly and you can use Lerp function for snapping back.
Attach the holder script to the droppable area and DraggableObject script to the draggable objects.
This should solve your issue.

Related

Moving Prefabs, Unity Spawner, Move Position

I have one problem. I want my prefabs to spawn every time my player picks them up. I did research on Google and YouTube and I tried to use the random function and instantiate. I don't know how to use them. I wrote this code I saw on YouTube and my prefab Sphere moves like 1cm to z position. I want to every time when I pick up object or my player go to spawn more of this on the z position. How do I do this?
My smaller script:
public GameObject Sphere;
public float zrange;
// Use this for initialization
void Start () {
RandomPosition();
}
void RandomPosition()
{
zrange = Random.Range(0f, 2f);
this.transform.position = new Vector3(0, 0, zrange);
}
You achieve that by not messing with the x and y values (your code sets them both to 0).
Vector3 p = transform.position;
p.z = zrange;
transform.position = p;
This assumes that your code to instantiate the object is already correctly placing the object. If not, more information is needed.

Why gameObject get such positions in Unity?

I want to create 2d game.
Structure of objects
[]
"View" present a square where everything happens.
"Initial" This is the same square with a mask
"SquareList" is empty panel.
Why am I doing this?
I created a script where when I click on the screen, a new square is added to the SquareList. SquareList is the parent. I created a script to determine the position of the cursor.
createPos = new Vector2 (Input.mousePosition.x, Input.mousePosition.y);
Next, I want to set for new object position = cursor position.
Cursor.position equivalent = (562,1134 / 242.6486)
But the new object get position (94931.29 / 103365.6 / -17280)
But if I set position of new object to = new Vector(0,0);
Then everything is fine
What is the problem?
Input.mousePosition returns position in pixel-coordinate. You have to use Camera.main.ScreenToWorldPoint to convert it to world position then assign that to the object. If 3D Object, add an offset to it before converting with ScreenToWorldPoint.
public GameObject prefab;
public float offset = 10f;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector3 clickPos;
clickPos = Input.mousePosition;
clickPos.z = offset;
clickPos = Camera.main.ScreenToWorldPoint(clickPos);
GameObject obj = Instantiate(prefab, clickPos, Quaternion.identity);
}
}
Hello nameless brother/sister! I believe you are seeing this issue because your game object's coordinates are affected by its parent's coordinates. You may need to use localPosition to find more relative coordinates.
More info: https://docs.unity3d.com/ScriptReference/Transform-localPosition.html

dragging/dropping and triggers

I am trying to create a drag and drop functionality. I have a game object that lies on a cube (like a table). I want to allow the player to drag and drop this object to multiple "hot spots". At the same time, while this is a 3D game, they should not be allowed to drag the object off of the table, above it, bellow it, etc. Just drag across the top of the table. The way I have this setup is like this:
On the table top (a cube with a rigid body)I have two planes. This plane is just decorative to let the user see where to drop to. Each of these planes has a child plane (a much smaller area, centered in the parent). This child has a Box Collider (isTrigger= true) that extends high up into the air, like a pole sticking out of the ground)
I then have a cube that has a rigid body and a box collider (isTrigger= true). This cube is instantiated onto the "table" top, over one of the hot spots.
HotSpotCode.cs
public class HotSpotCodeScript : MonoBehaviour {
public void OnTriggerEnter(Collider other){
DraggableObject ds = other.GetComponent<DraggableObject>();
ds.startPos = this.gameObject.transform.position;
}
}
DraggableObject.cs
public class DraggableObject : MonoBehaviour {
float lerpTime = 1f;
float currentLerpTime;
float moveDistance = 10f;
Plane plane;
public Vector3 startPos, endPos;
public bool drag;
Vector3 position;
void Start(){
startPos = transform.position;
endPos = transform.position + transform.up * moveDistance;
}
void OnMouseDown(){
plane = new Plane(Vector2.up, transform.position);
drag = true; // start dragging
}
void OnMouseUp(){
lerp();
drag = false;
}
private void lerp() {
currentLerpTime += Time.deltaTime;
if (currentLerpTime > lerpTime)
{
currentLerpTime = lerpTime;
}
float perc = currentLerpTime / lerpTime;
transform.position = Vector3.Lerp(startPos, endPos, perc);
}
public void Update()
{
position = transform.localPosition;
if (drag)
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
float distance;
if (plane.Raycast(ray, out distance))
{
transform.position = ray.GetPoint(distance);
}
}
}
public void OnTriggerEnter(Collider other){
endPos = other.transform.position;
}
}
This code mostly works somewhat. I am able to drag the object around and it won't leave the plan it it resides on, for short distances. But, the further away I drag the object, then release it, the more likely it is that the object will not snap back to its original start position and will get stuck somewhere along the way. I am not sure if this is an issue with the Lerp() function or maybe how the plane it is dragged across is created. Can anyone help me out? If you need more info, please let me know. I've been working on this for weeks and haven't gotten much further that this.
Another issue that pops up, as well, is that when the object that is being dragged is released, and comes into contact with the hot spot's collider, it stops the object at its exact point. So, if a corner of the cube comes into contact with the collider, the cube will not come to rest centered on the hot spot.

Stop rigidbodies from moving after colliding

I have a very simple case: 2 cubes on a plane, the player can move a cube via mouse input.
Each cube has a collider and rigidbody attached, as well as a high friction material with 0 bounce.
What I want to achieve is: if one cube is to come into contact with the other, I want to stop both cubes from pushing each other or going into one another - I want them to act as walls to each other.
What I'm getting is this:
What I have tried is to switch on the IsKinematic option of the cubes while I drag them, but this lead to no result.
Here is my code:
private Vector3 screenPoint;
private Vector3 offset;
private Vector3 oldPosition;
private Rigidbody rBody;
private bool dragging = false;
void Awake()
{
rBody = gameObject.GetComponent<Rigidbody>();
}
void OnMouseUp()
{
dragging = false;
rBody.isKinematic = false;
}
void FixedUpdate()
{
if(dragging)
{
Vector3 cursorPoint = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z);
Vector3 cursorPosition = Camera.main.ScreenToWorldPoint(cursorPoint) + offset;
cursorPosition.y = oldPosition.y;
rBody.MovePosition(cursorPosition);
}
}
void OnMouseDown()
{
screenPoint = Camera.main.WorldToScreenPoint(rBody.position);
offset = rBody.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z));
oldPosition = rBody.position;
}
void OnMouseDrag()
{
dragging = true;
rBody.isKinematic = true;
}
Any help is appreciated. Thanks.
Did you try to put a physic material and tweek the settings (especially the friction settings)?
If you did, and didn't achieve what you want, you could use MonoBehaviour.OnCollisionExit to detect when your objects aren't in contact anymore and either set the velocity to 0 or lock the position. If you lock the position, you'll probably need a coroutine to unlock it a while later.
This solution may lead to strange bugs later (in case of bumpy move for exemple, where your objects lose contact for one frame only). I would prefer using the physic material if possible.
Might not be the best solution but maybe try destroying the rigid body component after the collision enter, this way the cube will become a static object, thus turning into a wall. You can add the rigidbody again shortly afterwards.

How can i create an in-game system that lets me draw lines with the cursor that can act as walkable terrain

Hi im creating a 2D platformer, and i want to be able to draw for example lines ingame (playmode) with my cursor (like paint) that can act as walkable terrain. Im pretty new to coding and c# which im using, and im having a really hard time imagining how this can be achieved let alone if its possible? Would appreciate if you guys could give me some ideas and maybe could help me push it in the right direction. Thanks!
EDIT:
So i got got some code now which makes me being able to draw in playmode. The question now is how i can implement a type of collider to this? Maybe each dot can represent a little square or something? How can i go through with it? Heres some code. Thanks.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class DrawLine : MonoBehaviour
{
private LineRenderer line;
private bool isMousePressed;
private List<Vector3> pointsList;
private Vector3 mousePos;
// Structure for line points
struct myLine
{
public Vector3 StartPoint;
public Vector3 EndPoint;
};
// -----------------------------------
void Awake()
{
// Create line renderer component and set its property
line = gameObject.AddComponent<LineRenderer>();
line.material = new Material(Shader.Find("Particles/Additive"));
line.SetVertexCount(0);
line.SetWidth(0.1f,0.1f);
line.SetColors(Color.green, Color.green);
line.useWorldSpace = true;
isMousePressed = false;
pointsList = new List<Vector3>();
}
// -----------------------------------
void Update ()
{
// If mouse button down, remove old line and set its color to green
if(Input.GetMouseButtonDown(0))
{
isMousePressed = true;
line.SetVertexCount(0);
pointsList.RemoveRange(0,pointsList.Count);
line.SetColors(Color.green, Color.green);
}
else if(Input.GetMouseButtonUp(0))
{
isMousePressed = false;
}
// Drawing line when mouse is moving(presses)
if(isMousePressed)
{
mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mousePos.z=0;
if (!pointsList.Contains (mousePos))
{
pointsList.Add (mousePos);
line.SetVertexCount (pointsList.Count);
line.SetPosition (pointsList.Count - 1, (Vector3)pointsList [pointsList.Count - 1]);
}
}
}
}
Regarding means and tools:
Input.mousePosition
Input.GetKey, Input.GetKeyDown, Input.GetKeyUp
Line renderer manual
Line renderer scripting API
Regarding general idea:
Your script may use Input.GetKey to trigger the feature functionality (keyboard key, for example).
When the feature is activated the script awaits of mouse button to be clicked by the means of Input.GetKeyUp and when the event happens it must capture current mouse position using Input.mousePosition.
Only two points are necessary to build a segment of line, so when the script detects input of the second point it may create game object that will represent a piece of walkable terrain.
Visually such an object may be represented with line renderer. To enable iteraction with other game objects (like player character) it is usually enough to enhance object with a collider component (presumably, with a BoxCollider).
Regarding of how-to-add-a-collider:
GameObject CreateLineCollider(Vector3 point1, Vector3 point2, float width)
{
GameObject obj = new GameObject("LineCollider");
obj.transform.position = (point1+point2)/2;
obj.transform.right = (point2-point1).normalized;
BoxCollider boxCollider = obj.AddComponent<BoxCollider>();
boxCollider.size = new Vector3( (point2-point1).magnitude, width, width );
return obj;
}
You can add collider to object with line renderer but you still must orient it properly.
Example of integration in your code:
void Update ()
{
...
// Drawing line when mouse is moving(presses)
if(isMousePressed)
{
mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mousePos.z=0;
if (!pointsList.Contains (mousePos))
{
pointsList.Add (mousePos);
line.SetVertexCount (pointsList.Count);
line.SetPosition (pointsList.Count - 1, (Vector3)pointsList [pointsList.Count - 1]);
const float ColliderWidth = 0.1f;
Vector3 point1 = pointsList[pointsList.Count - 2];
Vector3 point2 = pointsList[pointsList.Count - 1];
GameObject obj = new GameObject("SubCollider");
obj.transform.position = (point1+point2)/2;
obj.transform.right = (point2-point1).normalized;
BoxCollider boxCollider = obj.AddComponent<BoxCollider>();
boxCollider.size = new Vector3( (point2-point1).magnitude, ColliderWidth , ColliderWidth );
obj.transform.parent = this.transform;
}
}
}

Categories