- Home /
Moving a Game Object in a specific volume/area
Hello All. I am very new at Unity and C# and I think this is my first ever forum post. I have some beginner experience in Java but that's about it.
So I am writing... well lets be honest, trying to write a follow script to control a game object(Sphere) which is connected and relative to another game object.
Inputs touch and mouse.
The Sphere should follow the mouse as I navigate though the screen up and down left and right however the movement is constrained using mathf.clamp. When the mouse/ touch is in the center of the screen the Sphere moves on the z axis to -1 as to not visually collide with the relative game object.
Sphere following mouse/touch:
I have managed to get the Sphere to move on the Z axis from -1 to 0 so it moves in a gradual semicircle motion while being moved on the X Axis. However there is no movement on the Y Axis and the values which I am getting on the debug log don't really add up.
Can anyone shed some light please. I have attached the Script below. Thank you.
using UnityEngine;
using System.Collections;
public class Follow_Script : MonoBehaviour {
// Range of movement on X axis this will set from x to -x.
public float clampForX = 2;
//Value for minimum/lowest movement value on Y axis.
public float clampForYmin = -5;
//Value for maximum/highest movement value on Y axis.
public float clampForYmax = 10;
//Value for where the Game object starts off on the Z axis.
public float startPositionOnZAxis = 0;
void FixedUpdate ()
{
MoveUpdate ();
}
void MoveUpdate () {
//Vector3 to store position of mouse or touch
Vector3 pos;
//keepUpWithPointX to store float value of X axis from pos for mathf.clamp
float keepUpWithPointX;
//keepUpWithPointY to store float value of Y axis from pos for mathf.clamp
float keepUpWithPointY;
// Variable used to hold mathf.clamp value of X axis
float clampX;
// Variable used to hold mathf.clamp value of Y axis
float clampY;
//if Android is running get touch input and store into Vector3 pos
if (Application.platform == RuntimePlatform.Android)
{
pos = Camera.main.ScreenToWorldPoint (new Vector3 (Input.GetTouch (0).position.x,
Input.GetTouch (0).position.y, -1));
}
// else to add in mouse input for testing mostly
else
{
pos = Camera.main.ScreenToWorldPoint (new Vector3(Input.mousePosition.x,
Input.mousePosition.y, -1));
}
// Passing X Axis position of Game object value to keepUpWithPointX
// Cursor was not following the mose properly and was lagging behind.
// * by 30f seems to have solved it with the mouse as input
// although I have not tried on a touch device yet.
keepUpWithPointX = pos.x * 30f;
// Passing Y Axis position of Game object to keepUpWithPointY
// This is giving a strange value and not working as expected.
// Multiplying by 30f does not help.
keepUpWithPointY = pos.y;
// Added Debug to check what is happening when passing over values.
Debug.Log ("pos.y " + pos.y + " " + "keepUpWithPointY " + keepUpWithPointY);
// Clamping X Axis Values into clampX
clampX = Mathf.Clamp (keepUpWithPointX, -clampForX, clampForX);
// Clamping Y Axis Values into clampY
clampY = Mathf.Clamp (keepUpWithPointY, clampForYmin, clampForYmax);
Debug.Log ("clampY " + clampY);
/* Give object new position by passing X and Y as Clamp values and calling Rotation
* in order to move the game object in fixed space on Z axis.
*/
transform.position = new Vector3 ( -clampX, -clampY, Rotation (clampX));
}
/* create motion for Static Game Object.
* When mouse/touch at center of sceen the game object moves out on the z axis to 1-
* When mouse/touch is at max or min of mathf.clamp for movement them game object is
* moved gradually to 0.
* Basically Range 1 from 3 to 0
* Range 2 from 0 to -1
* When Range 1 = 3 , Range 2 = 0
*/
float Rotation(float clamp){
// change from 0 to 1;
float change;
float rotation;
float absVal = Mathf.Abs (clamp);
change = absVal / clampForX;
rotation = change - 1;
return rotation;
}
}
Answer by maccabbe · Apr 24, 2015 at 07:16 PM
First, when you do Camera.ScreenPointToWorld with a negative z value you are getting a point behind the camera which you then need to fix by negating the x and y pos.
However the core of your problem is that when you get a point in world space and then modify the z value you also need to modify the x and y values to follow the mouse. That is why x needs to be multiplied by 30 to kind of follow the mouse and why y is also having a hard time when you modify the z to Rotation(clampX).
For instance the code
void MoveUpdate() {
Vector3 pos=Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,
Input.mousePosition.y, 10));
float clampX=Mathf.Clamp(pos.x, -clampForX, clampForX);
float clampY=Mathf.Clamp(pos.y, clampForYmin, clampForYmax);
transform.position=new Vector3(clampX, clampY, pos.z);
}
But as soon as you modify the new position so z=Rotation(clampX) you'll also have to change clampX and clampY.
Here are a few possible solutions depending on the intended effect. Personally I would go with Physics.Raycast with a fallback since this would be the easiest way to get your object to "avoid" multiple objects.
1) You could take one ScreenToWorldPoint's x pos as an estimate of how far from the camera your point should be and input that value in another ScreenToWorldPoint. This wouldn't be entirely accurate but it might be good enough.
void MoveUpdate() {
Vector3 tempPos=Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,
Input.mousePosition.y, 10));
float tempClampX=Mathf.Clamp(tempPos.x, -clampForX, clampForX);
Vector3 pos=Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,
Input.mousePosition.y, Rotation(tempClampX)+5));
float clampX=Mathf.Clamp(pos.x, -clampForX, clampForX);
float clampY=Mathf.Clamp(pos.y, clampForYmin, clampForYmax);
transform.position=new Vector3(clampX, clampY, pos.z);
}
You could improve the following code with Plane.Raycast to define a plane z=estimate and intersect it with the ray from the mouse but it would still be inaccurate. You could also try using the screen coordinates to define a z distance from camera or z world position. http://docs.unity3d.com/ScriptReference/Plane.Raycast.html
2) Use Physics.Raycast (with a collider on the other object), Sphere.Raycast, or define your own Cylinder.Raycast. If the mouse ray intersects then use the position, otherwise just use Plane.Raycast with the plane z=value to get the new position.
http://docs.unity3d.com/ScriptReference/Physics.Raycast.html http://docs.unity3d.com/ScriptReference/Physics.SphereCast.html
Thank you very much for your answer. It's much more than I expected and it is exactly what I wanted.
Such an elegant solution compared to $$anonymous$$e :)
However if I may, when attaching the script the game object (ball) it is growing 3 times in size.
And I can't really figure out why.
Accuracy wise for my use I think it is since what I am trying to do is : Player holds a ball and the ball follows where the mouse pointer/touch is. So the ball needs to stay within scale with the player and also the ball needs to stay within the body limits since the player does not physically move (therefor using Clamp Values).
I can see I have so much to learn.
Yet again thanks for all the info :)