How would I go about creating a melee attack system like Blood & Iron?

The title is pretty self explanatory. I’m creating a system that allows a player to choose between different attacks depending on which way the cursor is moving. Not unlike blood & iron or other games in the same category.

This is an abstract overview of how I’ve tried to implement such a system so far:
When new mouse movement is detected*
-Get mouse position (relative to the frame)
-wait a few frames
-Compare original mouse position to current
-Perform some arithmetic
-Depending on previous arithmetic it can be determined which direction the mouse was moving in.

This system works fine with the mouse in free view, but it breaks in shiftlock (for obvious reasons being that the mouse position is locked to the center of the screen)

I’ve thought of using CFrames and the Mouse.hit property, this would work with the up/down since that would present in the same results no matter the direction the player is facing in, but how would I get the XPosition? I’ve currently thinking of trying to get the direction of the HumanoidRootPart’s look vector and comparing that to the look vector a few frames previously but at this moment I’m not sure how to achieve that.

Any help would be appreciated!

Sorry that I am responding to this a bit late (3 days), but the way I have figured out to the problem of not being able to detect mouse movement in shift lock. My solution is to check if the mouse is locked, and if it is locked, then it will capture camera rotation, wait a frame, then capture the camera rotation again and compare the current rotation to the previously captured rotation, then work from there. This will also work when the player is holding RMB in 3rd person or in first person. here’s the code.

local player = game:GetService("Players").LocalPlayer
local UIS = game:GetService("UserInputService")
local runService = game:GetService("RunService")
local camera = workspace.CurrentCamera
local mouse = player:GetMouse()

local function mouseIconFunction()
	if UIS.MouseBehavior == Enum.MouseBehavior.LockCurrentPosition or UIS.MouseBehavior == Enum.MouseBehavior.LockCenter then
		local startingCamY,startingCamX = camera.CFrame:ToOrientation()
		wait(runService.Heartbeat)
		--Feel free to adjust time between first capturing cam rotation and next time checking cam rotation.
		local currentCamY,currentCamX = camera.CFrame:ToOrientation()
		local xDifference = currentCamX - startingCamX
		local yDifference = currentCamY - startingCamY
		if math.abs(xDifference) >= math.abs(yDifference) then
			if math.abs(xDifference) < 0.023 then
				--In mouse deadzone
				return
			end
			
			if xDifference < 0 then
				print("looking right, mouse locked")
				--Add your code
			else
				print("looking left, mouse locked")
				--Add your code
			end
		else
			if math.abs(yDifference) < 0.023 then
				--In mouse deadzone
				return
			end
			
			if yDifference > 0 then
				print("looking up, mouse locked")
				--Add your code
			else
				print("looking down, mouse locked")
				--Add your code
			end
		end
	else
		--Add your current code to check the direction the mouse is heading in here, it will be only ran when the mouse is unlocked/player is in 3rd person and not holding RMB.
	end
end

local functionCallDebounce = false

UIS.InputChanged:Connect(function(input,gameProcessed)
	if functionCallDebounce or gameProcessed then
		return
	end
	
	functionCallDebounce = true
	
	if input.UserInputType == Enum.UserInputType.MouseMovement then
		mouseIconFunction()
	end
	
	for i = 1,3 do
		wait(runService.Heartbeat)
	end
	
	functionCallDebounce = false
end)

If you need for me to explain anything else or something isn’t working, then feel free to ask me. I hope this has helped you.

1 Like

Dude this is awesome, thanks so much. I wish you hadn’t written out the code so I’d be forced to figure it out myself but nevertheless I changed a lot of things. xD

I did eventually get this mechanic to work by using cframes and the look vector of the torso and some acos math to get the angle from the CFrame’s look vectors.
With nothing more being said your system is far more elegant.

NOTES/CHANGES TO YOUR CODE

-Using your code unchanged it will print both Left/Right and Up/Down, this is undesired because I need to pick out of the 4 directions. Nothing a lil math.abs comparison between the two difference can’t fix.
-Another undesired behavior of your code is at the 180/-180 inflection point. This was fixed with a little bit of math trickery to determine if an inflection point occurred and then determine what the direction is by comparing which is negative.

local tool = script.Parent
local cam = game.Workspace.CurrentCamera

local P = game:GetService("Players")
local p = P.LocalPlayer 

local mouse = p:GetMouse()

local UIS = game:GetService("UserInputService")
local RS = game:GetService("RunService")

local startCamY, startCamX = cam.CFrame:ToOrientation()
local startMouseY, startMouseX = mouse.y, mouse.x

local MOUSEDEADZONE = 0.023

local inputChangeDebounce = false

local function mouseDirection() 
	wait(RS.Heartbeat)
	wait(RS.Heartbeat)
	
	if UIS.MouseBehavior == Enum.MouseBehavior.LockCenter then --detects if shiftlock enabled
		local curCamY, curCamX = cam.CFrame:ToOrientation()
		local XDiff = curCamX - startCamX
		local YDiff = curCamY - startCamY
		
		if math.abs(curCamX + -1 * startCamX) > math.abs(curCamX) and math.abs(curCamX) > math.rad(170) then --detects rear inflection point
			
			if curCamX > 0 and startCamX < 0 then 
				print("Right Locked")
			else
				print("Left Locked")
			end
		else
			
			if math.abs(YDiff) > math.abs(XDiff) then --runs up/down detection depending on which difference is greater
				if math.abs(YDiff) < MOUSEDEADZONE then
					if YDiff < 0 then
						print("Up Locked")
					else
						print("Down Locked")
					end
				end
			else
				if math.abs(XDiff) > MOUSEDEADZONE then
					if XDiff < 0 then
						print("Right Locked")
					else
						print("Left Locked")
					end
				end
			end
		end
	else -- if shift locked not enabled check mouse position
		
		local curMouseY, curMouseX = mouse.y, mouse.x
		local xDiff = curMouseX - startMouseX
		local yDiff = curMouseY - startMouseY
		
		if math.abs(yDiff) > math.abs(xDiff) then
			if yDiff < 0 then
				print("Up MouseNotLocked")
			else
				print("Down MouseNotLocked")
			end
		else
			if xDiff > 0 then
				print("Right MouseNotLocked")
			else
				print("Left MouseNotLocked")
			end
		end
	end
	startCamY, startCamX = cam.CFrame:ToOrientation()
	startMouseY, startMouseX = mouse.y, mouse.x

end

UIS.InputChanged:Connect(function(input,gameProcessed)
	if inputChangeDebounce == false then
		inputChangeDebounce = true
		mouseDirection()
		inputChangeDebounce = false
	end
end)

Thanks again for taking the time to write out the code! I’ll mark yours as the solution, since it is.
One last question though; What is the point of the purpose of gameProcessed? What does it represent?

2 Likes

Not really sure, now thinking back it is redundant in this case so you can just remove it, but gameProcessed means if the player is in a built in Roblox GUI, like the escape key
menu screen, but since this script isn’t dealing with registering key presses then it doesn’t really serve a purpose as the player mouse would already be unlocked if they’re in the menu screen. Once again, I’m happy that you found this useful and that I was able to help you!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.