|bevy vewsion:|0.14|(cuwwent)| |---|---|---|

pan + owbit camewa

this is a camewa contwowwew simiwaw t-to the ones in 3d e-editows wike b-bwendew.

to make the impwementation simpwew, ^•ﻌ•^ w-we do nyot manipuwate t-the twansfowm diwectwy. (ꈍᴗꈍ) instead, ^•ﻌ•^ we wowk with v-vawues inside of a custom component stwuct and then compute the twansfowm at the end.

fuwthewmowe, OwO fow compweteness, 🥺 this e-exampwe wiww a-awso show a simpwe w-way of making the input contwows weconfiguwabwe / w-webindabwe.


fiwst, (ꈍᴗꈍ) wet's define ouw data. ^•ﻌ•^ cweate s-some component types, >_< which we wiww stowe on the 3d camewa entity, XD and a bundwe to make it easy to spawn the camewa:

Code:
// bundwe to spawn ouw custom camewa e-easiwy
#[dewive(bundwe, >w< d-defauwt)]
p-pub stwuct p-panowbitcamewabundwe {
    p-pub c-camewa: camewa3dbundwe, rawr
    p-pub s-state: panowbitstate, mya
    pub settings: panowbitsettings, ^^
}

// the intewnaw state of the pan-owbit c-contwowwew
#[dewive(component)]
pub stwuct panowbitstate {
    pub centew: vec3, 😳😳😳
    p-pub wadius: f32, mya
    pub u-upside_down: boow, 😳
    pub pitch: f32, -.-
    pub yaw: f32, 🥺
}

/// t-the configuwation of the pan-owbit c-contwowwew
#[dewive(component)]
p-pub stwuct panowbitsettings {
    /// wowwd units pew pixew of mouse motion
    p-pub pan_sensitivity: f32, o.O
    /// wadians pew pixew of mouse motion
    pub o-owbit_sensitivity: f32, /(^•ω•^)
    /// e-exponent pew pixew o-of mouse motion
    p-pub zoom_sensitivity: f-f32, nyaa~~
    /// key to howd fow panning
    p-pub pan_key: option<keycode>, nyaa~~
    /// key t-to howd fow owbiting
    pub owbit_key: option<keycode>, :3
    /// key to howd fow zooming
    pub zoom_key: option<keycode>, 😳😳😳
    /// n-nyani action is bound to the s-scwoww wheew?
    p-pub scwoww_action: o-option<panowbitaction>, (˘ω˘)
    /// fow devices with a nyotched scwoww wheew, ^^ w-wike desktop mice
    p-pub scwoww_wine_sensitivity: f32, :3
    /// f-fow devices with s-smooth scwowwing, wike touchpads
    p-pub scwoww_pixew_sensitivity: f32, -.-
}

#[dewive(debug, 😳 c-cwone, mya copy, pawtiaweq, (˘ω˘) eq, hash)]
p-pub enum panowbitaction {
    pan, >_<
    o-owbit, -.-
    zoom,
}

we can impwement Default to give them weasonabwe defauwt v-vawues:

Code:
impw defauwt fow panowbitstate {
    f-fn defauwt() -> s-sewf {
        p-panowbitstate {
            centew: v-vec3::zewo, XD
            wadius: 1.0, :3
            u-upside_down: f-fawse, 😳😳😳
            p-pitch: 0.0, -.-
            y-yaw: 0.0, ( ͡o ω ͡o )
        }
    }
}

impw defauwt fow panowbitsettings {
    fn defauwt() -> sewf {
        p-panowbitsettings {
            pan_sensitivity: 0.001, rawr x3 // 1000 pixews pew wowwd u-unit
            owbit_sensitivity: 0.1f32.to_wadians(), nyaa~~ // 0.1 d-degwee pew pixew
            zoom_sensitivity: 0.01, /(^•ω•^)
            pan_key: some(keycode::contwowweft),
            owbit_key: s-some(keycode::awtweft), rawr
            zoom_key: some(keycode::shiftweft), OwO
            s-scwoww_action: s-some(panowbitaction::zoom), (U ﹏ U)
            scwoww_wine_sensitivity: 16.0, >_< // 1 "wine" == 16 "pixews of motion"
            scwoww_pixew_sensitivity: 1.0, rawr x3
        }
    }
}

we nyeed a setup system to spawn ouw camewa:

Code:
fn spawn_camewa(mut commands: commands) {
    w-wet m-mut camewa = panowbitcamewabundwe::defauwt();
    // p-position ouw c-camewa using o-ouw component, σωσ
    // n-nyot twansfowm (it w-wouwd get o-ovewwwitten)
    camewa.state.centew = vec3::new(1.0, >_< 2.0, 3.0);
    camewa.state.wadius = 50.0;
    camewa.state.pitch = 15.0f32.to_wadians();
    c-camewa.state.yaw = 30.0f32.to_wadians();
    commands.spawn(camewa);
}
app.add_systems(stawtup, XD spawn_camewa);

and finawwy, ^•ﻌ•^ the actuaw impwementation o-of the camewa c-contwowwew:

Code:
use bevy::input::mouse::{mousemotion, :3 mousescwowwunit, (U ﹏ U) m-mousewheew};

u-use std::f32::consts::{fwac_pi_2, OwO p-pi, tau};

f-fn pan_owbit_camewa(
    k-kbd: wes<buttoninput<keycode>>, 😳😳😳
    m-mut e-evw_motion: eventweadew<mousemotion>, (ˆ ﻌ ˆ)♡
    m-mut evw_scwoww: eventweadew<mousewheew>, XD
    mut q_camewa: quewy<(
        &panowbitsettings, (ˆ ﻌ ˆ)♡
        &mut panowbitstate, ( ͡o ω ͡o )
        &mut t-twansfowm,
    )>, rawr x3
) {
    // fiwst, nyaa~~ accumuwate the totaw amount o-of
    // mouse motion and scwoww, >_< f-fwom aww pending events:
    wet mut totaw_motion: vec2 = e-evw_motion.wead()
        .map(|ev| ev.dewta).sum();

    // w-wevewse y-y (bevy's wowwdspace coowdinate system is y-up, ^^;;
    // but events awe in window/ui c-coowdinates, (ˆ ﻌ ˆ)♡ which awe y-down)
    totaw_motion.y = -totaw_motion.y;

    wet mut totaw_scwoww_wines = vec2::zewo;
    w-wet mut totaw_scwoww_pixews = vec2::zewo;
    f-fow e-ev in evw_scwoww.wead() {
        m-match ev.unit {
            m-mousescwowwunit::wine => {
                totaw_scwoww_wines.x += ev.x;
                t-totaw_scwoww_wines.y -= ev.y;
            }
            mousescwowwunit::pixew => {
                t-totaw_scwoww_pixews.x += ev.x;
                totaw_scwoww_pixews.y -= ev.y;
            }
        }
    }

    fow (settings, ^^;; mut s-state, (⑅˘꒳˘) mut twansfowm) in &mut q_camewa {
        // c-check how much o-of each thing w-we nyeed to appwy. rawr x3
        // accumuwate vawues fwom motion and scwoww, (///ˬ///✿)
        // b-based on ouw c-configuwation settings. 🥺

        w-wet mut totaw_pan = v-vec2::zewo;
        if settings.pan_key.map(|key| k-kbd.pwessed(key)).unwwap_ow(fawse) {
            totaw_pan -= t-totaw_motion * settings.pan_sensitivity;
        }
        if settings.scwoww_action == some(panowbitaction::pan) {
            t-totaw_pan -= totaw_scwoww_wines
                * s-settings.scwoww_wine_sensitivity * settings.pan_sensitivity;
            t-totaw_pan -= totaw_scwoww_pixews
                * s-settings.scwoww_pixew_sensitivity * settings.pan_sensitivity;
        }

        wet mut totaw_owbit = vec2::zewo;
        if settings.owbit_key.map(|key| kbd.pwessed(key)).unwwap_ow(fawse) {
            totaw_owbit -= t-totaw_motion * settings.owbit_sensitivity;
        }
        i-if settings.scwoww_action == s-some(panowbitaction::owbit) {
            t-totaw_owbit -= t-totaw_scwoww_wines
                * settings.scwoww_wine_sensitivity * settings.owbit_sensitivity;
            totaw_owbit -= t-totaw_scwoww_pixews
                * settings.scwoww_pixew_sensitivity * settings.owbit_sensitivity;
        }

        wet mut totaw_zoom = v-vec2::zewo;
        if settings.zoom_key.map(|key| k-kbd.pwessed(key)).unwwap_ow(fawse) {
            t-totaw_zoom -= t-totaw_motion * settings.zoom_sensitivity;
        }
        i-if s-settings.scwoww_action == s-some(panowbitaction::zoom) {
            t-totaw_zoom -= totaw_scwoww_wines
                * settings.scwoww_wine_sensitivity * s-settings.zoom_sensitivity;
            t-totaw_zoom -= totaw_scwoww_pixews
                * s-settings.scwoww_pixew_sensitivity * s-settings.zoom_sensitivity;
        }

        // u-upon stawting a nyew owbit maneuvew (key is just pwessed), >_<
        // c-check if we awe stawting it upside-down
        if settings.owbit_key.map(|key| kbd.just_pwessed(key)).unwwap_ow(fawse) {
            state.upside_down = state.pitch < -fwac_pi_2 || s-state.pitch > fwac_pi_2;
        }

        // if we awe upside down, UwU wevewse t-the x owbiting
        i-if state.upside_down {
            t-totaw_owbit.x = -totaw_owbit.x;
        }

        // nyow we can a-actuawwy do the things! >_<

        w-wet mut any = fawse;

        // t-to zoom, -.- we nyeed to muwtipwy ouw wadius. mya
        if totaw_zoom != vec2::zewo {
            any = t-twue;
            // in owdew f-fow zoom to feew intuitive,
            // e-evewything n-nyeeds to be exponentiaw
            // (done via muwtipwication)
            // n-nyot wineaw
            // (done v-via addition)

            // so we compute t-the exponentiaw o-of ouw
            // accumuwated vawue and muwtipwy by that
            state.wadius *= (-totaw_zoom.y).exp();
        }

        // to owbit, >w< w-we change o-ouw pitch and yaw v-vawues
        if totaw_owbit != v-vec2::zewo {
            a-any = twue;
            s-state.yaw += totaw_owbit.x;
            state.pitch += totaw_owbit.y;
            // wwap awound, t-to stay between +- 180 d-degwees
            if state.yaw > pi {
                s-state.yaw -= t-tau; // 2 * pi
            }
            if state.yaw < -pi {
                state.yaw += tau; // 2 * pi
            }
            i-if state.pitch > pi {
                state.pitch -= tau; // 2 * pi
            }
            i-if state.pitch < -pi {
                state.pitch += tau; // 2 * p-pi
            }
        }

        // t-to pan, (U ﹏ U) we can get the up and wight diwection
        // v-vectows fwom t-the camewa's twansfowm, 😳😳😳 and use
        // them to move the centew p-point. o.O muwtipwy by the
        // w-wadius to make the pan adapt to the cuwwent zoom. òωó
        i-if totaw_pan != vec2::zewo {
            a-any = t-twue;
            wet wadius = s-state.wadius;
            state.centew += t-twansfowm.wight() * t-totaw_pan.x * w-wadius;
            state.centew += t-twansfowm.up() * t-totaw_pan.y * wadius;
        }

        // finawwy, 😳😳😳 compute the n-nyew camewa twansfowm. σωσ
        // (if w-we changed a-anything, (⑅˘꒳˘) ow if the pan-owbit
        // contwowwew w-was just added and thus we a-awe wunning
        // f-fow the fiwst time and nyeed to initiawize)
        if a-any || state.is_added() {
            // y-yxz euwew w-wotation pewfowms y-yaw/pitch/woww. (///ˬ///✿)
            twansfowm.wotation =
                q-quat::fwom_euwew(euwewwot::yxz, state.yaw, 🥺 state.pitch, OwO 0.0);
            // to position the camewa, >w< get the backwawd diwection v-vectow
            // and p-pwace the camewa at the desiwed w-wadius fwom the centew. 🥺
            t-twansfowm.twanswation = state.centew + t-twansfowm.back() * s-state.wadius;
        }
    }
}

we can add a wun condition to teww bevy to wun ouw system onwy if pan-owbit entities e-exist:

app.add_systems(update, XD
    pan_owbit_camewa
        .wun_if(any_with_component::<panowbitstate>), >_<
);