How to make a line-drawing 2d game, like flight control

here is what I thought

1 if (touch.phase == iPhoneTouchPhase.Began)

2.get the finger’s movement to an array,

3 if (touch.phase == iPhoneTouchPhase.End)

4 draw line (but how? have no idea)

5 move the object,through the line, I guess will use waypoint?

any thoughts?

pleasee, anyone?

Hi There

I haven’t had a reason yet to try to create a line so I’m no expert however I’ve seen many posts that talk about using the LineRenderer Class to create this affect, link: Unity - Scripting API: LineRenderer

If you haven’t done so already try searching the forum with this keyword.

@1r0nM0nkey has created a game called Air Boss link: http://www.youtube.com/watch?v=Wm5D3ncL-PQ

He mentions in this post here:
http://forum.unity3d.com/viewtopic.php?t=26099&highlight=air+boss that he used a LineRenderer to create the 2D line.

Hope this helps!

thanks, I triedm but cannot work, here is the code
// Moves object according to finger movement on the screen
var speed:float = 0.1;
var lineArray= new Array();
var i1=0;

function Update () {

for (var touch:iPhoneTouch in iPhoneInput.touches)
if ( iPhoneInput.GetTouch(0).phase == iPhoneTouchPhase.Moved) {

var touchDeltaPosition:Vector2 =iPhoneInput.GetTouch(0).deltaPosition;
lineArray.Push(touchDeltaPosition);
Drawline();}
}

function Drawline(){
var line: LineRenderer;
line=gameObject.AddComponent(LineRenderer);
line=gameObject.GetComponent(LineRenderer);
var pointlength=lineArray.length;
line.useWorldSpace= false;
for (a=0;a<pointlength;)
{
line.SetPosition(a,lineArray[a]);
a++;
}
}

but I got:InvalidCastException: Cannot cast from source type to destination type.
touchmove.Drawline () (at Assets/test/touchmove.js:34)
touchmove.Update () (at Assets/test/touchmove.js:19)

js 34 is line.SetPosition(a,lineArray[a]);

what’s wrong with my code? thanks a lot

Assign lineArray[a] to a Vector3 variable before calling SetPosition:-

var pt: Vector3 = lineArray[a];
line.SetPosition(a, pt);

anyone got this to work?

i tried

var speed:float = 0.1; 
var lineArray= new Array(); 
var i1=0; 


function Update () { 

for (var touch:iPhoneTouch in iPhoneInput.touches) 
if ( iPhoneInput.GetTouch(0).phase == iPhoneTouchPhase.Moved) { 

var touchDeltaPosition:Vector2 =iPhoneInput.GetTouch(0).deltaPosition; 
lineArray.Push(touchDeltaPosition); 
Drawline();} 
} 

function Drawline(){ 
var line: LineRenderer; 
line=gameObject.AddComponent(LineRenderer); 
line=gameObject.GetComponent(LineRenderer); 
var pointlength=lineArray.length; 
line.useWorldSpace= false; 
for (a=0;a<pointlength;) 
{ 
var pt: Vector3 = lineArray[a]; 
line.SetPosition(a, pt);
a++; 
} 
}

coming with an error InvalidCastException “can not cast from source type to destination type”

thanx in advance

Here are some snippets from the AirBoss line rendering code:

GameObject line_object =
	Instantiate (BootStrap.route_server.line_prefab, Vector3.zero, Quaternion.identity) as GameObject;
line_object.name = Convert.ToString ("help_line_" + callsign);			
line_object.transform.parent = GameObject.Find ("/game/route_repository").transform;

help_route = new Route ();
help_route.initialize (line_object);
help_route.connect_data (transform.position, transform.position + transform.up * 30.0f, 0);
help_route.connect_data (help_target.transform.position + help_target.transform.right * 15.0f, 0);
help_route.connect_data (help_target.transform.position, 0);
help_route.smooth_all ();
help_route.connect_line (0, help_route.data.Count);

Route.cs

using UnityEngine;
using System.Collections;

public class Route {

	public static int 	segment_max = 32;
	public static float	segment_spacing = 8;

	public bool	 is_assigned;
	public float timestamp;
	
	public ArrayList data;
	
	LineRenderer line;
	Vector3 last;
	Vector3 step;
	bool first_point;
	
	public void initialize (GameObject line_object) {
		
		data = new ArrayList ();
		line = line_object.GetComponent ("LineRenderer") as LineRenderer;
		line.SetWidth (2.0f, 2.0f);
		line.useWorldSpace = true;
		
		reset ();
	}
	
	public void reset () {

		data.Clear ();
		clear_line ();
		last = Vector3.zero;
		step = Vector3.up;
		first_point = true;
	}
	
	public void assign (bool value) {
		
		is_assigned = value;
		if (is_assigned)
			timestamp = Time.time;
		else
			timestamp = 0.0f;
	}
	
	public void set_color (Color color) {		
		line.material.color = color;
	}
	
	public Vector3 points_to () {	
		return step;
	}
	
	public Vector3 ends_at () {	
		return last;
	}
	
	public void connect_data (Vector3 target, int extension) {
		
		first_point = false;
		connect_data (last, target, extension);
		first_point = true;
	}
		
	public void connect_data (Vector3 anchor, Vector3 target, int extension) {
		
		float distance = Vector2.Distance ((Vector2)anchor, (Vector2)target);
		
		if (first_point) {
			last = Vector3.right * anchor.x + Vector3.up * anchor.y;
			data.Add (last);
		}
		
		step = (Vector2)target - (Vector2)last;
		step.Normalize ();
		
		while (distance >= segment_spacing  data.Count <= segment_max + extension) {
			
			last += step * segment_spacing;			
			data.Add (last);
			
			distance = Vector2.Distance (last, (Vector2)target);
		}
	}
	
	public void connect_data_last (Vector3 target) {
		if (0 < data.Count)
			data.Add (target);
	}
	
	public void smooth_front () {
		
		if (2 < data.Count) {
			
			Vector3 weighted_average;
			weighted_average =
				(Vector3)data[0] * 0.3f +
				(Vector3)data[1] * 0.4f +
				(Vector3)data[2] * 0.3f;
			data [1] = weighted_average;
		}
	}
	
	public void smooth_back () {
		
		if (2 < data.Count) {
			
			Vector3 weighted_average;
			weighted_average =
				(Vector3)data[data.Count - 3] * 0.3f +
				(Vector3)data[data.Count - 2] * 0.4f +
				(Vector3)data[data.Count - 1] * 0.3f;
			data [data.Count - 2] = weighted_average;
		}
	}
	
	public void smooth_at (int index) {
		
		if (4 < data.Count  0 <= index - 2  index + 2 < data.Count) {

			Vector3 weighted_average;
			weighted_average =
				(Vector3)data[index - 2] * 0.1f +
				(Vector3)data[index - 1] * 0.15f +
				(Vector3)data[index    ] * 0.5f +
				(Vector3)data[index + 1] * 0.15f +
				(Vector3)data[index + 2] * 0.1f;

			data[index] = weighted_average;
		}
	}
	
	public void smooth_between (int lo, int hi) {
		
		if (4 < data.Count  2 <= lo - 2  hi < data.Count - 2) {
			
			for (int i = lo; i < hi; i++) smooth_at (i);
		}
	}
	
	public void smooth_all () {
		
		if (4 < data.Count) {
			
			for (int i = 2; i < data.Count - 2; i++) smooth_at (i);
			
			smooth_front ();
			smooth_back ();
		}
	}

	public void connect_line (int data_range_min, int data_range_max) {
		
		if (data_range_min > data_range_max)
			data_range_min = data_range_max;
		if (data_range_max > data.Count)
			data_range_max = data.Count;
		
		int position_count = data_range_max - data_range_min;
		
		line.SetVertexCount (data_range_max - data_range_min);
		line.material.SetTextureScale ("_MainTex", Vector2.right * position_count + Vector2.up);
		
		
		for (int i = 0; i < position_count; i++)	
			line.SetPosition (i, (Vector3)data[data_range_min + i]);
	}
	
	public void clear_line () {
		
		line.SetVertexCount (0);	
	}
}

thanx a million

thanks a zillion

:smile:

Thanks for supplying the line rendering code, 1r0nMonkey. I’m having some difficulties understanding the usage portion of the code provided. I would like to draw a path with the mouse, from a GameObject to wherever the mouse goes.

I’m not sure what the following line does… I’m not sure what the “/game/route_repository” holds

line_object.transform.parent = GameObject.Find ("/game/route_repository").transform;

And in

help_route = new Route ();
help_route.initialize (line_object);
help_route.connect_data (transform.position, transform.position + transform.up * 30.0f, 0);
help_route.connect_data (help_target.transform.position + help_target.transform.right * 15.0f, 0);
help_route.connect_data (help_target.transform.position, 0);
help_route.smooth_all ();
help_route.connect_line (0, help_route.data.Count);

What is the help_target?

Thanks

Those are just positions… I cut that code right out of Airboss, so it won’t work as-is. Just take a look at the Route class and test it out.

initialize(…), connect_data(…), smooth_xxx(…), and connect_line(…) are all you need to know, really.

Please somebody post up the game project where the function is working :slight_smile:

Come on man,
I dropped code from a project to help out, and you won’t touch it until it is stripped down enough to run out of the box? That is either serious laziness or an overdeveloped sense of entitlement.

Step through it! It represents a fair amount of discovery and work on my part, and comes from a real-world project. With even just a LITTLE effort, I’m sure you will get the jist of it.

So, I’m pretty late to the party on this one.

Quick explanation of how I used 1r0nM0nkey’s code with a few small changes. This (v.brief) explanation will allow you to draw nice smooth lines with your mouse.

First, drop the below Route.cs into your project:

using UnityEngine;

using System.Collections;

public class Route {
// increase for longer lines
public static int   segment_max = 32;
// decrease for smoother lines
public static float segment_spacing = 8;
public bool  is_assigned;
public float timestamp;
public ArrayList data;

public float lineWidthStart = 0.5f;
public float lineWidthEnd = 0.5f;
LineRenderer line;
Vector3 last;
Vector3 step;
bool first_point;

    

	public void initialize (GameObject line_object, Vector3 startPosition) {
		data = new ArrayList ();
		line = line_object.GetComponent ("LineRenderer") as LineRenderer;
		line.SetWidth (lineWidthStart, lineWidthEnd);
		line.useWorldSpace = true;
		reset (startPosition);
	}

    

    public void reset (Vector3 startPosition) {
		data.Clear ();
        clear_line ();
		// was : 
        //  last = Vector3.zero;
		 last =startPosition;
        step = Vector3.up;
        first_point = true;
    }

    

    public void assign (bool value) {
	 is_assigned = value;
		if (is_assigned){
            timestamp = Time.time;
		}
        else{
            timestamp = 0.0f;
		}
    }

    

    public void set_color (Color color) {       
        line.material.color = color;
    }

    

    public Vector3 points_to () {   
        return step;
    }

    

    public Vector3 ends_at () { 
        return last;
    }

    

    public void connect_data (Vector3 target, int extension) {
        first_point = false;
        connect_data (last, target, extension);
        first_point = true;
    }

        

    public void connect_data (Vector3 anchor, Vector3 target, int extension) {
		float distance = Vector2.Distance ((Vector2)anchor, (Vector2)target);

		if (first_point) {
            last = Vector3.right * anchor.x + Vector3.up * anchor.y;
            data.Add (last);
        }

        

        step = (Vector2)target - (Vector2)last;
        step.Normalize ();

        while (distance >= segment_spacing  data.Count <= segment_max + extension) {
 		 	last += step * segment_spacing;         
			data.Add (last);
			distance = Vector2.Distance (last, (Vector2)target);
		}

    }

    

    public void connect_data_last (Vector3 target) {

        if (0 < data.Count){
            data.Add (target);
		}
    }

    

    public void smooth_front () {

        if (2 < data.Count) {
            Vector3 weighted_average;
            weighted_average =

                (Vector3)data[0] * 0.3f +
                (Vector3)data[1] * 0.4f +
                (Vector3)data[2] * 0.3f;
            data [1] = weighted_average;

        }

    }

    

    public void smooth_back () {

        

        if (2 < data.Count) {
			Vector3 weighted_average;
            weighted_average =
                (Vector3)data[data.Count - 3] * 0.3f +
                (Vector3)data[data.Count - 2] * 0.4f +
                (Vector3)data[data.Count - 1] * 0.3f;
            data [data.Count - 2] = weighted_average;

        }

    }

    

    public void smooth_at (int index) {

        if (4 < data.Count  0 <= index - 2  index + 2 < data.Count) {

            Vector3 weighted_average;
            weighted_average =

                (Vector3)data[index - 2] * 0.1f +
                (Vector3)data[index - 1] * 0.15f +
                (Vector3)data[index    ] * 0.5f +
                (Vector3)data[index + 1] * 0.15f +
                (Vector3)data[index + 2] * 0.1f;
            data[index] = weighted_average;

        }

    }

    

    public void smooth_between (int lo, int hi) {

        

        if (4 < data.Count  2 <= lo - 2  hi < data.Count - 2) {  
            for (int i = lo; i < hi; i++) smooth_at (i);
        }

    }

    

    public void smooth_all () {

        

        if (4 < data.Count) {
            for (int i = 2; i < data.Count - 2; i++) smooth_at (i);
            smooth_front ();
            smooth_back ();

        }

    }

 

    public void connect_line (int data_range_min, int data_range_max) {

        if (data_range_min > data_range_max)
            data_range_min = data_range_max;

        if (data_range_max > data.Count)
            data_range_max = data.Count;

        

        int position_count = data_range_max - data_range_min;
        line.SetVertexCount (data_range_max - data_range_min);
        line.material.SetTextureScale ("_MainTex", Vector2.right * position_count + Vector2.up);

        

        

        for (int i = 0; i < position_count; i++)    
            line.SetPosition (i, (Vector3)data[data_range_min + i]);

    }

    

    public void clear_line () {
        line.SetVertexCount (0);    

    }

}

Then, create Draw.cs and attach it to the camera:

using UnityEngine;
using System.Collections;

public class Draw : MonoBehaviour {

private Route line_route;
public bool ran;
public Vector3 toPoint;
public float distanceFromCamera = 10f;
	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
		if(Input.GetMouseButton(0)){
			// hack to get a point in world space.
			RaycastHit hit;
			Ray ray = GameObject.Find("Main Camera").GetComponent<Camera>().ScreenPointToRay(new Vector3 (Input.mousePosition.x, Input.mousePosition.y,0));
			toPoint = ray.GetPoint (distanceFromCamera);
			if (ran ==false){
				LineInitialize();
				ran = true;
			}
			else{
				line_route.connect_data (toPoint, 0);
			// smooth and connect
				line_route.smooth_all ();
				line_route.connect_line (0, line_route.data.Count);
			}
		}
		else{
		// mouse isnt held down, reset,
			ran = false;	
		}
		
		}
	
	

	private	void LineInitialize(){
		GameObject line_object = Instantiate (Resources.Load("LineObject"),  toPoint, Quaternion.identity) as GameObject;
		line_route = new Route ();
		line_route.initialize (line_object,toPoint );
	
	}

}

When the mouse is held down, I use a ScreenPointToRay from “Main Camera” (which has to be Orthographic ) to get a Vector3 using the mouse X and Y as well as the distanceFromCamera variable.

This isn’t very efficient, I’m certain someone could find a quicker and easier way of doing it… but it works

For the first frame (when ran==false), LineInitialize() is run. This creates a new Route with line_route.initialize. I slightly changed the line_route.initialize and reset functions within Route.cs to include a startPosition Vector3 variable.

After this (ran==true) Update dumps the mouse position using line_route.connect_data.

Simple stuff, hope this helps people. Thanks 1r0nM0nkey for the script, great work! :slight_smile:

Thanks for sharing. I try the code from gonzorob. But the line drawing is not very stable. The curve is keeping changing its shape until I stop moving my mouse. Is it normal?

I think it’s because the line renderer is a billboardet mesh which keeps trying to face the camera.

If you’re going to draw stuff then I would recommend Vectrosity instead. It’s superior in every way. It’s available in the asset store.

I think its not caused by the billboard mesh because the whole curve is floating and changing its scope and radian. Tnanks anyway : )

i have an error during gameplay that object u want to instantiate is null. how can it be removed?