How can I make rotations/tilts like how the 'ye olde' build tools worked?

Trying to make my own simple little “Build your own base to <x>” sort of game just for fun (and to get myself used to working inside of/with Roblox), was wondering how the old games did the ‘r to rotate’ and ‘t to tilt’ thing.

I am currently using what I assume is a fairly naïve approach. For rotations, I am rotating the CFrame’s lookVector using a map of vectors to vectors. Similarly for tilting, I’m rotating the CFrame’s upVector.

-- MAPA and MAPB are linked lists. MAPA is the "current" value, MAPB is the "next" value.
local FACING_MAPA = {
	Vector3.new(1, 0, 0),
	Vector3.new(0, 0, 1),
	Vector3.new(-1, 0, 0),
	Vector3.new(0, 0, -1),
	default = Vector3.new(1, 0, 0)
}

local FACING_MAPB = {
	Vector3.new(0, 0, 1),
	Vector3.new(-1, 0, 0),
	Vector3.new(0, 0, -1),
	Vector3.new(1, 0, 0)
}

local UP_MAPA = {
	Vector3.new(1, 0, 0),
	Vector3.new(0, -1, 0),
	Vector3.new(-1, 0, 0),
	Vector3.new(0, 1, 0),
	default = Vector3.new(0, 1, 0)
}

local UP_MAPB = {
	Vector3.new(0, -1, 0),
	Vector3.new(-1, 0, 0),
	Vector3.new(0, 1, 0),
	Vector3.new(1, 0, 0)
}


function module:UpdateFacing() -- called when 'r' is pressed.
	for i, v in ipairs(FACING_MAPA) do
		if v == self.facingVector then
			self.facingVector = FACING_MAPB[i]
			return
		end
	end

	self.facingVector = FACING_MAPA.default
end

function module:UpdateUp() -- called when 't' is pressed.
	for i, v in ipairs(UP_MAPA) do
		if v == self.upVector then
			self.upVector = UP_MAPB[i]
			return
		end
	end

	self.upVector = UP_MAPA.default
end

Like I said, this is likely a naïve approach, as I am not 100% sure on how to manipulate CFrames yet.

The problem is that I now get stuck in a rotation-lock.

I’ve googled around for a bit and searched for a bit on here, but have been unable to find anything helpful (or perhaps just glanced over it while skimming titles).

What would be a better approach to this?

1 Like

Perhaps look into offsetting the CFrame’s rotation instead. You can do this by multiplying the current CFrame by a new CFrame. Something like:

partA.CFrame *= CFrame.angles(math.rad(90), 0, 0)
or if you would want to write the thing out:
partA.CFrame = partA.CFrame * CFrame.angles(math.rad(90), 0, 0)

Which would take the current CFrame and rotate it by 90 degrees on the X axis.

That fixes the rotation-lock problem (and shrinks my codebase by a good 50 lines, thanks!), however now depending on which direction the block is held, r will sometimes tilt, and t will sometimes rotate.

Sorry I just remembered but there is an API for a default roblox dragger:

If you still want to make your own, to answer your question you may want to look into a CFrame’s lookvector to find which direction it faces.

Ooh the dragger looks promising, though the problem with that is that it’s not really being dragged. It’s the sort where you use a tool’s UI to select a part you want to insert, then a hologram appears displaying where it’s at…

Actually, on second read, it looks like I’d be able to hook into the dragger. It’ll require a bit of modifications to my existing code, but it should work… I’ll mark this as the solution for now and look into it more sometime this week.

Thanks!

1 Like

Posted this so that anyone else with my predicament may be able to use my code as well.

Alright, so 7 months later I came back to this and completely rewrote my system. I wound up with an absolutely insane idea and it worked, I barely have to deal with CFrames except with a single CFrame.Angles call when I need to use it.

I ended up making a 2D array of rotations which incrementing by 1 column will tilt, and incrementing by 1 row will rotate.

local rotationArray = {}

local function generateRotationMatrix()
	local function generateRow(Z)
		return {Vector3.new(0, 0, Z), Vector3.new(0, 90, Z), Vector3.new(0, 180, Z), Vector3.new(0, -90, Z)}
	end

	rotationArray[1] = generateRow(  0)
	rotationArray[2] = generateRow( 90)
	rotationArray[3] = generateRow(180)
	rotationArray[4] = generateRow(-90)
end
generateRotationMatrix()

local module = {}
local mti = {}

function mti:Rotate()
	self.r = self.r % 4 + 1
end

function mti:Tilt()
	self.t = self.t % 4 + 1
end

function mti:Determine()
	return rotationArray[self.t][self.r]
end

function module.new()
	return setmetatable({r = 1, t = 1}, {__index = mti})
end

return module

Usage down the line is like so:

local rotation = Rotator.new() -- create the new object
rotation:Rotate()
rotation:Tilt() -- rotate / tilt arbitrarily

local determined = Rotation:Determine() -- get the facing

model:SetPrimaryPartCFrame(
  CFrame.new(BLOCK_POSITION) * CFrame.Angles(math.rad(determined.X), math.rad(determined.Y), math.rad(determined.Z))
)

I’m planning on converting the vectors to CFrame.Angles (Actually that’s my next job right now) so I don’t have to call it here repeatedly, but it works like a charm! I’ve done it, it works. This is epic.

1 Like