2
votes

I'm trying to paint "pixels". I created Conway's game of life in Unity and i want to add a feature where you press mouse button and when it's pressed you "paint" - set cells alive. So my idea was:

In Update() if mouse button is pressed Start Coroutine.

In Coroutine you have loop that sets cell pointed by Input.mousePosition to Alive-state then waits for end of frame. Loop, and by that Coroutine ends when that mouse button is released.

My problem is that if you move mouse rapidly created line will not be continuous, because inputs form mouse from two frames will be different (far apart).

Since you can take Input.mousePosition only once per frame i tried approximating this by storing mousePosition from previous frame and calculating all points that lie on Edge between CurrentMousePosition and PreviousMousePosition

However i was not happy with the result.

My Question is: is there a better way to prevent this un-continuous line than letting it be and then fixing it? And if not is there a better way to approximate points that lie on Edge?

void Update()
{
  if (Input.GetMouseButtonDown(1))
  {
    StartCoroutine(BrushPixel());
  }
  UpdatePlane(true);
}

IEnumerator BrushPixel()
        {
            int prevX = 0;
            int prevY = 0;
            bool firstIteratrion = true;
            while (true)
            {
                IntVector3 currentPos = new IntVector3(Camera.main.ScreenToWorldPoint(Input.mousePosition));
                int xPos = currentPos.x + sizeX/2;
                int yPos = currentPos.y + sizeY/2;

                if (xPos >= 0 && xPos < sizeX && yPos >= 0 && yPos < sizeY)
                {
                    if (firstIteratrion)
                    {
                        firstIteratrion = false;
                        prevX = xPos;
                        prevY = yPos;
                    }

                    mainMatrix[xPos, yPos] = true;
                    Queue<IntVector3> pointsAlong = AproximatePoints(xPos, yPos, prevX, prevY);
                    while(pointsAlong.Count != 0)
                    {
                        IntVector3 temp = pointsAlong.Dequeue();
                        if(temp.x >= 0 && temp.x < sizeX &&  temp.y >= 0 &&  temp.y < sizeY)
                        {
                            mainMatrix[temp.x, temp.y] = true;
                        }
                    }
                    UpdatePlane();
                    prevX = xPos;
                    prevY = yPos;
                }
                if (Input.GetKeyUp(KeyCode.Mouse1))
                {
                    break;
                }
                yield return null;
            }
        }

public Queue<IntVector3> AproximatePoints (int curX, int curY, int prevX, int prevY)
        {
            Queue<IntVector3> additional = new Queue<IntVector3>();
            if(curX == prevX && curY == prevY)
            {
                return additional;
            }
            int distance = (int)Math.Sqrt( (double)((curX - prevX) * (curX - prevX))  +  (double)((curY - prevY) * (curY - prevY)) );
            int count = distance;
            int d = distance / count;
            double angle = Math.Atan2(curX - prevX, curY - prevY);
            for (int i = 0; i < count; i++)
            {
                additional.Enqueue(new IntVector3((int)Math.Floor(curX + i * d * Math.Cos(angle)), (int)Math.Floor(curY + i * d * Math.Cos(angle)), 0));
            }
            return additional;
        }

Additional notes: MainMatrix is bool[sizeX, sizeY] that holds states of current generation where true is Alive and false is dead. In line

IntVector3 currentPos = new IntVector3(Camera.main.ScreenToWorldPoint(Input.mousePosition));
int xPos = currentPos.x + sizeX/2;
int yPos = currentPos.y + sizeY/2;

i translate Input from mouse to addres in MainMatrix. IntVector3 is my struct just like Unity Vector3 but holds x, y, z as Integers. UpdatePlane() is generating image that will be changed. UpdatePlane(bool k) is generating image that will be changed and calculates next generation of cells.

Current approximation gives pic1 when trying to draw line form UpperLeft corner to LowerRight corner. pic1

1
Do you want a way to approximate the mouse’s path in that frame to paint all the cells? I don’t quite understand. - ken
I would use IPointer events. You can get the position every frame using OnDrag or get the location when the user clicks and releases the mouse. - TEEBQNE

1 Answers

1
votes

You could interpolate with a resolution variable. Interpolation finds a point between two points, based on t. t is between 0 and 1, and when closer to 0, the closer to the output is to the first vector, and when closer to one, the closer the output is to the second. We could use a for loop using a resolution value as the resolution of the interpolation (a resolution of 2 would have t as 0, 0.5, and 1).

Here is a script (not yours) that uses interpolation with the mouse position:

using UnityEngine;
using System.Collections.Generic;
...
int resolution;
Vector2 mouse, lastMouse;

List<Vector2> points = new List<Vector2>();
void Update()
{
   mouse = Input.mousePosition;
   for (int i = 0; i < resolution; i++)
   {
      points.Add(Vector2.Lerp(lastMouse, mouse, (float)i / (float)resolution));
   }
   lastMouse = Input.mousePosition;
}

Using this script, we interpolate between mouse positions to get an approximate guess of the mouse’s path. You can apply these points in something like this (later in the script):

void Update()
{
   ...
   Draw();
   points.Clear();
}
void Draw()
{
   foreach (Vector2 mousePos in points)
   {
      Apply(mousePos);
   }
}

The people who visit this page may have different systems for doing the ‘Apply()’ step, so I will just give you the foreach loop, and let you apply the Vector2.