Hello, can you help me with this Motor6D problem?

So, I have this tank that turns its turret with motor6D, but it only works correctly on 1 alightment. (I hope that make sense)

I’ve tried googling about this, but I only found very few relevant results… I did get to where I am now from the search, though. (It used to rotate randomly, but now there’s at least some consistency :slight_smile: )

while _alive.Value and currentTarget ~= nil and (currentTarget.Position - self.Position).Magnitude < _range do
			if currentTarget.Parent == nil then
				break
			end
			local property = {
				Position = currentTarget.Position
			}
			
			for i,turret in ipairs(turrets) do
				local _attackBullet = GetBulletFromTable()
				local _distance = (currentTarget.Position - self.Position).Magnitude
				local _timeTaken = _distance/ _bulletSpeed
				local p1 = turret.Position
				local p2 = currentTarget.Position
				local info = TweenInfo.new(_timeTaken, Enum.EasingStyle.Linear, Enum.EasingDirection.In, 0, false, 0)
				local _faceDir = Vector3.new(p2.x, self.Position.y,p2.z)
				local _forward = turret.CFrame:ToWorldSpace(CFrame.new(0,0,-1)).Position
				local _faceAngle = CFrame.lookAt(turret.Motor6D.C1.Position, Vector3.new(_forward.x, p2.y, _forward.z))
				local angle =  GetAngle(p1, p2)
				local turretC1 = CFrame.Angles(0,math.rad(angle),0) 
				
				_attackBullet.CFrame = CFrame.lookAt(p1, p2)
				turret.Motor6D.C1 = CFrame.new() * self.CFrame:ToWorldSpace(turretC1) - self.Position -- Relative angle
				-- uses tween v2 by steadyon
				local tween = Tween:Create(_attackBullet, info, property)
				tween:Play()
				coroutine.resume(coroutine.create(function()
					wait(_timeTaken)
					ReturnBullet(_attackBullet)
					local _attack
					if currentTarget ~= nil then
						if Logic.TakeDamage(currentTarget, _damage/ #turrets)== false then
							-- takedamage returns false if target is dead
							currentTarget = nil
						end
					end
				end))
			end

			wait(_cooldown)
		end
local function GetAngle (p1, p2)
		local ratio = (p1.X-p2.X)/(p1.Z-p2.Z)
		local beta = math.atan(ratio) 
		local beta = beta*(180/math.pi)
	if (p1.Z-p2.Z)<0 then
		if (p1.X-p2.X)<0 then	-- Quad 3
			beta= 180 + beta
		else	-- Quad 4
			beta = -180 + beta
		end 
	end
	print(beta)
	return beta
end
-- Got this from devforum, thanks person who wrote this!

This is what I have, do you know how I can fix my issue?
(Also bear with me if the way I calculate the Motor6D.C0 looks out of the ordinary, I literally just typed in whatever until it works. Been doing this for four hours, now. I think it’s time to ask people more knowledgable than me. lol)

Okay so I actually had an issue similar to this when I made a followspot light for A lighting group that I develop for and it just boils down to an orientation error screwing up the math of it which requires you to flip your equation for certain cases it seems like on that specific orientation the turret is just the direct opposite of the correct angle, it just boils down to a math thing. What I did was I just sat down with it for a couple hours until I was able to get the negatives and stuff mixed with some subtraction of the orientation to get it to work 95% effectively given different orientations.

Instead of measuring the angle which gets pretty complicated as @BostonWhaIer mentions especially with the angle being relative to object space and all, you can just make it look at the target position then constrain it so it only yaws left and right as a tank turret should, or up and down.

I made a module just for that check it out, hope it helps.

Just require the module, set the constraints then use the lookAt function provided like in the instructions semi provided in the post please ask if you have a question.

2 Likes

Thanks for the quick reply.

Ah, yes I’ve checked my orientation, it’s a little bit like 0, -90, 0… I’ve thought of making it 0, 0, 0 but I didn’t bother because I thought it doesnt really matter >.>

Oh. Thanks! I appreciate your generosity.
I’ll look into it :slight_smile:

When I was working with the followspot I did see instances where it did change and it just gave me headaches trying to fix mathematical things to make it as precise as I could, if it doesn’t help it might be a good place to start if you are determined to make it function with motor6d. Angle math is never easy especially when you don’t know the distance at all times and cheat with any of the stuff you learn in basic geometry lol. Some of the issues I have scene is that if you subtract from a position along a certain orientation it’ll cause one side of the position to be positive and the other negative.

2 Likes

Uh. Guys. I’ve figured it out.
You know what’s the worst part of this?

I never needed any angle calculations…
I’ve spent so much hours making those calculations work T-T, turns out I just overlooked something that should’ve been quite noticeable. (However, the 100 google searches about CFrame led me to understand few things I didn’t know before, and you can learn from this too (if you didn’t know already)

  1. Substracting a part.CFrame by its part.Position gives you only the rotational value of the CFrame.
  2. CFrame rotation is relative to object, which is why CFrame.lookAt(pos1, pos2) doesn’t work for Motor6Ds.

So, how did I solve this?

  1. Firstly, my Motor6D rotation is actually relative to my tank’s body.
  2. CFrame.lookAt(pos 1, pos2) can’t be used, but I actually did this instead;
    CFrame.lookAt( Vector3.new(), pos2 - pos1) to get the rotational value of the CFrame.lookAt(). (CFrame with rotation value only is basically CFrame at 0,0,0)
  3. Then, I applied the lookRotation I got with the Motor6D.C1. At first, it seemed like it didn’t work, but I’ve noticed that its rotation is correct when the tank is at certain rotation. (It’s 0, 0, 0)
  4. This means that the rotation is relative to the tank’s body. I know that multiplication adds up CFrames. So, with my limited knowledge of CFrame, I divided the lookRotation with the (self.CFrame - self.Position) to negate the relative rotation.

bruh

  1. Yep that didn’t work Imao. You can’t divide CFrames apparently. So instead I multiplied the lookRotation with (self.CFrame - self.Position):Inverse(). And…
    it’s a miracle

And that’s how you change Motor6D CFrame by using CFrame.lookAt()! All the calculations are abstracted by those handy functions. Thanks Roblox! :happy3:

Since you guys have been helpful, I’ll mark a solution. :slight_smile:

3 Likes

Nice work, experimenting with CFrames, but I’d like to point out that this:

Is not completely true. This is because the C0 or C1 is relative to the part 0 or part 1 of the Motor6ds so you can just inverse the part0 in order to position it in world terms which is what I did in my module sauce:

local relativeToWorld = currentJointMotor6D.Part0.CFrame:Inverse()
	local lookAtWorld = CFrame.lookAt(turretPosition,lookAtPosition,baseCFrame.UpVector)

	local goalCFrame

	if self.Constraints then
		local turretRelativeCF = baseCFrame:ToObjectSpace(lookAtWorld)
		local x , y , z = turretRelativeCF:ToOrientation()
		--print(math.deg(x),math.deg(y),math.deg(z))
		local constrainedX , constrainedY = self:EulerClampXY(x,y)
		--print(math.deg(constrainedX),math.deg(constrainedY))
		--print(math.deg(constrainedY))
		goalCFrame = relativeToWorld*baseCFrame*CFrame.fromOrientation(constrainedX,constrainedY,z)*turretCFrameRotationOffset
	else
		goalCFrame = relativeToWorld*lookAtWorld*turretCFrameRotationOffset
end

The end result you get a CFrame in world terms yet is also relative to the Part0 Motor. BTW I learnt this from @Rare_tendo and I’m really grateful for this CFrame trick and have been using it in all my projects.

I believe this is the easier method, but whatever floats your boat I guess. It’s just an alternative method you can use to find the relative rotation CFrame.

Good morning.
Thanks for pointing that out! That’s very helpful to know. I’m learning a lot from the community. :slight_smile: