mrhands

Sexy game(s) maker

  • he/him

I do UI programming for AAA games and I have opinions about adult games


Discord
mrhands31

I spent my entire Sunday getting the player's gun to attach in front of the player. You see, I'm using Box2D for my physics and pushing my entities (player, bullets, enemies, etc.) around by setting a linear velocity every frame. That works great in most cases, except when you want something with a parent-child relationship, like a gun entity hovering in front of the player entity.


To make something physically-enabled with Box2D, you give it a body and a fixture. The body defines the type (static, dynamic, or kinematic) and the position in the world. The fixture defines the shape, the density, the friction, and all that good stuff. The separation of concerns is confusing, but I'm sure the library authors had their reasons. One advantage is that you can make a new fixture that attaches to an existing body. This was my first attempt, and it works pretty well. Box2D will automatically match the new collision shape with the existing body's position. Unfortunately, you can't do offsets with this technique, so you can't make the gun hover in front of the player. Which is, again, what I'm really looking to do.

Next, I looked into joints, which was a very deep rabbit hole indeed. These objects are used to attach fixtures in some physically-accurate manner. This overview page was a huge help because joints are one hell of a confusing mess. "Alright," says Box2D, "so you're looking for a joint? We've got weld joints, line joints, distance joints, revolute joints, prismatic joints, pulley joints, gear joints, and mouse joints. Good luck!"

Long story short, none of these joint types worked for me. Because the gun doesn't have a velocity of its own, all the joints can do is drag the gun behind the player. But if I need to match the velocity exactly, why bother using a joint? I can use the parent's world transform to offset the child object directly, which is what I ultimately ended up doing:

if (auto anchorCom = params.registry.try_get<AnchorComponent>(entity))
{
	if (auto parentPhysicsCom = params.registry.try_get<PhysicsComponent>(anchorCom->parent))
	{
		auto parentBody = parentPhysicsCom->body;

		glm::mat3 model = glm::rotate(
			glm::mat3{ 1.0f },
			glm::radians(transformCom.rotationDegrees)
		);
		glm::mat3 view = math::toGlmMat3(parentBody->GetTransform());

		glm::mat3 mvp = m_projection * view * model;

		transformCom.position = glm::xy(mvp * anchorCom->parentLocalAnchor);

		physicsCom.velocity = math::toGlm(parentBody->GetLinearVelocity());
	}
}

If it's ugly, but it works, ship it.


You must log in to comment.

in reply to @mrhands's post:

hm, can't you weld two bodies without actually having them touching? like, weld them relative to each other

i'm not actually sure but this seems like something that should definitely be supported already

I've tried all the joint types! Technically, the weld joint works and keeps the bodies in the same position relative to each other. But the weapon doesn't rotate around the player when I use that joint type. The prismatic joint keeps it on the same axis but doesn't rotate around either. The revolute joint behaves the same as the weld joint. Like, I'm probably not understanding this whole """physics""" correctly, but what I want is probably not physically accurate anyway. Using a parent transform has proven workable for me in this case.

Omfg I've figured it out. If you want the gun to rotate around the player with a weld joint,,,you have to,,,,,,,,,,,,actually,,,,rotate the player,,,,,,,,

	auto view = params.registry.view<TransformComponent, PhysicsComponent>();
	view.each([](auto entity, TransformComponent& transformCom, PhysicsComponent& physicsCom) {
		physicsCom.body->SetLinearVelocity(math::toBox2D(physicsCom.velocity));
		physicsCom.body->SetTransform(
			physicsCom.body->GetPosition(),
			glm::radians(transformCom.rotationDegrees)
		);
	});