Jul 12, 2011
Tuesday, July 12, 2011

เขียนบอทเกมด้วย AutoIt : Bot 4 ค้นหาพิกัดเกมแฟลชในหน้าเว็บ

      ผ่านมา 3 บทกับพื้นฐานทั่วไปในการทำบอท ในบทนี้จะเป็นการแสดงให้เห็นถึง วิธีการนำเอาความรู้ทั้ง 3 บอทมาสร้างเป็นบอทสำหรับแฟลชเกม (Flash Game) ซึ่งเมื่อเข้าใจหลักวิธีทำ ก็สามารถนำไปประยุกต์สร้างบอทเกมแบบอื่นได้ตามต้องการ

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

    ฟังก์ชัน (function) ความหมายตามชื่อคือ ส่วนของชุดคำสั่งชุดหนึ่งหรือกลุ่มหนึ่ง ที่ผู้ใช้ทั่วไปสามารถสร้างขึ้นมา เพื่อใช้ในการคำนวนหาผลลัพธ์ หรือทำงานอย่างใดอย่างหนึ่งตามคำสั่งให้เสร็จสิ้นไป อีกนัยยะหนึ่งของฟังก์ชัน ก็คือชุดคำสั่งย่อยที่ซ้อนอยู่ในคำสั่งหลักนั่นเอง ฟังก์ชันถึงแม้จะเป็นชุดคำสั่งที่อยู่ภายในสคริปต์ AutoIt แต่จะไม่มีวันทำงานหากไม่มีการเรียกใช้งาน การจะเรียกใช้งานฟังก์ชันใดก็ตาม คุณต้องสร้างและประกาศตัวฟังก์ชันในสคริปต์ขึ้นมาเสียก่อน หรืออาจเชื่อมโยงไฟล์ .au3 เข้ามาในสคริปต์ด้วยคำสั่ง #include ก็ได้

     วิธีการสร้างฟังก์ชันใน AutoIt ทำได้ด้วยการ พิมพ์คำสั่ง Func ตามด้วยชื่อของฟังก์ชั่น โดยมีเครื่องหมายวงเล็บเปิดปิด () อยู่ต่อท้าย จากนั้นก็ใส่คำสั่งเข้าไปในบรรทัดถัดไป เมื่อเขียนคำสั่งภายในฟังก์ชัน เสร็จแล้วก็ปิดฟังก์ชันที่สร้างขึ้นด้วย EndFunc โครงสร้างของฟังก์ชั่นก็จะเป็นรูปแบบดังนี้

Func ชื่อฟังก์ชัน() ;หัวของฟังก์ชัน
;คำสั่งที่อยู่ในฟังก์ชัน เพื่อให้ทำงานเมื่อมีการเรียกใช้ (กี่คำสั่งก็ได้)
;คำสั่งที่อยู่ในฟังก์ชัน เพื่อให้ทำงานเมื่อมีการเรียกใช้ (กี่คำสั่งก็ได้)
;คำสั่งที่อยู่ในฟังก์ชัน เพื่อให้ทำงานเมื่อมีการเรียกใช้ (กี่คำสั่งก็ได้)
;คำสั่งที่อยู่ในฟังก์ชัน เพื่อให้ทำงานเมื่อมีการเรียกใช้ (กี่คำสั่งก็ได้)
Return [ค่าที่ส่งกลับ] ;จะใส่หรือไม่ใส่ก็ได้ ขึ้นอยู่ว่าคุณต้องการรับค่าที่ส่งกลับมา เพื่อประมวลผลต่อหรือไม่
EndFunc ;ส่วนท้ายของฟังก์ชัน ประกาศปิด เพื่อให้รู้ว่าจุดสิ้นสุดการทำงานของฟังก์ชันอยู่ที่ไหน


    ทีนี้มาดูตามตัวอย่างการสร้างฟังก์ชั่น ผมจะสร้างฟังก์ชันชื่อ PSsix เมื่อเรียกใช้งาน ฟังก์ชันนี้จะแสดงกล่องข้อความขึ้นมา 3 ครั้ง ฟังก์ชั่น PSsix ที่สมบูรณ์ทำงานได้จะเป็นดังโค้ดด้านล่างนี้

Func PSsix() ;ประกาศสร้างฟังก์ชัน
MsgBox(0,"","สวัสดี 1")  ;คำสั่งที่อยู่ในฟังก์ชัน
MsgBox(0,"","สวัสดี 2") ;คำสั่งที่อยู่ในฟังก์ชัน
MsgBox(0,"","สวัสดี 3") ;คำสั่งที่อยู่ในฟังก์ชัน
EndFunc ;ประกาศปิดฟังก์ชัน

ทีนี้เวลาจะเรียกใช้งานฟังก์ชันที่เขียนขึ้น ก็พิมพ์ชื่อฟังก์ชันดังกล่าวพร้อมกับวงเล็บเปิดปิด () ในสคริปต์ เช่น

PSsix() ;เรียกใช้งานฟังก์ชั่น PSsix


Func PSsix()
MsgBox(0,"","สวัสดี 1")
MsgBox(0,"","สวัสดี 2")
MsgBox(0,"","สวัสดี 3")
EndFunc

ถ้าจะเรียกใช้งาน 2 ครั้งก็พิมพ์ชื่อฟังก์ชัน 2 ครั้ง (ลองเอาไปรันดูครับ จะได้เข้าใจยิ่งขึ้น)

PSsix()
PSsix()



Func PSsix()
MsgBox(0,"","สวัสดี 1")
MsgBox(0,"","สวัสดี 2")
MsgBox(0,"","สวัสดี 3")
EndFunc

การทำบอทเพื่อให้มีการทำงานของฟังก์ชันตลอดเวลา เราจะใส่ฟังก์ชั่นไว้ในลูป While เช่น

While 1
PSsix() ;ฟังก์ชั่น PSsix อยู่ในลูปจะถูกเรียกใช้งานตลอดเวลา
WEnd

Func
PSsix()
MsgBox(0,"","สวัสดี 1")
MsgBox(0,"","สวัสดี 2")
MsgBox(0,"","สวัสดี 3")
EndFunc

     สำหรับการใส่ฟังก์ชันในลูป มีข้อควรระวังไว้อย่างหนึ่งก็คือ ต้องมีการหน่วงเวลาของลูปด้วย (เคยย้ำเรื่องนี้ไปหลายครั้งแล้ว) เพราะการทำงานตลอดเวลาไม่มีการหยุดพัก จะดึงเอาพลัง CPU ออกมามากเกินไป โดยทั่วไปแล้วเพื่อให้ลูปมีการหยุดพักช่วงหนึ่ง เราจะใช้คำสั่ง Sleep คั่นเอาไว้ ก่อนหรือหลังการเรียกฟังก์ชั่น แต่ถ้าหากอยากให้บรรทัดสั้นลงก็เขียนใส่ต่อ While ไปเลยก็ได้ครับ เช่น

While  Sleep(100)
PSsix()
WEnd


Func PSsix()
MsgBox(0,"","สวัสดี 1")
MsgBox(0,"","สวัสดี 2")
MsgBox(0,"","สวัสดี 3")
EndFunc


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

ชื่อฟังก์ชันที่จะเรียกใช้งาน(อากิวเมนท์) ;ส่วนเรียกใช้งาน

Func ชื่อฟังก์ชัน(พารามิเตอร์) ;ส่วนหัวฟังก์ชัน
;ชุดคำสั่งภายในฟังก์ชั่น
;ชุดคำสั่งภายในฟังก์ชั่น
;ชุดคำสั่งภายในฟังก์ชั่น  
EndFunc ;คำสั่งปิดฟังก์ชันต้องใส่ทุกครั้ง

     ผมขอขยายความอากิวเมนท์ (argument) กับ พารามิเตอร์ (parameter) ก่อนจะได้ไม่งงกัน ตามปกติคุณสามารถสร้างฟังก์ชั่นเพื่อทำงานอย่างใดอย่างหนึ่งให้เสร็จสิ้นไป จะใช้วิธีการเรียกฟังก์ชั่นง่ายๆ คือ ชื่อฟังก์ชันตามด้วยเครื่องหมายวงเล็บ () ต่อท้าย เช่น PSsix() ทีนี้หากคุณต้องการส่งค่าบางอย่างเข้าไปในฟังก์ชัน ก็ต้องใส่ค่าดังกล่าวเข้าไปในวงเล็บ เช่น ต้องการส่งค่า 2 คูณ 2 เข้าไปในฟังก์ชั่น PSsix ก็เขียนดังนี้ PSsix(2*2) สิ่งที่ใส่เข้าไปในวงเล็บเรียกกันว่า อากิวเมนท์

     และก็ต้องเข้าใจต่อไปว่า ค่าที่ส่งไปในฟังก์ชันนั้น ต้องสร้างตัวแปรมารองรับ วิธีประกาศตัวแปรก็ประกาศได้ที่ตรงหัวคำสั่ง Func เลยครับ เช่น Func PSsix($text) ตัวแปรนี้จะเรียกกว่า พารามิเตอร์ จะรับค่า 2 คูณ 2 ที่ส่งเข้ามา เพื่อนำไปใช้ในชุดคำสั่งที่อยู่ในฟังก์ชัน เมื่อเขียนออกมาเป็นสคริปต์สมบูรณ์ก็จะได้ดังนี้

PSsix(2*2) ; 2*2 อากิวเมนท์ (argument) ที่ส่งเข้าไปในฟังก์ชั่น

Func PSsix($text) ;$text เป็น พารามิเตอร์ (parameter) คอยรับค่าที่ส่งมา หรือกำหนดค่าที่ส่งเข้ามา
MsgBox(0,"",$text)
EndFunc


ตัวอย่างการรับส่งค่าระหว่างอากิวเมนท์กับพารามิเตอร์ ซึ่งเคยแสดงตัวอย่างไว้ในบทก่อนหน้านี้

Global $GameHD

Func _PSsixLoadH($gtitle) ;พารามิเตอร์ $gtitle รับค่าที่ส่งเข้ามา             
$GameHD = WinGetHandle($gtitle)
If @error Then
MsgBox(4096, "Error", "ไม่พบหน้าต่างเกม")
Else
Return 1
EndIf
EndFunc

If _PSsixLoadH("เปลี่ยนชื่อไตเติลเกม") Then WinActivate($GameHD)
;อากิวเมนท์คือชื่อไตเติลที่ส่งเข้าไป


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


# Recursion นรกสำหรับฟังก์ชันสคริปต์
     ลักษณะการทำงานของฟังก์ชันโดยทั่วไป เมื่อมีการเรียกใช้ ณ จุดใดใดก็ตามของสคริปต์ ตัวแปลคำสั่งจะวิ่งออกจากเส้นทางปกติไปยังฟังก์ชัน ตัวอย่าง

MsgBox(0,"","คำสั่งที่ 1")
PSsix() ; จุดวิ่งออกจากเส้นทางปกติ ไปทำงานในฟังก์ชัน PSsix()
MsgBox(0,"","คำสั่งที่ 2")


Func PSsix() ; ทำงานในฟังก์ชั่นเสร็จแล้วถึงจะกลับไปยังเส้นทางปกติ
MsgBox(0,"ฟังก์ชัน PSsix","สวัสดี 123")
EndFunc

    การเขียนคำสั่งภายในฟังก์ชันนั้น เราสามารถใส่ชื่อฟังก์ชันอื่นเข้าไปในชุดคำสั่ง หรือแม้กระทั่งเรียกตัวของฟังก์ชันที่ทำงานอยู่ได้ด้วย การเขียนเรียกใช้ฟังก์ชันซึ่งซ้อนอยู่ในฟังก์ชันเช่นนี้ มีชื่อเรียกว่า Recursion ตัวอย่าง เช่น

PSsix(1) ;ส่งค่า 1 เข้าไปในฟังก์ชัน


Func PSsix($i)
$i +=1
    ConsoleWrite( $i & @CRLF) ;แสดงผลค่าตัวแปร
PSsix($i) ;เรียกใช้ฟังก์ชันซ้ำ โดยส่งค่าที่มีการบวกหนึ่งเข้าไปในฟังก์ชันใหม่
EndFunc


    ถ้าคุณเอาโค้ดด้านบนนี้ไปรันจะพบกับข้อความแจ้งเตือน AutoIt will quit to prevent stack overflow และหยุดการทำงานของสคริปต์ทันที เนื่องจากสคริปต์ทำงานจนใกล้ถึงขีดจำกัดของ stack (สแต็ก = พื้นที่เก็บข้อมูลชั่วคราวในหน่วยความจำที่โปรแกรมสร้างขึ้น)
     สาเหตุเพราะ ทุกครั้งที่มีการเรียกใช้ฟังก์ชันใน AutoIt  จะต้องมีการทำเครื่องหมายจุด ณ ตำแหน่งบรรทัดคำสั่งที่วิ่งออกไปด้วย โดยเก็บข้อมูลไว้ในสแต็ก เมื่อทำงานในฟังก์ชันเสร็จ จะใช้ข้อมูลในสแต็กเป็นเส้นทางกลับมาทำงานต่อยังตำแหน่งสคริปต์ที่เคยวิ่งออก   ตามตัวอย่างเช่นสคริปต์ด้านล่างนี้  ผมจะใช้ตัวเลขกำกับขั้นตอนการทำงาน เพื่อให้เห็นภาพการวิ่งของฟังก์ชัน ว่ามีการทำงานอย่างไรบ้าง

1. จุดที่วิ่งออกนอกสคริปต์ เมื่อมีการเรียกใช้ฟังก์ชันใสสคริปต์เส้นทางปกติ
2. เข้าไปทำงานในฟังก์ชัน
3. ออกจากฟังก์ชัน กลับไปทำงานต่อจากจุดที่เคยวิ่งออกมา
4. จุดที่กลับไปทำงานต่อในบรรทัดถัดไป ของเส้นการวิ่งสคริปต์ปกติ


MsgBox(0,"","คำสั่งที่ 1")
    PSsix() ; >>>>>>>>> 1
MsgBox(0,"","คำสั่งที่ 2") ; <<<<<<< 4


Func PSsix() ; <<<<<< 2
     MsgBox(0,"ฟังก์ชัน PSsix","สวัสดี 123")
EndFunc ; >>>>>>>>> 3



    ในสคริปต์ด้านบน สแต็กที่ใช้เก็บข้อมูลจุดย้อนกลับ จะล้างข้อมูลในหน่วยความจำออกทันทีที่กลับมาถึงจุดที่ 4 ถ้าคุณอ่านมาถึงตรงนี้แล้วคงพอเข้าใจปัญหาที่เกิดขึ้นนะครับว่าเป็นอะไร ปัญหาที่พบข้อความ AutoIt will quit to prevent stack overflow เพราะการเรียกฟังก์ชันซ้อนในฟังก์ชันนั่นเอง

     การเรียกฟังก์ชันในฟังก์ชัน เช่น ฟังก์ชัน 1 มีคำสั่งเรียกใช้ฟังก์ชัน 2 และในฟังก์ชัน 2 มีการเรียกใช้ฟังก์ชัน 1 (หรือฟังก์ชันอื่น) การทำแบบนี้จะเกิดสภาพไม่สิ้นสุดของการใช้สแต็กในการบันทึกจุดกลับ ซึ่งความไม่สิ้นสุดนี้ มันไปขัดแย้งกับหลักความจริงที่หน่วยความจำของเครื่องคอมพิวเตอร์มีจำนวนจำกัด ดังนั้นก่อนที่หน่วยความจำจะถูกใช้จนหมด AutoIt จะหยุดการทำงานของสคริปต์ไปก่อน เพื่อป้องกันเครื่องค้าง เนื่องจากหน่วยความจำถูกสูบไปหมด คุณจึงเห็นข้อความแจ้งเตือน Stack Overflow ขึ้นมา


  # วิธีการแก้ไขปัญหาเบื้องต้นไม่ให้เกิด stack overflow ก็คือ
- อย่าเรียกใช้ฟังก์ชันซ้อนเข้าไปในฟังก์ชัน โดยไม่จำเป็น
- หากจำเป็นต้องเรียกใช้ฟังก์ชันซ้อนไปในฟังก์ชัน ต้องแน่ใจว่าฟังก์ชันสุดท้ายที่มีการเรียกใช้จะไม่มีเรียกฟังก์ชันอื่นอีก เพื่อให้การทำงานวิ่งกลับมายังจุดที่ออก
- คำสั่ง return จะเป็นตัวยับยั้ง Recursion โดยการสร้างเงื่อนไข เมื่อได้ผลลัพธ์ที่ต้องการ (รายละเอียดจะสอนในคราวต่อไป ถ้าไม่ลืมนะครับ)
- หากต้องการเรียกใช้ฟังก์ชันซ้ำซ้อนควรใช้คำสั่งพวกลูป เช่น While , For, Do แทน
    อย่างไรก็ตามอย่างเพิ่งไปกลัวการเรียกใช้ฟังก์ชันซ้อน เพราะการใช้ Recursion นั้นถือเป็นระบบการสร้าง AI ที่มีประสิทธิภาพมาก (ถ้าใช้เป็น) ซึ่งในบทต่อๆ ไป ผมจะสาธิตวิธีการใช้ให้ (ถ้าไม่ลืมนะครับ)

#สร้างบอทให้แฟลชเกม (Flash Game)
     หลังจากรู้จักฟังก์ชั่นกันแล้ว ต่อไปเป็นเวิร์กช็อปการทำบอทกับเกมแฟลชที่อยู่ในหน้าเว็บ สำหรับการทำบอทกับเกมแฟลชในเวิร์กช็อปนี้ จะไม่จำกัดที่ตัวเบราเซอร์ คุณจะใช้ IE หรือ Firefox หรือ Google Chrome ก็ได้ และก่อนเริ่มเขียนสคริปต์คุณควรใส่คำสั่งฮอตคีย์สำหรับกดปุ่ม ESC  เพื่อกดออกสคริปต์อย่างรวดเร็ว ก๊อปปี้สคริปต์ด้านล่างนี้ไปวางไว้ที่หัวสคริปต์ได้เลยครับ

HotKeySet("{ESC}", "stop") ;ใส่ประกาศไว้ด้านบนสุด

;คำสั่งภายในสคริปต์


Func stop()
Exit
EndFunc

# วิธีการทำบอทเกมแฟลชบนเว็บ
    ในตัวอย่างนี้ผมจะใช้เกมจากลิงก์ http://game.hunsa.com//flashgame.php?gid=2358 ในเริ่มแรกจะแนะนำวิธีการสร้างสคริปต์เพื่ออัปเดทพิกัดเกมในหน้าเว็บก่อน เพราะเกมแฟลชในเว็บการเปิดขึ้นมาพิกัดหน้าต่างเกมจะไม่ตรงกันทุกครั้ง และเนื่องจากบทความบอทถ้าเขียนทั้งหมดจะยาวเกินไป ผมจึงแบ่งส่วนที่เหลือเอาไว้อีกบทความหนึ่ง เมื่อเข้าใจตามนี้ก็มาเริ่มเขียนดูวิธีการทำบอทเกมแฟลชเลยครับ

1. เปิดหน้าเว็บ http://game.hunsa.com//flashgame.php?gid=2358 ขึ้นมาคลิกปุ่ม Start Game เพื่อเริ่มเล่นเกม



2. เลือกการเล่นแบบ Normal มาถึงตรงนี้คุณอาจสงสัยว่าทำไมไม่เขียนสคริปต์ตั้งแต่แรกไปเลย คำตอบก็คือ เสียเวลาครับ เราจะเขียนสคริปต์ตรวจหาหน้าต่างเกมที่มีการเปิดขึ้นมาพร้อมเล่นไปเลย หน้าต่างสำหรับการเลือกวิธีเล่นก็คลิกเอาเอง



3. เมื่อหน้าต่างสำหรับเล่นเกมปรากฏขึ้นมา ตรวจดูอัตราส่วนการแสดงผลด้านล่างด้วยนะครับ ต้องเป็น 100% หากมากหรือน้อยกว่านี้จะทำให้เกิดปัญหาเวลาวัดพิกัดหน้าจอเกม




4. เปิดโปรแกรม AutoIt Window Info ขึ้นมา แล้วทำตามขั้นตอนย่อยดังนี้
[A] ที่ AutoIt Window Info คลิกแท็บ Mouse
[B] ลากเป้าใน Finder Tool ไปวาง ที่ซ้ายบน (ดูรูปขยาย)
[C] สีดำที่มุมซ้ายบนสุดจะได้รหัสสีคือ 0x020504 (มือต้องนิ่งตอนลาก หรือปรับการขยับเม้าส์ให้ช้าลงก็ได้)




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


5. ต่อไปเป็นการเริ่มเขียนสคริปต์ เพื่ออัปเดทพิกัดเกมแฟลช คุณสามารถนำเอาโค้ดด้านล่างนี้ไปปรับเปลี่ยนตามเกมที่คุณเล่นได้เลย ด้านล่างนี้เป็นโค้ดแบบที่ทำเสร็จแล้ว (คุณต้องคลิกไปที่หน้าต่างเกมเอง)

Global $gameX1, $gameY1
;ประกาศตัวแปร $gameX1 และ $gameY2 เพื่อเก็บพิกัด
HotKeySet("{ESC}", "stop")
;สร้างฮอตคีย์ เมื่อกดปุ่ม ESC จะปิดสคริปต์
ShellExecute('http://game.hunsa.com//flashgame.php?gid=2358')
;คำสั่งสำหรับเปิดหน้าต่างเกมแฟลช
While Sleep(100)
;สร้างลูป While เพื่อการทำงานทุก 100 MS (10 ครั้งใน 1 วินาที)
    If WinActive("Rapid Fire2") Then
;ตรวจสอบว่าหน้าต่างเว็บเกมมีการคลิกหรือไม่ เพื่อให้สคริปต์ทำงานเฉพาะตอนที่มีการคลิกหน้าต่างเท่านั้น
        ToolTip("หาหน้าต่างเกม") 
;สร้างทูลทิป เพื่อให้ทราบว่าสคริปต์เริ่มทำงานแล้ว
        FindGame() 
;เรียกใช้งานฟังก์ชัน FindGame() 
    Else 
;ถ้าไม่มีการคลิกที่หน้าต่างเกม จะทำงานในบรรทัดคำสั่งด้านล่างนี้
        ToolTip("") 
;ลบข้อความในทูลทิป
    EndIf
;จบเงื่อนไข
WEnd
;สิ้นสุดการวนลูป While

Func FindGame()
;เริ่มฟังก์ชั่น FindGame()
    $size = WinGetPos("[active]") 
;ดึงเอาขนาดหน้าต่างเกมที่คลิกออกมาเก็บไว้ในตัวแปร $size (ถูกปรับเป็นตัวแปรอาร์เรย์อัตโนมัติ)
    ConsoleWrite("หน้าต่างเกมอยู่ที่ตำแหน่ง: " &$size[0]  & "," & $size[1]& @CRLF)
;แจ้งอัปเดทพิกัดเมื่อเจอในหน้าต่าง SciTE คำสั่งนี้จะลบทิ้งเมื่อเขียนโค้ดเสร็จ
    $coord = PixelSearch($size[0], $size[1], $size[2], $size[3], 0x020504)
;ใช้คำสั่ง PixelSearch ค้นหาสีจากหน้าต่างเกม เปลี่ยนรหัสสีที่คุณต้องการหา
    If IsArray($coord)Then 
;ตั้งเงื่อนไข หากค่าตัวแปร $coord เป็นอาร์เรย์ (คือได้พิกัดสีมาแล้ว) จะทำงานบรรทัดถัดไป
        ConsoleWrite("พิกัด X , Y คือ:" & $coord[0] & "," & $coord[1] & @CRLF) 
;ตั้งให้แสดงพิกัดในหน้าต่ง SciTE  ตัดออกเมื่อทดสอบเสร็จ
        MouseMove($coord[0], $coord[1]) 
;สั่งขยับเม้าส์ไปยังจุดที่เจอสี ตัดออกภายหลังทดสอบเสร็จเช่นกัน
        $gameX1 = $coord[0] 
;โอนค่าพิกัดสีในแกน X เก็บไว้ในตัวแปร $gameX1
        $gameY1 = $coord[1]
;โอนค่าพิกัดสีในแกน Y เก็บไว้ในตัวแปร $gameY1
        Return 1
;ส่งค่า 1 ออกไปจากฟังก์ชัน (ใช้ในการสร้างเงื่อนไข ในบทต่อไป)
    EndIf
EndFunc

Func stop()
    Exit
EndFunc



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


###แถมท้าย
    การหาพิกัดและยืนยันด้วยการสั่งเลื่อนเม้าสไปที่พิกัดนั้น อาจจะทำให้ลำบากในการขยับเม้าส์เปิดปิดหน้าต่าง คุณสามารถปรับเปลี่ยนเป็นสร้างเส้นระบุตำแหน่งเหมือนเป้าปืน (Cross Hair) แทนก็ได้ ด้วยการใช้สคริปต์ด้านล่างนี้  (ถ้าสงสัยในคำสั่งใด ก็คลิกที่ตัวคำสั่งแล้วกดปุ่ม F1 ดูคำอธิบายได้เลยครับ)


#include <WindowsConstants.au3>
#include <WinAPI.au3>


HotKeySet("{ESC}", "stop")
ShellExecute("http://game.hunsa.com//flashgame.php?gid=2358")

Global $gameX1, $gameY2
While Sleep(100)
    If WinActive("Rapid Fire2") Then
        ToolTip("หาหน้าต่างเกม")
        FindGame()
    Else
        ToolTip("")
    EndIf
WEnd

Func FindGame()
    $size = WinGetPos("[active]")
    ConsoleWrite("หน้าต่างเกมอยู่ที่ตำแหน่ง: " &$size[0]  & "," & $size[1]& @CRLF)
    $coord = PixelSearch($size[0], $size[1], $size[2], $size[3], 0x020504)
    If IsArray($coord)Then
        ConsoleWrite("พิกัด X , Y คือ:" & $coord[0] & "," & $coord[1] & @CRLF)
        ShowCross($coord[0], $coord[1], 20, 4, 0x00FF00, 100)
        $gameX1 = $coord[0]
        $gameY2 = $coord[1]
        Return 1
    EndIf
EndFunc


Func stop()
    Exit
EndFunc


Func ShowCross($start_x, $start_y, $length, $width, $color, $time)
    Local $hDC, $hPen, $obj_orig

    $hDC = _WinAPI_GetWindowDC(0) ; DC of entire screen (desktop)
    $hPen = _WinAPI_CreatePen($PS_SOLID, $width, $color)
    $obj_orig = _WinAPI_SelectObject($hDC, $hPen)

    _WinAPI_DrawLine($hDC, $start_x - $length, $start_y, $start_x - 10, $start_y) ; horizontal left
    _WinAPI_DrawLine($hDC, $start_x + $length, $start_y, $start_x + 10, $start_y) ; horizontal right
    _WinAPI_DrawLine($hDC, $start_x, $start_y - $length, $start_x, $start_y -10) ; vertical up
     _WinAPI_MoveTo($hDC, $start_x, $start_y + $length)
    _WinAPI_LineTo($hDC, $start_x, $start_y + 10)

    Sleep($time) ; show cross over screen for defined seconds
     _WinAPI_RedrawWindow(_WinAPI_GetDesktopWindow(), 0, 0, $RDW_INVALIDATE + $RDW_ALLCHILDREN)
    ; clear resources
    _WinAPI_SelectObject($hDC, $obj_orig)
    _WinAPI_DeleteObject($hPen)
    _WinAPI_ReleaseDC(0, $hDC)
EndFunc

###จบแล้วครับ###

2 comments:

  1. เขียนตอนต่อไปด่วน

    ReplyDelete
  2. WinActive นี่คือ ถ้าเกิดเราคลิกที่หน้าต่างเกมส์นี่มันจะทำงาน

    แต่ถ้าเกิดเราพับหน้าจอลง นี่มันจะไม่ทำงานใช่ไหมครับ

    แล้วในวงเลยนี่คือชื่อ Title หรือ Text หรอครับ

    ReplyDelete

    ส่วนนี้สำหรับแสดงความคิดเห็นทั่วไป สอบถามปัญหาตั้งถามได้ที่ฟอรั่ม


>>> [โปรดอ่าน] เนื่องจาก บทความการใช้งานบางโปรแกรมได้โฟสไปนานแล้ว
โปรแกรมอาจมีการอัปเดท วิธีการใช้งาน อาจใช้ไม่ได้ หรือมีวิธีที่ง่ายกว่าในเวอร์ชั่นใหม่
หากคุณพบว่าวิธีการใช้งานไม่ตรงกับบทความในบล็อกนี้ สามารถแนะนำเพิ่มเติมได้ครับ