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)

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.