void KillBlock(LiveBlock block)
    {
        double offset = Math.Abs(block.startTime - MusicPlayer.LiveTime);

        if (offset <= TimeGood)
        {
            combo += 1;
            if (combo > maxCombo)
            {
                maxCombo = combo;
            }
        }
        else
        {
            combo = 0;
        }

        float inc = block.isParallel ? BaseParaScore : BaseNormalScore;

        inc   *= GetScoreOffsetAddon(offset) * GetScoreComboAddon(combo);
        score += (int)inc;

        AudioFxConfig audioConfig;
        Material      material;
        string        text;

//		float scale;
        if (offset <= TimePerfect)
        {
//			scale = 2;
            material    = perfectMaterial;
            text        = perfectText;
            audioConfig = block.isLong ? specialAudioConfigs[Random.Range(0, specialAudioConfigs.Length)] : perfectAudioConfigs[Random.Range(0, perfectAudioConfigs.Length)];
        }
        else if (offset <= TimeGreat)
        {
//			scale = 1.5f;
            material    = greatMaterial;
            text        = greatText;
            audioConfig = greatAudioConfigs[Random.Range(0, greatAudioConfigs.Length)];
        }
        else if (offset <= TimeGood)
        {
//			scale = 1;
            material    = goodMaterial;
            text        = goodText;
            audioConfig = goodAudioConfigs[Random.Range(0, goodAudioConfigs.Length)];
        }
        else              // if (offset <= TimeBad)
//			scale = 0.5f;
        {
            material    = badMaterial;
            text        = badText;
            audioConfig = badAudioConfigs[Random.Range(0, badAudioConfigs.Length)];
        }

        var blockPosition = block.transform.position;

        hitParticleFxPreset.configs[0].count = combo << 1;          // 0th must be CubeCoin
        ParticleFxPool.Emit(hitParticleFxPreset, blockPosition, block.transform.rotation);
        FlashFxPool.Flash(hitFlashFxConfig, blockPosition);
        AudioFxPool.Play(audioConfig, blockPosition);

        uiScoreText.material    = material;
        uiIncText.material      = material;
        uiComboText.material    = material;
        uiScoreText.text        = score.ToString("N0");
        uiIncText.text          = "+" + inc.ToString("N0");
        uiComboText.text        = string.Format(combo == 0 ? "x {0:N0} x" : combo == maxCombo ? "^ {0:N0} ^" : "+ {0:N0} +", combo);
        uiScoreTextTween.isDead = false;
        uiScoreTextTween.time   = 0;
        uiComboTextTween.isDead = false;
        uiComboTextTween.time   = 0;
        uiIncTextTween.isDead   = false;
        uiIncTextTween.time     = 0;

        block.Die();

        block.uiComboText.material = material;
        block.uiJudgeText.material = material;
        block.uiScoreText.material = material;

        block.uiScoreText.text = string.Format("+{0:N0}", inc);
        block.uiJudgeText.text = text;
        if (combo > 10)
        {
            block.uiComboText.text = string.Format("{0:N0}", combo);
        }

        TweenManager.AddTween(new Tween(recycleInterval, textTween.easingType, textTween.easingPhase, step => {
            float inverse = (1f - step);            // * scale;
            var size      = new Vector3(inverse, inverse, inverse);
//			block.uiScoreText.color = new Color(1, 1, 1, inverse);
//			block.uiJudgeText.color = new Color(1, 1, 1, inverse);
//			block.uiComboText.color = new Color(1, 1, 1, inverse);
            block.uiScoreText.transform.localScale = size;
            block.uiJudgeText.transform.localScale = size;
            block.uiComboText.transform.localScale = size;
        }));
    }
    void UpdateBlock(LiveBlock block)
    {
//		const float damping = 100;

        if (!block.isClosed)
        {
            double closeTime = MusicPlayer.LiveTime - block.createTime;
            if (closeTime > closeDuration)
            {
                block.isClosed = true;
                ParticleFxPool.Emit(closeParticleFxPreset, block.transform.position, block.transform.rotation);
                FlashFxPool.Flash(closeFlashFxConfig, block.transform.position);

                block.leftShellTrans.localPosition  = Vector3.zero;
                block.rightShellTrans.localPosition = Vector3.zero;
            }
            else
            {
                block.leftShellTrans.localPosition  = new Vector3(0, 0, Easing.Ease(closeEasingType, closeEasingPhase, shellStart, -shellStart, closeTime, closeDuration));
                block.rightShellTrans.localPosition = new Vector3(0, 0, Easing.Ease(closeEasingType, closeEasingPhase, -shellStart, shellStart, closeTime, closeDuration));
            }
        }

        if (MusicPlayer.LiveTime <= block.startTime)            // Not yet
        {
            var offset = block.startTime - MusicPlayer.LiveTime;
            var x      = Easing.Ease(bufferEasingTypeX, bufferEasingPhaseX, block.x, bufferX * block.startX, offset, bufferInterval);
            var y      = Easing.Ease(bufferEasingTypeY, bufferEasingPhaseY, block.y, bufferY * block.startY, offset, bufferInterval);
            var z      = Easing.Ease(bufferEasingTypeZ, bufferEasingPhaseZ, 0, bufferZ, offset, bufferInterval);
//			block.transform.localPosition = Vector3.Lerp(block.transform.localPosition, new Vector3(x, y, z), UnityEngine.Time.deltaTime * damping);
            block.transform.localPosition = new Vector3(x, y, z);
        }
        else if (MusicPlayer.LiveTime - block.startTime < cacheInterval && MusicPlayer.LiveTime - block.startTime < TimeBad)              // Over due
        {
            var offset = MusicPlayer.LiveTime - block.startTime;
            var x      = Easing.Ease(cacheEasingTypeX, cacheEasingPhaseX, block.x, cacheX * block.x, offset, cacheInterval);
            var y      = Easing.Ease(cacheEasingTypeY, cacheEasingPhaseY, block.y, cacheY * block.y, offset, cacheInterval);
            var z      = Easing.Ease(cacheEasingTypeZ, cacheEasingPhaseZ, 0, cacheZ, offset, cacheInterval);
//			block.transform.localPosition = Vector3.Lerp(block.transform.localPosition, new Vector3(x, y, z), UnityEngine.Time.deltaTime * damping);
            block.transform.localPosition = new Vector3(x, y, z);
        }
        else              // Miss
        {
            AudioFxPool.Play(badAudioConfigs[Random.Range(0, badAudioConfigs.Length)], block.transform.position);
            FlashFxPool.Flash(badFlashFxConfig, block.transform.position);

            uiScoreText.material    = badMaterial;
            uiComboText.material    = badMaterial;
            uiIncText.text          = "";
            uiComboText.text        = "x 0 x";
            uiComboTextTween.isDead = false;
            uiComboTextTween.time   = 0;

            block.shouldDie         = true;
            block.shouldDieSilently = true;
            combo = 0;
        }

        if (block.isClosed && MusicPlayer.LiveTime - block.startTime < cacheInterval) // Detect collision
        {
            if (block.side == Side.Left)                                              // Left hand
            {
                if (Vector3.Dot(leftTip.velocity, -block.transform.up) > block.minDyingSpeed)
                {
                    DetectCollision(leftTip, block);
                }
            }
            else                  // Right hand
            {
                if (Vector3.Dot(rightTip.velocity, -block.transform.up) > block.minDyingSpeed)
                {
                    DetectCollision(rightTip, block);
                }
            }
        }
    }