Placing Furniture on drag in AR Core - unity3d

I am new to ARCore in Unity. My colleague has already made this app on iOS. But I am having some problems dragging the object around and set it on a position. Also you can rotate it with 2 fingers. Here I am giving the ARKit equivalent code of m dragging. Can anyone help me with doing the same on AR Core
switch (Input.touchCount) {
case 1:
if (touch.phase == TouchPhase.Began || touch.phase == TouchPhase.Moved) {
var screenPosition = Camera.main.ScreenToViewportPoint(touch.position);
ARPoint point = new ARPoint {
x = screenPosition.x,
y = screenPosition.y
};
// prioritize reults types
ARHitTestResultType[] resultTypes = {
//ARHitTestResultType.ARHitTestResultTypeExistingPlaneUsingExtent,
// if you want to use infinite planes use this:
ARHitTestResultType.ARHitTestResultTypeExistingPlane,
ARHitTestResultType.ARHitTestResultTypeHorizontalPlane,
ARHitTestResultType.ARHitTestResultTypeFeaturePoint
};
foreach (ARHitTestResultType resultType in resultTypes) {
if (HitTestWithResultType(point, resultType)) {
return;
}
}
}
break;
case 2:
if (touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Ended) {
Vector2 direction = touch.deltaPosition;
float yAngleIncrementVal = touch.deltaPosition.magnitude;
Vector3 currentRotation = transform.rotation.eulerAngles;
if (direction.x > 0) {
currentRotation.y += (yAngleIncrementVal * rotationScale);
}
else {
currentRotation.y -= (yAngleIncrementVal * rotationScale);
}
transform.rotation = Quaternion.Euler(currentRotation);
}
break;
}
}
This script will attached to the gameobject which will move across the plane. How can I drag a gameobject and update the anchors so that it stays on the plane. Thank You

I am pretty much doing the same thing, and this is a simplified version of my own code:
case 1:
if (Input.GetTouch(0).phase == TouchPhase.Began)
{
touchOffset = ARCamera.WorldToScreenPoint(gameObject.position) - new Vector3(Input.GetTouch(0).position.x, Input.GetTouch(0).position.y, 0);
}
RaycastHit hit;
LayerMask layers = (1 << LayerMask.NameToLayer(layersOfGroundObjects))
Ray ray = ARCamera.ScreenPointToRay(Input.GetTouch(0).position + new Vector2(mouseOffset.x, mouseOffset.y));
if (!ClickedOnUI && Physics.Raycast(ray, out hit, Mathf.Infinity, layers))
{
if(Input.GetTouch(0).phase == TouchPhase.Began)
{
touchOffset = hit.point - gameObject.position;
}
if (Input.GetTouch(0).phase == TouchPhase.Moved)
{
gameObject.position = hit.point - touchOffset ;
}
}
case 2:
float currentTouchDistance = Vector2.Distance(touches[0].position, touches[1].position);
float diff_y = touches[0].position.y - touches[1].position.y;
float diff_x = touches[0].position.x - touches[1].position.x;
float currentTouchAngle = Mathf.Atan2(diff_y, diff_x) * Mathf.Rad2Deg;
if (isFirstFrameWithTwoTouches)
{
cachedTouchDistance = currentTouchDistance;
cachedTouchAngle = currentTouchAngle;
isFirstFrameWithTwoTouches = false;
}
float angleDelta = currentTouchAngle - cachedTouchAngle;
float scaleMultiplier = (currentTouchDistance / cachedTouchDistance);
float scaleAmount = cachedAugmentationScale * scaleMultiplier;
float scaleAmountClamped = Mathf.Clamp(scaleAmount, scaleRangeMin, scaleRangeMax);
if (enableRotation)
{
gameObject.localEulerAngles = cachedAugmentationRotation - new Vector3(0, angleDelta * 3f, 0);
}
if (enableRotation && enableScaling)
{
gameObject.localScale = new Vector3(scaleAmountClamped, scaleAmountClamped, scaleAmountClamped);
}
I also use a touchOffset which is the distance away from the object's pivot (position) that the user has clicked to start dragging. I apply that offset to the final position every time it is dragged. It makes dragging feels much more natural. I also use it for a lot more in my own code, therefore it was quite important for me, but that is irrelevant for this question so I removed the rest of the code.
Moreover, I use many layers for what is considered ground, because I can put my furniture on more surfaces other than the ARCore tracked plane. If you only care about the tracked plane then either assign a layer to it, or use ARCore's raycast instead of Unity's one. Or just use my Eazy ARCore Interface plugin that can do both (uses layers for tracked planes and makes things easier with raycasting for multiple surfaces)

Related

My variable changes after my gameobject moves, when there is no input to change the variable

I'm trying to get the last position of my gameobject, before it stops moving, but the "lastPos" variable keeps being magically changed to the current position, after my gameobject moves. (I'm probs being stupid)
The reason I'm doing this is to try to add smoothing to the end of the movement of my gameobject, instead of stopping suddenly after it being let go, I want it to gradually slide to a stop. So I was thinking if I found the difference between last position before it stops and the current one, I could add some velocity to the end of it, depending on the distance between the two points, the more velocity it would add.
I couldn't find a tutorial on how to add this.
The problem is probably in the last 3 If statements.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Move : MonoBehaviour
{
bool dragging = false;
Vector2 offset;
Transform toDrag;
Transform lastPos;
float posDif;
private void Start()
{
Application.targetFrameRate = 60;
}
void Update()
{
Vector2 v2;
if(Input.touchCount != 1)
{
dragging = false;
return;
}
Touch touch = Input.touches[0];
Vector2 pos = touch.position;
if(touch.phase == TouchPhase.Began)
{
toDrag = gameObject.transform;
v2 = new Vector2(pos.x, pos.y);
v2 = Camera.main.ScreenToWorldPoint(v2);
offset.x = toDrag.position.x - v2.x; offset.y = toDrag.position.y - v2.y;
dragging = true;
Debug.Log("TOUCH");
}
if (dragging && touch.phase == TouchPhase.Moved)
{
if (touch.phase != TouchPhase.Stationary && touch.phase != TouchPhase.Ended && touch.phase != TouchPhase.Canceled)
{
lastPos = gameObject.transform;
}
v2 = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
v2 = Camera.main.ScreenToWorldPoint(v2);
toDrag.position = v2 + offset;
gameObject.transform.position = new Vector2(v2.x + offset.x, -2.67f);
Debug.Log("Last Pos: " + lastPos.position.x);
}
if (dragging && (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled))
{
dragging = false;
posDif = gameObject.transform.position.x - lastPos.position.x;
gameObject.GetComponent<Rigidbody2D>().velocity = new Vector2(posDif * 10f, 0f);
Debug.Log("Current Pos: " + gameObject.transform.position.x);
Debug.Log(posDif);
}
}
}
Your lastPos is set to your current gameObject transform. It’s a class, so a reference object, so you’ll always have the latest values.
If you change the type of the lastPos variable to be a Vector3 you’ll be storing that value, at the time you record it. transform.position is a Vector3 which is a struct (a value type).
...
private Vector3 lastPos;
...
if (dragging && touch.phase == TouchPhase.Moved)
{
lastPos = gameObject.transform.position;
v2 = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
v2 = Camera.main.ScreenToWorldPoint(v2);
toDrag.position = v2 + offset;
gameObject.transform.position = new Vector2(v2.x + offset.x, -2.67f);
Debug.Log("Last Pos: " + lastPos);
}
If what I’m talking about sounds alien, here’s the official docs at Microsoft describing the difference between structs (value types) and classes (reference types). Choosing Between Class and Struct

Unity Mobile - How to make fluid swipe to move movement?

So I am working on a personal project, and I had this idea for a movement that I don't know to realize. So the project(game) is for phone, and what I want to, is to when I swap in a direction, that vector becomes its closest axis (ex: 70 degrees will be (0,1) while 35 will be (1,0)). So when I get a swipe, I want my character to move in that direction. MAIN ISSUE is I don't know how to make him dash to that location. I know how to make him TELEPORT, but not how to make him DASH. I tried going for Vector2.Lerp, but it is a bit buggy and feels like character does not react on time. Here is the script:
private void Update()
{
if (gameObject.GetComponent<OnDeath>().isDead == false)
{
if (trigger)
{
fractionOfWayThere += 0.1f;
transform.position=Vector2.Lerp(transform.position, finish, fractionOfWayThere);
isMoving = true;
}
if ((Vector2)transform.position == finish)
{
isMoving = false;
animator.SetBool("isMoving", false);
}
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
startPos = cam.ScreenToWorldPoint(touch.position);
}
if (touch.phase == TouchPhase.Ended)
{
endPos = cam.ScreenToWorldPoint(touch.position);
destination = endPos - startPos;
if (Mathf.Abs(destination.x) > Mathf.Abs(destination.y))
{
destination = new Vector2(Funkcija(destination.x), 0);
}
if (Mathf.Abs(destination.x) < Mathf.Abs(destination.y))
{
destination = new Vector2(0, Funkcija(destination.y));
}
if (Mathf.Abs(destination.x) == Mathf.Abs(destination.y))
{
destination = new Vector2(Funkcija(destination.x), 0);
}
finish = (Vector2)transform.position + destination.normalized;
if (Physics2D.OverlapCircle(finish, 0.1f, spike) != null)
{
if (finish.x < transform.position.x)
{
sr.flipX = true;
}
else if (finish.x > transform.position.x)
sr.flipX = false;
trigger = true;
fractionOfWayThere = 0;
isMoving = true;
animator.SetBool("isMoving", true);
}
else
{
finish = transform.position;
}
}
}
}
}
Most of the code is getting the swipe into its axis and flipping sprite, only thing regarding the movement is this:
if (trigger)
{
fractionOfWayThere += 0.1f;
transform.position=Vector2.Lerp(transform.position, finish, fractionOfWayThere);
isMoving = true;
}
Playtesting, this feels realy weird. I tried Vector2.MovePosition(), rb.AddForce() and rb.MovePosition. Any other way to do this, or even using those functionts I have not thought of? All feedback would be highly appreciated!
Lerp is a good Start, but it's linear. A Dash would typically start instantly and slow down over time.
to achieve framerate independent movement, you should use Time.deltaTime in Update. It's neglectable in FixedUpdate as these occur at (almost) fixed timesteps.
You can achieve that by adding an AnimationCurve to your code:
[...]
public AnimationCurve dashCurve = new AnimationCurve.Linear(0,0,1,1); // modify this in inspector!
[...]
if (trigger)
{
fractionOfWayThere += 0.5f * Time.deltaTime; // fractionOfWayThere takes 0.5 second to grow from 0 to 1.
transform.position = Vector2.Lerp(transform.position, finish, dashCurve.Evaluate(fractionOfWayThere));
isMoving = true;
}
Set up the AnimationCurve like this:

Moving object with raycast issues

I have written a script where a gameobject is intended to move to a raycast.point thrown from the player camera. For the most part this works fine, however there are times (approximately when camera is 45 degrees up from the object) when the object rapidly moves towards the camera (i.e. raycast source).
I have tried a number of approaches attempting to resolve this, however I can’t seem to dig out the root of this issue. Managed to prevent this from occurring by deactivating the collider attached to the object being moved. However I need the collider for various reasons so this approach is not appropriate.
If anyone can provide any pointers as to where I am going wrong I would be incredibly grateful.
NB: coding in uJS
Many thanks in advance, Ryan
function FixedUpdate() {
if (modObj != null && !guiMode) {
//Panel Control
if (!selectObjPanel.activeSelf && !modifySelectObjPanel.activeSelf) //if the selectpanel not open and modSelect not already activated
{
activateModSelectObjPanel(true); //activate it
} else if (selectObjPanel.activeSelf) {
activateModSelectObjPanel(false);
}
//Move
if (Input.GetKey(KeyCode.E)) {
if (Input.GetKeyDown(KeyCode.E)) {
// modObj.GetComponent(BoxCollider).enabled = false;
modObj.GetComponent(Rigidbody).isKinematic = true;
modObj.GetComponent(Rigidbody).useGravity = false;
//
initPos = modObj.transform.position;
var initRotation = modObj.transform.rotation;
}
moveObject(modObj, initPos, initRotation);
} else {
// modObj.GetComponent(BoxCollider).enabled = true;
modObj.GetComponent(Rigidbody).isKinematic = false;
modObj.GetComponent(Rigidbody).useGravity = true;
}
}
}
function moveObject(modObj: GameObject, initPos: Vector3, initRotation: Quaternion) {
//Debug.Log("Moving Object");
var hit: RaycastHit;
var foundHit: boolean = false;
foundHit = Physics.Raycast(transform.position, transform.forward, hit);
//Debug.DrawRay(transform.position, transform.forward, Color.blue);
if (foundHit && hit.transform.tag != "Player") {
//Debug.Log("Move to Hit Point: " + hit.point);
modifyObjGUIscript.activateMoveDisplay(initPos, hit.point);
var meshHalfHeight = modObj.GetComponent. < MeshRenderer > ().bounds.size.y / 2; //helps account for large and small objects
// Debug.Log("CurObj Mesh Min: " + meshHalfHeight);
// modObj.transform.position = hit.point; //***method 01***
// modObj.transform.position = Vector3.Lerp(initPos, hit.point, speed); //***method 02***
// modObj.transform.position = Vector3.SmoothDamp(initPos, hit.point, velocity, smoothTime); //***method 02***
var rb = modObj.GetComponent. < Rigidbody > ();
rb.MovePosition(hit.point); //***method 03***
modObj.transform.position.y = modObj.transform.position.y + meshHalfHeight + hoverHeight;
modObj.transform.rotation = initRotation;
}
}
Turns out the issue was being caused by the raycast hitting the object being moved. Resolved this by only allowing hits from the terrain to be used as points to move to.
if(foundHit && hit.transform.tag == "Terrain")

Character is not colliding with other other objects in Unity3D

I'm working on a game right now (first 3D styled game), and I have a problem with my character colliding. I have a Player object, which has another object(s) as the real moveable characters (now I have only one). I have rigidbody and box collider too attached to my character. I have made a level (with hand-placed platforms), and I would like avoid my character of falling of the platforms, while the user control it. I have tried to place a cube on the side of the platform(s), with box collider attached to it, but the character not detect the colliding for some reasons. I would like my character to be stopped by the collider "cube", but it doesn't happen.
I use this script to move my object (attached to my character Player object) :
public class Bounce : MonoBehaviour {
float lerpTime;
float currentLerpTime;
float perc = 1;
Vector3 startPos;
Vector3 endPos;
bool firstInput;
public bool justJump;
public GameObject player;
// Update is called once per frame
void Update () {
if (Input.GetButtonDown("up") || Input.GetButtonDown("down") || Input.GetButtonDown("left") || Input.GetButtonDown("right")) {
if (perc == 1) {
lerpTime = 1;
currentLerpTime = 0;
firstInput = true;
justJump = true;
}
}
startPos = gameObject.transform.position;
if (Input.GetButtonDown("up") && gameObject.transform.position == endPos) {
endPos = transform.position + player.transform.rotation * (new Vector3(0, 0, 1f));
}
if (Input.GetButtonDown("down") && gameObject.transform.position == endPos) {
endPos = transform.position + player.transform.rotation * (new Vector3(0, 0, -1f));
}
if (firstInput == true) {
currentLerpTime += Time.deltaTime * 5;
perc = currentLerpTime / lerpTime;
gameObject.transform.position = Vector3.Lerp(startPos, endPos, perc);
if (perc > 0.8f) {
perc = 1;
}
if (Mathf.Round(perc) == 1) {
justJump = false;
}
}
}
void OnCollisionEnter(Collision collision) {
Debug.Log("!!!!!!!");
}
}
And I use this script on the character itself: (to rotate and animate it)
Code (csharp):
public class AnimationController : MonoBehaviour {
Animator anim;
public GameObject thePlayer;
// Use this for initialization
void Start () {
anim = gameObject.GetComponent<Animator>();
}
// Update is called once per frame
void Update () {
Bounce bounceScript = thePlayer.GetComponent<Bounce>();
if (bounceScript.justJump == true) {
anim.SetBool("Jump", true);
}
else {
anim.SetBool("Jump", false);
}
if (Input.GetButtonDown("right")) {
//transform.rotation *= Quaternion.Euler(0,30,0);
transform.RotateAround(transform.position, Vector3.up, 90);
}
if (Input.GetButtonDown("left")) {
transform.Rotate (0, -90, 0, 0);
}
}
}
It's only colliding when the cube not isKinematic, but it doesn't stop my player from getting through the cube for some reason.
I have read some sites about problems like this, but nothing helped yet.
It would be great if you can give me any code improvements :)
EDIT1:
Sorry, for answering so late, I couldn't do anything with my game in the last few days. Today I have tried to do something with raycasts, you can see my code, for my first try just in the update method:
void Update () {
if (Input.GetButtonDown("up") || Input.GetButtonDown("down") || Input.GetButtonDown("left") || Input.GetButtonDown("right")) {
if (perc == 1) {
lerpTime = 1;
currentLerpTime = 0;
firstInput = true;
justJump = true;
}
}
startPos = gameObject.transform.position;
/* if (Input.GetButtonDown("right") && gameObject.transform.position == endPos) {
//endPos = new Vector3(transform.position.x + 0.5f, transform.position.y, transform.position.z);
}
if (Input.GetButtonDown("left") && gameObject.transform.position == endPos) {
endPos = new Vector3(transform.position.x - 0.5f, transform.position.y, transform.position.z);
}*/
Vector3 fwd = player.transform.TransformDirection(Vector3.forward);
RaycastHit objectHit;
if (Physics.Raycast(player.transform.position, fwd, out objectHit, 2)) {
if (objectHit.collider.tag == "Wall") {
Debug.Log("WALL RAYCAST HIT!");
}
}
if (Input.GetButtonDown("up") && gameObject.transform.position == endPos) {
endPos = transform.position + player.transform.rotation * (new Vector3(0, 0, 1f));
}
if (Input.GetButtonDown("down") && gameObject.transform.position == endPos) {
//endPos = transform.position + player.transform.rotation * (new Vector3(0, 0, -1f));
}
if (firstInput == true) {
currentLerpTime += Time.deltaTime * 5;
perc = currentLerpTime / lerpTime;
gameObject.transform.position = Vector3.Lerp(startPos, endPos, perc);
if (perc > 0.8f) {
perc = 1;
}
if (Mathf.Round(perc) == 1) {
justJump = false;
}
}
}
With this I can get the Debug Log WALL RAYCAST HIT 2-3 times at a time, but only once. As I move the character it won't appear again, for some reason (I think it should because the update method is called in every frame).
Although when I place it in the Input.GetButtonDown("up") method, it won't log anything:
if (Input.GetButtonDown("up") && gameObject.transform.position == endPos) {
fwd = player.transform.TransformDirection(Vector3.forward);
if (Physics.Raycast(player.transform.position, fwd, out objectHit, 2)) {
if (objectHit.collider.tag == "Wall") {
Debug.Log("WALL RAYCAST HIT!");
}
}
endPos = transform.position + player.transform.rotation * (new Vector3(0, 0, 1f));
}
When you update the transform.position you actually "teleport" yout object to the new position. Even when you do this smoothly, using something like the Lerp function, as you do on your move script, Unity understands that you are teleporting your object to a new coordinate.
This is not wrong, All games work like that. Functions like Lerp just create the ilusion of movement, so the player fell like the character actually moved from one point to another.
What is happening is that your script keeps telling the character to move to a new position, despite any objects on its path. The collision happens, but your Update function still places the character on ints new position.
I can think of two possible ways for you to solve this:
Create an OnCollisionEnter() function for your Move script that somehow stops your movement. By creating a flag like you did with the firstInput you could achieve this.
Instead of updating your transform.position you could use a rigidbody to handle the movement and collisions for your. This way, everytime you need to move your character you will need to add some velocity to the character rigidbody, instead of directly updating the transform.position.
Since your character doesn't move in a constant way, but in a "one step at a time" way, I think the first solution is a better suitted for you.
The code will be something like this
void OnCollisionenter(Collision collision)
{
collided = true;
}
and the end of your Move script should be updated to
if(!collided)
{
if (firstInput == true) {
currentLerpTime += Time.deltaTime * 5;
perc = currentLerpTime / lerpTime;
transform.position = Vector3.Lerp(startPos, endPos, perc);
if (perc > 0.8f) {
perc = 1;
}
if (Mathf.Round(perc) == 1) {
justJump = false;
}
}
}
else
{
// Remember to reset this when you stop your character
endPos = transform.position;
collided = false;
}
This time I couldn't test the code before writting it here, so you may need to do some changes before it works.
ps: I don't think you need your firstInput variable. Once it is set to true, you never set it to false again. Also, the Input.GetButtonDown() function will only return true on the first frame a button is pressed wich is what I guess you intended when you created firstInput.
ps2: My solution added a collided boolean attribute to your class, so don't forget to add a line private bool collided; with your other class attributes. Also, read that chapter about the state machine pattern I recommended you on your other question. Creating this kind of control attribute suach as the collided boolean is not a good practice and if you had implemented a state machine this could be avoided.
Hey I had a similar problem i was using an animated model from Mixamo.com and I notices that for some strange reason the character didn't collide with boundaries despite having collider, for some reason it didn't work until i added a CharacterController to my model instead of a collider just search for the CharacterController and add it as you would add any script or rigid body to your game object.
Hope it Works XD Greetings

How to find all colliders below mouse?

public static bool IsTopmost(GameObject go)
{
RaycastHit[] hits;
Vector3 wp = Camera.main.ScreenToWorldPoint(Input.mousePosition);
wp.z = Camera.main.transform.position.z;
hits = Physics.RaycastAll(wp, Vector3.forward, Vector3.Distance(Camera.main.transform.position, go.transform.position) * 2);
if (hits.Length == 0)
{
return false;
}
GameObject topMostSoFar = hits[0].collider.gameObject;
RaycastHit hit;
for (int i = 1; i < hits.Length; i++)
{
hit = hits[i];
if (Compare(topMostSoFar, hit.collider.gameObject) == -1)
{
topMostSoFar = hit.collider.gameObject;
}
}
Debug.Log("finishes method");
return topMostSoFar.name == go.name;
}
My camera position.z = -5, all sprites in the game position.z = 0. This method never finishes, it always enters the first if which says there are no colliders under the mouse position. What do I do wrong here?
EDIT: It turns out my mistake is very stupid, I'm using Physics instead of Physics2D for 2d colliders. How can I check same thing but with 2d raycasting?
The solution is to use the 2d methods and variable types everywhere and for the direction of the RaycastAll to use Vector2.zero, unless you put Vector2.zero it;s not going to work.