<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/pretty_feed.xsl" type="text/xsl"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Mev Lyshkin</title>
      <link>https://www.mevlyshkin.com</link>
      <description>Technical blog from gamedev enthusiast</description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://www.mevlyshkin.com/atom.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Fri, 07 Nov 2025 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Bevy Android Setup</title>
          <pubDate>Fri, 07 Nov 2025 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/notes/bevy-android-setup/</link>
          <guid>https://www.mevlyshkin.com/notes/bevy-android-setup/</guid>
          <description xml:base="https://www.mevlyshkin.com/notes/bevy-android-setup/">Getting bevy game to run on Android</description>
      </item>
      <item>
          <title>Bevy Plugins</title>
          <pubDate>Fri, 07 Nov 2025 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/projects/bevy-plugins/</link>
          <guid>https://www.mevlyshkin.com/projects/bevy-plugins/</guid>
          <description xml:base="https://www.mevlyshkin.com/projects/bevy-plugins/">Various plugin crates for the Bevy Engine.</description>
      </item>
      <item>
          <title>ClapNET</title>
          <pubDate>Fri, 07 Nov 2025 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/projects/clapnet/</link>
          <guid>https://www.mevlyshkin.com/projects/clapnet/</guid>
          <description xml:base="https://www.mevlyshkin.com/projects/clapnet/"> Small library to make creating C# scripts easier.</description>
      </item>
      <item>
          <title>Rpack</title>
          <pubDate>Fri, 07 Nov 2025 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/projects/rpack/</link>
          <guid>https://www.mevlyshkin.com/projects/rpack/</guid>
          <description xml:base="https://www.mevlyshkin.com/projects/rpack/">Create spritesheets in no time! Attempt to build texture atlas packer GUI.</description>
      </item>
      <item>
          <title>Rusty Unity Hub</title>
          <pubDate>Fri, 07 Nov 2025 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/projects/rusty-unity-hub/</link>
          <guid>https://www.mevlyshkin.com/projects/rusty-unity-hub/</guid>
          <description xml:base="https://www.mevlyshkin.com/projects/rusty-unity-hub/">Very simple alternative for Unity Hub.</description>
      </item>
      <item>
          <title>Trim Those Packages</title>
          <pubDate>Thu, 02 Oct 2025 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/blog/trim-those-packages/</link>
          <guid>https://www.mevlyshkin.com/blog/trim-those-packages/</guid>
          <description xml:base="https://www.mevlyshkin.com/blog/trim-those-packages/">Basic rules for keeping your Rust packages in shape</description>
      </item>
      <item>
          <title>Touch input on Android</title>
          <pubDate>Tue, 05 Aug 2025 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/notes/bevy-android-input/</link>
          <guid>https://www.mevlyshkin.com/notes/bevy-android-input/</guid>
          <description xml:base="https://www.mevlyshkin.com/notes/bevy-android-input/"><h2 id="update">Update</h2>
<p>Simple fix- use updated android activity.</p>
<p>To do so, specify the <code>android_activity</code> dependency in your <code>Cargo.toml</code> file:</p>
<pre data-lang="toml" style="background-color:#2b303b;color:#c0c5ce;" class="language-toml "><code class="language-toml" data-lang="toml"><span>[patch.crates-io]
</span><span style="color:#bf616a;">android-activity </span><span>= { </span><span style="color:#bf616a;">git </span><span>= &quot;</span><span style="color:#a3be8c;">https://github.com/damus-io/android-activity</span><span>&quot;, </span><span style="color:#bf616a;">rev </span><span>= &quot;</span><span style="color:#a3be8c;">a8948332c7c551303d32eb26a59d0abd676e47a5</span><span>&quot; }
</span></code></pre>
<p>In <code>app/build.gradle</code> update the <code>android-activity</code> dependency from:</p>
<p><code>implementation libs.games.activity</code></p>
<p>to:</p>
<p><code>implementation "androidx.games:games-activity:4.0.0"</code></p>
<p>That's it! All of the stuff below is my previous fix that is not required now.</p>
<h2 id="content">Content</h2>
<p>My game didn't received any touch input on Android, even though it was working just fine on desktop and iOS. But I was able to write a plugin that detects touch input in GameActivity and sends it to Bevy.</p>
<h2 id="using-in-bevy">Using in Bevy</h2>
<p>Add plugin:</p>
<pre data-lang="rust" style="background-color:#2b303b;color:#c0c5ce;" class="language-rust "><code class="language-rust" data-lang="rust"><span>...
</span><span>app.</span><span style="color:#96b5b4;">add_plugins</span><span>(bevy_android_input::AndroidInputPlugin);
</span><span>...
</span></code></pre>
<p>Also, it will require adding android depencency and entry binding that will be used by GameActivity.</p>
<p>Android only dependency:</p>
<pre data-lang="toml" style="background-color:#2b303b;color:#c0c5ce;" class="language-toml "><code class="language-toml" data-lang="toml"><span>[target.&#39;</span><span style="color:#a3be8c;">cfg(target_os = &quot;android&quot;)</span><span>&#39;.dependencies]
</span><span style="color:#bf616a;">jni </span><span>= &quot;</span><span style="color:#a3be8c;">0.21</span><span>&quot;
</span></code></pre>
<p>Function that will be used by java <code>GameActivity</code>:</p>
<pre data-lang="rust" style="background-color:#2b303b;color:#c0c5ce;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#[</span><span style="color:#bf616a;">cfg</span><span>(target_os = &quot;</span><span style="color:#a3be8c;">android</span><span>&quot;)]
</span><span>#[</span><span style="color:#bf616a;">no_mangle</span><span>]
</span><span style="color:#b48ead;">pub extern </span><span>&quot;</span><span style="color:#a3be8c;">system</span><span>&quot; </span><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">Java_org_mycompany_idlerpg_MainActivity_setTouchState</span><span>(
</span><span>    </span><span style="color:#bf616a;">_env</span><span>: jni::sys::JNIEnv,
</span><span>    </span><span style="color:#bf616a;">_class</span><span>: jni::objects::JClass,
</span><span>    </span><span style="color:#bf616a;">id</span><span>: jni::sys::jint,
</span><span>    </span><span style="color:#bf616a;">x</span><span>: jni::sys::jfloat,
</span><span>    </span><span style="color:#bf616a;">y</span><span>: jni::sys::jfloat,
</span><span>    </span><span style="color:#bf616a;">state</span><span>: jni::sys::jint,
</span><span>) {
</span><span>    bevy_android_input::queue_input_update(id as </span><span style="color:#b48ead;">i32</span><span>, x as </span><span style="color:#b48ead;">f32</span><span>, y as </span><span style="color:#b48ead;">f32</span><span>, state as </span><span style="color:#b48ead;">i32</span><span>);
</span><span>}
</span></code></pre>
<p>Notice name of the function, it should follow the pattern: <code>Java_</code> + ID of the application with _ instead of dots + <code>_</code> + activity class + <code>_</code> + method name. So, since I have <code>org.mycompany.idlerpg</code> id with <code>MainActivity</code> class and <code>setTouchState</code> method, it should be named <code>Java_org_mycompany_idlerpg_MainActivity_setTouchState</code>.</p>
<p>In the java <code>MainActivity</code> class, you need to add the following code:</p>
<pre data-lang="java" style="background-color:#2b303b;color:#c0c5ce;" class="language-java "><code class="language-java" data-lang="java"><span style="color:#b48ead;">public class </span><span style="color:#ebcb8b;">MainActivity </span><span style="color:#b48ead;">extends </span><span style="color:#a3be8c;">GameActivity </span><span style="color:#eff1f5;">{
</span><span style="color:#eff1f5;">...
</span><span style="color:#eff1f5;">    </span><span style="color:#b48ead;">public native void </span><span style="color:#8fa1b3;">setTouchState</span><span style="color:#eff1f5;">(</span><span style="color:#b48ead;">int </span><span style="color:#bf616a;">id</span><span style="color:#eff1f5;">, </span><span style="color:#b48ead;">float </span><span style="color:#bf616a;">x</span><span style="color:#eff1f5;">, </span><span style="color:#b48ead;">float </span><span style="color:#bf616a;">y</span><span style="color:#eff1f5;">, </span><span style="color:#b48ead;">int </span><span style="color:#bf616a;">state</span><span style="color:#eff1f5;">);
</span><span style="color:#eff1f5;">
</span><span style="color:#eff1f5;">    @</span><span style="color:#bf616a;">Override
</span><span style="color:#eff1f5;">    </span><span style="color:#b48ead;">public boolean </span><span style="color:#8fa1b3;">onTouchEvent</span><span style="color:#eff1f5;">(</span><span style="color:#ebcb8b;">MotionEvent </span><span style="color:#bf616a;">event</span><span style="color:#eff1f5;">) {
</span><span style="color:#eff1f5;">        </span><span style="color:#b48ead;">int</span><span style="color:#eff1f5;"> action </span><span>=</span><span style="color:#eff1f5;"> event.</span><span style="color:#bf616a;">getActionMasked</span><span style="color:#eff1f5;">();
</span><span style="color:#eff1f5;">        </span><span style="color:#b48ead;">int</span><span style="color:#eff1f5;"> pointerIndex </span><span>=</span><span style="color:#eff1f5;"> event.</span><span style="color:#bf616a;">getActionIndex</span><span style="color:#eff1f5;">();
</span><span style="color:#eff1f5;">        </span><span style="color:#b48ead;">int</span><span style="color:#eff1f5;"> pointerId </span><span>=</span><span style="color:#eff1f5;"> event.</span><span style="color:#bf616a;">getPointerId</span><span style="color:#eff1f5;">(pointerIndex);
</span><span style="color:#eff1f5;">        </span><span style="color:#65737e;">// Yes, you can detect touch events here
</span><span style="color:#eff1f5;">        </span><span style="color:#b48ead;">float</span><span style="color:#eff1f5;"> x </span><span>=</span><span style="color:#eff1f5;"> event.</span><span style="color:#bf616a;">getX</span><span style="color:#eff1f5;">();
</span><span style="color:#eff1f5;">        </span><span style="color:#b48ead;">float</span><span style="color:#eff1f5;"> y </span><span>=</span><span style="color:#eff1f5;"> event.</span><span style="color:#bf616a;">getY</span><span style="color:#eff1f5;">();
</span><span style="color:#eff1f5;">
</span><span style="color:#eff1f5;">        </span><span style="color:#b48ead;">switch </span><span style="color:#eff1f5;">(action) {
</span><span style="color:#eff1f5;">            </span><span style="color:#b48ead;">case </span><span style="color:#ebcb8b;">MotionEvent</span><span style="color:#eff1f5;">.</span><span style="color:#d08770;">ACTION_DOWN</span><span>:
</span><span style="color:#eff1f5;">            </span><span style="color:#b48ead;">case </span><span style="color:#ebcb8b;">MotionEvent</span><span style="color:#eff1f5;">.</span><span style="color:#d08770;">ACTION_POINTER_DOWN</span><span>:
</span><span style="color:#eff1f5;">                </span><span style="color:#bf616a;">setTouchState</span><span style="color:#eff1f5;">(pointerId, x,y, </span><span style="color:#d08770;">0</span><span style="color:#eff1f5;">);
</span><span style="color:#eff1f5;">                </span><span style="color:#b48ead;">break</span><span style="color:#eff1f5;">;
</span><span style="color:#eff1f5;">            </span><span style="color:#b48ead;">case </span><span style="color:#ebcb8b;">MotionEvent</span><span style="color:#eff1f5;">.</span><span style="color:#d08770;">ACTION_MOVE</span><span>:
</span><span style="color:#eff1f5;">                </span><span style="color:#b48ead;">for </span><span style="color:#eff1f5;">(</span><span style="color:#b48ead;">int</span><span style="color:#eff1f5;"> i </span><span>= </span><span style="color:#d08770;">0</span><span style="color:#eff1f5;">; i </span><span>&lt;</span><span style="color:#eff1f5;"> event.</span><span style="color:#bf616a;">getPointerCount</span><span style="color:#eff1f5;">(); i</span><span>++</span><span style="color:#eff1f5;">) {
</span><span style="color:#eff1f5;">                    </span><span style="color:#b48ead;">int</span><span style="color:#eff1f5;"> id </span><span>=</span><span style="color:#eff1f5;"> event.</span><span style="color:#bf616a;">getPointerId</span><span style="color:#eff1f5;">(i);
</span><span style="color:#eff1f5;">                    </span><span style="color:#b48ead;">float</span><span style="color:#eff1f5;"> px </span><span>=</span><span style="color:#eff1f5;"> event.</span><span style="color:#bf616a;">getX</span><span style="color:#eff1f5;">(i);
</span><span style="color:#eff1f5;">                    </span><span style="color:#b48ead;">float</span><span style="color:#eff1f5;"> py </span><span>=</span><span style="color:#eff1f5;"> event.</span><span style="color:#bf616a;">getY</span><span style="color:#eff1f5;">(i);
</span><span style="color:#eff1f5;">                    </span><span style="color:#bf616a;">setTouchState</span><span style="color:#eff1f5;">(id, px,py, </span><span style="color:#d08770;">1</span><span style="color:#eff1f5;">);
</span><span style="color:#eff1f5;">                }
</span><span style="color:#eff1f5;">                </span><span style="color:#b48ead;">break</span><span style="color:#eff1f5;">;
</span><span style="color:#eff1f5;">            </span><span style="color:#b48ead;">case </span><span style="color:#ebcb8b;">MotionEvent</span><span style="color:#eff1f5;">.</span><span style="color:#d08770;">ACTION_UP</span><span>:
</span><span style="color:#eff1f5;">            </span><span style="color:#b48ead;">case </span><span style="color:#ebcb8b;">MotionEvent</span><span style="color:#eff1f5;">.</span><span style="color:#d08770;">ACTION_POINTER_UP</span><span>:
</span><span style="color:#eff1f5;">                </span><span style="color:#bf616a;">setTouchState</span><span style="color:#eff1f5;">(pointerId, x,y, </span><span style="color:#d08770;">2</span><span style="color:#eff1f5;">);
</span><span style="color:#eff1f5;">                </span><span style="color:#b48ead;">break</span><span style="color:#eff1f5;">;
</span><span style="color:#eff1f5;">        }
</span><span style="color:#eff1f5;">
</span><span style="color:#eff1f5;">        </span><span style="color:#b48ead;">return </span><span style="color:#bf616a;">super</span><span style="color:#eff1f5;">.</span><span style="color:#bf616a;">onTouchEvent</span><span style="color:#eff1f5;">(event);
</span><span style="color:#eff1f5;">    }
</span><span style="color:#eff1f5;">...
</span><span style="color:#eff1f5;">
</span><span style="color:#eff1f5;">}
</span></code></pre>
<h2 id="code">Code</h2>
<p>In this example there are external dependencies besides Bevy, here is the <code>Cargo.toml</code> file of the plugin:</p>
<pre data-lang="toml" style="background-color:#2b303b;color:#c0c5ce;" class="language-toml "><code class="language-toml" data-lang="toml"><span>[package]
</span><span style="color:#bf616a;">name </span><span>= &quot;</span><span style="color:#a3be8c;">bevy_android_input</span><span>&quot;
</span><span style="color:#bf616a;">version </span><span>= &quot;</span><span style="color:#a3be8c;">0.1.0</span><span>&quot;
</span><span style="color:#bf616a;">edition </span><span>= &quot;</span><span style="color:#a3be8c;">2021</span><span>&quot;
</span><span style="color:#bf616a;">authors </span><span>= [&quot;</span><span style="color:#a3be8c;">Piotr Siuszko &lt;siuszko@zoho.com&gt;</span><span>&quot;]
</span><span style="color:#bf616a;">license </span><span>= &quot;</span><span style="color:#a3be8c;">MIT OR Apache-2.0</span><span>&quot;
</span><span style="color:#bf616a;">keywords </span><span>= [&quot;</span><span style="color:#a3be8c;">bevy</span><span>&quot;, &quot;</span><span style="color:#a3be8c;">android</span><span>&quot;, &quot;</span><span style="color:#a3be8c;">plugin</span><span>&quot;]
</span><span>
</span><span>[dependencies]
</span><span style="color:#bf616a;">once_cell </span><span>= &quot;</span><span style="color:#a3be8c;">1</span><span>&quot;
</span><span style="color:#bf616a;">crossbeam </span><span>= &quot;</span><span style="color:#a3be8c;">0.8</span><span>&quot;
</span><span style="color:#bf616a;">bevy </span><span>= {</span><span style="color:#bf616a;">version </span><span>= &quot;</span><span style="color:#a3be8c;">0.16</span><span>&quot;, </span><span style="color:#bf616a;">default_features </span><span>= </span><span style="color:#d08770;">false</span><span>, </span><span style="color:#bf616a;">features </span><span>= [
</span><span>&quot;</span><span style="color:#a3be8c;">bevy_ui</span><span>&quot;,&quot;</span><span style="color:#a3be8c;">bevy_picking</span><span>&quot;,&quot;</span><span style="color:#a3be8c;">bevy_window</span><span>&quot;]}
</span><span style="color:#bf616a;">bevy_reflect </span><span>= &quot;</span><span style="color:#a3be8c;">0.16</span><span>&quot;
</span></code></pre>
<p>Plugin <code>src/lib.rs</code></p>
<pre data-lang="rust" style="background-color:#2b303b;color:#c0c5ce;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#65737e;">//! Android input handling plugin for Bevy applications.
</span><span style="color:#65737e;">//!
</span><span style="color:#65737e;">//! This module provides functionality to bridge Android touch input events
</span><span style="color:#65737e;">//! with Bevy&#39;s input system, handling coordinate transformations and camera
</span><span style="color:#65737e;">//! viewport adjustments.
</span><span>
</span><span style="color:#b48ead;">use </span><span>bevy::{
</span><span>    app::{First, Plugin, Startup, Update},
</span><span>    ecs::{
</span><span>        entity::{ContainsEntity, Entity},
</span><span>        event::EventWriter,
</span><span>        query::{Changed, With},
</span><span>        resource::Resource,
</span><span>        schedule::IntoScheduleConfigs,
</span><span>        system::{Query, Res, ResMut, Single},
</span><span>    },
</span><span>    input::touch::{TouchInput, TouchPhase},
</span><span>    math::Vec2,
</span><span>    prelude::ReflectResource,
</span><span>    reflect::Reflect,
</span><span>    render::camera::{Camera, NormalizedRenderTarget},
</span><span>    ui::IsDefaultUiCamera,
</span><span>    window::PrimaryWindow,
</span><span>};
</span><span style="color:#b48ead;">use </span><span>crossbeam::queue::SegQueue;
</span><span style="color:#b48ead;">use </span><span>once_cell::sync::Lazy;
</span><span>
</span><span style="color:#65737e;">/// Thread-safe queue for storing Android touch input events.
</span><span style="color:#65737e;">/// This allows the Android native layer to push events that will be
</span><span style="color:#65737e;">/// processed by the Bevy systems.
</span><span style="color:#b48ead;">static </span><span style="color:#d08770;">INPUT_QUEUE</span><span>: Lazy&lt;SegQueue&lt;AndroidTouchData&gt;&gt; = Lazy::new(|| SegQueue::new());
</span><span>
</span><span style="color:#65737e;">/// Bevy plugin that integrates Android touch input handling.
</span><span style="color:#65737e;">///
</span><span style="color:#65737e;">/// This plugin sets up the necessary systems to:
</span><span style="color:#65737e;">/// - Track camera information for coordinate transformations
</span><span style="color:#65737e;">/// - Process queued Android touch events
</span><span style="color:#65737e;">/// - Convert them to Bevy input events
</span><span style="color:#b48ead;">pub struct </span><span>AndroidInputPlugin;
</span><span>
</span><span style="color:#b48ead;">impl </span><span>Plugin </span><span style="color:#b48ead;">for </span><span>AndroidInputPlugin {
</span><span>    </span><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">build</span><span>(&amp;</span><span style="color:#bf616a;">self</span><span>, </span><span style="color:#bf616a;">app</span><span>: &amp;</span><span style="color:#b48ead;">mut </span><span>bevy::app::App) {
</span><span>        app.init_resource::&lt;AndroidCameraInformation&gt;()
</span><span>            .register_type::&lt;AndroidTouchData&gt;()
</span><span>            .register_type::&lt;AndroidCameraInformation&gt;()
</span><span>            .</span><span style="color:#96b5b4;">add_systems</span><span>(Startup, AndroidCameraInformation::read_info)
</span><span>            .</span><span style="color:#96b5b4;">add_systems</span><span>(
</span><span>                Update,
</span><span>                AndroidCameraInformation::read_info
</span><span>                    .</span><span style="color:#96b5b4;">run_if</span><span>(AndroidCameraInformation::should_rebuild),
</span><span>            )
</span><span>            .</span><span style="color:#96b5b4;">add_systems</span><span>(
</span><span>                First,
</span><span>                (AndroidCameraInformation::pass_android_input_events)
</span><span>                    .</span><span style="color:#96b5b4;">in_set</span><span>(bevy::ecs::event::EventUpdates),
</span><span>            );
</span><span>    }
</span><span>}
</span><span>
</span><span style="color:#65737e;">/// Raw touch data received from the Android platform.
</span><span style="color:#65737e;">///
</span><span style="color:#65737e;">/// This struct represents a single touch event with its basic properties
</span><span style="color:#65737e;">/// before being converted to Bevy&#39;s touch input format.
</span><span>#[</span><span style="color:#bf616a;">derive</span><span>(Debug, Clone, Reflect, Copy)]
</span><span style="color:#b48ead;">pub struct </span><span>AndroidTouchData {
</span><span>    </span><span style="color:#65737e;">/// Unique identifier for this touch point
</span><span>    </span><span style="color:#bf616a;">id</span><span>: </span><span style="color:#b48ead;">i32</span><span>,
</span><span>    </span><span style="color:#65737e;">/// Current phase of the touch (started, moved, ended, canceled)
</span><span>    </span><span style="color:#bf616a;">phase</span><span>: TouchPhase,
</span><span>    </span><span style="color:#65737e;">/// X coordinate
</span><span>    </span><span style="color:#bf616a;">x</span><span>: </span><span style="color:#b48ead;">f32</span><span>,
</span><span>    </span><span style="color:#65737e;">/// Y coordinate
</span><span>    </span><span style="color:#bf616a;">y</span><span>: </span><span style="color:#b48ead;">f32</span><span>,
</span><span>}
</span><span>
</span><span style="color:#b48ead;">impl </span><span>Default </span><span style="color:#b48ead;">for </span><span>AndroidTouchData {
</span><span>    </span><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">default</span><span>() -&gt; </span><span style="color:#b48ead;">Self </span><span>{
</span><span>        </span><span style="color:#b48ead;">Self </span><span>{
</span><span>            id: </span><span style="color:#d08770;">0</span><span>,
</span><span>            phase: TouchPhase::Canceled,
</span><span>            x: </span><span style="color:#d08770;">0.0</span><span>,
</span><span>            y: </span><span style="color:#d08770;">0.0</span><span>,
</span><span>        }
</span><span>    }
</span><span>}
</span><span>
</span><span style="color:#65737e;">/// Resource containing camera information needed for input coordinate transformation.
</span><span style="color:#65737e;">///
</span><span style="color:#65737e;">/// This resource tracks the camera&#39;s viewport offset and scaling factor
</span><span style="color:#65737e;">/// to properly convert Android screen coordinates to Bevy world coordinates.
</span><span>#[</span><span style="color:#bf616a;">derive</span><span>(Debug, Clone, Copy, Resource, Reflect)]
</span><span>#[</span><span style="color:#bf616a;">reflect</span><span>(Resource)]
</span><span style="color:#b48ead;">pub</span><span>(</span><span style="color:#b48ead;">crate</span><span>) </span><span style="color:#b48ead;">struct </span><span>AndroidCameraInformation {
</span><span>    </span><span style="color:#65737e;">/// Viewport offset from the camera&#39;s physical viewport rectangle
</span><span>    </span><span style="color:#b48ead;">pub </span><span style="color:#bf616a;">offset</span><span>: Vec2,
</span><span>    </span><span style="color:#65737e;">/// Scaling factor for input coordinates based on camera target scaling
</span><span>    </span><span style="color:#b48ead;">pub </span><span style="color:#bf616a;">input_multiplier</span><span>: </span><span style="color:#b48ead;">f32</span><span>,
</span><span>    </span><span style="color:#65737e;">/// Reference to the primary window
</span><span>    </span><span style="color:#b48ead;">pub </span><span style="color:#bf616a;">window</span><span>: Entity,
</span><span>}
</span><span>
</span><span style="color:#b48ead;">impl </span><span>AndroidCameraInformation {
</span><span>    </span><span style="color:#65737e;">/// Determines if camera information should be rebuilt.
</span><span>    </span><span style="color:#b48ead;">pub fn </span><span style="color:#8fa1b3;">should_rebuild</span><span>(</span><span style="color:#bf616a;">camera</span><span>: Query&lt;(), (Changed&lt;Camera&gt;, With&lt;IsDefaultUiCamera&gt;)&gt;) -&gt; </span><span style="color:#b48ead;">bool </span><span>{
</span><span>        camera.</span><span style="color:#96b5b4;">iter</span><span>().</span><span style="color:#96b5b4;">next</span><span>().</span><span style="color:#96b5b4;">is_some</span><span>()
</span><span>    }
</span><span>
</span><span>    </span><span style="color:#65737e;">/// Reads and updates camera information from the current camera and window state.
</span><span>    </span><span style="color:#b48ead;">pub fn </span><span style="color:#8fa1b3;">read_info</span><span>(
</span><span>        </span><span style="color:#b48ead;">mut </span><span style="color:#bf616a;">res</span><span>: ResMut&lt;</span><span style="color:#b48ead;">Self</span><span>&gt;,
</span><span>        </span><span style="color:#bf616a;">camera</span><span>: Option&lt;Single&lt;&amp;Camera, With&lt;IsDefaultUiCamera&gt;&gt;&gt;,
</span><span>        </span><span style="color:#bf616a;">window</span><span>: Option&lt;Single&lt;Entity, With&lt;PrimaryWindow&gt;&gt;&gt;,
</span><span>    ) {
</span><span>        </span><span style="color:#b48ead;">let </span><span>Some(camera) = camera </span><span style="color:#b48ead;">else </span><span>{
</span><span>            </span><span style="color:#b48ead;">return</span><span>;
</span><span>        };
</span><span>        </span><span style="color:#b48ead;">let </span><span>Some(window) = window </span><span style="color:#b48ead;">else </span><span>{
</span><span>            </span><span style="color:#b48ead;">return</span><span>;
</span><span>        };
</span><span>        </span><span style="color:#b48ead;">let </span><span>Some(NormalizedRenderTarget::Window(window_ref)) =
</span><span>            camera.target.</span><span style="color:#96b5b4;">normalize</span><span>(Some(*window))
</span><span>        </span><span style="color:#b48ead;">else </span><span>{
</span><span>            res.offset = Vec2::</span><span style="color:#d08770;">ZERO</span><span>;
</span><span>            res.input_multiplier = </span><span style="color:#d08770;">1.0</span><span>;
</span><span>            res.window = Entity::</span><span style="color:#d08770;">PLACEHOLDER</span><span>;
</span><span>            </span><span style="color:#b48ead;">return</span><span>;
</span><span>        };
</span><span>        res.window = window_ref.</span><span style="color:#96b5b4;">entity</span><span>();
</span><span>        </span><span style="color:#b48ead;">let</span><span> offset = camera.</span><span style="color:#96b5b4;">physical_viewport_rect</span><span>().</span><span style="color:#96b5b4;">unwrap_or_default</span><span>().min;
</span><span>        res.offset = Vec2::new(offset.x as </span><span style="color:#b48ead;">f32</span><span>, offset.y as </span><span style="color:#b48ead;">f32</span><span>);
</span><span>        res.input_multiplier = camera.</span><span style="color:#96b5b4;">target_scaling_factor</span><span>().</span><span style="color:#96b5b4;">unwrap_or</span><span>(</span><span style="color:#d08770;">1.0</span><span>);
</span><span>    }
</span><span>
</span><span>    </span><span style="color:#65737e;">/// System that processes queued Android input events and converts them to Bevy events.
</span><span>    </span><span style="color:#65737e;">///
</span><span>    </span><span style="color:#65737e;">/// This system runs during the First schedule to ensure input events are available
</span><span>    </span><span style="color:#65737e;">/// for other systems in the same frame. It pops events from the INPUT_QUEUE and
</span><span>    </span><span style="color:#65737e;">/// sends them as both TouchInput and WindowEvent.
</span><span>    </span><span style="color:#b48ead;">pub fn </span><span style="color:#8fa1b3;">pass_android_input_events</span><span>(
</span><span>        </span><span style="color:#bf616a;">info</span><span>: Res&lt;</span><span style="color:#b48ead;">Self</span><span>&gt;,
</span><span>        </span><span style="color:#b48ead;">mut </span><span style="color:#bf616a;">window_writer</span><span>: EventWriter&lt;bevy::window::WindowEvent&gt;,
</span><span>        </span><span style="color:#b48ead;">mut </span><span style="color:#bf616a;">touch_writer</span><span>: EventWriter&lt;TouchInput&gt;,
</span><span>    ) {
</span><span>        </span><span style="color:#b48ead;">while let </span><span>Some(event) = </span><span style="color:#d08770;">INPUT_QUEUE</span><span>.</span><span style="color:#96b5b4;">pop</span><span>() {
</span><span>            </span><span style="color:#b48ead;">let</span><span> touch_input = info.</span><span style="color:#96b5b4;">to_bevy_input</span><span>(event);
</span><span>            touch_writer.</span><span style="color:#96b5b4;">write</span><span>(touch_input);
</span><span>            window_writer.</span><span style="color:#96b5b4;">write</span><span>(touch_input.</span><span style="color:#96b5b4;">into</span><span>());
</span><span>        }
</span><span>    }
</span><span>
</span><span>    </span><span style="color:#65737e;">/// Converts raw Android touch data into a Bevy TouchInput event.
</span><span>    </span><span style="color:#b48ead;">pub</span><span>(</span><span style="color:#b48ead;">crate</span><span>) </span><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">to_bevy_input</span><span>(&amp;</span><span style="color:#bf616a;">self</span><span>, </span><span style="color:#bf616a;">touch</span><span>: AndroidTouchData) -&gt; TouchInput {
</span><span>        TouchInput {
</span><span>            window: </span><span style="color:#bf616a;">self</span><span>.window.</span><span style="color:#96b5b4;">clone</span><span>(),
</span><span>            phase: touch.phase,
</span><span>            position: </span><span style="color:#bf616a;">self</span><span>.</span><span style="color:#96b5b4;">adjust_pos</span><span>(touch.x, touch.y),
</span><span>            id: touch.id as </span><span style="color:#b48ead;">u64</span><span>,
</span><span>            force: None,
</span><span>        }
</span><span>    }
</span><span>
</span><span>    </span><span style="color:#65737e;">/// Adjusts raw screen coordinates to account for camera viewport and scaling.
</span><span>    #[</span><span style="color:#bf616a;">inline</span><span>]
</span><span>    </span><span style="color:#b48ead;">pub fn </span><span style="color:#8fa1b3;">adjust_pos</span><span>(&amp;</span><span style="color:#bf616a;">self</span><span>, </span><span style="color:#bf616a;">x</span><span>: </span><span style="color:#b48ead;">f32</span><span>, </span><span style="color:#bf616a;">y</span><span>: </span><span style="color:#b48ead;">f32</span><span>) -&gt; Vec2 {
</span><span>        Vec2::new(x, y) / </span><span style="color:#bf616a;">self</span><span>.input_multiplier + </span><span style="color:#bf616a;">self</span><span>.offset
</span><span>    }
</span><span>}
</span><span>
</span><span style="color:#b48ead;">impl </span><span>Default </span><span style="color:#b48ead;">for </span><span>AndroidCameraInformation {
</span><span>    </span><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">default</span><span>() -&gt; </span><span style="color:#b48ead;">Self </span><span>{
</span><span>        </span><span style="color:#b48ead;">Self </span><span>{
</span><span>            offset: Vec2::</span><span style="color:#d08770;">ZERO</span><span>,
</span><span>            input_multiplier: </span><span style="color:#d08770;">1.0</span><span>,
</span><span>            window: Entity::</span><span style="color:#d08770;">PLACEHOLDER</span><span>,
</span><span>        }
</span><span>    }
</span><span>}
</span><span>
</span><span style="color:#65737e;">/// Entry point for Android native code to send touch input events.
</span><span style="color:#65737e;">///
</span><span style="color:#65737e;">/// This function is called from the Android native layer (typically JNI)
</span><span style="color:#65737e;">/// to queue touch events for processing by the Bevy input system.
</span><span style="color:#65737e;">///
</span><span style="color:#65737e;">/// # Arguments
</span><span style="color:#65737e;">/// * `id` - Unique identifier for the touch point
</span><span style="color:#65737e;">/// * `x` - X coordinate in screen space
</span><span style="color:#65737e;">/// * `y` - Y coordinate in screen space
</span><span style="color:#65737e;">/// * `state` - Touch state (0=Started, 1=Moved, 2=Ended, other=Canceled)
</span><span style="color:#b48ead;">pub fn </span><span style="color:#8fa1b3;">queue_input_update</span><span>(</span><span style="color:#bf616a;">id</span><span>: </span><span style="color:#b48ead;">i32</span><span>, </span><span style="color:#bf616a;">x</span><span>: </span><span style="color:#b48ead;">f32</span><span>, </span><span style="color:#bf616a;">y</span><span>: </span><span style="color:#b48ead;">f32</span><span>, </span><span style="color:#bf616a;">state</span><span>: </span><span style="color:#b48ead;">i32</span><span>) {
</span><span>    </span><span style="color:#b48ead;">let</span><span> phase = </span><span style="color:#b48ead;">match</span><span> state {
</span><span>        </span><span style="color:#d08770;">0 </span><span>=&gt; TouchPhase::Started,
</span><span>        </span><span style="color:#d08770;">1 </span><span>=&gt; TouchPhase::Moved,
</span><span>        </span><span style="color:#d08770;">2 </span><span>=&gt; TouchPhase::Ended,
</span><span>        _ =&gt; TouchPhase::Canceled,
</span><span>    };
</span><span>    </span><span style="color:#d08770;">INPUT_QUEUE</span><span>.</span><span style="color:#96b5b4;">push</span><span>(AndroidTouchData { phase, id, x, y });
</span><span>}
</span></code></pre>
</description>
      </item>
      <item>
          <title>Joystick area node</title>
          <pubDate>Fri, 18 Apr 2025 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/notes/bevy-joystick-node/</link>
          <guid>https://www.mevlyshkin.com/notes/bevy-joystick-node/</guid>
          <description xml:base="https://www.mevlyshkin.com/notes/bevy-joystick-node/"><h2 id="concept">Concept</h2>
<p>In mobile games it is often the case that you can start input on screen and have virtual joystick to control the character in game. This is a implmentation of that.</p>
<h2 id="using-in-bevy">Using in Bevy</h2>
<p>Code with plugin is in the next chapter, here is the example guide.</p>
<p>Add plugin first:</p>
<pre data-lang="rust" style="background-color:#2b303b;color:#c0c5ce;" class="language-rust "><code class="language-rust" data-lang="rust"><span>...
</span><span>app.</span><span style="color:#96b5b4;">add_plugins</span><span>(JoystickNodePlugin);
</span><span>...
</span></code></pre>
<p>Spawn node with <code>JoystickNode</code> component:</p>
<pre data-lang="rust" style="background-color:#2b303b;color:#c0c5ce;" class="language-rust "><code class="language-rust" data-lang="rust"><span>...
</span><span>    commands
</span><span>        .</span><span style="color:#96b5b4;">spawn</span><span>((
</span><span>            Node {
</span><span>                width: Val::Percent(</span><span style="color:#d08770;">100.</span><span>),
</span><span>                height: Val::Percent(</span><span style="color:#d08770;">100.</span><span>),
</span><span>                position_type: PositionType::Absolute,
</span><span>                ..</span><span style="color:#96b5b4;">default</span><span>()
</span><span>            },
</span><span>            JoystickNodeArea::default(),
</span><span>        ))
</span><span>        .</span><span style="color:#96b5b4;">insert</span><span>(Name::new(&quot;</span><span style="color:#a3be8c;">JoystickRoot</span><span>&quot;));
</span><span>...
</span></code></pre>
<p>Then it should be possible to access input from <code>Update</code> system:</p>
<pre data-lang="rust" style="background-color:#2b303b;color:#c0c5ce;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">act_on_input</span><span>(</span><span style="color:#bf616a;">joystick</span><span>: SingleJoystick){
</span><span>    </span><span style="color:#b48ead;">if let </span><span>Some(input) = joystick.</span><span style="color:#96b5b4;">get_movement</span><span>() {
</span><span>        info!(&quot;</span><span style="color:#a3be8c;">Input: {}</span><span>&quot;, input);
</span><span>    }
</span><span>}
</span></code></pre>
<h2 id="code">Code</h2>
<p>In this example there are no external dependencies, just Bevy.</p>
<pre data-lang="rust" style="background-color:#2b303b;color:#c0c5ce;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#65737e;">//! A Bevy plugin providing a UI node that behaves like a joystick area,
</span><span style="color:#65737e;">//! allowing relative cursor movement tracking for game or UI input purposes.
</span><span style="color:#b48ead;">use </span><span>bevy::{ecs::system::SystemParam, picking::pointer::PointerId, prelude::*};
</span><span>
</span><span style="color:#65737e;">/// A Bevy plugin that registers the `JoystickNodeArea` component and sets up
</span><span style="color:#65737e;">/// the `update_joystick` system for handling joystick interactions.
</span><span style="color:#b48ead;">pub struct </span><span>JoystickNodePlugin;
</span><span>
</span><span style="color:#b48ead;">impl </span><span>Plugin </span><span style="color:#b48ead;">for </span><span>JoystickNodePlugin {
</span><span>    </span><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">build</span><span>(&amp;</span><span style="color:#bf616a;">self</span><span>, </span><span style="color:#bf616a;">app</span><span>: &amp;</span><span style="color:#b48ead;">mut</span><span> App) {
</span><span>        app.register_type::&lt;JoystickNodeArea&gt;()
</span><span>            .register_type::&lt;JoystickNodeInputStart&gt;()
</span><span>            .</span><span style="color:#96b5b4;">add_observer</span><span>(add_joystick_observers);
</span><span>    }
</span><span>}
</span><span>
</span><span style="color:#65737e;">/// SystemParam helper for accessing `JoystickNodeArea`
</span><span>#[</span><span style="color:#bf616a;">derive</span><span>(SystemParam, DerefMut, Deref)]
</span><span style="color:#b48ead;">pub struct </span><span>SingleJoystick&lt;</span><span style="color:#b48ead;">&#39;w</span><span>&gt;(pub Option&lt;Single&lt;</span><span style="color:#b48ead;">&#39;w</span><span>, &amp;</span><span style="color:#b48ead;">&#39;static</span><span> JoystickNodeArea&gt;&gt;);
</span><span>
</span><span style="color:#b48ead;">impl </span><span>SingleJoystick&lt;&#39;_&gt; {
</span><span>    </span><span style="color:#65737e;">/// Calculates the current movement vector from the start position to the current cursor position.
</span><span>    </span><span style="color:#65737e;">///
</span><span>    </span><span style="color:#65737e;">/// The Y-axis is inverted to match screen coordinates.
</span><span>    </span><span style="color:#b48ead;">pub fn </span><span style="color:#8fa1b3;">get_movement</span><span>(&amp;</span><span style="color:#bf616a;">self</span><span>) -&gt; Option&lt;Vec2&gt; {
</span><span>        </span><span style="color:#b48ead;">if let </span><span>Some(Some(axis)) = </span><span style="color:#bf616a;">self</span><span>.</span><span style="color:#96b5b4;">as_ref</span><span>().</span><span style="color:#96b5b4;">map</span><span>(|</span><span style="color:#bf616a;">j</span><span>| j.</span><span style="color:#96b5b4;">get_movement</span><span>()) {
</span><span>            </span><span style="color:#b48ead;">return</span><span> axis.</span><span style="color:#96b5b4;">into</span><span>();
</span><span>        }
</span><span>        None
</span><span>    }
</span><span>
</span><span>    </span><span style="color:#65737e;">/// Returns if Joystick area exists and is currently pressed
</span><span>    </span><span style="color:#b48ead;">pub fn </span><span style="color:#8fa1b3;">is_pressed</span><span>(&amp;</span><span style="color:#bf616a;">self</span><span>) -&gt; Option&lt;</span><span style="color:#b48ead;">bool</span><span>&gt; {
</span><span>        </span><span style="color:#bf616a;">self</span><span>.</span><span style="color:#96b5b4;">as_ref</span><span>().</span><span style="color:#96b5b4;">map</span><span>(|</span><span style="color:#bf616a;">j</span><span>| j.</span><span style="color:#96b5b4;">is_pressed</span><span>())
</span><span>    }
</span><span>}
</span><span>
</span><span style="color:#65737e;">/// A UI component representing a joystick interaction zone.
</span><span style="color:#65737e;">/// Tracks the cursor&#39;s start and current positions when pressed,
</span><span style="color:#65737e;">/// enabling directional movement calculation.
</span><span>#[</span><span style="color:#bf616a;">derive</span><span>(Default, Debug, Reflect, Component)]
</span><span>#[</span><span style="color:#bf616a;">require</span><span>(
</span><span>    bevy::ui::FocusPolicy = bevy::ui::FocusPolicy::Block,
</span><span>    Interaction,
</span><span>    Pickable,
</span><span>    Node
</span><span>)]
</span><span>#[</span><span style="color:#bf616a;">reflect</span><span>(Component)]
</span><span style="color:#b48ead;">pub struct </span><span>JoystickNodeArea {
</span><span>    </span><span style="color:#65737e;">/// The current direction of the cursor, in normalized coordinates.
</span><span>    </span><span style="color:#bf616a;">current_dir</span><span>: Option&lt;Vec2&gt;,
</span><span>}
</span><span>
</span><span style="color:#65737e;">/// Tracks the pointer start position and ID
</span><span>#[</span><span style="color:#bf616a;">derive</span><span>(Default, Debug, Reflect, Component)]
</span><span>#[</span><span style="color:#bf616a;">reflect</span><span>(Component)]
</span><span>#[</span><span style="color:#bf616a;">require</span><span>(JoystickNodeArea)]
</span><span style="color:#b48ead;">pub struct </span><span>JoystickNodeInputStart {
</span><span>    </span><span style="color:#65737e;">/// The position where the cursor initially pressed down, in normalized coordinates.
</span><span>    </span><span style="color:#b48ead;">pub </span><span style="color:#bf616a;">pos</span><span>: Vec2,
</span><span>    </span><span style="color:#65737e;">/// The ID of the pointer that is currently pressing the joystick.
</span><span>    </span><span style="color:#b48ead;">pub </span><span style="color:#bf616a;">id</span><span>: PointerId,
</span><span>}
</span><span>
</span><span style="color:#b48ead;">impl </span><span>From&lt;&amp;Pointer&lt;Pressed&gt;&gt; </span><span style="color:#b48ead;">for </span><span>JoystickNodeInputStart {
</span><span>    </span><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">from</span><span>(</span><span style="color:#bf616a;">pointer</span><span>: &amp;Pointer&lt;Pressed&gt;) -&gt; </span><span style="color:#b48ead;">Self </span><span>{
</span><span>        JoystickNodeInputStart {
</span><span>            pos: pointer.pointer_location.position,
</span><span>            id: pointer.pointer_id,
</span><span>        }
</span><span>    }
</span><span>}
</span><span>
</span><span style="color:#b48ead;">impl </span><span>JoystickNodeArea {
</span><span>    </span><span style="color:#65737e;">/// Calculates the current movement vector from the start position to the current cursor position.
</span><span>    </span><span style="color:#65737e;">///
</span><span>    </span><span style="color:#65737e;">/// The Y-axis is inverted to match screen coordinates.
</span><span>    </span><span style="color:#b48ead;">pub fn </span><span style="color:#8fa1b3;">update_dir</span><span>(&amp;</span><span style="color:#b48ead;">mut </span><span style="color:#bf616a;">self</span><span>, </span><span style="color:#bf616a;">start_data</span><span>: &amp;JoystickNodeInputStart, </span><span style="color:#bf616a;">new_pos</span><span>: Vec2) {
</span><span>        </span><span style="color:#bf616a;">self</span><span>.current_dir =
</span><span>            Some((new_pos - start_data.pos) * Vec2::new(</span><span style="color:#d08770;">1.0</span><span>, -</span><span style="color:#d08770;">1.0</span><span>).</span><span style="color:#96b5b4;">normalize_or_zero</span><span>());
</span><span>    }
</span><span>
</span><span>    </span><span style="color:#65737e;">/// Returns the current direction of the cursor
</span><span>    </span><span style="color:#b48ead;">pub fn </span><span style="color:#8fa1b3;">get_movement</span><span>(&amp;</span><span style="color:#bf616a;">self</span><span>) -&gt; Option&lt;Vec2&gt; {
</span><span>        </span><span style="color:#bf616a;">self</span><span>.current_dir
</span><span>    }
</span><span>
</span><span>    </span><span style="color:#b48ead;">pub fn </span><span style="color:#8fa1b3;">clear</span><span>(&amp;</span><span style="color:#b48ead;">mut </span><span style="color:#bf616a;">self</span><span>) {
</span><span>        </span><span style="color:#bf616a;">self</span><span>.current_dir = None;
</span><span>    }
</span><span>
</span><span>    </span><span style="color:#65737e;">/// Returns if Joystick area is currently pressed
</span><span>    </span><span style="color:#b48ead;">pub fn </span><span style="color:#8fa1b3;">is_pressed</span><span>(&amp;</span><span style="color:#bf616a;">self</span><span>) -&gt; </span><span style="color:#b48ead;">bool </span><span>{
</span><span>        </span><span style="color:#bf616a;">self</span><span>.current_dir.</span><span style="color:#96b5b4;">is_some</span><span>()
</span><span>    }
</span><span>}
</span><span>
</span><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">add_joystick_observers</span><span>(</span><span style="color:#bf616a;">trigger</span><span>: Trigger&lt;OnAdd, JoystickNodeArea&gt;, </span><span style="color:#b48ead;">mut </span><span style="color:#bf616a;">commands</span><span>: Commands) {
</span><span>    commands
</span><span>        .</span><span style="color:#96b5b4;">entity</span><span>(trigger.</span><span style="color:#96b5b4;">target</span><span>())
</span><span>        .</span><span style="color:#96b5b4;">with_child</span><span>(Observer::new(on_remove_start_info).</span><span style="color:#96b5b4;">with_entity</span><span>(trigger.</span><span style="color:#96b5b4;">target</span><span>()))
</span><span>        .</span><span style="color:#96b5b4;">with_child</span><span>(Observer::new(joystick_release).</span><span style="color:#96b5b4;">with_entity</span><span>(trigger.</span><span style="color:#96b5b4;">target</span><span>()))
</span><span>        .</span><span style="color:#96b5b4;">with_child</span><span>(Observer::new(joystick_update).</span><span style="color:#96b5b4;">with_entity</span><span>(trigger.</span><span style="color:#96b5b4;">target</span><span>()))
</span><span>        .</span><span style="color:#96b5b4;">with_child</span><span>(Observer::new(joystick_start).</span><span style="color:#96b5b4;">with_entity</span><span>(trigger.</span><span style="color:#96b5b4;">target</span><span>()));
</span><span>}
</span><span>
</span><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">on_remove_start_info</span><span>(
</span><span>    </span><span style="color:#bf616a;">trigger</span><span>: Trigger&lt;OnRemove, JoystickNodeInputStart&gt;,
</span><span>    </span><span style="color:#b48ead;">mut </span><span style="color:#bf616a;">q</span><span>: Query&lt;&amp;</span><span style="color:#b48ead;">mut</span><span> JoystickNodeArea&gt;,
</span><span>) {
</span><span>    </span><span style="color:#b48ead;">if let </span><span>Ok(</span><span style="color:#b48ead;">mut</span><span> area) = q.</span><span style="color:#96b5b4;">get_mut</span><span>(trigger.</span><span style="color:#96b5b4;">target</span><span>()) {
</span><span>        area.</span><span style="color:#96b5b4;">clear</span><span>();
</span><span>    }
</span><span>}
</span><span>
</span><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">joystick_start</span><span>(
</span><span>    </span><span style="color:#bf616a;">trigger</span><span>: Trigger&lt;Pointer&lt;Pressed&gt;&gt;,
</span><span>    </span><span style="color:#b48ead;">mut </span><span style="color:#bf616a;">commands</span><span>: Commands,
</span><span>    </span><span style="color:#bf616a;">q</span><span>: Query&lt;(), (With&lt;JoystickNodeArea&gt;, Without&lt;JoystickNodeInputStart&gt;)&gt;,
</span><span>) {
</span><span>    </span><span style="color:#b48ead;">if</span><span> q.</span><span style="color:#96b5b4;">get</span><span>(trigger.target).</span><span style="color:#96b5b4;">is_err</span><span>() {
</span><span>        </span><span style="color:#b48ead;">return</span><span>;
</span><span>    }
</span><span>    </span><span style="color:#b48ead;">let</span><span> input_start: JoystickNodeInputStart = trigger.</span><span style="color:#96b5b4;">event</span><span>().</span><span style="color:#96b5b4;">into</span><span>();
</span><span>    commands.</span><span style="color:#96b5b4;">entity</span><span>(trigger.target).</span><span style="color:#96b5b4;">try_insert</span><span>(input_start);
</span><span>}
</span><span>
</span><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">joystick_release</span><span>(
</span><span>    </span><span style="color:#bf616a;">trigger</span><span>: Trigger&lt;Pointer&lt;Released&gt;&gt;,
</span><span>    </span><span style="color:#b48ead;">mut </span><span style="color:#bf616a;">commands</span><span>: Commands,
</span><span>    </span><span style="color:#b48ead;">mut </span><span style="color:#bf616a;">q</span><span>: Query&lt;&amp;JoystickNodeInputStart&gt;,
</span><span>) {
</span><span>    </span><span style="color:#b48ead;">let </span><span>Ok(start_data) = q.</span><span style="color:#96b5b4;">get_mut</span><span>(trigger.target) </span><span style="color:#b48ead;">else </span><span>{
</span><span>        </span><span style="color:#b48ead;">return</span><span>;
</span><span>    };
</span><span>    </span><span style="color:#b48ead;">if</span><span> start_data.id == trigger.</span><span style="color:#96b5b4;">event</span><span>().pointer_id {
</span><span>        commands
</span><span>            .</span><span style="color:#96b5b4;">entity</span><span>(trigger.target)
</span><span>            .try_remove::&lt;JoystickNodeInputStart&gt;();
</span><span>    }
</span><span>}
</span><span>
</span><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">joystick_update</span><span>(
</span><span>    </span><span style="color:#bf616a;">trigger</span><span>: Trigger&lt;Pointer&lt;Move&gt;&gt;,
</span><span>    </span><span style="color:#b48ead;">mut </span><span style="color:#bf616a;">q</span><span>: Query&lt;(&amp;</span><span style="color:#b48ead;">mut</span><span> JoystickNodeArea, &amp;JoystickNodeInputStart)&gt;,
</span><span>) {
</span><span>    </span><span style="color:#b48ead;">let </span><span>Ok((</span><span style="color:#b48ead;">mut</span><span> joystick, start_data)) = q.</span><span style="color:#96b5b4;">get_mut</span><span>(trigger.</span><span style="color:#96b5b4;">target</span><span>()) </span><span style="color:#b48ead;">else </span><span>{
</span><span>        </span><span style="color:#b48ead;">return</span><span>;
</span><span>    };
</span><span>    </span><span style="color:#b48ead;">if</span><span> start_data.id == trigger.pointer_id {
</span><span>        joystick.</span><span style="color:#96b5b4;">update_dir</span><span>(&amp;*start_data, trigger.pointer_location.position);
</span><span>    }
</span><span>}
</span></code></pre>
</description>
      </item>
      <item>
          <title>Gamepad as a cursor system</title>
          <pubDate>Thu, 17 Apr 2025 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/notes/bevy-gamepad-mouse/</link>
          <guid>https://www.mevlyshkin.com/notes/bevy-gamepad-mouse/</guid>
          <description xml:base="https://www.mevlyshkin.com/notes/bevy-gamepad-mouse/">How to use gamepad as a cursor in Bevy</description>
      </item>
      <item>
          <title>Github Actions And Unreal</title>
          <pubDate>Mon, 07 Oct 2024 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/blog/unreal-github-actions/</link>
          <guid>https://www.mevlyshkin.com/blog/unreal-github-actions/</guid>
          <description xml:base="https://www.mevlyshkin.com/blog/unreal-github-actions/">Struggles with connecting two different worlds</description>
      </item>
      <item>
          <title>Bevy &lt;3 Github Actions</title>
          <pubDate>Sat, 08 Apr 2023 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/blog/bevy-github-actions/</link>
          <guid>https://www.mevlyshkin.com/blog/bevy-github-actions/</guid>
          <description xml:base="https://www.mevlyshkin.com/blog/bevy-github-actions/">How to make automatic bevy game builds on GitHub Pages using GitHub Actions</description>
      </item>
      <item>
          <title>lwa_simple_server</title>
          <pubDate>Tue, 10 Jan 2023 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/projects/lwa-simple-server/</link>
          <guid>https://www.mevlyshkin.com/projects/lwa-simple-server/</guid>
          <description xml:base="https://www.mevlyshkin.com/projects/lwa-simple-server/">Simple server made with hosting locally webgl games in mind. 

 It makes testing Unity webgl games easy, even allows connecting with different domains(less CORS issues during tests).</description>
      </item>
      <item>
          <title>The Last Barbarian</title>
          <pubDate>Mon, 07 Nov 2022 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/projects/the-last-barbarian/</link>
          <guid>https://www.mevlyshkin.com/projects/the-last-barbarian/</guid>
          <description xml:base="https://www.mevlyshkin.com/projects/the-last-barbarian/">The Last Barbarian finds himself in an endless dungeon assaulted by wave after wave of dangerous undead. Some of them use their fists, some their blades, and others bow and arrows!</description>
      </item>
      <item>
          <title>blog init</title>
          <pubDate>Tue, 27 Sep 2022 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/blog/good-first-post/</link>
          <guid>https://www.mevlyshkin.com/blog/good-first-post/</guid>
          <description xml:base="https://www.mevlyshkin.com/blog/good-first-post/">Mandatory first blog post about nothing</description>
      </item>
      <item>
          <title>Tight Space</title>
          <pubDate>Sun, 10 Jan 2021 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/projects/tight-space/</link>
          <guid>https://www.mevlyshkin.com/projects/tight-space/</guid>
          <description xml:base="https://www.mevlyshkin.com/projects/tight-space/">Made during internal game jam. The game is about very Small Universe traversing through tight space of the multiverse. You can also help the Small Universe to GATHER SMALL PLANETS to strengthen itself (they give POINTS!) What&#x27;s more, contrary to popular belief the MULTIVERSE IS SHRINKING!</description>
      </item>
      <item>
          <title>Slavic Castles</title>
          <pubDate>Thu, 07 Nov 2019 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/projects/slavic-castles/</link>
          <guid>https://www.mevlyshkin.com/projects/slavic-castles/</guid>
          <description xml:base="https://www.mevlyshkin.com/projects/slavic-castles/">Game strongly inspired by the Arcomage game from Might &amp; Magic series. Written using Rust.</description>
      </item>
      <item>
          <title>Doppler Engine</title>
          <pubDate>Sun, 10 Jan 2016 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/projects/doppler-engine/</link>
          <guid>https://www.mevlyshkin.com/projects/doppler-engine/</guid>
          <description xml:base="https://www.mevlyshkin.com/projects/doppler-engine/">My OpenGL engine. I want to recreate look and feel from PS1 console using more modern tech.</description>
      </item>
      <item>
          <title>i3gs</title>
          <pubDate>Sun, 10 Jan 2016 00:00:00 +0000</pubDate>
          <link>https://www.mevlyshkin.com/projects/i3gs/</link>
          <guid>https://www.mevlyshkin.com/projects/i3gs/</guid>
          <description xml:base="https://www.mevlyshkin.com/projects/i3gs/">My own status made for i3-gaps.</description>
      </item>
    </channel>
</rss>
