workspace:Blockcast() is acting weird

I was building a custom character controller by using blockcast for the collisions, but blockcast is not working as I expected it to. When you use workspace:Blockcast() with a direction of Vector3.new(0, -1, 0), the result is not what you would expect.

When you land, you get in a slightly different position than expected.

Instead of using blockcast, I tried raycasting from all four corners of the block and selecting the first raycast. Is there another alternative to workspace:Blockcast()?

Could you please provide the code? Particularly the one where you show how you create the direction?

2 Likes

Try using this to get the end position of the blockcast

BlockcastOriginPosition + BlockcastDirection.Unit*BlockcastResult.Distance

You are using the intersection point of the blockcast.

I got this formula from the release post.

1 Like

Sure! Do you need the place file?

local Raycast = require(RaycastModuleThingy)

--Physics.MACHINE_EPSILION = 0.02

export type Character = {
	name: string,
	
	bbox: Vector3,
	
	position: Vector3, 
	velocity: Vector3,

	modelPose: {
		["RootJoint"]: {
			C0: CFrame, 
			C1: CFrame 
		},

		["Neck"]: {
			C0: CFrame, 
			C1: CFrame 
		},

		["Left Hip"]: {
			C0: CFrame, 
			C1: CFrame 
		},

		["Left Shoulder"]: {
			C0: CFrame, 
			C1: CFrame
		},

		["Right Hip"]: {
			C0: CFrame, 
			C1: CFrame 
		},

		["Right Shoulder"]: {
			C0: CFrame,
			C1: CFrame 
		}
	},

	ownerUserId: number,

	ownerOnly: {
		jumpBias: number,
		
		airControl: number,
		airControlRecoveryDelay: number,
		airControlLock: boolean,

		grounded: boolean,
		
		stance: number,
		crouching : boolean,

		health: number,

		primaryFireBuffer: boolean,
		secondaryFireBuffer: boolean,
		jumpBuffer: boolean,

		currentSlot: number,
		inventory: {[number]: string},
	},

	serverOnly: {
		healthDecay: number,
		recentDamage: {[number]: number},
	},
	
	Think: Think,
	Control: Control
}


	Step = function(self: Character, fraction: number)
		local ownerOnly = self.ownerOnly
		
		local resolutionAttempts = 0
		local maxAttempts = 16
		
		local previousNormal = Vector3.zero
		
		local stepDist = 1.1
		
		local model = nil
		local params = Raycast.newRaycastParams({model})
		
		local previousPosition = self.position
		
		repeat	
			resolutionAttempts += 1
			
			local travel = self.velocity * Physics.DELTA_TIME * fraction
			local flatTravel = Vector3.new(travel.X, 0, travel.Z)
			
			local miniOffset = self.velocity.Unit * Physics.MACHINE_EPSILON
			
			local sweepcastResult = Raycast:blockCast(self.position - miniOffset, travel + miniOffset, self.bbox, params)
			
			if not sweepcastResult then
				self.position += travel

				if ownerOnly.grounded then
					local groundStickCheck = Raycast:blockCast(self.position, Vector3.new(0, -stepDist - Physics.MACHINE_EPSILON, 0), self.bbox, params)
					
					if groundStickCheck and groundStickCheck.Distance >= Physics.MACHINE_EPSILON and groundStickCheck.Normal.Y > module.MaxSlope  then
						self.position += groundStickCheck.Position + Vector3.new(0, Physics.MACHINE_EPSILON, 0)
					end
				end

				break
			else
				if sweepcastResult.Normal.Y > module.MaxSlope then
					if not ownerOnly.grounded then
						--We just landed
						ownerOnly.grounded = true
						ownerOnly.airControlLock = false
						
						print(self.position - miniOffset)
						
						print(travel + miniOffset) --something like 0, -0.1923, 0
					end
				else
					if ownerOnly.grounded then
						
						local stepUpCast1 = Raycast:blockCast(self.position, Vector3.new(0, stepDist, 0), self.bbox, params)
						
						if not stepUpCast1 then
							local upPos = self.position + Vector3.new(0, stepDist, 0)
							
							local stepUpCast2 = Raycast:blockCast(upPos, flatTravel, self.bbox, params)
							
							if not stepUpCast2 then
								local acrossUp = upPos + flatTravel
								
								local stepUpCast3 = Raycast:blockCast(acrossUp, Vector3.new(0, -stepDist - Physics.MACHINE_EPSILON, 0), self.bbox, params)
								
								if stepUpCast3 and stepUpCast3.Distance <= stepDist + Physics.MACHINE_EPSILON and stepUpCast3.Normal.Y > module.MaxSlope then
									self.position = stepUpCast3.Position + Vector3.new(0, Physics.MACHINE_EPSILON, 0)
									
									break
								end
							end
						end
					end
				end
				
				local position, normal = sweepcastResult.Position, sweepcastResult.Normal
				
				self.position = position - miniOffset
				self.velocity = Physics.ClipVector(self.velocity, normal) + (normal * Physics.MACHINE_EPSILON)
				
				--If we are jammed between two spaces, then try to squeeze out
				if previousNormal:Dot(normal) < 0 then
					local comboNormal = (previousNormal + normal).Unit
					
					self.velocity = Physics.ClipVector(self.velocity, previousNormal)
					self.velocity = Physics.ClipVector(self.velocity, comboNormal) + (comboNormal.Unit) - miniOffset
					self.velocity += (normal * Physics.MACHINE_EPSILON) + (comboNormal.Unit * Physics.MACHINE_EPSILON) - miniOffset
					self.position += comboNormal.Unit * Physics.MACHINE_EPSILON
					
					print("jammed!")
				end
				
				previousNormal = normal
			end
			
			if resolutionAttempts >= maxAttempts then
				warn("Stuck! Giving up on collision resolution!")
			end
		until resolutionAttempts >= maxAttempts
		
		--[[
		if stuck then
			self.position = previousPosition
		end
		]]--
	end,

Hmmm…

function module:blockCast(origin, direction, size, params)
	if origin ~= origin then
		return nil
	end
	
	if direction ~= direction then
		return nil
	end
	
	if size ~= size then
		return nil
	end

        --Origin and direction are offset, because objects too close to the origin are ignored
	
	local multi = 1
	
	local offset = direction.Unit * multi
	
	local newOrigin = CFrame.new(origin - offset)
	local newDir = direction + offset
	
	if not params then
		params = RaycastParams.new()
		params.CollisionGroup = "Hitscan"
	end
	
	local blockcast = workspace:Blockcast(newOrigin, size, newDir, params)
	
	if blockcast then
		return {
			Instance = blockcast.Instance,
			Position = newOrigin.Position + (newDir.Unit * blockcast.Distance),
			Normal = blockcast.Normal, blockcast,
			Material = blockcast.Material,
			Distance = blockcast.Distance
		}
	end
	
	return nil --workspace:Blockcast(newOrigin, size, newDir, params)
end