iT邦幫忙

2025 iThome 鐵人賽

DAY 29
1

Greetings~ 這裡是鐵匠史密斯!
昨日確定了角色橫移、旋轉矩陣的功能
接下來就是碰撞檢測的部分了

前情提要

還記得我們目前的功能嗎?
當玩家超出界線外,會看到無盡的牆壁
https://ithelp.ithome.com.tw/upload/images/20250829/20157653b764K43gru.png

// Check if ray is out of bounds
if ((nTestX < 0) || (nTestX >= nMapWidth) || (nTestY < 0) || (nTestY >= nMapHeight))
{
    bHitWall = true; // Out of bounds, we hit the wall
    fDistanceToWall = fDepth; // Set distance to the maximum depth
}
else // If ray is still in bounds
{
    if (map[nTestY * nMapWidth + nTestX] == '#') // If we hit a wall
    {
        bHitWall = true;
    }
}

現在,我們要避免玩家超出界線外

如何限制玩家? 得由 map 來決定

當我們進行移動( W / S / A / D)時,
fPlayerX 以及 fPlayerY 都會隨時記錄著我們的位置
玩家當下的位置,想必也會與世界地圖 map 同步,
我們可以藉由玩家在地圖 map 的當前位置,查看是否為 # (牆壁)
如果:

  • 玩家前進一步,是牆壁 => 就後退一步回到原點
  • 玩家前進一步,不是牆壁 -> 就繼續前進

我們可以判定玩家在前進一步時在 map 地圖上的位置是否為牆壁 # 來決定玩家前進的這一步是否有效~

 if (GetAsyncKeyState((unsigned short)'W') & 0x8000)
 {
     fPlayerX += sinf(fPlayerA) * fElaspedTime * 5;
     fPlayerY += cosf(fPlayerA) * fElaspedTime * 5;

     if (map[(int)fPlayerY * nMapWidth + (int)fPlayerX] == '#')
     {
         fPlayerX -= sinf(fPlayerA) * fElaspedTime * 5;
         fPlayerY -= cosf(fPlayerA) * fElaspedTime * 5;
     }
 }

 if (GetAsyncKeyState((unsigned short)'S') & 0x8000)
 {
     fPlayerX -= sinf(fPlayerA) * fElaspedTime * 5;
     fPlayerY -= cosf(fPlayerA) * fElaspedTime * 5;


     if (map[(int)fPlayerY * nMapWidth + (int)fPlayerX] == '#')
     {
         fPlayerX += sinf(fPlayerA) * fElaspedTime * 5;
         fPlayerY += cosf(fPlayerA) * fElaspedTime * 5;
     }
 }

 // Move Left&Right in the map
 if (GetAsyncKeyState((unsigned short)'A') & 0x8000)
 {
     fPlayerX += cosf(fPlayerA) * fElaspedTime * 5;
     fPlayerY -= sinf(fPlayerA) * fElaspedTime * 5;


     if (map[(int)fPlayerY * nMapWidth + (int)fPlayerX] == '#')
     {
         fPlayerX -= cosf(fPlayerA) * fElaspedTime * 5;
         fPlayerY += sinf(fPlayerA) * fElaspedTime * 5;
     }
 }

 if (GetAsyncKeyState((unsigned short)'D') & 0x8000)
 {
     fPlayerX -= cosf(fPlayerA) * fElaspedTime * 5;
     fPlayerY += sinf(fPlayerA) * fElaspedTime * 5;

     if (map[(int)fPlayerY * nMapWidth + (int)fPlayerX] == '#')
     {
         fPlayerX += cosf(fPlayerA) * fElaspedTime * 5;
         fPlayerY -= sinf(fPlayerA) * fElaspedTime * 5;
     }
 }

p.s. 記得,map 是 1-D wstring array -> 使用 index (int) 提取矩陣的值。

在移動的功能( W / S / A / D) 增加判定式,就可以進行碰撞偵測了~
https://ithelp.ithome.com.tw/upload/images/20250829/201576539N4k0uCCGR.png

今日總結

  • 原本光線檢測時,只處理了「超出界線 → 畫無盡牆壁」的情況

  • 為了避免玩家衝出地圖,我們在( W / S / A / D) 的移動程式碼中加上了「先移動 → 判斷是否撞牆 → 撞牆就退回」的邏輯

  • 藉由 map[(int)fPlayerY * nMapWidth + (int)fPlayerX] 來判斷玩家當前位置是否為 '#' 牆壁,實現了 簡單的碰撞偵測

現在,我們有了方向旋轉前後左右移動牆壁與地板渲染碰撞偵測 ——
基本上,已經是一個完整的第一人稱小遊戲引擎了。

明天就是 Day30 最終回,我會做一個完整的總結,回顧這一路的推導、程式碼與學到的觀念,
也會講講接下來繼續這一系列文章的下一步。

鐵人賽 30 天的旅程,終於要走到終點了。
最後一哩路,我們繼續走下去!


上一篇
Day 28 | Ray Casting 角色橫移 - Part 2
下一篇
Day 30 | Ray Casting :30 天鐵人挑戰的完結篇
系列文
用 C++ 實作簡易第一人稱視角遊戲:從入門到理解 Ray Casting30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言