Chopping tree exactly where you clicked

I know that this has been asked before but the answer I got from the creator of Lumber Tycoon I could not make anything out of. How to make a choppable tree like in lumber tycoon 2?
can somebody please explain how I would use
treePart.CFrame:pointToObjectSpace(mouseClickPos)
and if you want, it would be nice to give a script for the axe to do this, instead of just clicking the tree. but that is only if you like.

1 Like

treePart.CFrame:PointToObjectSpace(mouseClickPos gives the position of the mouse hit relative to the tree part (the tree part is kind of treated as the center of the world).

If your tree part has other tree parts connected to it, this might work, if their size is biggest on their y axis and they are positioned and oriented so that connectedTreePart.CFrame*Vector.new(0, -connectedTreePart.Size.Y/2, 0) is closer to the tree part’s CFrame on the tree part’s CFrame’s x and z axis than connectedTreePart.CFrame*Vector.new(0, connectedTreePart.Size.Y/2, 0) is.

local function createWeldConstraint(part0, part1)
    local weldConstr = Instance.new("WeldConstraint")
    weldConstr.Part0, weldConstr.Part1 = part0, part1
    weldConstr.Parent = part0
end

local function makeTreeFall(treePart, hitPos)
    local treePartCf = treePart.CFrame
    local relY = treePartCf:PointToObjectSpace(hitPos).Y
    local upperParts, lowerParts = {}, {}
    for i, v in ipairs(treePart:GetConnectedParts()) do
        if v ~= treePart then
             if treePart.CFrame:PointToObjectSpace(v.CFrame*Vector3.new(0, -v.Size.Y/2, 0).Y > relY then
                upperParts[#upperParts+1] = v
            else lowerParts[#lowerParts+1] = v
            end
        end
        for i, child in ipairs(v:GetChildren()) do
            if child:IsA("WeldConstraint") and (child.Part0 == treePart or child.Part1 = treePart) then
             child:Destroy()
        end
    end
    
    local orgSize = treePart.Size
    treePart.Size = Vector3.new(orgSize.X, orgSize.Y/2+relY, orgSize.Z)
    local newTreePartSize = orgSize-Vector3.new(0, treePart.Size.Y, 0)
    treePart.Position = treePartCf*Vector3.new(0, -newTreePartSize.Y/2, 0)
    
    local newTreePart = treePart:Clone()
    newTreePart.Size = newTreePartSize
    newTreePart.Position = treePart.CFrame*Vector3.new(0, newTreePartSize.Y/2+treePart.Size.Y/2, 0)
    -- new welds
    for i, upperPart in ipairs(upperParts()) do
        createWeldConstraint(newTreePart, upperPart)
    end
    newTreePart.Parent = treePart.Parent
    
    for i, lowerPart in ipairs(lowerParts) do
        createWeldConstraint(treePart, lowerPart)
    end
end

To do the axe thing, raycasting might be useful. When the axe is swinging, you could cast a ray from where the ax blade starts to the end of the ax blade every frame to detect when it hits a tree part. Then give the position and the part of the raycastresult to the makeTreeFall function if the part is a tree part.

local RunService = game:GetService("RunService")

local function makeParams(char)
    local params = RaycastParams.new()
    params.FilterType = 
    Enum.RaycastFilterType.Blacklist
    params.FilterDescendantsInstances = {char}
end

local function isTreePart(part)
    -- your logic here (maybe check the name or parent)
    -- return true if it is a tree part
end

local function detectHits(axe, animTrack)
    local char = axe.Parent
    local params = makeParams(char)
    while animTrack.IsPlaying do
        local startPos = -- where the ray should start
        local endPos = -- where the ray should end
        local rayDirection = endPos-StartPos
        local raycastResult = workspace:Raycast(startPos, rayDirection, params)
        local hitPart = rayCastResult and raycastResult.Instance
        if hitPart and isTreePart(hitPart) then
            makeTreeFall(raycastResult.Position, hitPart)
            return
        end
        RunService.Heartbeat:Wait()
    end
end
4 Likes

well if the tree part had different parts to it, wouldn’t I be able to use mouse.Target, and make it have the part be a specific name so it can recognize it and unweld it? ( changing the name to get rid of script confusion )

You can get the distance of the players click from the middle of the part. This will give you a rough idea of where the player is attempting to chop it.

You would most likely need to round values for this as a players click would be on the surface of the part.

Yes, you can use mouse.Hit and mouse.Target. I just thought raycasting from the axe blade was what you wanted.

It depends, I now have made the code to do so but It will be hard to fire a server to get it to work for a tool. If you could help it would be nice:

local player = game.Players.LocalPlayer 
local mouse = player:GetMouse() 
local camera = workspace.CurrentCamera
local RunService = game:GetService("RunService") 
local target 
local down
local WalkKey = "G"




game:GetService("UserInputService").InputBegan:Connect(function(inputObject, gameProcessedEvent)
	if inputObject.KeyCode == Enum.KeyCode[WalkKey] then
		if mouse.Target.Name == "UnweldTest" then
			mouse.Target.Weld:Destroy()
			mouse.Target.Name = "Welded"
			if mouse.Target ~= nil and mouse.Target.Locked == false then
				
			end
		end		
	end		
end)

this finds the weld, gets rid of it from the name and then changes the name so no confusion happens. but its keybinded, and also will only work in StarterCharacterScripts. Is there a way to put this in Server Script Service, so it can receive the fired event? While I could try and make it look for the tool it would be hard.

wait forget that… I just did it. I simply searched for the player model and tryed to see if I could find the tool. now It works perfectly! Here is the script, for people who want it. ( local script inside Starter Character Scripts )

   local player = game.Players.LocalPlayer -- finds local player
local mouse = player:GetMouse()  -- finds local players mouse
local camera = workspace.CurrentCamera -- finds current camera
local RunService = game:GetService("RunService")  -- finds runservice
local target  -- dont change, finds mouses target
local down
local WalkKey = "G" -- set G to whatever keybind you want it to be for.
local PlayerModel = workspace:WaitForChild(player.Name,1) -- dont change, finds the players model.



game:GetService("UserInputService").InputBegan:Connect(function(inputObject, gameProcessedEvent) -- plays by keybind, change this to click if you like.
	if inputObject.KeyCode == Enum.KeyCode[WalkKey] then
		if mouse.Target.Name == "UnweldTest" then -- set UnweldTest to whatever part you want to be unwelded.
			if PlayerModel.Hello then -- change Hello to the axe or tools name
				mouse.Target.Weld:Destroy() -- this destroys weld
				mouse.Target.Name = "Welded" -- change to whatever you want besides Unweld test, or what you made the wanted targets name.
				if mouse.Target ~= nil and mouse.Target.Locked == false then -- extra
				end
			end
		end		
	end		
end)

Everything works for me! After this I will probably find out how to put force on the part so it falls over.