สุดยอดคู่มือภาษาการประมวลผล ตอนที่ 1: ความรู้พื้นฐาน
เผยแพร่แล้ว: 2022-03-11คุณกำลังดิ้นรนกับความเบื่อหน่ายและอยากใช้ความคิดสร้างสรรค์ของคุณ คุณต้องการสร้างบางสิ่ง บางสิ่งที่น่าประทับใจ บางสิ่งที่มีศิลปะ หรือบางทีคุณอาจต้องการเรียนรู้การเขียนโปรแกรมและสร้างความประทับใจให้เร็วที่สุด ถ้าเป็นเช่นนั้น ภาษาการประมวลผลก็เป็นวิธีที่จะไป
ในบรรดาภาษาการเขียนโปรแกรมทั้งหมดที่ฉันเคยทำงานด้วยมาจนถึงตอนนี้ การประมวลผลเป็นหนึ่งในภาษาที่ให้ความบันเทิงมากที่สุดอย่างไม่ต้องสงสัย เป็นภาษาที่ตรงไปตรงมา เรียนรู้ เข้าใจและใช้งานได้ง่าย แต่ทรงพลังมาก มันเกือบจะเหมือนกับว่าคุณกำลังวาดภาพบนผืนผ้าใบที่ว่างเปล่าด้วยบรรทัดของรหัส ไม่มีกฎเกณฑ์หรือแนวทางที่เข้มงวดใดๆ ที่จะจำกัดความคิดสร้างสรรค์ของคุณ ข้อจำกัดเพียงอย่างเดียวคือจินตนาการของคุณ
ในวิทยาลัย ฉันเป็นผู้ช่วยสอนของโครงการที่รวบรวมนักเรียนมัธยมและสอนการประมวลผล ส่วนใหญ่ไม่มีพื้นฐานการเขียนโปรแกรมที่แข็งแกร่ง บางคนไม่เคยเขียนโค้ดแม้แต่บรรทัดเดียวมาก่อน ในเวลาเพียงห้าวัน พวกเขาได้รับการคาดหวังให้เรียนรู้ภาษาและสร้างเกมง่ายๆ ของตนเอง อัตราความสำเร็จเกือบร้อยเปอร์เซ็นต์ เราแทบไม่เคยประสบความล้มเหลว ในบทความนี้ นี่คือสิ่งที่เราจะทำ ฉันย่อโปรแกรมทั้งหมดออกเป็นสองส่วน ส่วนแรกฉันจะพูดถึงภาษา ฉันจะให้ภาพรวมพื้นฐาน คำแนะนำสำหรับการประมวลผล และฉันจะให้เคล็ดลับและลูกเล่นบางอย่าง จากนั้นในตอนต่อไป เราจะสร้างเกมง่ายๆ ทีละขั้นตอน โดยแต่ละขั้นตอนจะอธิบายโดยละเอียด ฉันจะแปลงรหัสของเกมเป็น JavaScript โดยใช้ p5js เพื่อให้เกมของเราสามารถทำงานในเว็บเบราว์เซอร์ได้
สิ่งที่คุณควรรู้อยู่แล้ว
เพื่อให้เข้าใจและติดตามบทความเหล่านี้ได้ง่าย คุณควรมีความรู้พื้นฐานเกี่ยวกับการเขียนโปรแกรม เพราะฉันจะไม่พูดถึงพื้นฐานการเขียนโปรแกรม ส่วนใหญ่ฉันจะไม่แตะต้องแนวคิดการเขียนโปรแกรมขั้นสูงใด ๆ ดังนั้นความเข้าใจเพียงผิวเผินจะทำได้ มีบางส่วนที่ฉันพูดถึงแนวคิดและแนวคิดระดับต่ำ เช่น การเขียนโปรแกรมเชิงวัตถุ (OOP) แต่ก็ไม่สำคัญ สำหรับผู้อ่านที่อยากรู้อยากเห็นซึ่งมีความสนใจในโครงสร้างของภาษา ถ้าไม่อยากทราบ ก็ข้ามส่วนนั้นไปได้เลย นอกจากนั้น สิ่งเดียวที่คุณควรมีคือความทะเยอทะยานที่จะเรียนรู้ภาษาที่ยอดเยี่ยมและความกระตือรือร้นในการสร้างเกมของคุณเอง!
วิธีการติดตาม
ฉันมักจะชอบที่จะเรียนรู้การเขียนโปรแกรมด้วยการลองและทดลอง ยิ่งคุณดำดิ่งสู่เกมของตัวเองเร็วเท่าไหร่ คุณก็จะยิ่งคุ้นเคยกับการประมวลผลเร็วขึ้นเท่านั้น นั่นจะเป็นคำแนะนำแรกของฉัน ลองทำทุกขั้นตอนในสภาพแวดล้อมของคุณเอง การประมวลผลมี IDE ที่ใช้งานง่าย (เช่น ตัวแก้ไขโค้ด) เป็นสิ่งเดียวที่คุณจะต้องดาวน์โหลดและติดตั้งเพื่อติดตาม คุณสามารถดาวน์โหลดได้จากที่นี่
มาเริ่มกันเลย!
ภาษาการประมวลผลคืออะไร?
ส่วนนี้ประกอบด้วยภาพรวมทางเทคนิคโดยย่อของภาษา โครงสร้าง และหมายเหตุบางประการเกี่ยวกับกระบวนการรวบรวมและดำเนินการ รายละเอียดจะรวมถึงความรู้ขั้นสูงเกี่ยวกับการเขียนโปรแกรมและสภาพแวดล้อม Java หากคุณไม่สนใจรายละเอียดในตอนนี้ และรอไม่ไหวที่จะเรียนรู้และเขียนโค้ดเกมของคุณเอง คุณสามารถข้ามไปที่ส่วน "พื้นฐานของการประมวลผล" ได้
การประมวลผลเป็นภาษาการเขียนโปรแกรมแบบภาพที่ช่วยให้คุณสามารถสเก็ตช์ด้วยโค้ดได้ อย่างไรก็ตาม มันไม่ใช่ภาษาการเขียนโปรแกรมเพียงอย่างเดียว แต่เป็นสิ่งที่พวกเขาเรียกว่าภาษาการเขียนโปรแกรม "แบบ Java" ซึ่งหมายความว่าภาษานั้นถูกสร้างขึ้นบนแพลตฟอร์ม Java แต่ไม่ใช่ Java ต่อตัวอย่างแน่นอน มันขึ้นอยู่กับ Java และรหัสทั้งหมดของคุณจะได้รับการประมวลผลล่วงหน้าและแปลงเป็นรหัส Java โดยตรงเมื่อคุณกดปุ่มเรียกใช้ คลาส PApplet ของ Java เป็นคลาสพื้นฐานสำหรับการประมวลผลสเก็ตช์ทั้งหมด ยกตัวอย่าง ลองใช้บล็อคโค้ดประมวลผลพื้นฐานสองสามอัน:
public void setup() { // setup codes goes here } public void draw() { // draw codes goes here }
บล็อคโค้ดเหล่านี้จะถูกแปลงเป็นดังนี้:
public class ExampleFrame extends Frame { public ExampleFrame() { super("Embedded PApplet"); setLayout(new BorderLayout()); PApplet embed = new Embedded(); add(embed, BorderLayout.CENTER); embed.init(); } } public class Embedded extends PApplet { public void setup() { // setup codes goes here } public void draw() { // draw codes goes here } }
คุณจะเห็นว่าบล็อกรหัสการประมวลผลถูกห่อด้วยคลาสที่ขยายจาก PApplet ของ Java ดังนั้น คลาสทั้งหมดที่คุณกำหนดในโค้ดการประมวลผลของคุณ ถ้ามี จะถือว่าเป็นคลาสภายใน
ความจริงที่ว่าการประมวลผลเป็นแบบ Java ทำให้เราได้เปรียบมากมาย โดยเฉพาะอย่างยิ่งหากคุณเป็นนักพัฒนา Java ไม่เพียงแต่ไวยากรณ์ที่คุ้นเคยเท่านั้น แต่ยังช่วยให้คุณสามารถทำสิ่งต่างๆ เช่น การฝังโค้ด Java, ไลบรารี, ไฟล์ JAR ในแบบสเก็ตช์ของคุณ, การใช้แอปเพล็ตการประมวลผลของคุณโดยตรงในแอปพลิเคชัน Java ของคุณ, การกำหนดคลาส และการใช้ประเภทข้อมูลมาตรฐาน เช่น int , ลอย, ถ่านและอื่น ๆ คุณยังสามารถเขียนโค้ด Pocessing ของคุณได้โดยตรงจาก Eclipse หากคุณต้องการใช้เวลาในการตั้งค่า สิ่งหนึ่งที่คุณไม่สามารถทำได้คือใช้ส่วนประกอบ AWT หรือ Swing ในสเก็ตช์การประมวลผลของคุณ เนื่องจากมันขัดแย้งกับลักษณะการวนซ้ำของการประมวลผล แต่อย่ากังวล เราจะไม่ทำสิ่งแฟนซีในบทความนี้
พื้นฐานของการประมวลผล
รหัสการประมวลผลประกอบด้วยสองส่วนหลัก คือ บล็อก การตั้งค่า และบล็อก การวาด บล็อกการตั้งค่าจะทำงานหนึ่งครั้งเมื่อมีการเรียกใช้โค้ด และบล็อกการดึงทำงานอย่างต่อเนื่อง แนวคิดหลักเบื้องหลังการประมวลผลคือ สิ่งที่คุณเขียนภายในบล็อกการวาดจะถูกดำเนินการ 60 ครั้งต่อวินาทีจากบนลงล่าง จนกว่าโปรแกรมของคุณจะยุติ เราจะสร้างทุกอย่างโดยใช้ประโยชน์จากแนวคิดนี้ เราจะทำให้วัตถุของเราเคลื่อนที่ รักษาคะแนน ตรวจจับการชน ใช้แรงโน้มถ่วง และทำทุกอย่างโดยใช้คุณสมบัตินี้ การวนรอบการรีเฟรชนี้เป็นจังหวะการเต้นของหัวใจของโครงการของเรา ฉันจะอธิบายวิธีใช้ฮาร์ทบีตนี้เพื่อทำให้โค้ดของคุณเป็นจริงในตอนต่อไป ขั้นแรก ให้ฉันแนะนำคุณเกี่ยวกับ IDE การประมวลผล
กำลังประมวลผล IDE
หากคุณอ่านมาถึงจุดนี้แล้วและยังไม่ได้ดาวน์โหลด Processing IDE โปรดดำเนินการเลย ในบทความนี้ ฉันจะสรุปงานง่ายๆ ให้คุณลองทำด้วยตัวเอง คุณสามารถฝึกฝนได้ก็ต่อเมื่อคุณมี IDE ทำงานอยู่เท่านั้น ต่อไปนี้คือข้อมูลเบื้องต้นเกี่ยวกับการประมวลผล IDE มันง่ายมากและอธิบายตัวเองได้ ดังนั้นฉันจะพูดให้สั้น
ตามที่คุณคาดหวัง ปุ่มเรียกใช้และหยุดทำตามที่แนะนำ เมื่อคุณคลิกที่ปุ่ม รัน โค้ดของคุณจะได้รับการรวบรวมและดำเนินการ โดยธรรมชาติ โปรแกรมประมวลผลจะไม่มีวันสิ้นสุด โปรแกรมจะทำงานตลอดไปและตลอดไปจนกว่าจะถูกรบกวน คุณสามารถยุติการทำงานได้โดยทางโปรแกรม แต่ถ้าไม่ทำ คุณสามารถใช้ปุ่ม หยุด ได้
ปุ่มที่ดูเหมือนผีเสื้อทางด้านขวาของ run & stop คือตัว ดีบั๊ก การใช้ดีบักเกอร์จำเป็นต้องมีบทความอื่นทั้งหมดโดยเฉพาะ มันอยู่นอกขอบเขตของบทความนี้ ดังนั้นคุณสามารถละเว้นได้ในตอนนี้ ดรอปดาวน์ข้างปุ่มดีบักเกอร์เป็นที่ที่คุณเพิ่ม / ตั้งค่าม็อด ม็อด มีฟังก์ชันบางอย่างให้คุณ ให้คุณเขียนโค้ดสำหรับ Android ให้คุณเขียนโค้ดใน Python และอื่นๆ ได้ ม็อดยังอยู่นอกขอบเขต ดังนั้นคุณสามารถเก็บไว้ในโหมด Java เริ่มต้นและละเว้นมันได้เช่นกัน
หน้าต่างบนโปรแกรมแก้ไขโค้ดเป็นที่ที่สเก็ตช์ของคุณทำงานตามปกติ ในภาพว่างเปล่า เนื่องจากเรายังไม่ได้ตั้งค่าคุณสมบัติใดๆ เช่น ขนาดหรือสีพื้นหลัง หรือเราไม่ได้วาดอะไรเลย
ไม่มีอะไรจะพูดมากเกี่ยวกับตัวแก้ไขโค้ด มันเป็นเพียงที่ที่คุณเขียนโค้ดของคุณ มีหมายเลขบรรทัด (!) การประมวลผลเวอร์ชันเก่าไม่มีสิ่งนั้น และคุณไม่สามารถจินตนาการได้ว่าฉันมีความสุขแค่ไหนเมื่อฉันเห็นพวกเขาครั้งแรก
กล่องดำด้านล่างคือ คอนโซล เราจะใช้มันเพื่อพิมพ์สิ่งต่าง ๆ เพื่อจุดประสงค์ในการดีบักอย่างรวดเร็ว แท็บ ข้อผิดพลาด ถัดจากคอนโซลเป็นที่ที่ข้อผิดพลาดของคุณจะปรากฏขึ้น นี่เป็นคุณสมบัติใหม่ที่มีประโยชน์ซึ่งมาพร้อมกับการประมวลผล 3.0 ในเวอร์ชันเก่า มีการพิมพ์ข้อผิดพลาดไปที่คอนโซลและติดตามได้ยาก
บล็อกการตั้งค่า
ตามที่ระบุไว้ก่อนหน้านี้ บล็อกการตั้งค่าจะทำงานเพียงครั้งเดียวเมื่อโปรแกรมเริ่มทำงาน คุณสามารถใช้เพื่อกำหนดค่าและสำหรับสิ่งที่คุณต้องการเรียกใช้เพียงครั้งเดียว เช่น การโหลดภาพหรือเสียง นี่คือตัวอย่างบล็อกการตั้งค่า เรียกใช้รหัสนี้ในสภาพแวดล้อมของคุณและดูผลลัพธ์ด้วยตัวคุณเอง
public void setup() { // Size of our sketch will be 800x600, // and use the P2D rendering engine. size(800, 600, P2D); // We could have used this function instead of size() // fullScreen(P2D); // The background color of our sketch will be black // by default, unless specified otherwise background(0); // We could have used this to set a background image. // Note that size of our sketch should be the same as the image. // background(loadImage("test.jpg")); // Shapes and objects will be filled with red by default, // unless specified otherwise. fill(255,0,0); // Shaped and objects will have a white border by default, // unless specified otherwise. stroke(255); }
วิธีการที่เกี่ยวข้องกับการจัดสไตล์ (พื้นหลัง การเติม การลากเส้น) จะอธิบายไว้ที่ส่วนคุณสมบัติและการตั้งค่า สำหรับตอนนี้ สิ่งที่คุณต้องรู้คือการตั้งค่าและการกำหนดค่าที่เราตั้งค่าไว้ที่นี่ส่งผลต่อภาพสเก็ตช์ทั้งหมดของเราอย่างไร รหัสที่เขียนที่นี่ใช้เพื่อกำหนดชุดกฎพื้นฐานบางส่วนที่ใช้ได้ตลอดทั้งร่าง สิ่งที่คุณควรเข้าใจในส่วนนี้ด้วยคือวิธีการที่แสดงด้านล่าง:
size() - ตามชื่อที่แนะนำ ฟังก์ชันนี้ใช้เพื่อกำหนดขนาดของภาพสเก็ตช์ของเรา ต้องอยู่ในบรรทัดแรกของบล็อกรหัสการตั้งค่า สามารถใช้ในรูปแบบต่อไปนี้:
- ขนาด(กว้าง,สูง);
- ขนาด(กว้าง สูง เรนเดอร์);
ค่าความกว้างและความสูงสามารถกำหนดเป็นพิกเซลได้ ฟังก์ชัน Size ยอมรับพารามิเตอร์ตัวที่สามคือ renderer ซึ่งใช้เพื่อตั้งค่าเครื่องมือการเรนเดอร์ที่ร่างของเราจะใช้ ตามค่าเริ่มต้น ตัวแสดงภาพจะถูกตั้งค่าเป็น P2D ตัวแสดงภาพที่ใช้ได้คือ P2D (กำลังประมวลผล 2D), P3D (ควรใช้การประมวลผล 3D หากภาพสเก็ตช์ของคุณจะมีกราฟิก 3D) และ PDF (กราฟิก 2D จะถูกวาดลงในไฟล์ PDF ของ Acrobat โดยตรง สามารถดูข้อมูลเพิ่มเติมได้ที่นี่) ตัวแสดงภาพ P2D และ P3D ใช้ฮาร์ดแวร์กราฟิกที่เข้ากันได้กับ OpenGL
fullScreen() - ในการประมวลผล 3.0 ตอนนี้สามารถใช้ฟังก์ชัน fullScreen แทนฟังก์ชัน size() เช่นเดียวกับฟังก์ชัน size() ควรอยู่ในบรรทัดแรกของบล็อกการตั้งค่าเช่นกัน การใช้งานมีดังนี้:
- เต็มจอ();
- เต็มหน้าจอ (แสดงผล);
- เต็มหน้าจอ (เรนเดอร์);
- เต็มหน้าจอ (จอแสดงผล, ตัวแสดงภาพ);
หากคุณใช้งานโดยไม่มีพารามิเตอร์ใดๆ สเก็ตช์การประมวลผลของคุณจะทำงานแบบเต็มหน้าจอและทำงานบนจอแสดงผลหลักของคุณ พารามิเตอร์ 'display' ใช้เพื่อตั้งค่าการแสดงผลสเก็ตช์ของคุณ ตัวอย่างเช่น หากคุณเชื่อมต่อจอภาพภายนอกกับคอมพิวเตอร์ของคุณ คุณสามารถตั้งค่าตัวแปรการแสดงผลเป็น 2 (หรือ 3, 4 เป็นต้น) และสเก็ตช์ของคุณจะทำงานที่นั่น พารามิเตอร์ 'renderer' มีคำอธิบายที่ส่วน size() ด้านบน
บล็อกการตั้งค่า
นี่เป็นอีกหนึ่งคุณลักษณะที่นำมาใช้กับการประมวลผลรุ่นใหม่ มันเป็นบล็อคโค้ด เช่นเดียวกับการตั้งค่าและการวาดภาพ มีประโยชน์เมื่อคุณต้องการกำหนดวิธี size() หรือ fullScreen() ด้วยพารามิเตอร์ตัวแปร นอกจากนี้ยังจำเป็นต้องกำหนด size() และคุณสมบัติการจัดรูปแบบอื่นๆ เช่น smooth() ในบล็อคโค้ดนี้ หากคุณใช้สภาพแวดล้อมอื่นที่ไม่ใช่ IDE ของการประมวลผล เช่น Eclipse แต่คุณไม่จำเป็นต้องใช้ในกรณีส่วนใหญ่ ซึ่งไม่มีในบทความนี้แน่นอน
วาดบล็อค
ไม่มีอะไรพิเศษที่จะพูดถึงบล็อกการวาด แต่ทุกอย่างก็พิเศษเกี่ยวกับมัน บล็อกวาดเป็นที่ที่เวทมนตร์ทั้งหมดเกิดขึ้น เป็นหัวใจของโปรแกรมของคุณ โดยจะเต้น 60 ครั้งต่อวินาที บล็อกรหัสนี้เก็บตรรกะรหัสทั้งหมดของคุณ รูปร่าง วัตถุ ฯลฯ ทั้งหมดของคุณจะถูกเขียนไว้ที่นี่
โค้ดส่วนใหญ่ที่เราจะพูดถึงในบทความนี้จะมาจากบล็อกการวาด ดังนั้นจึงเป็นสิ่งสำคัญที่คุณจะต้องเข้าใจอย่างชัดเจนว่าบล็อกโค้ดนี้ทำงานอย่างไร เพื่อเป็นการสาธิต นี่คือสิ่งที่คุณสามารถลองได้ อันดับแรก เราสามารถพิมพ์อะไรก็ได้ไปยังคอนโซลโดยใช้เมธอด print() หรือ println() วิธีการพิมพ์จะพิมพ์ไปที่คอนโซลเท่านั้น อย่างไรก็ตาม println จะพิมพ์และต่อท้ายบรรทัดใหม่ ดังนั้นแต่ละ println() จะพิมพ์ในแถวแยกกัน
ลองดูที่บล็อกรหัสต่อไปนี้ ขั้นแรก ให้ลองเดาว่าจะพิมพ์อะไรในคอนโซล จากนั้นไปข้างหน้าและลองใช้:
void setup(){ } void draw(){ int x = 0; x += 1; print(x+" "); }
ถ้าคุณเดาว่า “1 2 3 4…” เข้าใจแล้ว! นี่เป็นหนึ่งในความสับสนในการประมวลผล จำบล็อกนี้ซ้ำแล้วซ้ำอีก? เมื่อคุณกำหนดตัวแปรที่นี่ ตัวแปรจะถูกกำหนดในแต่ละลูปซ้ำแล้วซ้ำอีก ในการวนซ้ำแต่ละครั้ง x ถูกตั้งค่าเป็น 0 เพิ่มขึ้น 1 และพิมพ์ไปยังคอนโซล ดังนั้นเราจึงได้ผลลัพธ์ “1 1 1 1…” ตัวอย่างนี้ค่อนข้างชัดเจน แต่อาจทำให้สับสนเมื่อสิ่งต่างๆ ซับซ้อนขึ้นเล็กน้อย
เราไม่ต้องการให้ x ถูกเขียนทับ ดังนั้นเราจะบรรลุสิ่งนี้ได้อย่างไรและได้ผลลัพธ์ “1 2 3 4…” โดยใช้ ตัวแปรโกลบอล นี่ไม่ใช่สิ่งแฟนซี เราเพียงกำหนดตัวแปรภายนอกบล็อกการวาด ดังนั้นจึงไม่ได้กำหนดซ้ำในการวนซ้ำแต่ละครั้ง นอกจากนี้ ขอบเขตของตัวแปรจะสามารถเข้าถึงได้ตลอดทั้งร่าง ดูรหัสด้านล่าง:
int x = 0; void setup(){ } void draw(){ x += 1; print(x+" "); }
คุณอาจจะถามตัวเองว่าตัวแปรที่กำหนดนอกบล็อคของเราทำงานอย่างไร? และทำไมเราถึงไม่ใช้บล็อค setup() เนื่องจากมันถูกดำเนินการเพียงครั้งเดียวในตอนเริ่มต้น คำตอบเกี่ยวข้องกับการเขียนโปรแกรมเชิงวัตถุและขอบเขต หากคุณไม่คุ้นเคย คุณสามารถข้ามย่อหน้านี้ได้ อ้างถึงส่วนที่ฉันอธิบายว่าการประมวลผลโค้ดถูกแปลงเป็น Java อย่างไร จำได้ไหมว่าพวกเขาถูกห่อหุ้มด้วยคลาส Java อย่างไร? ตัวแปรที่เราเขียนนอก set() และ draw() block จะถูกห่อหุ้มด้วย ดังนั้นตัวแปรเหล่านี้จึงถือเป็น ฟิลด์ ของคลาสภายนอกที่ล้อมโค้ดของเรา การใช้ x+=1 เหมือนกับการใช้ this.x+=1 มันยังทำงานเหมือนกันในกรณีของเรา ไม่มีการกำหนดตัวแปรที่เรียกว่า x ในขอบเขตของ draw() และขอบเขตภายนอกถูกค้นหา ซึ่งเป็นขอบเขตของ this และทำไมเราไม่กำหนดตัวแปร x ของเราในส่วนการตั้งค่า () หากเราทำ ขอบเขตที่กำหนด x จะเป็นขอบเขตของฟังก์ชันการตั้งค่า และจะไม่สามารถเข้าถึงได้จากบล็อก draw()
การวาดรูปร่างและข้อความ
ตอนนี้เรารู้วิธีกำหนดค่าภาพร่างของเราโดยใช้บล็อกการตั้งค่า และเรารู้ว่าบล็อกการวาดทำอะไร ดังนั้นจึงเป็นเวลาที่จะได้เห็นภาพและเรียนรู้เกี่ยวกับส่วนที่สนุกสนานของการประมวลผล: วิธีการวาดรูปร่าง
ก่อนที่เราจะเริ่มต้น คุณควรเข้าใจ ระบบพิกัด . ในการประมวลผล คุณจะกำหนดพิกัดของทุกวัตถุที่คุณวาดบนหน้าจอ ระบบพิกัดอยู่ในหน่วยพิกเซล ต้นทาง (เช่น จุดเริ่มต้น) คือมุมบนซ้าย คุณควรให้พิกัดของคุณสัมพันธ์กับจุดนั้น อีกสิ่งหนึ่งที่คุณควรทราบคือ รูปร่างแต่ละรูปมีจุดอ้างอิงต่างกัน ตัวอย่างเช่น rect() มีมุมบนซ้ายเป็นจุดอ้างอิง สำหรับวงรี () เป็นจุดศูนย์กลาง จุดอ้างอิงเหล่านี้สามารถเปลี่ยนได้ด้วยวิธีการต่างๆ เช่น rectMode() และ ellipseMode() ซึ่งผมจะอธิบายในส่วนคุณสมบัติและการตั้งค่า รูปตัวอย่างมีไว้เพื่อช่วยให้คุณเข้าใจได้ดีขึ้น

บทความนี้เป็นภาพรวมพื้นฐานของการประมวลผล ดังนั้นเราจะไม่แตะต้องรูปร่างที่ซับซ้อนใดๆ เช่น จุดยอดหรือรูปร่าง 3 มิติ รูปร่าง 2D พื้นฐานจะมากเกินพอสำหรับเราที่จะสร้างเกมของเราเอง ในรูป คุณสามารถดูตัวอย่างวิธีการวาดรูปร่างได้ รูปร่างแต่ละรูปมีไวยากรณ์ของตัวเองที่จะสร้าง แต่แนวคิดพื้นฐานคือการให้พิกัดหรือขนาดของมัน หรือทั้งสองอย่าง ต่อไปนี้คือรูปทรงบางส่วนที่คุณน่าจะคุ้นเคย (สำหรับค่าทั้งหมดที่ระบุด้านล่าง 'x' และ 'y' หมายถึงพิกัด x และ y เป็นพิกเซล 'w' และ 'h' หมายถึงค่าความกว้างและความสูงในหน่วยพิกเซลด้วย):
point() - จุดง่าย ๆ ต้องการเพียงพิกัดเดียวเท่านั้น การใช้งาน:
- จุด (x, y)
- point(x, y, z) - ในกรณีที่คุณใช้สามมิติ
line() - สำหรับสร้างบรรทัด คุณสามารถสร้างเส้นที่มีจุดเริ่มต้นและจุดสิ้นสุดเท่านั้น การใช้งาน:
- เส้น(x1, y1, x2, y2)
- line(x1, y1, z1, x2, y2, z2) - ในกรณีที่คุณใช้สามมิติ
Triangle() - สำหรับสร้างรูปสามเหลี่ยม การใช้งาน: สามเหลี่ยม (x1, y1, x2, y2, x3, y3)
quad() - สำหรับสร้างรูปสี่เหลี่ยม การใช้งาน: รูปสี่เหลี่ยม (x1, y1, x2, y2, x3, y3, x4, y4)
rect() - สำหรับสร้างสี่เหลี่ยม จุดอ้างอิงอยู่ที่มุมซ้ายบนโดยค่าเริ่มต้น (ดูรูป) นี่คือการใช้งาน:
- ตรง (x, y, w, h)
- rect(x, y, w, h, r) - 'r' หมายถึงรัศมีเป็นพิกเซลเพื่อทำให้มุมโค้งมน
- rect(x, y, w, h, tl, tr, br, bl) - รัศมีสำหรับมุมซ้ายบน ขวาบน ล่างขวา มุมล่างซ้ายตามลำดับ นี่ยังเป็นพิกเซลด้วย
ellipse() - สำหรับสร้างรูปทรงวงรี นอกจากนี้ยังใช้เพื่อสร้างวงกลม ควรให้ค่าความกว้างและความสูงเท่ากัน จุดอ้างอิงสำหรับรูปร่างนี้คือจุดศูนย์กลางโดยค่าเริ่มต้น (ดูรูป) นี่คือการใช้งาน:
- วงรี(x, y, w, h)
arc() - วาดส่วนโค้ง การใช้งาน:
- arc(x, y, w, h, start, stop) - 'start' และ 'stop' ใช้เพื่อกำหนดมุมที่จะเริ่มต้นและหยุดการวาดส่วนโค้ง ค่าอยู่ในหน่วยเรเดียน สามารถใช้ค่าคงที่เช่น “PI, HALF_PI, QUARTER_PI และ TWO_PI”
- arc(x, y, w, h, start, stop, mode) - ตัวแปร 'mode' คือการกำหนดรูปแบบการแสดงผลของส่วนโค้ง (สตริง) ตัวเลือกที่ใช้ได้คือ “OPEN, CHORD, PIE” OPEN จะทำให้ส่วนที่ไม่ได้วาดไม่มีขอบ คอร์ดจะทำให้ส่วนที่ไม่ได้วาดสมบูรณ์ด้วยเส้นขอบ PIE จะทำให้ส่วนโค้งของคุณดูเหมือนแผนภูมิวงกลม
การแสดงข้อความบนหน้าจอคล้ายกับการแสดงรูปร่าง แนวคิดพื้นฐานคือคุณกำหนดพิกัดที่คุณต้องการให้แสดงข้อความของคุณ อย่างไรก็ตาม ยังมีอีกมากในการจัดการข้อความ คุณจะสามารถควบคุมข้อความของคุณได้มากขึ้นหลังจากส่วนคุณสมบัติและการตั้งค่า ซึ่งคุณจะได้เรียนรู้วิธีนำการตั้งค่าและคุณสมบัติไปใช้กับวัตถุ สำหรับตอนนี้ ผมจะแสดงพื้นฐานในการแสดงข้อความ มีหลายวิธีที่จะทำ ฉันจะแสดงเฉพาะสิ่งจำเป็นเท่านั้น
text() - แสดงข้อความ การใช้งาน:
- text(c, x, y) - 'c' หมายถึงอักขระ อักขระที่เป็นตัวอักษรและตัวเลขใดๆ จะปรากฏขึ้น
- text(c, x, y, z) - ในกรณีที่คุณทำงานกับสามมิติ
- text(str, x, y) - 'str' คือสตริงที่จะแสดง
- text(str, x, y, z) - ในกรณีที่คุณทำงานกับสามมิติ
- text(num, x, y) - 'num' คือค่าตัวเลขที่จะแสดง
- text(num, x, y, z) - ในกรณีที่คุณทำงานกับสามมิติ
คุณสมบัติ & การตั้งค่า
สิ่งแรกที่ควรอธิบายในส่วนนี้ก็คือตรรกะเบื้องหลังการตั้งค่าคุณสมบัติของวัตถุ สีเติม สีพื้นหลัง เส้นขอบ ความกว้างของเส้นขอบ สีเส้นขอบ การจัดตำแหน่งของรูปร่าง ลักษณะเส้นขอบ ฯลฯ อาจเป็นตัวอย่างของคุณสมบัติเหล่านี้
เมื่อคุณตั้งค่าคุณสมบัติ คุณต้องจำไว้ว่ารหัสจะทำงานจาก บนลงล่าง สมมติว่าคุณตั้งค่าคุณสมบัติ "เติม" เป็นสีแดง วัตถุทั้งหมดที่อยู่ใต้บรรทัดนั้นจะถูกเติมด้วยสีแดงจนกว่าจะถูกแทนที่ด้วยคุณสมบัติการเติมอื่น สิ่งเดียวกันนี้ใช้กับคุณสมบัติอื่นๆ เช่นกัน อย่างไรก็ตาม โปรดทราบว่าคุณสมบัติทั้งหมดจะไม่เขียนทับซึ่งกันและกัน ตัวอย่างเช่น คุณสมบัติ "stroke" จะไม่เขียนทับคุณสมบัติ "fill" แต่ทำงานร่วมกันได้ นี่คือการแสดงภาพเพื่อให้คุณเข้าใจตรรกะ:
ดังที่คุณเห็นในภาพ บรรทัดแรกกำหนดสีเติมเป็นสีแดง และบรรทัดที่สองตั้งค่าสีเส้นขีดเป็นสีน้ำเงิน ตอนนี้เรามีการตั้งค่าที่ใช้งานอยู่สองแบบ: เติมจังหวะสีแดงและสีน้ำเงิน อย่างที่คุณคาดไว้ ไม่ว่าวัตถุของเราจะเป็นอย่างไรในบรรทัดถัดไป มันจะเต็มไปด้วยสีแดงและมีเส้นสีน้ำเงิน (ถ้ามี) คุณสามารถตรวจสอบภาพด้วยวิธีนี้และคุณจะเข้าใจตรรกะ
ต่อไปนี้คือคุณสมบัติและการตั้งค่าที่จำเป็นบางอย่างที่ใช้กันทั่วไป:
การตั้งค่าสไตล์
fill() - ตั้งค่าสีเติมให้กับวัตถุ การตั้งค่านี้ยังใช้เพื่อระบายสีข้อความ สำหรับตอนนี้ เราต้องรู้การใช้งานต่อไปนี้เท่านั้น:
- เติม(r, g, b) - ค่าสีแดง สีเขียว และสีน้ำเงินเป็นจำนวนเต็ม
- เติม (r, g, b, a) - ค่าอัลฟาเพิ่มเติม สูงสุดคือ255
noFill() - ตั้งค่าสีเติมให้โปร่งใส
stroke() - กำหนดสีเส้นโครงร่างให้กับวัตถุ คุณสมบัติจังหวะใช้ได้กับเส้นและเส้นขอบรอบวัตถุ สำหรับตอนนี้ เราต้องรู้การใช้งานต่อไปนี้เท่านั้น:
- stroke(r, g, b) - ค่าสีแดง สีเขียว และสีน้ำเงินเป็นจำนวนเต็ม
- stroke(r, g, b, a) - ค่าอัลฟาเพิ่มเติม สูงสุดคือ255
noStroke() - ลบจังหวะ
strokeWeight() - กำหนดความกว้างของจังหวะ การใช้งาน:
- strokeWeight(x) - x เป็นจำนวนเต็มและแสดงถึงความกว้างของเส้นขีดเป็นพิกเซล
background() - ตั้งค่าสีพื้นหลัง สำหรับตอนนี้ เราต้องรู้การใช้งานต่อไปนี้เท่านั้น:
- background(r, g, b) - ค่าสีแดง สีเขียว และสีน้ำเงินเป็นจำนวนเต็ม
- background(r, g, b, a) - ค่าอัลฟาเพิ่มเติม สูงสุดคือ255
การตั้งค่าการจัดตำแหน่ง
ellipseMode() - ตั้งค่าตำแหน่งที่จะใช้เป็นจุดอ้างอิงในการจัดตำแหน่งวงรี การใช้งาน:
- ellipseMode(mode) - 'mode' คือพารามิเตอร์ นี่คือพารามิเตอร์ที่มีอยู่:
- CENTER (ค่าเริ่มต้น): ใช้จุดศูนย์กลางเป็นจุดอ้างอิง
- RADIUS: นี่ใช้จุดศูนย์กลางเป็นจุดอ้างอิงเช่นกัน แต่ในโหมดนี้ ค่า w และ h ที่คุณระบุจะถือเป็นครึ่งเดียว (เช่น รัศมีแทนที่จะเป็นเส้นผ่านศูนย์กลาง)
- CORNER: ใช้มุมบนซ้ายเป็นจุดอ้างอิง
- มุม: ตั้งค่าพารามิเตอร์สองตัวแรก (x และ y) เป็นตำแหน่งของมุมบนซ้าย และพารามิเตอร์สองตัวสุดท้าย (w และ h) เป็นตำแหน่งของมุมล่างซ้ายของวงรี ดังนั้นโหมดนี้ "ความกว้าง" และ "ความสูง" จึงไม่เกี่ยวข้อง คิดว่าเป็นวงรี(x_tl,y_tl,x_br,y_br) เหมาะสมกว่าในกรณีนี้
rectMode() - ตั้งค่าตำแหน่งที่จะใช้เป็นจุดอ้างอิงในการจัดตำแหน่งสี่เหลี่ยมผืนผ้า การใช้งาน:
- rectMode(mode) - 'mode' คือพารามิเตอร์ นี่คือพารามิเตอร์ที่ใช้ได้:
- CENTER: ใช้จุดศูนย์กลางเป็นจุดอ้างอิง
- RADIUS: นี่ใช้จุดศูนย์กลางเป็นจุดอ้างอิงเช่นกัน แต่ในโหมดนี้ ค่า w และ h ที่คุณระบุจะถือเป็นครึ่งหนึ่ง
- CORNER (ค่าเริ่มต้น): ใช้มุมบนซ้ายเป็นจุดอ้างอิง
- มุม: ตั้งค่าพารามิเตอร์สองตัวแรก (x และ y) เป็นตำแหน่งของมุมบนซ้าย และพารามิเตอร์สองตัวสุดท้าย (w และ h) เป็นตำแหน่งของมุมล่างซ้ายของวงรี ดังนั้นโหมดนี้ "ความกว้าง" และ "ความสูง" จึงไม่เกี่ยวข้อง คิดว่าเป็น rect(x_tl,y_tl,x_br,y_br) เหมาะสมกว่าในกรณีนี้
การตั้งค่าที่เกี่ยวข้องกับข้อความ
textSize() - กำหนดขนาดตัวอักษรของข้อความ การใช้งาน:
- textSize(size) - ค่าจำนวนเต็มของขนาดที่ต้องการ
textLeading() - กำหนดความสูงของบรรทัดข้อความของคุณ การใช้งาน:
- textLeading(lineheight) - ค่าพิกเซลของช่องว่างระหว่างบรรทัด
textAlign() - ตั้งค่าตำแหน่งที่จะใช้เป็นจุดอ้างอิงการจัดตำแหน่งข้อความ การใช้งาน
- textAlign(alignX) - 'alignX' ใช้สำหรับการจัดตำแหน่งแนวนอน ที่มีจำหน่าย: LEFT, CENTER, RIGHT
- textAlign(alignX, alignY) - 'alignY' ใช้สำหรับการจัดตำแหน่งแนวตั้ง ที่มีจำหน่าย: TOP, BOTTOM, CENTER, BASELINE
แอนิเมชั่น
จนถึงตอนนี้ เราได้เรียนรู้วิธีการวาดวัตถุและข้อความ แต่ปัญหากับพวกมันคือพวกมันคงที่ ตอนนี้เราจะทำให้พวกเขาเคลื่อนไหวได้อย่างไร ง่าย ๆ แทนที่จะให้พิกัดเป็นจำนวนเต็ม เราใช้ตัวแปรเพื่อให้เราสามารถเพิ่ม/ลดค่า ได้ มีเหตุผล? ดูรหัสต่อไปนี้:
// initialize x and y as 0 int x=0; int y=0; void setup(){ size(800,600); background(255); // set background color to white } void draw(){ fill(255,0,0); // fill color red stroke(0,0,255); // stroke color blue ellipseMode(CENTER); // ref. point to ellipse is its center ellipse(x, y, 20, 20); // draw the ellipse // increment x and y x+=5; y+=5; }
คุณเห็นว่าเราจัดการแอนิเมชั่นอย่างไร? เราตั้งค่า x และ y เป็นตัวแปรส่วนกลางและค่าเริ่มต้นเป็น 0 ในการวาดวงรี เราสร้างวงรี ตั้งค่าสีเติมเป็นสีแดง สีเส้นเป็นสีน้ำเงิน และพิกัดเป็น x และ y เมื่อเราเพิ่มค่า x และ y ลูกบอลก็จะเปลี่ยนตำแหน่งของลูกบอล แต่รหัสนี้มีปัญหา คุณสังเกตเห็นไหม เป็นความท้าทายที่ง่ายสำหรับตัวคุณเอง พยายามคิดว่าปัญหาคืออะไร และทดสอบมัน นี่คือผลลัพธ์:
ความตั้งใจของฉันที่จะปล่อยให้สิ่งนี้เกิดขึ้นคือการทำให้คุณรู้ว่าลักษณะการวนซ้ำของการประมวลผลทำงานอย่างไร ดูตัวอย่างในส่วน “Draw Block” คุณจำได้ไหมว่าทำไมเราถึงได้ “1 1 1…” แทนที่จะเป็น “1 2 3…” เหตุผลเดียวกับที่ลูกทิ้งเครื่องหมายไว้เบื้องหลัง ทุกครั้งที่บล็อกการวาดวนซ้ำ x และ y จะเพิ่มขึ้น 5 ดังนั้นลูกบอลจะถูกวาดใหม่เป็น 5 พิกเซลลงและไปทางขวา อย่างไรก็ตาม ลูกบอลถูกดึงจากการวนซ้ำครั้งก่อนยังคงอยู่ในมุมมอง เราจะทำอย่างไรให้พวกเขาหายไป? คาดเดาอะไร?
ในการกำจัดเครื่องหมายที่ลูกบอลทิ้งไว้ เราเพียงแค่ลบพื้นหลัง (255) ออกจากบล็อกการตั้งค่า และวางให้เป็นบรรทัดแรกของบล็อกการจั่ว เมื่อโค้ดพื้นหลังของเราอยู่ในบล็อกการตั้งค่า มันทำงานหนึ่งครั้งในตอนเริ่มต้น ทำให้พื้นหลังของเราเป็นสีขาว แต่นั่นยังไม่พอ เราต้องการมันเพื่อตั้งค่าพื้นหลังของเราเป็นสีขาวในแต่ละลูปเพื่อให้ครอบคลุมลูกบอลที่ดึงมาจากลูปก่อนหน้า พื้นหลังเป็นบรรทัดแรกหมายความว่ารันก่อน จะกลายเป็นเลเยอร์ฐาน ในแต่ละวง ผ้าใบของเราทาสีขาว และองค์ประกอบใหม่จะถูกวาดบนพื้นหลังสีขาว ดังนั้นเราจึงไม่มีเครื่องหมาย
นั่นคือแนวคิดเบื้องหลังการสร้างภาพเคลื่อนไหวในการประมวลผล การจัดการพิกัดของออบเจ็กต์โดยทางโปรแกรมเพื่อเปลี่ยนตำแหน่ง แต่เราจะทำอะไรแฟนซีเช่นเก็บลูกบอลไว้ในหน้าจอได้อย่างไร? หรืออาจจะใช้แรงโน้มถ่วง? ฉันจะสอนวิธีทำสิ่งนี้ในส่วนถัดไปของบทความนี้ เราจะเรียนรู้จากการพยายามสร้าง เราจะเรียนรู้วิธีการทำและนำไปใช้กับเกมของเราทันที ในตอนท้าย เราจะมีเกมที่สมบูรณ์ น่าเล่น และหวังว่าจะสนุก
การโต้ตอบกับแป้นพิมพ์และเมาส์
การโต้ตอบระหว่างแป้นพิมพ์และเมาส์ในการประมวลผลทำได้ง่ายและตรงไปตรงมา มีวิธีที่คุณสามารถเรียกใช้สำหรับแต่ละเหตุการณ์ได้ และสิ่งที่คุณเขียนภายในจะถูกดำเนินการหนึ่งครั้งเมื่อเหตุการณ์เกิดขึ้น นอกจากนี้ยังมีตัวแปรส่วนกลางเช่น mousePressed และ keyPressed ที่คุณสามารถใช้ในบล็อกการวาดของคุณเพื่อใช้ประโยชน์จากการวนซ้ำ นี่คือวิธีการบางส่วนพร้อมคำอธิบาย:
void setup() { size(500, 500); } void draw() { if (mousePressed) { // Codes here will be executed as long as the mouse // button is pressed if (mouseButton == LEFT){ // This lines will be executed as long as // the clicked mouse button is the left mouse // button. } } if (keyPressed) { // Codes here will be executed as long as a key // on the keyboard is pressed if (key == CODED) { // This if statement checks if the pressed key // is recognised by Processing. if (keyCode == ENTER) { // This lines will be executed if the pressed key // is the enter key. } } else{ // This lines will be executed if the pressed key // is not recognised by processing. } } } void mousePressed() { // These codes will be executed once, when mouse // is clicked. Note that mouseButton variable is // also be used here. } void keyPressed() { // These codes will be executed once, when a key // is pressed. Note that key and keyCode variables // are also usable here. }
อย่างที่คุณเห็น เป็นเรื่องง่ายมากที่จะตรวจสอบว่ามีการคลิกเมาส์หรือมีการกดปุ่มใด อย่างไรก็ตาม มีตัวเลือกเพิ่มเติมสำหรับตัวแปร mousePressed และ keyCode ตัวเลือกที่ใช้ได้สำหรับ mousePressed คือ LEFT, RIGHT และ CENTER มีอีกมากมายสำหรับ keyCode ; ขึ้น, ลง, ซ้าย, ขวา, ALT, ควบคุม, SHIFT, BACKSPACE, TAB, ENTER, RETURN, ESC และ DELETE
สิ่งหนึ่งที่ต้องรู้เกี่ยวกับตัวแปรของเมาส์ และเราจะใช้สิ่งนี้เป็นอย่างมาก คือการหาพิกัดของเมาส์ได้อย่างไร เพื่อให้ได้พิกัดที่แน่นอนของเคอร์เซอร์ เราสามารถใช้ตัวแปร mouseX และ mouseY ได้โดยตรงในบล็อก draw() สุดท้ายแต่ไม่ท้ายสุด มีวิธีอื่นๆ ที่เป็นประโยชน์มากมายที่คุณควรพิจารณา ทั้งหมดได้รับการบันทึกไว้ในเอกสารอ้างอิงการประมวลผล
บทสรุป
คุณควรทำความคุ้นเคยกับการประมวลผลในตอนนี้ อย่างไรก็ตาม หากคุณหยุดที่นี่ ความรู้ทั้งหมดนี้จะ หาย ไป ฉันขอแนะนำอย่างยิ่งให้คุณฝึกฝนต่อไป เล่นกับสิ่งที่คุณได้เรียนรู้ เพื่อช่วยให้คุณฝึกฝน ฉันจะให้แบบฝึกหัดสองข้อแก่คุณ คุณควรพยายามทำให้ดีที่สุดด้วยตัวเอง หากคุณติดขัด Google และผู้อ้างอิงการประมวลผลควรเป็นเพื่อนที่ดีที่สุดของคุณ ฉันจะให้รหัสสำหรับรหัสแรก แต่การดูรหัสควรเป็นสิ่งสุดท้ายที่คุณทำ
แบบฝึกหัดแนะนำ 1
คุณควรสร้าง ลูกบอล 4 ลูก ด้วย สีที่ต่างกัน โดยเริ่มจาก มุมทั้ง 4 ของหน้าจอ ที่เคลื่อนผ่านศูนย์กลาง ด้วย ความเร็วต่างกัน เมื่อคุณคลิกปุ่มเมาส์ค้างไว้ ลูกบอลควร หยุดนิ่ง และเมื่อคุณปล่อยเมาส์ ลูกบอลสามารถกลับไปยังตำแหน่งเริ่มต้นและเคลื่อนที่ต่อไปได้ ดังนั้นฉันกำลังมองหาบางอย่างเช่นนี้
หลังจากที่คุณลองทำแบบฝึกหัดเองแล้ว คุณสามารถตรวจสอบรหัสได้ที่นี่
แบบฝึกหัดแนะนำ2
จำสกรีนเซฟเวอร์ DVD ที่มีชื่อเสียงซึ่งโลโก้ DVD เด้งไปมาบนหน้าจอและเราทุกคนต่างรอคอยอย่างสิ้นหวังเพื่อให้มันชนมุม? ฉันต้องการให้คุณจำลองสกรีนเซฟเวอร์นั้นซ้ำ แต่ใช้รูปสี่เหลี่ยมผืนผ้าแทนโลโก้ดีวีดีเท่านั้น เมื่อคุณเริ่มแอป หน้าจอควรเป็นสีดำ และสี่เหลี่ยมผืนผ้าควรเริ่มต้นที่ตำแหน่งแบบสุ่ม ทุกครั้งที่สี่เหลี่ยมผืนผ้ากระทบกับมุม สี่เหลี่ยมผืนผ้าควรเปลี่ยนสี (และทิศทางที่ชัดเจน) เมื่อคุณเลื่อนเมาส์ไปรอบๆ สี่เหลี่ยมผืนผ้าควรหายไป และสีพื้นหลังควรเปลี่ยนเป็นสีขาว (เป็นโปรแกรมรักษาหน้าจอใช่ไหม) ฉันจะไม่ให้รหัสสำหรับแบบฝึกหัดนี้ในบทความนี้ คุณควรพยายามอย่างเต็มที่เพื่อนำไปใช้ และรหัสจะอยู่ในส่วนที่สองของบทความนี้
ส่วนที่สองของคู่มือขั้นสูงสุดสำหรับการประมวลผล ซึ่งเป็นบทช่วยสอนทีละขั้นตอนในการสร้างเกมง่ายๆ ได้รับการเผยแพร่แล้ว
อ่านเพิ่มเติมในบล็อก Toptal Engineering:
- วิธีการเขียนล่ามตั้งแต่เริ่มต้น