Created
February 1, 2026 19:14
-
-
Save KiraStack/6acd4ca5abf7e37f4f56232ac68543be to your computer and use it in GitHub Desktop.
Shared module for generating and caching chunks for collision.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --[=[ | |
| @class TerrainCollision | |
| @deprecated | |
| Shared module for generating and caching chunks for collision. | |
| ]=] | |
| -- Could use convex hulls (bloat) | |
| -- but it is more complex and memory intensive | |
| -- Constants | |
| local GRIDSIZE = 4 | |
| local FATGRIDSIZE = 16 | |
| local SKIN_THICKNESS = vector.create(0.1, 0.1, 0.1) | |
| -- Instances | |
| local module = { | |
| grid = {}, | |
| fatGrid = {}, | |
| } | |
| -- Types | |
| type self = typeof(module) | |
| --[=[ | |
| @within TerrainCollision | |
| @function FindAABB | |
| @param part BasePart | |
| @return number, number, number, number, number, number -- Handles AABB computation | |
| ]=] | |
| local function FindAABB(part: BasePart): (number, number, number, number, number, number) | |
| local pos = part.Position | |
| local scale = part.Size / 2 | |
| local minx, miny, minz = math.huge, math.huge, math.huge | |
| local maxx, maxy, maxz = -math.huge, -math.huge, -math.huge | |
| -- Define min bounds | |
| minx = math.min(minx, pos.X - scale.X) | |
| miny = math.min(miny, pos.Y - scale.Y) | |
| minz = math.min(minz, pos.Z - scale.Z) | |
| -- Define max bounds | |
| maxx = math.max(maxx, pos.X + scale.X) | |
| maxy = math.max(maxy, pos.Y + scale.Y) | |
| maxz = math.max(maxz, pos.Z + scale.Z) | |
| -- Return | |
| return minx, miny, minz, maxx, maxy, maxz | |
| end | |
| --[=[ | |
| @within TerrainCollision | |
| @method Sweep | |
| @param a vector | |
| @param b vector | |
| @return table [Distance, Fraction, Instance, Normal, Position] -- sweep result | |
| ]=] | |
| function module:Sweep( | |
| a: vector, | |
| b: vector | |
| ): { Distance: number, Fraction: number, Instance: BasePart | nil, Normal: vector, Position: vector } | |
| local data = { | |
| Distance = 0, | |
| Fraction = 1, -- refers to distance between `a` and collision point | |
| Instance = nil, | |
| Normal = vector.create(0, 1, 0), -- default to up | |
| Position = b, -- default to given end position | |
| } | |
| -- Cache common variables | |
| local checks = 0 | |
| local direction = (b - a) | |
| local distance = vector.magnitude(direction) | |
| local steps = math.ceil(distance / GRIDSIZE) | |
| local delta = direction / steps | |
| -- Loop through each step | |
| for i = 1, steps do | |
| -- Cache | |
| local pos = a + (delta * i) | |
| local min = pos - SKIN_THICKNESS | |
| local max = pos + SKIN_THICKNESS | |
| local candidates = self:QueryBox(min, max) | |
| checks += #candidates | |
| -- Loop through each candidate | |
| for _, candidate: BasePart in ipairs(candidates) do | |
| -- Cache | |
| local cminx, cminy, cminz, cmaxx, cmaxy, cmaxz = FindAABB(candidate) | |
| -- Compute intersection | |
| if | |
| max.x >= cminx | |
| and min.x <= cmaxx | |
| and max.y >= cminy | |
| and min.y <= cmaxy | |
| and max.z >= cminz | |
| and min.z <= cmaxz | |
| then | |
| -- Found collision | |
| data.Distance = distance * data.Fraction | |
| data.Fraction = (i - 1) / steps -- (i - 1) because `i` is 1-indexed, but `Fraction` (>= 0) is 0-indexed | |
| data.Position = a + direction * data.Fraction | |
| data.Instance = candidate | |
| print(candidate) | |
| -- Return | |
| return data | |
| end | |
| end | |
| end | |
| -- No collision found | |
| data.Distance = math.huge -- distance is (basically) inf. at this point | |
| return data | |
| end | |
| --[=[ | |
| @within TerrainCollision | |
| @method QueryBox | |
| @param min vector | |
| @param max vector | |
| @return () -- Handles AABB query | |
| ]=] | |
| function module.QueryBox(self: self, min: vector, max: vector): { BasePart } | |
| local results = {} | |
| local size = vector.magnitude(max - min) | |
| -- Handle fat grid | |
| for x = (min.x // FATGRIDSIZE), (max.x // FATGRIDSIZE) do | |
| for y = (min.y // FATGRIDSIZE), (max.y // FATGRIDSIZE) do | |
| for z = (min.z // FATGRIDSIZE), (max.z // FATGRIDSIZE) do | |
| local key = vector.create(x, y, z) | |
| local cell = self.fatGrid[key] | |
| if cell then | |
| for i = 1, #cell do | |
| results[#results + 1] = cell[i] | |
| end | |
| end | |
| end | |
| end | |
| end | |
| -- Handle normal grid | |
| for x = (min.x // GRIDSIZE), (max.x // GRIDSIZE) do | |
| for y = (min.y // GRIDSIZE), (max.y // GRIDSIZE) do | |
| for z = (min.z // GRIDSIZE), (max.z // GRIDSIZE) do | |
| local key = vector.create(x, y, z) | |
| local cell = self.grid[key] | |
| if cell then | |
| for i = 1, #cell do | |
| results[#results + 1] = cell[i] | |
| end | |
| end | |
| end | |
| end | |
| end | |
| -- Return | |
| return results | |
| end | |
| --[=[ | |
| @within TerrainCollision | |
| @method Visualize | |
| @deprecated | |
| @param state boolean | |
| @return () -- Describe world grid | |
| ]=] | |
| function module.SpawnDebugGridBox(self: self, state: boolean) end | |
| --[=[ | |
| @within TerrainCollision | |
| @method ComputeGrid | |
| @return () -- Handles grid computation | |
| ]=] | |
| function module.ComputeGrid(self: self) | |
| self.grid = {} | |
| self.fatGrid = {} | |
| -- Loop through each part | |
| for _, part in pairs(workspace:GetDescendants()) do | |
| -- Handle ignored objects | |
| if not part:IsA("BasePart") or part:IsA("Terrain") or not part.CanCollide then | |
| continue | |
| end | |
| local minx, miny, minz, maxx, maxy, maxz = FindAABB(part) | |
| local size = vector.magnitude(vector.create(maxx - minx, maxy - miny, maxz - minz)) | |
| if size > FATGRIDSIZE then | |
| -- Handle fat grid | |
| for x = (minx // FATGRIDSIZE), (maxx // FATGRIDSIZE) do | |
| for y = (miny // FATGRIDSIZE), (maxy // FATGRIDSIZE) do | |
| for z = (minz // FATGRIDSIZE), (maxz // FATGRIDSIZE) do | |
| local key = vector.create(x, y, z) | |
| local cell = self.fatGrid[key] | |
| if not cell then | |
| cell = {} | |
| self.fatGrid[key] = cell | |
| end | |
| table.insert(cell, part) | |
| end | |
| end | |
| end | |
| else | |
| -- Handle normal grid | |
| for x = (minx // GRIDSIZE), (maxx // GRIDSIZE) do | |
| for y = (miny // GRIDSIZE), (maxy // GRIDSIZE) do | |
| for z = (minz // GRIDSIZE), (maxz // GRIDSIZE) do | |
| local key = vector.create(x, y, z) | |
| local cell = self.grid[key] | |
| if not cell then | |
| cell = {} | |
| self.grid[key] = cell | |
| end | |
| table.insert(cell, part) | |
| end | |
| end | |
| end | |
| end | |
| end | |
| end | |
| return module |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment