0
\$\begingroup\$

I have a game in which I have an eye in a head that should always move towards a piece of food (or another piece of food). The head is moved by the player. The food has a fixed position (for some amount of time).

I have solved the maths somehow, but I do not get the precision I want. As I turn around the food, the eye moves almost perfectly, but it is (1) outside the circle sometimes and (2) not perfectly positioned.

Example (left: food, right: head with eye ball with pupil outside the eye ball):

cropped screenshot

The position of the eye ball is updated on each move of the player.

Screen recording

screen recording

Relevant code in MainWindow.xaml.cs

EyeBall eb = MyEyeBall;

eb.HorizontalAlignment = HorizontalAlignment.Stretch;
eb.VerticalAlignment = VerticalAlignment.Stretch;

c.Content = eb;

Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
{
    Cell foodCell = GetFoodCell();

    // A - eye center
    // B - first food center (TODO : automatically the closest food)
    // A' - pupil center
    // d1 - distance from center of eye to boundary of pupil
    // d3 - pupil radius

    // hard coded values from the proportions in EyeBall.xaml
    double d1 = 6,
        d3 = 9;

    Point A = eb.TransformToAncestor(MyUniformGrid).Transform(new Point(eb.ActualWidth / 2, eb.ActualHeight / 2));

    Point B = foodCell.TransformToAncestor(MyUniformGrid).Transform(
            new Point(foodCell.ActualWidth / 2, foodCell.ActualHeight / 2));

    double sum = (d1 + d3) * (((FrameworkElement)eb.Child).ActualHeight / eb.ActualHeight);
    //double sum = eb.TransformToAncestor(MyUniformGrid).Transform(new Point(d1 + d3, 0)).X;

    double k = sum / (DistDouble(A, B) - sum);

    Point Ap = new Point(((A.X + k * B.X) / (1d + k)),
        (A.Y + k * B.Y) / (1d + k));

    Point Ap2 = MyUniformGrid.TransformToDescendant(eb).Transform(Ap);

    Debug.WriteLine("===============================================");
    Debug.WriteLine($"food cell position = {Board.Food.Pieces[0].Pos}");
    Debug.WriteLine($"d1 = {d1}, d3 = {d3}");
    Debug.WriteLine($"A = {A}, B = {B}");
    Debug.WriteLine($"k = {k}");
    Debug.WriteLine($"A': {Ap}     ->     {Ap2}");
    Debug.WriteLine("===============================================");


    eb.SetBallCenter(Ap2.X, Ap2.Y);
}));

EyeBall.xaml.cs

public partial class EyeBall : Viewbox
{
    public EyeBall()
    {
        InitializeComponent();
    }

    internal void SetBallCenter(double x, double y)
    {
        MyBallGeometry.Center = new Point(x, y);
    }

    internal Point GetBallCenter()
    {
        return MyBallGeometry.Center;
    }
}

EyeBall.xaml

<Viewbox x:Class="wpf_snake.EyeBall"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:wpf_snake"
             mc:Ignorable="d"
             d:DesignHeight="450" d:DesignWidth="800">
    <Canvas Name="svg8" Width="61.809998" Height="60.000003">
        <Canvas.RenderTransform>
            <TranslateTransform X="0" Y="0"/>
        </Canvas.RenderTransform>
        <Canvas Name="layer1">
            <Canvas.RenderTransform>
                <TranslateTransform X="-46.272926" Y="-62.673704"/>
            </Canvas.RenderTransform>
            <Ellipse Canvas.Left="46.3" Width="61.8" Canvas.Top="62.7" Height="60" Name="ellipse971" Fill="#FF8A8EFF" StrokeThickness="0.45579806" StrokeMiterLimit="4" StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round" Opacity="1"/>
            <Ellipse Canvas.Left="52.9" Canvas.Top="68.4" Width="48.5" Height="48.5" Name="ellipse973" Fill="#FFFFFFFF" StrokeThickness="0.49706328" StrokeMiterLimit="4" StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round" Opacity="1"/>
            <Ellipse Canvas.Left="59.9" Canvas.Top="75.4" Width="34.5" Height="34.5" Name="circle979" Fill="#7BA9F2FF" StrokeThickness="0.07413793" StrokeMiterLimit="4" StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round" Opacity="1"/>
        </Canvas>
        <Path Stroke="Transparent" StrokeThickness="0" Fill="Blue" >
            <Path.Data>
                <EllipseGeometry  Center="45,30"  RadiusX="9"  RadiusY="9" x:Name="MyBallGeometry"/>
            </Path.Data>
        </Path>
    </Canvas>
</Viewbox>

Thank you.

\$\endgroup\$
1
\$\begingroup\$

I can't provide you with the code solution, but here's some formulas you might find useful to do it yourself:

Calculate eyeball position:

position = normalize(food_piece - head_center) * head_radius

Vector normalization:

unit_vector = vector / length(vector)

be careful not to divide by zero here, so check length before performing division

Vector length:

length = sqrt(vector.x^2 + vector.y^2)

Alternatively, you may also use atan2(y, x), if available, to get angle between head and food piece. And then convert the angle to the unit vector using vector = (cos(angle), sin(angle)) and then multiply it by the head radius.

atan2(food.y - head.y, food.x - head.x)

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .