void Update() { for (int i = 0; i < rockCount; i++) { Rock rock = allRocks[i]; // cheesy and fps-dependent rock.size += (rock.targetSize - rock.size) * 3f * Time.deltaTime; if (rock.state == Rock.State.Conveyor) { rock.position.x += conveyorSpeed * Time.deltaTime; if (rock.position.x > maxConveyorX) { rock.position.x -= ArmManager.armRowWidth + conveyorMargin * 2f; rock.size = 0f; } } else if (rock.state == Rock.State.Thrown) { rock.position += rock.velocity * Time.deltaTime; rock.velocity += Vector3.up * -gravityStrength * Time.deltaTime; TinCan nearestCan = TinCanManager.GetNearestCan(rock.position, false); if (nearestCan != null) { if ((nearestCan.position - rock.position).sqrMagnitude < .5f * .5f) { TinCanManager.HitCan(nearestCan, rock.velocity); rock.velocity = Random.insideUnitSphere * 3f; } } if (rock.position.y < -5f) { rock.state = Rock.State.Conveyor; conveyorRocks.Add(rock); rock.position = new Vector3(Random.Range(minConveyorX, maxConveyorX), 0f, 1.5f); rock.size = 0f; } } Matrix4x4 matrix = matrices[i]; matrix.m03 = rock.position.x; matrix.m13 = rock.position.y; matrix.m23 = rock.position.z; matrix.m00 = rock.size; matrix.m11 = rock.size; matrix.m22 = rock.size; matrices[i] = matrix; } Graphics.DrawMeshInstanced(rockMesh, 0, rockMaterial, matrices); }
void Update() { float time = Time.time + timeOffset; // resting position Vector3 idleHandTarget = transform.position + new Vector3(Mathf.Sin(time) * .35f, 1f + Mathf.Cos(time * 1.618f) * .5f, 1.5f); if (heldRock == null && windupTimer <= 0f) { if (intendedRock == null && reachTimer == 0f) { // we're idle - see if we can grab a rock Rock nearestRock = RockManager.NearestConveyorRock(transform.position - Vector3.right * .5f); if (nearestRock != null) { if ((nearestRock.position - transform.position).sqrMagnitude < maxReachLength * maxReachLength) { // found a rock to grab! // mark it as reserved so other hands don't reach for it intendedRock = nearestRock; intendedRock.reserved = true; lastIntendedRockSize = intendedRock.size; } } } else if (intendedRock == null) { // stop reaching if we've lost our target reachTimer -= Time.deltaTime / reachDuration; } if (intendedRock != null) { // we're reaching for a rock (but we haven't grabbed it yet) Vector3 delta = intendedRock.position - transform.position; if (delta.sqrMagnitude < maxReachLength * maxReachLength) { // figure out where we want to put our wrist // in order to grab the rock Vector3 flatDelta = delta; flatDelta.y = 0f; flatDelta.Normalize(); grabHandTarget = intendedRock.position + Vector3.up * intendedRock.size * .5f - flatDelta * intendedRock.size * .5f; lastIntendedRockPos = intendedRock.position; reachTimer += Time.deltaTime / reachDuration; if (reachTimer >= 1f) { // we've arrived at the rock - pick it up heldRock = intendedRock; RockManager.RemoveFromConveyor(heldRock); heldRock.state = Rock.State.Held; // remember the rock's position in "hand space" // (so we can position the rock while holding it) heldRockOffset = handMatrix.inverse.MultiplyPoint3x4(heldRock.position); intendedRock = null; // random minimum delay before starting the windup windupTimer = Random.Range(-1f, 0f); throwTimer = 0f; } } else { // we didn't grab the rock in time - forget it intendedRock.reserved = false; intendedRock = null; } } } if (heldRock != null) { // stop reaching after we've successfully grabbed a rock reachTimer -= Time.deltaTime / reachDuration; if (targetCan == null) { // find a target targetCan = TinCanManager.GetNearestCan(transform.position, true, targetXRange); } if (targetCan != null) { // found a target - prepare to throw targetCan.reserved = true; windupTimer += Time.deltaTime / windupDuration; } } reachTimer = Mathf.Clamp01(reachTimer); // smoothed reach timer float grabT = reachTimer; grabT = 3f * grabT * grabT - 2f * grabT * grabT * grabT; // reaching overrides our idle hand position handTarget = Vector3.Lerp(idleHandTarget, grabHandTarget, grabT); if (targetCan != null) { // we've got a target, which means we're currently throwing if (windupTimer < 1f) { // still winding up... float windupT = Mathf.Clamp01(windupTimer) - Mathf.Clamp01(throwTimer * 2f); windupT = 3f * windupT * windupT - 2f * windupT * windupT * windupT; handTarget = Vector3.Lerp(handTarget, windupHandTarget, windupT); Vector3 flatTargetDelta = targetCan.position - transform.position; flatTargetDelta.y = 0f; flatTargetDelta.Normalize(); // windup position is "behind us," relative to the target position windupHandTarget = transform.position - flatTargetDelta * 2f + Vector3.up * (3f - windupT * 2.5f); } else { // done winding up - actual throw, plus resetting to idle throwTimer += Time.deltaTime / throwDuration; // update our aim until we release the rock if (heldRock != null) { aimVector = AimAtCan(targetCan, lastIntendedRockPos); } // we start this animation in our windup position, // and end it by returning to our default idle pose Vector3 restingPos = Vector3.Lerp(windupHandTarget, handTarget, throwTimer); // find the hand's target position to perform the throw // (somewhere forward and upward from the windup position) Vector3 throwHandTarget = windupHandTarget + aimVector.normalized * 2.5f; handTarget = Vector3.LerpUnclamped(restingPos, throwHandTarget, throwCurve.Evaluate(throwTimer)); if (throwTimer > .15f && heldRock != null) { // release the rock heldRock.reserved = false; heldRock.state = Rock.State.Thrown; heldRock.velocity = aimVector; heldRock = null; } if (throwTimer >= 1f) { // we've completed the animation - return to idle windupTimer = 0f; throwTimer = 0f; TinCanManager.UnreserveCanAfterDelay(targetCan, 3f); targetCan = null; } } } // solve the arm IK chain first FABRIK.Solve(armChain, armBoneLength, transform.position, handTarget, handUp * armBendStrength); // figure out our current "hand vectors" from our arm orientation handForward = (armChain.Last(0) - armChain.Last(1)).normalized; handUp = Vector3.Cross(handForward, transform.right).normalized; handRight = Vector3.Cross(handUp, handForward); // create handspace-to-worldspace matrix handMatrix = Matrix4x4.TRS(armChain.Last(), Quaternion.LookRotation(handForward, handUp), Vector3.one); // how much are our fingers gripping? // (during a reach, this is based on the reach timer) float fingerGrabT = grabT; if (heldRock != null) { // move our held rock to match our new hand position heldRock.position = handMatrix.MultiplyPoint3x4(heldRockOffset); lastIntendedRockPos = heldRock.position; // if we're holding a rock, we're always gripping fingerGrabT = 1f; } // create rendering matrices for arm bones UpdateMatrices(armChain, 0, armBoneThickness, handUp); int matrixIndex = armChain.Length - 1; // next: fingers Vector3 handPos = armChain.Last(); // fingers spread out during a throw float openPalm = throwCurve.Evaluate(throwTimer); for (int i = 0; i < fingerChains.Length; i++) { // find knuckle position for this finger Vector3 fingerPos = handPos + handRight * (fingerXOffset + i * fingerSpacing); // find resting position for this fingertip Vector3 fingerTarget = fingerPos + handForward * (.5f - .1f * fingerGrabT); // spooky finger wiggling while we're idle fingerTarget += handUp * Mathf.Sin((time + i * .2f) * 3f) * .2f * (1f - fingerGrabT); // if we're gripping, move this fingertip onto the surface of our rock Vector3 rockFingerDelta = fingerTarget - lastIntendedRockPos; Vector3 rockFingerPos = lastIntendedRockPos + rockFingerDelta.normalized * (lastIntendedRockSize * .5f + fingerThicknesses[i]); fingerTarget = Vector3.Lerp(fingerTarget, rockFingerPos, fingerGrabT); // apply finger-spreading during throw animation fingerTarget += (handUp * .3f + handForward * .1f + handRight * (i - 1.5f) * .1f) * openPalm; // solve this finger's IK chain FABRIK.Solve(fingerChains[i], fingerBoneLengths[i], fingerPos, fingerTarget, handUp * fingerBendStrength); // update this finger's rendering matrices UpdateMatrices(fingerChains[i], matrixIndex, fingerThicknesses[i], handUp); matrixIndex += fingerChains[i].Length - 1; } // the thumb is pretty much the same as the fingers // (but pointing in a strange direction) Vector3 thumbPos = handPos + handRight * thumbXOffset; Vector3 thumbTarget = thumbPos - handRight * .15f + handForward * (.2f + .1f * fingerGrabT) - handUp * .1f; thumbTarget += handRight * Mathf.Sin(time * 3f + .5f) * .1f * (1f - fingerGrabT); // thumb bends away from the palm, instead of "upward" like the fingers Vector3 thumbBendHint = (-handRight - handForward * .5f); Vector3 rockThumbDelta = thumbTarget - lastIntendedRockPos; Vector3 rockThumbPos = lastIntendedRockPos + rockThumbDelta.normalized * (lastIntendedRockSize * .5f); thumbTarget = Vector3.Lerp(thumbTarget, rockThumbPos, fingerGrabT); FABRIK.Solve(thumbChain, thumbBoneLength, thumbPos, thumbTarget, thumbBendHint * thumbBendStrength); UpdateMatrices(thumbChain, matrixIndex, thumbThickness, thumbBendHint); // draw all of our bones Graphics.DrawMeshInstanced(boneMesh, 0, material, matrices); }