In my game, pets currently use a humanoid to navigate around the map. Anyone who is familiar with optimization knows that humanoids can be pretty expensive on resources, especially when there are 50+ players in a server, each with a humanoid pet (that means over 100 humanoids). I find this really inefficient, so I am seeking an alternative.
What I’ve Tried
Currently I am experimenting with using a BodyPosition and BodyGyro to face and follow the player, but you can guess that this creates a lot of unnatural movement compared to a humanoid. For example:
Notice how the “pet” is lifting off the ground. This is pretty unnatural. I can fix this by lowering the force on the respective axis, but as it moves along rough terrain, it gets caught and doesn’t hold it’s orientation as well, causing it to twist and turn and snap back into position.
What I Need
I need a better solution to making a natural-moving pet that doesn’t use a humanoid. It is very important that this pet is able to navigate over possibly rough terrain. I am not sure if BodyPosition and BodyGyro is the way to go. If it is, I would really appreciate if someone helped me adjust the values on those forces. If there is a more efficient option, I am all ears!
I have provided a sample place file and a pet. Don’t get hung up on my janky code - it’s just for testing and getting it to work. Thanks everyone playground.rbxl (405.4 KB)
The first thing I would suggest, is identifying and quantifying your performance problem, if there is one. In my experience, statements about what developers imagine are efficient, performant (or not) are more often than not preludes to many hours spent trying to solve problems that don’t exist, or making premature optimizations to systems that are working just fine. In some cases, creating new systems based on notions of what is efficient, rather than actual measurements, can result in a new system that is actually worse off in some way.
That said, if you wish to roll your own pet following solution, it’s not going to be as simple as giving the pet a target location to move towards. Without using any pathfinding, it’s going be very easy to get the pet stuck on things. PathfindingService will be useful here, and does not require a humanoid.
The pet lifting off the ground is just because you’re pointing it directly at the character’s HumanoidRootPart, which is normally a couple of studs off the ground, a bit above waist height. That’s not really the orientation you want to use for a natural motion. What you really want is for the pet (I’m assuming a turtle) to prefer to be upright, but to follow the contours of the ground when not on a level surface. This is most easily done by raycasting down (note: down is not necessarily -Y direction, but more likely the negation of your pet’s primary part CFrame’s upVector) and using the surface normal returned from the raycast. Your pet will orient to the surface it’s climbing if its BodyGyro is trying to align the pet’s upVector with the ground surface’s normal vector.
In practice, the natural damping of the BodyGyro may not be enough to smooth out motion. You’ll probably want more than just one ground sample point (more than one raycast), and some averaging of the results, and maybe even some integration over time.
Because your system will not have identical performance to a humanoid-based player character, it would be very wise to include a fail-safe mechanism whereby a pet that gets stuck too far from the player’s character has the ability to teleport closer. There is no shame in doing this, AAA titles with player-following NPCs have this kind of backup logic also.
Another thing you can do, is for the pet to be visually a turtle, but with its visible parts set to CanCollide false, and the whole critter welded to an invisible sphere that is collidable and with friction set very low. This is also a common tactic, as spheres are very resistant to getting hung up on ground details (when appropriately sized), and you can pretty much just push them with enough force. Sphere collisions are also computationally as cheap as you can get.
Thanks for the advice! I’m curious though, I was always told that raycasting is expensive on performance? I feel like 50-100 pets making raycasts every split second is pretty intensive. Also, I do have that fail-safe implemented in my current game Turtles teleport after 50 studs.
I actually experimented with this a little bit! But I never welded the physical parts. Won’t the welded parts rotate with the sphere rolling? Or am I supposed to keep the BodyGyro on the sphere?
Edit: I went ahead and implemented this. I think the sphere is going to be the solution - my problem before was not keeping the BodyGyro and the friction was also not set low. Thanks a bunch for your help! I really appreciate your time.
No, raycasts are relatively cheap as long as you don’t have a large white or black list of character or multi-part models. Short raycasts with just an ignored part or two are incredibly cheap. A few raycasts per frame from pets to the ground is not any kind of performance concern. It’s also not necessary to raycast on every frame if your turtles are moving at realistic turtle speeds, you can throttle it until you see accuracy suffer, then dial it back up a bit from there.
The BodyGyro will affect the whole assembly; the collision sphere is welded to the pet, part of the same model. The BodyGyro will prevent the sphere from rolling. It’s not necessary for the sphere to roll, it can just slide along and the rounded shape is enough to keep it from getting stuck on things. It could roll, but it would require something like a ball socket constraint rather than a weld in this case. Maybe try it?
Wow, I did not know that about raycasting. I kind of just assumed it was all expensive. That will help for the future. Also thank you for clarifying the ball mechanics. I am sticking with the BodyGyro and it seems to be performing pretty perfectly.
I do have one question if you have time - In my game currently that utilizes humanoids, I would have turtles jump over obstacles like fences if they got stuck. For obvious reasons, I can’t do the same as easily with this new contraption. What would you suggest for getting the turtle out of a situation like that? I feel that it is kind of clunky to path-find all the way around a fence versus just jumping over it.
Lots of options here. Some types of fencing I’d expect turtles to be able to crawl under, when there is a gap.
I can’t say I’ve ever observed a turtle jumping IRL, but realism aside, jumping is not a difficult thing to implement. The quick and dirty way is a BodyForce or LineForce for a few frames to launch them over. At full framerate, this works great. If the server lags for any reason, you can get some unexpectedly high jumps because you’ll apply the force for too long on a long frame. The more frame-rate stable way is to use force, but at jump time give the pet a quality of momentum (a jump impulse) which is depleted over the course of a few frames proportional to the elapsed time between frames (the delta argument you get with heartbeat or stepped). This sounds more complicated than it is, you’re really just saying “I want the jump force applied for 2.5 seconds, so I’ll set this variable to 2.5 and subtract dt from it on each heartbeat until it’s at or below zero, at which point the force is removed”. The idea here is a lower force, amortized over a variable number frames until the impulse is depleted, rather than a large force applied for a single (or fixed number) of frames. That’s how I do jumps in my custom character controllers, and it works pretty well.
The more cheaty option, which is particularly easy to do if you go with a non-collide character model, is to just make a jump animation and let any collidable parts pass through the fence (via collision groups, for example). As the collision part passes through a fence, trigger the animation
Awesome! Yeah I actually had initially used a BodyForce in my experimentation that had a lot of Y force and was destroyed with a script .05 seconds later. And I was worried that wouldn’t work on a laggy server as you said. Your animation idea is very enticing… however, I should note that the network ownership of the sphere part (which contains the body forces) is the player. I’m wondering if I initiate the jump from a local script if that will eliminate those high jumps?
Yes! I recommend using a sphere part for doing all the traversing along the terrain. Then put in a BodyPosition for movement and BodyGyro for rotation. My sphere part is just invisible and then I welded everything else to the sphere. Hope that made sense!
It’s actually quite simple. I just tell the sphere to move to a certain stud distance behind the player, for example, BodyPosition.Position = HumanoidRootPart.Position + HumanoidRootPart.CFrame.lookVector * -10
The BodyPosition has a weak Y MaxForce of 50, while X and Z are 750000. Dampening is set to 10000 and power set to 300000. I also set Density to 3, Friction and Elasticity to 0, and the weights to 1. I think the biggest factors are density and friction, so that it stays sliding against the terrain but doesn’t bump around too much. The only raycasting I do is to detect what material is below the sphere so I can see if I can make it jump if I want to. Hopefully that was helpful