English Check if two players are facing each other

26 replies
Goto Page
To the start Previous 1 2 Next To the start
Up
Mami Tomoe
User
Offline Off
Hello, how can I check if two players are facing each other (I'm trying to override the shield logic with one of my own, but I got stuck at the part where I need to check for the angles).

All I need is some simple function like so:
Code:
1
2
3
4
5
6
7
8
local function RotationsFacingEachOther(rotA, rotB)
     if <math stuffs> then
          
          return true
     end
     
     return false
end


Of course, feel free to guide me to reaching the solution by myself, either way works!

Thanks.
fish
31.05.21 09:03:17 am
Up
Hador
User
Offline Off
That should be simple - it's basically if rotA = 360 - rotB (although you might want to use a margin here that gives you a wider range of angles the closer the opponent is).

You could also go all out Linear Algebra on this and assume the players viewing direction is a line and your opponent a circle and you check for collisions that way (and in reverse, too if you want both to be facing each other).
Hador
31.05.21 11:45:34 am
Up
Mami Tomoe
User
Offline Off
Well, I tried this:

Code:
1
2
3
4
5
6
7
8
9
local function rotations_facing_each_other(rotA, rotB)
     print(rotA, 360 -rotB)
     if rotA == 360 -rotB then

          return true
     end

     return false
end


And my output when doing this
Code:
1
lua print(rotations_facing_each_other(player(1,'rot'),player(2,'rot')))
in the console is this:
Code:
1
2
179.76782226563    357.00073242188
false

It seems to be too far off, even when we're pretty much looking at each other.
fish
31.05.21 12:12:57 pm
Up
Cebra
User
Offline Off
i suggest to look if the delta between the angles is 180.
In code, this means:
Code:
1
if math.abs(rotA-rotB) == 180 then ...


as user Hador suggested, i would use a margin, since it is really hard to get exactly 180 degree, for example like this:
Code:
1
if math.abs(rotA-rotB) <= 190 and math.abs(rotA-rotB) >= 170  then ...


edit: watch out, i noticed that player(id,"rot") return a value between -90 and 270. But - if i'm not wrong - it should work anyway. If this cause problems simply use
rotA % 360
loading...
31.05.21 03:20:30 pm
Up
Mami Tomoe
User
Offline Off
I tried the following:
Code:
1
2
3
4
5
6
7
8
9
10
function rotations_facing_each_other(rotA, rotB)
     local abs = math.abs((rotA % 360) - (rotB % 360))

     if abs <= 190 and abs >= 170 then

          return true
     end

     return false
end


With and without the
% 360
part.
I don't seem to get consistent results.
When I look at the player holding the shield, it only sometimes works properly, and some other times, it reports
true
when it's supposed to be
false
.

Any idea on why that might be?
fish
31.05.21 03:26:07 pm
Up
Masea
Super User
Offline Off
@user Mami Tomoe: Print out
abs
to see whether that if statement even makes sense in the first place.
Shit your pants: file cs2d Outlast II Mod (29) | Create your UI faster: CS2D UI Framework
31.05.21 03:46:14 pm
Up
Mami Tomoe
User
Offline Off
@user Masea: In order to get the function to return
true
when I hit the shield I had to do this:
Code:
1
2
3
4
5
6
7
8
9
10
11
function rotations_facing_each_other(rotA, rotB)
     local abs = math.abs((rotA % 360) - (rotB % 360))
     hc.event(0,tostring(abs))

     if abs >= 120 and abs <= 240 then

          return true
     end

     return false
end


But, it still returns true even when I'm not pointing at the bot, I think it's hard to explain, someone might need to look into that.

Equip a bot with a Tactical Shield and just look at them and test to see if the function returns
true
when it's supposed to.
fish
31.05.21 04:19:03 pm
Up
ohaz
User
Offline Off
Calculate the angle of the line between the two players:
Quote:
angle1 = atan2(y2 - y1, x2 - x1)
angle2 = atan2(y1-y2, x1-x2)

And then check if angle1 is "close" to the view rotation of player1 and angle2 is close to the view rotation of player2
Code:
1
2
3
if abs(angle1-rot1) < 20 and abs(angle2-rot2) < 20:
     return true
return false
https://ohaz.engineer - Software Engineering
31.05.21 04:32:11 pm
Up
Cebra
User
Offline Off
i've tested it now.
it does kinda work, but this part of the code only test, if the rotation of the players is "right".
Look here: https://prnt.sc/13mwhof
For your code both cases are the same.

edit: user ohaz was faster
loading...
31.05.21 04:35:57 pm
Up
Mami Tomoe
User
Offline Off
@user ohaz: This always returns
false
:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function rotations_facing_each_other(p, id)
     local atan2     = math.atan2
     local abs     = math.abs

     local x1, y1 = player(p, 'x'),     player(p, 'y')
     local x2, y2 = player(id, 'x'),     player(id, 'y')

     local angle1 = atan2(y2 - y1, x2 - x1)
     local angle2 = atan2(y1 - y2, x1 - x2)

     if abs(angle1 - player(p, 'rot')) < 32 and abs(angle2 - player(id, 'rot')) < 32 then

          return true
     end

     return false
end
fish
31.05.21 09:03:27 pm
Up
Bowlinghead
User
Offline Off
The player-rotation (player(id,"rot")) is kinda scary. It goes from -90 to +270.

Use this script on a local server to see what I mean:
Code:
1
2
3
4
addhook("ms100","yAy")
function yAy()
     msg("Rotation: "..player(1,"rot"))
end




You wrote: atan(deltaY / deltaX) - player(p, 'rot') (in line 9, 11) which does NOT make sense. Your atan2 is in radiant while your rotation is in "degree"
edited 2×, last 31.05.21 09:14:50 pm
Share time limited free games here
31.05.21 09:11:52 pm
Up
Mami Tomoe
User
Offline Off
@user Bowlinghead: Will using
math.rad
on the two rotations help?

Also, should I always
rot % 360
my player rotations? Is it better? Should I write a wrapper for that?
fish
31.05.21 09:37:30 pm
Up
Bowlinghead
User
Offline Off
Im by no means an expert, but I would rather convert everything into degree for more precision and less perfomance costs.
Since I dont have further information: I assume your function runs on a timer, hence returns most of the time "FALSE".

Therefore I would split the directions the player is looking into 4 quadrants. Then I only have to deal with 0-90° and not with ugly negative numbers.

If one is looking top left and the other also top left, there is no point in doing heavy math.

Code example >
Share time limited free games here
31.05.21 09:55:09 pm
Up
Mami Tomoe
User
Offline Off
@user Bowlinghead:

It's used on the hit hook, to verify that a player is shooting onto another player's shield.
fish
31.05.21 10:02:33 pm
Up
Bowlinghead
User
Offline Off
I dont think that changes my argument, but I dunno. I just dont want to think about shotguns that shoot in a wide angle and you hit with the most outer bullet...

What is the shield length by the way?
Share time limited free games here
31.05.21 10:22:11 pm
Up
Gaios
Security Supporter
Offline Off
user Bowlinghead has written:
What is the shield length by the way?

The angle will be another depending on how close you're to the shield.

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
local atan2 = math.atan2
local deg = math.deg
local abs = math.abs

function point_direction(x1, y1, x2, y2)
     return -deg(atan2(x1 - x2, y1 - y2))
end

function players_facing_each_other(p1, p2)
    local angle = 12
    local p1_rot, x1, y1 = player(p1, 'rot'), player(p1, 'x'), player(p1, 'y')
    local p2_rot, x2, y2 = player(p2, 'rot'), player(p2, 'x'), player(p2, 'y')
    p1_rot = (p1_rot < 0) and (p1_rot + 360) or p1_rot
    p2_rot = (p2_rot < 0) and (p2_rot + 360) or p2_rot
    local p1_dir = p1_rot - 180
    local p2_dir = p2_rot - 180

    local dir1 = point_direction(x2, y2, x1, y1)
    local dir2 = point_direction(x1, y1, x2, y2)

    if (abs(abs(dir1) - abs(p1_dir)) <= angle) and (abs(abs(dir2) - abs(p2_dir)) <= angle) then
        return true
    end

    return false
end


Now what you need to do is to edit the
angle
, depending on how far the players are. Because the close players are, the wider angle is.
IMG:https://i.postimg.cc/XqzHFCRy/image.png


I know how to calculate x1 y1 and x2 y2 of player width positions, but I have no idea how to deal with the 3 vectors and calculate the α (alpha).
edited 3×, last 01.06.21 12:01:05 am
01.06.21 01:51:51 am
Up
SQ
Moderator
Offline Off
Calculating angles is more like a workaround.


This is how it should be solved properly using vectors.

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
function Dot(a, b)
    return((a.x*b.x)+(a.y*b.y))
end

function Distance(a, b)
     return math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y))
end

function PointToLine(pixelPos, vA, vB)
    local v0, v1 = {}, {}

    v0.x = vB.x - vA.x
    v0.y = vB.y - vA.y

    local sqrt = math.sqrt(v0.x * v0.x + v0.y * v0.y)

    v0.x = v0.x / sqrt
    v0.y = v0.y / sqrt

    v1.x = pixelPos.x - vA.x
    v1.y = pixelPos.y - vA.y

    local t = Dot(v0, v1)

    if (t <= 0) then
        return(vA)
    elseif (t >= Distance(vA, vB)) then
        return(vB)
    end

    local v2 = {}

    v2.x = vA.x + v0.x * t
    v2.y = vA.y + v0.y * t

    return(v2)
end

function PlayersFaceEachOther(pid1, pid2)
    if (player(pid1, "exists") == false or player(pid2, "exists") == false) then
        return(false);
    end

    if (player(pid1, "health") < 1 or player(pid2, "health") < 1) then
        return(false)
    end

    local p1, p2 = {}, {}
    local p1raycast, p2raycast = {}, {}

    -- needs check if player is in other player screen
    
    -- player 1
    p1.x = player(pid1, "x")
    p1.y = player(pid1, "y")
    p1.rot = math.rad(player(pid1, "rot") - 90)

    p1raycast.x = p1.x + math.cos(p1.rot) * 320
    p1raycast.y = p1.y + math.sin(p1.rot) * 320

    -- player 2
    p2.x = player(pid2, "x")
    p2.y = player(pid2, "y")
    p2.rot = math.rad(player(pid2, "rot") - 90)
    
    -- raycast offsets
    p2raycast.x = p2.x + math.cos(p2.rot) * 320
    p2raycast.y = p2.y + math.sin(p2.rot) * 320

    -- raycast p1
    p1vec = PointToLine(p1, p2, p2raycast)
    p2seeOther = Distance(p1vec, p1) < 16

    -- raycast p2
    p2vec = PointToLine(p2, p1, p1raycast)
    p1seeOther = Distance(p2vec, p2) < 16  
    
    -- combine results
    return(p1seeOther and p2seeOther)
end

function always() 
    seeEachOther = PlayersFaceEachOther(1, 2)

    if (seeEachOther) then
        parse("msg seeeachother *"..os.clock())
    end
    
end

addhook("always", "always")
edited 7×, last 01.06.21 04:12:32 am
01.06.21 03:28:14 am
Up
Mami Tomoe
User
Offline Off
@user SQ:
I'm going to assume I can just modify that always hook to be called whenever I need to check (which is on the hit hook).

I do have a question though, what does this mean:
Code:
1
-- needs check if player is in other player screen


Because in CS2D, there's a thing called cs2d cmd sv_offscreendamage, and depending on that, the check (if any was added) may or may not be needed.
fish
01.06.21 03:52:45 am
Up
SQ
Moderator
Offline Off
I just left note that you might need to check if other player is offscreen in some scenarios.

Separated the logics from always hook.

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
function Dot(a, b)
    return((a.x*b.x)+(a.y*b.y))
end

function Distance(a, b)
     return math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y))
end

function PointToLine(pixelPos, vA, vB)
    local v0, v1 = {}, {}

    v0.x = vB.x - vA.x
    v0.y = vB.y - vA.y

    local sqrt = math.sqrt(v0.x * v0.x + v0.y * v0.y)

    v0.x = v0.x / sqrt
    v0.y = v0.y / sqrt

    v1.x = pixelPos.x - vA.x
    v1.y = pixelPos.y - vA.y

    local t = Dot(v0, v1)

    if (t <= 0) then
        return(vA)
    elseif (t >= Distance(vA, vB)) then
        return(vB)
    end

    local v2 = {}

    v2.x = vA.x + v0.x * t
    v2.y = vA.y + v0.y * t

    return(v2)
end

function PlayersFaceEachOther(pid1, pid2)
    if (player(pid1, "exists") == false or player(pid2, "exists") == false) then
        return(false)
    end

    if (player(pid1, "health") < 1 or player(pid2, "health") < 1) then
        return(false)
    end

    local p1, p2 = {}, {}
    local p1raycast, p2raycast = {}, {}

    -- needs check if player is in other player screen
    
    -- player 1
    p1.x = player(pid1, "x")
    p1.y = player(pid1, "y")
    p1.rot = math.rad(player(pid1, "rot") - 90)

    -- player 2
    p2.x = player(pid2, "x")
    p2.y = player(pid2, "y")
    p2.rot = math.rad(player(pid2, "rot") - 90)
    
    -- raycast offsets
    p1raycast.x = p1.x + math.cos(p1.rot) * 320
    p1raycast.y = p1.y + math.sin(p1.rot) * 320

    p2raycast.x = p2.x + math.cos(p2.rot) * 320
    p2raycast.y = p2.y + math.sin(p2.rot) * 320

    -- raycast p1
    p1vec = PointToLine(p1, p2, p2raycast)
    p2seeOther = Distance(p1vec, p1) < 16

    -- raycast p2
    p2vec = PointToLine(p2, p1, p1raycast)
    p1seeOther = Distance(p2vec, p2) < 16  
    
    -- combine results
    return(p1seeOther and p2seeOther)
end

function always() 
    seeEachOther = PlayersFaceEachOther(1, 2)

    if (seeEachOther) then
        parse("msg seeeachother *"..os.clock())
    end
end

addhook("always", "always")
edited 6×, last 01.06.21 12:03:44 pm
01.06.21 04:01:13 am
Up
Mami Tomoe
User
Offline Off
@user SQ: Thank you, I will test it out tomorrow!

EDIT:
I tried it, and it appears to be inconsistent as well, when I hit by standing at the left/right sides of the victim, it will register the hit, but when I aim for the same place while standing in front of the victim, it does not register.
edited 1×, last 01.06.21 12:05:57 pm
fish
To the start Previous 1 2 Next To the start