ทำ Animation ให้เจ๋งกว่าเดิม ด้วย Flutter Flame Engine — Ep.1 Sprite Sheet

Amorn Apichattanakul
KBTG Life
Published in
3 min readDec 16, 2022

--

จากประสบการณ์ส่วนตัวแล้ว เวลาเราพูดถึงแอปดีๆ ที่คนชื่นชม ผมจะมองว่าเป็นแอปที่ใช้แล้วสนุก ไม่อารมณ์เสีย!! ไม่ว่าจะเป็นเพราะแอปนั้นช่วยลดความลำบากของเรา ทำให้ชีวิตเราง่ายขึ้น หรือใช้แล้วเพลินดี สนุกดีก็ตาม ซึ่งในบทความนี้ผมจะเล่าว่า ทำยังไงให้แอปของเราใช้แล้วสนุก

เมื่อพูดถึงคำว่าสนุกสำหรับแอปแล้ว เราอาจจะนึกถึงอะไรที่ทำให้เราประทับใจ อะไรที่ทำให้คนใช้แล้วอมยิ้ม หรือน่าจดจำจนเราอยากจะนำไปเล่าต่อ ซึ่งอย่างแรกที่ผมนึกถึงเลยก็คือ Animation จากนั้นผมก็มานั่งนึกๆ แอปประเภทไหนจะมีสิ่งที่เราไล่มาบ้างนะ… คำตอบหนีไม่พ้น เกม แน่นอน ถ้าอยากให้คนสนุก อยากเล่นแอปเรา แบบนี้เราเอาเกมมาใส่ในแอปเลยดีไหมนะ? ด้วยสาเหตุเหล่านี้ผมจึงตัดสินใจไปหาเกมมาลงใน Flutter ดีกว่า แล้วก็ได้มาเจอกับ “Flame Engine” นั้นเอง

อันนี้คือลิงก์ไปหาที่ Lib ครับ

ความเจ๋งของ Lib ตัวนี้คือการที่มันเป็น Game Engine เลย มีสิ่งที่จำเป็นในการทำเกมเข้ามาให้อยู่แล้ว ยกตัวอย่างเช่น

  1. Advanced Animation ด้วย Sprite Sheet
  2. Physics ฟิสิกส์ต่างๆ
  3. Collision Detection การตรวจการชนกันของวัตถุ
  4. Particles
  5. Sound*

*ผมขอข้ามเรื่อง Sound ไป เพราะส่วนตัวแล้วผมไม่ชอบสักเท่าไหร่ 😃 ลองจินตนาการว่าเรากำลังเล่นแอปอยู่ดีๆ ก็มีเสียงที่ไม่ได้คาดไว้โผล่มา กดปุ่มแล้วดังตึ้งๆ น่ารำคาญ ถ้าเป็นแอปที่คนคาดหวังให้มีเสียงอยู่แล้ว เช่น เกม เพลง หนัง พวกนี้โอเคครับ แต่ถ้าต้องมาเจอกับแอปที่เราใช้ทำงาน ได้มีเสียอารมณ์กันแน่นอน 😅

จากข้อมูลด้านบนทำให้ผมแบ่งเรื่องที่จะทำออกมาเป็น 4 บทความ คือ Sprite Sheet, Physics, Collision Detection, และ Particles ประเดิมด้วยบทความนี้ที่เราจะว่ากันด้วยเรื่อง Sprite Sheet

มาเล่น Advanced Animation ด้วย Sprite Sheet

Sprite Sheet คือภาพขนาดใหญ่ที่ประกอบด้วยภาพตัวละครเล็กๆ ทำแอคชั่นต่างๆ วางเรียงกัน อย่างภาพตัวอย่างด้านล่างนี้ เราจะเห็น Animation ของการเดินในแต่ละเฟรม โดยวิธีการคือเราจะสลับรูปแต่ละช่องไปช่องถัดไป ทำแบบนี้ไปเรื่อยๆ ก็จะออกมาเป็น Animation นั่นเอง

Credit

ด้านล่างเป็นตัวอย่าง Sprite Sheet อีกอัน เป็นคอนเซ็ปต์ของการทำ Animation แบบเก่าๆ ที่ใช้มือวาด แล้วมาสลับทีละเฟรมแบบเร็วๆ ซึ่งก็ยังมีการนำมาใช้กับเกมในปัจจุบัน

Credit

สำหรับโปรเจคที่ผมสร้างครั้งนี้ได้แรงบันดาลใจมาจากแอป “Apollo for Reddit” ซึ่งเขาทำให้ตัวแมวหมาวิ่งบน Dynamic Island

เห็นอย่างนั้น ก็มาสิครับ Challenge Accepted เลย

Flutter ต้องทำได้เหมือนกันสิๆๆ เราไม่ยอม ผมจึงนำเจ้า Flutter Flame เนี่ยแหละมาใช้กับเคสนี้ เพื่อที่เราจะได้ไปเต้นๆ บนนั้นกันบ้าง

มาเริ่มกันเลย

ขั้นแรกเรามาลง Lib กันก่อน

dependencies:
flame: ^1.4.0

จริงๆ Flame Widget ก็ใช้กับ Flutter Widget ได้นะ จัดการ Import เข้ามาในโปรเจคเลยครับ

import 'package:flame/game.dart';

จากนั้นใส่ GameWidget แบบนี้เลย เหมือนกับ Widget ธรรมดา

GameWidget(game: SpriteSheetWidget());

ผมลองวัดพื้นที่ Dynamic Island ที่เราจะเล่น Animation ได้ความสูงออกมา 12px ผมจึงนำไปคลุมด้วย SizedBox เพื่อกำหนดขนาด

SizedBox(
width: 12,
height: 12,
child: Align(
alignment: Alignment.topCenter,
child: GameWidget(game: SpriteSheetWidget())))

ส่วนตัว SpriteSheetWidget ก็มาจาก Class ด้านล่างเลยครับ

import 'package:flame/game.dart';
import 'package:flame/input.dart';
import 'package:flame/sprite.dart';
import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flutter/animation.dart';

class SpriteSheetWidget extends FlameGame with TapDetector {
@override
void onTapDown(TapDownInfo info) {
print(info.eventPosition.game);
}
@override
Future<void> onLoad() async {
final spriteSheet = SpriteSheet(
image: await images.load('cat_sprite_long.png'),
srcSize: Vector2(50.0, 50.0),
);
final spriteSize = Vector2(50.0, 50.0);
final animation =
spriteSheet.createAnimation(row: 0, stepTime: 0.1, to: 60);
final component1 = SpriteAnimationComponent(
animation: animation,
scale: Vector2(0.4, 0.4),
position: Vector2(160, -5),
size: spriteSize,
);
add(
component1
..add(
MoveEffect.to(
Vector2(250, -5),
EffectController(
duration: 10,
reverseDuration: 10,
infinite: true,
curve: Curves.linear,
),
),
),
);
}
}

ซึ่ง SpriteSheetWidget จะไป Extend FlameGame และทำ Interface กับ TapDetector ในกรณีที่อยากให้กดได้

หลังจากที่เรา ExtendFlameGame อย่าลืม Override OnLoad นะครับ

ในที่นี่ผมได้โหลด cat_sprite_long.png มาจากอินเทอร์เน็ต และนำมาปรับเปลี่ยนให้กลายเป็นรูปยาวๆ แบบด้านล่าง

ลดขนาดให้เหลือ 50 x 50 ตามขนาดตัวละคร และปรับให้เล่น Animation ทุกๆ 0.1 วินาทีแทน

สุดท้ายก็ได้ผลลัพธ์แบบด้านล่างครับ เล็กเหลือเกิน 😅 พอลองๆ มานั่งเล่น ก็ทำได้แค่เพียงอะไรขำๆ เท่านั้นเองครับ ทำเป็นฟีเจอร์จริงๆ จังๆ คงไม่ไหว พื้นที่น้อยเกินไปที่จะทำอะไร

It’s too small!!

ทั้งนี้ไม่ได้จำกัดแค่ Animation ทั่วไปเท่านั้นนะครับ เราสามารถทำ Animation แบบสลับซับซ้อนได้ ไม่ว่าจะเป็นการ์ตูน สัตว์ หรือแม้กระทั่ง Effect ต่างๆ ก็ได้ครับ ผมลองนั่งหาว่ามี Lib อื่นๆ เล่นได้อีกไหมนะ ก็ไปเจอ Rive มาเหมือนกัน ซึ่งก็เพลินดี แต่จากที่ลองแล้ว รู้สึกว่ามันทำ Animation ได้แค่แบบง่ายๆ ดูไม่สะใจสายเกมอย่างผม ในขณะที่ Sprite Sheet นั้นทำได้ดีกว่าทั้งความสมจริงและความลื่นไหลต่างๆ ครับ หลักๆ ก็ขึ้นอยู่กับความสามารถของดีไซเนอร์ละ ซึ่งนอกจาก 2D Animation แล้ว เรายังทำ 3D Animation 3D ได้อีกด้วย มาดูตัวอย่าง 3D Sprite Sheet กันครับ

Credit

อย่างที่บอกไม่ใช่แค่การ์ตูนเท่านั้น มันทำได้มากกว่านั้นอีก ยกตัวอย่างเช่น Effect ฝนกระเด็น ถ้าฝนมาโดน Widget ของเรา โดยเคสนี้เราจะใช้ Collosion Detection + Sprite Sheet เมื่อพบว่าเกิดการชน Widget ก็ให้เล่น Sprite Sheet ของเราได้ทันที แค่นี้ก็จะดูเหมือนว่าฝนตกมากระเด็นแล้ว เหมือนกับที่เราเห็นกันในแอป Weather ของ iOS ครับ ซึ่งการทำฝนตกใน Flutter App นี่แหละคือเป้าหมายสุดท้ายของซีรีย์ 4 บทความนี้

The particle with Sprite sheet effect

สำหรับใครที่สนใจลองเล่น สามารถดาวน์โหลดตัวอย่างได้ที่ Repo ด้านล่างเลยครับ ผมจะมาอัพเดต Animation เพิ่มขึ้นเรื่อยๆ แต่ตอนนี้มีแต่ Sprite Sheet ไปก่อนนะ

และในบทความหน้า เราจะมาเล่น Physics เพื่อที่เราจะมาทำฝนตกในแอปกันครับ 🆒 🆒 🆒

สำหรับชาวเทคคนไหนที่สนใจเรื่องราวดีๆแบบนี้ หรืออยากเรียนรู้เกี่ยวกับ Product ใหม่ๆ ของ KBTG สามารถติดตามรายละเอียดกันได้ที่เว็บไซต์ www.kbtg.tech

--

--

Amorn Apichattanakul
KBTG Life

Google Developer Expert for Flutter & Dart | Senior Flutter/iOS Software Engineer @ KBTG