<aside> 💡 Description: Basic example of Geenee Joystick control integration and how to move model around the AR scene. Custom Animation Mixer implementation. crossFade animation function helps to make animation transitions smother and seamless. Extra UI HTML control element for vertical object animation.

</aside>

gee-video 13.MP4

  1. Add your model to the 3D Scene.
  2. Open Custom Code Editor (press ”{}” icon) and insert the code below.
let joystick;
let started = false;
let flyUp = false;
let onTheGround = true;
let falling = false;
let afterLanded = true;
let activeAnimation = 0;
const animationList = ['Idle 01', 'Dance 01', 'Dance 02', 'Dance Happy 01', 'Dance Breakin 01', 'Wave 01',  'Hi 01',  'Mad 01', 'Salute 01'];

    const loadScript = (_name, src, callback) => {
        let s;
        let r;
        let t;
        r = false;
        s = document.createElement('script');
        s.type = 'text/javascript';
        s.src = src;
        s.onload = s.onreadystatechange =  ()  => {
            if (!r && (!this.readyState || this.readyState == 'complete')) {
                r = true;
                callback(_name);
            }
        };
        t = document.getElementsByTagName('script')[ 0 ];
        t.parentNode.insertBefore(s, t);
    }

    const stopAnim = (name) => {
        window.GeeneeAR.stopAnimationClip(name);
    }
    const playAnim = (name) => {
        window.GeeneeAR.stopAllAnimations();
        window.GeeneeAR.playAnimationClip(name);
    }
    const animAction = (name) => {
        return window.GeeneeAR.getAnimationActionByName(name);
    }

    const crossFade = (out, to, time) => {
        window.GeeneeAR.stopAllAnimations();
        animAction(out).play();
        animAction(out).crossFadeTo( animAction(to), time );
        animAction(to).play();
    }

    const startFlame = () => {
        getObjectByName.scale.set(1, 1, 1);
        window.GeeneeAR.playAnimationClip('Take 01_Flame0.001');
        window.GeeneeAR.playAnimationClip('Take 01_Flame0');
    }

    const stopFlame = () => {
        this.activeSceneModel.getObjectByName('Flame_wrap').scale.set(0, 0, 0);
    }

    const createMoveButton = (isRight) => {
         const changeAnimBlock = document.createElement("div");
         changeAnimBlock.innerHTML = `<div 
                className="changeAnim"
                style="
                    position: absolute;
                    bottom: 135px;
                    left: 50%;
                    transform: translateX(calc(-50% + ${isRight ? '110px': '-110px'}));
                    z-index: 3;
                    background: rgba(255, 255, 255, 0.15);
                    -webkit-backdrop-filter: blur(15px);
                    backdrop-filter: blur(15px);
                    -webkit-user-select: none;
                    -moz-user-select: none;
                    -ms-user-select: none;
                    width: 45px;
                    height: 45px;
                    border-radius: 100%;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                ">
                <img
                src="<https://eu-central-1-staging-cms-01-attachments-upload.geenee.io/attachments/a2f84bb6-a86a-49bd-810a-82e0591b9dfd/preview>"
                style="${isRight? 'transform: scaleX(-1);':''}; width:85%"
                />
                <span style="
                    position:absolute;
                    bottom: -15px;
                    font-family: 'Lato', sans-serif;
                    font-size: 10px;
                    font-weight: bold;
                    white-space: nowrap;
                    color: white;
                    user-select: none;
                    text-shadow: 0px 0px 15px rgba(103, 86, 103, 0.75);
                ">
                    ${isRight? 'Next':'Fly'}
                </span>
            </div>
         `;
         const element = changeAnimBlock.firstChild
        document.getElementById('app').appendChild(element)

            element.addEventListener('touchstart', (e) => {
                flyUp = true;
                e.preventDefault();
                e.stopPropagation();
                if (falling) {
                        crossFade( 'Free Fall 01',  'Fall 01', 0.2);
                 } else {
                        crossFade( 'Jump 01',  'Fall 01', 0.2);
                 }

                 startFlame();
            })

            element.addEventListener('touchend', (e) => {
                flyUp = false;
                e.preventDefault();
                e.stopPropagation();
                if (this.object3D.position.y > 1) {
                    falling = true;
                    crossFade( 'Fall 01',  'Free Fall 01', 0.3);
                } else {
                    falling = false;
                }
            });
    }

    // After all necessary scripts loaded 
    const onLoadScript = (_name) => {
        if (_name == 'joystick') {

            document.getElementById('geenee-joystick').style.display = 'block'
            createMoveButton();

            const object = this.object3D;
            const matrix = this.activeSceneModel.getObjectByName('geenee-3d-matrix-target-group');
            let run = false;
            let joyX, joyY;

            const joystick2 = this.activeSceneModel.joystick

            const render = () => {
                if (started) {

                    joyX = joystick2.value.x;
                    joyY = joystick2.value.y;

                    if (flyUp) {
                        object.position.y += 0.02;
                    } else if (!flyUp && !onTheGround) {
                        object.position.y -= 0.015;
                        afterLanded = false;
                    }

                    onTheGround = !(object.position.y > 0);

                    if (onTheGround && !afterLanded) {
                        const isLandingAnim = falling ? 'Free Fall 01': 'Fall 01';
                            crossFade(isLandingAnim,  'Land 01', 0.2);
                            setTimeout(() => {
                                run ? crossFade( 'Land 01',  'Run 01', 0.2) : crossFade( 'Land 01',  'Idle 01', 0.2);
                                }, 100);
                            falling = false;
                            afterLanded = true;
                            stopFlame();
                    }

                    if (joyX || joyY) {
                        const dist = -Math.max(Math.abs(joyX), Math.abs(joyY));
                        object.translateZ(dist / 2000);
                        object.rotation.y = Math.atan2(-joyX, joyY) - matrix.rotation.y;

                        if  (!run && onTheGround) {
                            crossFade('Idle 01', 'Run 01', 0.2);
                            run = true;
                        }

                    } else {

                        if (run && onTheGround) {
                            crossFade( 'Run 01', 'Idle 01', 0.2);
                            run = false;
                        }
                    }
                }
            };

            // Reassign to Geenee Render Loop
            this.activeSceneModel.userCallbacks.onRender = render;

        } 
    }

onLoadScript('joystick')

this.activeSceneModel.$parent.emitter.addListener('geenee-model-placed', () => {
        stopFlame();
        started = true;
        this.activeSceneModel.setScene3DSettingsOption('defaultCanvasClick', false);
        this.activeSceneModel.setGestureOption('dragOn', false);

        setTimeout( () => window.GeeneeAR.stopAllAnimations(), 300);
        setTimeout( () => window.GeeneeAR.playAnimationClip('Idle 01'),  500);
});