1. วิเคราะห์การหมุนของเข็มนาฬิกา
การหมุนของเข็มนาฬิกาเป็นงานในส่วนของ animation เพราะการวาดเข็มจะเปลี่ยนไปเมื่อเวลาเปลี่ยน การหมุนของเข็มนาวินาที,นาทีและชั่วโมงใช้หลักการเดียวกัน ต่างกันที่ความเร็วเเชิงมุม การวิเคราะห์นี้จะช่วยทำให้เราทราบว่าในแต่ละหน่วยเวลาจะต้องทำการวาดเข็มนาฬิกาอย่างไร
![]() |
รูปที่ 1 |
การหมุนของเข็มวินาที
การหมุน 1 รอบหมายถึงเวลาเดินไป 60 วินาทีหรือ 1 นาที คิดเป็นค่าของมุมคือ
การหมุนของเข็มนาที
เหมือนกับการหมุนของเข็มวินาที แต่เปลี่ยนหน่วยเวลา 1 รอบหมายถึงเวลาเดินไป 60 นาทีหรือ 1 ชั่วโมง
การหมุนของเข็มชั่วโมง
การหมุน 1 รอบหมายถึงเวลาเดินไป 12 ชั่วโมง คิดเป็นค่าของมุมคือ
2. การแปลงค่าของมุมไปสู่ coordinates
ขั้นตอนนี้เป็นการหา coordinates ที่จะนำไปใช้เป็นจุดปลายในการวาดเข็มนาฬิกาทั้ง 3 เส้น (ดูรายละเอียดจากตอนที่ 1) จาก (1.0),(1.1) และ (1.2) บอกถึงการได้มาซึ่งมุมของแต่ละเข็มที่จะกวดไปเมื่อเวลาผ่านไป 1 หน่วยเวลา จะนำค่าเหล่านี้ไปใช้งานได้ต้องทำการเปลี่ยนให้เป็น coordinates ที่ตรงตามชุดคำสั่งต้องการก่อน โดยอาศัยการแปลงจากระบบ polar coordinates ไปสู่่ cartesian coordinates
ถ้า
หรือ ถ้าให้ t คือเวลาที่ผ่านไป เช่น 5 วินาที, 10 นาที จะได้
แต่ raylib การเริ่มนับมุมที่ 0 radian เริ่มจากเส้นแนวนอน (X+ axis) แล้ววัดมุมตามเข็มนาฬิกา ในขณะที่การนับเวลาจะเริ่มนับจากแนวตั้ง (Y+ axis) ดังนั้นค่ามุมที่คำนวณได้ ก่อนนำไปใช้คำนวณต่อต้องนำเอา
ดังนั้น (1.3),(1.4) จะต้องเชียนใหม่เป็น
![]() |
รูปที่ 2 |
เมื่อนำเอาค่ามุมของ 1 หน่วยเวลาของวินาที,นาทีและชั่วโมงมาแทนค่า สามารถสรุปเป็นสูตรคำนวณสำหรับตำแหน่งของเข็มทั้งสามดังนี้
» coordinate ของปลายเข็มวินาที
» coordinate ของปลายเข็มนาที
» coordinate ของปลายเข็มชั่วโมง
(1.7) ถึง (1.12) จะยังไม่สามารถนำไปใช้งานได้ ยังขาดขั้นตอนการย้ายตำแหน่ง (translation) เพราะจุดกำเนิดของนาฬิกาไม่ได้อยู่ที่ (0,0) แต่ย้ายไปที่ (cx,cy) ดังนั้นค่า coordinates ที่คำนวณได้ต้องย้ายตำแหน่งตามไปด้วย โดยการนำค่าของ cx และ cy รวมเข้าไปด้วย ดังนี้
3. การหาค่าของเวลา และการแปลงค่าไปสู่ coordinates
Raylib ใช้ชุดคำสั่ง SetTargetFPS(fps) เพื่อใช้กำหนดอัตราเร็วในการวาดใน 1 วินาที หมายความว่าสามารถคำนวณเวลาได้อัตราเร็วนี้
เช่น ถ้ากำหนดให้ fps = 20 และจำนวน frames ที่วาดไปแล้วคือ 1200 frames จะได้เวลาที่ใช้ไปคือ
ค่าที่ได้จากการคำนวณนี้จะนำไปใช้แทนค่าให้กับตัวแปร s,m,และ h ใน (1.13) - (1.18) ถ้ากำหนดให้ cx = 100, cy = 100 และ r = 10 จะได้
นั่นคือการวาดภาพ frame ที่ 1200 หมายถึงเป็นเวลา HH:MM:SS = 00:10:60 หรือ 00:11:00 และจุดปลายของเข็มวินาที,นาทีและชั่วโมงจะเป็น (89,90),(109,95),(100,90) ตามลำดับ
เขียน Code เพื่อทำ animation
เนื้อหาในตอนนี้จะมีผลต่อการปรับแต่ง code จากตอนที่ 1 ใน 3 functions คือ draw_sec_arm(),draw_min_arm() และ draw_hrs_arm() โดยต้องส่งค่า parameter ที่บอกเวลาให้กับทั้ง 3 function เพื่อนำไปคำนวณ coordinates ใหม่
void draw_sec_arm(int tick){
float thickness = 3.0f;
float ang = (tick * M_PI/30 - M_PI/2);
Vector2 start = {(float)cx,(float)cy};
Vector2 end;
end.x = (float)(cx + SEC_ARM_LEN * cos(ang));
end.y = (float)(cy + SEC_ARM_LEN * sin(ang));
DrawLineEx(start,end,thickness,RED);
}
void draw_min_arm(int tick){
float thickness = 7.0f;
float ang = (tick * M_PI/30 - M_PI/2);
Vector2 start = {(float)cx,(float)cy};
Vector2 end;
end.x = (float)(cx + SEC_MIN_LEN * cos(ang));
end.y = (float)(cy + SEC_MIN_LEN * sin(ang));
DrawLineEx(start,end,thickness,BLUE);
}
void draw_hrs_arm(int tick){
float thickness = 11.0f;
float ang = (tick * M_PI/6 - M_PI/2);
Vector2 start = {(float)cx,(float)cy};
Vector2 end;
end.x = (float)(cx + SEC_HRS_LEN * cos(ang));
end.y = (float)(cy + SEC_HRS_LEN * sin(ang));
DrawLineEx(start,end,thickness,DARKPURPLE);
}
cos(),sin() เป็น function ทาง trigonometry และ M_PI เป็นค่าคงที่ของ
4. การแปลงค่าลำดับของ frame ไปเป็นหน่วยของเวลา
ใน workshop เราใช้หน่วยเวลา 3 หน่วยคือ วินาที, นาที และชั่วโมง ดังนั้นค่าลำดับของ frame ที่ได้จะต้องถูกแปลงไปเป็นหน่วยของเวลาทั้ง 3 หน่วย
จาก (2.0) ค่าของตัวแปร time จะมีหน่วยเป็น วินาที เนื่องจากหน่วยของ frame rate คือ frames per second จะได้ว่า
จะเห็นได้ว่าความสัมพันธ์ระหว่างค่าของเวลาทั้ง 3 หน่วยมีลักษณะเดียวกับการทำงานของเฟืองทด
![]() |
รูปที่ 3 |
เมื่อจำนวน frame ที่วาดไปแล้วมีจำนวนเท่ากับ fps ก็จะทำให้ค่าของ second เพิ่มขึ้น 1
เมื่อจำนวน second มีจำนวนเท่ากับ 60 ก็จะทำให้ค่าของ minute เพิ่มขึ้น 1
เมื่อจำนวน minute มีจำนวนเท่ากับ 60 ก็จะทำให้ค่าของ hour เพิ่มขึ้น 1
เขียน Code เพื่อคำนวณเวลา
const int FPS = 20;
int second = 0;
int minute = 0;
int hour = 0;
int frame_count = 0;
while(!WindowShouldClose()){
if(frame_count < FPS){
frame_count+=1;
}else{
second+=1;//increase one second
if(second > 59){
minute+=1; // increase one minute
if(minute > 59){
hour+=1; // increase one hour
}
}
}
...
}
ข้อจำกัดจาก code ข้างบนคือตัวแปร frame_count จะเพิ่มจำนวนไปเรื่อยๆ ตราบที่โปรแกรมยังทำงาน ในทางปฏิบัติแล้วไม่ควรทำ เพราะหน่วนความจำของคอมพิวเตอร์มีจำกัดไม่สามารถเพิ่มค่าได้อย่างไม่มีจำกัด ดังนั้นต้องมีเงื่อนไขในการล้างค่าที่ใช้ไปแล้ว ซึ่งก็คือการเดินทางครบรอบของเข็มนาฬิกา เมื่อเข็มเดินทางครบ 1 รอบก็ทำการล้างค่าที่มีอยู่แล้วเริ่มรอบการทำงานใหม่ code ที่ได้ควรจะเป็น
const int FPS = 20;
int second = 0;
int minute = 0;
int hour = 0;
int frame_count = 0;
while(!WindowShouldClose()){
if(frame_count < FPS){
frame_count+=1;
}else{
frame_count = 0; // clean up
second+=1;//increase one second
if(second > 60){
second =0; //clean up
minute+=1; //increase one minute
if(minute > 60){
minute = 0; //clean up
hour+=1; // increase one hour
if(hour > 12){
hour = 0; //clean up
}
}
}
}
...
}
Complete code
#include "math.h"
#include "raylib.h"
/ constants :
//clock
const int WIN_WIDTH=400;
const int WIN_HEIGHT=400;
const int cx = (int)(WIN_WIDTH / 2);
const int cy = (int)(WIN_HEIGHT / 2);
// arms
const int SEC_ARM_LEN = 70;
const int MIN_ARM_LEN = 60;
const int HRS_ARM_LEN = 50;
const int FPS = 20;
int second = 0;
int minute = 0;
int hour = 0;
int frame_count = 0;
// functions
void draw_clock();
void draw_sec_arm(int tick);
void draw_min_arm(int tick);
void draw_hrs_arm(int tick);
int main(void){
InitWindow(SCR_WIDTH,SCR_HEIGHT,"Clock");
SetTargetFPS(FPS);
while(!WindowShouldClose()){
if(frame_count < FPS){
frame_count+=1;
}else{
frame_count = 0; // clean up
second+=1;//increase one second
if(second > 60){
second =0; //clean up
minute+=1; //increase one minute
if(minute > 60){
minute = 0; //clean up
hour+=1; // increase one hour
if(hour > 12){
hour = 0; //clean up
}
}
}
}
ClearBackground(WHITE);
draw_hrs_arm(hour);
draw_min_arm(minute);
draw_sec_arm(second);
draw_clock();
EndDrawing();
}
CloseWindow();
return 0;
}
void draw_clock(){
float thickness = 5.0f; // thickness of the ring
float inrad = 100.0f; // inner radius
float ourad = inrad + thickness; // outter radius
float stang = 0.0f; // start angle
float enang = 360.0f; // end angle
float seg = 360.0f; // segments
Vector2 cnt = {(int)cx,(int)cy}; // cast cx,cy to Vector2
DrawRing(cnt,inrad,ourad,stang,enang,seg,BLUE); // draw blue clock
DrawCircle(cx,cy,10,BLACK); // draw black circle with radius 10 pixels on the middle of the clock
}
void draw_sec_arm(int tick){
float thickness = 3.0f;
float ang = (tick * M_PI/30 - M_PI/2);
Vector2 start = {(float)cx,(float)cy};
Vector2 end;
end.x = (float)(cx + SEC_ARM_LEN * cos(ang));
end.y = (float)(cy + SEC_ARM_LEN * sin(ang));
DrawLineEx(start,end,thickness,RED);
}
void draw_min_arm(int tick){
float thickness = 7.0f;
float ang = (tick * M_PI/30 - M_PI/2);
Vector2 start = {(float)cx,(float)cy};
Vector2 end;
end.x = (float)(cx + MIN_ARM_LEN * cos(ang));
end.y = (float)(cy + MIN_ARM_LEN * sin(ang));
DrawLineEx(start,end,thickness,BLUE);
}
void draw_hrs_arm(int tick){
float thickness = 11.0f;
float ang = (tick * M_PI/6 - M_PI/2);
Vector2 start = {(float)cx,(float)cy};
Vector2 end;
end.x = (float)(cx + HRS_ARM_LEN * cos(ang));
end.y = (float)(cy + HRS_ARM_LEN * sin(ang));
DrawLineEx(start,end,thickness,DARKPURPLE);
}
Home work
ค้นคว้าหาวิธีนำเอาค่าของเวลาจากระบบปฏิบัติการมาใช้เป็นค่าเริ่มต้นสำหรับ เพื่อให้นาฬิกานี้สามารถใช้บอกเวลาได้จริง
ความคิดเห็น
แสดงความคิดเห็น