ThaiGameDevX - Thai Game Developers eXchange Forums
27 September 2017, 12:14:25 PM *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
News: หากมาครั้งแรก เชิญอ่าน ประกาศเจตนารมณ์ของ ThaiGameDevX และ กติกา ข้อตกลงในการใช้เว็บบอร์ด ครับ
 
   Home   Help Search Calendar Login Register  
Pages: [1] 2   Go Down
  Print  
Author Topic: เกี่ยวกับ Physics สำหรับเกม 2D  (Read 24968 times)
0 Members and 1 Guest are viewing this topic.
chanon
Administrator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +26/-1
Offline Offline

Posts: 738


« on: 04 November 2005, 11:50:30 AM »

ก่อนนอนคืนนี้แวะมาแชร์ความรู้ซักนิด เผื่อจะเป็นประโยชน์ครับ

เรื่องการจำลอง physics ในเกมครับ

สมมติว่าเราต้องการจะจำลองลูกบอล ที่
- มีตำแหน่งเริ่มต้น x, y (0, 100)
- มีความเร็วเริ่มต้น 10 หน่วยต่อวินาที  ไปทางขวา
- และมีความเร่ง -10 ในแกนตั้ง (แรงโน้มถ่วง)
แล้วเราต้องการวาดลูกบอลนี้ให้มันเคลื่อนที่บนหน้าจอ โดยให้ 1 หน่วย = 1 pixel เราจะเขียนสิ่งเหล่านี้ใน code ได้อย่างไร?

อันดับแรก เราต้องเก็บข้อมูลเหล่านี้ให้อยู่ในรูปตัวแปรให้ได้ก่อน

สมมติมีวัตถุหนึ่ง มีตำแหน่งเป็น (x, y) มีความเร็วเป็น (vx, vy) และมีความเร่งเป็น (ax, ay)
เราอาจจะเก็บค่าเหล่านี้ไว้ใน struct POS ที่ประกาศดังนี้:

struct POS {
float x;
float y;
float vx;
float vy;
float ax;
float ay;
}

จากนั้น ในขั้นแรก เราก็ init ค่าเริ่มต้น, ในกรณีตัวอย่างด้านบน อาจเขียนได้เป็น

POS ballPos;  // ประกาศไว้เป็น global var หรือ member var

// ใน function init
ballPos.x = 0;
ballPos.y = 100;
ballPos.vx = 0;
ballPos.vy = 10;
ballPos.ax = 0;
ballPos.ay = -10;

จากนั้น ทุกๆ frame เราก็จะต้อง update ข้อมูลใน ballPos เพื่อให้ตำแหน่งของลูกบอลเปลี่ยนแปลงไปตามเวลา

เราเคยเรียนกันมา ว่า
การเปลี่ยนแปลงตำแหน่ง = ความเร็ว * (ระยะเวลาที่ผ่านไป)
และ
การเปลี่ยนแปลงความเร็ว = ความเร่ง * (ระยะเวลาที่ผ่านไป)

จากตรงนี้เราอาจจะเขียนได้ว่า

ตำแหน่งใหม่ = ตำแหน่งเก่า + การเปลี่ยนแปลงตำแหน่ง
และ
ความเร็วใหม่ = ความเร็วเก่า + การเปลี่ยนแปลงความเร็ว

ถ้าเราเก็บระยะเวลาที่ผ่านไประหว่างการ update แต่ละครั้งไว้ในตัวแปร dt
การ update ตำแหน่งลูกบอลในแต่ละ frame เราก็อาจเขียนได้ง่ายๆ ว่า

ballPos.x = ballPos.x + ballPos.vx * dt
ballPos.y = ballPos.y + ballPos.vy * dt
ballPos.vx = ballPos.vx + ballPos.ax * dt
ballPos.vy = ballPos.vy + ballPos.ay * dt

จากนั้นก็เอาค่า ballPos.x กับ ballPos.y ไปใช้เป็นตำแหน่งในการวาดลูกบอลบนหน้าจอ เพียงเท่านี้ก็เรียบร้อย สามารถจำลองได้แล้ว

การเขียนสมการ update แบบข้างบน เขาเรียกว่า Euler Integration ซึ่งเดิมทีผมนึกว่ามีวิธีนี้วิธีเดียว และนึกว่าเป็นวิธีที่แม่นยำซะอีก

แต่จากการที่ผมได้ศึกษาข้อมูลเพิ่มเติมเรื่องการจำลอง physics ในเกม พบว่า ที่แท้ Euler Integration นั้น มันไม่ใช่วิธีที่ดีหรือแม่นยำเลย จริงๆ มันจะแม่นยำก็ต่อเมือ dt เข้าใกล้ 0 ครับ เหตุผลเพราะอะไร ลองคิดดูเองนะครับ (คือประมาณว่าไอ้การคูณ/บวกกันย่อยๆ หลายๆ ที มันจะออกมาไม่เท่ากับถ้าคูณ/บวกรวดเดียว) หรือจะไปดูที่ wikipedia ก็ได้จะเห็นสมการ Taylor Series  Grin

จริงๆ ความไม่แม่นยำนี้ มันก็ไม่หนักหนาอะไร ถ้าเป็นเกมง่ายๆ เพราะถ้าเห็นภาพการเคลื่อนไว้ที่ได้ ก็ต้องบอกว่ามันก็ดูสมจริงใช้ได้แล้ว ... แต่ในการจำลองระบบ physics ที่ซับซ้อนมากขึ้น error ที่เกิดนี้มันจะสะสมมากขึ้นเรื่อยๆ และจะทำให้ระบบไม่เสถียรได้ครับ

วิธีที่แม่นยำกว่า ที่มีใช้กันในการเขียนเกมนั้น มีอยู่ 2 แบบ คือ Verlet Integration และ Runge-Kutta Integration ซึ่งต่างก็มีข้อดีข้อเสียของมัน .. แต่ในเรื่องความแมนยำ ถือว่าดีทั้งคู่ครับ

แล้วไว้ผมจะมาเขียนต่อแล้วกัน
« Last Edit: 04 November 2005, 12:00:52 PM by chanon » Logged
chanon
Administrator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +26/-1
Offline Offline

Posts: 738


« Reply #1 on: 04 November 2005, 12:40:15 PM »

ขอเขียนต่อเลยแล้วกัน .. พอดีไปอ่านกระทู้นี้ http://forums.thaigamedevx.com/index.php?topic=195.0 เข้า แล้วมันเป็นเรื่องใกล้เคียงกัน

มาต่อที่ Verlet Integration กับ Runge-Kutta Integration
มันคืออะไร ?

คำตอบก็คือ มันคือสมการที่เราจะเอามาใช้เขียนแทน

ballPos.x = ballPos.x + ballPos.vx * dt
ballPos.y = ballPos.y + ballPos.vy * dt
ballPos.vx = ballPos.vx + ballPos.ax * dt
ballPos.vy = ballPos.vy + ballPos.ay * dt

เพื่อให้ผลลัพธ์มันแม่นยำขึ้น

ข้อแตกต่างของ verlet กับ runge-kutta คือ สมการมันจะเป็นคนละตัวกัน

ในส่วนของ runge-kutta นั้น ค่อนข้างตรงไปตรงมา คือจะใช้ ตำแหน่ง, ความเร็ว, ความเร่ง เหมือนเดิม เพียงแต่การคำนวณจะใช้สมการที่ซับซ้อนกว่า ไม่ใช่แค่ x = x + vx * dt แต่จะมีส่วนที่เพิ่มเข้ามาเพื่อให้แม่นยำมากขึ้น ซึ่งสามารถดูสมการได้ที่ http://en.wikipedia.org/wiki/Runge-Kutta หรือ search ใน google ได้เลย - ข้อเสียของ runge-kutta ก็คือ การ implement ระบบ physics จะต้องใช้ระบบ spring เป็นหลัก ซึ่งจะทำให้ระบบเสียเสถียรภาพได้ง่าย ต้องคอยทำการปรับค่า spring co-efficient ให้ดีๆ เพื่อรักษาความเสถียร

วิธีที่ผมคิดว่า work กว่าก็คือ Verlet Integration ซึ่งจะแปลกกว่า Euler Integration และ Runge-Kutta Integration ตรงที่ state ที่เก็บ แทนที่เราจะเก็บ ตำแหน่ง, ความเร็ว และ ความเร่ง ของวัตถุ จะกลายเป็นว่าเก็บ ตำแหน่งปัจจุบัน, ตำแหน่งก่อนหน้านี้ และ ความเร่งแทน จะเห็นว่าตกความเร็วไป ซึ่งมันจะ implicit อยู่ในตำแหน่งปัจจุบัน กับตำแหน่งก่อนหน้านี้ ... ข้อดีของ Verlet Integration คือการ implement constraint ต่างๆ จะง่ายกว่า โดยสามารถ lock ค่าตำแหน่งได้ทันที ไม่ต้องปวดหัวว่าจะ set ค่าความเร็วเท่าไหร่ .. แต่ข้อเสียคือไม่สามารถกำหนดค่าความเร็วได้โดยตรง .. แต่จริงๆ แล้วกำหนดผ่านค่าตำแหน่งก่อนหน้านี้ ก็ไม่ยาก

โดยปกติในการจำลองระบบ physics เพื่อช่วยให้ระบบเสถียรนั้น ค่า dt หรือเวลาที่ผ่านไปในแต่ละครั้งของการ update นั้น ควรจะคงที่ เพราะจะช่วยให้ error จากการคำนวณน้อยที่สุด

ในส่วนของ Verlet Integration หาก dt ไม่คงที่ ก็มีวิธีการปรับสมการเพื่อช่วยรักษาความแม่นยำได้เหมือนกัน แต่หาก dt คงที่จะดีที่สุด

สำหรับสูตรของ Verlet Integration ดูได้ที่ บทความ GameDev อันนี้ ซึ่งมีแสดง graph ความแม่นยำให้ดูด้วย
Logged
chanon
Administrator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +26/-1
Offline Offline

Posts: 738


« Reply #2 on: 04 November 2005, 12:49:04 PM »

ยกสูตร Verlet Integration มาให้ดูซักนิด จาก GameDev:

xi+1 = xi + (xi - xi-1) + a * dt * dt
(มันเป็นสูตรเดียวกับที่ wikipedia แหละ)

นั่นคือ
ตำแหน่งถัดไป = ตำแหน่งปัจจุบัน + (ตำแหน่งปัจจุบัน - ตำแหน่งครั้งก่อน) + ความเร่ง * การเปลี่ยนแปลงเวลา * การเปลี่ยนแปลงเวลา

นั่นคือ struct POS จะเขียนเป็น

struct POS {
float x;
float y;
float ox; // x ครั้งที่แล้ว
float oy; // y ครั้งที่แล้ว
float ax;
float ay;
}

การ update ก็สามารถเขียนเป็น

int tmpx = ballPos.x; // เก็บค่า x,y ไว้เพื่อใส่เป็นค่าเก่า
int tmpy = ballPos.y;
ballPos.x = ballPos.x + (ballPos.x - ballPos.ox) + ballPos.ax * dt * dt;
ballPos.y = ballPos.y + (ballPos.y - ballPos.oy) + ballPos.ay * dt * dt;
ballPos.ox = tmpx;
ballPos.oy = tmpy;

เพียงเท่านี้การ update ก็เรียบร้อย เอา ballPos.x กับ ballPos.y ไปใช้วาดได้
Logged
chanon
Administrator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +26/-1
Offline Offline

Posts: 738


« Reply #3 on: 04 November 2005, 12:58:46 PM »

ผลการคำนวณที่ได้จาก Verlet Integration นั้น จะแม่นยำกว่า Euler Integration มาก ลองดูกราฟเปรียบเทียบสำหรับกรณีที่ dt คงที่ได้ที่หน้านี้: http://www.gamedev.net/reference/programming/features/verlet/page2.asp

(X_exact คือค่าที่แม่นยำ 100%, X_verlet คือ Verlet Integration, TCV คือ Verlet Integration ที่เพิ่มการปรับรักษาความแม่นยำสำหรับกรณี dt ไม่คงที่, X_euler คือกรณีใช้ Euler Integration)

ปัญหาจะเกิดเมื่อค่า dt ไม่คงที่ ลองดูกราฟหน้าถัดไป (http://www.gamedev.net/reference/programming/features/verlet/page3.asp) ปรากฏว่า X_verlet เน่าสนิท (แต่กรณีนี้เป็นกรณีที่เขาใช้ dt แบบ สุ่มเลย ซึ่งเป็น worst case) ในขณะที่ TCV นั้น ดีกว่ามาก แต่ก็แย่กว่ากรณี dt คงที่อยู่ดี .. ก็เรียกว่ายังพอได้

สมการ TCV นั้น เพิ่มขึ้นมาจาก Verlet ธรรมดาคือต้องมีการหารเพิ่มขึ้น (ลองดูในบทความได้)

(ลืมบอกไป คือที่ใช้ float เพราะว่าการจำลองระบบ physics เราต้องการความแมนยำครับ ดังนั้น int เฉยๆ คงไม่ work เท่าไหร่)

ดังนั้นสรุปอีกทีว่า ถ้า dt คงที่ จะดีที่สุด
Logged
chanon
Administrator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +26/-1
Offline Offline

Posts: 738


« Reply #4 on: 04 November 2005, 01:16:04 PM »

(เอ้อ ขอแทรกหน่อย จริงๆ หลักการพวกนี้ ไม่ใช่แค่สำหรับเกม 2D หรอกครับ จริงๆ มันสำหรับเกม 3D มากกว่าด้วยซ้ำ เพราะเกม 3D คงต้องการความแม่นยำมากกว่า ... คนทำเกม Hitman เขาก็เขียนบทความลง Gamasutra เรื่อง Verlet Integration ด้วยครับ ลองอ่านได้ที่ http://www.gamasutra.com/resource_guide/20030121/jacobson_01.shtml)

คราวนี้กลับมาที่กระทู้เรื่อง การเขียน game loop และ fix frame rate กับ var frame rate

อ่านมาถึงตรงนี้ อาจจะคิดว่า fix frame rate สิ work กว่าเห็นๆ ก็ต้องการให้ dt คงที่ไม่ใช่เหรอ

จริงๆ มันก็ได้ครับ .. แต่ข้อเสียคือมันจะทำให้เราวาดภาพเกมได้ไม่ smooth ที่สุดที่เป็นไปได้บนเครื่องนั้นๆ ครับ อย่างเช่นถ้า fix ไว้ที่ 60 fps แต่ถ้าเครื่องมันแรง หน้าจอแสดง refresh rate ได้ 85 Hz (ซึ่งก็ไม่แปลก) แล้ว CPU/การ์ดจอ ก็มีเวลาเหลือ ทำไมไม่เอาให้เต็ม max 85fps ไปเลยล่ะ งี้คนเครื่องแรงก็ใช้เครื่องไม่คุ้มดิ ... คราวนี้ก็บอก อ้าว งั้นก็ fix ที่ 100fps ไปเลยสิ... ปัญหาคือ แล้วถ้าเครื่องช้าแสดงขนาดนั้นไม่ไหวล่ะ อย่างนั้นค่า dt มันก็ไม่คงที่สิ หรือจริงๆ จะ force ให้ dt คงที่ก็ได้ แต่บนเครื่องช้า เกมก็จะรันช้าลง ประมาณว่ามันจะออกมาเป็น slow motion ซึ่งถ้าเป็น network game ก็คงจะมีปัญหา

อ้าวพูดงี้ งั้นจะใช้ var fps เหรอ? แล้วจะทำให้ dt คงที่ทุกรอบได้อย่างไร?

ตกลงจะใช้ var fps หรือ fix fps กันแน่?
Logged
chanon
Administrator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +26/-1
Offline Offline

Posts: 738


« Reply #5 on: 04 November 2005, 01:30:23 PM »

คำตอบก็คือ ใช้ลูกผสมครับ!!! โดยการ fix updates per second แต่ var frame rate .. นั่นหมายความว่ารอบในการ update กับรอบในการวาด จะไมเท่ากัน

เราอาจเลือกให้ update 60 ครั้งต่อวิ
เครื่องที่เร็วๆ ก็จะ update 60 ครั้งต่อวิ และวาด 85 ครั้งต่อวิ
ส่วนเครื่องที่ช้าๆ อาจจะ update 60 ครั้งต่อวิ และวาด 40 ครั้งต่อวิ ก็เป็นได้
เนื่องจากการ update นั้นใช้เวลาน้อยมากๆ อยู่แล้ว (ถ้าเทียบกับการวาด) ดังนั้นเครื่องที่ช้าๆ ก็น่าจะยังรักษาระดับของการ update ต่อวิได้ แต่วาดได้ไม่บ่อยก็ไม่เป็นไร

แล้วเครื่องที่เร็วๆ มันจะวาดไปทำไม 85 ครั้งต่อวิ ถ้า update แค่ 60 ครั้งต่อวิ ? แบบนั้น object ต่างๆ มันก็ยังหยุดอยู่ที่เดิมล่ะสิ วาดซ้ำไปก็ไม่ช่วยไรไม่ใช่เหรอ?

คำตอบก็คือ ในการวาด 85 ครั้งต่อวินั้น ทุกครั้งที่วาด เราจะส่งค่าเวลาที่เป็นเศษเข้าไป หรือไม่ก็ค่าสัดส่วนว่าผ่าน dt ไปเท่าไหร่แล้ว (0.0-1.0) ซึ่ง function วาด ก็จะเอาค่านี้มาใช้ในการ interpolate ตำแหน่ง เพื่อให้การเคลื่อนที่ smooth

เพราะฉะนั้น เครื่องยิ่งเร็วยิ่ง smooth, แต่การ update ค่า dt คงที่เสมอ

ซึ่งการที่ dt คงที่นั้น มีประโยชน์หลายอย่าง นอกจากความแม่นยำของ physics แล้ว มันจะช่วยให้การสร้างระบบบันทึก record/playback การเล่น ง่ายขึ้น, และคิดว่าน่าจะเป็นผลดีกับ network game ด้วย .. การคำนวณของตัวเกมจะมี predictibility มากขึ้น ควบคุมง่ายขึ้น

แล้วที่พูดมานี่จะทำได้ไง? เขียน game loop ยังไง?

ผมก็ไม่เคยรู้มาก่อนเหมือนกัน แต่ไปเจอบทความนี้ครับ:
http://www.gaffer.org/articles/Timestep.html

ลองดู game loop ของเขาดู

ที่ web นี้มีบทความ series เรื่องการ implement ระบบ physics ในเกม แบบใช้ Runge-Katta Integration จนกระทั่งเรื่องของ Network Code ด้วยครับ:
http://www.gaffer.org/articles/

ขอจบเพียงเท่านี้ก่อนนะครับ ราตรีสวัสดิ์ ขอไปนอนก่อน
Logged
yod
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +150/-15
Offline Offline

Posts: 3,240


WWW
« Reply #6 on: 04 November 2005, 06:56:44 PM »

+1 ครับ

เนื้อหามาแต่เนื้อๆ� �;D

ตั้งข้อสังเกต
อืม ลองดู Time-Corrected Verlet

(1)� �xi+1 = xi + (xi - xi-1) * (dti / dti-1) + a * dti * dti

เทียบกับ Original Verlet

(2)� xi+1 = xi + (xi - xi-1) + a * dt * dt

และ basic principles:
(3)� a=dv/dt
(4)� v=dx/dt
(ในหน้า http://www.gamedev.net/reference/programming/features/verlet/)

term
(5)� (xi - xi-1)
จาก (1) และ (2)

term (5) ไม่ผูกพันกับเวลา ดังนั้นเมื่อเวลาแกว่ง ค่าที่ได้จาก (1) จึงไม่ถูกต้อง
ส่วน (2) เพิ่ม term
(6) (dti / dti-1)�
ทำให้ได้ค่าที่ถูกต้องเมื่อเวลาไม่สมำเสมอ

คำเตือน จากจุดนี้ไป เป็นข้อสันนิษฐาน ไม่ยืนยันความถูกต้องนะครับ Cheesy

แต่ (5) และ (6) จะแปลว่าอะไรยังนึกไม่ออก ? dx/dt = v ? ตาม (4) ?
ถ้าเป็นเช่นนั้น (1) จะเขียนอยู่ในรูปง่ายๆ ได้เป็น

(7) xi+1 = xi + vi*ti + ai*ti2


กลับมา พิจารณาฝั่งท่าน Euler ที่โดนโจมตีดูบ้าง

(8.1)� ballPos.x = ballPos.x + ballPos.vx * dt
(8.2)� ballPos.y = ballPos.y + ballPos.vy * dt
(8.3)� ballPos.vx = ballPos.vx + ballPos.ax * dt
(8.4)� ballPos.vy = ballPos.vy + ballPos.ay * dt

จาก (8.1) - (8.4) ดังนั้นสรุปได้คือ
(9)� x = x + v*t
(10)� v =v + a*t

แทนค่า v (10) ใน (9)
(11)� �x = x + (v+a*t)*t
กระจาย
(12)� �x = x + v*t + a*t*t

ลองดู (7) กับ (12) เปรียบเทียบดูครับ

อย่าลืม: โปรดอ่านคำเตือนด้านบน ประกอบ
Logged

..
chanon
Administrator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +26/-1
Offline Offline

Posts: 738


« Reply #7 on: 04 November 2005, 08:41:33 PM »

อืม .. ถึงสมการการคำนวณตำแหน่ง x ของ TCV จะออกมาเหมือนกับ Euler Integration
แต่ประเด็นสำคัญของความแม่นยำอยู่ที่ค่า v ใหม่ด้วยครับ ไม่ใช่แค่ค่า x
Error มันเกิดเพราะ v ไม่คงที่ แล้วเอาไปใช้คำนวณ x เป็นรอบๆ

ถ้าเกิด v คงที่ (a = 0) ผลของสมการ Euler จะถูกต้อง 100% และเท่ากับ Integration แบบอื่นๆ อยู่แล้ว
(อ่านได้ที่ http://www.gaffer.org/articles/Integration.html)
« Last Edit: 04 November 2005, 08:53:31 PM by chanon » Logged
chanon
Administrator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +26/-1
Offline Offline

Posts: 738


« Reply #8 on: 04 November 2005, 09:02:48 PM »

อืมม อีกอย่าง (xi - xi-1) / dti-1 = vi
แต่การแทนที่ใน (11) ตัว v ที่ได้ใน (12) นั้นเป็น vi+1 นะ รู้สึก
« Last Edit: 04 November 2005, 09:06:04 PM by chanon » Logged
diver_duck
Newbie
*

จำนวน ชม/ไม่พอใจ: +6/-2
Offline Offline

Posts: 32


« Reply #9 on: 04 November 2005, 09:16:01 PM »

Idea ดีนะครับ ทำ 60 FPS แต่ถ้าคอมวาดได้มากกว่า 60 อาจจะเป็น 120 FPS
ก็ให้ช่วง Frame 0.5 นั้นทำการเคลื่อนที่วัตถุเฉยๆ โดยไม่มีการคำนวณอย่างอื่น
วิธีนี้ถ้าคอมวาดภาพได้เร็วกว่า FPS ที่ FIX ไว้ก็ยังทำให้การเคลื่อนที่นุ่มนวล

ถึงจอคอมปัจจุบันยัง refresh ที่ 60-85 Mz ก็ตาม แต่เป็นแนวคิดที่ดี
เพราะไม่แน่ว่าอนาคตอันใกล้นี้เราอาจจะเห็นเจอคอมที่มีอัตตรา refresh ที่
120 Mz เป็นมาตราฐานใหม่ๆ และเกมที่ทำได้ต่ำกว่า 120 FPS ก็จะกลายเป็นเกมโบราณ
หรือถ้าจอพัฒนามากกว่านี้จนสามารถ refresh ได้ที่ 1000 Mz เป็นมาตราฐานใหม่
และไม่แน่ว่าอนาคตต่ออาจจะมีจอที่ไม่มีการ refresh อีกต่อไป
แต่จะเป็นจอแบบ Variable Refresh Late แทนก็ได้ใครจะไปรู้
(จอพิเศษที่จะแสดงจุด Pixel ที่มีการเปลี่ยนแปลงทันทีที่ส่งข้อมูลไป เหอะๆๆๆ)

แต่ไม่ว่ายังไงการทำแบบ FIX มันมีข้อดีตรงที่ว่ามันง่ายมาก และหลักการก็เข้าใจง่ายด้วย
ทีนี้ก็ล่อมันซะระดับ nanosec ไปเลย แต่เราจะ FIX เพื่อทำการควบคุมและการพิสูจน์ต่างๆ
นอกเหนือจากการวาดอย่างเดียวที่ความละเอียดกี่ millisec ก็ตามใจเรา
(ความละเอียดที่ว่าอาจจะเป็น 60-100-200 FPS ก็ตามแต่ใจปราถนา)

แต่ส่วนตัวผมแล้วยังยืนยันว่าการวาดภาพมากกว่าอัตราการ Refresh ของจอ ไม่มีความหมายอยู่ดี
เพราะถ้าจอเราเซ็ทที่ 85 FPS แต่วาดภาพได้ 200 FPS มันจะมีประโยชน์อะไรครับ
การสร้างภาพเคลื่อนไหวให้เนียบสุดต้องเท่ากันพอดีกับ Refresh ของจอ
หรือถ้าจะให้เนียบจริงๆก็ต้องวาดตอนช่วงที่จอทำการ Blanking นั่นแหละ

แต่ถ้าเราจะเอาช่วงที่จอ Blanking ให้เนียบก็ต้องเอา WaitForVerticalBlank() มาใช้
และถ้าคุณใช้มันเมื่อไหร่คุณก็จะติด Loop ฟังชั่นของมันทันที
เมื่อคุณติด Loop ฟังชั่นของมัน ต่อให้ใช้วิธี Var FPS ก็ตาม
ก็จะไม่มีวันได้ FPS ที่เกินกว่าอัตราการ Refresh ของจอเลย

แล้วสุดท้ายถึงจะบอกว่า Variable Frame Late เป็นวิธีที่ละเอียดก็ตาม
แต่เวลาจะไม่มีวันละเอียดได้สูงสุดจริง เพราะจะติดที่อัตราการ Refresh จอ
ยิ่งถ้าคุณทำใน Full Screen คุณก็ต้องใช้ Flip() เพื่อสลับฉาก
เมื่อใช้ Flip ก็จะได้ FPS ตามอัตราการ Refresh จอพอดีอีกแหละ
ถ้าคุณปรับจอที่ 85 Mz เวลาที่คิดว่าละเอียดก็จะ Update ที่ 85 ครั้งต่อวินาที

(อย่าหาว่าผมพูดมากเลยนะ เพราะอยากให้เห็นความเป็นจริงไม่ใช่ทฤษฎี)

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

คำพูดข้างบน ผมขอถอน ผมผิด วิธีการของคุณ CHANON สามารถทำให้ภาพเรียบเนียนได้จริง

ขอประทานโทษต่อทุกคนมา ณ โอกาศนี้ ด้วยครับ

* Diver Duck.png (14.43 KB - downloaded 526 times.)
« Last Edit: 14 November 2005, 02:18:35 AM by diver_duck » Logged
yod
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +150/-15
Offline Offline

Posts: 3,240


WWW
« Reply #10 on: 04 November 2005, 10:24:40 PM »

ตอนแรกว่าจะ no comment เรื่อง fix/var fps
มันเรื่องความเห็นส่วนตัวคุยกันเหนื่อยเปล่า

แต่มาตามหลักวิทยาศาสตร์ต้องทดลอง ลองวัดกันดูหน่อยมั้ยครับ� Grin

คำนวน particle สัก n ชิ้น หลายๆ แบบ (n = 1, 1K = 1000, 1M = 1 ล้าน) แล้วก็
1. วาดได้เท่าไหร่ pump ลงจอ (var fps)
2. วาดได้เท่าไหร่ คำนวนตาม loop ให้ครบก่อน� ค่อยวาด (fix fps)
2.1 ปรับคำนวนที่ 60 fps
2.2 ปรับคำนวนที่ 1000 fps
3. ผสมแบบที่คุณชานนท์เสนอ

ดูสิว่า fps actual ที่วาดลงไปจริงๆ ระหว่าง 2.1 กับ 2.2 จะ drop ลงเท่าไหร่ครับ
วัดแบบ vsync กับ no wait vsync

เอากราฟมาดูจะได้ให้มันรู้กันไปเลย� Cheesy

เอ่อ ส่วน (xi - xi-1) * (dti / dti-1)
v =� (xi - xi-1)� /� ( dti-1)
ตรง (xi - xi-1) / dti-1 น่าจะคือ vi หรือ vi-1� หรือ vอะไรสักอย่าง ? ไม่แน่ใจครับ
ใครจบ major math ขอความเห็นหน่อยครับ
 Cheesy

ปล. เอ่อ ดูเหมือนคุณ diver duck โจมตีผิดประเด็นไปนิดนะครับ คนที่ใช้ var fps ละในฐานที่เข้าใจอยู่แล้วครับว่า "no wait vsync" และ "เอาเร็วเข้าว่า" แต่อาจจะปล่อยเป็น default ให้ผู้ใช้เลือกได้ว่าจะ wait หรือไม่ (ผ่านทาง driver) ซึ่ง wait ไม่ wait ก็ไม่ต้องแก้โค๊ด
« Last Edit: 04 November 2005, 10:30:53 PM by yod » Logged

..
diver_duck
Newbie
*

จำนวน ชม/ไม่พอใจ: +6/-2
Offline Offline

Posts: 32


« Reply #11 on: 04 November 2005, 11:03:53 PM »

เอ่อ ขอโทษด้วยครับ จริงๆไม่ได้ตั้งใจโจมตีนะครับ แค่อยากสะกิดนิดหน่อยว่า
ตรง ความคิดเห็นของคุณ chanon ที่ 6 น่ะครับ ตามทฤษฎีจะเป็นแบบที่คุณ chanon พูด
แต่การทำงานจริงๆของคอมมันจะเป็นแบบนี้นะ เดี๋ยวจะคิดเลยเถิดไปไกล
แล้วเราจะพลาดไม่ได้เห็นในสิ่งที่เราคิดว่าเราเห็นตามทฤษฎี

ส่วนด้านล่างๆลงมา ผมไม่ขอออกความคิดเห็น (โง่ในส่วนนั้น)
ขอโทษทีครับ
« Last Edit: 04 November 2005, 11:07:55 PM by diver_duck » Logged
yod
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +150/-15
Offline Offline

Posts: 3,240


WWW
« Reply #12 on: 04 November 2005, 11:59:22 PM »

อ่าว อ่ะ ไม่ต้องซีเรียสขนาดนั้นหรอกครับ Cheesy
บอร์ด TGDX สู้กันด้วยวิชาการ อ้างกันตามหลักเหตุผล

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

ไม่อย่างนั้นความรู้ที่มีอยู่ก็เข้าใจผิดกันไปเรื่อยๆ ไม่ดีครับ

เช่น กรณี gettickcount, timegettime, queryperformance แบบนี้ครับ ดีเลย เห็นๆ
ถ้าผมผิดเองผมก็ยอมรับผิดครับ 555 ซึ่งกรณีอันนี้ยอบรับว่าผมผิดเองนะ
พอผิดแล้วก็ทำให้ดู ดูแล้วน้องๆ ที่ยังไม่เคยทราบมาก่อน ก็จะได้เห็นกันชัดๆ ไงครับ เห็นทั้งวิธีคิด วิธีทำ เหตุผล แล้วก็เข้าใจ� Wink

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

ถกเถียง โต้แย้งกันแบบนี้ เพื่อความก้าวหน้าของคนไทยครับ

วันนี้ผมสวมหมวก var fps เต็มที่ สักพักปีหน้าผมอาจแกล้งๆ สวมหมวก fix fps เต็มที่ มาลองสู้กับคนใช้ var fps ก็ได้นะ�� Tongue

มาแบบนี้ไม่สนุกเลย� Grin

ก็จะบอกว่าที่ผมใช้จริงๆ ก็คือตามสถานการณ์ครับ
ส่วนใหญ่จะเป็น var fps แต่ถ้า การวาดภาพและส่วนคำนวนมันกระโดดมากๆ (fps น้อยๆ) ช่วงห่างมันเยอะ ก็ต้องใส่ loop ข้างในสัก� 2-3 loop แล้วค่อยวาด ก็เท่ากับ fix fps ที่คุณ diver_duck เสนอมาครับ

ถ้า fps < 20 ก็เพิ่ม loop ทุกๆ 5 (fix fps)
ถ้า fps >= 20 ก็ไม่ทำอะไรปล่อยไปจนถึง 500 fps (var fps)
ถ้า fps >500 ให้ lock อยู่ที่ 500

ส่วนใหญ่ถ้าขี้เกียจก็ ทำแค่ 2 ข้อแรก� หรือไม่ก็ ข้อ 2 ข้อเดียว

ซึ่ง .. ถ้าคุยกันอีกสักพักนึง วันสองวันใน TGDX ก็น่าจะได้ข้อสรุปนี้นะ หุหุ� Tongue
กฏพวกนี้แล้วแต่ใครจะใช้อะไรครับ ไม่ผิดหรอกนะ ทำอะไรๆ ก็มีข้อยกเว้น

ก็มีเพิ่มเติมสักเล็กน้อย สำหรับ fps เยอะๆ ดียังไง
1. กรณี ต้องแสดงผล ที่มีมากกว่า 1 หน้าจอ เช่น flight sim (มัวรอ vsync 3 หน้าจอ ตายแน่ๆ .. รอก็คงได้แต่ต้องปรับ loop ใหม่)
2. กรณี ทำ off screen สำหรับคำนวน (vsync ไม่เกี่ยว)
3. กรณี ใช้ GPU ช่วยคำนวน (ยิ่งเยอะยิ่งคำนวนได้มาก ต้องแยกนิดนึงระหว่างการแสดงผลในหน้าจอ กับหน่วยความจำของการด์จอนะครับ)

ใครไม่เห็นด้วยเชิญต่อได้เลยครับ หุหุ� Grin
Logged

..
chanon
Administrator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +26/-1
Offline Offline

Posts: 738


« Reply #13 on: 05 November 2005, 12:10:43 AM »

จริงๆ จะเรียกว่ามันเป็นทฤษฎี หรือความเป็นจริง ความเห็นอะไร ผมว่าไม่สำคัญครับ
สิ่งที่ผมนำเสนอเป็นเพียงข้อมูลความรู้ที่ผมได้มาจากบทความ
http://www.gaffer.org/articles/Timestep.html
หัวใจหลักของมันคือการแยกรอบการ update กับรอบการวาด ออกจากกัน
ซึ่งผมเอามาลอง implement ดู ก็พบว่าทำให้การเคลื่อนไหว smooth ขึ้นจริงๆ ครับ
แถมได้ dt คงที่ด้วยสำหรับการคำนวณ physics ของเกม
ก็เลยเอามาแชร์กันครับ

เพิ่มเนื้อหาอีกนิด ... คือสำหรับการ interpolate ที่ว่านั้น ทำได้ง่ายๆ โดยการ interpolate ระหว่าง xi และ xi-1 นั่นเองครับ
Logged
chanon
Administrator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +26/-1
Offline Offline

Posts: 738


« Reply #14 on: 05 November 2005, 12:17:24 AM »

อีกนิดนึง ที่ผมเสนอวิธีนี้ ก็เพราะเห็นว่าเป็นวิธีที่เป็นมาตรฐาน ใช้กันโดยทั่วไป
ดังเช่นในบทความ Gamasutra นี้
http://www.gamasutra.com/features/20010713/dickinson_02.htm
ประวัติคนเขียนบทความ
http://facs.lincoln.ac.uk/Research/People/index.php?person=pdickinson

อีกบทความที่พูดถึงแบบเดียวกัน (แยก game updates จาก frame rate)
http://www.gamasutra.com/resource_guide/20030121/maclaurin_01.shtml
« Last Edit: 05 November 2005, 12:29:46 AM by chanon » Logged
yod
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +150/-15
Offline Offline

Posts: 3,240


WWW
« Reply #15 on: 05 November 2005, 12:24:58 AM »


หุหุ เชื่อคนที่เรียนได้ BSc(Hons) Physics ล่ะกันนะครับ ของแท้แน่นอน   Grin
Logged

..
diver_duck
Newbie
*

จำนวน ชม/ไม่พอใจ: +6/-2
Offline Offline

Posts: 32


« Reply #16 on: 05 November 2005, 03:35:25 AM »

เอ่อ พอดีผมไปนอนคิดเกี่ยวกับเรื่อง var fps เกี่ยวกับการ กระโดดและแรงดึงดูด
ไม่ทราบว่ากรณีที่เราไม่ fix fps จะทำแบบไหนครับ (คิดไม่ออก)

คือกรณีที่ทำแบบ fix fps ในส่วนของ code มันจะง่ายๆแบบนี้เลยคือ

loop
{
� hight += jump;
� jump -= g;
}

แต่ถ้าทำแบบกรณีที่ไม่ fix ตัว code มันจะเป็นยังไงครับ

ใช่สูตรนี้หรือเปล่าครับ Sy = Uy * T + 1/2 * G * T^2

(ผมเอาทั้งสองแบบทั้ง var และ fix เพราะจะประยุกเอาข้อดี)

ขอบคุณครับ
« Last Edit: 05 November 2005, 04:28:00 AM by diver_duck » Logged
yod
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +150/-15
Offline Offline

Posts: 3,240


WWW
« Reply #17 on: 05 November 2005, 04:44:56 AM »

ใส่ term t เข้าไปครับ

อืม แว้ก ลืม วิธีทำไปแล้ว Tongue
คร่าวๆ ประมาณนี้ครับ คือใส่ f เข้าไป

 a =� f / m
 v1 = v0 + a * t
 s = v1 * t
 x1 = x0 + s

.. สำหรับ รอบต่อไป

 v0 = v1
 x0 = x1

สมการพวกนี้ปกติคิดที่ 1 หน่วยเวลา หรือ 1s
ดังนั้นต้องใส่ตัวคูณ เศษของเวลาเพิ่มลงในสมการครับ

ขอไปขุด source เก่าๆ ก่อนครับ เคยเขียนไว้ประมาณปี 2000  Cheesy
Logged

..
diver_duck
Newbie
*

จำนวน ชม/ไม่พอใจ: +6/-2
Offline Offline

Posts: 32


« Reply #18 on: 09 November 2005, 10:26:34 PM »

หลังจากเวบล่ม คำถามที่ค้างไว้ หายไปเลย

ถามใหม่ล่ะกัน คือตอนนี้เจอปัญหาในระบบ Var FPS ที่ยังหาสูตรไม่ได้เลยครับ

สูตรธรรมดา

tdiff = curtime - lasttime;

x += vx * tdiff;
y += vy * tdiff;

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

struct
{
  float x, y;
  float vx, vy;
  float ax, ay; <=== สองตัวปัญหา
} OBJ;

จะทำยังไงกับค่าความเร่ง ax, ay

ผมลองดูวิธีที่คุณ chanon บอกแล้วมันไม่ได้ผลครับ
หากค่า tdiff มีความห่างไม่เท่ากัน การคำนวณจะเพี้ยนทันที

จะมีสูตร วิธีการคำนวณที่มันแม่นยำไหมครับ
Logged
Hoo
Administrator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +52/-0
Offline Offline

Posts: 597


« Reply #19 on: 09 November 2005, 11:48:19 PM »

ประเด็นคือ
รอบการประมวลผล != รอบการวาด
ที่ผมเคยเขียนในกระทู้ ที่แล้วนะครับ

ถ้าตราบใดในใจคุณ diver_duck ยังคงยึดติดว่า "คิด 1 ที วาด 1 ที"
ปริศนาก็จะไม่ไขกระจ่างครับ (เอาชื่อ อ.ดัน เป็นเดิมพัน???)

ถ้าจะให้เห็นจะแจ้ง ของเอาโค้ดจากบทความ มาแปะนะครับ
http://www.gaffer.org/articles/Timestep.html
Code:

    float t = 0.0f;
    const float dt = 0.01f;

    float currentTime = 0.0f;
    float accumulator = 0.0f;

    while (!quit)
    {
         float newTime = time();
         float deltaTime = newTime - currentTime;
         currentTime = newTime;

         accumulator += deltaTime;

         while (accumulator>=dt)
         {
              integrate(state, t, dt);
              t += dt;
              accumulator -= dt;
         }

         render(state);
    }

จะเห็นว่ารอบการคิด ในบทความ คือ integrate() จะไม่เท่ากับรอบการวาด render()
เพราะ render() จะถูกเรียกทุกครั้งในรอบใหญ่ while (!quit)
แต่ integrate() จะได้รับการเรียกก็ต่อเมื่อ เวลาผ่านไปตาม dt ที่เรากำหนดไว้ (รอบวาด>รอบคิด)
หรืออาจโดนเรียกรอบคิดหลายๆครั้ง ถ้า accumulator มันเยอะมากๆ (รอบวาด < รอบคิด)

เช่น Counter Strike ลองตั้ง Server แบบ Dedicate ดูจะเห็นเค้าตั้งไว้ที่ 100 รอบ
แต่รอบการวาดจะขึ้นอยู่กับแรงเครื่องที่เราเล่นครับ
Logged

You ask for Freedom of Speech
or
Freedom of Lies???
Hoo
Administrator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +52/-0
Offline Offline

Posts: 597


« Reply #20 on: 10 November 2005, 12:02:02 AM »

แล้วทีนี้เจ้าสูตร interpolate 0.0 - 1.0 เนี่ย
ไม่ใช่ว่า 0.0 วินาทีที่แล้ว - 1.0 วินาทีต่อมา นะ
แต่เป็น 0.0 คือ ค่าในรอบคิดปัจจุบัน - 1.0 คือ ค่าในรอบคิดข้างหน้า

ทำให้แม้ไม่ได้เข้า ส่วน process (ในที่นี้คือ integrate())
x,y,dx,dy ของวัตถุก็ไม่เปลี่ยน
แต่ drawx,drawy ที่ได้จะต่างกันในแต่ละ frame เพราะ
drawx = x + (dx*(accumulator/dt));
drawy = y + (dy*(accumulator/dt));
ครับ
Logged

You ask for Freedom of Speech
or
Freedom of Lies???
yod
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +150/-15
Offline Offline

Posts: 3,240


WWW
« Reply #21 on: 10 November 2005, 12:04:14 AM »

ความเดิมตอนที่แล้ว .. �:D
#19

เหอๆ สรุป modify สูตรให้แล้วครับ� �
(อันที่เขียนไว้ข้างบน a=f/m นั่น อีกวิธีนึงครับ)

สูตรจริงๆ ของกระโดด ก็คือ projectile ตำแหน่ง y ที่เวลา t ใดๆ

(1) ...� � � � � y(t) = v0*sin(theta)*t - 0.5*g*t2

ไม่ได้คิดเองนะ มาจากหนังสือ Devid M. Bourg., "Physics for Game Developers" ISBN 0-596-0006-5 ครับ
มีหนังสือดีสบายไป 10 อย่าง�

timediff หาจาก timegettime หน่วย ms (หรือ queryperf แล้วแต่ๆ)
tfrac หน่วย s
tsum หน่วย s� = t ในสูตร (1)
g = 9.8 m/s2� = g ในสูตร (1)
สมมติ มุมกระโดด 45 องศา (x หาเองนะ x(t) = v0 * cos(45) * t)
v = 10 m/s = v0 ในสูตร (1)

โค๊ดในส่วนของ timer

Code:
� � �timediff := timeGetTime() - start_time ;
� � �start_time := timeGetTime() ;
� � �caption:=floattostr(timediff);

� � �tfrac:=timediff/1000; tsum:=tsum+tfrac;

� � �if jump then begin tsum:=0; jump:=false; end;

� � �g:=9.8;
� � �v0:=+10;
� � �pos_y:=ground - 50*( (v0)*sin(45*pi/180)*tsum -0.5*g*sqr( tsum ));

� � �if pos_y>ground then pos_y:=ground;
ตามนี้จะได้ pos_y ไปใช้ได้เลย�

ส่วน แกนคือ บนซ้าย = (0,0) นะครับ
Logged

..
yod
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +150/-15
Offline Offline

Posts: 3,240


WWW
« Reply #22 on: 10 November 2005, 12:15:11 AM »

ตอบ #18 (#24 เดิม)

เหอๆ โค๊ดนั่นถูกต้อง 99.99% ครับ ลองแล้ว
จุดและเวลาที่กระโดดในครั้งแรก ทำไมหาไม่ได้ล่ะครับ�

ส่วน
x = x + v*dt;
v = v + a*dt;
วิธีทำให้มันถูก 99.99% ไม่บอกล่ะกันนะครับ� เก็บไปคิดเป็นการบ้าน คิดออกเองก็เอาไปใช้เอง นั่งลองสัก 3 เดือนคงออก

ขอเก็บ trick ไว้ให้เซียนๆ ใช้มั่งสิครับ บอกหมดก็แย่เลย� �
..

อันนี้คำตอบเก่า ดังนั้นยืนยันครับว่าทำได้ ลองไปคิดเป็นการบ้านดู� Cheesy

hint อีกนิด:
s = x - x0
v = s/dt
v หน่วยเป็น m/s

(ศัตรู อ.ดัน Huh ไม่บอกก็รู้ว่าใคร จากการคาดเดา เป็นพ่อของเด็กที่หายตัวไป
เอาชื่อปู่เป็นเดิมพัน อ่ะ ข้ามเรื่องๆ� Grin)
Logged

..
diver_duck
Newbie
*

จำนวน ชม/ไม่พอใจ: +6/-2
Offline Offline

Posts: 32


« Reply #23 on: 10 November 2005, 01:38:12 AM »

อันที่คุณ Hoo พูด มันคล้ายวิธี Fix FPS นี่ครับ ต่างจากที่ผมยกตัวอย่างหรือเปล่า เพราะของผมมันก็ข้าม Frame ที่วาดไม่ทันเหมือนกัน
ที่ผมกำลังหาคืออยากทราบสูตรที่ใช้คำนวณ ฟิสิกส์ ในแบบ Var FPS น่ะครับ จะเอาไปประยุคใช้กับอย่างอื่น

ส่วนที่คุณ Yod บอกมันก็ใช่ครับ แต่มันใช้ไม่ได้ทุกกรณีครับ วิธีการกระโดดที่บอกมามันจะ 99.99% หรือ 100.00% ก็ตามล่ะ
แต่เราต้องใช้ tsum เพื่อจำเวลาที่เรากระโดด แล้วมาคำนวณเวลาที่ผ่านมา เพื่อให้ได้ตำแหน่งที่เราจะอยู่ในเวลานี้

แต่ใช้ไม่ได้เพราะ สมมุติว่าเกมนี้ไม่ใช่มีแค่การกระโดดล่ะครับ เป็นเกมแข่งรถแทนล่ะครับ
ซึ่งการเปลี่ยนแปลงการบังคับของเรามันจะปัจจุบันทันด่วน รถมีการเลี้ยวกระทันหัน การเร่งความเร็วรถพร้อมกับการเข้าโค้งและลงหลุม
หรืออาจจะโดนรถคันอื่นชน หรือเข้าโค้ง หรือกระเด้งหลุมกระทันหัน ที่กล่าวมานี้ล้วนแล้วแต่ต้องอาศัยตัวแปล ax, ay หรือความเร่งน่ะแหละครับ

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

คือวิธีการของการเคลื่อนที่แบบ Var FPS มันต้องคิดได้ที่ เวลานั้นที่วาด กับเวลาก่อนที่วาด
เหมือนสูตรที่คุณๆบอกก่อนหน้านี้แหละครับ

x += vx * tdiff;
y += vy * tdiff;

แต่ปัญหามันอยู่ที่ ฟิสิกส์ จะไม่มีเลยถ้าใช้แค่สูตรนี้
เพราะมันต้องมี ค่าความเร่งมาคิดด้วย ax, ay

ถ้ากรณีของการกำหนดช่วงเวลาในการคำนวณ เป็น 60 ครั้ง 100 ครั้ง
มันจะใช้สูตรได้ง่ายๆ โดยคิดแบบทีละ frame

x += vx;
y += vy;
vx += ax;
vy += ay;

แต่ถ้าหากว่าไม่ได้กำหนดช่วงเวลาให้แน่นอนล่ะแบบ Var FPS เราจะจัดการยังไงกับขั้นตอนพวกนี้
จากสูตรที่คุณ chanon ยกตัวอย่าง link มาให้ดู ปรากฏว่าเมืองนอกเขายังหาสูตรที่มันให้ผลที่แน่นอนไม่ได้เลย

ผมลองใช้วิธีที่คุณ chanon ยกตัวอย่างปรากฏว่าค่าที่ได้คาดเคลื่อนมากครับ

x += vx * tdiff;
y += vy * tdiff;
vx += ax * tdiff;
vy += ay * tdiff;

(แต่สูตรที่ว่าหายไปแล้วตอนเวบล่ม)

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

แต่ยังไงถ้าหากว่าใครมีสูตรที่ใช้กับวิธี FPS ได้แบบ 99.99% ช่วยแนะนำด้วยนะครับ

ขอบคุณครับ
« Last Edit: 10 November 2005, 01:53:34 AM by diver_duck » Logged
diver_duck
Newbie
*

จำนวน ชม/ไม่พอใจ: +6/-2
Offline Offline

Posts: 32


« Reply #24 on: 10 November 2005, 01:49:59 AM »

เอ่อ คุณ Hoo ครับ ช่วยดู code ของผมทีครับ มันต่างจากวิธีที่ คุณ Hoo ยกตัวอย่างไหมครับ

while ( TRUE )
{
    while ( lastframe < frame )
    {
        x += vx;
        y += vy;
        vx += ax;
        vy += ay;

        lastframe++;
        frame = get_current_frame();
    }

    rander( x, y );
}
Logged
yod
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +150/-15
Offline Offline

Posts: 3,240


WWW
« Reply #25 on: 10 November 2005, 06:21:08 AM »

Q: คุณ Hoo พูด มันคล้ายวิธี Fix FPS นี่ครับ

A: ตอบแทนคุณ Hoo ^^
ไม่ fix ครับ เพราะมี dt หุหุ

fps = frame per sec = 1 วินาทีวาดได้กี่ภาพเฟรม
cps = computation per sec = 1 วินาทีคำนวนได้กี่รอบ (ให้ศัพท์ใหม่ละกันมั่วเอง� )


Q: ... แต่ใช้ไม่ได้เพราะ สมมุติว่าเกมนี้ไม่ใช่มีแค่การกระโดดล่ะครับ เป็นเกมแข่งรถแทนล่ะครับ
ซึ่งการเปลี่ยนแปลงการบังคับของเรามันจะปัจจุบันทันด่วน รถมีการเลี้ยวกระทันหัน การเร่งความเร็วรถพร้อมกับการเข้าโค้งและลงหลุม
หรืออาจจะโดนรถคันอื่นชน หรือเข้าโค้ง หรือกระเด้งหลุมกระทันหัน ที่กล่าวมานี้ล้วนแล้วแต่ต้องอาศัยตัวแปล ax, ay หรือความเร่งน่ะแหละครับ ..

A: สูตรอะไรก็ตามที่คุณแปลงเป็น f(t) ได้ ดุณก็หาได้ เช่น y(t) = v0*sin(theta)*t - 0.5*g*t2
หรือถ้าไม่เอา f(t) เพราะมันหาสูตรยาก .. ก็ใช้วิธีหา dt แทน
ฟิสิกส์ engine หลายๆ อันส่วนใหญ่ใช้� var cps ครับ (เอาศัพท์ใหม่มาใช้ โยน fps ทิ้งไป)

แล้วการเช็คชนเค้าใช้เวกเตอร์ครับ ซึ่งระยะห่างไม่เกี่ยวกับ step ของ dt  Wink
Logged

..
diver_duck
Newbie
*

จำนวน ชม/ไม่พอใจ: +6/-2
Offline Offline

Posts: 32


« Reply #26 on: 10 November 2005, 12:55:29 PM »

เอ่อ ขอใช้ศัพท์ของคุณ YOD นะครับ เหอๆๆ

ผมใช้คำไม่ถูกเอง ตอนนี้อยากถามคุณ HOO ว่าวิธีของผมมันต้องจากที่คุณ HOO ยกตัวอย่างไหมครับ

while (TRUE)
{
  while ( last_frame < frame )
  {
    x += vx;
    vx += ax;

    last_frame++;
    frame = get_current_frame();
  }

  rander( x );
}

วิธีนี้รอบประมวลผล ต่างจากรอบการวาดเหมือนกันครับ
ผมดูตัวอย่างจากเวบที่คุณ HOO ให้มา โหลด code มาดู
มันทำคล้ายๆแบบนี้อ่ะครับ

เขาตั้ง dt อยู่ที่ 0.01 ก็จะเท่ากับว่า รอบประมวลผลอยู่ที่ 1/100 วินาที หรือ 100 cps
ถ้าของผมตั้งให้ get_current_frame() ได้ที่ 1/100 เหมือนกัน ผลมันต่างอะไรกันไหมครับ

ถ้าต่างกันต่างกันตรงไหรครับ วิธีจากตัวอย่างของคุณ HOO ดีกว่ามากไหมครับ
(แต่ถ้าหากไม่ต่างกัน ทำไมจากตัวอย่างต้องทำ code ให้เข้าใจยากเหลือเกิน)

คือที่ผมพยายามจะสื่อให้ทุกคนฟังก็คือว่า var frame late น่ะมันเรื่องของการ วาด (วาดเร็วเท่าไหร่เอาเท่านั้น)
แต่รอบการประมวลผล มันต้อง fix ไม่ใช่เหรอครับ (จากตัวอย่างของคุณ HOO อยู่ที่ 1/100 sec)

เพราะหากไม่ fix แล้ว มันจะทำฟิสิกส์ได้ยังไง มันทำได้จริง อย่างที่คุณ YOD บอก
แต่ต้องมาคอยหาสมการผมว่าตายแหงๆ
แต่ถ้าหากเราทำรอบการประมวลผล fix มันจะได้ผลที่แม่นยำเสมอ ไม่ว่าจะรันจากเครื่องไหนๆ

หรือทุกท่านคิดว่าไงครับ (รอคุณ HOO มาเฉลยว่ามันต่างกันไหม)
Logged
Hoo
Administrator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +52/-0
Offline Offline

Posts: 597


« Reply #27 on: 11 November 2005, 02:10:03 AM »

อืม เท่าที่อ่าน ผมว่าต้อง บัญญัติศัพท์ใหม่กันละมั๊ง

ถ้า FPS = Frame Per Second = รอบการวาด/1วิ
ถ้า PPS = Process Per Second = รอบการทำงาน/1วิ

เราควรเขียนให้เกมดำเนิน PPS เท่ากันในทุกเครื่อง
หรืออย่างน้อยก็เครื่อง Server ที่เป็นตัวตัดสินชี้ขาดผลลัพธ์ของเกม
แต่ FPS จะขึ้นอยู่กับแรงเครื่อง

แต่ทีนี้ ที่เค้าทำงาน PPS แบบ ส่ง dt เข้าไป ก็เพื่อให้ง่ายต่อการปรับค่า PPS ในภายหลัง
แต่.... แต่... แต่...
ทุกครั้งที่ปรับค่า PPS หรือ อีกนัย dt
ถ้าใช้ Interpolate ปกติ
ผลของการประมวล จะมีความแตกต่าง ตามแต่ความผิดพลาดสะสมที่คุณชานนท์ว่าไว้!!

ถ้าเราใช้วิธี Interpolate ปกติ
คุณตั้ง PPS ที่ 60
การประมวลจะได้ผลลัพธ์แตกต่างจาก เมื่อคุณตั้ง PPS ที่ 100 ในระดับทศนิยม
และมันจะทบไปเรื่อยๆ เมื่อเกมรันไปนานๆ

ปัญหาคือ ถ้าเราตั้ง Data ของเกม เช่น
หินที่ตั้งใจจะให้มันหล่นจากภูเขาสูงๆ มาตรงขอบของหน้าผา
แล้วชิ่งไปโดนกิ่งไม้ที่ตั้งใจไว้
แล้ว Item จะหล่นมาหาเรา

ตั้งค่าเรียบร้อยที่ PPS 60
ปรากฏว่า ตอนหลังมีการเพิ่มมันเป็น 100 PPS
Data ที่คุณเคยตั้งไว้ มันไม่แน่แล้วว่ามันจะได้ผลเหมือนเดิม
หินอาจจะตกมา ปุ ที่ขอบหน้าผา แต่องศาที่ชิ่งกะขอบ ไม่ใช่มุมเดิมแล้ว
มันจึงชิงไป ไม่โดนกิ่งไม้ แล้ว Item ก็ไม่หล่นลงมา
เกมคุณเน่าทันที !!!

การใช้ Verlet Integration หรือ อัลกอ อื่นๆ
ก็เพื่อลดความผิดพลาดเหล่านี้ให้น้อยที่สุด
----------------------------------
จะเห็นว่าที่คุณชานนท์มาเขียน อ้างอิงถึงเรื่อง fix/var FPS
เพราะเห็นว่าเกี่ยวเนื่องกัน แต่มันไม่ใช่เรื่องเดียวกัน ครับ

เพราะเราสามารถใช้ Verlet Integration ตอนวาดด้วยก็ได้
แต่ที่ ซีเรียส กันจริงๆในกระทู้นี้คือส่วน Process มากกว่า
----------------------------------
อ๊ะ เพิ่งกลับไปดู
พี่ยอด ตั้ง cps ตัดหน้าไปก่อนแล้วรึนี่ Huh
« Last Edit: 11 November 2005, 02:19:03 AM by Hoo » Logged

You ask for Freedom of Speech
or
Freedom of Lies???
yod
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +150/-15
Offline Offline

Posts: 3,240


WWW
« Reply #28 on: 11 November 2005, 02:59:40 AM »

Q: เพราะหากไม่ fix แล้ว มันจะทำฟิสิกส์ได้ยังไง มันทำได้จริง อย่างที่คุณ YOD บอก
แต่ต้องมาคอยหาสมการผมว่าตายแหงๆ

A: ไม่ตายหรอกครับ คนที่หาได้มีเยอะแยะนะ integrate ธรรมดาๆ ต้องทาง major math หรือ major physics น่ะ .. ส่วน major computer ถ้าทำไม่ได้ก็ไปหามาใช้สิครับ

แล้วส่วน var fps/pps มันไม่ได้ยากอะไรหรอกครับ เขียนแบบนี้คนที่ใช้ var fps/pps นั่งอมยิ้มหลายคนแล้ว เกมทั่วไปใช้ var fps/pps หมดแล้วจะมานั่งยืนกระต่ายขาเดียวมันก็ไม่ถูกนักหรอกครับ

ที่เขียนมานี่พยายามชวนให้"ข้ามกำแพง" fix fps ก็เท่านั้น
อะไรที่คุณว่ายาก บางทีมันไม่ยากหรอก เปิดใจให้กว้างหน่อยครับ

เห็นย้ำเหลือเกินเรื่อง 100% นี่

จะให้ถูกต้อง 100% ทำไม่ได้ครับ กฏนิวตันยังไม่ถูกต้อง 100% เลย ถ้าอยากให้ถูกต้อง 99.99999999%  ในยุคสมัยนี้ คุณต้องประยุกต์ใช้ทฤษฎีสัมพัทธภาพของไอสไตน์ และบวกกับทฤษฎีสตริง ถึงจะคำนวนแรงได้อย่างถูกต้อง ถึงคำนวนได้จริงแล้วจะวัดอย่างไรว่ามันถูกต้อง แล้วการใช้เวลาคำนวนนั่นต้องใช้เวลา real-time ได้หรือเปล่า

ส่วนไอเท็มชนกิ่งไม้กระดอนลงมานั่น ให้นักฟิสิกส์จริงๆ บวกทฤษฎีทั้งหมดที่กล่าวมาคำนวนก็ไม่มีอะไรถูกต้อง 100% หรอกนะครับ คือคำนวนให้ถูก 100% ไม่ได้ เพราะมันคือสิ่งที่เรียกกันว่า chaos นะครับ  Cheesy

สูตรคือการประมาณค่าครับ ถ้าให้ผลคำนวน error อยู่ในช่วงยอมรับได้ ก็ควรจะยอบรับมัน
Logged

..
diver_duck
Newbie
*

จำนวน ชม/ไม่พอใจ: +6/-2
Offline Offline

Posts: 32


« Reply #29 on: 11 November 2005, 04:06:06 AM »

เอ่อ คือขอโทษที่ครับ จริงๆที่ผมใช้คำว่า FIX FPS มาตลอด เพราะไม่รู้ว่าจะเรียกยังไง ต่อไปขอใช้คำว่า
Var FPS/PPS แทนล่ะกันจะได้เข้าใจถูก

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

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

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

ที่ผมพยายามจะสื่อก็เรื่องแค่ว่าถ้าเกมไม่มี PPS ที่แน่นอนจะทำเกมได้จริงเหรอ
ยกตัวอย่าง สมมุติว่าอยากจะทำเกมจำลองการปล่อยวัตถุตกจากที่สูง เอาง่ายๆแค่นี้

ถ้าเราไม่กำหนด PPS ให้เกมเลย เราจะมีขั้นตอนใน code ยังไง ผมถามแค่เนี้ย

ผมไม่ได้ใจแคบนะครับ ผมเองก็อยากศึกษาวิธีการอื่นๆ แต่ผมอยากเห็นตัว code มัน
code ที่หลายๆคนบอกว่า ทำได้โดยไม่กำหนด PPS มันเป็นแบบไหนครับ
« Last Edit: 11 November 2005, 04:12:07 AM by diver_duck » Logged
Pages: [1] 2   Go Up
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2013, Simple Machines Valid XHTML 1.0! Valid CSS!