I drew lines using LineRenderer. Game is 2d. What I want now is to create whitespace in line with the line on the gameobject with collider.
Here is the code I use for drawing and adding collider to line. What I want is not to draw a line on gameobjects, like circles(with CircleCollider2D) or squares(with BoxCollider2D). I want to have two different LineRenderers: one is finishing on collider, second is starting on it. What I'm trying to achieve That square does not hide the line. There is not a line where the square is.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
public class DrawLine : MonoBehaviour
{
private LineRenderer line;
private bool isMousePressed;
public bool isLeftMousePressed = false;
public List<Vector3> pointsList;
private Vector3 mousePos;
public float width = 0.05f;
// Structure for line points
struct myLine
{
public Vector3 StartPoint;
public Vector3 EndPoint;
};
// -----------------------------------
void Awake()
{
// Create line renderer component and set its property
line = gameObject.AddComponent<LineRenderer>();
line.material = (Material)Resources.Load("Materials/LineMat");
line.startWidth = width;
line.endWidth = width;
line.useWorldSpace = true;
isMousePressed = false;
pointsList = new List<Vector3>();
}
// -----------------------------------
void Update()
{
if (Input.GetMouseButtonDown(1))
{
isLeftMousePressed = true;
}
if (isLeftMousePressed||Game.currentLevel == 0) return;
if (Input.GetMouseButton(0))
{
isMousePressed = true;
}
// Drawing line when mouse is moving(pressed)
if (isMousePressed)
{
if (Input.GetMouseButtonUp(0))
{
isMousePressed = false;
GameObject curve = (GameObject)Instantiate(Resources.Load("Curve"), transform.parent);
curve.GetComponent<DrawLine>().width = width;
if (line.GetPosition(line.positionCount - 1).z == 1) Destroy(gameObject);
else enabled = false;
}
mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mousePos.z = 0;
if (!pointsList.Contains(mousePos))
{
pointsList.Add(mousePos);
line.positionCount = pointsList.Count;
line.SetPosition(pointsList.Count - 1, (Vector3)pointsList[pointsList.Count - 1]);
try
{
AddColliderToLine(line, (Vector3)pointsList[pointsList.Count - 2], (Vector3)pointsList[pointsList.Count - 1]);
}
catch(Exception e)
{
}
}
}
}
// -----------------------------------
// Following method checks whether given two points are same or not
// -----------------------------------
private bool checkPoints(Vector3 pointA, Vector3 pointB)
{
return (pointA.x == pointB.x && pointA.y == pointB.y);
}
private void AddColliderToLine(LineRenderer line, Vector3 startPoint, Vector3 endPoint)
{
//create the collider for the line
GameObject lcObject = new GameObject("LineCollider");
BoxCollider2D lineCollider = lcObject.AddComponent<BoxCollider2D>();
lcObject.layer = 2; // ignore raycast
//set the collider as a child of your line
lineCollider.transform.parent = line.transform;
// get width of collider from line
float lineWidth = line.endWidth;
// get the length of the line using the Distance method
float lineLength = Vector3.Distance(startPoint, endPoint);
// size of collider is set where X is length of line, Y is width of line
//z will be how far the collider reaches to the sky
lineCollider.size = new Vector3(lineLength, lineWidth);
// get the midPoint
Vector3 midPoint = (startPoint + endPoint) / 2;
// move the created collider to the midPoint
lineCollider.transform.position = midPoint;
//heres the beef of the function, Mathf.Atan2 wants the slope, be careful however because it wants it in a weird form
//it will divide for you so just plug in your (y2-y1),(x2,x1)
float angle = Mathf.Atan2((endPoint.y - startPoint.y), (endPoint.x - startPoint.x));
// angle now holds our answer but it's in radians, we want degrees
// Mathf.Rad2Deg is just a constant equal to 57.2958 that we multiply by to change radians to degrees
angle *= Mathf.Rad2Deg;
//were interested in the inverse so multiply by -1
//angle *= -1;
// now apply the rotation to the collider's transform, carful where you put the angle variable
// in 3d space you don't wan't to rotate on your y axis
lineCollider.transform.Rotate(0, 0, angle);
}
}
If you want to make dotted line, you can see 2D Dotted LineRenderer
And add the collider on the part you want
gameObject.AddComponent<PolygonCollider2D>();
Edit:
Check if mouse is inside the square by raycast
if (isMousePressed)
{
var stopDrawing = false;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
if(hit.transform.tag == "square" || hit.transform.tag == "circle") {
stopDrawing = true;
}
}
if(stopDrawing)
{
// Stop current line, prepare a new List<Vector3> for next line
}
else{
// Continue draw current line
}
}
Related
So I only want one line on screen at a time. I would like to destroy the current line when a ball collides or another line is drawn. Here is my code so far. It creates a line renderer with two points, adds a Box collider. I've tried making a loop and setting the vector points back to zero.
Every time I add a OnCollisonEnter2D I cannot get the Line to register that it hit a ball. In the Ball script I can get the Log to print Hit when the Ball hits the collider.
using System.Runtime.CompilerServices;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.UI;
public class Player1 : MonoBehaviour
{
private LineRenderer line; // Reference to LineRenderer
private Vector3 mousePos;
public Vector3 startPos; // Start position of line
public Vector3 endPos; // End position of line
void Update()
{
GetLine(); // On mouse down new line will be created
}
private void GetLine()
{
if (Input.GetMouseButtonDown(0))
{
if (line == null)
CreateLine();
SetFirstPos();
}
else if (Input.GetMouseButtonUp(0))
{
if (line)
{
SetSecondPos();
AddColliderToLine();
line = null;
}
}
else if (Input.GetMouseButton(0))
{
if (line)
{
mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mousePos.z = 0;
line.SetPosition(1, mousePos);
}
}
}
// Following method creates line runtime using Line Renderer component
private void CreateLine()
{
line = new GameObject("Line").AddComponent<LineRenderer>();
line.positionCount = 2;
line.numCapVertices = 2;
line.startWidth = .25f;
line.endWidth = .25f;
line.startColor = Color.black;
line.endColor = Color.black;
line.useWorldSpace = true;
}
// Following method adds collider to created line
private void AddColliderToLine()
{
BoxCollider2D col = new GameObject("Collider").AddComponent<BoxCollider2D>();
col.transform.parent = line.transform; // Collider is added as child object of line
float lineLength = Vector3.Distance(startPos, endPos);// length of line
lineLength.ToString();
col.size = new Vector3(lineLength, 0.25f, 1f); // size of collider is set where X is length of
line, Y is width of line, Z will be set as per requirement
Vector3 midPoint = (startPos + endPos) / 2;
col.transform.position = midPoint; // setting position of collider object
// Following lines calculate the angle between startPos and endPos
float angle = (Mathf.Abs(startPos.y - endPos.y) / Mathf.Abs(startPos.x - endPos.x));
if ((startPos.y < endPos.y && startPos.x > endPos.x) || (endPos.y < startPos.y && endPos.x >
startPos.x))
{
angle *= -1;
}
angle = Mathf.Rad2Deg * Mathf.Atan(angle);
col.transform.Rotate(0, 0, angle);
}
//Sets the first position
private void SetFirstPos()
{
mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mousePos.z = 0;
line.SetPosition(0, mousePos);
startPos = mousePos;
Debug.Log("Line");
}
//Sets the second position
private void SetSecondPos()
{
mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mousePos.z = 0;
line.SetPosition(1, mousePos);
endPos = mousePos;
}
//Cannot get it to Log Hit. Line show up in the Hierarchy.
private void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameobject.name =="Line")
{
Debug.Log("Hit!");
}
}
}
If your OnCollisionEnter2D gets called why don't you use just use it?
private void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameObject.name =="Line")
{
Debug.Log("Hit!");
// destroy the hit line
GameObject.Destroy(collision.gameObject);
}
}
If you want the line to be destroyed once a new one gets created you have to store it and destroy it manually:
public class Player1 : MonoBehaviour
{
private GameObject lastLine;
[...]
private void GetLine()
{
if (Input.GetMouseButtonDown(0))
{
if(lastLine != null)
{
GameObject.Destroy(lastLine);
}
if (line == null)
CreateLine();
SetFirstPos();
}
else if (Input.GetMouseButtonUp(0))
{
if (line)
{
SetSecondPos();
AddColliderToLine();
lastLine = line.gameObject;
line = null;
}
}
[...]
}
I want to use OverlapBox to create a box in front of a vehicle and be able to check collision on demand.
I use draw gizmo to be able to see the size and position of the box to confirm that size and position are correct.
This is my code:
public class TestCollision : MonoBehaviour
{
[Header("Collision")]
public Vector3 m_DetectorOffset = Vector3.zero;
public Vector3 m_DetectorSize = Vector3.zero;
private LayerMask m_LayerMask;
private Renderer m_Renderer = null;
void Start()
{
m_LayerMask = (1 << LayerMask.NameToLayer("Pedestrian")) | (1 << LayerMask.NameToLayer("Vehicle"));
m_Renderer = GetComponent<Renderer>();
m_Renderer.material.color = Color.green;
}
private void FixedUpdate()
{
if (CheckForCollisions())
m_Renderer.material.color = Color.red;
else
m_Renderer.material.color = Color.green;
}
public bool CheckForCollisions()
{
Vector3 colliderPos = transform.position + m_DetectorOffset;
Collider[] colliders = Physics.OverlapBox(colliderPos, m_DetectorSize, transform.rotation, m_LayerMask);
if (colliders.Length == 1)
{
// Ignore collision with itself
if (colliders[0].gameObject == gameObject)
return false;
return true;
}
if (colliders.Length > 0)
return true;
return false;
}
#if UNITY_EDITOR
protected virtual void OnDrawGizmos()
{
Gizmos.color = Color.red;
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.DrawWireCube(m_DetectorOffset, m_DetectorSize * 2.0f);
}
#endif
}
This script works great if I don't rotate the "vehicle" - in the picture the blue cube is the object I want to detect, the green/red cube is the vehicle and the red wireframe box is the OverlapBox represented by the gizmo:
The issue occurs when the "vehicle" rotates - in the following picture I rotate the vehicle 90 degrees in the Y axis, the gizmo represents the box in the correct position, however the OverlapBox logic is ignoring such rotation (even though when the rotation of the object is passed in the function):
How can I make the OverlapBox to work properly taking rotation into consideration and being able to represent an accurate gizmo.
Many thanks in advance.
Managed to fix the issue, the problem is the position and rotation of the OverlapBox. I thought the rotation will be applied from the position of the gameobject (which of course it doesn't make any sense), what was happening is that the rotation was applied to the OverlapBox itself.
In my case, since the OverlapBox is only in the front of the vehicle, I replaced the Vector3 offset for a float OffsetZ (only in the Z axis as in the forward of the vehicle), and changed the calculation of the position of the OverlapBox in the CheckForCollisions function as follows:
Vector3 colliderPos = transform.position + (transform.forward * m_DetectorOffsetZ);
This is the final scrip that makes the OverlapBox work properly and match the respresentation of the gizmo:
public class TestCollision : MonoBehaviour
{
[Header("Collision")]
public Vector3 m_DetectorOffset = Vector3.zero;
public Vector3 m_DetectorSize = Vector3.zero;
public float m_DetectorOffsetZ = 0.0f;
private LayerMask m_LayerMask;
private Renderer m_Renderer = null;
void Start()
{
m_LayerMask = (1 << LayerMask.NameToLayer("Pedestrian")) | (1 << LayerMask.NameToLayer("Vehicle"));
m_Renderer = GetComponent<Renderer>();
m_Renderer.material.color = Color.green;
}
private void FixedUpdate()
{
if (CheckForCollisions())
m_Renderer.material.color = Color.red;
else
m_Renderer.material.color = Color.green;
}
public bool CheckForCollisions()
{
Vector3 colliderPos = transform.position + (transform.forward * m_DetectorOffsetZ);
Collider[] colliders = Physics.OverlapBox(colliderPos, m_DetectorSize, transform.rotation, m_LayerMask);
if (colliders.Length == 1)
{
// Ignore collision with itself
if (colliders[0].gameObject == gameObject)
return false;
return true;
}
if (colliders.Length > 0)
return true;
return false;
}
#if UNITY_EDITOR
protected virtual void OnDrawGizmos()
{
Gizmos.color = Color.red;
Gizmos.matrix = transform.localToWorldMatrix;
Vector3 pos = Vector3.zero;
pos.z = m_DetectorOffsetZ;
Gizmos.DrawWireCube(pos, m_DetectorSize * 2.0f);
}
#endif
}
Thanks to a reply of xxmariofer in the Unity answer forums, I'll post here just in case the line to use if you want to use a Vector3 as offset instead of a float (for only one axis):
Vector3 colliderPos = transform.TransformPoint(m_DetectorOffset);
Here is the link to the post:
https://answers.unity.com/questions/1757338/mismatch-between-overlapbox-and-gizmo.html
Why don't you use collider with is Trigger = true flag?
Overlapping is good for rare invoking, but if you want to check every frame you better just use trigger and OnTriggerEnter method.
I'm working currently at a 2D Game for Android. There is a player in my scene and if the user tilts his device the player Object is moving on the ground. But he is just moving out of the screen at the left and the right side. I tried to make a "wall" but I had no success. At my player-Gameobject there is an edge collider. Now my question is: how can my player gameobject collide with the side of the screen?
This is my code:
public GameObject player;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
Vector3 dir = Vector3.zero;
dir.y = Input.acceleration.x;
player.transform.Translate(new Vector2(dir.y, 0) * Time.deltaTime * 2000f);
}
Thank you very much! :)
Jul
EDIT:
Image 1 is my Wall's and Image 2 my Player's.
I'm trying to solve it with a wall at the side of the screen. These are the images of
Solved
Solution code:
Vector3 position = player.transform.position;
translation = Input.acceleration.x * movementSpeed * 50f;
if (player.transform.position.x + translation < LeftlimitScreen)
{
position.x = -LeftlimitScreen;
}
else if(transform.position.x + translation > RightlimitScreen)
{
position.x = RightlimitScreen;
}
else
{
position.x += translation;
player.transform.position = position;
}
This code is working for me! :)
This will generate edge colliders around the screen (for 2d):
void GenerateCollidersAcrossScreen()
{
Vector2 lDCorner = camera.ViewportToWorldPoint(new Vector3(0, 0f, camera.nearClipPlane));
Vector2 rUCorner = camera.ViewportToWorldPoint(new Vector3(1f, 1f, camera.nearClipPlane));
Vector2[] colliderpoints;
EdgeCollider2D upperEdge = new GameObject("upperEdge").AddComponent<EdgeCollider2D>();
colliderpoints = upperEdge.points;
colliderpoints[0] = new Vector2(lDCorner.x, rUCorner.y);
colliderpoints[1] = new Vector2(rUCorner.x, rUCorner.y);
upperEdge.points = colliderpoints;
EdgeCollider2D lowerEdge = new GameObject("lowerEdge").AddComponent<EdgeCollider2D>();
colliderpoints = lowerEdge.points;
colliderpoints[0] = new Vector2(lDCorner.x, lDCorner.y);
colliderpoints[1] = new Vector2(rUCorner.x, lDCorner.y);
lowerEdge.points = colliderpoints;
EdgeCollider2D leftEdge = new GameObject("leftEdge").AddComponent<EdgeCollider2D>();
colliderpoints = leftEdge.points;
colliderpoints[0] = new Vector2(lDCorner.x, lDCorner.y);
colliderpoints[1] = new Vector2(lDCorner.x, rUCorner.y);
leftEdge.points = colliderpoints;
EdgeCollider2D rightEdge = new GameObject("rightEdge").AddComponent<EdgeCollider2D>();
colliderpoints = rightEdge.points;
colliderpoints[0] = new Vector2(rUCorner.x, rUCorner.y);
colliderpoints[1] = new Vector2(rUCorner.x, lDCorner.y);
rightEdge.points = colliderpoints;
}
You can place in your scene, outside of the region which will be displayed in your device 2 empty game objects with a collider, so the player will crash against them.
You can also limit by code the boundaries within the player can move. You apply this using Mathf.Clamp(), and there you will need to set the boundaries in the x coordinate for your scene.
You will see that instead of modifying the position of the player using its transform, we use the rigidbody instead.
public class PlayerController : MonoBehaviour
{
public float speed;
public float tilt;
public Boundary boundary;
void FixedUpdate ()
{
float moveHorizontal = Input.GetAxis ("Horizontal");
float moveVertical = Input.GetAxis ("Vertical");
Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
rigidbody.velocity = movement * speed;
rigidbody.position = new Vector3
(
Mathf.Clamp (rigidbody.position.x, boundary.xMin, boundary.xMax),
0.0f,
5.0f
);
}
}
You can check the whole tutorial here: https://unity3d.com/earn/tutorials/projects/space-shooter/moving-the-player?playlist=17147
Update Other options:
//You select here the speed you consider
float speed = 1.0f;
void Update () {
Vector3 dir = Vector3.zero;
float InputValue = Input.acceleration.x * speed;
//You need to set the values for this limits (max and min) based on your scene
dir.y = Mathf.Clamp(InputValue, 0.5f, 50.5f);
player.transform.position = dir;
}
Update 2:
Without Clamp, just setting the limits on the script
void Update () {
Vector3 position = player.transform.position ;
translation = Input.acceleration.x * speed;
if( player.transform.position.y + translation < leftLimitScreen )
position.y = -leftLimitScreen ;
else if( myTransform.position.x + translation > rightLimitScreen )
position.y = rightLimitScreen ;
else
position.y += translation ;
player.transform.position = position ;
}
In a prototype i'm creating the solution i had arrived was creating "walls" with objects without sprites in the borders and checking if there is something there with Raycast with a script like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour {
RaycastHit2D[] hit;
Vector2[] directions;
private Vector2 targetPosition;
private float moveSpeed;
private float moveHDir;
private float wallPos;
private bool hitLeft;
private bool hitRight;
// Use this for initialization
void Start () {
directions = new Vector2[2] {Vector2.right, Vector2.left};
hitLeft = false;
hitRight = false;
}
// Update is called once per physics timestamp
void FixedUpdate () {
foreach (Vector2 dir in directions) {
hit = Physics2D.RaycastAll(transform.position, dir);
Debug.DrawRay(transform.position, dir);
if (hit[1].collider != null) {
// Keyboard control
if (Input.GetAxisRaw("Horizontal") != 0) {
moveHDir = Input.GetAxisRaw("Horizontal");
// I have found that a 5% of the size of the object it's a
// good number to set as a minimal distance from the obj to the borders
if (hit[1].distance <= (transform.localScale.x * 0.55f)) {
if (dir == Vector2.left) {
hitLeft = true;
} else {
hitRight = true;
}
wallPos = hit[1].collider.transform.position.x;
// Condition that guarantee that the paddle do not pass the borders of the screen
// but keeps responding if you try to go to the other side
if ((wallPos > this.transform.position.x && moveHDir < 0) ||
(wallPos < this.transform.position.x && moveHDir > 0)) {
moveSpeed = gControl.initPlayerSpeed;
} else {
moveSpeed = 0;
}
} else {
if (dir == Vector2.left) {
hitLeft = false;
} else {
hitRight = false;
}
if (!hitRight && !hitLeft)
{
moveSpeed = gControl.initPlayerSpeed;
}
}
}
}
}
targetPosition = new Vector2((transform.position.x + (moveSpeed * moveHDir)), transform.position.y);
}
}
Maybe it's not the best solution or the shortest one, but it's working wonders to me.
Good luck.
In case if you want to generate collider on the borders of the canvas (2D)
Attach this script in the main canvas object.
using UnityEngine;
public class BorderCollider: MonoBehaviour
{
private EdgeCollider2D _edgeCollider2D;
private Rigidbody2D _rigidbody2D;
private Canvas _canvas;
private float y, x;
private Vector2 _topLeft, _topRight, _bottomLeft, _bottomRight;
private void Start() {
//Adding Edge Collider
_edgeCollider2D = gameObject.AddComponent<EdgeCollider2D>();
//Adding Rigid body as a kinematic for collision detection
_rigidbody2D = gameObject.AddComponent<Rigidbody2D>();
_rigidbody2D.bodyType = RigidbodyType2D.Kinematic;
//Assigning canvas
_canvas = GetComponent<Canvas>();
GetCanvasDimension(); // Finds height and width fo the canvas
GetCornerCoordinate(); // Finds co-ordinate of the corners as a Vector2
DrawCollider(); // Draws Edge collide around the corners of canvas
}
public void GetCornerCoordinate() {
// Assign corners coordinate in the variables
_topLeft = new Vector2(-x,y); // Top Left Corner
_topRight = new Vector2(x,y); // Top Right Corner
_bottomLeft = new Vector2(-x,-y); // Bottom Left Corner
_bottomRight = new Vector2(x,-y); // Bottom Right Corner
}
void GetCanvasDimension(){
y = (_canvas.GetComponent<RectTransform>().rect.height) / 2;
x = (_canvas.GetComponent<RectTransform>().rect.width) / 2;
}
void DrawCollider() {
_edgeCollider2D.points = new[] {_topLeft, _topRight, _bottomRight, _bottomLeft,_topLeft};
}
}
Does anyone how to make lines to be more guided in unity? Guided as in how you use a real life ruler where the drawn lines is not suppose to be straight and the direction of the line or the end point of the line is affected by how you place the ruler. Currently my code, im able to draw lines and collide with the ruler but the lines are more of a freehand drawing. So how do i make it like what i say earlier on.
Heres my code for the line properties script.
void Update()
{
this.GetComponent<Rigidbody> ().useGravity = false;
this.gameObject.GetComponent<Rigidbody> ().constraints =
RigidbodyConstraints.FreezeRotationX |
RigidbodyConstraints.FreezeRotationZ |
RigidbodyConstraints.FreezePositionY |
RigidbodyConstraints.FreezePositionZ;
}
public void OnCollisionStay(Collision col)
{
if (col.gameObject.tag == "Ruler")
{
if (LockPosX == false && LockPosY == false)
{
Debug.Log ("Collision with Ruler");
//line.SetPosition(1 , newPosititon);
//newPosititon.y = col.transform.position.y;
//newPosititon.z = 0;
LockPosY = true;
}
}
}
Heres my code for spawning the line/drawing the line:
void Update ()
{
if(Input.GetMouseButtonDown(0))
{
if (line == null)
{
createLine ();
mousePos = Camera.main.ScreenToWorldPoint (Input.mousePosition);
mousePos.z = 0;
line.SetPosition (0, mousePos);
startPos = mousePos;
}
}
else if(Input.GetMouseButtonUp(0) && line)
{
mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mousePos.z = 0;
line.SetPosition(1,mousePos);
endPos = mousePos;
addColliderToLine ();
line = null;
currLines++;
}
else if(Input.GetMouseButton(0) && line)
{
mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mousePos.z = 0;
line.SetPosition(1, mousePos);
}
}
public void createLine()
{
line = new GameObject("Line"+currLines).AddComponent<LineRenderer>();
line.material = material;
line.tag = "DrawnLines";
line.SetVertexCount(2);
line.SetWidth(0.15f,0.15f);
line.useWorldSpace = true;
}
private void addColliderToLine()
{
BoxCollider col = new GameObject("Collider").AddComponent<BoxCollider> ();
col.transform.parent = line.transform;
float lineLength = Vector3.Distance (startPos, endPos);
col.size = new Vector3 (lineLength, 1.0f, 5f);
Vector3 midPoint = (startPos + endPos)/2;
col.transform.position = midPoint;
float angle = (Mathf.Abs (startPos.y - endPos.y) / Mathf.Abs (startPos.x - endPos.x));
if((startPos.y<endPos.y && startPos.x>endPos.x) || (endPos.y<startPos.y && endPos.x>startPos.x))
{
angle*=-1;
}
angle = Mathf.Rad2Deg * Mathf.Atan (angle);
col.transform.Rotate (0, 0, angle);
col.gameObject.AddComponent<LineProperties> ();
col.gameObject.AddComponent<Rigidbody> ();
}
It sounds like what you want to do is snap the position of the user's mouse input to the line created by ruler. If this is the case, you could simply use a simple function to accomplish this before setting the line's position.
//This function returns a point which is a projection from a point to a line.
//The line is regarded infinite. If the line is finite, use ProjectPointOnLineSegment() instead.
public static Vector3 ProjectPointOnLine(Vector3 linePoint, Vector3 lineVec, Vector3 point)
{
//get vector from point on line to point in space
Vector3 linePointToPoint = point - linePoint;
float t = Vector3.Dot(linePointToPoint, lineVec);
return linePoint + lineVec * t;
}
For the first argument - linePoint - you would pass in some point along the ruler's edge. If the ruler gameObject's pivot is along this edge, you can just use the ruler's position. The second argument - lineVec - is the direction vector created by the ruler. Depending on how the ruler's gameObject is oriented, this could be as simple as ruler.transform.forward. Finally, for the third argument, you would pass in Input.mousePosition. If you do not want their input to be snapped to the ruler, but where they started, you would then pass in your Input.mousePosition to the first parameter as well. This will snap it to a line following the same direction as the ruler, but the line will start where their input started.
So your void Update() method becomes:
void Update ()
{
if(Input.GetMouseButtonDown(0))
{
if (line == null)
{
createLine ();
mousePos = Camera.main.ScreenToWorldPoint (ProjectPointOnLine(Input.mousePosition, ruler.transform.forward, Input.mousePosition));
mousePos.z = 0;
line.SetPosition (0, mousePos);
startPos = mousePos;
}
}
else if(Input.GetMouseButtonUp(0) && line)
{
mousePos = Camera.main.ScreenToWorldPoint(ProjectPointOnLine(Input.mousePosition, ruler.transform.forward, Input.mousePosition));
mousePos.z = 0;
line.SetPosition(1,mousePos);
endPos = mousePos;
addColliderToLine ();
line = null;
currLines++;
}
else if(Input.GetMouseButton(0) && line)
{
mousePos = Camera.main.ScreenToWorldPoint(ProjectPointOnLine(Input.mousePosition, ruler.transform.forward, Input.mousePosition));
mousePos.z = 0;
line.SetPosition(1, mousePos);
}
}
Edit
If you need a method to retrieve all of the lines with a specific tag - such as DrawnLines which you use in the createLines() method, you can do something like the following
private void UpdateLines()
{
GameObject[] drawnLineGameObjects = GameObject.FindGameObjectsWithTag("DrawnLines");
foreach (var lineObject in drawnLineGameObjects)
{
LineRenderer drawnLine = lineObject.GetComponent<LineRenderer>();
if (drawnLine != null)
{
// Do stuff to your Line Renderer Here
}
}
}
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class Waypoints : MonoBehaviour
{
public GameObject[] waypoints;
public Transform target;
public float moveSpeed = 10f;
public float slowDownSpeed = 3f;
public float reverseSlowDownSpeed = 3f;
public float rotationSpeed = 1f;
private int targetsIndex = 0;
private Vector3 originalPosition;
private GameObject[] players;
public Transform reverseTarget;
private int reverseTargetsIndex = 0;
private Vector3 reverseOriginalPosition;
public bool random = false;
public bool getNextRandom = true;
// Use this for initialization
void Start()
{
waypoints = GameObject.FindGameObjectsWithTag("Blocks");
players = GameObject.FindGameObjectsWithTag("Player");
originalPosition = players[0].transform.localPosition;
}
// Update is called once per frame
void Update()
{
if (random == true)
{
RandomWayPointsAI();
}
else
{
WayPointsAI();
}
}
private void WayPointsAI()
{
if (targetsIndex == waypoints.Length)
targetsIndex = 0;
target = waypoints[targetsIndex].transform;
if (MovePlayer())
targetsIndex++;
}
private void ReverseWayPointsAI()
{
if (reverseTargetsIndex == 0)
reverseTargetsIndex = waypoints.Length - 1;
reverseTarget = waypoints[reverseTargetsIndex].transform;
if (MovePlayer())
reverseTargetsIndex--;
}
void RandomWayPointsAI()
{
if (random == true && getNextRandom)
{
int index = UnityEngine.Random.Range(0, waypoints.Length);
target = waypoints[index].transform;
getNextRandom = false;
}
getNextRandom = MovePlayer();
}
bool MovePlayer()
{
float distance = Vector3.Distance(players[0].transform.position, target.transform.position);
players[0].transform.localRotation = Quaternion.Slerp(players[0].transform.localRotation, Quaternion.LookRotation(target.position - players[0].transform.localPosition), rotationSpeed * Time.deltaTime);
//move towards the player
if (distance < 30)
{
players[0].transform.localPosition += players[0].transform.forward * slowDownSpeed * Time.deltaTime;
}
else
{
players[0].transform.localPosition += players[0].transform.forward * moveSpeed * Time.deltaTime;
}
if (distance < target.transform.localScale.magnitude)
return true;
else
return false;
}
void DrawLinesInScene()
{
// draw lines between each checkpoint //
for (int i = 0; i < waypoints.Length - 1; i++)
{
Debug.DrawLine(waypoints[i].transform.position, waypoints[i + 1].transform.position, Color.blue);
}
// draw a line between the original transform start position
// and the current transform position //
Debug.DrawLine(originalPosition, players[0].transform.position, Color.red);
Debug.DrawLine(reverseOriginalPosition, players[1].transform.position, Color.red);
// draw a line between current transform position and the next waypoint target
// each time reached a waypoint.
if (target != null)
Debug.DrawLine(target.transform.position, players[0].transform.position, Color.green);
if (reverseTarget != null)
Debug.DrawLine(reverseTarget.transform.position, players[1].transform.position, Color.green);
}
void AddColliderToWaypoints()
{
foreach (GameObject go in waypoints)
{
SphereCollider sc = go.AddComponent<SphereCollider>() as SphereCollider;
sc.isTrigger = true;
}
}
}
There are two problems with the script.
If the Move Speed is set to 3 i need to set the Rotation Speed at least to the 10 if i will set the Rotation Speed to 3 or 4 or 5 the rotation will be too wide and it will take much time to the player to get back on track after rotating.
But if it's 10 it seems he is almost rotating on the place so it's not good either.
I want to be able to change the player rotation speed but also to keep him on the waypoints track at any time. I mean that each waypoint the player is reaching to that he will get to it's center. In this case cubes so not only to touch the waypoint but to get to it's center then moving to the next waypoint.
Another problem i see is with the RandomWayPointsAI()
When i change it to use the random method i'm not sure if it's just picking up random positions around the grid or if it's picking random blocks(Cubes).
But it's never getting to a cube it' getting close or between two or sometimes on a cube and same as before also on the random i want to make the random waypoints not just positions but blocks and to get to each random block center.
Center i mean like standing on it.
A glaring problem in the showcased code is the use of local positions and rotations. When moving and rotating characters and objects in the world you should use world space.
Localposition and localrotation are based on the position and rotation of the parent object. Using those to move your object across the world will cause weird problems.
I think the following answer has a pretty accurate explanation of that stuff https://teamtreehouse.com/community/global-space-vs-local-space