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.
Related
enter image description here
enter image description here
I made this slingshot with objects in Unity3D. I know im suppose to add components but I dont know exactly which ones. The two yellows would represent as my elastics. Keep in note i have a robot arm made on the side. My claw hand will grab the object holder and pull it back so it can shoot. I need help in
what component should i add to them to make them like rubberband or elastic
how attach them to my objectHolder
how to make it shoot
please help and be patient with me :( Any advice I would appreciate it. Thank you :)
private Animation anim;
Rigidbody rb;
void Start()
{
anim = gameObject.GetComponent<Animation>();
}
void Update()
{
//********************Open pincher ********************
if (Input.GetKey(KeyCode.X))
{
anim.Play("clawopen");
//rb.AddTorque(Vector3.up * speed);
}
//*******************Close pincher ********************
if (Input.GetKey(KeyCode.Y))
{
anim.Play("clawclose");
// rb.AddTorque(Vector3.down * speed);
}
//*******************if not both ********************
else
{
rb.angularVelocity = Vector3.zero;
}
}
}
There is no magic component that will achieve what you want. You should break it down into smaller chunks.
Start with the movement of the object holder. There are different ways to achieve this. If you want to manipulate it via mouse, you can write a script that makes the objectHolder follow mouse movement while a mouse button is pressed. Once you release the mouse button you can let the object holder smoothly go back to it's start position.
For the elastic parts, I would replace the objects with LineRenderers. But also here are different ways to achieve this.
Maybe this tutorial helps you.
attach a spring component to the dark orange round shape and set connectedAnchor to the position of where it should stretch from. You can play around with soeing and damoer to make it more or less stretchy.
I would reccomend a very high spring (spring should be about 100-1000) and mass (mass should be about 5-10) on this. The ball should have a mass of 1 or less.
You can set the Rigidbody.position to the claw's position (while it is closed) Then when it opens' it will release and launch.
For example SlingShot.cs (on the dark orange object.
[HideInInspector] public bool closedClaw = false;
[HideInInspector] public Vector3 clawPos;
Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void Update()
{
if (closedClaw)
{
rb.positon = clawPos;
}
}
And then somewhere in the script that closes the claw
SlingShot ss;
GameObject slingShot;
void Start()
{
ss = slingShot.GetComponent<SlingShot>();
}
...
if (right claw.localEulerAngles.z /*or whatever axis it rotates on*/
> 90f /*or whatever threshHold*/)
{
ss.closedClaw = true;
}
else
{
ss.closedClaw = false;
}
ss.clawPos = rightClaw.transform.position +
leftClaw.transform.position;
ss.clawPos /= 2f;
All of this is untested and may have errors.
If this all works,I will come back later and do the stretchy yellow rubber bands.
Requested by comment Open closing arm
public float speed;
public Transform rightClaw;
public Transform leftClaw;
public Vector3 axis;
public Vector2 minMaxRotation;
void Update()
{
if (Input.GetKey(KeyCode.X))
{
Press(1);
}
if (Input.GetKey(KeyCode.Y))
{
Press(-1);
}
}
void Press(int open)
{
float r = rightClaw.transform.localEulerAngles * axis;
rightClaw.transform.localEulerAngles =
axis * Mathf.Clamp(r + speed * open, minMaxRotation.x, minMaxRotation.y);
float l = leftClaw.transform.localEulerAngles * axis;
leftClaw.transform.localEulerAngles =
axis * Mathf.Clamp(l - speed * open, 180f - minMaxRotation.x, 180f - minMaxRotation.y);
}
I have a character selection screen, when a certain button is pressed the assigned character will slide into view. Unfortunately for one of my character's they transform position seems to change putting them higher, etc making them not appear on the game screen. I'm not sure why as both character's have the same components and no where in code tells them to change position. The only script they have works as slide animation to make seem as though they glide into view instead of just appear. (attached below)
If I change the character's avatar int he animator to the same as the other character's then their position will be correct but then they will not do any animations.
Winter is the working GameObject.
Eunha is the one that changes position. Before the game starts Eunha's transform position is the same as Winter's shown in the Inspector.
private Vector3 startPosition;
private void Awake()
{
finalPosition = transform.position;
startPosition = finalPosition - transform.right * 5.0f;
}
private void Update()
{
transform.position = Vector3.Lerp(transform.position, finalPosition, 0.1f);
}
private void OnEnable()
{
transform.position = startPosition;
} ```
The codeing order seems to be problem. I recommend using 2 empty objects to determine location. This script is more efficient and reliable.
public Transform startPosition; // place on start position
public Transform endPosition; // place on shifted position
public float duration = 1f;
public void OnEnable() => StartCoroutine(Shift());
public IEnumerator Shift()
{
var shiftProgress = 0f;
while (shiftProgress < 1)
{
shiftProgress += Time.deltaTime/duration;
transform.position = Vector3.Lerp(startPosition.position, endPosition.position, shiftProgress);
yield return new WaitForEndOfFrame();
}
}
I am trying to make a game in Unity that when the player (with oculus quest controller) grab the object he can rotate it without moving the object ,on a fixed position. I am having problem with transforming the local position of the controllers to the locale position of the object and rotate it with c#. please help me I am still new in this.
**edit: here is the last I tried:
[Header("Controller")]
public OVRInput.Button grabButton;
public GameObject target;
public float speed=0.01f;
//Quaternion m_MyQuaternion;
public Vector3 targetPosition;
void Update()
{
if (OVRInput.Get(grabButton))
{
targetPosition = new Vector3(taregt_eulerAngX, taregt_eulerAngY, target_eulerAngZ);
if (Vector3.Distance(transform.eulerAngles, targetPosition) > 0.01f)
{
transform.eulerAngles = Vector3.Lerp(transform.rotation.eulerAngles, targetPosition, Time.time * speed);
}
}
}
the target in this case is the left hand, so when I grab the gameobj it will rotate accordingly to the hand(controller)
I am trying to make a 2d plat-former where you see the player from the side. I want him to be continuously moving and you have to press space at the right time so he doesn't fall. Right now everything works but he doesn't collide with the ground. I want it to be like he's running behind a wall so I want to ignore a certain layer I have made and collide with the boxes below that. So far I have tried ray casting, watched multiple tutorials, and did box collisions. Box collisions worked but to get all the platforms counted as solid I'd need like 50 box colliders. Here is my current code:
public int playerSpeed = 10;
public int playerJumpPower = 1250;
public float moveX;
public float playerYSize = 2;
public LayerMask mainGround;
public float playerFallSpeed = 5;
void Awake(){
}
// Update is called once per frame
void Update()
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, new Vector2(10, 0));
if(hit.distance < 0.7f){
print("hi");
}
Vector3 characterTargetPosition = new Vector3(transform.position.x + playerSpeed, transform.position.y, transform.position.z);
transform.position = Vector3.Lerp(transform.position, characterTargetPosition, playerSpeed * Time.deltaTime);
if(Input.GetKeyDown("space")){
// float playerTargetPosY = transform.position.y + playerJumpPower;
// Vector3 characterTargetPosition = new Vector3(transform.position.x, playerTargetPosY, transform.position.z);
// transform.position = Vector3.Lerp(transform.position, characterTargetPosition, playerJumpPower * Time.deltaTime);
gameObject.GetComponent<Rigidbody2D>().AddForce(Vector2.up * playerJumpPower);
}
//PlayerMove();
}
I have a rigidBody2D on my player so right now he just falls through the ground but the jump does work. If there is any easy way to do this. Like some script, a tutorial, or website I'm open for it. Please help.
Do you have a Rigidbody2D in your player? Things that will move usually have to have a RigidBody
(sorry for posting this as an answer. Cant comment yet)
EDIT:
try this:
Rigidbody2D rb;
void Awake()
{
rb = GetComponent<Rigidbody2D>();
}
//Physics usually are done in FixedUpdate to be more constant
public void FixedUpdate(){
if (Input.GetKeyDown("space"))
{
if(!rb.simulated)
//player can fall
rb.simulated = true;
rb.AddForce(Vector2.up * playerJumpPower);
}
else
{
//third argument is the distance from the center of the object where it will collide
//therefore you want the distance from the center to the bottom of the sprite
//which is half of the player height if the center is actually in the center of the sprite
RaycastHit2D hit = Physics2D.Raycast(transform.position, -Vector2.up, playerYSize / 2);
if (hit.collider)
{
//make player stop falling
rb.simulated = false;
}
}
}
If the player is the only thing that will collide with something you can just take out the colliders from the object that the player will not collide with.
Else you can check for the layer of the collided object with hit.collider.gameObject.layer and decide if the player will collide with that layer or not
(note that you have to compare with the index of the layer. If you want to get the index by its name you can use LayerMask.NameToLayer(/*layer name*/))
you will have to do rb.simulated = true everytime you want to do something with the RigidBody (like AddForce())
hope it helped :)
Tried doing 2D and 3D. I want the player to click and drag objects to stack them. I tried copying and pasting code from a video and it still didn't work.
UPDATE! Now it kinda works but it teleports the object far off the screen. I want the player to be able to smoothly click and drag the object.
The code as of now:
using UnityEngine;
using System.Collections;
public class DragObject : MonoBehaviour
{
private float deltax, deltay;
private void OnMouseDown()
{
deltax = 1f;//Camera.main.ScreenToWorldPoint(Input.mousePosition).x - transform.position.x;
deltay = 1f; //Camera.main.ScreenToWorldPoint(Input.mousePosition).y - transform.position.y;
}
private void OnMouseDrag()
{
Vector2 currentTouchPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
transform.position = new Vector2((Input.mousePosition).x - deltax, (Input.mousePosition.y) - deltay);
}
}
It's not clear at all what exact problem is for you, you obviously need to give more info and describe your issue better. But I'll try to help as I can.
To get mouse position you need to use:
Input.mousePosition
If you need mouse position in 2d coordinates you can use this:
Vector2 currentTouchPos = mainCamera.ScreenToWorldPoint(Input.mousePosition);
This method works regardless of your camera type or how the camera moves between frames
First, when the object is first clicked, get the plane at the object's position perpendicular to the direction of the camera:
Plane dragPlane = new Plane(Camera.main.transform.forward, transform.position);
Then, get the ray coming out of the camera from where the mouse is:
Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition);
Find out where that ray intersects using Plane.Raycast:
float enter = 0.0f;
if (dragPlane.Raycast(camRay, out enter))
{
Vector3 fingerPosition = camRay.GetPoint(enter);
This gives you a position in world space, which is where you can consider the finger to currently be at. Just put the object there:
transform.position = fingerPosition;
Altogether it might look like this:
void OnMouseDrag()
{
Plane dragPlane = new Plane(Camera.main.transform.forward, transform.position);
Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition);
float enter = 0.0f;
if (dragPlane.Raycast(camRay, out enter))
{
Vector3 fingerPosition = camRay.GetPoint(enter);
transform.position = fingerPosition;
}
}
No OnMouseDown necessary.
An alternative is to keep track of how fingerPosition changes over time, and Translate the transform based on that. Keeping as much math as possible in world units makes it easy:
Vector3 prevFingerPosition = Vector3.zero;
// Same code as before but we use it in different places so it goes in its own method.
// If the Raycast fails, return a decent guess.
private Vector3 GetMouseOnPositionInWorldSpace()
{
Plane dragPlane = new Plane(Camera.main.transform.forward, transform.position);
Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition);
float enter = 0.0f;
if (dragPlane.Raycast(camRay, out enter))
{
return camRay.GetPoint(enter);
}
return prevFingerPosition;
}
void OnMouseDown()
{
prevFingerPosition = GetMouseOnPositionInWorldSpace();
}
void OnMouseDrag()
{
Vector3 fingerPosition = GetMouseOnPositionInWorldSpace();
transform.Translate(fingerPosition-prevFingerPosition);
prevFingerPosition = fingerPosition;
}
There is an issue behind using OnMouseDrag to handle dragging objects around is that if the cursor moves too quickly, it can move off the object in the span of a single frame, and it will "drop" the object.
If that's not desirable, you might want to set a bool to be true in OnMouseDown that remembers that the object was clicked and a move the logic out of OnMouseDrag and into Update where it can do its thing if that bool is true. And when the mouse is released, set that bool back to false.