Pantograph raising and lowering system

Ok, sure!

The Pantographs hand doesn’t match up with the wire yet so it might confuse you if it makes the arm look too high or something.

Edit: I’ve just realized that the hand is slightly moved forwards a bit from when I was testing something so that might have issues but it shouldn’t do, if it does then let me know and I’ll provide you with a fixed pantograph.

Yes, I’ve thought of this but at the moment this is just a test for the pantograph raising to its point and lowering but when I think it is ready I’ll make it so it keeps checking. I have to anyways because there will be multiple wires as for sizing and placement and things. Yes, you may ask how I’m going to get the wires that’s above the pantograph… I’m using raycast to find that… only because I know its the only way plus I’ve actually used chat GBT to help me with this, I definitely wouldn’t have figured out how to do it without the AI lol. I’ll be checking to make sure the train is moving before it raises otherwise its kind of wasteful (in my mind at least).

1 Like

I tested the file a lil bit. Rotating the models and changing some coordinates for the check, and yup, on certain rotations the code should be changed… Im sorry not being good at manipulating the CFrames this way, I would love to come with a solution but I would require more time.
And when stuff gets like this when doing this kind of tasks I prefer to go with the approach that makes more sense to me, which would be Raycasting to always find the proper Y coordinate to place the hand.
I mean shooters games cast many rays to work, meele combat games and many others too.
I dont think would be too intensive to handle the hand raising with a raycast or shapecast.

I did check your code and its pretty similar to what I was suggesting, and proof that its not properly setup is the slight differenced when orientation changes…
We need better math to solve this, someone who knows how to manipulate CFrames in this way and taking in count that this will require a loop check when the train is moving…

For now I suggest check the Raycasting and learn it. The chatGPT could work, but surely it doesnt know about ShapeCasting cause its a new feature. I recommend check both approaches and change the system to work with those. I would do that, sorry for not being helpful with the CFrame approach u ,u

Nah, its all good, everyone knows different things.

I’ve got this function that has to find the wire that’s above the pantograph

But is there any bad things or downsides about calling raycasts every frame?

Is it another problem if there’s multiple trains and it checks multiple pantographs?

Thanks anyways!

How many trains will be raycasting for the pantograph?
Raycasts will be on server? would be more performant if its on client side, specially if the game is not multiplayer.
If the game is multiplayer, how many trains?
Giving networkownership to client of the pantograph could be good idea while performing the raycasts on client.
You mean everyframe as a RunService.RenderStep? or server.Heartbeat?

Btw, that raycast script will not raycast 25 studs above the pantograph, it will cast it directly to world position 0,25,0.
It should be like

local PantoPos = Pantograph.Position
local Direction = PantoPos + Vector3.new(0,25,0)

I don’t really know yet but I was thinking of making it so the trains are automatic (for the public but I can drive it if I want to) so people don’t be crazy with them lol. I was going to make the trains timetabled and go every couple minutes but that’s more of a future thing.

Yup heartbeat or render step I’m not sure what would be better for this case.

Oh ok, that’s just what chat GBT told me to do lol. Would that script be good for getting the positions too or should I use a different function for that?

Does trains has more than one pantograph? I mean like one per car/wagon? or its only one per train?
So its not a game like each player has its own train?
The position of the pantograph is more like a cosmetic/visual thing, would be good that the client solve the raycast.
But if its only a few amount of trains and only one panto per train, server could handle it I think, so RunService.Heartbeat could not be too intensive

It totally depends on the mechanics of your game, single player/multiplayer, replication of that cosmetic/visual feature, amount of trains, amount of raycast per train.

The positions should happen in the same function that does the raycast, each iteration it uses the current position of the panto and cast the ray upwards


Btw, you should NOT try to get a hit on the wire, or you would need the ShapeCast which is more expensive. Would be better to add an invisible part which is wide, covering the whole that the wires uses. So it can easily hit the invisible part with only one raycast, and the wire its just the visual

Each train only has 1 pantograph. Nope, its more of a passenger thing. I’m wanting the pantographs hand to just be touching the wires. I have to update the hinges targets position because some wires will be higher and some wires would be lower and it would look weird having the pantograph just be above the wire or too low or something. If the train was coupled then I would have to do it per pantograph because one train could be under a high wire and the other('s) could be under a lower wire.

The game was going to be multiplayer. I’ll make sure the train would be moving before ray casting. I could also make detect when the pantograph loses contact with that wire using like a touch ended event? Lol could you give me a few examples how I could use the raycast please?

I was thinking about using a spring and making the wires have collisions and make the springs have enough force to raise up until it pushes into the wire and the wire would be there to stop it from raising and to push it down a bit maybe that could work? I think it might be more of a performance issue since collisions. I was also thinking I could check with like a loop or something for the next wire or the closest wire going in the trains direction might be cheaper?

For the wires I was just going to use a mesh constrains and the collision fidelity to be box. Yes the wires just a visual.

I dont think you need anything related with springs or touch events.
If you already are into the Raycast approach, that all you need. If you are firing the raycast constantly, the hit will always should collide with the proper Y coordinate where the wire is placed, so it should constantly update the panto hinges with the function you already have in the file you sent to constantly update the position of the hand.

So when the train is moving or not, is constantly checking how high the hand should be placed, and constantly updating that height. Not needing any other check.

What kind of Raycast example would you need? Actually raycasting its pretty simple to use, when I forgot how to use it or when I did learn how to use it I just visit the documentation of raycasting.

This is the example in docs, theres nothing else to cover about raycasting:

white/black list

local rayOrigin = Vector3.new(0, 0, 0)
local rayDirection = Vector3.new(0, -100, 0)

local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {script.Parent}
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
raycastParams.IgnoreWater = true

local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)

Using a raycast:

local rayOrigin = Vector3.new(0, 0, 0)
local rayDirection = Vector3.new(0, -100, 0)

local raycastResult = workspace:Raycast(rayOrigin, rayDirection)

if raycastResult then
	print("Instance:", raycastResult.Instance)
	print("Position:", raycastResult.Position)
	print("Distance:", raycastResult.Distance)
	print("Material:", raycastResult.Material)
	print("Normal:", raycastResult.Normal)
else
	warn("No raycast result!")
end

Is there like a hit point I get the Y axis from or?

yes, raycast return the hit coordinate as a Vector3, just extract the Y coordinate from it:

raycastResult.Position.Y

Ok thank you! I will let you know how it goes it goes tomorrow night because I’m in bed now.

1 Like

Hello @Dev_Peashie , so it is working, but is am I supposed to be timesing it or adding it?

If I do this then it doesn’t work
local RaycastResult = workspace:Raycast(Pantograph.Hand.Position, Pantograph.Hand.Position + Vector3.new(0, 10, 0), nil, nil)

But, if I do this it does work
local RaycastResult = workspace:Raycast(Pantograph.Hand.Position, Pantograph.Hand.Position * Vector3.new(0, 10, 0), nil, nil)

Also it makes the hand go like halfway though the wire and not under the wire, how could I fix this?

It may have something to do with the hand angle doesn’t change automatically yet.

local RaycastResult = workspace:Raycast(Pantograph.Hand.Position, Pantograph.Hand.Position * Vector3.new(0, 10, 0), nil, nil)
		
		if RaycastResult then
			local RaycastInstance = RaycastResult.Instance
			
			if RaycastInstance.Name == "Wire" then
				local Distance = RaycastResult.Distance
				
				local UpperArmAngle = math.deg(math.asin((Distance - Pantograph.Hand.Size.Y / 2) / Pantograph.UpperArm.Size.Z))
				local LowerArmAngle = math.deg(math.asin(Distance / Pantograph.LowerArm.Size.Z))

				Pantograph.UpperArm.LowerArmHinge.TargetAngle = UpperArmAngle
				Pantograph.Base.LowerArmHinge.TargetAngle = LowerArmAngle / 2
			end
		end

image

You can get the target position with CFrames and a little vector math. I’m sure there’s a more concise solution, but this is what I came up with.

I’m going to assume that the wire is rotated around its Z axis.

local wire = workspace.Wire
local part = workspace.Part

local dist = ((part.Position - wire.Position)*Vector3.new(1, 0, 1)).Magnitude -- horizontal distance between the parts
dist /= math.acos(math.rad(wire.Orientation.Z)) -- hyp = adj/arccos(theta)

local pos = (wire.CFrame * CFrame.new(dist, 0, 0)).Position -- translate the wire CFrame along its RightVector

part.Position = pos

The wire changes all the time. While the train is moving the wire might change to a different part. The pantograph has 3 hinges, one to control the lower arm, one to control the upper arm and one to control the hands hinge so it’ll need to do some calculation to figure out what the target angle of each hinge should be.

Thanks.

I dont understand why adding the Vector3 offset for direction would not work.
You can’t use the CFrame cause it has orientation, if the hand is slighty rotated the Ray will go in a different angle and not upwards.
Did you included params? to avoid colliding with anything else but the wire part?

Is the wire part wide enough to be hit by the ray?

local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {pantoGraphModel}
raycastParams.FilterType = Enum.RaycastFilterType.Exclude

local origin = Pantograph.Hand.Position
local direction = origin + Vector3.new(0,10,0) -- this is where the hand is position and upwards by 10 studs
local RaycastResult = workspace:Raycast(origin, direction, raycastParams)

Additionally you can set collision groups, so you make sure it the ray is allowed to only hit the wires and nothing else.


And after getting the hit.Position, be sure to offset that Vector3 downwards the half of the size of the Hand, so the Hand is always position under the wires and not in the middle

Is this about the timesing it or adding question? If it is then I’ve tried to raycasting only the cube and the wire far away from anything else with using the + symbol instead of * and that also did not work.

Everything around the pantograph and the wire has collisions off.

Can I do the pantographs model in there or should it be done as PantographModel:GetChildren() or should it be done as {pantograph.Base, Pantograph.UpperArm, etc}

So it should be removing the half the size of the hand from the origin?
workspace:Raycast(Pantograph.Hand.Position, Pantograph.Hand.Position * Vector3.new(0, 10, 0))

Or should I try making the origin the top of the hand?

I mean you said that adding the offset for direction didnt work, and multiplying it did work.


image


If its a Vector3, you can add (+) the offset without issues.

Thats not enough, in that case you want to set CanQuery to false too, so it cant interact with Raycast.

The Model is enough, the rule is, it will ignore any descendant of the Instance you provided in FilterDescendantsInstances

Nope, remove it from the RaycastResult.Postion - Vector3.new(0, Hand.Size.Y/2, 0)
That Vector3 is the one that you need to supply to your pantograph function to place the Hand at that Vector, a little underneath the wire hit point of raycast
But, you should play with the values, in order to get the exact desired result

Didn’t work the first attempts when I’ve tried but I could try now with the adjustments. It doesn’t really matter if its * since it works but I’m not sure if that could be causing any miscalculations in the code.

Already is.

Ok

I don’t actually use RaycastResult.Position no more because it the raycast result already provided me with a distance and the purpose of the position was to get the distance.

Ok ok ok… I’ll agree that raycast made this a bit better because it kind of made it shorter not needing to calculate the distance since it has a built in distance feature.

Got it, ok, then just keep in mind that the distance is between 2 points which, the origin its in the very center of the Hand, not its boundary, it comes from the center, thats why you still need to offset the half size of Hand.Y, or offset the Distance value the half size of Hand, or yeah offset the Origin of Ray half size of the hand