I’m currently working on a 2D game based off the popular game Just Shapes and Beats.
Recently I was working on the character framework when I came across a small issue. In the original Just Shapes and Beats, your character would face towards your movement goal/position.
Example of Character movement
Currently, I have only been able to replicate a similar humanoid feature for the character but I have not been able to replicate the Rotation goal or “LookCFrame” if you want to put it as so.
My version of movement
Comparing both movement styles together makes mine look very bland, lazy and boring which is why I want to at least try to replicate a similar style for my own game.
So the end-goal here should be to set the Rotation of your UI object to face the direction of movement. To do this, we first need to figure out the angle between the UP direction (where Rotation is 0) and the movement direction. To do this, we can get the arc-cosine of the dot product of both normalized directional vectors. This works for both Vector2s and Vector3s. But only need Vector2 in this case:
local function AngleBetween(vectorA, vectorB)
return math.acos(math.clamp(vectorA.Unit:Dot(vectorB.Unit), -1, 1))
end
--// Inputs
UserInputService.InputBegan:Connect(function(Input, Processed)
if Processed then return end
--// Movement Handling
if table.find(MovementKeyCodes, Input.KeyCode) then
table.insert(MoveDirections, Input.KeyCode);
end
--// Mechanic Handling
if Mechanics[Input.KeyCode] or table.find(Mechanics, Input.KeyCode) then
Mechanics[Input.KeyCode](Character);
end
--// Tween Movements
if next(MoveDirections) and table.getn(MoveDirections) <= 1 then
if Tweens["BackSize"] then
Tweens["BackSize"]:Cancel();
end
Tweens["Shrink"] = TweenService:Create(Character, TweenInfo.new(0.5, Enum.EasingStyle.Back), {
Size = UDim2.fromOffset(15, 15),
});
Tweens["Shrink"]:Play();
Tweens["Shrink"].Completed:Wait();
Tweens["Shrink"] = nil;
end
end)
UserInputService.InputEnded:Connect(function(Input)
if table.find(MoveDirections, Input.KeyCode) then
table.remove(MoveDirections, table.find(MoveDirections, Input.KeyCode));
if not next(MoveDirections) then
Character.Rotation = 0;
Tweens["BackSize"] = TweenService:Create(Character, TweenInfo.new(1, Enum.EasingStyle.Elastic), {
Size = UDim2.fromOffset(20, 20),
});
Tweens["BackSize"]:Play();
Tweens["BackSize"].Completed:Wait();
Tweens["BackSize"] = nil;
end
end
end)
--// Mover
RunService.Heartbeat:Connect(function()
if next(MoveDirections) ~= nil and Character ~= nil then
local Humanoid = Character.Humanoid;
local MoveSpeed = Humanoid.Speed.Value;
--// Handle Movements
if table.find(MoveDirections, Enum.KeyCode.W) then
Character.Position -= UDim2.fromOffset(0, MoveSpeed);
end
if table.find(MoveDirections, Enum.KeyCode.A) then
Character.Position -= UDim2.fromOffset(MoveSpeed, 0);
end
if table.find(MoveDirections, Enum.KeyCode.S) then
Character.Position += UDim2.fromOffset(0, MoveSpeed);
end
if table.find(MoveDirections, Enum.KeyCode.D) then
Character.Position += UDim2.fromOffset(MoveSpeed, 0);
end
--// Rotation Handling
local CharaterPos = Character.Size;
Character.Rotation = math.deg(AngleBetween(Vector2.new(0, 1), Vector2.new(CharaterPos.X.Scale, CharaterPos.Y.Scale)))
end
end)
This is pretty much the code I use to move the character.
The character itself is just a frame inside of a “PlayerDump” frame with the size of 1,0,1,0