@@ -2452,7 +2452,7 @@ <h3 id="_step_2_set_up_the_gameviewmodel">Step 2: Set up the GameViewModel</h3>
24522452< div class ="sect2 ">
24532453< h3 id ="_step_3_add_the_snowflakescontrol "> Step 3: Add the SnowflakesControl</ h3 >
24542454< div class ="paragraph ">
2455- < p > Now it’s time to add the needed < code > Control</ code > to render our game. Therefore, we add a new folder < code > Controls</ code > and inside we add a new class < code > SnowflakesControl.cs</ code > . This class must inherit < code > Control</ code > . In addition, we want to implement the interface < code > ICustomHitTest </ code > in order to control hit-testing on our own. </ p >
2455+ < p > Now it’s time to add the needed < code > Control</ code > to render our game. Therefore, we add a new folder < code > Controls</ code > and inside we add a new class < code > SnowflakesControl.cs</ code > . This class must inherit < code > Control</ code > .</ p >
24562456</ div >
24572457< div class ="paragraph ">
24582458< p > The control needs some < a href ="https://docs.avaloniaui.net/docs/guides/custom-controls/how-to-create-advanced-custom-controls "> [AvaloniaProperties]</ a > to allow us to bind to it.</ p >
@@ -2583,7 +2583,7 @@ <h3 id="_step_3_add_the_snowflakescontrol">Step 3: Add the SnowflakesControl</h3
25832583 < span class ="c1 "> // Request next frame as soon as possible, if the game is running. Remember to reset the stopwatch.</ span >
25842584 < span class ="k "> if</ span > < span class ="p "> (</ span > < span class ="n "> IsRunning</ span > < span class ="p "> )</ span >
25852585 < span class ="p "> {</ span >
2586- < span class ="n "> Dispatcher</ span > < span class ="p "> .</ span > < span class ="n "> UIThread</ span > < span class ="p "> .</ span > < span class ="nf "> Post</ span > < span class ="p "> (</ span > < span class ="n "> InvalidateVisual</ span > < span class ="p "> ,</ span > < span class ="n "> DispatcherPriority</ span > < span class ="p "> .</ span > < span class ="n "> Background </ span > < span class ="p "> );</ span >
2586+ < span class ="n "> Dispatcher</ span > < span class ="p "> .</ span > < span class ="n "> UIThread</ span > < span class ="p "> .</ span > < span class ="nf "> Post</ span > < span class ="p "> (</ span > < span class ="n "> InvalidateVisual</ span > < span class ="p "> ,</ span > < span class ="n "> DispatcherPriority</ span > < span class ="p "> .</ span > < span class ="n "> Render </ span > < span class ="p "> );</ span >
25872587 < span class ="n "> _stopwatch</ span > < span class ="p "> .</ span > < span class ="nf "> Restart</ span > < span class ="p "> ();</ span >
25882588 < span class ="p "> }</ span >
25892589< span class ="p "> }</ span > </ code > </ pre >
@@ -2609,22 +2609,48 @@ <h3 id="_step_3_add_the_snowflakescontrol">Step 3: Add the SnowflakesControl</h3
26092609< p > < span class ="image "> < img src ="_docs/single_render_frame.png " alt ="single render frame "> </ span > </ p >
26102610</ div >
26112611< div class ="paragraph ">
2612- < p > As we wanted to implement < code > ICustomHitTest </ code > , we will add the needed interface members, which is just the following method here: </ p >
2612+ < p > As we want to check if our pointer is over any < code > Snowflake </ code > , we will override the < code > OnPointerEntered </ code > and < code > OnPointerMoved </ code > methods. From the < code > PointerEventArgs </ code > we can read the pointer position and forward it to a method that tests for hits, </ p >
26132613</ div >
26142614< div class ="listingblock ">
26152615< div class ="content ">
2616- < pre class ="rouge highlight "> < code data-lang ="csharp "> < span class ="k "> public</ span > < span class ="kt "> bool</ span > < span class ="nf "> HitTest</ span > < span class ="p "> (</ span > < span class ="n "> Point</ span > < span class ="n "> point</ span > < span class ="p "> )</ span >
2616+ < pre class ="rouge highlight "> < code data-lang ="csharp "> < span class ="c1 "> /// <inheritdoc /></ span >
2617+ < span class ="k "> protected</ span > < span class ="k "> override</ span > < span class ="k "> void</ span > < span class ="nf "> OnPointerEntered</ span > < span class ="p "> (</ span > < span class ="n "> PointerEventArgs</ span > < span class ="n "> e</ span > < span class ="p "> )</ span >
26172618< span class ="p "> {</ span >
2618- < span class ="k "> if</ span > < span class ="p "> (!</ span > < span class ="n "> IsRunning</ span > < span class ="p "> )</ span > < span class ="k "> return</ span > < span class ="k "> false</ span > < span class ="p "> ;</ span >
2619+ < span class ="nf "> HitTestSnowFlakes</ span > < span class ="p "> (</ span > < span class ="n "> e</ span > < span class ="p "> .</ span > < span class ="nf "> GetPosition</ span > < span class ="p "> (</ span > < span class ="k "> this</ span > < span class ="p "> ));</ span >
2620+ < span class ="k "> base</ span > < span class ="p "> .</ span > < span class ="nf "> OnPointerEntered</ span > < span class ="p "> (</ span > < span class ="n "> e</ span > < span class ="p "> );</ span >
2621+ < span class ="p "> }</ span >
26192622
2620- < span class ="kt "> var</ span > < span class ="n "> snowFlake</ span > < span class ="p "> =</ span > < span class ="n "> Snowflakes</ span > < span class ="p "> .</ span > < span class ="nf "> FirstOrDefault</ span > < span class ="p "> (</ span > < span class ="n "> x</ span > < span class ="p "> =></ span > < span class ="n "> x</ span > < span class ="p "> .</ span > < span class ="nf "> IsHit</ span > < span class ="p "> (</ span > < span class ="n "> point</ span > < span class ="p "> ,</ span > < span class ="n "> Bounds</ span > < span class ="p "> ));</ span >
2621- < span class ="k "> if</ span > < span class ="p "> (</ span > < span class ="n "> snowFlake</ span > < span class ="p "> !=</ span > < span class ="k "> null</ span > < span class ="p "> )</ span >
2623+ < span class ="c1 "> /// <inheritdoc /></ span >
2624+ < span class ="k "> protected</ span > < span class ="k "> override</ span > < span class ="k "> void</ span > < span class ="nf "> OnPointerMoved</ span > < span class ="p "> (</ span > < span class ="n "> PointerEventArgs</ span > < span class ="n "> e</ span > < span class ="p "> )</ span >
2625+ < span class ="p "> {</ span >
2626+ < span class ="nf "> HitTestSnowFlakes</ span > < span class ="p "> (</ span > < span class ="n "> e</ span > < span class ="p "> .</ span > < span class ="nf "> GetPosition</ span > < span class ="p "> (</ span > < span class ="k "> this</ span > < span class ="p "> ));</ span >
2627+ < span class ="k "> base</ span > < span class ="p "> .</ span > < span class ="nf "> OnPointerMoved</ span > < span class ="p "> (</ span > < span class ="n "> e</ span > < span class ="p "> );</ span >
2628+ < span class ="p "> }</ span >
2629+
2630+ < span class ="c1 "> /// <summary></ span >
2631+ < span class ="c1 "> /// This method will check if the pointer has hit any snowflake. If so, it will remove the snowflake from the list</ span >
2632+ < span class ="c1 "> /// and update the score.</ span >
2633+ < span class ="c1 "> /// </summary></ span >
2634+ < span class ="c1 "> /// <param name="point">the pointer point to test.</param></ span >
2635+ < span class ="k "> private</ span > < span class ="k "> void</ span > < span class ="nf "> HitTestSnowFlakes</ span > < span class ="p "> (</ span > < span class ="n "> Point</ span > < span class ="n "> point</ span > < span class ="p "> )</ span >
2636+ < span class ="p "> {</ span >
2637+ < span class ="c1 "> // if the game is not running, we don't need to do anything.</ span >
2638+ < span class ="k "> if</ span > < span class ="p "> (!</ span > < span class ="n "> IsRunning</ span > < span class ="p "> )</ span > < span class ="k "> return</ span > < span class ="p "> ;</ span >
2639+
2640+ < span class ="c1 "> // loop through all snowflakes and check if the pointer is inside one of them.</ span >
2641+ < span class ="c1 "> // Copy the list to avoid concurrent modification exceptions.</ span >
2642+ < span class ="k "> foreach</ span > < span class ="p "> (</ span > < span class ="kt "> var</ span > < span class ="n "> snowFlake</ span > < span class ="k "> in</ span > < span class ="n "> Snowflakes</ span > < span class ="p "> .</ span > < span class ="nf "> ToArray</ span > < span class ="p "> ())</ span >
26222643 < span class ="p "> {</ span >
2623- < span class ="n "> Snowflakes</ span > < span class ="p "> .</ span > < span class ="nf "> Remove</ span > < span class ="p "> (</ span > < span class ="n "> snowFlake</ span > < span class ="p "> );</ span >
2624- < span class ="n "> Score</ span > < span class ="p "> +=</ span > < span class ="n "> snowFlake</ span > < span class ="p "> .</ span > < span class ="nf "> GetHitScore</ span > < span class ="p "> ();</ span >
2625- < span class ="p "> }</ span >
2644+ < span class ="k "> if</ span > < span class ="p "> (</ span > < span class ="n "> snowFlake</ span > < span class ="p "> .</ span > < span class ="nf "> IsHit</ span > < span class ="p "> (</ span > < span class ="n "> point</ span > < span class ="p "> ,</ span > < span class ="n "> Bounds</ span > < span class ="p "> ))</ span >
2645+ < span class ="p "> {</ span >
2646+ < span class ="n "> Snowflakes</ span > < span class ="p "> .</ span > < span class ="nf "> Remove</ span > < span class ="p "> (</ span > < span class ="n "> snowFlake</ span > < span class ="p "> );</ span >
2647+ < span class ="n "> Score</ span > < span class ="p "> +=</ span > < span class ="n "> snowFlake</ span > < span class ="p "> .</ span > < span class ="nf "> GetHitScore</ span > < span class ="p "> ();</ span >
26262648
2627- < span class ="k "> return</ span > < span class ="n "> snowFlake</ span > < span class ="p "> !=</ span > < span class ="k "> null</ span > < span class ="p "> ;</ span >
2649+ < span class ="c1 "> // Add a text hint about the earned score. We also hand over the containing collection,</ span >
2650+ < span class ="c1 "> // so it can auto-remove itself after 1 second.</ span >
2651+ < span class ="n "> _scoreHintsCollection</ span > < span class ="p "> .</ span > < span class ="nf "> Add</ span > < span class ="p "> (</ span > < span class ="k "> new</ span > < span class ="nf "> ScoreHint</ span > < span class ="p "> (</ span > < span class ="n "> snowFlake</ span > < span class ="p "> ,</ span > < span class ="n "> _scoreHintsCollection</ span > < span class ="p "> ));</ span >
2652+ < span class ="p "> }</ span >
2653+ < span class ="p "> }</ span >
26282654< span class ="p "> }</ span > </ code > </ pre >
26292655</ div >
26302656</ div >
@@ -2634,17 +2660,14 @@ <h3 id="_step_3_add_the_snowflakescontrol">Step 3: Add the SnowflakesControl</h3
26342660< div class ="ulist ">
26352661< ul >
26362662< li >
2637- < p > If the game is not running, the control shouldn’t receive any hit</ p >
2638- </ li >
2639- < li >
2640- < p > If the game is running, search for any snowflake hit by the pointer</ p >
2663+ < p > If the game is running, search for all snowflakes hit by the pointer</ p >
26412664< div class ="ulist ">
26422665< ul >
26432666< li >
2644- < p > If one is found, remove it from the collection</ p >
2667+ < p > If any is found, remove it from the collection</ p >
26452668</ li >
26462669< li >
2647- < p > If one is found, add the score to the total score</ p >
2670+ < p > If any is found, add the score to the total score</ p >
26482671</ li >
26492672</ ul >
26502673</ div >
@@ -3038,7 +3061,7 @@ <h2 id="_related">Related</h2>
30383061</ div >
30393062< div id ="footer ">
30403063< div id ="footer-text ">
3041- Last updated 2025-11-06 19:42:36 UTC
3064+ Last updated 2025-12-25 12:47:29 UTC
30423065</ div >
30433066</ div >
30443067</ body >
0 commit comments