Tìm hiểu về các Easing Functions: Áp dụng (#3)

3 min read

I. Mở đầu

Những phần trước đã chúng ta đã tìm hiểu về các easing function và cấu trúc code để tạo nên chúng. Bây giờ, chúng ta sẽ đi vào phân tích tích một vài animation hiệu ứng thực tế mà mình đã áp dụng.

Sau đây là một vài hiệu ứng animation từ game Fishing Frenze – đây là một game câu cá chill chill.

II. Các hiệu ứng

1. Hiệu ứng Upgrade Asseccory

Hiệu ứng mở popup:

Khi popup Upgrade mình dùng easeOutBack để tạo hiệu ứng mở popup tự nhiên hơn, khi mở ra tạo cảm giác do quán tính nên mở hơi quá và thu lại. Ngoài ra, cũng có thể sử dụng easeOutElastic để làm hiệu ứng mở popup để tạo cảm giác popup được đàn hồi theo quán tính tốt hơn.

Hiệu ứng upgrade: (00:04s -> 00:14s)

Đầu tiên mình ẩn hết những UI không cần thiết để hướng người dùng tập trung vào animation sắp tới, ở đầy mình dùng linear function để ẩn.

  • Tiếp dùng easeInSine easeOutSine để tạo hiệu ứng nhấp nháy lần lượt cho item bên trái.
  • Đồng thời, item bên phải cũng dùng linear function để làm mở dần tạo cảm giác cung cấp năng lượng theo thời gian.
  • Sau khi hoàn thành, dùng easingOutElastic function để đưa item vào giữ màn hình, với easing này thì di chuyển hoàn thành sẽ tạo cảm giác về quán tính tự nhiên hơn.

Code:

private giveEffect(): Promise<void>
    {
        const createGiveUpgradeEffect = () => {
            const effect =instantiate(this.item_effectUpgrade);
            effect.parent = this.upgradeEffectParent;
            effect.position = Vec3.ZERO;
        }

        return new Promise((resolve) => {
            // Tạo vfx truyền năng lượng
            createGiveUpgradeEffect();
            
            // Làm mờ dần item 2
            tween(this.targetAccessory)
                .to(1.2, {opacity: 0})
            .start();

            const expand = new Vec3(1.1, 1.1, 1.1);

            // Tạo hiệu ứng nhấp nháy của item 1
            tween(this.currentAccessory.node)
                .to(0.1, {scale: expand}, {easing: 'sineIn'})
                .to(0.2, {scale: Vec3.ONE}, {easing: 'sineOut'})
                .union()
                .repeat(6)
                .call(() => resolve(null))
            .start();
        });
    }

    private currentMoveCenter(): Promise<void>
    {
        // Di chuyển item vào giữa
        return new Promise((resolve) => {
            tween(this.currentAccessory.node)
                .delay(0.5)
                .to(0.75, {position: Vec3.ZERO}, {easing: 'elasticOut'})
                .call(() => resolve(null))
            .start();
        });
    }

Cuối cùng là hiệu ứng hoàn thành:

  • Ẩn đi nhưng component không cần thiết trên item, dùng linear function.
  • Tạo hiệu ứng “đột phá”: Scale về 0 để tạo cảm giác tụ lực, sau đó dùng easeOutElastic easing mở ra về vị trí cũng đồng thời bật lại các componet detail.

Note: Đoạn effect này có thể improve bằng cách khi “tụ lực” đồng thời ẩn component thì hiệu ứng sẽ mượt hơn.

Code:

private hideCurrentAttached():  Promise<void>
    {
        return new Promise((resolve) => {

            const duration = 0.2;

            tween(this.currentEffectLevelNode)
                .to(duration, {opacity: 0})
            .start();

            tween(this.currentEffectStarNode)
                .to(duration, {opacity: 0})
            .start();
            
            tween(this.currentEffectValueNode)
                .to(duration, {opacity: 0})
                .delay(0.2)
                .call(() => resolve(null))
            .start();
        });
    }

    private changeFrame():  Promise<void>
    {
        const targetScale = Vec3.ZERO;
        
        this.newAccessory.active = false;
        this.newAccessory.scale = targetScale;
        this.newAccessoryEffectNode.scale = Vec3.ZERO;
        
        this.afterUpgradeNameNode.node.worldPosition = this.prepareUpgradeNameNode.worldPosition.clone();

        return new Promise((resolve) => {
            // Tụ lực
            tween(this.currentAccessory.node)
                .to(0.2, {scale: targetScale})
                .call(() => {this.prepareUpgradePanel.active = false})
            .start();

            // Bùng nổ item sau khi tụ lực đồng thời bật lại các component đã được đổi thông tin
            tween(this.newAccessory)
                .delay(0.2)
                .call(() => {
                    this.afterUpgradeNameNode.node.active = true;
                    this.upgradePanel.active = true;
                    this.newAccessory.active = true;
                })
                .to(0.5, {scale: Vec3.ONE}, {easing: 'elasticOut'})
            .start();

            // Bật hiệu ứng ánh sáng phía sau để chúc mừng hoàn thành.
            tween(this.newAccessoryEffectNode)
                .delay(0.5)
                .call(() => {this.newAccessoryEffectNode.active = true;})
                .to(0.7, {scale: Vec3.ONE}, {easing: 'elasticOut'})
                .call(() => resolve(null))
            .start();
        });
    }

Bật cái các UI đã ẩn để hoàn thành một lần chạy hiệu ứng upgrade. Với các component này dùng đồng thời nhiều loại chuyển động và easing khác nhau để tạo ra kết quả như trên, cụ thể:

  • Với dòng tile “Rod handle” ta di chuyển xuống với Linear function.
  • Dòng “chúc mừng” đồng thời vừa di chuyển với easing easeOutElastic để tạo quán tính và fade opecity với Linear function.
  • Nút “Ok” thì làm tương tự dòng “chúc mừng” nhưng với hướng di chuyển ngược lại.

2. Hiệu ứng Open chest

Tới đây thì bài viết cũng khá dài rồi nên mình chỉ đưa vào vài demo về open chest animation effect, bạn hãy xem và phân tích xem mình dùng những easing function nào để tạo chuyển động cho nó.

III. Tổng kết

Easing function là các công thức toán học thường dùng trong animation, UI/UX, game, quảng cáo, đồ họa máy tính,… để điều khiển các thức thay đổi tốc độ của các chuyển động từ đó tạo cảm giác chuyển động được tự nhiên, chân thật và mượt mà hơn thay vì chỉ chuyển động tuyến tính từ A đến B.

Sử dụng easing function có thể giúp nâng cao trải nghiệm người dùng, giúp giao diện hoặc game trở nên thân thiện, sinh động và “sống” hơn.

Previous: Tìm hiểu về các Easing Functions (#2) – NCC ANT

Avatar photo

Leave a Reply

Your email address will not be published. Required fields are marked *