φυβλαςのβλογ
phyblas的博客



maya python เบื้องต้น บทที่ ๑๓: การทำภาพเคลื่อนไหวต่อเนื่องไปเรื่อยๆ
เขียนเมื่อ 2016/03/10 20:31
แก้ไขล่าสุด 2021/09/28 16:42
ในบทที่แล้วได้เข้าใจวิธีการทำภาพเคลื่อนไหวโดยใช้คีย์เฟรมไปแล้ว แต่จะเห็นว่าหลังจากที่เลยเฟรมสุดท้ายที่มีตั้งคีย์เฟรมไปแล้ว โดยทั่วไปวัตถุจะคงอยู่ในสภาพนั้นต่อไปถ้าเราไม่ได้ตั้งคีย์เฟรมต่อไปแล้ว

หากต้องการทำให้วัตถุมีการเปลี่ยนแปลงต่อไปจะต้องเซ็ตคีย์เฟรมต่อไปเรื่อยๆโดยอาจใช้ whileหรือ for เพื่อการทำซ้ำ

หรืออาจใช้วิธีการตั้งค่าเพื่อให้มีการทำต่อเนื่องไปจากคีย์เฟรมสุดท้ายที่ตั้งไว้โดยอาศัยแนวโน้มที่ต่อเนื่องมา

หากต้องการให้มันมีการเปลี่ยนแปลงค่าต่อไปโดยอาศัยแนวโน้มของคีย์เฟรมที่ตั้งไปแล้วก็สามารถทำได้โดยใช้ฟังก์ชัน setInfinity()



บทนี้ขอเริ่มอธิบายด้วยการยกตัวอย่าง

ลูกแก้วพลัง เป็นท่าไม้ตายหนึ่งของโกคู จากมังงะ (และอนิเมะ) เรื่องดรากอนบอล ในภาษาญี่ปุ่นเรียกว่าเกงกิดามะ (元気玉, genkidama) เป็นท่าที่รวบรวมพลังจากสิ่งรอบข้างมาไว้บนฝ่ามือเพื่อสร้างเป็นลูกบอลแสง ขึ้นมา ยิ่งรวบรวมพลังได้ก็ยิ่งใหญ่ขึ้น แต่ก็ต้องใช้เวลานาน

เราจะใช้มันเป็นตัวอย่างสำหรับบทนี้

เราจะให้ลูกแก้วพลังมีขนาดเริ่มต้นเป็น ๑๐ ซม. แล้วขยายขึ้นวินาทีละประมาณ ๑๐ ซม. หมายความว่าภายใน ๙ วินาทีก็จะขยายขึ้นเป็น ๑ เมตร

ดังนั้นเราต้องสร้างทรงกลมและตั้งคีย์เฟรมให้ขนาดเปลี่ยนไปตามเวลา องค์ประกอบที่กำหนดขนาดของทรงกลมก็คือ r (radius)

และเนื่องจากว่าเมื่อลูกแก้วพลังขยายขึ้นตำแหน่งของมันจะต้องอยู่สูงขึ้นเพื่อ ให้จุดล่างสุดของมันลอยอยู่บนฝ่ามือของโกคูเช่นเดิมตลอด ดังนั้นเราต้องเปลี่ยนตำแหน่งไปด้วย จึงต้องปรับ ty (translationY)

เริ่มแรกลองพิมพ์ตามนี้
genkidama = mc.polySphere(r=10)
mc.setKeyframe(genkidama[1],at='r',v=10,t=1)
mc.setKeyframe(genkidama[1],at='r',v=20,t=26)
mc.setKeyframe(genkidama[0],at='ty',v=10,t=1)
mc.setKeyframe(genkidama[0],at='ty',v=20,t=26)

ก็จะได้ลูกบอลที่เริ่มต้นมีขนาดเป็น 10 ในเฟรมที่ 1 และขนาดเป็น 20 ในเฟรมที่ 26



อนึ่ง ต้องอย่าลืมว่าองค์ประกอบ r เป็นส่วนหนึ่งของโหนดที่กำหนดรูปร่างทรงกลม ในขณะที่โหนด ty นั้นอยู่ในโหนดหลักของวัตถุ ดังนั้นอาร์กิวเมนต์ที่ต้องใส่จึงเป็นชื่อต่างกัน ในที่นี้ใช้ลิสต์ชื่อ genkidama เป็นตัวเก็บค่าของโหนดทั้งสองซึ่งเป็นค่าคืนกลับตอนสร้างทรงกลม

จะเห็นว่าเฟรมหลังจาก 26 ไปทุกอย่างก็จะหยุดนิ่ง ไม่มีอะไรเปลี่ยนแปลงต่อ

จากตรงนี้เราต้องการให้บอลมีการขยายขนาดต่อไปเรื่อยๆโดยใช้ฟังก์ชัน setInfinity()

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

กรณี ต้องการกำหนดรูปแบบหลังคีย์เฟรมสุดท้ายให้ใช้แฟล็ก poi (postInfinite) ส่วนถ้าต้องการกำหนดรูปแบบก่อนคีย์แรกให้ใช้แฟล็ก pri (preInfinite)

โครงสร้าง เป็นดังนี้ mc.setInfinity(ชื่อวัตถุ, at=องค์ประกอบที่ต้องการตั้งค่า ,poi=รูปแบบการเปลี่ยนแปลงหลังคีย์เฟรมสุดท้าย, pri=รูปแบบการเปลี่ยนแปลงก่อนคีย์เฟรมแรก)

ในที่นี้เราจะใช้แค่ poi เพราะคีย์เฟรมแรกเราอยู่ที่คีย์เฟรมที่ 1 อยู่แล้ว ไม่จำเป็นต้องสนใจก่อนหน้านั้น

ส่วนรูปแบบนั้นมีอยู่ ๕ แบบ ประกอบไปด้วย
poi='constant' คงที่ (一定, Constant) คือไม่มีการเปลี่ยนแปลง โดยปกตินี่คือค่าตั้งต้น
poi='linear' เชิงเส้น (リニア, Linear) เปลี่ยนแปลงอย่างคงที่ต่อไปโดยไปตามความชันที่คีย์เฟรมสุดท้าย
poi='cycle' วัฏจักร (サイクル Cycle) วนซ้ำในรูปแบบเดิมไปเรื่อยๆโดยกลับมาเริ่มที่ค่าของคีย์เฟรมแรกใหม่ทุกครั้ง
poi='cycleRelative' วัฏจักรพร้อมปรับแก้ (オフセット付きサイクル, Cycle with Offset) วนซ้ำในรูปแบบเดิมไปเรื่อยๆ โดยค่าเริ่มต้นเปลี่ยนไปทุกครั้งตามความต่างของคีย์เฟรมแรกและคีย์เฟรม สุดท้าย
poi='oscillate' กลับไปกลับมา (折り返し, Oscillate) ถอยหลังกลับจากค่าที่คีย์เฟรมสุดท้ายไปยังคีย์เฟรมแรก แล้วก็วนซ้ำอีกไปเรื่อยๆ

ในที่นี้เราต้องการให้บอลขยายต่อไปเรื่อยๆโดยใช้แนวโน้มจากคีย์สุดท้าย ดังนั้นก็อาจเลือกใช้แบบ linear ดังนั้นพิมพ์ตามนี้
mc.setInfinity(genkidama[1],at='r',poi='linear')

อย่างไรก็ตาม หากพิมพ์เพิ่มลงไปแค่นี้ก็จะพบว่าไม่มีอะไรเปลี่ยนแปลงเกิดขึ้น เพราะอะไร?

ลองมาดูที่ graph editor แล้วคลิกที่กราฟของรัศมีทรงกลมก็จะพบว่า เส้นสัมผัสทั้งด้านซ้ายและขวาชี้ไปในแนวราบ นี่เกิดจากการที่กราฟถูกปรับให้มีความชันเป็น 0 ที่จุดต้นแหละปลาย



ดัง นั้นต้องทำการเปลี่ยนรูปแบบเส้นสัมผัสด้วย โดยคราวนี้จะใช้เป็นแบบ clamped ซึ่งจะคล้ายกับแบบ linear เพียงแต่เส้นสัมผัสเข้าและออกจะถูกปรับให้มีความชันเท่ากัน กราฟจึงดูต่อเนื่อง
mc.keyTangent(genkidama[1],t=(26,26),at='r',ott='clamped',itt='clamped')

พอพิมพ์ไปตามนี้แล้วก็จะเห็นว่ารูปร่างของกราฟเปลี่ยนไป



และลูกแก้วพลังก็จะขยายขนาดขึ้นไปเรื่อยๆไม่มีที่สิ้นสุด

อย่างไรก็ตามจะเห็นว่าเส้นกราฟในช่วงยังไม่ตรง ต้องปรับเส้นสัมผัสขาออกที่คีย์เฟรมแรกให้เป็น clamped ไปด้วย หรือจะเป็น linear ก็ได้
mc.keyTangent(genkidama[1],t=(1,1),at='r',ott='linear')



คราวนี้จะเห็นว่าลูกแก้วพลังขยายไปเรื่อยๆด้วยความเร็วคงที่

นอกจากนี้แล้วก็ยังต้องไปปรับตำแหน่งเพื่อให้ลอยขึ้นเรื่อยๆด้วย ก็สามารถปรับได้โดยทำแบบเดียวกัน

รูปแบบเส้นสัมผัสสามารถกำหนดได้ตั้งแต่ตอนที่ตั้งคีย์เฟรม ดังนั้นลบอันเก่าแล้วสร้างใหม่เลยอาจง่ายกว่า พิมพ์ตามนี้ได้เลย
genkidama = mc.polySphere(r=10)
mc.setKeyframe(genkidama[1],at='r',ott='linear',v=10,t=1)
mc.setKeyframe(genkidama[1],at='r',itt='clamped',ott='clamped',v=20,t=26)
mc.setInfinity(genkidama[1],at='r',poi='linear')
mc.setKeyframe(genkidama[0],at='ty',ott='linear',v=10,t=1)
mc.setKeyframe(genkidama[0],at='ty',itt='clamped',ott='clamped',v=20,t=26)
mc.setInfinity(genkidama[0],at='ty',poi='linear')

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

เพื่อที่จะให้เล่นได้ยาว ๑๐ วินาทีโดยเล่นรอบเดียวหยุด พิมพ์
mc.playbackOptions(min=1,max=250,l='once')

โดยแฟล็ก min (minTime) คือขอบเขตเวลาเริ่มต้น ส่วน max (maxTime)

แฟล็ก l (loop) เป็นตัวกำหนดว่าพอเล่นไปจนสุดแล้วจะเป็นยังไงต่อ มีเลือกได้ ๓ แบบคือ
l='once' เล่นครั้งเดียวจบแล้วหยุด
l='continuous' พอเล่นจนจบก็กลับมาเริ่มที่จุดเริ่มใหม่
l='oscillate' พอเล่นจนจบก็จะเล่นย้อนถอยหลังกลับ

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

ซึ่งตรงนี้เราอาจทำได้ด้วยการตั้งให้เส้นสัมผัสขาเข้าของคีย์เฟรมหลังเป็นมุมติดลบนิดหน่อย
mc.keyTangent(genkidama[1],t=(26,26),at='r',ia=-30)



แล้วตั้ง poi เป็นแบบ cycleRelative
mc.setInfinity(genkidama[1],at='r',poi='cycleRelative')

แบบนี้ก็จะได้บอลที่ขยายแล้วมีการยุบเล็กน้อยเป็นช่วงๆ ดูเป็นธรรมชาติมากขึ้น



นอกจากนี้อาจลองทำให้มันมีการเคลื่อนที่ส่ายไปมาสักหน่อยด้วยอาจดูมีอะไรมากขึ้นมา ลองตั้งการเคลื่อนที่แนวแกน x โดยตั้งคีย์เฟรมสองจุดแล้วตั้ง poi เป็น oscillate
mc.setKeyframe(genkidama[0],at='tx',v=-10,t=1)
mc.setKeyframe(genkidama[0],at='tx',v=10,t=11)
mc.setInfinity(genkidama[0],at='tx',poi='oscillate')

แกน z ก็ให้มีการสั่นไปด้วย แต่ให้คาบต่างกัน เริ่มที่เวลาต่างกัน และเนื่องจากคราวนี้ไม่ได้ตั้งให้เริ่มที่เฟรมที่ 1 ดังนั้นจึงควรตั้ง pri ด้วย
mc.setKeyframe(genkidama[0],at='tz',v=-10,t=6)
mc.setKeyframe(genkidama[0],at='tz',v=10,t=14)
mc.setInfinity(genkidama[0],at='tz',pri='oscillate',poi='oscillate')





สรุปโค้ดทั้งหมดที่ใช้ในการสร้างลูกแก้วพลัง
genkidama = mc.polySphere(r=10)
mc.setKeyframe(genkidama[1],at='r',ott='linear',v=10,t=1)
mc.setKeyframe(genkidama[1],at='r',itt='clamped',ott='clamped',v=20,t=26)
mc.setInfinity(genkidama[1],at='r',poi='cycleRelative')
mc.keyTangent(genkidama[1],t=(26,26),at='r',ia=-30)
mc.setKeyframe(genkidama[0],at='ty',ott='linear',v=10,t=1)
mc.setKeyframe(genkidama[0],at='ty',itt='clamped',ott='clamped',v=20,t=26)
mc.setInfinity(genkidama[0],at='ty',poi='linear')
mc.setKeyframe(genkidama[0],at='tx',v=-10,t=1)
mc.setKeyframe(genkidama[0],at='tx',v=10,t=11)
mc.setInfinity(genkidama[0],at='tx',poi='oscillate')
mc.setKeyframe(genkidama[0],at='tz',v=-10,t=6)
mc.setKeyframe(genkidama[0],at='tz',v=10,t=14)
mc.setInfinity(genkidama[0],at='tz',pri='oscillate',poi='oscillate')

อย่างไรก็ตามการใช้ setInfinity() ก็มีข้อจำกัด นั่นคือเมื่อใช้แล้วจะเป็นการทำซ้ำไปเรื่อยๆโดยที่ไม่สามารถเปลี่ยนแปลงอะไร ได้แล้ว ดังนั้นกรณีที่ทำซ้ำไปสักระยะหนึ่งแล้วต้องการมาเปลี่ยนอะไรเอาตอนหลัง วิธีนี้อาจไม่ค่อยเหมาะสมสักเท่าไหร่

เช่นลูกแก้วพลังนั้น หลังจากที่มันเติบโตจนได้ที่แล้วก็ได้เวลาเอาไปโยนเพื่อโจมตีศัตรู

ดังนั้นต่อไปจะแนะนำวิธีการทำซ้ำอีกแบบ ซึ่งสามารถปรับอะไรได้อย่างอิสระมากกว่า วิธีนั้นก็คือใช้คำสั่ง for เพื่อวนซ้ำ

ลองสร้างใหม่ตามนี้ คราวนี้ใช้ for แทน setInfinity() แล้วก็เพิ่มส่วนท้ายที่สั่งให้พุ่งไปข้างหน้า
genkidama = mc.polySphere(r=10)
for i in range(11):
    t=1+25*i
    mc.setKeyframe(genkidama[1],at='r',v=10+10*i,t=t)
    mc.keyTangent(genkidama[1],t=(t,t),at='r',ia=-30)
    mc.setKeyframe(genkidama[0],at='ty',v=10+10*i,t=t)
for i in range(13):
    mc.setKeyframe(genkidama[0],at='tx',v=-10,t=1+i*20)
    mc.setKeyframe(genkidama[0],at='tx',v=10,t=11+i*20)
for i in range(16):
    mc.setKeyframe(genkidama[0],at='tz',v=-10,t=6+i*16)
    mc.setKeyframe(genkidama[0],at='tz',v=10,t=i*16-2)
mc.setKeyframe(genkidama[0],at='tx',v=1000,t=301)
mc.playbackOptions(min=1,max=300,l='once')

ผลของโค้ดนี้ได้ทำเป็นคลิปเอาไว้





ขอยกอีกตัวอย่าง คือพิจารณาวัตถุที่ร่วงจากที่สูงลงไปถึงพื้นแล้วเด้งขึ้นมาใหม่

สมมุติว่าปล่อยให้วัตถุตกอย่างอิสระโดยไม่มีแรงต้านอากาศ ความสูงจะเป็นไปตามสมการ y=H-0.5*g*t**2

โดย ในที่นี้ H คือความสูงที่ปล่อย g คือค่าคงที่โน้มถ่วง มีค่าประมาณ 9.8 m/s**2 แต่ในที่นี้ขอประมาณเป็น 10 ส่วน t คือเวลาในหน่วยวินาที

จากสูตรนี้ถ้าวัตถุตกลงมาจากที่สูง ๕ เมตรก็จะถึงพื้นใน ๑ วินาที

ระหว่าง นั้นถ้าหากมีความเร็วตั้งต้นในแนวระดับด้วยวัตถุก็จะเคลื่อนที่ในแนวระดับ ด้วยความเร็วคงที่ไปพร้อมกัน x=v*t กลายเป็นการเคลื่อนที่แบบโพรเจ็กไทล์

ลองสร้างวัตถุให้ตกลงจากที่สูง ๕ เมตร โดยมีความเร็วตั้งต้นแนวระดับเป็น ๔ ใช้คำสั่ง for เพื่อตั้งคีย์เฟรมของความสูงจากพื้นทุกเฟรม
mc.polyCylinder(r=1,h=2,n='watthu')
for k in range(1,27):
    t=(k-1.)/25
    mc.setKeyframe('watthu',at='ty',v=6-5*t**2,t=k)
mc.setKeyframe('watthu',at='tx',v=0,t=1,ott='linear')
mc.setKeyframe('watthu',at='tx',v=4,t=26,itt='linear')

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



อนึ่ง ty ในที่นี้บวกครึ่งความสูงของวัตถุไปด้วย เพราะความสูงของวัตถุภายในโปรแกรมวัดจากกึ่งกลาง ไม่ใช่ใต้ก้น ดังนั้นจึง +1

จากนั้นสามารถใช้ setInfinity() เพื่อทำให้วัตถุเด้งกระดอนอย่างต่อเนื่อง

ลองลบแล้วสร้างใหม่
mc.polyCylinder(r=1,h=2,n='watthu')
for k in range(1,27):
    t=(k-1.)/25
    mc.setKeyframe('watthu',at='ty',v=6-5*t**2,t=k)
mc.setKeyframe('watthu',at='tx',v=0,t=1,ott='linear')
mc.setKeyframe('watthu',at='tx',v=4,t=26,itt='clamped',ott='clamped')
mc.setInfinity('watthu',at='tx',poi='linear')
mc.setInfinity('watthu',at='ty',poi='oscillate')





ด้วยการวนซ้ำด้วย for หรือ while บวกกับฟังก์ชัน setInfinity() ตอนนี้เราสามารถสร้างการเคลื่อนที่ในแบบต่างๆได้มากมายหลากหลายพอสมควรแล้ว

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



อ้างอิง

<< บทที่แล้ว      บทถัดไป >>
หน้าสารบัญ


-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> python >> mayapython
-- คอมพิวเตอร์ >> 3D >> maya

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

目录

从日本来的名言
模块
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
机器学习
-- 神经网络
javascript
蒙古语
语言学
maya
概率论
与日本相关的日记
与中国相关的日记
-- 与北京相关的日记
-- 与香港相关的日记
-- 与澳门相关的日记
与台湾相关的日记
与北欧相关的日记
与其他国家相关的日记
qiita
其他日志

按类别分日志



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  查看日志

  推荐日志

ตัวอักษรกรีกและเปรียบเทียบการใช้งานในภาษากรีกโบราณและกรีกสมัยใหม่
ที่มาของอักษรไทยและความเกี่ยวพันกับอักษรอื่นๆในตระกูลอักษรพราหมี
การสร้างแบบจำลองสามมิติเป็นไฟล์ .obj วิธีการอย่างง่ายที่ไม่ว่าใครก็ลองทำได้ทันที
รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
การใช้ unix shell เบื้องต้น ใน linux และ mac
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ