I have a character in a 2D game, my goal is to get his eyes look as if they are looking at the cursor by moving the circles used for pupils towards the cursor, with limits. I have been stuck trying to create this for two days now and have yet to find a way that works!!
Using the following code I was able move my eyes towards the cursor, however this only works around the bottom left of the screen! (If the cursor is not below the screen or to the left of the screen the eyes move to the top right)
using UnityEngine;
using System.Collections;
public class LookAtMouse : MonoBehaviour {
public float speed = 5f;
private Vector3 target;
public Transform origin;
void Start () {
target = transform.position;
}
void Update () {
target = (Input.mousePosition);
target.z = transform.position.z;
transform.position = Vector3.MoveTowards(origin.position, target, speed * Time.deltaTime);
}
}
If anyone could point me in the right direction I would be incredibly grateful!
Thank you :)
Likely the reason it's only working in one quadrant for you is because Input.mousePosition returns a position in pixel coordinates. Basically, if your window is 800x600 pixels, it will return...
(0, 0, 0) for the bottom left pixel of the screen
(0, 600, 0) for the top left pixel of the screen
(800, 0, 0) for the bottom right pixel of the screen
(800, 600, 0) for the top right pixel of the screen
Since your eye pupil is in world space, you want the mouse position in world space also. Either way, even if you fixed that, I don't think Vector3.MoveTowards is going to do quite what you're wanting to do. I think this is more along the lines of what you want:
using UnityEngine;
public class LookAtMouse : MonoBehaviour {
public float speed = 5f;
public float maxDistance = 1f;
public Camera mainCamera;
private Vector3 _origin;
void Start () {
_origin = transform.position;
}
void Update () {
/* Get the mouse position in world space rather than screen space. */
var mouseWorldCoord = mainCamera.ScreenPointToRay(Input.mousePosition).origin;
/* Get a vector pointing from initialPosition to the target. Vector shouldn't be longer than maxDistance. */
var originToMouse = mouseWorldCoord - _origin;
originToMouse = Vector3.ClampMagnitude(originToMouse, maxDistance);
/* Linearly interpolate from current position to mouse's position. */
transform.position = Vector3.Lerp (transform.position, _origin + originToMouse, speed * Time.deltaTime);
}
}
Related
I am working in a Game which is pretty similar to Mario. So when player touches the coin object in World Space, I need to animate by moving that coin object to Coin meter, when the render mode of Canvas is Screen Space - Overlay, I can get the sprite object position easily with below code
CoinSprite Code
GameObject coinCanvasObject = Instantiate(prefab, canvas.transform);//Instantiate coin inside Canvas view
coinCanvasObject.transform.position = Camera.main.WorldToScreenPoint(coinSpriteObject.transform.position);//getting coin position from World Space and convert to Screen Space and set to coinCanvasobject position
AnimateCoin animate = coinCanvasObject.GetComponent<AnimateCoin>();
animate.animateCoin(coinSpriteObject.transform.position);
coinSpriteObject.SetActive(false);
AnimateCoin
public class AnimateCoin : MonoBehaviour
{
private float speed = 0f;
private bool isSpawn = false;
private Vector3 screenPos;
public void animateCoin(Vector3 screenPosTemp, Camera cam, Canvas canvas)
{
screenPos = Camera.main.WorldToScreenPoint(screenPosTemp);
isSpawn = true;
}
private void Update()
{
if (isSpawn)
{
speed += 0.025f;
transform.position = Vector3.Lerp(screenPos, targetObject.transform.position, speed);
if (Vector3.Distance(transform.position, targetObject.transform.position) <= 0)
{
StartCoroutine(deActivateCoin());
}
}
}
private IEnumerator deActivateCoin()
{
isSpawn = false;
yield return new WaitForSeconds(0.2f);
gameObject.SetActive(false);
}
}
Since I need to bring particle effect into Canvas view, I am changing the Canvas render mode to Screen Space - Camera.
When I change the Canvas to this render mode I could not get the exact sprite object position to trail the coin effect.
Hope this helps:
public Camera cam; // Camera containing the canvas
public Transform target; // object in the 3D World
public RectTransform icon; // icon to place in the canvas
public Canvas canvas; // canvas with "Render mode: Screen Space - Camera"
void Update()
{
Vector3 screenPos = cam.WorldToScreenPoint(target.position);
float h = Screen.height;
float w = Screen.width;
float x = screenPos.x - (w / 2);
float y = screenPos.y - (h / 2);
float s = canvas.scaleFactor;
icon.anchoredPosition = new Vector2(x, y) / s;
}
PD: It worked perfectly for me in a 2D video game, I didn't test it in a 3D game, but I think it should work too.
I rewrote my previous solution because it might not work correctly on some devices with non-standard resolutions.
This code should always work.
uiObject.anchoredPosition = GetUIScreenPosition(myPin.position, cam3d, uiObject.anchorMin);
public static Vector2 GetUIScreenPosition(Vector3 obj3dPosition, Camera cam3d, Vector2 anchor)
{
Vector2 rootScreen = _rootCanvasRect.sizeDelta;
Vector3 screenPos = cam3d.WorldToViewportPoint(obj3dPosition);
return (rootScreen * screenPos) - (rootScreen * anchor);
}
We take the sizeDelta of our UI Canvas, because it may differ from the screen resolution of the device.
Then we cast the WorldToViewportPoint from our 3d camera to get the relative position on the screen in the format from 0 to 1 by X and Y.
With anchors in the lower left corner ((0,0)(0,0)) this is our final anchoredPosition. However with anchors for example in the center ((0.5,0.5)(0.5,0.5)) we need to adjust the positions by subtracting half the canvas size.
In this example, we will get an unpredictable result when using different min and max anchors in the final object. For example ((0,25,0.25)(0.75,0.75)). But I sincerely doubt that you really need such anchors on an object with a dynamic position depending on the 3d object.
Currently I am trying to write a script that will mirror the movements of one controller onto the other for users who only have 1 functioning arm. How do I mirror the position of the one controller onto the other that way the arms are in bilateral movement?
Mirroring the y axis and z axis were easy since they move together and the rotation was easy to mirror. I am unable to mirror the x axis movement. I want it so that if the one hand moves out the other does the same, and they both move in together. Any ideas how I may be able to do this? I have attached my current script. I also disabled the position tracking of the non mirrored controller with simple Boolean logic in the OVRCemeraRig script to prevent stutters in movement Need to use OVRCemeraRig since using final IK
I have tried taking a difference in the x positions in the working arm and then adding that value to the none working arm. That did not work.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class VRMirror : MonoBehaviour
{
public bool mirrorLeft;
public bool mirrorRight;
public GameObject leftHand; //left hand anchor in OVRCameraRig
public GameObject rightHand; //right hand anchor from OVRCameraRig
void Start()
{
}
void FixedUpdate()
{
Transform left = leftHand.GetComponent<Transform>();
Transform right = rightHand.GetComponent<Transform>();
if (mirrorLeft)
{
Vector3 leftPos = left.position;
Quaternion leftRot = left.rotation;
leftRot.y = -leftRot.y;
right.position = leftPos;
right.rotation = leftRot;
}
else if (mirrorRight)
{
Vector3 rightPos = right.position;
Quaternion rightRot = right.rotation;
rightRot.y = -rightRot.y;
left.position = rightPos;
left.rotation = rightRot;
}
}
}
For the sake of robustness, let's assume your player's body's rotation might not necessarily always have its right pointing in the (1,0,0) world direction. Instead, we can get a reference to the player's Transform, playerTransform, (be sure to assign it using the inspector or in Start if you must) and make our calculations using that.
To calculate the bilateral symmetric position for a relative vector, you can calculate relativeVec - 2f * playerTransform.right * Vector3.Dot(relativeVec, playerTransform.right);. Explanation for why that works is in the comments.
For position, we can convert the absolute position of the source hand to be relative to the player's position, then find the relative position of the destination hand, then convert that back into an absolute position.
For rotation, determine up & forward for the source hand and reflect them to determine the up & forward for the destination hand. Use Quaternion.SetLookRotation to convert the vectors to the rotation for the destination hand.
We can use the same code for relative positions and for our direction vectors, so it actually doesn't take much code once you have the math. And also, since Transform is a class, we can make one method that does the reflection procedure, and then pass into it which transforms we want to be the source and destination:
public class VRMirror : MonoBehaviour
{
public bool mirrorLeft;
public bool mirrorRight;
public GameObject leftHand; //left hand anchor in OVRCameraRig
public GameObject rightHand; //right hand anchor from OVRCameraRig
public Transform playerTransform;
void Start()
{
}
void FixedUpdate()
{
Transform left = leftHand.GetComponent<Transform>();
Transform right = rightHand.GetComponent<Transform>();
if (mirrorLeft)
{
MirrorFromTo(left, right);
}
else if (mirrorRight)
{
MirrorFromTo(right, left);
}
}
void MirrorFromTo(Transform sourceTransform, Transform destTransform)
{
// Determine dest position
Vector3 playerToSourceHand = sourceTransform.position - playerTransform.position;
Vector3 playerToDestHand = ReflectRelativeVector(playerToSourceHand);
destTransform.position = playerTransform.position + playerToDestHand ;
// Determine dest rotation
Vector3 forwardVec = ReflectRelativeVector(sourceTransform.forward);
Vector3 upVec = ReflectRelativeVector(sourceTransform.up);
destTransform.rotation = Quaternion.LookRotation(forwardVec,upVec);
}
Vector3 ReflectRelativeVector(Vector3 relativeVec)
{
// relativeVec
// Take the relative vector....
// + Vector3.Dot(relativeVec, playerTransform.right)
// and for how far along the player's right direction it is
// away from the player (may be negative),
// * playerTransform.right
// move it that distance along the player's right...
// * -2f
// negative two times (i.e., along the left direction 2x)
return relativeVec
+ Vector3.Dot(relativeVec, playerTransform.right)
* playerTransform.right
* -2f;
}
}
I made some alteration to what #Ruzihm had. Thank you so much for the help. Everything works perfectly in the code I sampled below but I would recommend #Ruzihm answer since how he handles rotations. This code works if the player model is stationary and you are not turning your full body. If you need to turn use: playerTransform.right instead of Vector3.right in the ReflectRelativeVector function but using playerTransform.right will move the arm as the head moves.
public class VRMirror : MonoBehaviour
{
public bool mirrorLeft;
public bool mirrorRight;
public GameObject leftHand; //left hand anchor in OVRCameraRig
public GameObject rightHand; //right hand anchor from OVRCameraRig
public Transform playerTransform;
void Start()
{
}
void FixedUpdate()
{
Transform left = leftHand.GetComponent<Transform>();
Transform right = rightHand.GetComponent<Transform>();
if (mirrorLeft)
{
MirrorFromTo(left, right);
}
else if (mirrorRight)
{
MirrorFromTo(right, left);
}
}
void MirrorFromTo(Transform sourceTransform, Transform destTransform)
{
// Determine dest position
Vector3 playerToSourceHand = sourceTransform.position - playerTransform.position;
Vector3 playerToDestHand = ReflectRelativeVector(playerToSourceHand);
destTransform.position = playerTransform.position + playerToDestHand;
// Determine dest rotation
Quaternion destRot = sourceTransform.rotation;
destRot.y = -destRot.y;
destRot.z = -destRot.z;
destTransform.rotation = destRot;
}
Vector3 ReflectRelativeVector(Vector3 relativeVec)
{
// relativeVec
// Take the relative vector....
// + Vector3.Dot(relativeVec, playerTransform.right)
// and for how far along the player's right direction it is
// away from the player (may be negative),
// * playerTransform.right
// move it that distance along the player's right...
// * -2f
// negative two times (i.e., along the left direction 2x)
return relativeVec
+ Vector3.Dot(relativeVec, Vector3.right)
* Vector3.right
* -2f;
}
}
Here is a screen shot of the editor:
i'm currently working on a small game in Unity, and i use c# for my code.
The game is a small role playing game, where you control a character like you do in games like world of warcraft or dragon age.
You run around on small maps, with hills around the map. Currently, you can walk up the hills at some places and i want to stop that.
so i thought i write a code where i get the last position of my character and the current position, than i get the angle between them and if the angle is to high, i would force the player to go back (or disable moving forward).
this is what i got for the code so far:
Vector3 lastPos; // last Position
Vector3 curPos; // current Position
float slopeAngle; // The angle between curPos and lastPos
void Start()
{
curPos = transform.position;
}
void FixedUpdate()
{
lastPos = curPos;
curPos = transform.position;
slopeAngle = Vector3.Angle(lastPos, curPos);
}
unfortunately, slopeAngle is mostly between 0 and 0.04. it doesn't matter if i run up a steep mountain or just a flat hill.
any ideas?! (if there is a better solution, than getting the angle between two points, i am very interested!! ) :)
Thanks for your help!
You could try using a Raycast from your character to check if there's a slope high enough to halt your movement.
float maxSlopeAngle;
Transform raycastOrigin;
void FixedUpdate()
{
RaycastHit hit;
Vector3 direction = Quaternion.Euler(-MaxSlopeAngle, 0, 0) * transform.forward;
if (Physics.Raycast(raycastOrigin.position, direction, out hit, 1f))
{
Debug.Log("Slope ahead!");
}
}
Obviously, you'd want to figure out the right maxDistance for the raycast so you won't stop too soon.
Also, you can use the following script to help you figure out the right angle:
[ExecuteInEditMode]
public class SlopeAngleHelper : MonoBehaviour {
public float MaxSlopeAngle;
public Transform RayOrigin;
void Update ()
{
Vector3 direction = Quaternion.Euler(-MaxSlopeAngle, 0, 0) * transform.forward;
Debug.DrawRay(RayOrigin.position, transform.forward, Color.black, 0.01f, false);
Debug.DrawRay(RayOrigin.position, direction, Color.red, 0.01f, false);
}
}
Just put it on your character object and it will "visualize" the raycast in scene view.
I have a ball which rotates around the point 0,0,0 in the Z-axis. When the space button is pressed, the ball has to go inside the large circle. Now my code looks like this. When you press space, the ball does not behave as they should. I want to know how to make a balloon down exactly down
that's how the ball should behave ->
behavior image
my code:
void Update () {
if (Input.GetKeyDown (KeyCode.Space)) {
transform.position = new Vector3 (transform.position.x - 1, transform.position.y - 1, 0);
} else {
transform.RotateAround(new Vector3(0,0,0), new Vector3(0,0,1), 2);
}
}
Your code to 'jump' the orbit doesn't do what you want because Transform.RotateAround modifies both the rotation and the position of the object's transform.
So jumping to (position - 1,1,0) in the world is going to return wildly different results every time.
What you want to do instead is calculate the (Vector) direction from the object to the centre of orbit (the difference), then scale that down to how far you want it to move, then apply it to the position.
private Vector3 _orbitPos = Vector3.zero;
private float _orbitAngle = 2f;
private float _distanceToJump = 2f;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var difference = (_orbitPos - transform.position).normalized * _distanceToJump;
transform.Translate(difference);
}
transform.RotateAround(_orbitPos, Vector3.forward, _orbitAngle);
}
This will move the object to be orbiting 2 units closer when space is pressed immediately.
If you wanted to have a smooth transition instead of a jump, look into using Mathf.Lerp, Vector3.Lerp and the routines involved.
I have a class below that I attach to a object in order to make it rotate around its pivot. I sent the pivot of the sprite via the inspector.
This works exactly how I want it too, BUT the issue I am having is that whenever I touch and drag it, and then touch and drag it again, it snaps to a new position.
What I would like for it to do is, when it is rotated and then rotated again, the sprite stays in its same rotation and not snap to a new position and I would like the angle of the sprite to be reset to 0. The next then is that I want the angle to continually rotate. So if I rotate it in the positive direction, the angle should keep increasing in the positive direction and not change..Such as 0---> 360 ----> 720 -----> etc, etc. And then when the mouse is released, the sprite stays in the same position but the angle is now set back to 0. And then when clicked again to rotate, it rotates from that exact position.
Here is my code thus far which works well for rotating, but I would like to modify it to achieve the above scenario. Any help with this?
public class Steering : MonoBehaviour {
float prevAngle,wheelAngle,wheelNewAngle = 0;
public SpriteRenderer sprite;
void Start () {
}
void Update () {
}
public float GetAngle(){
return wheelAngle;
}
void OnMouseDrag(){
Vector3 mouse_pos = Input.mousePosition;
Vector3 player_pos = Camera.main.WorldToScreenPoint(this.transform.position);
mouse_pos.x = mouse_pos.x - player_pos.x;
mouse_pos.y = mouse_pos.y - player_pos.y;
wheelNewAngle = Mathf.Atan2 (mouse_pos.y, mouse_pos.x) * Mathf.Rad2Deg;
if (Input.mousePosition.x > sprite.bounds.center.x) {
wheelAngle += wheelNewAngle - prevAngle;
} else {
wheelAngle -= wheelNewAngle - prevAngle;
}
this.transform.rotation = Quaternion.Euler (new Vector3(0, 0, wheelAngle));
Debug.Log (wheelAngle);
prevAngle = wheelNewAngle;
}
void OnMouseUp(){
prevAngle = wheelNewAngle;
wheelAngle = 0;
}
}
By angle of the sprite, do you mean the rotation? I'm not sure how the position is changing if there's nothing in your code doing that. Does it always move to the same position? I'm having a little trouble visualizing how your system is supposed to look but I hope this helps.
It looks like you might want to store the previous mouse position so you can get the relative amount to rotate each frame.
At the top:
Vector3 prevMousePos = Vector3.zero;
This method will help get the position when the player pressed:
void OnMouseDown(){
prevMousePos = Input.mousePosition;
}
Then in OnMouseDrag() get the difference between the two mouse positions to get the relative position (if you moved the mouse left, right, up, or down since pressing):
Vector3 mouseDiff = Input.mousePosition - prevMousePos;
With this it will use the relative mouse position after pressing instead of the current one, which should smooth things out.