Unity3d How to make line start from mouse position on camera - c#

Currently, the line starts where I click and locks to those coordinates.
However, I want the line to start where I click on the screen and not start moving until I let go of the mouse.
(Game currently has a ball falling).
using UnityEngine;
using System.Collections;
public class TrailCollider : MonoBehaviour
{
private LineRenderer line; // Reference to LineRenderer
private Vector3 mousePos;
public PhysicsMaterial2D phys;
private Vector3 startPos; // Start position of line
private Vector3 endPos; // End position of line
void Update ()
{
// On mouse down new line will be created
if(Input.GetMouseButtonDown(0))
{
if(line == null)
createLine();
mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mousePos.z = 0;
line.SetPosition(0,mousePos);
line.SetPosition(1,mousePos);
startPos = mousePos;
}
else if(Input.GetMouseButtonUp(0))
{
if(line)
{
mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mousePos.z = 0;
line.SetPosition(1,mousePos);
endPos = mousePos;
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.material = new Material(Shader.Find("Diffuse"));
line.SetVertexCount(2);
line.SetWidth(0.1f,0.1f);
line.SetColors(Color.black, 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
col.sharedMaterial = phys;
float lineLength = Vector3.Distance (startPos, endPos); // length of line
col.size = new Vector3 (lineLength, 0.1f, 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);
}
}

Related

Draw line between two points at runtime using Line Renderer

I'm new to coding and trying to make a game where objects appear on a wall, and players will connect 2 that match via a line (this game will feel 2D but is in fact 3D).
My first step is just to be able to draw a line between 2 points. Later I'll try to figure out whether to use bools, or triggers, or colliders or what to determine whether the player connected the correct objects.
I'm having a lot of trouble with this. I want to click the screen once to determine a starting point, then a second time to determine an end point for the line renderer in unity. Then have this all repeat with a new line.
This script is almost working, but for some reason my first point is always set to (0, 0, 0), and I have no idea why. This is my current script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DrawLine_2PT : MonoBehaviour
{
// Apply these values in the editor
public LineRenderer Line;
public GameObject newLine;
public float minimumVertexDistance = 0.1f;
public Vector3 GetWorldCoordinate(Vector3 mousePosition)
{
Vector3 mousePos = new Vector3(mousePosition.x, mousePosition.y, 1);
return Camera.main.ScreenToWorldPoint(mousePos);
}
void Start()
{
// set the color of the line
Line.startColor = Color.red;
Line.endColor = Color.red;
// set width of the renderer
Line.startWidth = 0.3f;
Line.endWidth = 0.3f;
Line.positionCount = 0;
}
void Update()
{
if ((Input.GetMouseButtonDown(0)) && Line.positionCount == 0)
{
Vector3 mousePos = GetWorldCoordinate(Input.mousePosition);
Line.SetPosition(0, mousePos);
Line.positionCount = Line.positionCount + 2;
}
if (Input.GetMouseButtonDown(0) && Line.positionCount == 2)
{
Vector3 mousePos = GetWorldCoordinate(Input.mousePosition);
Line.SetPosition(1, mousePos);
Instantiate(newLine, transform.position, Quaternion.Euler(0, 0, 0));
GetComponent<DrawLine_2PT>().enabled = false;
}
}
}
The last bit spawns a new line after the 2nd point is made, and turns off the initial line renderer. Again, seems to be working except for the first point always spawning at (0, 0, 0) instead of the mouse position.
Any/all advice is much appreciated.
Thanks
You are doing
Line.positionCount = Line.positionCount + 2;
after you call
Line.SetPosition(0, mousePos);
so this has no effect yet since there are no points yet when you try to set it.
And then both positions you add just keep the default position 0,0,0 until you override the second one later.
You should first increase the count and then set the position.
Also note that actually both your code blocks will be executed in the same frame since after increasing the count the second condition matches as well. Is this intended?
I would rather expect something like e.g.
void Update()
{
if ((Input.GetMouseButtonDown(0))
{
if(Line.positionCount == 0))
{
Line.positionCount = 1;
var mousePos = GetWorldCoordinate(Input.mousePosition);
Line.SetPosition(0, mousePos);
}
else
{
Line.positionCount = 2;
var mousePos = GetWorldCoordinate(Input.mousePosition);
Line.SetPosition(1, mousePos);
Instantiate(newLine, transform.position, Quaternion.Euler(0, 0, 0));
GetComponent<DrawLine_2PT>().enabled = false;
}
}
}
And if you want to you could even visually update the line while the user is drawing it like e.g.
void Update()
{
if ((Input.GetMouseButtonDown(0))
{
if(Line.positionCount == 0))
{
Line.positionCount = 1;
Vector3 mousePos = GetWorldCoordinate(Input.mousePosition);
Line.SetPosition(0, mousePos);
}
else
{
Line.positionCount = 2;
var mousePos = GetWorldCoordinate(Input.mousePosition);
Line.SetPosition(1, mousePos);
Instantiate(newLine, transform.position, Quaternion.Euler(0, 0, 0));
GetComponent<DrawLine_2PT>().enabled = false;
}
}
else if(Line.positionCount == 1)
{
var mousePos = GetWorldCoordinate(Input.mousePosition);
Line.SetPosition(1, mousePos);
}
}

How to draw a sine wave using line renderer from point A to point B

Following this tutorial, I'm trying to use Line Renderer component to draw a sine wave from point A to B.I'm using the input mouse position. However what I did so far is not working, it just draw the sine wave along the x axis, I need to draw it from the start point to the input mouse position.
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector3 newPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
newPos.z = 0;
CreatePointMarker(newPos);
GenerateNewLine();
}
}
private void CreatePointMarker(Vector3 pointPosition)
{
Instantiate(linePointPrefab, pointPosition, Quaternion.identity);
}
private void GenerateNewLine()
{
GameObject[] allPoints = GameObject.FindGameObjectsWithTag("PointMarker");
Vector3[] allPointPositions = new Vector3[allPoints.Length];
var pointList = new List<Vector3>();
for (int i = 0; i < 50; i ++)
{
var dir = allPoints[0].transform.position - allPoints[1].transform.position;
float x = dir.x * i;
float y = Mathf.Sin(x * Time.time);
var sine = new Vector3(x, y, 0f);
var tangentLine = allPoints[0].transform.position + sine;
pointList.Add(tangentLine);
}
SpawnLineGenerator(pointList.ToArray());
}
}
private void SpawnLineGenerator(Vector3[] linePoints)
{
GameObject newLineGen = Instantiate(lineGeneratorPrefab);
LineRenderer lRend = newLineGen.GetComponent<LineRenderer>();
lRend.positionCount = linePoints.Length;
lRend.SetPositions(linePoints);
}
As an alternative I would suggest to instead use
lineRenderer.useWorlsSpace = false;
so the points are no longer set in worldspace but in the local space relative to the linerenderer transform.
Now you can simply rotate the linerenderer transform to point towards the latest user input position.
I couldn't use your code example since I don't know what your prefabs are and do so I created my own from the code in the given Video. I hope you can reuse/recreate the parts necessary
public class SinusWave : MonoBehaviour
{
public Vector3 initalPosition;
public int pointCount = 10;
public LineRenderer line;
private Vector3 secondPosition;
private Vector3[] points;
private float segmentWidth;
private void Awake()
{
line = GetComponent<LineRenderer>();
line.positionCount = pointCount;
// tell the linerenderer to use the local
// transform space for the point coorindates
line.useWorldSpace = false;
points = new Vector3[pointCount];
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
// Camera.main.ScreenToWorldPoint needs a value in Z
// for the distance to camera
secondPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition - Vector3.forward * Camera.main.transform.position.z);
secondPosition.z = 0;
var dir = secondPosition - initalPosition;
// get the segmentWidth from distance to end position
segmentWidth = Vector3.Distance(initalPosition, secondPosition) / pointCount;
// get the difference angle in the Z axis between the current transform.right
// and the direction
var angleDifference = Vector3.SignedAngle(transform.right, dir, Vector3.forward);
// and rotate the linerenderer transform by that angle in the Z axis
// so the result will be that it's transform.right
// points now towards the clicked position
transform.Rotate(Vector3.forward * angleDifference, Space.World);
}
for (var i = 0; i < points.Length; ++i)
{
float x = segmentWidth * i;
float y = Mathf.Sin(x * Time.time);
points[i] = new Vector3(x, y, 0);
}
line.SetPositions(points);
}
}
Btw I just assume here that GameObject.FindGameObjectsWithTag("PointMarker"); actually retuns all the so far spawned linePointPrefab objects.
It would be better to store them right away when you spawn them like
private List<Transform> allPoints = new List<Transform>();
...
allPoints.Add(Instantiate(linePointPrefab, pointPosition, Quaternion.identity).transform);
then you can skip the usage of FindObjectsWithType completely
For adopting this local space position also for the line point prefabs you instantiate simply instantiate them as child of the linerenderer transform or spawn them and the linerenderer under the same parent so the relative positions are the same. In this case you wouldn't rotate the linerenderer but the entire parent object so the spawned linepoints are rotated along with it

touch control to move cube (in an array that generates them) left and right

I am moving an object that consists of two cubes: left and right. These cubes are randomly generated on the y-axis upwards.
I am able to move them left and right with no problem, however, when I move one of the cubes left or right they all move.
How am I able to only move one of the cubes when touched only left or right rather than all of them? Here is my code below:
Generate cubes code:
public Transform block;
public Transform player;
private float objectSpawnedTo = 5.0f;
public static float distanceBetweenObjects = 5.0f;
private float nextCheck = 0.0f;
private ArrayList objects = new ArrayList();
void Start () {
maintenance(0.0f);
}
void Update () {
float playerX = player.position.y;
if(playerX > nextCheck)
{
maintenance(playerX);
}
}
private void maintenance(float playerX)
{
nextCheck = playerX + 30;
for (int i = objects.Count-1; i >= 0; i--)
{
Transform blck = (Transform)objects[i];
if(blck.position.y < (transform.position.y - 30))
{
Destroy(blck.gameObject);
objects.RemoveAt(i);
}
}
spawnObjects(5);
}
private void spawnObjects(int howMany)
{
float spawnX = objectSpawnedTo;
for(int i = 0; i<howMany; i++)
{
Vector3 pos = new Vector3(-3.5f,spawnX, 0);
//float firstRandom = Random.Range(-6.0f, 1.0f);
Transform blck = (Transform)Instantiate(block, pos, Quaternion.identity);
//blck.localScale+=new Vector3(firstRandom*2,0,0);
objects.Add(blck);
//pos = new Vector3(0,spawnX, 0);
//blck = (Transform)Instantiate(block, pos, Quaternion.identity);
//blck.localScale +=new Vector3((8.6f-firstRandom)*2,0,0);
//objects.Add(blck);
spawnX = spawnX + distanceBetweenObjects;
}
objectSpawnedTo = spawnX;
}
}
Cube-control code:
public float speed = 5;
// Use this for initialization
void Start () {
}
void Update() {
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved) {
// Get movement of the finger since last frame
Vector3 touchDeltaPosition = Input.GetTouch(0).deltaPosition;
// Move object across XY plane
transform.Translate(touchDeltaPosition.x * speed, 0, 0);
Vector3 boundaryVector = transform.position;
boundaryVector.x = Mathf.Clamp (boundaryVector.x, -5.5f, -2.8f);
transform.position = boundaryVector;
}
}
}
The problem is your cube-control code has no condition in it about which cube is selected : you simply wait for Input.touchCount to be > 0 and then move the cube. So all the cube having this script will move.
I think what you need to do is a raycast to check which cube has been "touched" and then only move it if raycast is successful :
[SerializeField]
private float speed = 0.5f;
private int MAX_TOUCH_COUNT = 5;
private bool[] touched;
protected void Start()
{
touched = new bool[MAX_TOUCH_COUNT];
}
void Update()
{
if (Input.touchCount > 0)
{
for(int i = 0; i < (Input.touchCount <= MAX_TOUCH_COUNT ? Input.touchCount : MAX_TOUCH_COUNT); i++)
{
if(touched[i] && Input.GetTouch(i).phase == TouchPhase.Ended)
{
touched[i] = false;
}
else if (!touched[i] && Input.GetTouch(i).phase == TouchPhase.Began)
{
Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(i).position);
RaycastHit hitInfo;
if (Physics.Raycast(ray, out hitInfo))
{
if (hitInfo.transform.GetComponentInChildren<*YOUR_CUBE_CONTROL_CLASS_NAME*>() == this)
{
touched[i] = true;
}
}
}
else if (touched[i] && Input.GetTouch(i).phase == TouchPhase.Moved)
{
// Get movement of the finger since last frame
Vector3 touchDeltaPosition = Input.GetTouch(i).deltaPosition;
// Move object across XY plane
transform.Translate(touchDeltaPosition.x * speed, 0, 0);
Vector3 boundaryVector = transform.position;
boundaryVector.x = Mathf.Clamp (boundaryVector.x, -5.5f, -2.8f);
transform.position = boundaryVector;
}
}
else
{
touched = false;
}
}
}
Hope this help,

Create shapes in android with unity

I am trying to create custom shapes in unity with free hand but i am getting very thick shapes Like this
I am using this code
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
public class DrawLine : MonoBehaviour
{
public Text toshow;
private LineRenderer line;
private bool isMousePressed;
public List<Vector3> pointsList;
private Vector3 mousePos;
// 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 = new Material (Shader.Find ("Particles/Additive"));
line.SetVertexCount (0);
line.SetWidth (0.1f, 0.1f);
line.SetColors (Color.green, Color.green);
line.useWorldSpace = true;
isMousePressed = false;
pointsList = new List<Vector3> ();
// renderer.material.SetTextureOffset(
}
// Following method adds collider to created line
private void addColliderToLine(Vector3 startPos, Vector3 endPos)
{
BoxCollider col = new GameObject("Collider").AddComponent<BoxCollider> ();
col.gameObject.tag = "Box";
col.transform.parent = line.transform; // Collider is added as child object of line
float lineLength = Vector3.Distance (startPos, endPos); // length of line
col.size = new Vector3 (lineLength, 0.1f, 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);
}
void createCollider(){
for (int i=0; i < pointsList.Count - 1; i++) {
addColliderToLine(pointsList[i],pointsList[i+1]);
}
}
public void MouseDrag(){
#if UNITY_ANDROID
Vector2 touchPos = Input.touches[0].position;
mousePos = Camera.main.ScreenToWorldPoint (new Vector3(touchPos.x, touchPos.y, 0));
mousePos.z = 0;
if (!pointsList.Contains (mousePos)) {
pointsList.Add (mousePos);
line.SetVertexCount (pointsList.Count);
line.SetPosition (pointsList.Count - 1, (Vector3)pointsList [pointsList.Count - 1]);
}
#endif
}
public void MouseBeginDrag(){
isMousePressed = true;
line.SetVertexCount (0);
GameObject[] ob = GameObject.FindGameObjectsWithTag("Box");
for(int i=0; i<ob.Length; i++){
Destroy(ob[i]);
}
pointsList.RemoveRange (0, pointsList.Count);
line.SetColors (Color.green, Color.green);
}
public void MouseFinishDrag(){
createCollider();
isMousePressed = false;
}
// -----------------------------------
void Update ()
{
}
}
Please Help me to decrease the width of a line. I am not able to decrease the width of a line. Please help me

Move player along Y pos

I've made the move from SpriteKit and Swift to Unity recently, and I've started an Android project. What I want is to move the player up and down so along the Y position only. In xcode I would have simply done this:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let action = SKAction.moveToY(location.y, duration: 0.7)
action.timingMode = .EaseInEaseOut
player.runAction(action)
}
}
Would this sort of code translate to C# in the same sort of way? Or does it work differently.
** UPDATE **
This is the code I tried with one of the answers below, but the player is disappearing rather than moving, any suggestions? What I am after is for the player to move to where the screen is tapped, but only on the Y axis. Thanks :)
Vector2 touchPosition;
[SerializeField] float speed = 1f;
void Update() {
for (var i = 0; i < Input.touchCount; i++) {
if (Input.GetTouch(i).phase == TouchPhase.Began) {
// assign new position to where finger was pressed
transform.position = new Vector3 (transform.position.x, Input.GetTouch(i).position.y, transform.position.z);
}
}
}
You can move any object in your scene by calling transform.Translate() from a script on that object, or by getting a reference to another object's transform and calling Translate() on that.
Move current script's scene object based on finger movement
Vector2 touchDeltaPosition;
[SerializeField] float speed = 1f;
void Update() {
for (var i = 0; i < Input.touchCount; i++) {
if (Input.GetTouch(i).phase == TouchPhase.Moved) {
// get new finger position
touchDeltaPosition = Input.GetTouch(i).deltaPosition;
// move scene object along y axis
transform.Translate(0f, -touchDeltaPosition.y * speed, 0f);
}
}
}
Move current script's scene object to finger press location
void Update() {
for (var i = 0; i < Input.touchCount; i++) {
if (Input.GetTouch(i).phase == TouchPhase.Began) {
// assign new position to where finger was pressed
transform.position = new Vector3 (transform.position.x, Input.GetTouch(i).position.y, transform.position.z);
}
}
}
public float speed = 5.0f;
public void Update()
{
Vector3 moveDelta = new Vector3(0f, Input.GetAxis("Vertical"), 0f);
transform.Translate(moveDelta * speed * Time.deltatime)
}
For anyone who needs to know, this is how it worked for me:
public GameObject thingToMove;
public float smooth = 2;
private Vector3 _endPosition;
private Vector3 _startPosition;
private void Awake()
{
_startPosition = thingToMove.transform.position;
}
private void Update()
{
if(Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)
{
_endPosition = HandleTouchInput();
}
else
{
_endPosition = HandleMouseInput();
}
thingToMove.transform.position = Vector3.Lerp(thingToMove.transform.position, new Vector3(transform.position.x, _endPosition.y, 0), Time.deltaTime * smooth);
}
private Vector3 HandleTouchInput()
{
for (var i = 0; i < Input.touchCount; i++)
{
if (Input.GetTouch(i).phase == TouchPhase.Began)
{
var screenPosition = Input.GetTouch(i).position;
_startPosition = Camera.main.ScreenToWorldPoint(screenPosition);
}
}
return _startPosition;
}
private Vector3 HandleMouseInput()
{
if(Input.GetMouseButtonDown(0))
{
var screenPosition = Input.mousePosition;
_startPosition = Camera.main.ScreenToWorldPoint(screenPosition);
}
return _startPosition;
}
This moves the object to touch location smoothly using .Lerp. More information:
http://docs.unity3d.com/ScriptReference/Vector3.Lerp.html

Categories