[Nhập môn Phát triển game] Cách làm cầu thang trong game
1. Yêu cầu cơ bản – Có kiến thức cơ bản về lập trình game – Biết xử lí va chạm (với đất, các object bằng AABB và SweptAABB) – Hiểu cơ bản về xử lí KeyBoards. – Biết cách làm cho nhân vật đi (vận tốc, trọng lực, điều khiển phím,…) 2. Ý tưởng cách làm cầu thang trong ...
1. Yêu cầu cơ bản
– Có kiến thức cơ bản về lập trình game
– Biết xử lí va chạm (với đất, các object bằng AABB và SweptAABB)
– Hiểu cơ bản về xử lí KeyBoards.
– Biết cách làm cho nhân vật đi (vận tốc, trọng lực, điều khiển phím,…)
2. Ý tưởng cách làm cầu thang trong game
– Làm cầu thang trong game thực chất là mình làm cho nhân vật đi như lúc đi lên cầu thang, không phải là mình làm từng bậc thang. Phần sprite về cầu thang bạn có thể load vào background map hoặc vẽ lên theo cách thông thường.
– Trong bài viết này mình sẽ hướng dẫn một cách làm cầu thang dựa vào việc xử lí giữa điểm đầu và cuối của cầu thang.
Ý tưởng cụ thể như sau:
a. Thiết kế chung
– Mỗi cầu thang sẽ có 2 object ẩn (tức là object không vẽ lên màn hình) có BoundingBox kích thước tương đối. Ở dưới chân cầu thang là StairBottom, trên đỉnh cầu thang là StairTop.
– Mình sẽ sử dụng 2 object ẩn này như là tín hiểu để biết khi nào bắt đầu vào cầu thang và khi nào ra.
b. Xử lí bắt đầu đi lên thang
– Như vậy, khi nhân vật đứng trên vùng StairBottom mà bấm phím UP (mũi tên đi lên) thì bạn bật trạng thái cho nhân vật là đang trên cầu thang. Hay nói cách khác, khi nhân vật bấm phím lên, bạn phải kiểm tra xem nhân vật có va chạm bằng AABB với StairBottom không? Nếu có thì bắt đầu cho nhân vật đi trên cầu thang.
Nói thêm: Trong trường hợp này, mình phải sử dụng AABB thay vì Swept AABB vì rõ ràng có thể nhân vật của mình đã va chạm với Object ẩn này trước khi bấm lên, nên SweptAABB không kiểm tra được va chạm này. (Hay nói cách khác dùng sweptAABB trong khi đã overlapping thì không kiểm tra được)
– Khi đi lên thì tùy vào game của bạn, có thể thang đều thì bạn có thể cho vận tốc vx, vy đều nhau,… Như vậy là xong phần lên thang.
c. Xử lí bắt đầu đi xuống thang
– Cũng tương tự như việc bắt đầu đi lên thang, bạn kiểm tra nhân vật có va chạm với StairTop và có bấm phím xuống hay không? Nếu có thì bật trạng thái đang trên thang.
d. Xử lí đi lên, đi xuống khi đã trên thang
– Sau những xử lí về bắt đầu lên thang, bắt đầu xuống thang, mình sẽ có được trạng thái Đang ở trên thang, lúc này mình chỉ việc kiểm tra, nếu đang trên thang mà bấm lên thì mình cho nhân vật đi lên, tương tự bấm xuống thì đi xuống.
– Đồng thời cũng chạy các animation về việc đi đứng trên thang.
e. Xử lí ra khỏi thang
– Tiếp đến, kết thúc ra khỏi thang, nếu nhân vật của bạn đi lên mà chạm StairTop thì bạn có thể kết thúc việc nhân vật bạn đang trên thang.
– Việc ra khỏi thang ở dưới chân cầu thang cũng tương tự.
Tất nhiên là phần xử lí về nó vẫn là 1 vấn đề khó, nên mình sẽ nói ở phần bên dưới.
3. Cách cài đặt
a. Object StairBottom & StairTop
Các thuộc tính cần có:
- Thuộc tính cơ bản của object như x, y, awidth, height (Cái này tùy vào code của bạn,chủ yếu để xét va chạm)
- int Direction. (hướng cầu thang, tùy vào game ở đây mình sẽ quy ước theo game mình đang làm là: -1: cầu thang hướng từ phải qua trái tính từ chân cầu thang, 1: hướng từ trái qua phải tính từ chân cầu thang).
Các object này các bạn tự tạo và thêm vào game nhé. Và cũng lưu ý theo như quy ước trên thì 1 cầu thang sẽ có đầy đủ StairBottom và StairTop, đồng thời sẽ có cùng hướng (Direction là -1 hoặc 1).
b. Object Player
– Theo mình ở object này nên có biến quản lí hướng cầu thang mà player đang đi (int directionStair). (Gán hướng cầu thang ngay khi bắt đầu vào thang từ Top hoặc Bottom. Cái này bạn có thể dùng nó để load các animation cho phù hợp, mình sẽ không nói rõ phần này.
– biến quản lí player có đang trên thang không (bool isOnStair).
– Phương thức SetSpeed(vx,vy);
c. Xử lí keyboards
– Phần này các bạn có thể dùy keydown hoặc keystate tùy ý, vì cũng không có khái niệm đúng sai rõ ràng trong trường hợp này, mà chỉ có thể nói là bạn chạy được hay không và có xử lí hết trường hợp hay không thôi.
c. 1. khi player bấm UP
– Dựa vào mô tả ý tưởng bên trên, khi player bấm UP, có thể có 2 trường hợp.
Trường hợp thứ nhất: Player chưa ở trên thang (isOnStair == false):
- Kiểm tra bằng AABB xem có va chạm với StairBottom nào không? Nếu có thì isOnStair = true; và player->directionStair = StairBottom->Direction;
Trường hợp thứ 2: Player đã ở trên thang (isOnStair == true):
- Cấp cho player vận tốc để player có thể đi lên. Cái này phụ thuộc vào hướng cầu thang, code bạn có đổi trục hay không? v.v..
- player->SetSpeed(player->directionStair * PLAYER_SPEED_ON_STAIR, -PLAYER_SPEED_ON_STAIR)
- PLAYER_SPEED_ON_STAIR là hằng số quản lí vận tốc đi trên thang. Như vậy khi set vx là player->directionStair * PLAYER_SPEED_ON_STAIR bạn sẽ tổng quát được cả cho cầu thang hướng 1 và -1. Ví dụ thang hướng -1, nghĩa là hướng từ phải sang trái, khi đó player đi lên có nghĩa là trục x của player sẽ giảm nên vx phải âm, tương tự thang hướng 1 thì vx phải dương. Phần về vy thì mình nghĩ các bạn sẽ hiểu.
- Ngoài ra bạn cũng cần đổi hướng của player, có thể là phục vụ cho animation chẳng hạn: player->SetDirection(player->directionStair); Hướng mặt player là hướng của cầu thang.
c. 2. khi player bấm DOWN
Khi bấm DOWN về cơ bản cũng tương tự cách xử lí như trên nhưng có khả năng sẽ sinh ra 1 trường hợp khác tùy game của bạn.
Phần chung thì sẽ có 3 trường hợp chính:
Trường hợp 1: Chưa trên cầu thang (isOnStair == false):
- Kiểm tra bằng AABB xem có va chạm với StairTop nào không? Nếu có thì isOnStair = true; và player->directionStair = StairBottom->Direction;
Trường hợp 2: Đã trên cầu thang (isOnStair == true):
- Cấp cho player vận tốc để player có thể đi xuống. Về các xử lí cũng tương tự như trên.
- player->SetSpeed(- player->directionStair * PLAYER_SPEED_ON_STAIR, PLAYER_SPEED_ON_STAIR)
- PLAYER_SPEED_ON_STAIR là hằng số quản lí vận tốc đi trên thang. Như vậy khi set vx là -player->directionStair * PLAYER_SPEED_ON_STAIR bạn sẽ tổng quát được cả cho cầu thang hướng 1 và -1. Ví dụ thang hướng -1, nghĩa là hướng từ phải sang trái, khi đó player đi xuống có nghĩa là trục x của player sẽ tăng nên vx phải dương, tương tự thang hướng 1 thì vx phải âm. Phần về vy do đi xuống nên mình phải để vy dương.
- Thay đổi hướng mặt của player: player->SetDirection(-player->directionStair);
Trường hợp thứ 3: Nhân vật ngồi xuống như bình thường.
d. Xử lí trong Update của Player
Theo mình thì trong Update của player sẽ là nơi xử lí các va chạm đất, xử lí trọng lực nhân vật, tính toán cập nhật x, y của nhân vật dựa vào vận tốc… Chính vì vậy mà phần lớn, xử lí việc cho nhân vật di chuyển trên thang như thế nào, ra thang làm sao? đều nằm ở đây.
- Khi player đang trên thang bạn phải tắt trọng lực, để tránh việc bạn đang đứng trên thang lại bị kéo xuống bởi trọng lực.
- Đang trên thang phải tắt xử lý va chạm với đất. Tránh trường hợp player sẽ bị vướng lại khi vô tình chạm đất.
Nếu player đang trên thang (isOnStair ==true) thì bạn xét va chạm như sau:
d. 1. Trường hợp player đang đi lên
– Kiểm tra xem player có chạm StairTop hay không?
Nếu có, nghĩa là ra khỏi cầu thang. Tuy nhiên, ngay đây nếu bạn chỉ tắt trạng thái đang trên thang hoặc nếu không khéo, player của bạn sẽ overlapping với Brick gây ra hiện tượng bị rớt xuống đất phía dưới.
Lúc này bạn sẽ đặt ra câu hỏi, tại sao không kéo StairTop cao hơn để khi vừa chạm, tắt trạng thái trên thang là xong? Tuy nhiên trở về ý tưởng ban đầu mình nói, mình sử dụng Bottom và Top như 1 tín hiệu vào và ra thang, có nghĩa là nó dùng cho cả 2 mục đích này. Nên object top mình chỉ đặt vừa chạm 1 tí khi player đứng thẳng ở trên là được.
Lúc này bạn lại hỏi, tại sao Object Top lại nằm trên đầu, mà không phải ở dưới ngang chân player chẳng hạn?
Mình đặt như vậy, bởi vì mình muốn khi player chạm Top thì sẽ đứng ngay trên đất, nếu theo ý muốn vừa nói, mà đặt Top ở dưới 1 chút, thì khi player chưa đi hết cầu thang, nó sẽ…. đùng 1 phát đứng ngay trên đất. (Vô lý).
– Ok sau khi hiểu xong về ý đồ của Object Top, mình sẽ trở lại vấn đề code như thế nào để nhân vật ra khỏi thang và đứng trên đất tốt nhất. Khi chạm Object trên, mình sẽ cần sử dụng sweptAABB để player có thể đứng được trên đất. Phần này mình sẽ không giải thích, vì nếu bạn làm player đứng trên đất được thì có lẽ đã hiểu.
– Tuy nhiên đời không như mơ, dùng sweptAABB nhưng vẫn lọt, là do ngay khi chạm vào, player có thể đã overlapping với đất, do mình đặt Object Top sao cho chạm 1 ít với player. Vậy lúc này có thể bạn sẽ kéo player lên (y=y- …..) để vừa đủ đứng trên đất.
Tuy nhiên, kéo bao nhiêu mới là đủ?
Do việc đi đứng có phụ thuộc vào vận tốc, dt và có thể mỗi thang bạn đặt Top ở độ cao không lý tưởng (rất có khả năng xui là trừ không đủ nên overlapping tiếp, mà chuyện hên xui trong lập trình là điều tối kị), nên thật sự để xác định chính xác là khá khó.
Để giải quyết, bạn chỉ cần kéo player lên cao ví dụ y-=100px sau đó cho vận tốc kéo xuống cực lớn ví dụ vy = 99999.0f sau đó dùng sweptAABB để player đứng trên đất và tắt các trạng thái trên thang, kết thúc quá trình ra khỏi thang. Việc đặt vy cực lớn mục đích giải quyết vấn đề này ngay trong 1 frame, để không xảy ra cảm giác player nhảy lên rồi rơi xuống từ từ.
– Nếu không va chạm Top thì x, y cập nhật bình thường x+=dx; y+=dy;
Lưu ý:
– Trước khi thay đổi vy bạn phải lưu lại vy hiện tại, và khôi phục ngay sau khi kết thúc việc dùng sweptAABB. Tránh trường hợp ảnh hưởng việc xét va chạm của player với các object khác.
– Khi thay đổi vy đồng thời phải cập nhật lại dy=vy+dt;
d. 2. Trường hợp player đang đi xuống
Tương tự như trường hợp player đi lên.
Bạn chỉ cần kiểm tra nếu đang đi xuống mà lại chạm Bottom thì kiểm tra bằng swept xem có va chạm đất chưa, nếu va chạm rồi thì kết thúc việc đi trên thang. Trường hợp này chỉ khác ở trên là không cần kéo player lên.
– Nếu không va chạm Top thì x, y cập nhật bình thường x+=dx; y+=dy;
4. Xử lí nâng cao
– Hướng dẫn bên trên là khá đơn giản, tuy vậy, để có những bước đi mượt và đều trên cầu thang, bạn phải nâng cao hơn khá nhiều, cụ thể:
– Ở game castlevania khi player đứng gần cần thang, ngược hướng, hoặc xung quanh thang mà bấm phím để vào thang, player sẽ tự đi đến mức nào đó xong mới bước lên thang. Làm điều này là không thừa và rất cần thiết, việc neo 1 điểm cố định trước khi lên thang sẽ giúp player luôn bắt đầu đi tại vị trí đó, nếu không sẽ có lúc bạn đi lệch thang, có lúc đi đúng.
– Nâng cao thứ 2, nếu bạn để ý ở game gốc, mỗi lần bấm phím UP player sẽ đi 1 bậc. Như vậy bạn nên làm cho player của minh “Đi theo lượt” mỗi lần bấm đi, player phải đi đúng 1 nấc thang mới được dừng. Và nếu để ý thêm bạn sẽ thấy mỗi bậc thang sẽ có kích thước 16px, như vậy nếu chuẩn hơn, bạn nên giới hạn 1 lần đi là 16px. Tuy nhiên do phụ thuộc vào dt nên đôi khi sẽ lệch trong quá trình đi lên và xuống nên nếu 1 lượt đi vượt quá 16px bạn hãy đặt lại vị trí player, để bỏ đi phần dư đó.
Tóm lại, bạn có thể:
– Viết thêm hàm để thiết lập việc tự đi cho player.
– Neo vị trí khi bắt đầu lên cầu thang để luôn có 1 cách đi lý tưởng nhất.
– giới hạn mỗi lượt đi phải đúng 16px, không hơn không kém.
Ngoài ra, tùy vào đặc tính game bạn có thể chặn các vấn đề khác như: nhảy trên thang, đụng trúng enemy trên thang sẽ không bị văng, hoặc liên quan đến nhiều hoạt động khác (đi, đứng,..). Bạn nên kiểm soát chặt điều kiện.
Chúc các bạn thành công :))