พัฒนาเกมลง iOS ด้วย Swift และ SpriteKit ตอนที่ 5 การใช้ SKAction.move
สำหรับบทความตอนนี้จะพูดถึงการใช้ SKAction.move เพื่อที่จะให้ภาพเคลื่อนที่ไปยังจุดต่าง ๆ ที่เรากำหนด
ใน SpriteKit จะมีคลาส SKAction ซึ่งเก็บรวบรวมฟังก์ชันที่ทำหน้าที่ในการเปลี่ยนแปลงโครงสร้างหรือคุณสมบัติของโหนดที่อยู่ในซีน ซึ่งฟังก์ชัน move เป็นหนึ่งในฟังก์ชันที่อยู่ใน SKAction โดยทำหน้าที่ในการทำให้โหนดหรือวัตถุนั้น ๆ เคลื่อนที่ไปยังตำแหน่งใหม่
เรามาเริ่มต้นเรียนรู้การใช้ SKAction.move โดยเริ่มต้นสร้างโปรเจคชื่อ ActionMove ดังรูปที่ 1
รูปที่ 1 สร้างโปรเจค ActionMove
จากนั้นเลือกไฟล์ GameScene.swift เพื่อทำการลบคำสั่งเดิมที่มีมาให้จากการสร้างโปรเจคเริ่มต้น โดยลบคำสั่งออกให้เหลือเฉพาะฟังก์ชัน didMove() ดังรูปที่ 2
รูปที่ 2 ลบคำสั่งใน GameScene.swift
จากนั้นเลือกไฟล์ GameScene.sks เพื่อลบ hellolabel ออก และปรับค่า Anchor Point เป็น (0,0) และปรับขนาดตามต้องการ ตัวอย่างดังรูปที่ 3
รูปที่ 3 ตั้งค่า SpriteKit Scene
จากนั้นโหลดรูป spaceship.png ซึ่งเป็นรูปที่แถมมากับ Xcode ในเวอร์ชันเก่า ๆ ก่อนหน้านี้
จากนั้นนำรูป spaceship.png ไปใส่ในใน Assets.xcassets ดังรูปที่ 4
รูปที่ 4 นำรูป spaceship เข้า Assets
จากนั้นกลับมาที่ไฟล์ GameScene.swift เพื่อเขียนคำสั่งแสดงรูป spaceship.png บนหน้าจอ (เหมือนบทความตอนที่ 4) โดยเพิ่มคำสั่งบรรทัดที่ 14 และบรรทัดที่ 17-20 ดังนี้
// // GameScene.swift // ActionMove // // Created by Aj.Montri on 21/12/2560 BE. // Copyright © 2560 SOFTZOL. All rights reserved. // import SpriteKit import GameplayKit class GameScene: SKScene { var spaceship: SKSpriteNode! override func didMove(to view: SKView) { spaceship = SKSpriteNode(imageNamed: "spaceship") spaceship.position = CGPoint(x: size.width/2, y: size.height/2) spaceship.setScale(0.3) addChild(spaceship) } }
จากข้างต้นบรรทัดที่ 19 : spaceship.setScale(0.3) เป็นคำสั่งสำหรับย่อส่วนภาพให้มีขนาด 0.3 เท่า เนื่องจากถ้าไม่ย่อส่วน ภาพจะมีขนาดเกือบเต็มจอซึ่งใหญ่เกินไป
ต่อไปเราจะทำให้ภาพเคลื่อนที่โดยใช้ SKAction.move
โดยในตัวอย่างเริ่มต้นนี้ขอใช้เป็นการเคลื่อนที่ของภาพ spaceship ไปยังตำแหน่งที่ทัช หรือตำแหน่งที่นิ้วสัมผัสหน้าจอนั่นเอง
ดังนั้นก่อนอื่นเราต้องสร้าง event เพื่อรับเหตุการณ์เมื่อนิ้วเริ่มทัชที่หน้าจอ โดยใช้ overide func touchesBegan(…) และเก็บค่าตำแหน่ง (x,y) ที่นิ้วทัช เพื่อที่จะให้เป็นตำแหน่งปลายทางที่รูปเคลื่อนที่ไป
สำหรับ SKAction.move จะมี 2 รูปแบบหลัก ๆ คือ move…to กับ move…by
แบบแรก move…to มีรูปแบบ ดังนี้
SKAction.move(to: CGPoint, duration: TimeInterval)
ซึ่งพารามิเตอร์ to จะรอรับตำแหน่งปลายทางที่ต้องการไปถึง
ส่วนพารามิเตอร์ duration จะรอรับค่าเวลาที่ใช้สำหรับเคลื่อนที่จากจุดเริ่มต้นไปยังจุดปลายทาง
ซึ่งตัวอย่างการใช้ดังคำสั่งบรรทัดที่ 23-30 ดังนี้
// // GameScene.swift // ActionMove // // Created by Aj.Montri on 21/12/2560 BE. // Copyright © 2560 SOFTZOL. All rights reserved. // import SpriteKit import GameplayKit class GameScene: SKScene { var spaceship: SKSpriteNode! override func didMove(to view: SKView) { spaceship = SKSpriteNode(imageNamed: "spaceship") spaceship.position = CGPoint(x: size.width/2, y: size.height/2) spaceship.setScale(0.3) addChild(spaceship) } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else{ return } let location = touch.location(in: self) let moveAction = SKAction.move(to: location, duration: 1.0) spaceship.run(moveAction) } }
เมื่อรันโปรเจคแล้วให้ลองทัช (หรือคลิ๊ก ถ้ารันบน Simulator) จะพบว่าภาพ space ship จะเคลื่อนที่ไปยังตำแหน่งนั้น ดังรูปที่ 5
รูปที่ 5 ผลจากการรัน
ทีนี้เรามาลองแบบที่ 2 ก็ดูบ้าง นั่นก็คือ move…by ซึ่งการใช้จะมีรูปแบบ ดังนี้
SKAction.move(by: CGVector, duration: TimeInterval)
ตัวอย่างเช่น
let moveByAction = SKAction.move(by: CGVector(dx: 100.0, dy: 200.0), duration: 1.0)
คำสั่งข้างต้น หมายถึง การให้วัตถุขยับแกน x ไป 100 points และขยับแกน y ไป 200 point โดยใช้เวลา 1 วินาที นั่นเอง
จากทั้ง 2 ตัวอย่างข้างต้น จะเห็นว่า การเคลื่อนที่ของภาพ ไม่ว่าจะไกล หรือใกล้ จะใช้เวลาเท่ากัน ทำให้ความเร็วในการเคลื่อนที่ของภาพ ช้าบ้าง เร็วบ้าง ความเร็วไม่คงที่
ดังนั้นหากเราต้องการให้ภาพเคลื่อนที่ด้วยความเร็วคงที่ ไม่ว่าตำแหน่งปลายทางจะใกล้หรือไกล เราจึงต้องคำนวณเวลาก่อน เพื่อให้ภาพเคลื่อนที่ไปยังปลายทางด้วยความเร็วคงที่
หลักการคือ ต้องหาระยะทางจากจุดเริ่มต้นไปยังปลายทาง เราจะใช้ทฤษฎีพีทากอรัส
ระยะทาง = square root ( (x2-x1)^2 + (y2-y1)^2)
จากนั้นหาเวลาได้จาก สมการ
เวลาเดินทาง = ระยะทาง / ความเร็ว
เราสามารถเขียนเป็นคำสั่งได้ ดังนี้
// // GameScene.swift // ActionMove // // Created by Aj.Montri on 21/12/2560 BE. // Copyright © 2560 SOFTZOL. All rights reserved. // import SpriteKit import GameplayKit class GameScene: SKScene { var spaceship: SKSpriteNode! override func didMove(to view: SKView) { spaceship = SKSpriteNode(imageNamed: "spaceship") spaceship.position = CGPoint(x: size.width/2, y: size.height/2) spaceship.setScale(0.3) addChild(spaceship) } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else{ return } let location = touch.location(in: self) let velocity = size.width/2.0 let moveDiff = CGPoint(x: location.x - spaceship.position.x, y: location.y-spaceship.position.y) let distance = sqrt(moveDiff.x * moveDiff.x + moveDiff.y*moveDiff.y) let moveDuration = distance / velocity let moveAction = SKAction.move(to: location, duration: Double(moveDuration)) spaceship.run(moveAction) } }
จากนั้นก็ลองรันดู จะพบว่าภาพเคลื่อนที่ด้วยความเร็วคงที่
วิดีโอประกอบบทความ
Aj.Montri