พัฒนาเกมลง 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