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
Related
I'm making my first game where obstacles (which are prefabs) are placed by a script into a scene. They are all different sizes in a 2D environment. I am placing them using this code below
Instantiate(normal1, new Vector3(x, y , 0), Quaternion.identity);
This works perfectly, except that I need all of the obstacles to be positioned from the top left. So if I would have 0, 0 for my x and y, only the obstacle's corner would be covering the 0, 0 position, not the entire thing. From the little I've worked with GUI elements, you can align to however you like. Is this the same with prefab, and if so, how? Or can you somehow move the origin of the object?
I assume you are talking about non-UI elements.
The easiest thing would be to give your objects a parent GameObject and arrange them in a way so the parent GameObject already has the pivot where you want it (the top-left corner). You do this by first positioning the (future) parent object correctly and then simply drag the child object into it in the hierachy (while it keeps its current position).
Then in your script you have to use Camera.ScreenToWorldPoint in order to find the top-left screen corner position in the 3D world like
// an optional offset from the top-left corner in pixels
Vector2 PixelOffsetFromTopLeft;
// Top Left pixel is at
// x = 0
// y = Screen height
// and than add the desired offset
var spawnpos = new Vector2(0 + PixelOffsetFromTopLeft.x, Screen.height - PixelOffsetFromTopLeft.y);
// as z we want a given distance to camera (e.g. 2 Units in front)
spawnedObject = Instantiate(Prefab, camera.ScreenToWorldPoint(new Vector3(spawnpos.x, spawnpos.y, 2f)), Quaternion.identity);
Full script I used as example
public class Spawner : MonoBehaviour
{
public GameObject Prefab;
public Vector2 PixelOffsetFromTopLeft = Vector2.zero;
private GameObject spawnedObject;
private Camera camera;
private void Start()
{
camera = Camera.main;
}
private void Update()
{
if (Input.anyKeyDown)
{
// you don't need this I just didn't want to mess up the scene
// so I just destroy the last spawned object before placing a new one
if (spawnedObject)
{
Destroy(spawnedObject);
}
// Top Left pixel is at
// x = 0
// y = Screen height
// and than add the desired offset
var spawnpos = new Vector2(0 + PixelOffsetFromTopLeft.x, Screen.height - PixelOffsetFromTopLeft.y);
// as z we want a given distance to camera (e.g. 2 Units in front)
spawnedObject = Instantiate(Prefab, camera.ScreenToWorldPoint(new Vector3(spawnpos.x, spawnpos.y, 2f)), Quaternion.identity);
}
}
}
This will now always spawn the object anchored to the top left. It will not keep it this way if the camera moves or the window is resized.
If your question was about keeping that position also when the window is resized you can use the same code e.g. in an Update method (later you due to efficiency you should only call it when really needed / window actually was resized) like
public class StickToTopLeft : MonoBehaviour
{
private Camera camera;
public Vector2 PixelOffsetFromTopLeft = Vector2.zero;
private void Start()
{
camera = Camera.main;
}
// Update is called once per frame
private void Update()
{
var spawnpos = new Vector2(0 + PixelOffsetFromTopLeft.x, Screen.height - PixelOffsetFromTopLeft.y);
// This time simply stay at the current distance to camera
transform.position = camera.ScreenToWorldPoint(new Vector3(spawnpos.x, spawnpos.y, Vector3.Distance(camera.transform.position, transform.position)));
}
}
This now keeps the object always anchored to the top-left corner also after resizing the window and allows to adjust its offset afterwards.
How do I get 2D position of a 3D object (ie. z axis should be 0) when this object is following another 3D object?
So far I have tried the below one line code but the z axis does not remain 0 since it follows another object it keeps fluctuating. Any solutions?
public GameObject Car;
public GameObject Icon;
// Update is called once per frame
void Update ()
{
Icon.transform.position = Vector3.MoveTowards(Car.transform.position, Car.transform.position, 0);
}
Icon.transform.position = Vector3.MoveTowards(Car.transform.position, Car.transform.position, 0);
this does absolutely nothing ... first of all you are move between the same positions and secondly with a speed = 0... so basically this equals
Icon.transform.position = Car.transform.position;
What you wanted to do instead is eliminating the z component of the position vector so e.g. something like
Icon.transform.position = new Vector3(Car.transform.position.x, Car.transform.position.y, 0);
or a little trick: you can typecast it to Vector2 which makes it "forget" the z value. It is than implicitely typecasted back to Vector3 with z=0
Icon.transform.position = (Vector2) Car.transform.position;
You just put the position in a local variable and set z to 0.
public GameObject Car;
public GameObject Icon;
// Update is called once per frame
void Update () {
Vector3 pos3d = Car.transform.position;
pos3d.z = 0;
Icon.transform.position = pos3d;
}
I have a GameObject named Player, and a Canvas. The HealthBar GameObject is a child of the Canvas (Canvas>HealthBar). I have the HP bar following the player with a script. It is supposed to follow the player at a fixed position above it, so it sits near the top of the sprite.
Here is the 'follow' part of the HP Follow Script.
void Update ()
{
Vector3 currentPos = transform.position;
Vector3 playerPos = Camera.main.WorldToViewportPoint (Player.transform.position);
transform.position = playerPos;
The problem is that the HP bar moves with the character, but at a very small fraction of the speed of which the player is moving. For example, if the player moves one unit, the bar moves 0.1 units.
Video of error: https://streamable.com/vaz7h
You want to make a UI Object(Image) follow a GameObject is a SpriteRenderer or MeshRenderer.
In another way, this can be described as converting world point to UI point.
This simple function below converts world position to UI space. It takes a the Canvas of the UI as parameter then the position you want to convert to UI position which in your case is the Player.transform.position variable.
The key for moving a UI object is the RectTransformUtility.ScreenPointToLocalPointInRectangle function which converts the screen point to ui rectangle local point.
public Vector3 worldToUISpace(Canvas parentCanvas, Vector3 worldPos)
{
//Convert the world for screen point so that it can be used with ScreenPointToLocalPointInRectangle function
Vector3 screenPos = Camera.main.WorldToScreenPoint(worldPos);
Vector2 movePos;
//Convert the screenpoint to ui rectangle local point
RectTransformUtility.ScreenPointToLocalPointInRectangle(parentCanvas.transform as RectTransform, screenPos, parentCanvas.worldCamera, out movePos);
//Convert the local point to world point
return parentCanvas.transform.TransformPoint(movePos);
}
Usage:
public GameObject Player;
public Canvas canvas;
void Update()
{
//Convert the player's position to the UI space then apply the offset
transform.position = worldToUISpace(canvas, Player.transform.position);
}
Now, let's say that you have already positioned the UI to be where it is supposed to be and you now want it to follow another Object(Player), you need to implement a simple offset with the code above. This is really easy. Below is what it is supposed to look like:
public GameObject Player;
public Canvas canvas;
Vector3 Offset = Vector3.zero;
void Start()
{
Offset = transform.position - worldToUISpace(canvas, Player.transform.position);
}
void Update()
{
//Convert the player's position to the UI space then apply the offset
transform.position = worldToUISpace(canvas, Player.transform.position) + Offset;
}
public Vector3 worldToUISpace(Canvas parentCanvas, Vector3 worldPos)
{
//Convert the world for screen point so that it can be used with ScreenPointToLocalPointInRectangle function
Vector3 screenPos = Camera.main.WorldToScreenPoint(worldPos);
Vector2 movePos;
//Convert the screenpoint to ui rectangle local point
RectTransformUtility.ScreenPointToLocalPointInRectangle(parentCanvas.transform as RectTransform, screenPos, parentCanvas.worldCamera, out movePos);
//Convert the local point to world point
return parentCanvas.transform.TransformPoint(movePos);
}
If the canvas is ScreenSpace, you can use:
healthbarTransform.position = camera.WorldToScreenPoint(playerTransform.position);
(if it is screen space, it can be child of player or whatever as long as you set the position every frame)
.... About your problem, there are some factors that will solve the issue:
You are setting the healthbar position in the update function, to let unity know to execute this code after player moves for sure, you need to add HPFollow script to below Default time in the script execution order (if you want to use Update).
Or you can use lateUpdate to set the ui positions. which I highly recommend.
If your problem is just flickering ui, it should be about ui rect transform scales...
Here's the solution:
void Start () {
Canvas.willRenderCanvases += canvasUpdate;
}
void canvasUpdate () {
var screenPoint = RectTransformUtility.WorldToScreenPoint( canvas.worldCamera, worldPoint );
Vector2 localPoint;
RectTransformUtility.ScreenPointToLocalPointInRectangle( canvas.GetComponent<RectTransform>(), screenPoint, canvas.worldCamera, out localPoint );
transform.localPosition = localPoint;
}
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.
I am trying to create an aiming system in Unity, where index finger is being used to control a crosshair. The game is being played with Oculus and Leap Motion is mounted on to the Oculus headset.
I have tried to calculate the position for the crosshair based on the angle of the index finger and draw a ray based on a given distance. I have also tried calculating the crosshair location just based on the direction of the index finger and given distance like this:
Vector3 targetPoint = direction * direction;
Here is what I have tried:
void Start () {
crossHair = GameObject.Find("Crosshair Component");
myCamera = GameObject.Find("CenterEyeAnchor");
target = crossHair.transform;
controller = new Controller();
indexFinger = new Finger();
void Update () {
crossHair.transform.position = myCamera.transform.position;
frame = controller.Frame();
List<Hand> handList = new List<Hand>();
for (int h = 0; h < frame.Hands.Count; h++)
{
Hand leapHand = frame.Hands[h];
handList.Add(leapHand);
}
if (handList != null && frame.Hands.Count > 0) {
indexFinger = frame.Hands[0].Fingers[(int)Finger.FingerType.TYPE_INDEX];
if (indexFinger.IsExtended)
{
Vector3 fingerTipPos = indexFinger.TipPosition.ToUnityScaled();
Vector3 originAngle = indexFinger.TipPosition.ToUnityScaled();
Vector3 targetAngle = crossHair.transform.position;
float distance = 1;
Vector3 direction = indexFinger.Direction.ToUnityScaled();
Ray rayToTest = new Ray(originAngle, targetAngle);
Vector3 targetPoint = rayToTest.GetPoint(distance);
//Problem is here. How should these targetPoint values be used to calculate correct position for the crosshair?
crossHair.transform.position = new Vector3(crossHair.transform.position.x + (targetPoint.x), y,
crossHair.transform.position.z + (targetPoint.z));
}
}
}
}
With similar calculations as these I have been able to move the crosshair accordingly on horizontal level by modifying x and z values but the y-axis seems to be the biggest problem. Y-axis seems to always receive too low values.
The crosshair element is placed as a child element for the CenterEyeAnchor camera object in Unity.
So questions are: Is the way of calculating the target point position right as I have been trying to make it?
What kind of calculations would I have to make for the crosshairs position based on the new target point value to make it behave accordingly to the index finger movement? Scaling factors for the values?
Pictures of the project setup and current object positions attached.
Here is the image for the setup of objects
Here is the image of the crosshair's false position