- Home /
Choppy lines when using GUI.Label
EDIT: New Screenshots attached.
This is the current result.
This is the app that I mentioned.
EDIT: Here is my codes, after doing some addons to it with Herman's suggestion.
Here is my new script. Now I can't figure out how to call the OnGui function to draw the other points. Currently when I press a finger on the screen and drag, it does not create a trail along. Instead, the texture is just moving with my finger.
var drawArray : ArrayList = new ArrayList( ); var pos : Vector2; var newPos : Vector2; var lastPos : Vector2;
var distanceBetweenLastANdNew : Vector2;
var brush : Texture;
var minimumDistanceBetweenNodes : float = 0.1;
function Update () {
for (var i = 0; i < iPhoneInput.touchCount; ++i)
{
var hit : RaycastHit;
var ray = Camera.main.ScreenPointToRay (iPhoneInput.GetTouch(i).position);
if(iPhoneInput.GetTouch(i).phase == iPhoneTouchPhase.Began)
{
pos = new Vector2(iPhoneInput.GetTouch(i).position.x,iPhoneInput.GetTouch(i).position.y);
}
if(iPhoneInput.GetTouch(i).phase == iPhoneTouchPhase.Moved)
{
pos = new Vector2(iPhoneInput.GetTouch(i).position.x, iPhoneInput.GetTouch(i).position.y);
drawArray.Add(pos);
AddPoint(pos);
}
if(iPhoneInput.GetTouch(i).phase == iPhoneTouchPhase.Ended)
{
drawArray.Clear();
}
}
}
function AddPoint(newPos : Vector2) { lastPos = drawArray[drawArray.Count - 1]; distanceBetweenLastAndNew = (lastPos - newPos).magnitude;
if(distanceBetweenLastAndNew > minimumDistanceBetweenNodes)
{
direction = (newPos - lastPos);
for(var k : int = 0; k < distanceBetweenLastAndNew / minimumDistanceBetweenNodes; k++)
{
// Add the interpolated point
drawArray.Add(lastPos + k * direction);
}
} }
function OnGUI () { if (drawArray.Count>0) { for (var lastPos : Vector2 in drawArray) {
GUI.Label(Rect (pos.x, Screen.height - pos.y, brush.width, brush.height),brush); }
} }
Answer by Herman-Tulleken · Oct 26, 2010 at 06:50 AM
I am not sure whether using GUI Labels is a good way to draw the lines. (Using a LineRenderer in 3D is perhaps better, although it might be tricky to get it to draw over everything else.)
However, you can make it work by interpolating between array points that are far apart. The easiest would be to add extra points as the array is being filled. Here is how it looks (based on your code sample).
var drawArray : ArrayList = new ArrayList( ); var lastPos : Vector2;
var brush : Texture; var minimumDistanceBetweenNodes : float = 0.1; var distanceBetweenLastAndNew : Vector2;
function Update () { for (var i = 0; i < iPhoneInput.touchCount; ++i) { var hit : RaycastHit; var ray = Camera.main.ScreenPointToRay (iPhoneInput.GetTouch(i).position);
if(iPhoneInput.GetTouch(i).phase == iPhoneTouchPhase.Began)
{ //Add the first point
AddPoint(iPhoneInput.GetTouch(i)); //WARNING: you should maintain separate arrays for each touch!
}
if(iPhoneInput.GetTouch(i).phase == iPhoneTouchPhase.Moved)
{
AddPoint(iPhoneInput.GetTouch(i)); //WARNING: you should maintain separate arrays for each touch!
}
if(iPhoneInput.GetTouch(i).phase == iPhoneTouchPhase.Ended)
{
drawArray.Clear();
}
}
}
function AddPoint(newPos : Vector2) { //what to do if the array is empty if(drawArray.Count == 0) { drawArra.Add(newPos); return; }
lastPos = drawArray[drawArray.Count - 1];
distanceBetweenLastAndNew = (lastPos - newPos).magnitude;
if(distanceBetweenLastAndNew > minimumDistanceBetweenNodes)
{
direction = (newPos - lastPos).normalized;
for(var k : int = 0; k < distanceBetweenLastAndNew / minimumDistanceBetweenNodes; k++)
{
// Add the interpolated point
drawArray.Add(lastPos + minimumDistanceBetweenNodes * k * direction);
}
}
//Only add the new pint after adding all the interpolated points. drawArray.Add(newPos ); }
function OnGUI () { if (drawArray.Count>0) { for (var lastPos : Vector2 in drawArray) {
GUI.Label(Rect (pos.x, Screen.height - pos.y, brush.width, brush.height),brush); }
} }
(But if I were you, I would really check out a method using LineRenderer).
i guess that is in C#? currently trying to convert it to JS. where do you initialise the $$anonymous$$imumDistanceBetweenNodes?
Yup, C#. $$anonymous$$imumDistanceBetweenNodes is just a constant that you can tweak to get the right effect - you can set it up in the inspector or initialise it in the Start method.
I managed to convert the script to Javascript. Didnt get any errors. But I am not quite sure how I should tell the onGUI to draw the other points at the same time. Currently when I drag, it does not leave a trail behind.
As for the LineRenderer, I cant quite understand how it works.
I spotted some errors in your code, but I am not sure whether fixing them will fix everything. I have put an updated script in my answer (your code with corrections). There fixes were adding the first point, checking whether the array is empty before trying to interpolate, and adding the new point after interpolation points. Give it a go, and see what happens.
O yes, and you should use separate arrays for each touch. (I am not sure how 2D arrays or arrays of arrays work in JavaScript, so I did not put in the code for this). The AddPoint function should also take the touch index, and add points to the right array. You drawing function must then draw all non-empty curves.
Answer by Bampf · Oct 26, 2010 at 07:22 PM
As others have said, GUI.Label is probably not the way to go. GUI.* doesn't perform well enough on iOS to use for gameplay, unless not much else is being drawn at the same time. And the worse it performs, the bigger your gaps.
Having said that, you may have gaps regardless of how you draw the texture. So there are two parts to my answer: alternatives to GUI.Label, and a way to fill the gaps.
1) Alternatives to GUI.Label.
You might want to look at Texture2D.SetPixel or Texture2D.SetPixels. Your framerate will be better because you'll be accumulating colors into a single texture instead of drawing a large number of GUI.Label objects.
Or, you could simply create a sprite- a triangle or plane (not the built-in Unity plane object- that has way more vertices than you need) with your brush texture on it. You would create one at every touch point, instead of a GUI.Label. This will have a lot less overhead, and the mesh combiner will draw them all with one draw call.
2) Here's a simple algorithm to fill in the gaps [EDIT: which I now see Herman has already coded in a previous answer. Still, here is the description.]
If your frame rate is sufficiently high and your brush (the texture you draw at each position) is big you might not need this. The idea is simply to add new points along the line between the start and end point.
- Take the starting position (previous touch position) and the new position.
- Subtract start from end- this gives you the vector that goes from start to end.
- If the vector length is less then some threshold (let's say 5) then there's no gap; add the new position to your list and you are done.
- If the vector is too long then there is a gap. But all you have to do is add a whole bunch of points along that line, instead of just the endpoint.
- So shrink the vector until it is only 5 pixels long, or whatever the threshold is. (This is easy- just call Vector2.Normalize to make it length 1, then multiple by 5.)
- Now add this vector repeatedly to the start point. Each time, add the result to your list of points to draw.
- You are done when you've gotten to (or past) the end point.
I'm on working on a game which requires the user to draw symbols to activate skills of the character. I'm currently stuck at how to make the texture to be shown on the screen at where the finger touches the screen.
Ok, thanks. I've clarified my answer in two ways: explained better why GUI.Label might be contributing to the problem, and wrote out an algorithm for filling in the gaps with a series of points.
OOPS. Herman is way ahead of me, I see now. He's already written pretty much this algorithm for you! I'll butt out now :-)
i did thought of using the first method that u mentioned. but i couldnt find a way to instantiate it when i touch the screen.