<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>
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);
});