Trouble with Raycast :(

I have mines out in my game, but they are only sometimes doing damage. There are no obstructing walls or other objects.

It's set up to raycast from the center of the explosion to the player and check if it finds the player - so that the explosion doesn't go through walls.

The problem I'm having is that I am often not able to get a hit to register. - but it does find a start and an end. Is there a minimum distance required for a raycast? Could it be because the player is moving?

Here is the part of my code that gets the startPos (position of the mine) and the endPos is at the player for a raycast.

local startPos = center    --- location of mine
local endPos = player:GetWorldPosition() + player:GetWorldTransform():GetUpVector()*50

print("startPos: ", startPos)
print("endPos: ", endPos)

local hit = World.Raycast(startPos, endPos)

if (hit == nil) then
	print("hit is nil")
elseif (hit.other == nil) then
	print("hit other is nil")
elseif (hit.other ~= player) then
	print("hit other is not player")

if(hit ~= nil and hit.other ~= nil and hit.other == player) then
	print("player hit:",
	-- apply damage

Sometimes it detects a hit and sometimes it doesn't. Below is what printed out from that after running over the mines. Only 2 say player hit.
Most say hit is nil.

startPos: X=421.777 Y=-1101.906 Z=-0.500
endPos: X=353.075 Y=-1049.293 Z=151.931
hit is nil

startPos: X=896.615 Y=-847.272 Z=-0.500
endPos: X=907.623 Y=-918.088 Z=151.931
player hit: Bot1

startPos: X=1352.508 Y=-608.237 Z=-0.500
endPos: X=1417.427 Y=-605.847 Z=151.931
hit is nil

startPos: X=89.154 Y=175.976 Z=-0.500
endPos: X=156.415 Y=130.417 Z=151.931
hit is nil

startPos: X=-612.749 Y=459.474 Z=-0.500
endPos: X=-549.406 Y=427.239 Z=151.931
hit is nil

startPos: X=-1102.584 Y=-539.945 Z=-0.500
endPos: X=-1073.672 Y=-465.573 Z=151.931
player hit: Bot1

I'm having a hard time figuring out what could cause the hit to be nil. :frowning:

Could it be because the player is running in combination with lag? I'm using multiplayer preview mode.


I tested this on my end. It looks like the issue is caused by gaps in the player mesh during running animations. If you draw a debug line you can see this happening:

2021-04-21 10-42-05

Normally I think a good solution for this would be to do a Spherecast to account for these sorts of gaps but it doesn't look like we have access to different cast types. Some alternative solutions I can think of are:

  1. Write a Raycast function which shoots multiple rays around a target point (e.g. corners of a box or sweeping along an arc).
  2. Consider a nil hit to be a valid damage dealer - a nil hitResult means that the Raycast reached its target point without hitting an obstacle. You could modify your checks to only disallow damage when the raycast hits something that isn't the player.
  3. Raycast at a Socket on the Player so the hit position stays relative to some body part (Example Below).

Socket Raycast Example
I had a read through the API docs - I don't think it's possible to fetch the position of a socket on the player via a single function call, someone correct me if I'm wrong and we can simplify this answer a bit.

Step 1 - SocketAttachment Script
This script (in a client context) will simply attach itself to a specific socket on the local player. We can reference it later to get the world position of a socket.

local player = Game.GetLocalPlayer();
local targetSocketName = script:GetCustomProperty("SocketName");
script:AttachToPlayer(player, targetSocketName);

Step 2 - Add SocketName Property to SocketAttachment Script Object
Add a string property called SocketName to the SocketAttachment script object in the hierarchy. Set the value to the name of the body part you wish the Raycast to target (use Socket Reference to choose a body part). I've selected 'pelvis' here.

Step 3 - Add SocketAttachment reference to Mine Script
On your Mine script object in the hierarchy, drag and drop in the SocketAttachment object we made earlier. Name it SocketAttachment.

Step 4 - Modify Mine Script
At the top of your script, fetch the socket attachment property:

local socketAttachment = script:GetCustomProperty("SocketAttachment"):WaitForObject()

Then modify your endPos variable to instead fetch the socket position:

local endPos = socketAttachment:GetWorldPosition()

This should cause the Raycast to get fired at a specific body part and eliminate those gaps.

2021-04-21 11-27-06

Thanks so much for your help! This was driving me crazy.