A Raycast is a method of projecting an imaginary line (a "ray) from a specific point in a given direction to detect intersections with objects. You can think of it as a quick way to figure out what object would be "hit" if you fired an infinitely thin laser beam from a certain point in the direction of another point.
If the raycast hit an object, it gives information on the object hit. In HYTOPIA a hit object will be a block or entity.
If the raycast does not hit an object, it will return null.
You can control the length of the cast ray, it's origin and direction, and a number of options to ignore certain intersections, and more.
Basic Raycast Example
Here's some basic code showing how we can perform a raycast. Raycasts are performed from the simulation instance of a world as follows.
constorigin= { x:10, y:2, z:0 }; constdirection= { x:0, y:1, z:0 }; // raycast straight upconstlength=5; // Max length in blocks the ray travels constraycastResult=world.simulation.raycast(origin, direction, length);
Block Breaking & Placing Example
Let's make something more useful. How about we setup our players so they can use the left click of their mouse to break blocks in front of the direction their facing, and the right click to place a block.
We can do that as follows.
// ... other code// Enable debug raycasting so we can visualize our raycasts for testingworld.simulation.enableDebugRaycasting(true);world.onPlayerJoin= player => {constplayerEntity=newPlayerEntity({ player, name:'Player', modelUri:'models/player.gltf', modelLoopedAnimations: [ 'idle' ], modelScale:0.5, });// PlayerEntity by default has a PlayerEntityController assigned to .controller,// but we explicitly assert that with ! to prevent typescript from complaining.playerEntity.controller!.onTickWithPlayerInput= (entity, input, cameraOrientation, deltaTimeMs) => {if (input.ml ||input.mr) { // ml = mouse left clicked, mr = mouse right clickedconstorigin=entity.position; // start at the player's current position.constdirection=entity.player.camera.facingDirection; // cast in the camera directionconstlength=5;constraycastResult=world.simulation.raycast(origin, direction, length, {// Prevent the raycast from registering our player entity as the hit object.// Raycast options typically work using raw physics engine handles, so we// have to use the raw rigid body of our player entity. filterExcludeRigidBody:playerEntity.rawRigidBody, });if (raycastResult?.hitBlock) { // see if the result hit a blockif (input.ml) { // left click, break blockconstbreakPosition=raycastResult.hitBlock.globalCoordinate;world.chunkLattice.setBlock(breakPosition,0); // 0 = no block/air } else { // right click, place block as neighbor of the hit blockconstplacePosition=raycastResult.hitBlock.getNeighborGlobalCoordinateFromHitPoint(raycastResult.hitPoint);world.chunkLattice.setBlock(placePosition,1); // 1 = bricks in the default block types } }// Explicitly cancel inputs to prevent raycast spam each tick// A player will need to let go of their click and click againinput.ml =false;input.mr =false; } };playerEntity.spawn(world, { x:0, y:10, z:0 });};
Here's a gif showcasing how our block breaking and placing code using raycasts works!
Diving Deeper
Raycasts make use of a few systems. To learn more and understand all of the features of raycasts, we recommend the following resources.
If there are features that we don't currently support for raycasts that you'd like to see added to the HYTOPIA SDK, you can submit a feature request here.