Making eyeball look at/follow cursor movement (C#)

3.8k Views Asked by At

I'm stuck here. I want to have the eyes drawn while 'looking' at (the angle of) the cursor. Also, it should be contained within the bigger circle/quadrant (just like an eyeball). Sadly, it just won't draw the eye for me at the right position/angle and at every mouse movement. The only thing it will do is initially draw an ellipse at (0,0), but that's not what I want.

My idea is to calculate the ratio of the triangles with pythagorean theorem. Then apply the right coordinates (With the correct ratio) in the drawEllipse(); method. This should be repeated everytime you move the cursor.

You can check my image for the mathematical reasoning. enter image description here

Here is my code, note that the panel is made in the designer mode which isn't included in this code, but shouldn't be a big deal:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace oogjes2
{
    public partial class Form1 : Form
    {
        public int mousex;
        public int mousey;

        public Form1()
        {
            InitializeComponent();
            panel1.Paint += paintpanel;
            panel1.MouseMove += panel1_MouseMove;     
        }

        //panel1 cover the screen from (0.0) and onwards,
        void panel1_MouseMove(object sender, MouseEventArgs mea)
        {
            int mousex = mea.X;
            int mousey = mea.Y;
        } 

        void paintpanel(object obj, PaintEventArgs pea)
        {
            Pen blackpen = new Pen(Color.Black, 3);

            // the black outer circle which doesnt move
            pea.Graphics.DrawEllipse(blackpen, -125, -125, 250, 250);

            // e = 63. Diagonal distance from (0,0) to starting point drawEllipse
            // factor = multiplication from mea.x and mea.y to the respective ex and ey of the small circle.
            // ey = factor * mousex (mea.X). Same for ex.

            float e = (float)Math.Sqrt(45 * 45 + 45 * 45); //=63
            float factor = (e / (float)Math.Sqrt(mousex * mousex + mousey * mousey));
            int ex = mousex * (int)factor;
            int ey = mousey * (int)factor;

            //  the eye that should be redrawn at every mousemovement
            pea.Graphics.DrawEllipse(blackpen, ex, ey, 50, 50);
            this.Invalidate();
        }
    }
}
2

There are 2 best solutions below

2
On BEST ANSWER

Try this out:

enter image description here

public partial class Form1 : Form
{

    private Timer tmr;
    private int PupilRadius = 20;
    private int EyeBallRadius = 50;
    private int DistanceBetweenEyes = 20;

    public Form1()
    {
        InitializeComponent();
        this.panel1.Paint += panel1_Paint;

        tmr = new Timer();
        tmr.Interval = 100;
        tmr.Tick += tmr_Tick;
        tmr.Start();
    }

    void tmr_Tick(object sender, EventArgs e)
    {
        panel1.Invalidate();
    }

    void panel1_Paint(object sender, PaintEventArgs e)
    {
        Point center = new Point(panel1.ClientSize.Width / 2, panel1.ClientSize.Height / 2);
        Point LeftEyeCenter = new Point(center.X - EyeBallRadius - (DistanceBetweenEyes / 2), center.Y);
        Point RightEyeCenter = new Point(center.X + EyeBallRadius + (DistanceBetweenEyes / 2), center.Y);

        Rectangle rc = new Rectangle(LeftEyeCenter, new Size(1, 1));
        rc.Inflate(EyeBallRadius, EyeBallRadius);
        e.Graphics.DrawEllipse(Pens.Black, rc);

        rc = new Rectangle(RightEyeCenter, new Size(1, 1));
        rc.Inflate(EyeBallRadius, EyeBallRadius);
        e.Graphics.DrawEllipse(Pens.Black, rc);

        Point curPos = panel1.PointToClient(Cursor.Position);
        Double DistanceFromLeftEyeToCursor = getDistance(LeftEyeCenter.X, LeftEyeCenter.Y, curPos.X, curPos.Y);
        Double DistanceFromRightEyeToCursor = getDistance(RightEyeCenter.X, RightEyeCenter.Y, curPos.X, curPos.Y);
        double angleLeft = getAngleInDegrees(LeftEyeCenter.X, LeftEyeCenter.Y, curPos.X, curPos.Y);
        double angleRight = getAngleInDegrees(RightEyeCenter.X, RightEyeCenter.Y, curPos.X, curPos.Y);

        rc = new Rectangle(new Point(Math.Min((int)DistanceFromLeftEyeToCursor, EyeBallRadius - PupilRadius), 0), new Size(1, 1));
        rc.Inflate(PupilRadius, PupilRadius);
        e.Graphics.TranslateTransform(LeftEyeCenter.X, LeftEyeCenter.Y);
        e.Graphics.RotateTransform((float)angleLeft);
        e.Graphics.FillEllipse(Brushes.Blue, rc);

        rc = new Rectangle(new Point(Math.Min((int)DistanceFromRightEyeToCursor, EyeBallRadius - PupilRadius), 0), new Size(1, 1));
        rc.Inflate(PupilRadius, PupilRadius);
        e.Graphics.ResetTransform();
        e.Graphics.TranslateTransform(RightEyeCenter.X, RightEyeCenter.Y);
        e.Graphics.RotateTransform((float)angleRight);
        e.Graphics.FillEllipse(Brushes.Blue, rc);
    }

    private Double getDistance(int Ax, int Ay, int Bx, int By)
    {
        return Math.Sqrt(Math.Pow((Double)Ax - Bx, 2) + Math.Pow((Double)Ay - By, 2));
    }

    private Double getAngleInDegrees(int cx, int cy, int X, int Y)
    {
        // draw a line from the center of the circle
        // to the where the cursor is...
        // If the line points:
        // up = 0 degrees
        // right = 90 degrees
        // down = 180 degrees
        // left = 270 degrees

        Double angle;
        int dy = Y - cy;
        int dx = X - cx;
        if (dx == 0) // Straight up and down | avoid divide by zero error!
        {
            if (dy <= 0)
            {
                angle = 0;
            }
            else
            {
                angle = 180;
            }
        }
        else
        {
            angle = Math.Atan((Double)dy / (Double)dx);
            angle = angle * ((Double)180 / Math.PI);

            if (X <= cx)
            {
                angle = 180 + angle;
            }
        }

        return angle;
    }

}
0
On

If you want to have an eye following the cursor, you'll need to calculate the angle from the eye to the cursor.

You'll need to know just three things:

the position of the eye, the position of the mouse, and how far the center of the pupil is from the center of the eye (I'm calling your inner circle the pupil and outer circle the eye). Since the eye never moves (only rotates around it's center) you already know it's position.

void direction_to_cursor(){
  float p = Math.sqrt((45 + r)*(45 + r)*2); // Distance from outer circle center to inner circle center
  // assuming you want the top left corner 63 away from 0, 0
  // r is radius of inner circle
  int x = mouseX - EyeX; // In your picture it looks like your eye is at 0,0
  int y = -(mouseY - EyeY); // inverted y axis (0 is at top)
  float dir = Math.atan2(x, y);
  int px = p * Math.cos(dir);   // x Center of inner circle
  int py = p * Math.cos(dir);   // y Center of inner circle
  px -= r;  // Get left x coordinate of circle
  py -= r;  // get right x coordinate of circle

  pea.Graphics.DrawEllipse(blackpen, px, py, 50, 50);
}

enter image description here

step1: Calculate distance from center of Eye to center of pupil
step2: Calculate x and y difference between the Mouse and Eye
step3: Calculate direction from eye to mouse.
step4: Calculate position of pupil from direction and distance from center of eye

you could use the following

void paintpanel(object obj, PaintEventArgs pea)
{
    Pen blackpen = new Pen(Color.Black, 3);
    pea.Graphics.DrawEllipse(blackpen, -125, -125, 250, 250);

    float p = Math.sqrt(2*70*70); // (45+25)*(45+25)+(45+25)*(45+25)
    float dir = Math.atan(y, x);
    int ex = Math.cos(dir) * p - 25;
    int ey = Math.sin(dir) * p - 25; 

    //  the eye that should be redrawn at every mousemovement
    pea.Graphics.DrawEllipse(blackpen, ex, ey, 50, 50);
    this.Invalidate();
}