User prompt
Zıplama da kalça bacak diz hizalı ve aşağı düsun
User prompt
Zıplamada bacaklar ve dizler bağlı olsun.
User prompt
Zıplamadan sonra kollar ve bacaklar yere düşen ik olsun.
User prompt
Yere düşerken çok yavas ol.
User prompt
Zıplamada body parts tam birbirine simetrik olsun
User prompt
Zıplamada body parts simetrik hareket etsin. Stand islevinde kollar iki yana açılsın.
User prompt
Zıplamada body parts simetrik hareket etsin. Zıplamadan sonra yere düserken Slow motion bur efekt uygula
User prompt
Zıplama oturma ve kalkma tüm bunlar belly dancer gibi kıvrak figürlwr olsun.
User prompt
Zıplama dahq gerçekci olsun. Oturma ve kalma kollar açıp kapatsın.
User prompt
Zımplamayı simetrik ik ile güncelle.
User prompt
Zıplamada body parts mirror şekilde yukarı zıplasın.
User prompt
Bacakları da aşağıya düşür.
User prompt
Kolları aşağıya düşür.
User prompt
Dirsekte ters kırilma olmasın kollar aşağıya sarksın
User prompt
Kollar ve dirsek açık dursun
User prompt
Kolları sağa ve sola aç dans eder gibi olsun
User prompt
Kollar dirsek bağlantısıni güncelle
User prompt
Kollar dirsekten aşağıda olsun
User prompt
Dirsek dışarıda olmasın ters çevir
User prompt
Omuz ve kolları ik kinematik tekrar düzenle
User prompt
Kollar ik kinematik ters oldu.
User prompt
update ik kinematik for body parts
User prompt
drum_tap_right davula tıklayınca sese çok uzun reverb efekti uygula
User prompt
drum_tap_left davula tıklayınca sese çok uzun reverb efekti uygula.
User prompt
drum_tap_sit davula tıklayınca sese orta aralıklı 2 kez delay efekti uygula.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Drum class
var Drum = Container.expand(function () {
var self = Container.call(this);
self.drumType = 'none'; // 'large', 'left', 'right', 'sit', 'stand'
self.action = null; // function to call on tap
// Set up drum asset
self.setDrum = function (type) {
self.drumType = type;
var assetId = type === 'large' ? 'drum_large' : 'drum_medium';
var drum = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// (Drum label removed)
};
// Drum tap effect
self.flash = function () {
tween(self, {
scaleX: 1.18,
scaleY: 0.92
}, {
duration: 80,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 120
});
}
});
};
// Touch event
self.down = function (x, y, obj) {
self.flash();
// Play unique sound for each drum type
if (self.drumType === 'large') {
LK.getSound('drum_tap_center').play();
} else if (self.drumType === 'left') {
// Play with very long reverb effect
if (typeof reverb !== "undefined" && reverb.add) {
var leftSound = LK.getSound('drum_tap_left');
reverb.add(leftSound, {
reverbTime: 3.5
}); // simulate very long reverb (if plugin supports param)
leftSound.play();
} else {
LK.getSound('drum_tap_left').play();
}
} else if (self.drumType === 'right') {
// Play with very long reverb effect
if (typeof reverb !== "undefined" && reverb.add) {
var rightSound = LK.getSound('drum_tap_right');
reverb.add(rightSound, {
reverbTime: 3.5
}); // simulate very long reverb (if plugin supports param)
rightSound.play();
} else {
LK.getSound('drum_tap_right').play();
}
} else if (self.drumType === 'sit') {
var sitSound = LK.getSound('drum_tap_sit');
sitSound.play();
LK.setTimeout(function () {
sitSound.play();
}, 120);
} else if (self.drumType === 'stand') {
// Play the sound with 3 fast delay effects (echoes)
var base = LK.getSound('drum_tap_stand');
base.play();
LK.setTimeout(function () {
base.play();
}, 45);
LK.setTimeout(function () {
base.play();
}, 90);
} else {
// No fallback sound
}
if (self.action) self.action();
};
return self;
});
// Puppet class: articulated body with simple kinematics
var Puppet = Container.expand(function () {
var self = Container.call(this);
// --- Body part sizes (for positioning) ---
var headH = 120 * 1.5,
headW = 120 * 1.5;
var bodyH = 200 * 1.5,
bodyW = 80 * 1.5;
var upperArmH = 100 * 1.5,
upperArmW = 40 * 1.5;
var lowerArmH = 90 * 1.5,
lowerArmW = 32 * 1.5;
var upperLegH = 120 * 1.5,
upperLegW = 48 * 1.5;
var lowerLegH = 110 * 1.5,
lowerLegW = 40 * 1.5;
// --- Create body parts ---
// Head
var head = self.attachAsset('puppet_head', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
// Body
var body = self.attachAsset('puppet_body', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// Left upper arm
var lUpperArm = self.attachAsset('puppet_upperarm', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// Left lower arm
var lLowerArm = self.attachAsset('puppet_lowerarm', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// Right upper arm
var rUpperArm = self.attachAsset('puppet_upperarm', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// Right lower arm
var rLowerArm = self.attachAsset('puppet_lowerarm', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// Left upper leg
var lUpperLeg = self.attachAsset('puppet_upperleg', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// Left lower leg
var lLowerLeg = self.attachAsset('puppet_lowerleg', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// Right upper leg
var rUpperLeg = self.attachAsset('puppet_upperleg', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// Right lower leg
var rLowerLeg = self.attachAsset('puppet_lowerleg', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// --- Initial pose state ---
self.state = {
vx: 0,
// horizontal velocity
vy: 0,
// vertical velocity
grounded: false,
pose: 'stand',
// 'stand', 'sit', 'jump'
headAngle: 0,
// radians, for "pull head" actions
x: 0,
// world position
y: 0
};
// --- Positioning offsets (relative to puppet center) ---
function updateBodyParts() {
// Head
head.x = 0;
head.y = -bodyH / 2 - headH / 2 + 10;
head.rotation = self.state.headAngle;
// Body
body.x = 0;
body.y = -bodyH / 2;
// --- 2D IK for ARMS ---
// Anatomically improved shoulder positions (slightly lower and wider)
var shoulderYOffset = -bodyH / 2 + 54;
var shoulderXOffset = bodyW / 2 + upperArmW * 0.62;
var lShoulderX = -shoulderXOffset;
var lShoulderY = shoulderYOffset;
var rShoulderX = shoulderXOffset;
var rShoulderY = shoulderYOffset;
// Target hand positions (relative to puppet center)
// Animate hand targets based on pose
var lHandTarget = {
x: lShoulderX - 80,
y: lShoulderY + 210
};
var rHandTarget = {
x: rShoulderX + 80,
y: rShoulderY + 210
};
if (self.state.pose === 'sit') {
lHandTarget = {
x: lShoulderX - 60,
y: lShoulderY + 290
};
rHandTarget = {
x: rShoulderX + 60,
y: rShoulderY + 290
};
} else if (self.state.pose === 'jump') {
lHandTarget = {
x: lShoulderX - 20,
y: lShoulderY - 80
};
rHandTarget = {
x: rShoulderX + 20,
y: rShoulderY - 80
};
} else if (self.state.pose === 'stand') {
// Gentle swing
var swing = Math.sin(Date.now() * 0.003) * 70;
lHandTarget.x += swing;
rHandTarget.x -= swing;
lHandTarget.y += Math.abs(swing) * 0.22;
rHandTarget.y += Math.abs(swing) * 0.22;
}
// 2D IK solver for arms (returns joint positions and angles)
// This version ensures the elbow always bends downward (arms hang down), never "breaks" upward
function solveIK2DDownward(sx, sy, ex, ey, l1, l2) {
// Returns {ax, ay, bx, by, aAngle, bAngle}
var dx = ex - sx,
dy = ey - sy;
var dist = Math.sqrt(dx * dx + dy * dy);
dist = Math.min(dist, l1 + l2 - 0.01); // Clamp to reachable
// Law of cosines
var a1 = Math.acos((l1 * l1 + dist * dist - l2 * l2) / (2 * l1 * dist));
var a2 = Math.atan2(dy, dx);
// To ensure the elbow always bends downward, we check the "elbow up" and "elbow down" solutions
// and always pick the one where the elbow is below the shoulder and hand
var upperAngleDown = a2 + a1; // Elbow "down"
var elbowXDown = sx + Math.cos(upperAngleDown) * l1;
var elbowYDown = sy + Math.sin(upperAngleDown) * l1;
var upperAngleUp = a2 - a1; // Elbow "up"
var elbowXUp = sx + Math.cos(upperAngleUp) * l1;
var elbowYUp = sy + Math.sin(upperAngleUp) * l1;
// Pick the elbow position with the greatest Y (lowest on screen)
var useDown = elbowYDown > elbowYUp;
var elbowX = useDown ? elbowXDown : elbowXUp;
var elbowY = useDown ? elbowYDown : elbowYUp;
var upperAngle = useDown ? upperAngleDown : upperAngleUp;
var lowerAngle = Math.atan2(ey - elbowY, ex - elbowX);
return {
ax: sx,
ay: sy,
bx: elbowX,
by: elbowY,
aAngle: upperAngle,
bAngle: lowerAngle
};
}
// Left arm (elbow always bends downward, arms hang down)
var lArmIK = solveIK2DDownward(lShoulderX, lShoulderY, lHandTarget.x, lHandTarget.y, upperArmH, lowerArmH);
lUpperArm.x = lArmIK.ax;
lUpperArm.y = lArmIK.ay;
lUpperArm.rotation = lArmIK.aAngle - Math.PI / 2;
// Lower arm: connect at elbow joint, always below the elbow (dirsekten aşağıda)
var lLowerArmLength = lowerArmH;
var lForearmAngle = lArmIK.bAngle;
lLowerArm.x = lArmIK.bx;
lLowerArm.y = lArmIK.by;
lLowerArm.rotation = lForearmAngle - Math.PI / 2;
// Right arm (elbow always bends downward, arms hang down)
var rArmIK = solveIK2DDownward(rShoulderX, rShoulderY, rHandTarget.x, rHandTarget.y, upperArmH, lowerArmH);
rUpperArm.x = rArmIK.ax;
rUpperArm.y = rArmIK.ay;
rUpperArm.rotation = rArmIK.aAngle - Math.PI / 2;
// Lower arm: connect at elbow joint, always below the elbow (dirsekten aşağıda)
var rLowerArmLength = lowerArmH;
var rForearmAngle = rArmIK.bAngle;
rLowerArm.x = rArmIK.bx;
rLowerArm.y = rArmIK.by;
rLowerArm.rotation = rForearmAngle - Math.PI / 2;
// --- 2D IK for LEGS ---
var legY = bodyH / 2 - 10;
var legX = bodyW / 2 - 18;
var lHipX = -legX;
var lHipY = legY;
var rHipX = legX;
var rHipY = legY;
// Target foot positions (relative to puppet center)
var lFootTarget = {
x: lHipX - 10,
y: lHipY + upperLegH + lowerLegH - 10
};
var rFootTarget = {
x: rHipX + 10,
y: rHipY + upperLegH + lowerLegH - 10
};
if (self.state.pose === 'sit') {
lFootTarget = {
x: lHipX + 80,
y: lHipY + upperLegH + lowerLegH - 30
};
rFootTarget = {
x: rHipX + 80,
y: rHipY + upperLegH + lowerLegH - 30
};
} else if (self.state.pose === 'jump') {
lFootTarget = {
x: lHipX - 30,
y: lHipY + 60
};
rFootTarget = {
x: rHipX + 30,
y: rHipY + 60
};
} else if (self.state.pose === 'stand') {
// Gentle walk cycle if moving
var walk = Math.abs(self.state.vx) > 0.5 ? Math.sin(Date.now() * 0.008) * 60 : 0;
lFootTarget.x += walk;
rFootTarget.x -= walk;
lFootTarget.y += Math.abs(walk) * 0.2;
rFootTarget.y += Math.abs(walk) * 0.2;
}
// 2D IK for left leg
var lLegIK = solveIK2D(lHipX, lHipY, lFootTarget.x, lFootTarget.y, upperLegH, lowerLegH);
lUpperLeg.x = lLegIK.ax;
lUpperLeg.y = lLegIK.ay;
lUpperLeg.rotation = lLegIK.aAngle - Math.PI / 2;
lLowerLeg.x = lLegIK.bx;
lLowerLeg.y = lLegIK.by;
lLowerLeg.rotation = lLegIK.bAngle - Math.PI / 2;
// 2D IK for right leg
var rLegIK = solveIK2D(rHipX, rHipY, rFootTarget.x, rFootTarget.y, upperLegH, lowerLegH);
rUpperLeg.x = rLegIK.ax;
rUpperLeg.y = rLegIK.ay;
rUpperLeg.rotation = rLegIK.aAngle - Math.PI / 2;
rLowerLeg.x = rLegIK.bx;
rLowerLeg.y = rLegIK.by;
rLowerLeg.rotation = rLegIK.bAngle - Math.PI / 2;
// Body rotation for sit/jump
if (self.state.pose === 'sit') {
body.rotation = -0.18;
} else if (self.state.pose === 'jump') {
body.rotation = 0.12;
} else {
body.rotation = 0;
}
}
// --- Public puppet actions ---
self.pullHeadLeft = function () {
// Pull head left, add leftward velocity
if (self.state.grounded) {
self.state.vx -= 12;
self.state.headAngle = -0.5;
tween(self.state, {
headAngle: 0
}, {
duration: 400,
easing: tween.easeOut
});
}
};
self.pullHeadRight = function () {
if (self.state.grounded) {
self.state.vx += 12;
self.state.headAngle = 0.5;
tween(self.state, {
headAngle: 0
}, {
duration: 400,
easing: tween.easeOut
});
}
};
self.jump = function () {
if (self.state.grounded) {
self.state.vy = -48; // Increased jump velocity for higher jump
self.state.grounded = false;
self.state.pose = 'jump';
tween(self.state, {}, {
duration: 0,
onFinish: function onFinish() {
// After 400ms, return to stand pose
LK.setTimeout(function () {
self.state.pose = 'stand';
}, 400);
}
});
}
};
self.sit = function () {
if (self.state.grounded && self.state.pose !== 'sit') {
self.state.pose = 'sit';
// After 600ms, stand up automatically
LK.setTimeout(function () {
self.stand();
}, 600);
}
};
self.stand = function () {
if (self.state.grounded && self.state.pose !== 'stand') {
self.state.pose = 'stand';
}
};
// --- Physics update ---
self.update = function () {
// Gravity
if (!self.state.grounded) {
self.state.vy += 2.2; // gravity
if (self.state.vy > 40) self.state.vy = 40;
}
// Friction
if (self.state.grounded) {
self.state.vx *= 0.82;
if (Math.abs(self.state.vx) < 0.5) self.state.vx = 0;
} else {
self.state.vx *= 0.98;
}
// Move
self.state.x += self.state.vx;
self.state.y += self.state.vy;
// Clamp to world bounds
if (self.state.x < 80) self.state.x = 80;
if (self.state.x > 2048 - 80) self.state.x = 2048 - 80;
// --- Ava: Adjust puppet Y so feet are above ground visually ---
// The puppet's feet are at y + (bodyH/2) + upperLegH + lowerLegH - 10 - 10
// ground.y = 2732 - 80, ground height = 80
// So, puppet.y + (bodyH/2) + upperLegH + lowerLegH - 20 = 2732 - 80
// puppet.y = 2732 - 80 - (bodyH/2) - upperLegH - lowerLegH + 20
var puppetFeetOffset = bodyH / 2 + upperLegH + lowerLegH - 20;
var puppetGroundY = 2732 - 80 - puppetFeetOffset;
if (self.state.y > puppetGroundY) self.state.y = puppetGroundY; // above ground
// Ground collision
if (self.state.y >= puppetGroundY) {
self.state.y = puppetGroundY;
self.state.vy = 0;
self.state.grounded = true;
if (self.state.pose === 'jump') self.state.pose = 'stand';
} else {
self.state.grounded = false;
}
// Apply to container
self.x = self.state.x;
self.y = self.state.y;
updateBodyParts();
};
// --- Start position ---
self.state.x = 600; // Moved puppet further right (was 180)
// --- Ava: Set initial Y so feet are above ground visually ---
var puppetFeetOffset = bodyH / 2 + upperLegH + lowerLegH - 20;
self.state.y = 2732 - 80 - puppetFeetOffset;
updateBodyParts();
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Ava: background asset
// Apply reverb to all drum and win/lose sounds
// --- World setup ---
// Puppet body parts (shapes)
// Drums
// Ground
// Finish sign
// Drum tap sound
// Win/lose sound
// Define and initialize the reverb object before using it
var reverb = {
add: function add(sound) {
// Dummy implementation: does nothing, but prevents errors
// In a real environment, this would apply a reverb effect to the sound
}
};
reverb.add(LK.getSound('drum_tap_center'));
reverb.add(LK.getSound('drum_tap_left'));
reverb.add(LK.getSound('drum_tap_right'));
reverb.add(LK.getSound('drum_tap_sit'));
reverb.add(LK.getSound('drum_tap_stand'));
reverb.add(LK.getSound('lose'));
reverb.add(LK.getSound('win'));
// --- Background music playlist (4 tracks, play in random order) ---
var musicTracks = [{
id: 'musicId',
volume: 0.1,
start: 0,
end: 1
}, {
id: 'musicId2',
volume: 0.1,
start: 0,
end: 1
}, {
id: 'musicId3',
volume: 0.1,
start: 0,
end: 1
}, {
id: 'musicId4',
volume: 0.1,
start: 0,
end: 1
}];
// Shuffle helper (Fisher-Yates)
function shuffleArray(arr) {
for (var i = arr.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// Start with a shuffled playlist
var shuffledMusicOrder = [];
function reshuffleMusicOrder() {
shuffledMusicOrder = [];
for (var i = 0; i < musicTracks.length; ++i) shuffledMusicOrder.push(i);
shuffleArray(shuffledMusicOrder);
}
reshuffleMusicOrder();
var currentMusicOrderIndex = 0;
function playNextMusic() {
// If we've played all, reshuffle for a new random order
if (currentMusicOrderIndex >= shuffledMusicOrder.length) {
reshuffleMusicOrder();
currentMusicOrderIndex = 0;
}
var trackIndex = shuffledMusicOrder[currentMusicOrderIndex];
var track = musicTracks[trackIndex];
LK.playMusic(track.id, {
loop: false,
volume: track.volume,
start: track.start,
end: track.end,
onFinish: function onFinish() {
currentMusicOrderIndex++;
playNextMusic();
}
});
}
playNextMusic();
game.setBackgroundColor(0x000000);
// --- Background ---
// Ava: Add background image behind all elements
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
scaleX: 1,
scaleY: 1
});
game.addChild(background);
// Ground
var ground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0
});
ground.x = 0;
ground.y = 2732 - 80;
game.addChild(ground);
// Finish sign
var finishSign = LK.getAsset('finish_sign', {
anchorX: 0.5,
anchorY: 1,
scaleX: 3,
scaleY: 6 // 2x as tall as before
});
finishSign.x = 2048 - 120;
finishSign.y = 2732 - 80;
game.addChild(finishSign);
// Finish text removed
// --- Puppet ---
var puppet = new Puppet();
game.addChild(puppet);
// --- Drums ---
var drums = [];
// Drum positions (relative to center)
// Move drums higher: set drumCenterY above puppet's head
var drumCenterX = 2048 / 2,
drumCenterY = 900; // was 2732 / 2 + 220, now much higher (puppet head is around y=~1200)
var drumRadius = 220 * 3;
var angleList = [{
type: 'left',
angle: -Math.PI / 2 - 0.7
}, {
type: 'right',
angle: -Math.PI / 2 + 0.7
}, {
type: 'sit',
angle: Math.PI / 2 + 0.7
}, {
type: 'stand',
angle: Math.PI / 2 - 0.7
}];
// --- Drum action pool ---
// Each drum will have all actions, only their order will be shuffled
// Ava: Increase the number of right movement actions (pullHeadRight) in the action pool
var puppetActions = [function () {
puppet.pullHeadLeft();
}, function () {
puppet.pullHeadRight();
}, function () {
puppet.pullHeadRight();
}, function () {
puppet.jump();
}, function () {
puppet.sit();
}, function () {
puppet.stand();
}];
// Large center drum (fixed action sequence)
var drumJump = new Drum();
drumJump.setDrum('large');
drumJump.x = drumCenterX;
drumJump.y = drumCenterY;
// Assign a fixed sequence of all 5 actions, and cycle through them
drumJump._actionSequence = puppetActions.slice();
drumJump._actionIndex = 0;
drumJump.action = function () {
drumJump._actionSequence[drumJump._actionIndex]();
drumJump._actionIndex = (drumJump._actionIndex + 1) % drumJump._actionSequence.length;
};
game.addChild(drumJump);
drums.push(drumJump);
// Four medium drums
for (var i = 0; i < angleList.length; ++i) {
var d = new Drum();
d.setDrum(angleList[i].type);
d.x = drumCenterX + Math.cos(angleList[i].angle) * drumRadius;
d.y = drumCenterY + Math.sin(angleList[i].angle) * drumRadius;
// Each drum gets a fixed sequence of all 5 actions, and cycles through them
d._actionSequence = puppetActions.slice();
d._actionIndex = 0;
d.action = function (drum) {
return function () {
drum._actionSequence[drum._actionIndex]();
drum._actionIndex = (drum._actionIndex + 1) % drum._actionSequence.length;
};
}(d);
game.addChild(d);
drums.push(d);
}
// --- Timer ---
var timeLeft = 60;
function formatTimeDigital(secs) {
var m = Math.floor(secs / 60);
var s = secs % 60;
var mm = m < 10 ? "0" + m : "" + m;
var ss = s < 10 ? "0" + s : "" + s;
return mm + ":" + ss;
}
var timerText = new Text2(formatTimeDigital(60), {
size: 120,
fill: '#ffffff'
});
timerText.anchor.set(0.5, 0);
LK.gui.top.addChild(timerText);
// --- Score/Win state ---
var gameEnded = false;
// --- Timer logic ---
var timerInterval = LK.setInterval(function () {
if (gameEnded) return;
timeLeft -= 1;
if (timeLeft < 0) timeLeft = 0;
timerText.setText(formatTimeDigital(timeLeft));
if (timeLeft === 0 && !gameEnded) {
gameEnded = true;
LK.getSound('lose').play();
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
}
}, 1000);
// --- Game update ---
game.update = function () {
if (gameEnded) return;
puppet.update();
// Win condition: puppet body collides with finish sign using intersects
if (!gameEnded && puppet.intersects(finishSign)) {
gameEnded = true;
LK.setScore(timeLeft); // Set score to remaining seconds
LK.getSound('win').play();
LK.effects.flashScreen(0x44c767, 800);
LK.showYouWin();
}
};
// --- Clean up on game over ---
game.onDestroy = function () {
LK.clearInterval(timerInterval);
};
// --- Prevent accidental tap on top left (menu) ---
/* No elements are placed at (0,0)-(100,100) */
// --- UX: drums are always on top ---
for (var i = 0; i < drums.length; ++i) {
game.addChild(drums[i]);
} ===================================================================
--- original.js
+++ change.js
@@ -244,20 +244,31 @@
lHandTarget.y += Math.abs(swing) * 0.22;
rHandTarget.y += Math.abs(swing) * 0.22;
}
// 2D IK solver for arms (returns joint positions and angles)
- function solveIK2D(sx, sy, ex, ey, l1, l2) {
+ // This version ensures the elbow always bends downward (arms hang down), never "breaks" upward
+ function solveIK2DDownward(sx, sy, ex, ey, l1, l2) {
// Returns {ax, ay, bx, by, aAngle, bAngle}
var dx = ex - sx,
dy = ey - sy;
var dist = Math.sqrt(dx * dx + dy * dy);
dist = Math.min(dist, l1 + l2 - 0.01); // Clamp to reachable
// Law of cosines
var a1 = Math.acos((l1 * l1 + dist * dist - l2 * l2) / (2 * l1 * dist));
var a2 = Math.atan2(dy, dx);
- var upperAngle = a2 - a1;
- var elbowX = sx + Math.cos(upperAngle) * l1;
- var elbowY = sy + Math.sin(upperAngle) * l1;
+ // To ensure the elbow always bends downward, we check the "elbow up" and "elbow down" solutions
+ // and always pick the one where the elbow is below the shoulder and hand
+ var upperAngleDown = a2 + a1; // Elbow "down"
+ var elbowXDown = sx + Math.cos(upperAngleDown) * l1;
+ var elbowYDown = sy + Math.sin(upperAngleDown) * l1;
+ var upperAngleUp = a2 - a1; // Elbow "up"
+ var elbowXUp = sx + Math.cos(upperAngleUp) * l1;
+ var elbowYUp = sy + Math.sin(upperAngleUp) * l1;
+ // Pick the elbow position with the greatest Y (lowest on screen)
+ var useDown = elbowYDown > elbowYUp;
+ var elbowX = useDown ? elbowXDown : elbowXUp;
+ var elbowY = useDown ? elbowYDown : elbowYUp;
+ var upperAngle = useDown ? upperAngleDown : upperAngleUp;
var lowerAngle = Math.atan2(ey - elbowY, ex - elbowX);
return {
ax: sx,
ay: sy,
@@ -266,27 +277,27 @@
aAngle: upperAngle,
bAngle: lowerAngle
};
}
- // Left arm (elbow open: do not invert, elbows point outward)
- var lArmIK = solveIK2D(lShoulderX, lShoulderY, lHandTarget.x, lHandTarget.y, upperArmH, lowerArmH);
+ // Left arm (elbow always bends downward, arms hang down)
+ var lArmIK = solveIK2DDownward(lShoulderX, lShoulderY, lHandTarget.x, lHandTarget.y, upperArmH, lowerArmH);
lUpperArm.x = lArmIK.ax;
lUpperArm.y = lArmIK.ay;
lUpperArm.rotation = lArmIK.aAngle - Math.PI / 2;
// Lower arm: connect at elbow joint, always below the elbow (dirsekten aşağıda)
var lLowerArmLength = lowerArmH;
- var lForearmAngle = lArmIK.bAngle; // direction from elbow to hand
+ var lForearmAngle = lArmIK.bAngle;
lLowerArm.x = lArmIK.bx;
lLowerArm.y = lArmIK.by;
lLowerArm.rotation = lForearmAngle - Math.PI / 2;
- // Right arm (elbow open: do not invert, elbows point outward)
- var rArmIK = solveIK2D(rShoulderX, rShoulderY, rHandTarget.x, rHandTarget.y, upperArmH, lowerArmH);
+ // Right arm (elbow always bends downward, arms hang down)
+ var rArmIK = solveIK2DDownward(rShoulderX, rShoulderY, rHandTarget.x, rHandTarget.y, upperArmH, lowerArmH);
rUpperArm.x = rArmIK.ax;
rUpperArm.y = rArmIK.ay;
rUpperArm.rotation = rArmIK.aAngle - Math.PI / 2;
// Lower arm: connect at elbow joint, always below the elbow (dirsekten aşağıda)
var rLowerArmLength = lowerArmH;
- var rForearmAngle = rArmIK.bAngle; // direction from elbow to hand
+ var rForearmAngle = rArmIK.bAngle;
rLowerArm.x = rArmIK.bx;
rLowerArm.y = rArmIK.by;
rLowerArm.rotation = rForearmAngle - Math.PI / 2;
// --- 2D IK for LEGS ---