WhiteBallPredicted.cs 6.88 KB
Newer Older
cann-alberto's avatar
cann-alberto committed
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
using System;
using UnityEngine;

namespace Mirror.Examples.BilliardsPredicted
{
    public class WhiteBallPredicted : NetworkBehaviour
    {
        public LineRenderer dragIndicator;
        public float dragTolerance = 1.0f;
        public Rigidbody rigidBody;
        public float forceMultiplier = 2;
        public float maxForce = 40;

        // remember start position to reset to after entering a pocket
        internal Vector3 startPosition;

        bool draggingStartedOverObject;

        // cast mouse position on screen to world position
        bool MouseToWorld(out Vector3 position)
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            Plane plane = new Plane(Vector3.up, transform.position);
            if (plane.Raycast(ray, out float distance))
            {
                position = ray.GetPoint(distance);
                return true;
            }
            position = default;
            return false;
        }

        void Awake()
        {
            startPosition = transform.position;
        }

        [ClientCallback]
        void Update()
        {
            // mouse down on the white ball?
            if (Input.GetMouseButtonDown(0))
            {
                if (MouseToWorld(out Vector3 position))
                {
                    // allow dragging if mouse is 'close enough'.
                    // balls are moving so we don't need to be exactly on it.
                    float distance = Vector3.Distance(position, transform.position);
                    if (distance <= dragTolerance)
                    {
                        // enable drag indicator
                        dragIndicator.SetPosition(0, transform.position);
                        dragIndicator.SetPosition(1, transform.position);
                        dragIndicator.gameObject.SetActive(true);

                        draggingStartedOverObject = true;
                    }
                }
            }
            // mouse button dragging?
            else if (Input.GetMouseButton(0))
            {
                // cast mouse position to world
                if (draggingStartedOverObject && MouseToWorld(out Vector3 current))
                {
                    // drag indicator
                    dragIndicator.SetPosition(0, transform.position);
                    dragIndicator.SetPosition(1, current);
                }
            }
            // mouse button up?
            else if (Input.GetMouseButtonUp(0))
            {
                // cast mouse position to world
                if (draggingStartedOverObject && MouseToWorld(out Vector3 current))
                {
                    // calculate delta from ball to mouse
                    // ball may have moved since we started dragging,
                    // so always use current ball position here.
                    Vector3 from = transform.position;

                    // debug drawing: only works if Gizmos are enabled!
                    Debug.DrawLine(from, current, Color.white, 2);

                    // calculate pending force delta
                    Vector3 delta = from - current;
                    Vector3 force = delta * forceMultiplier;

                    // there should be a maximum allowed force
                    force = Vector3.ClampMagnitude(force, maxForce);

                    // forward the event to the local player's object.
                    // the ball isn't part of the local player.
                    NetworkClient.localPlayer.GetComponent<PlayerPredicted>().OnDraggedBall(force);

                    // disable drag indicator
                    dragIndicator.gameObject.SetActive(false);
                }

                draggingStartedOverObject = false;
            }
        }

        // OnMouse callbacks don't work for predicted objects because we need to
        // move the collider out of the main object ocassionally.
        // besides, having a drag tolerance and not having to click exactly on
        // the white ball is nice.
        /*
        [ClientCallback]
        void OnMouseDown()
        {
            // enable drag indicator
            dragIndicator.SetPosition(0, transform.position);
            dragIndicator.SetPosition(1, transform.position);
            dragIndicator.gameObject.SetActive(true);
        }

        [ClientCallback]
        void OnMouseDrag()
        {
            // cast mouse position to world
            if (!MouseToWorld(out Vector3 current)) return;

            // drag indicator
            dragIndicator.SetPosition(0, transform.position);
            dragIndicator.SetPosition(1, current);
        }

        [ClientCallback]
        void OnMouseUp()
        {
            // cast mouse position to world
            if (!MouseToWorld(out Vector3 current)) return;

            // calculate delta from ball to mouse
            // ball may have moved since we started dragging,
            // so always use current ball position here.
            Vector3 from = transform.position;

            // debug drawing: only works if Gizmos are enabled!
            Debug.DrawLine(from, current, Color.white, 2);

            // calculate pending force delta
            Vector3 delta = from - current;
            Vector3 force = delta * forceMultiplier;

            // there should be a maximum allowed force
            force = Vector3.ClampMagnitude(force, maxForce);

            // forward the event to the local player's object.
            // the ball isn't part of the local player.
            NetworkClient.localPlayer.GetComponent<PlayerPredicted>().OnDraggedBall(force);

            // disable drag indicator
            dragIndicator.gameObject.SetActive(false);
        }
        */

        /* ball<->pocket collisions are handled by Pockets.cs for now.
           because predicted object's rigidbodies are sometimes moved out of them.
           which means this script here wouldn't get the collision info while predicting.
           which means it's easier to check collisions from the table perspective.
        // reset position when entering a pocket.
        // there's only one trigger in the scene (the pocket).
        [ServerCallback]
        void OnTriggerEnter(Collider other)
        {
            rigidBody.position = startPosition;
            rigidBody.Sleep(); // reset forces
            // GetComponent<NetworkRigidbodyUnreliable>().RpcTeleport(startPosition);
        }
        */

        [ClientCallback]
        void OnGUI()
        {
            // have a button to reply exactly the same force in every hit for easier testing.
            if (GUI.Button(new Rect(10, 150, 200, 20), "Hit!"))
            {
                // hit with a slight angle so the red balls spread out in all directions
                Vector3 force = Vector3.ClampMagnitude(new Vector3(10, 0, 600), maxForce);
                NetworkClient.localPlayer.GetComponent<PlayerPredicted>().OnDraggedBall(force);
            }
        }
    }
}