CFrame from point, to another point, but randomly to the side of the second point?

Hi, I’m working with this simple AI from yellowmustang (thanks Yellow - your free AI are really awesome considering they are free!) and I was wondering if someone could help me with this adjustment. I’m studying the docs about CFrames, and I still don’t totally get them… (sorry sorry)

So the code draws a Part from the turrets head to the character when it Hits.

I am using a random d20 number generator to make it miss sometimes. If my d20 rolls over 14, it misses, under 14 and it hits. depending on whether it hits or misses, it goes to a different function

The code to draw the laser for when it hits is :

		local l = Instance.new("Part",script.Parent)--create ray/laser
		l.Anchored = true
		l.CanCollide = false
		l.BrickColor = BrickColor.new("Really red")
		l.Material = Enum.Material.Neon
		l.Size = Vector3.new(0.2,0.2,(myHead.Position - target.Position).Magnitude)--creates laser size/length
		l.CFrame = myHead.CFrame * CFrame.new(0,0,-(myHead.Position - target.Position).Magnitude/2)--creates laser position directly between the heads (the /2 part) and in front of us (negative on z trait)
		--

So I’m wondering how to make a copy of this line:

		l.Size = Vector3.new(0.2,0.2,(myHead.Position - target.Position).Magnitude)--creates laser size/length
		l.CFrame = myHead.CFrame * CFrame.new(0,0,-(myHead.Position - target.Position).Magnitude/2)

But I’d like it to choose a random spot to the side/above the character, and the line should stretch off to infinity (or very far.)

It probably also needs to do a raycast to check that the spot it chooses doesn’t still hit the character (if it picks a spot in front of the character, it would still pass through the character, but not do damage, so I want to avoid that.)

I’ve jiggered it many ways, and my cframe knowledge just doesn’t seem to grow. I’m not smurt enough to understood the CFrame API info, even though I’ve tried and tried… (sorry)

any help appreciated. much !

you could create a math.random number of 1 and 2 if the character got hit. if the number is 1 you get hit and if its 2 it “misses” the target (optionally do some functions)

1 Like

Thank you very much Mystery/OxzMq! But it’s actually the CFrame that draws the laser that I need help with.

I need to alter this code:

		l.Size = Vector3.new(0.2,0.2,(myHead.Position - target.Position).Magnitude)--creates laser size/length
		l.CFrame = myHead.CFrame * CFrame.new(0,0,-(myHead.Position - target.Position).Magnitude/2)

So that instead of aiming at the character, it aims at a random spot near the character, but not touching the character.

I appreciate your response !

2 Likes

You could try to randomize the direction of the laser

1 Like

Thank you! But in this case, I want it to be an obvious “near miss” - so you see the laser flying past your head, but it misses. Appreciate your response!

Whenever you want the script to make it miss, you could slightly increase or decrease the X Y Z values of the laser direction.

1 Like

Totally, and I’m working on that. But I was hoping for a little help with the adjustment to the code :slight_smile:

I’m working with this from the API, but haven’t had luck. But I’m working on it

part.CFrame = CFrame.new(2, 3, 4) * CFrame.new(4, 5, 6) --> equal to CFrame.new(6, 8, 10)

I’m still trying. I tried this, and it failed - it still somehow shoots the HumanoidRootPart perfectly:

		local randomX = math.random(-20,20)
		local randomZ = math.random(-20,20)
		local missPosition = Vector3.new(target.CFrame) * Vector3.new(randomX,0,randomZ)
	
		l.Size = Vector3.new(0.2,0.2,(myHead.Position - missPosition).Magnitude)--creates laser size/length
		l.CFrame = myHead.CFrame * CFrame.new(0,0,-(myHead.Position - missPosition).Magnitude/2)

I don’t totally get it. It seems it should pick a spot somewhere to the side of the humanoid root part due to the randomX and randomZ added to the target CFrame with the vector math?

Anyone can help?

Well, you’re still using myHead.CFrame to set the rotation. You’re only using missPosition here to determine the line length (and not quite that even, because you’re not using it in l.Size).

What you want is to pick a random axis orthogonal to your ray direction, and then rotate the vector along that axis by some fixed angle. In a minute I’ll come up with an example

1 Like

Very Happy. :slight_smile: Thank you nice Mike ! I don’t totally get CFrames… So any explanations you offer will be appreciated. Code example also appreciated. I’d like to understand CFrames/Vector3’s better.

If it helps “myHead.Position” is the NPCs head that is firing the laser… “target.Position” is the characters humanoid root part. “missPosition” is supposed to a new spot to the side of the character…

Hope that helps.

–edit
oh wait I think I might get it. I’m trying something. Your help still appreciated, awaiting your response…but I’m trying something…

Dang, I tried this based on your advice (didn’t understand obviously how to do it) and it didn’t work. Still shoots right through character, but now it’s a big long line stretching through NPC too. Hm.

		local randomX = math.random(-.3,.3)
		local randomY = math.random(-.3,.3)
		local randomZ = math.random(-.3,.3)
	
		l.Size = Vector3.new(0.2,0.2,1000)--creates laser size/length
		l.CFrame = myHead.CFrame * CFrame.new(randomX,randomY,randomZ)--creates laser position directly between the heads (the /2 part) and in front of us (negative on z trait)
		--

I’m not looking to be spoonfed…still trying stuff… but thanks - eagerly await your response. !

Hm. Tried this below, and now it shoots random angles (not near character) that totally pass through NPC’s head at halfway point - am getting closer, but still doesn’t work!

		local randomX = math.random(-3,3)
		local randomY = math.random(-3,3)
		local randomZ = math.random(-3,3)
	
		l.Size = Vector3.new(0.2,0.2,1000)--creates laser size/length
		l.CFrame = myHead.CFrame * CFrame.Angles(randomX,randomY,randomZ)

holycrum I’m getting closer, thanks to your tip and the original code and a lot of flubbing. the following causes it to originate at PC’s head, and shoot at every odd angle ! closer!

		local randomX = math.random(-3,3)
		local randomY = math.random(-3,3)
		local randomZ = math.random(-3,3)
	
		l.Size = Vector3.new(0.2,0.2,1000)--creates laser size/length
		l.CFrame = myHead.CFrame * CFrame.Angles(randomX,randomY,randomZ) * CFrame.new(0,0,-500)--creates laser position directly between the heads (the /2 part) and in front of us (negative on z trait)
		--

still not quite right…gonna play with the randomangles now

ohwow I got it (that rush when your code works…ahhh)

this works:

		local negOrPosX = math.random(1,100)
		local negOrPosY = math.random(1,100)
		local negOrPosZ = math.random(1,100)

		local randomX = math.random(3,15)
		local randomY = math.random(6,15)
		local randomZ = math.random(3,15)

		if negOrPosX < 51 then randomX = -randomX end
		if negOrPosY < 51 then randomY = -randomY end
		if negOrPosZ < 51 then randomZ = -randomZ end


	
		l.Size = Vector3.new(0.2,0.2,1000)--creates laser size/length
		l.CFrame = myHead.CFrame * CFrame.Angles(math.rad(randomX),math.rad(randomY),math.rad(randomZ)) * CFrame.new(0,0,-500)

Thanks nicemike! Your tip did the trick!

1 Like

With your method, you’ll have to change the code that sets myHead.CFrame, since the laser it just going to shoot in that direction:

Anyways, this mostly works. I decided the “orthogonal vector” stuff was a bad solution because it would offset by the same angle no matter how far away you were from the target.

Instead, I imagine a circle whose center is at the target, and is flat from the perspective of the laser (i.e. it’s a ring around the laser). Pick a point on that circle as the new target.

-- given a ray's direction, redirect it so it misses by the given number of studs.
local function GetMissVector(dir: Vector3, missBy: number, angle: number?)
	-- pick an angle 0-360 degrees
	angle = angle or math.random() * math.pi * 2
	
	-- we need a right and an up vector so we'll use CFrame.lookAt
	
	-- I think lookAt breaks if you're pointing straight up or down,
	-- so we fix that here by giving it a different "up" direction.
	-- ROBLOX might already do this so it might be unnecessary.
	local up = Vector3.yAxis
	if dir.Y < -0.999 or dir.Y > 0.999 then up = Vector3.zAxis end
	
	-- pointing from 0,0,0 to the tip of the direction vector:
	local cf = CFrame.lookAt(Vector3.zero, dir, up)
	
	-- basic trig, just finding a point on a circle with a radius of missBy
	-- at our given angle
	local x = math.cos(angle) * missBy
	local y = math.sin(angle) * missBy
	
	-- offset it in the right and up directions
	return dir + cf.RightVector * x + cf.UpVector * y
end

You’d use that to replace your lines like this:

local origin = myHead.Position
local dir = target.Position - origin
local missDir = GetMissVector(dir, 5) -- miss by 5 studs

local dist = missDir.Magnitude

l.Size = Vector3.new(0.2, 0.2, dist)
l.CFrame = CFrame.lookAt(origin, origin + missDir) * CFrame.new(0, 0, -dist/2)

There’s some duplicated work between the lookAt in the function and the lookAt in the l.CFrame = lines, but it’s not a big deal.

You could make the GetMissVector function take in a cframe and a length or something to avoid it.

Edit: Posted before your edit :slight_smile: If your code works, go for it.

1 Like

Thanks, I came up with something that works, but I’ll have to try your result too!

Much thanks!

Code you wrote yourself is always easier to maintain than someone elses!

Also one small edit to mine if you want it to go 1000 studs instead of stopping next to the character:

local missDir = GetMissVector(dir, 5).Unit * 1000
1 Like