[Hướng Dẫn] Bài 3: Di chuyển Object đến một vị trí.

Focker_cFocker_c Posts: 1,577Registered
edited July 2019 in Game Maker: Studio
Bài 3: Di chuyển Object đến một vị trí.
>> Tác giả: Focker C, from Deskar Homiez.
Vâng, 2 bài trước chúng ta đã học đc 2 cách phổ thông để di chuyển object. Một là theo "speed", hai là theo tọa độ.
Và đã là "phổ thông" thì ko thể có sự độc đáo và ko thể áp dụng linh hoạt đc. Quan trọng nhất là phần linh hoạt. Một điều để ta hiểu rõ: Làm thế nào để di chuyển object đến 1 điểm ở hướng chéo lên trên, bên phải?
Để giải quyết vấn đề di chuyển chéo, thì đơn giản ta kết hợp hspeed(di chuyển ngang) và vspeed(di chuyển dọc).
Ví dụ:
hspeed=3
vspeed=3

Khi này object sẽ di chuyển từ từ về hướng phía trên, bên phải. Nhưng liệu nó có đến đúng đc điểm mà ta đặt ra? Xem hình:

diemcanden_zps9576d414png

Câu trả lời là "Không". Nó mới chỉ đi đúng hướng nhưng thiếu độ chuẩn xác.
Vậy làm sao để ta di chuyển 1 obj đến một vị trí một cách chính xác ?

Đó cũng chính là những gì chúng ta tìm hiểu bài này: Đưa obj tới điểm đến xác định. Và ở bài học này, mình xin hướng dẫn theo ví dụ khi điểm đến đó là điểm click chuột trái. Để đc vậy ta cần làm quen với 2 hàm có sẵn của G.Maker đó là mouse_x, mouse_y (2 hàm số này là giá trị tọa độ x,y của chuột ở thời điểm dùng lệnh)
Ta cần phải có 2 cái biến (VAR) để ghi nhớ 2 cái hàm số toạ độ chuột nói trên mỗi khi click chuột trái. 2 biến này mình đặt là:

"global.diem_den_x" và "global.diem_den_y"

Để thực hiện công việc ghi nhớ toạ độ chuột vào 2 biến khi click chuột trái, ta viết đoạn code ở Event [Global Mouse > Left Released] (Sự kiện xảy ra mỗi khi người chơi bấm và nhả chuột Trái):
global.diem_den_x=mouse_x;
global.diem_den_y=mouse_y

À ! Đến đây thì nhiều bạn sẽ hỏi: Đặt biến kiểu khỉ gì mà kì quặc thế kia? global là cái wth gì ? Vâng vâng , mình xin trả lời ngay. Loại biến này đc gọi là "Biến Chung" (Global VAR), theo đúng nghĩa như vậy, tức là biến dùng chung, nó đc sự dụng rộng rãi hơn biến thường, các obj khác nhau đều xài đc nó. Ở đây thì mình xài biến global là vì sau này còn dùng các obj khác can thiệp đc vào (ko phải bài này). Cách dùng thì đơn giản thôi. Chỉ việc thêm cụm "global." vào trước biến thường (Chú ý dấu chấm nha). Bạn cũng phải khởi tạo nó đấy. Mình khởi tạo nó là Event [Game Start] đó, đặt luôn là giá trị x,y của object, các bạn xem cái file gmk thì rõ hơn.

Công việc đc rút ngắn lại thành còn là : đưa obj tới điểm có toạ độ
(global.diem_den_x;global.diem_den_y)

Và theo quan điểm của mình thì việc gì cũng có rất nhiều con đường để đạt đc. Có cách nhanh, có cách hay, có cách lại tệ. Ở đây mình có 3 cách cho đề bài trên. Nhưng sẽ ko đi vào cách nhanh ngay, mà đi từ cách dễ hiểu trước.
Và chúng ta nói tới...

$ Method 1 $
Ở phương pháp này thì mình chỉ sự dụng hoàn toàn những gì đã có, ko có gì mới mà vẫn hoàn thành đề bài. (Như kiểu lấy kiến thức lớp 10 để thi đại học ý mà, khó nhưng ko phải là ko thể) Và cách này cũng dễ hiểu, thuận lợi cho bạn hình dung dễ hơn các cách sau.
Và thực tế là mình sẽ dùng và cải tiến cách làm thô sơ đã nêu ra đầu tiên. Tức là xài vspeed mà hspeed.

Và đến đây mình cùng xin đưa ra khái niệm mới cho các bạn mới đến với GML. Đó là câu lệnh "If" (Nếu)- câu lệnh điều kiện thường dùng nhất.
Vâng ! Chung chung thế này: Nếu <tôi học chăm> thì <tôi sẽ đếch trượt>.

=> Nếu <điều kiện> thì <lệnh>
Tức là <điều kiện> thoả mãn thì <lệnh> sẽ đc thực hiện.

Công thức thực tế:

if "điều_kiện"
 { "lệnh_1";
   "lệnh_2";
   "lệnh_3" }


Để ý rằng các dòng lệnh thực hiện đều để trong 2 cái ngoặc nhọn nha. Và hãy xuống dòng để tách ý, xem lại để sửa sẽ dễ dàng hơn. (Mình thấy ko nhiều bạnhiều bạn ko làm đc việc này. Việc làm thiếu nghiêm túc, thiếu khoa học sẽ các bạn 90% xụp đổ giữa chừng vì một đống bug mà ko biết bug ở đâu)
Để cho dễ hiểu hơn, mình xin đưa ra vài ví dụ thế này:
if a < 10
 { a += 1 }
Giải thích: Nếu a nhỏ hơn 10, thì a đc phép tăng thêm 1.
if a = 8
 { b = a;
  a = 0 }
Giải thích: Nếu a đạt bằng 10, thì xảy ra 2 lệnh liên tiếp: cho b bằng a (tức là bằng 8), khi xong rồi thì cho a bằng 0. Với đoạn code trên ta làm đc công việc "lưu nhớ giá trị của a vào b, rồi xoá a".
if x < 10
 { hspeed = 5 }
if x > 10
 { hspeed = 0 }
Giải thích: Nếu toạ độ x của obj mà nhỏ hơn 10, thì obj sẽ lao về bên phải với t.độ là 5, đến khi nào vượt qua điểm x=10 thì object sẽ có hspeed=0, tức là phanh lại.

Vâng ! Ví dụ cuối cũng là cốt lõi để thực hiện phương pháp thứ nhất này. Ở Event [Step], ta viết đoạn code:
if x < global.diem_den_x
    {hspeed=3}
if x > global.diem_den_x
    {hspeed=-3}
if y < global.diem_den_y
    {vspeed=3}
if y > global.diem_den_y
    {vspeed=-3}

Mấu chốt là chúng ta sẽ check (kiểm tra) vị trí toạ độ của object để đưa ra vận tốc và dấu +/- cho hợp lý.

Rồi .. bây giờ .. Thêm đoạn này để ngừng obj:
if x = global.diem_den_x
    {hspeed=0}
if y = global.diem_den_y
    {vspeed=0}

Tức là khi đã tới điểm đến thì cho hspeed và vspeed bằng 0 thôi. Có gì đâu ^^

Được rồi, có thể coi là 75% công việc đã làm xong, bây giờ hãy đặt obj vào 1 room và test game thử.
Ngon ko ? obj đã có thể di chuyển về đúng hướng điểm click chuột.
Nhưng có một lỗi mà chúng ta "rất dễ nhận ra". ^^
Đó là obj của chúng ta sẽ giật tưng tưng, rung lắc loạn xạ tại điểm đến (Op.. Op.. Oppa Gangnam Style).

Okê, để mình giải thích lý do. Việc giật tưng tưng đó thực ra là việc tốc độ của obj chưa bị đưa về 0 (hay nói cách khác là đoạn code thứ 2 chưa hoạt động). Tại sao như vậy ? Về mặt lý thuyết thì đúng rồi cơ mà ?
Vâng đó là lý thuyết thôi ạ. Để đi tới thực hành thì nó là một chân trời khác ^^
Bạn nên biết rằng toạ độ trong G.Maker tính theo từng pixel. Cho nên việc đi qua 1 pixel là rất nhanh, obj của chúng ta băng qua điểm đến mà ko kịp thực hiện lệnh cho speed=0.
Cái khuyết điểm này ta phải chập nhận thôi. Kể cả các phương pháp sau cũng đều mắc phải.

Nói là chấp nhận nhưng thực ra chúng ta phải khắc phục nó. "But, how?"
Cách khắc phục mình đưa ra thì các bạn hiểu đơn giản thế này: Khi obj gần tới điểm đến rồi (chỉ còn 5 pixel nữa chẳng hạn), ta sẽ lập tức chuyển obj ngay lập tức đến "điểm đến". Tức là nhảy cóc 5 pixel, nhưng vì khoảng cách 5 pixel này quá nhó nên người chơi sẽ ko nhận ra.
Đoạn code thì nó thế này:
if x <= (global.diem_den_x + 5) and x >global.diem_den_x
    {x=global.diem_den_x;hspeed=0}
if x >= (global.diem_den_x - 5) and x <global.diem_den_x
    {x=global.diem_den_x;hspeed=0}

if y <= (global.diem_den_y + 5) and y >global.diem_den_y
    {y=global.diem_den_y;vspeed=0}
if y >= (global.diem_den_y - 5) and y <global.diem_den_y
    {y=global.diem_den_y;vspeed=0}

Lại là một khái niệm mới mẻ đây ^^. Mình xin lỗi các bạn nôn nóng vì đi chi tiết như vậy. Nhưng có như thế thì các bạn mới hiểu rõ và ko phải thắc mắc nhiều. Còn những bạn biết rồi thì cứ đọc thêm, biết đâu ngộ ra điều gì.
Điều mới ở đây là chứ "and", nghĩa là "và", ai cũng biết. Nó được sử dụng để gắn kết các điều kiện khi câu lệnh "if" có nhiều điều kiện. Khi này các lệnh trong ngoặc nhọn chỉ đc thực hiện khi mà tất cả các điều kiện gắn với nhau này phải đúng, phải đc thoả mãn. Sai 1 cái cũng ko đc. Ngoài ra thì người ta còn xài chữ "or" đến gắn các điều kiện, cũng dễ, nghĩa của nó là "hoặc". Khi này thì chỉ cần một trong những điều kiện đúng là đc.
Nếu bạn nào học toán cấp 2 xong thì sẽ biết về hệ phương trình. Hệ ngoặc nhọn là phải thoả mãn hết, hệ ngoặc vuông thì chỉ cần một phương trình thoả mãn là đc. Phần này cũng tương tự như vậy.
Ở đây thì ta quan tâm đến từ "and" đã ^^.

Mình sẽ giải thích câu lệnh "if" đầu, các câu còn lại thì tương tự.
if x <= (global.diem_den_x + 5) and x >global.diem_den_x
    {x=global.diem_den_x;hspeed=0}

Nhiệm vụ của đoạn này như sau: khi toạ độ x lớn hơn "global.diem_den_x" (lúc này obj đang ở bên phải điểm đến và đang di chuyển gần về đến nơi rồi), ta thêm 1 điều kiện kèm theo, đó là toạ độ x nhỏ hơn cái tổng "global.diem_den_x + 5".
Hay ta viết kép lại theo toán như thế này:
global.diem_den_x < x < global.diem_den_x + 5

Tức là x nằm trong khoảng toạ độ x của điểm đến và gần đó 5 pixel.
Câu lệnh cho điều kiện đó là
{x=global.diem_den_x;hspeed=0}
chính là việc đưa chính xác obj về điểm đến và cho tốc độ bằng 0 luôn.
Tương tự với hướng di chuyển ngược lại và tương tự cả toạ độ y.

Được rồi ! Nhưng vậy là bạn đã khắc phục đc sự cố rung lắc rồi đấy. Test đi, và thưởng thức thành quả.

Được chửa ? Đến đây bạn nên uống 1 cốc nước và thư giãn vì bài học tạm khép lại tại đây rồi. Đến đây là quá dài rồi, do bài này gặp ko ít khái niệm mới.
Các method còn lại sẽ đc hướng dẫn ở bài sau.

Download file example GMK : Here you are, little bunny !
Download file để coi thử: Click me, funny butt !

À mà, xin lỗi các bạn nha. Học xong bài này chúng ta vẫn chưa làm đc Focker Tank đâu. Có lẽ phải hết bài 5, vì mình chưa nói vì về các câu lệnh đơn giản về đồ hoạ cả (bài 4 sẽ nói về cách di chuyển của Focker Tank dựa trên 2 method còn lại, còn bài 5 sẽ nói đơn giản về mặt sử dung các lệnh vẽ sprite)

Chúc thành công !

Comments

Sign In or Register to comment.